Line data Source code
1 : import 'package:flutter/material.dart';
2 :
3 : class GetModalBottomSheetRoute<T> extends PopupRoute<T> {
4 1 : GetModalBottomSheetRoute({
5 : this.builder,
6 : this.theme,
7 : this.barrierLabel,
8 : this.backgroundColor,
9 : this.elevation,
10 : this.shape,
11 : this.removeTop = true,
12 : this.clipBehavior,
13 : this.modalBarrierColor,
14 : this.isDismissible = true,
15 : this.enableDrag = true,
16 : @required this.isScrollControlled,
17 : RouteSettings settings,
18 0 : }) : assert(isScrollControlled != null),
19 0 : assert(isDismissible != null),
20 0 : assert(enableDrag != null),
21 1 : super(settings: settings);
22 :
23 : final WidgetBuilder builder;
24 : final ThemeData theme;
25 : final bool isScrollControlled;
26 : final Color backgroundColor;
27 : final double elevation;
28 : final ShapeBorder shape;
29 : final Clip clipBehavior;
30 : final Color modalBarrierColor;
31 : final bool isDismissible;
32 : final bool enableDrag;
33 :
34 : // remove safearea from top
35 : final bool removeTop;
36 :
37 0 : @override
38 0 : Duration get transitionDuration => Duration(milliseconds: 700);
39 :
40 1 : @override
41 1 : bool get barrierDismissible => isDismissible;
42 :
43 : @override
44 : final String barrierLabel;
45 :
46 1 : @override
47 1 : Color get barrierColor => modalBarrierColor ?? Colors.black54;
48 :
49 : AnimationController _animationController;
50 :
51 1 : @override
52 : AnimationController createAnimationController() {
53 1 : assert(_animationController == null);
54 1 : _animationController =
55 3 : BottomSheet.createAnimationController(navigator.overlay);
56 1 : return _animationController;
57 : }
58 :
59 1 : @override
60 : Widget buildPage(BuildContext context, Animation<double> animation,
61 : Animation<double> secondaryAnimation) {
62 : final BottomSheetThemeData sheetTheme =
63 3 : theme?.bottomSheetTheme ?? Theme.of(context).bottomSheetTheme;
64 : // By definition, the bottom sheet is aligned to the bottom of the page
65 : // and isn't exposed to the top padding of the MediaQuery.
66 1 : Widget bottomSheet = MediaQuery.removePadding(
67 : context: context,
68 1 : removeTop: removeTop,
69 1 : child: Padding(
70 : padding:
71 4 : EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
72 1 : child: _GetModalBottomSheet<T>(
73 : route: this,
74 1 : backgroundColor: backgroundColor ??
75 0 : sheetTheme?.modalBackgroundColor ??
76 0 : sheetTheme?.backgroundColor,
77 : elevation:
78 3 : elevation ?? sheetTheme?.modalElevation ?? sheetTheme?.elevation,
79 1 : shape: shape,
80 1 : clipBehavior: clipBehavior,
81 1 : isScrollControlled: isScrollControlled,
82 1 : enableDrag: enableDrag,
83 : ),
84 : ),
85 : );
86 1 : if (theme != null) bottomSheet = Theme(data: theme, child: bottomSheet);
87 : return bottomSheet;
88 : }
89 : }
90 :
91 : class _GetModalBottomSheet<T> extends StatefulWidget {
92 1 : const _GetModalBottomSheet({
93 : Key key,
94 : this.route,
95 : this.backgroundColor,
96 : this.elevation,
97 : this.shape,
98 : this.clipBehavior,
99 : this.isScrollControlled = false,
100 : this.enableDrag = true,
101 0 : }) : assert(isScrollControlled != null),
102 0 : assert(enableDrag != null),
103 1 : super(key: key);
104 :
105 : final GetModalBottomSheetRoute<T> route;
106 : final bool isScrollControlled;
107 : final Color backgroundColor;
108 : final double elevation;
109 : final ShapeBorder shape;
110 : final Clip clipBehavior;
111 : final bool enableDrag;
112 :
113 1 : @override
114 1 : _GetModalBottomSheetState<T> createState() => _GetModalBottomSheetState<T>();
115 : }
116 :
117 : class _GetModalBottomSheetState<T> extends State<_GetModalBottomSheet<T>> {
118 1 : String _getRouteLabel(MaterialLocalizations localizations) {
119 4 : if ((Theme.of(context).platform == TargetPlatform.android) ||
120 0 : (Theme.of(context).platform == TargetPlatform.fuchsia)) {
121 1 : return localizations.dialogLabel;
122 : } else {
123 : return '';
124 : }
125 : }
126 :
127 1 : @override
128 : Widget build(BuildContext context) {
129 1 : assert(debugCheckHasMediaQuery(context));
130 1 : assert(debugCheckHasMaterialLocalizations(context));
131 1 : final MediaQueryData mediaQuery = MediaQuery.of(context);
132 : final MaterialLocalizations localizations =
133 1 : MaterialLocalizations.of(context);
134 1 : final String routeLabel = _getRouteLabel(localizations);
135 :
136 1 : return AnimatedBuilder(
137 3 : animation: widget.route.animation,
138 1 : builder: (BuildContext context, Widget child) {
139 : // Disable the initial animation when accessible navigation is on so
140 : // that the semantics are added to the tree at the correct time.
141 1 : final double animationValue = mediaQuery.accessibleNavigation
142 : ? 1.0
143 4 : : widget.route.animation.value;
144 1 : return Semantics(
145 : scopesRoute: true,
146 : namesRoute: true,
147 : label: routeLabel,
148 : explicitChildNodes: true,
149 1 : child: ClipRect(
150 1 : child: CustomSingleChildLayout(
151 1 : delegate: _GetModalBottomSheetLayout(
152 2 : animationValue, widget.isScrollControlled),
153 1 : child: BottomSheet(
154 3 : animationController: widget.route._animationController,
155 0 : onClosing: () {
156 0 : if (widget.route.isCurrent) {
157 0 : Navigator.pop(context);
158 : }
159 : },
160 3 : builder: widget.route.builder,
161 2 : backgroundColor: widget.backgroundColor,
162 2 : elevation: widget.elevation,
163 2 : shape: widget.shape,
164 2 : clipBehavior: widget.clipBehavior,
165 2 : enableDrag: widget.enableDrag,
166 : ),
167 : ),
168 : ),
169 : );
170 : },
171 : );
172 : }
173 : }
174 :
175 : class _GetModalBottomSheetLayout extends SingleChildLayoutDelegate {
176 1 : _GetModalBottomSheetLayout(this.progress, this.isScrollControlled);
177 :
178 : final double progress;
179 : final bool isScrollControlled;
180 :
181 1 : @override
182 : BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
183 1 : return BoxConstraints(
184 1 : minWidth: constraints.maxWidth,
185 1 : maxWidth: constraints.maxWidth,
186 : minHeight: 0.0,
187 1 : maxHeight: isScrollControlled
188 0 : ? constraints.maxHeight
189 3 : : constraints.maxHeight * 9.0 / 16.0,
190 : );
191 : }
192 :
193 1 : @override
194 : Offset getPositionForChild(Size size, Size childSize) {
195 6 : return Offset(0.0, size.height - childSize.height * progress);
196 : }
197 :
198 1 : @override
199 : bool shouldRelayout(_GetModalBottomSheetLayout oldDelegate) {
200 3 : return progress != oldDelegate.progress;
201 : }
202 : }
|