will_pop_scope.dart 3.02 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'framework.dart';
6
import 'navigator.dart';
7 8 9 10 11
import 'routes.dart';

/// Registers a callback to veto attempts by the user to dismiss the enclosing
/// [ModalRoute].
///
12 13 14 15 16 17 18 19 20 21 22 23 24 25
/// {@tool snippet --template=stateful_widget}
///
/// Whenever the back button is pressed, you will get a callback at [onWillPop],
/// which returns a [Future]. If the [Future] returns true, the screen is
/// popped.
///
/// ```dart
/// bool shouldPop = true;
/// @override
/// Widget build(BuildContext context) {
///   return WillPopScope (
///     onWillPop: () async {
///       return shouldPop;
///     },
26
///     child: const Text('WillPopScope sample'),
27 28 29 30 31
///   );
/// }
/// ```
/// {@end-tool}
///
32
/// {@tool dartpad}
33
///
34 35
///
/// ** See code in examples/api/lib/widgets/will_pop_scope/will_pop_scope.1.dart **
36 37
/// {@end-tool}
///
38 39
/// See also:
///
40
///  * [ModalRoute.addScopedWillPopCallback] and [ModalRoute.removeScopedWillPopCallback],
41
///    which this widget uses to register and unregister [onWillPop].
42 43 44
///  * [Form], which provides an `onWillPop` callback that enables the form
///    to veto a `pop` initiated by the app's back button.
///
45 46 47 48 49
class WillPopScope extends StatefulWidget {
  /// Creates a widget that registers a callback to veto attempts by the user to
  /// dismiss the enclosing [ModalRoute].
  ///
  /// The [child] argument must not be null.
50
  const WillPopScope({
51 52 53
    Key? key,
    required this.child,
    required this.onWillPop,
54 55
  }) : assert(child != null),
       super(key: key);
56 57

  /// The widget below this widget in the tree.
58
  ///
59
  /// {@macro flutter.widgets.ProxyWidget.child}
60 61 62 63 64 65
  final Widget child;

  /// Called to veto attempts by the user to dismiss the enclosing [ModalRoute].
  ///
  /// If the callback returns a Future that resolves to false, the enclosing
  /// route will not be popped.
66
  final WillPopCallback? onWillPop;
67 68

  @override
69
  State<WillPopScope> createState() => _WillPopScopeState();
70 71 72
}

class _WillPopScopeState extends State<WillPopScope> {
73
  ModalRoute<dynamic>? _route;
74 75

  @override
76 77
  void didChangeDependencies() {
    super.didChangeDependencies();
78
    if (widget.onWillPop != null)
79
      _route?.removeScopedWillPopCallback(widget.onWillPop!);
80
    _route = ModalRoute.of(context);
81
    if (widget.onWillPop != null)
82
      _route?.addScopedWillPopCallback(widget.onWillPop!);
83 84 85
  }

  @override
86
  void didUpdateWidget(WillPopScope oldWidget) {
87
    super.didUpdateWidget(oldWidget);
88 89
    if (widget.onWillPop != oldWidget.onWillPop && _route != null) {
      if (oldWidget.onWillPop != null)
90
        _route!.removeScopedWillPopCallback(oldWidget.onWillPop!);
91
      if (widget.onWillPop != null)
92
        _route!.addScopedWillPopCallback(widget.onWillPop!);
93 94 95 96 97
    }
  }

  @override
  void dispose() {
98
    if (widget.onWillPop != null)
99
      _route?.removeScopedWillPopCallback(widget.onWillPop!);
100 101 102 103
    super.dispose();
  }

  @override
104
  Widget build(BuildContext context) => widget.child;
105
}