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!
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:
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:
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.
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.
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.
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 areabarrierLabel
: sets the labeltransitionDuration
: 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.