-
-
Save crimsonsuv/b25d5ebd04236f9be2aa66accba19446 to your computer and use it in GitHub Desktop.
| //Flutter Modal Bottom Sheet | |
| //Modified by Suvadeep Das | |
| //Based on https://gist.github.com/andrelsmoraes/9e4af0133bff8960c1feeb0ead7fd749 | |
| import 'dart:async'; | |
| import 'package:flutter/material.dart'; | |
| import 'package:meta/meta.dart'; | |
| const Duration _kBottomSheetDuration = const Duration(milliseconds: 200); | |
| class _ModalBottomSheetLayout extends SingleChildLayoutDelegate { | |
| _ModalBottomSheetLayout(this.progress, this.bottomInset); | |
| final double progress; | |
| final double bottomInset; | |
| @override | |
| BoxConstraints getConstraintsForChild(BoxConstraints constraints) { | |
| return new BoxConstraints( | |
| minWidth: constraints.maxWidth, | |
| maxWidth: constraints.maxWidth, | |
| minHeight: 0.0, | |
| maxHeight: constraints.maxHeight | |
| ); | |
| } | |
| @override | |
| Offset getPositionForChild(Size size, Size childSize) { | |
| return new Offset(0.0, size.height - bottomInset - childSize.height * progress); | |
| } | |
| @override | |
| bool shouldRelayout(_ModalBottomSheetLayout oldDelegate) { | |
| return progress != oldDelegate.progress || bottomInset != oldDelegate.bottomInset; | |
| } | |
| } | |
| class _ModalBottomSheet<T> extends StatefulWidget { | |
| const _ModalBottomSheet({ Key key, this.route }) : super(key: key); | |
| final _ModalBottomSheetRoute<T> route; | |
| @override | |
| _ModalBottomSheetState<T> createState() => new _ModalBottomSheetState<T>(); | |
| } | |
| class _ModalBottomSheetState<T> extends State<_ModalBottomSheet<T>> { | |
| @override | |
| Widget build(BuildContext context) { | |
| return new GestureDetector( | |
| onTap: widget.route.dismissOnTap ? () => Navigator.pop(context) : null, | |
| child: new AnimatedBuilder( | |
| animation: widget.route.animation, | |
| builder: (BuildContext context, Widget child) { | |
| double bottomInset = widget.route.resizeToAvoidBottomPadding | |
| ? MediaQuery.of(context).viewInsets.bottom : 0.0; | |
| return new ClipRect( | |
| child: new CustomSingleChildLayout( | |
| delegate: new _ModalBottomSheetLayout(widget.route.animation.value, bottomInset), | |
| child: new BottomSheet( | |
| animationController: widget.route._animationController, | |
| onClosing: () => Navigator.pop(context), | |
| builder: widget.route.builder | |
| ) | |
| ) | |
| ); | |
| } | |
| ) | |
| ); | |
| } | |
| } | |
| class _ModalBottomSheetRoute<T> extends PopupRoute<T> { | |
| _ModalBottomSheetRoute({ | |
| this.builder, | |
| this.theme, | |
| this.barrierLabel, | |
| RouteSettings settings, | |
| this.resizeToAvoidBottomPadding, | |
| this.dismissOnTap, | |
| }) : super(settings: settings); | |
| final WidgetBuilder builder; | |
| final ThemeData theme; | |
| final bool resizeToAvoidBottomPadding; | |
| final bool dismissOnTap; | |
| @override | |
| Duration get transitionDuration => _kBottomSheetDuration; | |
| @override | |
| bool get barrierDismissible => false; | |
| @override | |
| final String barrierLabel; | |
| @override | |
| Color get barrierColor => Colors.black54; | |
| AnimationController _animationController; | |
| @override | |
| AnimationController createAnimationController() { | |
| assert(_animationController == null); | |
| _animationController = BottomSheet.createAnimationController(navigator.overlay); | |
| return _animationController; | |
| } | |
| @override | |
| Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) { | |
| // By definition, the bottom sheet is aligned to the bottom of the page | |
| // and isn't exposed to the top padding of the MediaQuery. | |
| Widget bottomSheet = new MediaQuery.removePadding( | |
| context: context, | |
| removeTop: true, | |
| child: new _ModalBottomSheet<T>(route: this), | |
| ); | |
| if (theme != null) | |
| bottomSheet = new Theme(data: theme, child: bottomSheet); | |
| return bottomSheet; | |
| } | |
| } | |
| /// Shows a modal material design bottom sheet. | |
| /// | |
| /// A modal bottom sheet is an alternative to a menu or a dialog and prevents | |
| /// the user from interacting with the rest of the app. | |
| /// | |
| /// A closely related widget is a persistent bottom sheet, which shows | |
| /// information that supplements the primary content of the app without | |
| /// preventing the use from interacting with the app. Persistent bottom sheets | |
| /// can be created and displayed with the [showBottomSheet] function or the | |
| /// [ScaffoldState.showBottomSheet] method. | |
| /// | |
| /// The `context` argument is used to look up the [Navigator] and [Theme] for | |
| /// the bottom sheet. It is only used when the method is called. Its | |
| /// corresponding widget can be safely removed from the tree before the bottom | |
| /// sheet is closed. | |
| /// | |
| /// Returns a `Future` that resolves to the value (if any) that was passed to | |
| /// [Navigator.pop] when the modal bottom sheet was closed. | |
| /// | |
| /// See also: | |
| /// | |
| /// * [BottomSheet], which is the widget normally returned by the function | |
| /// passed as the `builder` argument to [showModalBottomSheet]. | |
| /// * [showBottomSheet] and [ScaffoldState.showBottomSheet], for showing | |
| /// non-modal bottom sheets. | |
| /// * <https://material.google.com/components/bottom-sheets.html#bottom-sheets-modal-bottom-sheets> | |
| Future<T> showModalBottomSheetApp<T>({ | |
| @required BuildContext context, | |
| @required WidgetBuilder builder, | |
| bool dismissOnTap: false, | |
| bool resizeToAvoidBottomPadding : true, | |
| }) { | |
| assert(context != null); | |
| assert(builder != null); | |
| return Navigator.push(context, new _ModalBottomSheetRoute<T>( | |
| builder: builder, | |
| theme: Theme.of(context, shadowThemeOnly: true), | |
| barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, | |
| resizeToAvoidBottomPadding: resizeToAvoidBottomPadding, | |
| dismissOnTap: dismissOnTap, | |
| )); | |
| } |
It works great, thanks!
I've just added a few lines to adjust the height to stay below the statusBar...What you have added? Can you please share?
Sure, this is the gist. Hope it works for you too! :)
great work
Hello @crimsonsuv
Could you fix the Input Fields for latest update https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/material/bottom_sheet.dart
Thank you.
great, thank you so much. but it doesn't close the bottom sheet when the grayed area touched. maybe is there any way to bring this feature back?
@idrisAkbarA Hi Idris ;)
found a very simple solution to close the modal when the gray area is touched.
You only need to set barrierDismissible to true.
@override
bool get barrierDismissible => false;
-->
@override
bool get barrierDismissible => true;
Hope it helps ;)
Great work, thanks
Thank you very much, you can also control the height if you put a container with your desired height as a background in the showModalBottomSheetApp ()
i would like to keep this:
@OverRide
bool get barrierDismissible => false;
but, when the barrier is pressed, i want to do an action with the modal content and then Navigator.pop(context);
how can i achieve that? thanks
I put my q here: https://stackoverflow.com/questions/61253718/manually-dismiss-and-handle-data-with-showmodalbottomsheet-when-isdismissible-is
thinking it would get more chance of getting answered.
What you have added? Can you please share?