Skip to content

Instantly share code, notes, and snippets.

@Shvet
Last active June 11, 2021 06:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Shvet/b7d63ef7bf73c8ae0356842946c8c93f to your computer and use it in GitHub Desktop.
Save Shvet/b7d63ef7bf73c8ae0356842946c8c93f to your computer and use it in GitHub Desktop.
Flutter Modal bottom sheet that supports full screen height
//Flutter Modal Bottom Sheet
//Modified by Shvet for null safety & tested. Also added Round corners on top.
//Based on https://gist.github.com/GiorgioBertolotti/5fc8fcca67aeda3ed9bbc74c856d06f1
import 'dart:async';
import 'package:flutter/material.dart';
const Duration _kBottomSheetDuration = const Duration(milliseconds: 200);
class _ModalBottomSheetLayout extends SingleChildLayoutDelegate {
_ModalBottomSheetLayout(this.progress, this.bottomInset, this.statusBarHeight);
final double progress;
final double bottomInset;
final double statusBarHeight;
@override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
return new BoxConstraints(
minWidth: constraints.maxWidth,
maxWidth: constraints.maxWidth,
minHeight: 0.0,
maxHeight: constraints.maxHeight - statusBarHeight);
}
@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, required 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,
widget.route.statusBarHeight!,
),
child: new BottomSheet(
animationController: widget.route._animationController,
onClosing: () => Navigator.pop(context),
builder: widget.route.builder!,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(5.0)),
),
),
),
);
},
),
);
}
}
class _ModalBottomSheetRoute<T> extends PopupRoute<T> {
final WidgetBuilder? builder;
final ThemeData? theme;
final bool? resizeToAvoidBottomPadding;
final bool? dismissOnTap;
final double? statusBarHeight;
_ModalBottomSheetRoute({
this.builder,
this.theme,
required this.barrierLabel,
RouteSettings? settings,
this.resizeToAvoidBottomPadding,
this.dismissOnTap,
this.statusBarHeight,
}) : super(settings: settings);
@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) {
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;
}
}
Future<T?> showModalBottomSheetApp<T>({
required BuildContext context,
required WidgetBuilder builder,
bool dismissOnTap: false,
bool resizeToAvoidBottomPadding: true,
double statusBarHeight: 0,
}) {
return Navigator.push(
context,
_ModalBottomSheetRoute<T>(
builder: builder,
theme: Theme.of(context),
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
dismissOnTap: dismissOnTap,
statusBarHeight: statusBarHeight,
));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment