Getting started:

To get started, go ahead and clone my Github repo: https://github.com/cybdom/pharmacy

If you find this tutorial useful please make a donation at: https://www.buymeacoffee.com/bi3cp0Zk5.

Global Variables:

First, let’s set our global variables such as the Colors class as well as a default lorem ipsum String and a list of week days.

import 'package:flutter/material.dart'; String lorem = "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Perferendis quasi impedit possimus quae officia repellat sint?"; class MyColors { static const Color blue = Color(0xff0021ac), pink = Color(0xfffbe4ee), red = Color(0xffe7576b); } List weekDays = ["Sun", "Mon", "Tue", "Wed", "Thur", "Fri", "Sat"];

Home Screen:

As you can see the home screen is composed of two parts, the first has a pink background color and the second one has a blue background. To achieve that we can either use a Stack or as I’ve done here set the Scaffold background color to pink and set the Container’s color to blue.

// Scaffold Background Color. backgroundColor: MyColors.pink,

Inside a Column we have another Column of a DropDownButton and an IconButton wrapped in a Row as well as a custom widget called WeekDaysList. All of these are wrapped in their own padded Container.

After that part we’ve got a kind of Bottom Sheet that is actually an Expanded blue Container with a blue color as a background and rounded top left and right corners as a decoration.

