will_pop_scope.dart 2.68 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
/// {@tool dartpad}
13 14 15 16
/// 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.
///
17
/// ** See code in examples/api/lib/widgets/will_pop_scope/will_pop_scope.0.dart **
18 19
/// {@end-tool}
///
20 21
/// See also:
///
22
///  * [ModalRoute.addScopedWillPopCallback] and [ModalRoute.removeScopedWillPopCallback],
23
///    which this widget uses to register and unregister [onWillPop].
24 25 26
///  * [Form], which provides an `onWillPop` callback that enables the form
///    to veto a `pop` initiated by the app's back button.
///
27 28 29 30 31
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.
32
  const WillPopScope({
33
    super.key,
34 35
    required this.child,
    required this.onWillPop,
36
  });
37 38

  /// The widget below this widget in the tree.
39
  ///
40
  /// {@macro flutter.widgets.ProxyWidget.child}
41 42 43 44 45 46
  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.
47
  final WillPopCallback? onWillPop;
48 49

  @override
50
  State<WillPopScope> createState() => _WillPopScopeState();
51 52 53
}

class _WillPopScopeState extends State<WillPopScope> {
54
  ModalRoute<dynamic>? _route;
55 56

  @override
57 58
  void didChangeDependencies() {
    super.didChangeDependencies();
59
    if (widget.onWillPop != null) {
60
      _route?.removeScopedWillPopCallback(widget.onWillPop!);
61
    }
62
    _route = ModalRoute.of(context);
63
    if (widget.onWillPop != null) {
64
      _route?.addScopedWillPopCallback(widget.onWillPop!);
65
    }
66 67 68
  }

  @override
69
  void didUpdateWidget(WillPopScope oldWidget) {
70
    super.didUpdateWidget(oldWidget);
71
    if (widget.onWillPop != oldWidget.onWillPop && _route != null) {
72
      if (oldWidget.onWillPop != null) {
73
        _route!.removeScopedWillPopCallback(oldWidget.onWillPop!);
74 75
      }
      if (widget.onWillPop != null) {
76
        _route!.addScopedWillPopCallback(widget.onWillPop!);
77
      }
78 79 80 81 82
    }
  }

  @override
  void dispose() {
83
    if (widget.onWillPop != null) {
84
      _route?.removeScopedWillPopCallback(widget.onWillPop!);
85
    }
86 87 88 89
    super.dispose();
  }

  @override
90
  Widget build(BuildContext context) => widget.child;
91
}