back_button_interceptor

repository·master·Indexed 41 days ago

https://github.com/marcglasberg/back_button_interceptor

A Flutter package for intercepting Android back-button events. It provides a more flexible alternative to WillPopScope, allowing developers to register multiple interceptor functions with priority (z-index) and name-based removal. Features include RouteInfo for navigation context, support for asynchronous interceptors, and tools for testing back-button behavior via popRoute().

Tokens
2.3K
Snippets
10
Records
11
Agent score
100%

What's inside back_button_interceptor

  1. How interceptor execution order and logic works

    master

    All added interceptor functions are called in order of priority (determined by zIndex).

    • Combined Result: If any function returns true, the combined result is true, and the default back button process is NOT fired. The default process only fires if all functions return false (or null).
    • stopDefaultButtonEvent: Each function receives a boolean stopDefaultButtonEvent which indicates the current combined result from all previous functions in the chain. If a function wants to skip its logic because a previous interceptor already handled the event, it can check if (stopDefaultButtonEvent) return false;.
    • ifNotYetIntercepted: Alternatively, you can set the ifNotYetIntercepted parameter to true when adding the function. This ensures the function is only called if all previous functions returned false (i.e., stopDefaultButtonEvent is false).
  2. Customize the iOS launch screen assets

    master

    To change the launch screen image for your iOS application, you can use one of two methods:

    1. Direct File Replacement: Replace the existing image files located in the example/ios/Runner/Assets.xcassets/LaunchImage.imageset/ directory with your own assets.
    2. Xcode Interface:
      • Open the iOS project in Xcode by running open ios/Runner.xcworkspace from your terminal.
      • In the Xcode Project Navigator, navigate to Runner/Assets.xcassets.
      • Drag and drop your desired images into the asset catalog to replace the current launch images.
    open ios/Runner.xcworkspace
  3. Test back button interception

    master

    To test back button behavior in unit or widget tests, use BackButtonInterceptor.popRoute() to simulate a back button press.

    To verify which interceptors were triggered and what they returned, inspect the BackButtonInterceptor.results static variable.

    Best Practice: Call BackButtonInterceptor.removeAll(); in your test's setUp block to ensure a clean state between tests.

    // In your test file
    @test
    void testBackInterceptor() {
      // ... setup code ...
      
      BackButtonInterceptor.popRoute();
      
      // Check results
      print(BackButtonInterceptor.results);
    }
  4. Complex back-button interception with state and dialogs

    master

    For more advanced scenarios, you can implement multi-step interception logic. For example, you can require a user to perform a series of actions (like clicking elements to change their state) before the back-button is allowed to navigate away.

    Additionally, the package allows for disabling interceptors when specific UI elements, such as a Dialog, are open, ensuring that the back-button behaves normally during modal interactions.

    // Reference to the complex implementation:
    // https://github.com/marcglasberg/back_button_interceptor/blob/master/example/lib/main_complex_example.dart
  5. Basic back-button interception example

    master

    The simplest way to use the package is to intercept the back-button and execute a side effect, such as printing a string to the console. This is useful for preventing accidental navigation or triggering specific logic when a user attempts to leave a screen.

    // Reference to the basic implementation:
    // https://github.com/marcglasberg/back_button_interceptor/blob/master/example/lib/main.dart
  6. Testing back-button interception

    master

    The package is designed to be test-friendly. You can write widget tests to simulate back-button presses and verify that your interceptors are correctly preventing or allowing navigation based on the application state.

    // Reference to the testing implementation:
    // https://github.com/marcglasberg/back_button_interceptor/blob/master/example/test/main_complex_example_test.dart
  7. Debug interceptors with describe()

    master

    If you are managing many interceptors and need to debug their priority or existence, use the BackButtonInterceptor.describe() static method. It returns a string describing the currently registered interceptors, including their names and z-indexes.

    print(BackButtonInterceptor.describe());
  8. Use RouteInfo to access navigation context in interceptors

    master

    The RouteInfo object provided to interceptors allows you to inspect the current navigation state.

    • info.currentRoute(context): Returns the name of the current route in the navigator.
    • info.routeWhenAdded: Contains the route name that was active when the interceptor was added (requires passing context to BackButtonInterceptor.add()).
    • info.ifRouteChanged(context): Returns true if the current route is different from the route that was active when the interceptor was added. This is useful for temporarily disabling interceptors (e.g., when a dialog is open).
    • Route Filtering: You can restrict an interceptor to specific routes by checking info.currentRoute(context) against a list of allowed route names.

    Note: For info.routeWhenAdded and info.ifRouteChanged() to work, you must pass the context to the BackButtonInterceptor.add(..., context: context) method.

    // Example: Disable interceptor if the route has changed (e.g. a dialog is open)
    bool myInterceptor(bool stopDefaultButtonEvent, RouteInfo info) {
      if (info.ifRouteChanged(context)) return false;
      ...
    }
    
    // Example: Only run interceptor on specific routes
    bool myInterceptor(bool stopDefaultButtonEvent, RouteInfo info) {
      if (["myRoute1", "myRoute2", "myRoute3"].contains(info.currentRoute(context))) return false;
      ...
    }
  9. Use z-index and names to manage interceptor priority

    master

    When adding interceptors, you can provide a zIndex and a name to control execution order and facilitate removal.

    • Priority: Functions with a higher zIndex are called first. Functions with a null z-index are called last. If z-indexes are equal, the function added most recently is called first.
    • Removal: If you provide a name, you can remove the interceptor later using BackButtonInterceptor.removeByName(name) instead of keeping a reference to the function itself.
    @override
    void initState() {
       super.initState();
       BackButtonInterceptor.add(myInterceptor, zIndex: 2, name: "SomeName");
    }
    
    @override
    void dispose() {
       BackButtonInterceptor.removeByName("SomeName");
       super.dispose();
    }
    
    bool myInterceptor(bool stopDefaultButtonEvent, RouteInfo info) {
       print("BACK BUTTON!");
       return true;
    }
  10. Add a back button interceptor function

    master

    You can add interceptor functions to BackButtonInterceptor to perform custom logic when the back button is tapped. If an interceptor returns true, the default back button process (like popping a route) is prevented.

    Interceptors can be added using BackButtonInterceptor.add(). You must manually remove them using remove() or removeByName(name) when the widget is disposed to prevent memory leaks.

    An interceptor function signature can be bool Function(bool stopDefaultButtonEvent, RouteInfo info) or Future<bool> Function(bool stopDefaultButtonEvent, RouteInfo info).

    @override
    void initState() {
       super.initState();
       BackButtonInterceptor.add(myInterceptor);
    }
    
    @override
    void dispose() {
       BackButtonInterceptor.remove(myInterceptor);
       super.dispose();
    }
    
    bool myInterceptor(bool stopDefaultButtonEvent, RouteInfo info) {
       print("BACK BUTTON!"); // Do some stuff.
       return true;
    }