modal_bottom_sheet 0.1.6+1

  • Readme
  • Changelog
  • Example
  • Installing
  • 97

Flutter Modal Bottom Sheet #

Create awesome and powerful modal bottom sheets.

Cupertino ModalMultiple ModalsMaterial ModalBar ModalCreate your own

Try it #

Explore the Web Demo or clone the repository.

Known problems on web demo:

  • Web demo can run very slow on mobile devides.

  • Fake status bar doesn't change color as the iOS, Android app

First Steps #

How to install it? Follow Instructions

Material Modal BottomSheet #

showMaterialModalBottomSheet(
  context: context,
  builder: (context, scrollController) => Container(),
)

What to use this over flutter showModalBottomSheet?

showMaterialModalBottomSheet supports closing bottoms sheets by dragging down even if there is a scrollview inside. showModalBottomSheet won't work correctly with scrollviews. Also it supports WillPopScope to prevent closing the dialog

Generic params for all modal bottom sheets

ParamDescription
bool expand = falseThe expand parameter specifies id the modal bottom sheet will be full screen size or will fit the content child
bool useRootNavigator = falseThe useRootNavigator parameter ensures that the root navigator is used to display the bottom sheet when set to true. This is useful in the case that a modal bottom sheet needs to be displayed above all other content but the caller is inside another Navigator.
bool isDismissible = trueThe isDismissible parameter specifies whether the bottom sheet will be dismissed when user taps on the scrim.
Color barrierColorThe barrierColor parameter controls the color of the scrim for this route
bool enableDrag = trueThe enableDrag parameter specifies whether the bottom sheet can be dragged up and down and dismissed by swiping downwards.
AnimationController secondAnimationThe secondAnimation parameter allows you to provide an animation controller that will be used to animate push/pop of the modal route. Using this param is advised against and will be probably removed in future versions
bool bounce = falseThe bounce parameter specifies if the bottom sheet can go beyond the top boundary while dragging
Duration duration = const Duration(milliseconds: 400)The duration of modal opening

Material params

The optional backgroundColor, elevation, shape, and clipBehavior parameters can be passed in to customize the appearance and behavior of material bottom sheets.

Cupertino Modal BottomSheet #

iOS 13 came with an amazing new modal navigation and now it is available to use with Flutter.

showCupertinoModalBottomSheet(
  context: context,
  builder: (context, scrollController) => Container(),
)

See generic paramameter in the Material section above

Cupertino specific params

The optional backgroundColor parameters can be passed in to customize the backgroundColor cupertino bottom sheets. Useful if you want a blurred transparent background as the example Cupertino Photo Share

CAUTION!: To animate the previous route some changes are needed. #

Why? MaterialPageRoute and CupertinoPageRoute do not allow animated translation to/from routes that are not the same type.

There are two options:

Replace your current route class with MaterialWithModalsPageRoute.

Notice this route type behaves the same as MaterialPageRoute and supports custom PageTransitionsBuilder and PageTransitionsTheme.

How can I change my route class? See cases:

1.

Using Navigator.of(context).push