Expanded( child: Container( padding: const EdgeInsets.all(15.0), decoration: BoxDecoration( color: MyColors.blue, borderRadius: BorderRadius.only( topLeft: Radius.circular(25), topRight: Radius.circular(25), ), ), child: Column(children: [// Content]), ), )

The first child of the Column is a Row that horizontally lays a Text widget and an IconButton wrapped in a pink circular Container.

Container( decoration: BoxDecoration( shape: BoxShape.circle, color: MyColors.pink), child: IconButton( icon: Icon(Icons.add, color: MyColors.blue), onPressed: () {}, ), ),

Now that we have finished with the casual stuff, let’s tackle the cool widgets!

The Dismissible Widget:

This Widget allows the user to swipe in defined direction(s) in order to trigger a certain action. For example the user of a Todo app can swipe an action to the right in order to mark it as done.

To learn more about Dimissible check out the Flutter Widget Of The Week video about it: https://www.youtube.com/watch?v=iEMgjrfuc58

Or the CookBook Tutorial: https://flutter.dev/docs/cookbook/gestures/dismissible

Or the Api docs: https://api.flutter.dev/flutter/widgets/Dismissible-class.html

For instance this is how I use it in our pharmacy app:

Container( margin: const EdgeInsets.symmetric(vertical: 15.0), decoration: BoxDecoration( color: MyColors.red, borderRadius: BorderRadius.circular(25.0), ), child: GestureDetector( onTap: () => Navigator.pushNamed(context, 'details'), child: Dismissible( direction: DismissDirection.endToStart, key: Key("$i"), onDismissed: (direction) { Scaffold.of(context).showSnackBar( SnackBar( content: Text("Done!"), ), ); }, background: Container( margin: const EdgeInsets.all(15.0), alignment: Alignment.centerRight, color: MyColors.red, child: Icon(Icons.check, color: Colors.white), ), child: Container( padding: const EdgeInsets.all(15.0), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(25.0), ), child: Row( children: [ Image.asset("assets/pill.png", height: 35, width: 35), SizedBox(width: 9), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Pharmacetical Product 250 mg", style: Theme.of(context) .textTheme .subtitle .copyWith( color: MyColors.blue), ), SizedBox(height: 5), Text( "1 pill, once per day", style: TextStyle( color: MyColors.blue), ), SizedBox(height: 15), Row( children: [ Icon(Icons.timelapse, color: MyColors.blue), Expanded( child: Text( "09:00 AM", style: TextStyle( color: MyColors.blue, ), ), ), Icon(Icons.notifications, color: MyColors.blue), ], ), ], ), ) ], ), ), ), ), );

In this example I wrapped the Dismissible widget in a GestureDetector that handle the onTap callback which takes us to the next screen.

Details Screen:

Let’s get the easy stuff out of the way, first let’s set the Scaffold color to blue. Same thing for the AppBar for which we give an IconButton as a leading widget.

backgroundColor: MyColors.blue, // Scaffold background Color ... appBar: AppBar( // AppBar backgroundColor: MyColors.blue, elevation: 0, leading: IconButton( icon: Icon(Icons.chevron_left, color: Colors.white), onPressed: () => Navigator.pop(context), ), ),

Now to the body of the Scaffold, to stay safe I started with a SafeArea then a Stack.

A Stack was chosen because we need to show both the details screen as well as the map screen in the same Scaffold.

Positioned.fill( child: Padding( padding: const EdgeInsets.all(15.0), child: Column( children: [ Icon(Icons.notifications, color: Colors.white), Text( "Medic Name", style: Theme.of(context) .textTheme .title .copyWith(color: Colors.white), ), Text( "1 Pill a day", style: Theme.of(context) .textTheme .subtitle .copyWith(color: Colors.white), ), Flexible( child: Padding( padding: const EdgeInsets.all(25.0), child: Image.asset("assets/pill.png"), ), ), Text( "8:00 AM", style: Theme.of(context) .textTheme .display1 .copyWith(color: Colors.white), ), ListTile( leading: Icon( Icons.info_outline, color: Colors.white, ), title: Text( "Description", style: Theme.of(context) .textTheme .title .copyWith(color: Colors.white), ), subtitle: Text( "$lorem", style: Theme.of(context) .textTheme .overline .copyWith(color: Colors.white), ), ), SizedBox( width: double.infinity, child: RaisedButton( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(25.0), ), color: MyColors.pink, child: Text( "Where to buy", style: TextStyle(color: MyColors.blue), ), onPressed: () { setState(() { _visible = true; }); }, ), ) ], ), ), ),

As you can see, the code is pretty straightforward. One thing to note is the “Where to buy” button that once it’s clicked changes the _visible variable to true. This will show the Map “screen”.

Map Screen:

AnimatedPositioned( duration: Duration(seconds: 1), left: 0, right: 0, bottom: 0, top: _visible ? 15 : MediaQuery.of(context).size.height - 70, child: Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.only( topLeft: Radius.circular(15), topRight: Radius.circular(15), ), ), child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ Flexible( fit: FlexFit.tight, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ SizedBox( width: 55, child: IconButton( icon: Icon(Icons.close, color: MyColors.blue), onPressed: () { setState(() { _visible = false; }); }, ), ), Text( "Where to buy", textAlign: TextAlign.center, style: Theme.of(context) .textTheme .title .copyWith(color: MyColors.blue), ), SizedBox(width: 55), ], ), ), Expanded( flex: 10, child: Stack( children: [ Positioned.fill( bottom: 70, child: GoogleMap( padding: const EdgeInsets.all(15.0), mapType: MapType.hybrid, initialCameraPosition: CameraPosition( target: LatLng(41, 0), ), onMapCreated: (GoogleMapController controller) {}, ), ), Positioned( bottom: 0, left: 0, right: 0, child: Container( padding: const EdgeInsets.all(15.0), decoration: BoxDecoration( color: MyColors.blue, borderRadius: BorderRadius.only( topLeft: Radius.circular(15.0), topRight: Radius.circular(15.0), ), ), child: Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Nice Pharmacy Name 1", style: Theme.of(context) .textTheme .title .copyWith(color: Colors.white), ), Text( "Location ABC, Route 123.", style: Theme.of(context) .textTheme .overline .copyWith(color: Colors.white), ), SizedBox(height: 5), Text( "08:00 AM - 09:00 PM", style: TextStyle(color: Colors.white), ) ], ), ), Container( decoration: BoxDecoration( shape: BoxShape.circle, color: MyColors.red), child: IconButton( icon: Icon( Icons.directions, color: Colors.white, ), onPressed: () {}, ), ) ], ), ), ) ], ), ) ], ), ), )

AnimatedPositioned is another widget I haven’t covered until now. This widget allows to transition the usual Positioned values such as: Top, Right, Bottom, Left from a value to another through a given period of time.

Here is the link to the API Docs: https://api.flutter.dev/flutter/widgets/AnimatedPositioned-class.html

As well as the Flutter Widget Of The Week: https://www.youtube.com/watch?v=hC3s2YdtWt8

Week Days List Widget: