Skip to content

Instantly share code, notes, and snippets.

@crimsonsuv
Created November 29, 2018 07:02
Show Gist options
  • Star 38 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save crimsonsuv/b25d5ebd04236f9be2aa66accba19446 to your computer and use it in GitHub Desktop.
Save crimsonsuv/b25d5ebd04236f9be2aa66accba19446 to your computer and use it in GitHub Desktop.
Flutter Modal bottom sheet whith input fix and full screen sheet
//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,
));
}
@crimsonsuv
Copy link
Author

Just drag this file to your project and when calling Modal Bottom sheet use it as follows
void _showSignupModalSheet() { showModalBottomSheetApp( context: context, builder: (builder) { return new SignupScreen(); }); }

If you dont want full screen sheet then you can modify
BoxConstraints getConstraintsForChild(BoxConstraints constraints) { return new BoxConstraints( minWidth: constraints.maxWidth, maxWidth: constraints.maxWidth, minHeight: 0.0, maxHeight: constraints.maxHeight ); }

and change maxHeight to default:
maxHeight: constraints.maxHeight * 9.0 / 16.0

### here is the usage sample:
Calling the modal bottom sheet
void _showSignupModalSheet() { showModalBottomSheetApp( context: context, builder: (builder) { return new buildSheetLogin(context); }); }

Declaring bottom sheet
Widget buildSheetLogin(BuildContext context) { return new Container( ....... ); }

@ErraticFox
Copy link

ErraticFox commented Dec 8, 2018

Just drag this file to your project and when calling Modal Bottom sheet use it as follows
void _showSignupModalSheet() { showModalBottomSheetApp( context: context, builder: (builder) { return new SignupScreen(); }); }

Can you tell us where to "just drag this file" and if we need to import it? This helps out us new people quite a bit. :^)

In example if I have this block of code, how do I implement something like that?

void _addFundBottomSheet(context) {
  showModalBottomSheet(
      context: context,
      builder: (BuildContext builder) {
        return Container(
          //bunch of code
          );
      });
}

EDIT: Didn't realize it was showModalBottomSheetApp and not showModalBottomSheet. 😄

@betapcode
Copy link

Great, you know exactly what i need to do !

@QSKOBE24
Copy link

Great Thanks

@ariefannur
Copy link

Great Work as well

@andreagr
Copy link

Great work

@jaceshim
Copy link

jaceshim commented Feb 23, 2019

Great Work!
Works fine! Thank you.

@qxwei
Copy link

qxwei commented Apr 2, 2019

Great work ,thank you

@mmustafasesudia
Copy link

Work like champ..!
Thank you.

@GiorgioBertolotti
Copy link

It works great, thanks!
I've just added a few lines to adjust the height to stay below the statusBar...

@slackvishal
Copy link

Wonderful...Awesome....Marvellous....Saved my time....Good day.....

@rubann7
Copy link

rubann7 commented Jun 15, 2019

Great Work Dude !!

@crimsonsuv
Copy link
Author

Thanks guys, I have many other ready to use things also but I though no one reads or needs it but after seeing the comments I guess it will be good Idea to put those as well..

@pookzzz
Copy link

pookzzz commented Jun 17, 2019

Yea thank you so much for sharing your fix!

@lanrehnics
Copy link

Awesome ... Works like charm.

@sidbait
Copy link

sidbait commented Jun 26, 2019

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?

@GiorgioBertolotti
Copy link

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! :)

@bennibau
Copy link

bennibau commented Jul 5, 2019

great work

@semutnest
Copy link

@idrisAkbarA
Copy link

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?

@muellercornelius
Copy link

@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 ;)

@a0y1a
Copy link

a0y1a commented Dec 19, 2019

Great work, thanks

@ermiry
Copy link

ermiry commented Jan 9, 2020

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 ()

@matthewkooshad
Copy link

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

@matthewkooshad
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment