flutterdarterror-handling

What's different between FlutterError.onError and runZonedGuarded.onError?


Please explain to me what is the difference between them. Can they be used together or only separately?


Solution

  • runZonedGuarded catches Asynchronous exceptions and FlutterError.onError catches Synchronous exceptions.

    Flutter App to demonstrate both behaviors:

    import 'dart:async';
    import 'package:flutter/material.dart';
    
    Future<void> main() async {
    
      // Application asynchronous uncaught exception handler
      runZonedGuarded<Future<void>>(    
        // body,
        () async {
          WidgetsFlutterBinding.ensureInitialized();
          runApp(const MyApp());
        },
        // onError
        (Object uncaughtException, StackTrace stackTrace) async {
          print("runZonedGuarded caught an Asynchronous exception");
          print("Uncaught Exception: $uncaughtException");
          print("Stacktrace:\n$stackTrace");
        },
      );
    
      // Application syncronous uncaught exception handler
      FlutterError.onError = (FlutterErrorDetails errorDetails) {
        print("FlutterError.onError caught a Synchronous exception");
        print("Uncaught Exception Details:\n$errorDetails");
      };
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({super.key});
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'FlutterError.onError vs. runZonedGuarded Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: const HomePage(title: 'FlutterError.onError vs. runZonedGuarded Demo'),
        );
      }
    }
    
    class HomePage extends StatefulWidget {
      const HomePage({super.key, required this.title});
    
      final String title;
    
      @override
      State<HomePage> createState() => _HomePageState();
    }
    
    class _HomePageState extends State<HomePage>{
    
      int _counter = 0;
    
    Future<void> asynchronousError() async {
    
      setState(() { _counter++; });
      throw UnimplementedError();
    }
    
    void synchronousError() {
    
      setState(() { _counter++; });
      throw UnimplementedError();
    }
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            appBar: AppBar(
              title: Text(widget.title),
            ),
            body: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  const Text(
                    'Count is: '
                  ),
                  Text(
                    '$_counter',
                    style: Theme.of(context).textTheme.headlineLarge
                  ),
                  ElevatedButton(
                    onPressed: asynchronousError,
                    child: const Text("Cause Asynchronous Exception"),
                  ),
                  SizedBox(height: 16),
                  ElevatedButton(
                    onPressed: synchronousError,
                    child: const Text("Cause Synchronous Exception"),
                  ),
                ],
              ),
            ),
          ),
        );
      }
    }
    

    DEBUG CONSOLE output when 'Cause Asynchronous Exception' button is tapped:

    I/flutter (27723): runZonedGuarded caught an Asynchronous exception
    I/flutter (27723): Uncaught Exception: UnimplementedError
    I/flutter (27723): Stacktrace:
    I/flutter (27723): #0      _HomePageState.asynchronousError (package:zone_test/main.dart:59:3)
    I/flutter (27723): #1      _InkResponseState.handleTap (package:flutter/src/material/ink_well.dart:1204:21)
    I/flutter (27723): #2      GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:345:24)
    I/flutter (27723): #3      TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:758:11)
    I/flutter (27723): #4      BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:383:5)
    I/flutter (27723): #5      BaseTapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:314:7)
    I/flutter (27723): #6      PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:721:9)
    I/flutter (27723): #7      PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:97:12)
    I/flutter (27723): #8      PointerRouter._dispatchEventToRoutes.<anonymous closure> (package:flutter/src/gestures/pointer_router.dart:142:9)
    I/flutter (27723): #9      _LinkedHashMapMixin.forEach (dart:_compact_hash:764:13)
    I/flutter (27723): #10     PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:
    I/ViewRootImpl@29f1b88[MainActivity](27723): Relayout returned: old=(0,0,1080,2220) new=(0,0,1080,2220) req=(1080,2220)0 dur=6 res=0x1 s={true 522720604160} ch=false
    

    DEBUG CONSOLE output when 'Cause Synchronous Exception' button is tapped:

    I/ViewRootImpl@29f1b88[MainActivity](27723): Relayout returned: old=(0,0,1080,2220) new=(0,0,1080,2220) req=(1080,2220)0 dur=5 res=0x1 s={true 522720604160} ch=false
    I/flutter (27723): FlutterError.onError caught a Synchronous exception
    I/flutter (27723): Uncaught Exception Details:
    I/flutter (27723): ══╡ EXCEPTION CAUGHT BY GESTURE ╞════════════════════════════════
    I/flutter (27723): The following UnimplementedError was thrown while handling a
    I/flutter (27723): gesture:
    I/flutter (27723): UnimplementedError
    I/flutter (27723):
    I/flutter (27723): When the exception was thrown, this was the stack:
    I/flutter (27723): #0      _HomePageState.synchronousError (package:zone_test/main.dart:65:3)
    I/flutter (27723): #1      _InkResponseState.handleTap (package:flutter/src/material/ink_well.dart:1204:21)
    I/flutter (27723): #2      GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:345:24)
    I/flutter (27723): #3      TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:758:11)
    I/flutter (27723): #4      BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:383:5)
    I/flutter (27723): #5      BaseTapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:314:7)
    I/flutter (27723): #6      PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:721:9)
    I/flutter (27723): #7      PointerRouter._dispatch (package:flutter/src/gestures/pointer_router