Intro:

What’s up everyone, today we are building a chat/messaging app. We can say that it’s a Facebook Messenger clone but in a different style. (Note: It’s been more than a year that I quit Facebook and I that was one of the best things I did).

Now let’s get the source code from GitHub: https://github.com/cybdom/messengerish

Show your support at https://www.buymeacoffee.com/bi3cp0Zk5

Ask for a quote: [email protected]

Home Screen:

Let’s start with the BottomNavigationBar. As you can see there is a button docked right in the center of it, in order to achieve that in flutter you need first to use BottomAppBar as your bottom navigation bar and then set floatingActionButtonLocation in your Scaffold to be FloatingActionButtonLocation.centerDocked. as you can see here in the code:

floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, floatingActionButton: FloatingActionButton( elevation: 5, backgroundColor: myGreen, child: Icon(Icons.camera), onPressed: () {}, ), bottomNavigationBar: BottomAppBar( shape: CircularNotchedRectangle(), notchMargin: 7.0, child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ IconButton( icon: Icon(Icons.message, color: Colors.black45), onPressed: () {}, ), IconButton( icon: Icon(Icons.view_list, color: Colors.black45), onPressed: () {}, ), SizedBox(width: 25), IconButton( icon: Icon(Icons.call, color: Colors.black45), onPressed: () {}, ), IconButton( icon: Icon(Icons.person_outline, color: Colors.black45), onPressed: () {}, ), ], ), ),

In order to show your received messages, you can use a ListView.builder that returns a ListTiles as children.

Clicking on one of the messages should take us to our chat screen. Flutter ListTile widget has a convenient onTap property that you can use to Navigate to the chat screen.

The contact profile picture is put in a CircleAvatar in order to get that circular shape, and that widget is in turn wrapped inside a Container that offers you the ability to add a Border as well as a box-shadow. Since it makes sense to know if our contact is online it is a good idea to add a green circle at the top right corner. For that, you wrap the Container inside a Stack and add a Container with a BoxShape.circle and green color.

leading: Container( width: 50, height: 50, decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all( color: Colors.white, width: 3, ), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(.3), offset: Offset(0, 5), blurRadius: 25) ], ), child: Stack( children: [ Positioned.fill( child: CircleAvatar( backgroundImage: NetworkImage(friendsList[i]['imgUrl']), ), ), friendsList[i]['isOnline'] ? Align( alignment: Alignment.topRight, child: Container( height: 15, width: 15, decoration: BoxDecoration( border: Border.all( color: Colors.white, width: 3, ), shape: BoxShape.circle, color: Colors.green, ), ), ) : Container(), ], ), ),

Title and subtitle in this case show respectively the username and the last message sent or received just like most/all messaging apps. You also check to see if the user saw the last message and in that case the text is greyish, otherwise, it’s in a black.

The trailing property of the ListTile allows the user to know:

If their recipient saw the last message they sent. The time they sent/received the last message. The number of unread messages they received.

trailing: Container( width: 60, child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Row( mainAxisSize: MainAxisSize.min, children: [ friendsList[i]['seen'] ? Icon( Icons.check, size: 15, ) : Container(height: 15, width: 15), Text("${friendsList[i]['lastMsgTime']}") ], ), SizedBox( height: 5.0, ), friendsList[i]['hasUnSeenMsgs'] ? Container( alignment: Alignment.center, height: 25, width: 25, decoration: BoxDecoration( color: myGreen, shape: BoxShape.circle, ), child: Text( "${friendsList[i]['unseenCount']}", style: TextStyle(color: Colors.white), ), ) : Container( height: 25, width: 25, ), ], ), ),

Chat Screen:

The appBar of this Scaffold has one particularity and it is that it shows the user name, online/offline status as well as the user profile picture. Here is the code:

