How to use modal in Flutter

In this shot, we are going to learn how to implement modal in the Flutter app. Then, we are going to implement three types of modal that are highly customizable and flexible enough to be incorporated into any type of UI.

We are going to start with a simple dialog modal and then move into a bit more complex and customized bottom sheet modal. The shot will end with a full-screen modal that can incorporate anything inside it.

Let’s get started!

How to create a new Flutter project

First, we need to create a new Flutter project.

To do that, make sure that the Flutter SDK and all other Flutter app development-related requirements are properly installed.

If everything is properly set up, run the following command in the desired local directory in order to create a project:

flutter create modalExample

After the project has been set up, we can navigate inside the project directory and execute the following command in the terminal to run the project in an available emulator or an actual device:

flutter run

After a successful build, we will get the following result on the emulator screen:

How to create a home screen

Since we have our flutter app up and running in the emulator, we can move on to creating screens. For that, we need to create a directory called ./screens inside the ./lib directory of our project. Then, inside the ./screens directory, we need to create a new dart file called Home.dart.

In Home.dart, we are going to create a stateful widget to show the image as well as the pre-loaders.

But first, we are going to show a simple App bar by returning a Scaffold widget:

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

class HomePage extends StatefulWidget {
  HomePage({Key key}) : super(key: key);
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<HomePage> {

  @override
  void initState() {
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.white,
          centerTitle: true,
          title: Text(
            "Modal",
            style: TextStyle(color: Colors.black87, fontFamily: 'Overpass', fontSize: 20),
          ),
          elevation: 0.0
        ),
        backgroundColor: Colors.white,
        body: Container()
    );
  }

Here, we have added an App bar with the AppBar widget in the Scaffold widget – the body property returns an empty Container widget.

Now, we need to import the Home.dart file in main.dart and replace the default widget with the HomePage widget:

import 'package:flutter/material.dart';
import 'package:modalExample/screens/Home.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: HomePage(),
    );
  }
}

Your should see:

How to scaffold body with buttons to trigger modals

Now, we are going to create a simple buttons view in the Scaffold widget body property. For buttons, we are going to use the RaisedButton widget with the onPressed functional property and Text child property.

The implementation is shown in the code snippet below:

body: Container(
          child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                RaisedButton(
                  onPressed: (){},
                  child: Text('Simple Dialog Modal'),
                ),
                RaisedButton(
                  onPressed: (){},
                  child: Text('Bottom Modal'),
                ),
                RaisedButton(
                  onPressed: (){},
                  child: Text('Full Screen Modal'),
                )
              ],
            ),
          ),
        )
    );

You should see:

Here, we have displayed three buttons for three types of modal are going to display on the screen.

How to build a simple Modal

First, we are going to implement the simple modal. To do so, we are going to create a function called _showSimpleModalDialog that returns a simple dialog.

Then, we will use the showDialog widget, which offers a builder property. Through this property, we can implement the modal in any design and accommodate anything inside it.

The implementation of a simple modal dialog is provided in the code snippet below:

_showSimpleModalDialog(context){
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return Dialog(
          shape: RoundedRectangleBorder(
            borderRadius:BorderRadius.circular(20.0)),
            child: Container(
            constraints: BoxConstraints(maxHeight: 350),
            child: Padding(
              padding: const EdgeInsets.all(12.0),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  RichText(
                    textAlign: TextAlign.justify,
                    text: TextSpan(
                      text:"Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?",
                      style: TextStyle(
                        fontWeight: FontWeight.w400,
                        fontSize: 14,
                        color: Colors.black,
                        wordSpacing: 1
                      )
                    ),
                  ),
                ],
              ),
            ),
          ),
        );
    });
  }

Here, we have returned a combination of different widgets inside the builder property with the Dialog widget as the primary parent widget.

The RoundedRectangleBorder widget allows the widget to have circular edges, and we have incorporated the Text.rich widget inside the modal.

Now, we need to call the function inside its respective button’s onPressed functional property:

RaisedButton(
  onPressed: (){
    _showSimpleModalDialog(context);
  },
  child: Text('Simple Dialog Modal'),
),

You should see the simple module shown below:

Note: you can accommodate any UI elements inside the modal.

How to build a customized bottom sheet modal

Now, let’s move on to a bit of advanced modal implementation. Here, we are going to create a bottom sheet modal that, once triggered, pops up and slides in from the bottom of the screen. For the Modal, we are going to use the showModalBottomSheet Widget returned from _showBottomModal function.

The overall implementation of the customized bottom sheet modal is provided below:

_showBottomModal(context) {
    showModalBottomSheet(
      context: context,
      backgroundColor: Colors.transparent,
      builder: (builder) {
        return new Container(
          // height: 800,
          color: Colors.transparent,
          child: new Container(
            decoration: new BoxDecoration(
              color: Colors.white,
              borderRadius: new BorderRadius.only(
                topLeft: const Radius.circular(10.0),
                topRight: const Radius.circular(10.0),
              ),
              boxShadow: [
                BoxShadow(
                  color: Colors.black26,
                  blurRadius: 10.0, // has the effect of softening the shadow
                  spreadRadius: 0.0, // has the effect of extending the shadow
                )
              ],
            ),
            alignment: Alignment.topLeft,
            child: Column(
              children: <Widget>[
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: <Widget>[
                    Container(
                      margin: EdgeInsets.only(top: 5, left: 10),
                      child: Text(
                        "Bottom Modal",
                        style: TextStyle(
                            fontSize: 16,
                            fontWeight: FontWeight.w900,
                            color: Colors.black87),
                      ),
                    ),
                    Container(
                      margin: EdgeInsets.only(top: 5, right: 5),
                      child: FlatButton(
                        padding: EdgeInsets.fromLTRB(5, 0, 5, 0),
                        onPressed: () {
                          Navigator.pop(context);
                        },
                        child: Text(
                          "Save",
                          style: TextStyle(
                            fontSize: 16,
                            fontWeight: FontWeight.bold,
                            color: Color(0xff999999),
                          ),
                        ),
                      )
                    ),
                  ],
                ),
                SizedBox(height: 5),
                Container(
                  padding: EdgeInsets.fromLTRB(20, 10, 20, 10),
                  decoration: BoxDecoration(
                    border: Border(
                      top: BorderSide(
                        color: const Color(0xfff8f8f8),
                        width: 1,
                      ),
                    ),
                  ),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      RichText(
                        textAlign: TextAlign.justify,
                        text: TextSpan(
                          text:"Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?",
                          style: TextStyle(
                            fontWeight: FontWeight.w400,
                            fontSize: 14,
                            color: Colors.black,
                            wordSpacing: 1
                          )
                        ),
                      ),
                      SizedBox(
                        height: 10,
                      ),
                    ],
                  ),
                )
              ],
            ),
          ),
        );
      });
  }
}

We have designed the main modal Container widget to show rounded edges, and divided the insides of the modal into two sections with the Column widget.

The upper section shows a header with a Text widget on the left and a FlatButton to the right. The lower body section is where we have incorporated the Text.rich component to display text. We can accommodate just about anything in the body section.

Now, we need to call the function inside its respective button:

RaisedButton(
  onPressed: (){
    _showBottomModal(context);
  },
  child: Text('Bottom Modal'),
),

You should see:

The modal seems cool, right? We can use this type of modal to show extra options or details that we are not able to accommodate on a screen.

How to create a full screen modal

Now, it is time to cover the whole screen with a modal overlay.

To do so, we are going to implement a full screen modal that will be on the transparent side to show the visual impact. For the modal, we are going to return the showGeneralDialog widget from the _showFullModal function.

We can configure the modal using its different properties:

  • barrierDismissible: controls the modal exit when tapped outside the modal area
  • barrierLabel: sets the label
  • transitionDuration: allows us to control the animation that pops the modal out into the screen.

Then, we use the pageBuilder property to set the body UI elements inside the modal. We can accommodate just about anything inside the modal.

The overall implementation of this modal is provided in the code snippet below:

_showFullModal(context) {
    showGeneralDialog(
      context: context,
      barrierDismissible: false, // should dialog be dismissed when tapped outside
      barrierLabel: "Modal", // label for barrier
      transitionDuration: Duration(milliseconds: 500), // how long it takes to popup dialog after button click
      pageBuilder: (_, __, ___) { // your widget implementation 
        return Scaffold(
          appBar: AppBar(
            backgroundColor: Colors.white,
            centerTitle: true,
            leading: IconButton(
              icon: Icon(
                Icons.close,
                color: Colors.black,
              ), 
              onPressed: (){
                Navigator.pop(context);
              }
            ),
            title: Text(
              "Modal",
              style: TextStyle(color: Colors.black87, fontFamily: 'Overpass', fontSize: 20),
            ),
            elevation: 0.0
          ),
          backgroundColor: Colors.white.withOpacity(0.90),
          body: Container(
            padding: EdgeInsets.fromLTRB(20, 10, 20, 10),
            decoration: BoxDecoration(
              border: Border(
                top: BorderSide(
                  color: const Color(0xfff8f8f8),
                  width: 1,
                ),
              ),
            ),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                RichText(
                  textAlign: TextAlign.justify,
                  text: TextSpan(
                    text:"Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?",
                    style: TextStyle(
                      fontWeight: FontWeight.w400,
                      fontSize: 14,
                      color: Colors.black,
                      wordSpacing: 1
                    )
                  ),
                ),
                SizedBox(
                  height: 10,
                ),
              ],
            ),
          ),
        );
      },
    );
  }

Finally, we need to call the function inside the RaisedButton widget:

RaisedButton(
  onPressed: (){
    _showFullModal(context);
  },
  child: Text('Full Screen Modal'),
)

Your should see the full modal display shown below:

Full-screen modals are really useful for showing a detailed view of any element in the list. It can also be used in different ways to make the user experience even more efficient.

Free Resources