-
Notifications
You must be signed in to change notification settings - Fork 2.6k
/
modal.dart
222 lines (202 loc) · 7.88 KB
/
modal.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
// Copyright 2019 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 'package:flutter/material.dart';
/// Signature for a function that creates a widget that builds a
/// transition.
///
/// Used by [PopupRoute].
typedef _ModalTransitionBuilder = Widget Function(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
);
/// Displays a modal above the current contents of the app.
///
/// Content below the modal is dimmed with a [ModalBarrier].
///
/// The `context` argument is used to look up the [Navigator] for the
/// modal. It is only used when the method is called. Its corresponding widget
/// can be safely removed from the tree before the modal is closed.
///
/// The `configuration` argument is used to determine characteristics of the
/// modal route that will be displayed, such as the enter and exit
/// transitions, the duration of the transitions, and modal barrier
/// properties.
///
/// The `useRootNavigator` argument is used to determine whether to push the
/// modal to the [Navigator] furthest from or nearest to the given `context`.
/// By default, `useRootNavigator` is `true` and the modal route created by
/// this method is pushed to the root navigator. If the application has
/// multiple [Navigator] objects, it may be necessary to call
/// `Navigator.of(context, rootNavigator: true).pop(result)` to close the
/// modal rather than just `Navigator.pop(context, result)`.
///
/// Returns a [Future] that resolves to the value (if any) that was passed to
/// [Navigator.pop] when the modal was closed.
///
/// See also:
///
/// * [ModalConfiguration], which is the configuration object used to define
/// the modal's characteristics.
Future<T> showModal<T>({
@required BuildContext context,
@required ModalConfiguration configuration,
bool useRootNavigator = true,
WidgetBuilder builder,
}) {
assert(configuration != null);
assert(useRootNavigator != null);
String barrierLabel = configuration.barrierLabel;
// Avoid looking up [MaterialLocalizations.of(context).modalBarrierDismissLabel]
// if there is no dismissible barrier.
if (configuration.barrierDismissible && configuration.barrierLabel == null) {
barrierLabel = MaterialLocalizations.of(context).modalBarrierDismissLabel;
}
assert(!configuration.barrierDismissible || barrierLabel != null);
return Navigator.of(context, rootNavigator: useRootNavigator).push<T>(
_ModalRoute<T>(
barrierColor: configuration.barrierColor,
barrierDismissible: configuration.barrierDismissible,
barrierLabel: barrierLabel,
transitionBuilder: configuration.transitionBuilder,
transitionDuration: configuration.transitionDuration,
reverseTransitionDuration: configuration.reverseTransitionDuration,
builder: builder,
),
);
}
// A modal route that overlays a widget on the current route.
class _ModalRoute<T> extends PopupRoute<T> {
/// Creates a route with general modal route.
///
/// [barrierDismissible] configures whether or not tapping the modal's
/// scrim dismisses the modal. [barrierLabel] sets the semantic label for
/// a dismissible barrier. [barrierDismissible] cannot be null. If
/// [barrierDismissible] is true, the [barrierLabel] cannot be null.
///
/// [transitionBuilder] takes in a function that creates a widget. This
/// widget is typically used to configure the modal's transition.
_ModalRoute({
this.barrierColor,
this.barrierDismissible = true,
this.barrierLabel,
this.transitionDuration,
this.reverseTransitionDuration,
_ModalTransitionBuilder transitionBuilder,
@required this.builder,
}) : assert(barrierDismissible != null),
assert(!barrierDismissible || barrierLabel != null),
_transitionBuilder = transitionBuilder;
@override
final Color barrierColor;
@override
final bool barrierDismissible;
@override
final String barrierLabel;
@override
final Duration transitionDuration;
// TODO(shihaohong): Remove the override analyzer ignore once
// Flutter stable contains https://github.com/flutter/flutter/pull/48274.
@override
// ignore: override_on_non_overriding_member
final Duration reverseTransitionDuration;
/// The primary contents of the modal.
final WidgetBuilder builder;
final _ModalTransitionBuilder _transitionBuilder;
@override
Widget buildPage(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
final ThemeData theme = Theme.of(context);
return Semantics(
child: SafeArea(
child: Builder(
builder: (BuildContext context) {
final Widget child = Builder(builder: builder);
return theme != null ? Theme(data: theme, child: child) : child;
},
),
),
scopesRoute: true,
explicitChildNodes: true,
);
}
@override
Widget buildTransitions(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return _transitionBuilder(
context,
animation,
secondaryAnimation,
child,
);
}
}
/// A configuration object containing the properties needed to implement a
/// modal route.
///
/// The `barrierDismissible` argument is used to determine whether this route
/// can be dismissed by tapping the modal barrier. This argument defaults
/// to true. If `barrierDismissible` is true, a non-null `barrierLabel` must be
/// provided.
///
/// The `barrierLabel` argument is the semantic label used for a dismissible
/// barrier. This argument defaults to "Dismiss".
abstract class ModalConfiguration {
/// Creates a modal configuration object that provides the necessary
/// properties to implement a modal route.
///
/// [barrierDismissible] configures whether or not tapping the modal's
/// scrim dismisses the modal. [barrierLabel] sets the semantic label for
/// a dismissible barrier. [barrierDismissible] cannot be null. If
/// [barrierDismissible] is true, the [barrierLabel] cannot be null.
///
/// [transitionDuration] and [reverseTransitionDuration] determine the
/// duration of the transitions when the modal enters and exits the
/// application. [transitionDuration] and [reverseTransitionDuration]
/// cannot be null.
ModalConfiguration({
@required this.barrierColor,
@required this.barrierDismissible,
this.barrierLabel,
@required this.transitionDuration,
@required this.reverseTransitionDuration,
}) : assert(barrierColor != null),
assert(barrierDismissible != null),
assert(!barrierDismissible || barrierLabel != null),
assert(transitionDuration != null),
assert(reverseTransitionDuration != null);
/// The color to use for the modal barrier. If this is null, the barrier will
/// be transparent.
final Color barrierColor;
/// Whether you can dismiss this route by tapping the modal barrier.
final bool barrierDismissible;
/// The semantic label used for a dismissible barrier.
final String barrierLabel;
/// The duration of the transition running forwards.
final Duration transitionDuration;
/// The duration of the transition running in reverse.
final Duration reverseTransitionDuration;
/// A builder that defines how the route arrives on and leaves the screen.
///
/// The [buildTransitions] method is typically used to define transitions
/// that animate the new topmost route's comings and goings. When the
/// [Navigator] pushes a route on the top of its stack, the new route's
/// primary [animation] runs from 0.0 to 1.0. When the [Navigator] pops the
/// topmost route, e.g. because the use pressed the back button, the
/// primary animation runs from 1.0 to 0.0.
Widget transitionBuilder(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
);
}