title: Row( mainAxisSize: MainAxisSize.min, children: [ MyCircleAvatar( imgUrl: friendsList[0]['imgUrl'], ), SizedBox(width: 15), Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Cybdom Tech", style: Theme.of(context).textTheme.subhead, overflow: TextOverflow.clip, ), Text( "Online", style: Theme.of(context).textTheme.subtitle.apply( color: myGreen, ), ) ], ) ], ),

When the user receives a message it is important to show that message (obviously), as well as the username of the sender, the time when the message was sent and the profile picture of the sender, here is the code:

Row( children: [ MyCircleAvatar( imgUrl: messages[i]['contactImgUrl'], ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "${messages[i]['contactName']}", style: Theme.of(context).textTheme.caption, ), Container( constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width * .6), padding: const EdgeInsets.all(15.0), decoration: BoxDecoration( color: Color(0xfff9f9f9), borderRadius: BorderRadius.only( topRight: Radius.circular(25), bottomLeft: Radius.circular(25), bottomRight: Radius.circular(25), ), ), child: Text( "${messages[i]['message']}", style: Theme.of(context).textTheme.body1.apply( color: Colors.black87, ), ), ), ], ), SizedBox(width: 15), Text( "${messages[i]['time']}", style: Theme.of(context).textTheme.body2.apply(color: Colors.grey), ), ], ),

The sent messages don’t need all of that, just the message and the time is enough. However, it is important to set a different color to the Container in order to differentiate between sent and received messages.

Inside a Row, you put an Expanded Container that has a box-shadow and a 25 borderRadius along with a Circular Container wrapping a Gesture Detector.

The first Container also has a Row as a child, inside there are 3 IconButtons and an expanded TextField.

Container( margin: EdgeInsets.all(15.0), height: 61, child: Row( children: [ Expanded( child: Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(35.0), boxShadow: [ BoxShadow( offset: Offset(0, 3), blurRadius: 5, color: Colors.grey) ], ), child: Row( children: [ IconButton( icon: Icon(Icons.face), onPressed: () {}), Expanded( child: TextField( decoration: InputDecoration( hintText: "Type Something...", border: InputBorder.none), ), ), IconButton( icon: Icon(Icons.photo_camera), onPressed: () {}, ), IconButton( icon: Icon(Icons.attach_file), onPressed: () {}, ) ], ), ), ), SizedBox(width: 15), Container( padding: const EdgeInsets.all(15.0), decoration: BoxDecoration( color: myGreen, shape: BoxShape.circle), child: InkWell( child: Icon( Icons.keyboard_voice, color: Colors.white, ), onLongPress: () { setState(() { _showBottom = true; }); }, ), ) ], ), )

There is a small flaw in this design where there is no button to show more sending options, that is why I used a Gesture Detector on the “Record Button” onLongPress that shows a kind of a BottomSheet custom made by Cybdom.

_showBottom ? Positioned( bottom: 90, left: 25, right: 25, child: Container( padding: EdgeInsets.all(25.0), decoration: BoxDecoration( color: Colors.white, boxShadow: [ BoxShadow( offset: Offset(0, 5), blurRadius: 15.0, color: Colors.grey) ], ), child: GridView.count( mainAxisSpacing: 21.0, crossAxisSpacing: 21.0, shrinkWrap: true, crossAxisCount: 3, children: List.generate( icons.length, (i) { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(15.0), color: Colors.grey[200], border: Border.all(color: myGreen, width: 2), ), child: IconButton( icon: Icon( icons[i], color: myGreen, ), onPressed: () {}, ), ); }, ), ), ), ) : Container(),

The only thing left is a way to dismiss this Bottom sheet and for that, you can use a Positioned.fill GestureDetector, when you take anywhere on the screen you change the value of _showBottom to false and that’s it!

Final thoughts:

While building this layout I had multiple ideas, I thought about making a full app out of this design, and it might just happen!

You may want to follow me on Twitter, every time I publish a tutorial I tweet it. Or if you want to suggest a design for my next tutorial you can tag me on twitter.

Until next time have a nice day and keep coding!