flutter
/

GetX Global Error Handler: Catch All Exceptions & Show User Feedback

Last Sync: Today

On this page

13
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

flutter

GetX Global Error Handler: Catch All Exceptions & Show User Feedback

Introduction

No matter how carefully you write your code, errors will happen. A robust app must handle them gracefully, showing users a friendly message and logging the details for debugging. GetX provides the tools to build a centralised error handler that catches both Flutter‑specific errors and uncaught exceptions from your Dart code. This guide covers how to set up a global error handler that integrates with GetX's reactive state and dependency injection.

  1. Flutter's Error Handling Mechanisms

Flutter provides two main ways to catch errors:

  • FlutterError.onError – Catches errors from the Flutter framework (e.g., build errors).
  • PlatformDispatcher.instance.onError – Catches uncaught Dart exceptions that occur outside the Flutter framework (e.g., async errors).
  • Zones – A more advanced way to isolate and handle errors in specific parts of your code.

  1. Creating a Global Error Service

Create a service that extends GetxService to centralise error handling. This service will register itself as the global error handler and expose a reactive variable to show errors in the UI.

DARTRead-only
1
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';

class GlobalErrorService extends GetxService {
  // Reactive error message that can be displayed in UI
  var lastError = ''.obs;
  var lastErrorDetails = ''.obs;

  @override
  void onInit() {
    super.onInit();
    _setupErrorHandlers();
  }

  void _setupErrorHandlers() {
    // Handle Flutter framework errors
    FlutterError.onError = (FlutterErrorDetails details) {
      // Show in UI
      lastError.value = 'Flutter Error';
      lastErrorDetails.value = details.toString();
      // Log to console
      FlutterError.dumpErrorToConsole(details);
      // Optionally show a snackbar
      Get.snackbar('Error', 'Something went wrong', snackPosition: SnackPosition.BOTTOM);
    };

    // Handle uncaught Dart exceptions
    PlatformDispatcher.instance.onError = (Object error, StackTrace stack) {
      lastError.value = error.toString();
      lastErrorDetails.value = stack.toString();
      Get.snackbar('Error', error.toString());
      return true; // Prevent the default handler
    };
  }

  void logError(dynamic error, StackTrace? stack) {
    lastError.value = error.toString();
    lastErrorDetails.value = stack?.toString() ?? '';
    // You can also send to a logging service
  }
}

  1. Registering the Service

Register the service as permanent in an initial binding. It will be available throughout the app.

DARTRead-only
1
class AppBinding extends Bindings {
  @override
  void dependencies() {
    Get.put(GlobalErrorService(), permanent: true);
  }
}

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      initialBinding: AppBinding(),
      home: HomePage(),
    );
  }
}

  1. Displaying Errors in the UI

You can use the service's reactive variables to show a global error banner, or listen to errors and show snackbars via workers.

DARTRead-only
1
class GlobalErrorConsumer extends StatelessWidget {
  final errorService = Get.find<GlobalErrorService>();

  @override
  Widget build(BuildContext context) {
    return Obx(() {
      if (errorService.lastError.value.isNotEmpty) {
        return SnackBar(
          content: Text(errorService.lastError.value),
          duration: Duration(seconds: 3),
        );
      }
      return SizedBox.shrink();
    });
  }
}

  1. Catching Errors in GetX Controllers

For async operations in controllers, always use try‑catch and update a reactive error variable. You can then use a worker to forward those errors to the global service.

DARTRead-only
1
class MyController extends GetxController {
  final errorService = Get.find<GlobalErrorService>();
  var error = ''.obs;

  @override
  void onInit() {
    super.onInit();
    ever(error, (err) {
      if (err.isNotEmpty) errorService.logError(err, null);
    });
  }

  Future<void> fetchData() async {
    try {
      // do something that might throw
      throw Exception('Network error');
    } catch (e) {
      error.value = e.toString();
    }
  }
}

  1. Handling Errors in Workers

Workers that perform async operations should also catch errors and forward them to the global service.

DARTRead-only
1
debounce(query, (value) async {
  try {
    await search();
  } catch (e) {
    Get.find<GlobalErrorService>().logError(e, null);
  }
}, time: Duration(milliseconds: 500));

  1. Using Zones for Even More Control

If you want to catch errors in a specific part of your app (e.g., a particular widget subtree), you can use a Zone. This is useful for isolating errors and providing custom handling.

DARTRead-only
1
runZonedGuarded(() {
  runApp(MyApp());
}, (error, stack) {
  Get.find<GlobalErrorService>().logError(error, stack);
});

  1. Logging Errors to a Remote Service

In production, you should send errors to a logging service like Firebase Crashlytics or Sentry. Extend your error service to include this.

DARTRead-only
1
void logError(dynamic error, StackTrace? stack) {
  // Local logging
  lastError.value = error.toString();
  // Remote logging
  if (kReleaseMode) {
    FirebaseCrashlytics.instance.recordError(error, stack);
  }
}

Best Practices

  • Use a centralised error service – One place to handle logging and user feedback.
  • Always catch exceptions in async code – Uncaught async errors will crash the app.
  • Show user‑friendly messages – Avoid displaying technical stack traces.
  • Log errors with context – Include screen name, user action, etc.
  • Test error handling – Simulate errors and verify they are caught and displayed.
  • Don't rely solely on global handlers – They are a safety net, but you should still handle expected errors locally.

Common Mistakes

  • ❌ Not initializing the error service before runApp – Global handlers may not be set. ✅ Set up the service in an initial binding and ensure it runs before any code that could error.
  • ❌ Swallowing errors without logging – Makes debugging impossible. ✅ Always log or forward errors to a central handler.
  • ❌ Showing raw exception messages to users – May expose internal details. ✅ Map exceptions to user‑friendly strings.
  • ❌ Forgetting to handle errors in workers and controllers – Only global handler catches uncaught; but if you catch locally, you must still log.

FAQ

  • Q: Does GetX have a built‑in global error handler?
    A: No, but it provides the tools to create one easily using Flutter's error handling APIs.
  • Q: How do I prevent the default Flutter error overlay from showing?
    A: In FlutterError.onError, after handling, do not call dumpErrorToConsole if you want to suppress the overlay. But for development, you might still want it.
  • Q: Can I catch errors from GetX navigation?
    A: Yes, errors inside onPressed or Get.to are caught by the global handlers. For async navigation, use try‑catch.
  • Q: How to handle errors in isolated parts of the app?
    A: Use runZonedGuarded around those parts. This gives you fine‑grained control.
  • Q: Should I use FlutterError.onError or PlatformDispatcher.instance.onError?
    A: Both. They cover different categories of errors. Use both for full coverage.

Conclusion

A global error handler is essential for production Flutter apps. By combining Flutter's error handling APIs with GetX's dependency injection and reactive state, you can build a system that not only logs errors but also provides a smooth user experience. Remember to test your error handling thoroughly and always provide a way for users to continue using the app after an error.

Test Your Knowledge

Q1
of 3

Which API should you use to catch uncaught Dart exceptions?

A
FlutterError.onError
B
PlatformDispatcher.instance.onError
C
Get.onError
D
Zone.current.onError
Q2
of 3

How can you make a global error service available throughout the app?

A
Create a singleton class
B
Use Get.put with permanent: true in an initial binding
C
Store it in a global variable
D
Inherit from GetxController
Q3
of 3

What is the recommended way to show a user‑friendly error message after an exception?

A
Show the full stack trace
B
Show a generic message and optionally log the details
C
Ignore the error
D
Crash the app

Previous

getx app lifecycle

Next

getx network retry

Related Content

Need help?

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