Line data Source code
1 : import 'package:flutter/material.dart';
2 : import 'package:nav/dialog/mutable_value.dart';
3 : import 'package:nav/nav.dart';
4 : import 'package:nav/route/clipper_circle.dart';
5 :
6 : export 'package:nav/dialog/dialog_state.dart';
7 :
8 : abstract class DialogWidget<ResultType> extends StatefulWidget {
9 2 : DialogWidget({
10 : super.key,
11 : this.animation = NavAni.Fade,
12 : this.barrierColor = Colors.black54,
13 : this.barrierDismissible = true,
14 : this.barrierLabel,
15 : bool? useRootNavigator,
16 : bool? useSafeArea,
17 : this.anchorPoint,
18 : this.routeSettings,
19 : this.context,
20 : }) : useRootNavigator =
21 1 : useRootNavigator ?? Nav.navSetting?.useRootNavigator ?? true,
22 1 : useSafeArea = useRootNavigator ?? Nav.navSetting?.useSafeArea ?? false;
23 :
24 : final BuildContext? context;
25 : final NavAni animation;
26 : final bool barrierDismissible;
27 : final Color barrierColor;
28 : final String? barrierLabel;
29 : final Offset? anchorPoint;
30 : final RouteSettings? routeSettings;
31 : final bool useSafeArea;
32 : final bool useRootNavigator;
33 :
34 : final MutableValue<BuildContext?> _builderContext =
35 : MutableValue(null); //context when dialog is actually use on navigator
36 : final MutableValue<bool> isShown = MutableValue(
37 : false); //use final reference wrapper to ingnore must_be_immutable lint
38 :
39 2 : void onHide() {
40 4 : isShown.value = false;
41 : }
42 :
43 2 : Future<ResultType?> show({bool? useRootNavigator}) async {
44 4 : final context = this.context ?? Nav.globalContext;
45 4 : if (context is StatefulElement && !context.mounted) {
46 : return null;
47 : }
48 :
49 4 : isShown.value = true;
50 2 : switch (animation) {
51 2 : case NavAni.Left:
52 2 : case NavAni.Right:
53 2 : case NavAni.Top:
54 2 : case NavAni.Bottom:
55 2 : return _showDialogWith<ResultType>(
56 2 : animation,
57 2 : barrierDismissible: barrierDismissible,
58 2 : barrierColor: barrierColor,
59 2 : barrierLabel: barrierLabel,
60 2 : useRootNavigator: useRootNavigator ?? this.useRootNavigator,
61 2 : routeSettings: routeSettings,
62 2 : anchorPoint: anchorPoint,
63 : context: context,
64 2 : builder: (context) {
65 4 : _builderContext.value = context;
66 : return this;
67 : },
68 : );
69 1 : case NavAni.Blink:
70 1 : return _showDialogWith<ResultType>(
71 1 : animation,
72 1 : barrierDismissible: barrierDismissible,
73 1 : barrierColor: barrierColor,
74 1 : barrierLabel: barrierLabel,
75 1 : useRootNavigator: useRootNavigator ?? this.useRootNavigator,
76 1 : routeSettings: routeSettings,
77 1 : anchorPoint: anchorPoint,
78 : context: context,
79 1 : builder: (context) {
80 2 : _builderContext.value = context;
81 : return this;
82 : },
83 : durationMs: 0,
84 : );
85 1 : case NavAni.Ripple:
86 1 : return _showDialogWith<ResultType>(
87 1 : animation,
88 1 : barrierDismissible: barrierDismissible,
89 1 : barrierColor: barrierColor,
90 1 : barrierLabel: barrierLabel,
91 1 : useRootNavigator: useRootNavigator ?? this.useRootNavigator,
92 1 : routeSettings: routeSettings,
93 1 : anchorPoint: anchorPoint,
94 : context: context,
95 1 : builder: (context) {
96 2 : _builderContext.value = context;
97 : return this;
98 : },
99 : );
100 : case NavAni.Fade:
101 : default:
102 1 : return showDialog<ResultType>(
103 : context: context,
104 1 : barrierDismissible: barrierDismissible,
105 1 : barrierColor: barrierColor,
106 1 : barrierLabel: barrierLabel,
107 1 : useSafeArea: useSafeArea,
108 1 : useRootNavigator: useRootNavigator ?? this.useRootNavigator,
109 1 : routeSettings: routeSettings,
110 1 : anchorPoint: anchorPoint,
111 1 : builder: (context) {
112 2 : _builderContext.value = context;
113 : return this;
114 : },
115 : );
116 : }
117 : }
118 :
119 1 : void hide([ResultType? result]) {
120 2 : if (!isShown.value) {
121 : return;
122 : }
123 2 : final context = _builderContext.value ?? this.context ?? Nav.globalContext;
124 1 : Nav.pop<ResultType>(context, result: result);
125 : }
126 : }
127 :
128 : ///build fade animation transition
129 1 : Widget _buildFromFadeTransition(
130 : BuildContext context,
131 : Animation<double> animation,
132 : Animation<double> secondaryAnimation,
133 : Widget child) {
134 1 : return FadeTransition(
135 2 : opacity: Tween<double>(begin: 0.0, end: 1.0).animate(animation),
136 : child: child,
137 : );
138 : }
139 :
140 : ///build moving from top to bottom animation transition
141 1 : Widget _buildFromTopTransition(
142 : BuildContext context,
143 : Animation<double> animation,
144 : Animation<double> secondaryAnimation,
145 : Widget child) {
146 1 : return SlideTransition(
147 1 : position: Tween<Offset>(
148 : begin: const Offset(0.0, -1.0),
149 : end: Offset.zero,
150 3 : ).chain(CurveTween(curve: const Cubic(0.4, 0, 0.2, 1))).animate(animation),
151 : child: child,
152 : );
153 : }
154 :
155 : ///build moving from bottom to top animation transition
156 2 : Widget _buildFromBottomTransition(
157 : BuildContext context,
158 : Animation<double> animation,
159 : Animation<double> secondaryAnimation,
160 : Widget child) {
161 2 : return SlideTransition(
162 2 : position: Tween<Offset>(
163 : begin: const Offset(0.0, 1.0),
164 : end: Offset.zero,
165 6 : ).chain(CurveTween(curve: const Cubic(0.4, 0, 0.2, 1))).animate(animation),
166 : child: child,
167 : );
168 : }
169 :
170 : ///build moving from right to left animation transition
171 1 : Widget _buildFromRightTransition(
172 : BuildContext context,
173 : Animation<double> animation,
174 : Animation<double> secondaryAnimation,
175 : Widget child) {
176 1 : return SlideTransition(
177 1 : position: Tween<Offset>(
178 : begin: const Offset(1.0, 0.0),
179 : end: Offset.zero,
180 3 : ).chain(CurveTween(curve: const Cubic(0.4, 0, 0.2, 1))).animate(animation),
181 : child: child,
182 : );
183 : }
184 :
185 : ///build moving from left to right animation transition
186 1 : Widget _buildFromLeftTransition(
187 : BuildContext context,
188 : Animation<double> animation,
189 : Animation<double> secondaryAnimation,
190 : Widget child) {
191 1 : return SlideTransition(
192 1 : position: Tween<Offset>(
193 : begin: const Offset(-1.0, 0.0),
194 : end: Offset.zero,
195 3 : ).chain(CurveTween(curve: const Cubic(0.4, 0, 0.2, 1))).animate(animation),
196 : child: child,
197 : );
198 : }
199 :
200 : ///build ripple animation transition from right bottom
201 1 : Widget _buildRippleTransition(BuildContext context, Animation<double> animation,
202 : Animation<double> secondaryAnimation, Widget child) {
203 3 : final height = MediaQuery.of(context).size.height;
204 3 : final width = MediaQuery.of(context).size.width;
205 :
206 1 : return ClipPath(
207 1 : clipper: CircularRevealClipper(
208 1 : fraction: animation.value,
209 : centerAlignment: Alignment.bottomRight,
210 : centerOffset: const Offset(10, 10),
211 2 : minRadius: height + width / 2,
212 : maxRadius: 10,
213 : ),
214 : child: child,
215 : );
216 : }
217 :
218 2 : Future<T?> _showDialogWith<T>(
219 : NavAni ani, {
220 : required BuildContext context,
221 : bool barrierDismissible = true,
222 : @Deprecated(
223 : 'Instead of using the "child" argument, return the child from a closure '
224 : 'provided to the "builder" argument. This will ensure that the BuildContext '
225 : 'is appropriate for widgets built in the dialog. '
226 : 'This feature was deprecated after v0.2.3.')
227 : Widget? child,
228 : WidgetBuilder? builder,
229 : String? barrierLabel,
230 : RouteSettings? routeSettings,
231 : Offset? anchorPoint,
232 : Color? barrierColor,
233 : bool useRootNavigator = true,
234 : int durationMs = 500,
235 : }) {
236 2 : assert(child == null || builder == null);
237 2 : assert(debugCheckHasMaterialLocalizations(context));
238 :
239 2 : final ThemeData theme = Theme.of(context);
240 2 : return showGeneralDialog(
241 : context: context,
242 2 : pageBuilder: (BuildContext buildContext, Animation<double> animation,
243 : Animation<double> secondaryAnimation) {
244 2 : final Widget pageChild = child ?? Builder(builder: builder!);
245 4 : return Builder(builder: (BuildContext context) {
246 2 : return Theme(data: theme, child: pageChild);
247 : });
248 : },
249 : barrierDismissible: barrierDismissible,
250 : barrierLabel: barrierLabel ??
251 4 : MaterialLocalizations.of(context).modalBarrierDismissLabel,
252 : useRootNavigator: useRootNavigator,
253 : routeSettings: routeSettings,
254 : anchorPoint: anchorPoint,
255 : barrierColor: barrierColor ?? Colors.black54,
256 2 : transitionDuration: Duration(milliseconds: durationMs),
257 2 : transitionBuilder: _getTransition(ani),
258 : );
259 : }
260 :
261 2 : Widget Function(
262 : BuildContext context,
263 : Animation<double> animation,
264 : Animation<double> secondaryAnimation,
265 : Widget child) _getTransition(NavAni ani) {
266 : switch (ani) {
267 2 : case NavAni.Left:
268 : return _buildFromLeftTransition;
269 2 : case NavAni.Right:
270 : return _buildFromRightTransition;
271 2 : case NavAni.Top:
272 : return _buildFromTopTransition;
273 2 : case NavAni.Bottom:
274 : return _buildFromBottomTransition;
275 1 : case NavAni.Ripple:
276 : return _buildRippleTransition;
277 : case NavAni.Blink:
278 : case NavAni.Fade:
279 : default:
280 : return _buildFromFadeTransition;
281 : }
282 : }
|