flutter
/

GetX Logging & Debugging: Track State, Errors & Performance

Last Sync: Today

On this page

15
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

flutter

GetX Logging & Debugging: Track State, Errors & Performance

Introduction

Debugging is an essential part of Flutter development. GetX provides several tools and patterns to make debugging easier, from simple logging to tracking reactive state changes. This guide covers how to effectively log events, debug controllers, monitor navigation, and use DevTools with GetX. You'll learn techniques to catch issues early and build more reliable apps.

  1. Basic Logging with print and debugPrint

The simplest way to log is using print(). However, on large outputs, Flutter may truncate long strings. Use debugPrint() instead – it splits long strings into chunks, preventing truncation.

DARTRead-only
1
void main() {
  debugPrint('App started');
  runApp(MyApp());
}

class MyController extends GetxController {
  void doSomething() {
    debugPrint('Button clicked');
  }
}

  1. GetX's Built‑in Logging

GetX provides a static Get.log() method that formats logs with a timestamp and a prefix. It's useful for distinguishing logs from different parts of the app.

DARTRead-only
1
Get.log('Login attempt');
Get.log('API call succeeded', isError: false);
Get.log('Error occurred', isError: true);

  1. Logging Controller Lifecycle

Add logs in onInit, onReady, and onClose to verify that controllers are being created and disposed correctly.

DARTRead-only
1
class MyController extends GetxController {
  @override
  void onInit() {
    super.onInit();
    debugPrint('MyController initialized');
  }

  @override
  void onReady() {
    super.onReady();
    debugPrint('MyController ready');
  }

  @override
  void onClose() {
    debugPrint('MyController closed');
    super.onClose();
  }
}

  1. Debugging Reactive State Changes

Use workers to log when reactive variables change. This is invaluable for understanding why your UI updates (or doesn't).

DARTRead-only
1
class CounterController extends GetxController {
  var count = 0.obs;

  @override
  void onInit() {
    super.onInit();
    ever(count, (value) {
      debugPrint('Count changed to $value');
    });
  }
}

  1. Debugging Navigation

To log every navigation, you can use Get.routeObserver or add a global middleware. A middleware can log route changes.

DARTRead-only
1
class LoggingMiddleware extends GetMiddleware {
  @override
  List<GetPage> onPageCalled(GetPage page) {
    debugPrint('Navigating to ${page.name}');
    return [page];
  }
}

// Add to GetPage
GetPage(
  name: '/home',
  page: () => HomePage(),
  middlewares: [LoggingMiddleware()],
);

Alternatively, set a global routing callback in GetMaterialApp:

DARTRead-only
1
GetMaterialApp(
  routingCallback: (routing) {
    debugPrint('Route: ${routing?.current}');
  },
);

  1. Logging API Calls with Interceptors

If you use Dio, add a logging interceptor to log requests and responses. This is extremely helpful for debugging network issues.

DARTRead-only
1
dio.interceptors.add(LogInterceptor(
  requestBody: true,
  responseBody: true,
));

  1. Using Flutter DevTools with GetX

Flutter DevTools provides a rich debugging experience. To inspect reactive state, you can use the 'Widget Inspector' to see widget rebuilds, and the 'Memory' tab to verify controllers are being disposed. Add breakpoints in onInit and onClose to trace lifecycle.

  1. Debugging Workers

If a worker isn't triggering, check that the reactive variable is actually changing. Add logs inside the worker callback and ensure the variable is updated. Also verify that the worker is registered after the variable is created (in onInit).

DARTRead-only
1
debounce(query, (value) {
  debugPrint('Debounce triggered for: $value');
}, time: Duration(milliseconds: 500));

  1. Logging Errors Globally

Use the global error handling techniques covered in the getx-global-error-handler topic to log all uncaught exceptions. This helps catch issues that might otherwise go unnoticed.

DARTRead-only
1
FlutterError.onError = (details) {
  debugPrint('Flutter error: ${details.exception}');
};
PlatformDispatcher.instance.onError = (error, stack) {
  debugPrint('Uncaught error: $error');
  return true;
};

  1. Performance Logging

To measure performance, use Stopwatch around critical operations. Log the duration to spot bottlenecks.

DARTRead-only
1
final stopwatch = Stopwatch()..start();
await heavyOperation();
stopwatch.stop();
debugPrint('Heavy operation took ${stopwatch.elapsedMilliseconds} ms');

Best Practices

  • Use debugPrint instead of print – Prevents truncation.
  • Log controller lifecycle – Helps ensure proper disposal.
  • Use workers to track state changes – Great for debugging reactive flow.
  • Add middleware for route logging – Track navigation easily.
  • Use a logging interceptor for HTTP – See exactly what is sent/received.
  • Keep logs informative but not noisy – Avoid flooding the console in production.
  • Use conditional logging – Wrap logs with kDebugMode to avoid logging in release builds.
  • Use DevTools – Leverage inspector and memory tools.

Common Mistakes

  • ❌ Using print for long strings – May get truncated. ✅ Use debugPrint.
  • ❌ Logging sensitive data – Tokens, passwords, etc. ✅ Redact or avoid logging sensitive info.
  • ❌ Forgetting to add logs in workers – Hard to know if they fire. ✅ Add logs to see when workers run.
  • ❌ Logging too much in production – Can impact performance and expose data. ✅ Use kDebugMode or a configuration flag.

FAQ

  • Q: How do I log only in debug mode?
    A: Use if (kDebugMode) debugPrint(...). kDebugMode is a constant from foundation.dart that is true in debug builds.
  • Q: Can I use Get.log with custom tags?
    A: Get.log doesn't support custom tags out of the box, but you can create your own wrapper.
  • Q: How to see which widgets are rebuilding?
    A: Use DevTools' 'Widget Inspector' with 'Highlight repaints' enabled. This shows green borders around widgets that rebuild.
  • Q: Why is my worker not triggering?
    A: Ensure the reactive variable is being updated (check with a log). Also ensure the worker is set up in onInit, not after the variable changes.
  • Q: How to log the entire state of a controller?
    A: Override toString in your controller and call it from a debug function, or use debugPrint(controller.toString()).

Conclusion

Effective logging and debugging are essential for building reliable Flutter apps. GetX provides the building blocks to log state changes, lifecycle events, and navigation. By combining these with Flutter's DevTools and best practices, you can quickly identify and fix issues during development.

Test Your Knowledge

Q1
of 3

Which function should you use to print long strings without truncation?

A
print()
B
debugPrint()
C
console.log()
D
log()
Q2
of 3

How can you log every time a reactive variable changes?

A
Use a worker like ever
B
Use Get.log inside the setter
C
Override the variable's setter
D
Use debugPrint in the UI
Q3
of 3

Which GetX feature can you use to log route changes globally?

A
Get.logRoute
B
routingCallback
C
Get.routeObserver
D
Both B and C

Previous

getx memory management

Next

getx route observer

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.