Navigator.of(context).push(MaterialPageRoute(builder: (context) => Container()));`

Replace it with

 Navigator.of(context).push(MaterialWithModalsPageRoute(builder: (context) => Container()));
2.

Using onGenerateRoute parameter of MaterialApp, CupertinoApp or Navigator

 onGenerateRoute: (settings) {
    ...
     return MaterialPageRoute(settings: settings, builder: (context) => Container());
 },

Replace it to

 onGenerateRoute: (settings) {
    ...
     return MaterialWithModalsPageRoute(settings: settings, builder: (context) => Container());
 },
3.

Using pageRouteBuilder parameter of WidgetApp

pageRouteBuilder: <T>(RouteSettings settings, WidgetBuilder builder) => MaterialWithModalsPageRoute<T>(settings: settings, builder: builder)
4.

Using routes parameter from MaterialApp or CupertinoApp

Unfortunately this parameter uses MaterialPageRoute and CupertinoPageRoute respectively and cannot be changed. You can modify the way you call the previous route with one of the previous methods or try option 2

OPTION 2. #

  1. Wrap previous route inside a CupertinoScaffold. Example with routes parameter from MaterialApp or CupertinoApp
    routes: <String, WidgetBuilder>{
      '/previous_route_where_you_push_modal': (BuildContext context) => CupertinoScaffold(body: Container()),
     },
  1. Push modal with this method
 CupertinoScaffold.showCupertinoModalBottomSheet(context:context, builder: (context) => Container())

These two options won't work correctly together.

It supports native features as bouncing, blurred background, dark mode, stacking modals and inside navigation.

Push new views inside the modal bottom sheet #

a. If you want to push a new modal bottom sheet just call showCupertinoModalBottomSheet again (works with two option)

b. For inside navigaton add a new Navigator or CupertinoTabScaffold inside

c. Also it supports flutter features as WillPopScope to prevent the modal bottom to be closed.

Build other BottomSheets #

Try showBarModalBottomSheet for a bottomSheet with the appearance used by Facebook or Slack

Check in the example project showAvatarModalBottomSheet for how to create your own ModalBottomSheet

Questions #

Ask a question and ping me @jamesblasco

Found an issue or have a proposal? #

Create an issue

Roadmap #

  • [X] Support closing by dragging fast on a modal with a scroll view.

  • [ ] Improve animation curves when user is not dragging.

  • [ ] Allow to set the initial size of the bottom sheet

  • [ ] Support hero animations Pull Request #2

[0.0.1] - Pre Release. #

[0.1.0] - Package Release. #

[0.1.4] - Clean code and fix small bugs #

[0.1.5] - Scroll improvements and bug fixes #

  • Support for closing a modal with a scroll view by dragging down fast.
  • Fix assertion in CupertinoBottomSheet and BottomSheetRoute when using the CupetinoApp or WidgetsApp as root
  • Fix assertion when scrollController isn't used by the builder

[0.1.6] - New custom params #

  • Use duration to define the opening duration of the modal
  • Change the top radius of the cupertino bottom sheet Thanks to @bierbaumtim @troyanskiy @rodineijf for the contributions

example/lib/main.dart

import 'package:example/modals/circular_modal.dart';
import 'package:example/web_frame.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';

import 'modals/floating_modal.dart';
import 'modals/modal_complex_all.dart';
import 'modals/modal_fit.dart';
import 'modals/modal_inside_modal.dart';
import 'modals/modal_will_scope.dart';
import 'modals/modal_with_navigator.dart';
import 'modals/modal_with_scroll.dart';

import 'examples/cupertino_share.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(platform: TargetPlatform.iOS),
      title: 'BottomSheet Modals',
      builder: (context, child) => WebFrame(
        child: child,
      ),
      onGenerateRoute: (RouteSettings settings) {
        switch (settings.name) {
          case '/':
            return MaterialWithModalsPageRoute(
                builder: (_) => MyHomePage(title: 'Flutter Demo Home Page'),
                settings: settings);
        }
        return MaterialPageRoute(
            builder: (context) => Scaffold(
                  body: CupertinoScaffold(
                    body: Builder(
                      builder: (context) => CupertinoPageScaffold(
                        backgroundColor: Colors.white,
                        navigationBar: CupertinoNavigationBar(
                          transitionBetweenRoutes: false,
                          middle: Text('Normal Navigation Presentation'),
                          trailing: GestureDetector(
                              child: Icon(Icons.arrow_upward),
                              onTap: () => CupertinoScaffold
                                      .showCupertinoModalBottomSheet(
                                    expand: true,
                                    context: context,
                                    backgroundColor: Colors.transparent,
                                    builder: (context, scrollController) =>
                                        Stack(
                                      children: <Widget>[
                                        ModalWithScroll(
                                            scrollController: scrollController),
                                        Positioned(
                                          height: 40,
                                          left: 40,
                                          right: 40,
                                          bottom: 20,
                                          child: MaterialButton(
                                            onPressed: () => Navigator.of(
                                                    context)
                                                .popUntil((route) =>
                                                    route.settings.name == '/'),
                                            child: Text('Pop back home'),
                                          ),
                                        )
                                      ],
                                    ),
                                  )),
                        ),
                        child: Center(child: Container()),
                      ),
                    ),
                  ),
                ),
            settings: settings);
      },
      debugShowCheckedModeBanner: false,
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    print(MediaQuery.of(context).size.height);
    return Material(
      child: CupertinoPageScaffold(
        backgroundColor: Colors.white,
        navigationBar: CupertinoNavigationBar(
          transitionBetweenRoutes: false,
          middle: Text('iOS13 Modal Presentation'),
          trailing: GestureDetector(
            child: Icon(Icons.arrow_forward),
            onTap: () => Navigator.of(context).pushNamed('ss'),
          ),
        ),
        child: SizedBox.expand(
          child: SingleChildScrollView(
            child: SafeArea(
              bottom: false,
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: <Widget>[
                  ListTile(
                      title: Text('Cupertino Photo Share Example'),
                      onTap: () => Navigator.of(context).push(
                          MaterialWithModalsPageRoute(
                              builder: (context) => CupertinoSharePage()))),
                  ListTile(
                      title: Text('Material fit'),
                      onTap: () => showMaterialModalBottomSheet(
                            expand: false,
                            context: context,
                            backgroundColor: Colors.transparent,
                            builder: (context, scrollController) =>
                                ModalFit(scrollController: scrollController),
                          )),
                  ListTile(
                      title: Text('Bar Modal'),
                      onTap: () => showBarModalBottomSheet(
                            expand: true,
                            context: context,
                            backgroundColor: Colors.transparent,
                            builder: (context, scrollController) =>
                                ModalInsideModal(
                                    scrollController: scrollController),
                          )),
                  ListTile(
                      title: Text('Avatar Modal'),
                      onTap: () => showAvatarModalBottomSheet(
                            expand: true,
                            context: context,
                            backgroundColor: Colors.transparent,
                            builder: (context, scrollController) =>
                                ModalInsideModal(
                                    scrollController: scrollController),
                          )),
                  ListTile(
                      title: Text('Float Modal'),
                      onTap: () => showFloatingModalBottomSheet(
                            context: context,
                            builder: (context, scrollController) =>
                                ModalFit(scrollController: scrollController),
                          )),
                  ListTile(
                      title: Text('Cupertino Modal fit'),
                      onTap: () => showCupertinoModalBottomSheet(
                            expand: false,
                            context: context,
                            backgroundColor: Colors.transparent,
                            builder: (context, scrollController) =>
                                ModalFit(scrollController: scrollController),
                          )),
                  ListTile(
                      title: Text('Cupertino Small Modal forced to expand'),
                      onTap: () => showCupertinoModalBottomSheet(
                            expand: true,
                            context: context,
                            backgroundColor: Colors.transparent,
                            builder: (context, scrollController) =>
                                ModalFit(scrollController: scrollController),
                          )),
                  ListTile(
                      title: Text('Cupertino Modal inside modal'),
                      onTap: () => showCupertinoModalBottomSheet(
                            expand: true,
                            context: context,
                            backgroundColor: Colors.transparent,
                            builder: (context, scrollController) =>
                                ModalInsideModal(
                                    scrollController: scrollController),
                          )),
                  ListTile(
                      title: Text('Cupertino Modal with inside navigation'),
                      onTap: () => showCupertinoModalBottomSheet(
                            expand: true,
                            context: context,
                            backgroundColor: Colors.transparent,
                            builder: (context, scrollController) =>
                                ModalWithNavigator(
                                    scrollController: scrollController),
                          )),
                  ListTile(
                      title:
                          Text('Cupertino Navigator + Scroll + WillPopScope'),
                      onTap: () => showCupertinoModalBottomSheet(
                            expand: true,
                            context: context,
                            backgroundColor: Colors.transparent,
                            builder: (context, scrollController) =>
                                ComplexModal(
                                    scrollController: scrollController),
                          )),
                  ListTile(
                      title: Text('Cupertino Modal with WillPopScope'),
                      onTap: () => showCupertinoModalBottomSheet(
                            expand: true,
                            context: context,
                            backgroundColor: Colors.transparent,
                            builder: (context, scrollController) =>
                                ModalWillScope(
                                    scrollController: scrollController),
                          )),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  modal_bottom_sheet: ^0.1.6+1

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
94
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
97
Learn more about scoring.

We analyzed this package on Jul 2, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.4
  • pana: 0.13.13
  • Flutter: 1.17.5

Analysis suggestions

Package not compatible with SDK dart

because of import path [modal_bottom_sheet] that is in a package requiring null.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.0.0 <3.0.0
flutter 0.0.0
Transitive dependencies
collection 1.14.12 1.14.13
meta 1.1.8
sky_engine 0.0.99
typed_data 1.1.6 1.2.0
vector_math 2.0.8
Dev dependencies
flutter_test
pedantic ^1.8.0+1