| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'framework.dart'; |
| import 'navigator.dart'; |
| import 'notification_listener.dart'; |
| import 'pop_scope.dart'; |
| |
| /// Enables the handling of system back gestures. |
| /// |
| /// Typically wraps a nested [Navigator] widget and allows it to handle system |
| /// back gestures in the [onPop] callback. |
| /// |
| /// {@tool dartpad} |
| /// This sample demonstrates how to use this widget to properly handle system |
| /// back gestures when using nested [Navigator]s. |
| /// |
| /// ** See code in examples/api/lib/widgets/navigator_pop_handler/navigator_pop_handler.0.dart ** |
| /// {@end-tool} |
| /// |
| /// {@tool dartpad} |
| /// This sample demonstrates how to use this widget to properly handle system |
| /// back gestures with a bottom navigation bar whose tabs each have their own |
| /// nested [Navigator]s. |
| /// |
| /// ** See code in examples/api/lib/widgets/navigator_pop_handler/navigator_pop_handler.1.dart ** |
| /// {@end-tool} |
| /// |
| /// See also: |
| /// |
| /// * [PopScope], which allows toggling the ability of a [Navigator] to |
| /// handle pops. |
| /// * [NavigationNotification], which indicates whether a [Navigator] in a |
| /// subtree can handle pops. |
| class NavigatorPopHandler extends StatefulWidget { |
| /// Creates an instance of [NavigatorPopHandler]. |
| const NavigatorPopHandler({ |
| super.key, |
| this.onPop, |
| this.enabled = true, |
| required this.child, |
| }); |
| |
| /// The widget to place below this in the widget tree. |
| /// |
| /// Typically this is a [Navigator] that will handle the pop when [onPop] is |
| /// called. |
| final Widget child; |
| |
| /// Whether this widget's ability to handle system back gestures is enabled or |
| /// disabled. |
| /// |
| /// When false, there will be no effect on system back gestures. If provided, |
| /// [onPop] will still be called. |
| /// |
| /// This can be used, for example, when the nested [Navigator] is no longer |
| /// active but remains in the widget tree, such as in an inactive tab. |
| /// |
| /// Defaults to true. |
| final bool enabled; |
| |
| /// Called when a handleable pop event happens. |
| /// |
| /// For example, a pop is handleable when a [Navigator] in [child] has |
| /// multiple routes on its stack. It's not handleable when it has only a |
| /// single route, and so [onPop] will not be called. |
| /// |
| /// Typically this is used to pop the [Navigator] in [child]. See the sample |
| /// code on [NavigatorPopHandler] for a full example of this. |
| final VoidCallback? onPop; |
| |
| @override |
| State<NavigatorPopHandler> createState() => _NavigatorPopHandlerState(); |
| } |
| |
| class _NavigatorPopHandlerState extends State<NavigatorPopHandler> { |
| bool _canPop = true; |
| |
| @override |
| Widget build(BuildContext context) { |
| // When the widget subtree indicates it can handle a pop, disable popping |
| // here, so that it can be manually handled in canPop. |
| return PopScope( |
| canPop: !widget.enabled || _canPop, |
| onPopInvoked: (bool didPop) { |
| if (didPop) { |
| return; |
| } |
| widget.onPop?.call(); |
| }, |
| // Listen to changes in the navigation stack in the widget subtree. |
| child: NotificationListener<NavigationNotification>( |
| onNotification: (NavigationNotification notification) { |
| // If this subtree cannot handle pop, then set canPop to true so |
| // that our PopScope will allow the Navigator higher in the tree to |
| // handle the pop instead. |
| final bool nextCanPop = !notification.canHandlePop; |
| if (nextCanPop != _canPop) { |
| setState(() { |
| _canPop = nextCanPop; |
| }); |
| } |
| return false; |
| }, |
| child: widget.child, |
| ), |
| ); |
| } |
| } |