After the positive review for part one of this tutorial, I decided to follow up with part two where we will build together this screen.

Source code: https://github.com/cybdom/crypto_app

AppBar:

Let’s start here, after setting our scaffold color to white we have to do the same to our AppBar as well as setting its elevation to 0.

The design says the AppBar should have a search icon button and a back button so that is what we will do here.

appBar: AppBar( backgroundColor: Colors.white, elevation: 0, actions: <Widget>[ IconButton(icon: Icon(Icons.search), onPressed: () {}) ], leading: IconButton(icon: Icon(Icons.arrow_back), onPressed: () {}), ),

The default Icon color is white, in order to change the icon colors we can do it right in the Icon Widget or inside our AppBar which has an IconTheme property that we use like this.

iconTheme: IconThemeData(color: Colors.black),

Transactions State Container:

Here we’ve got a simple GridView.builder the widget’s setup itself is pretty simple.

GridView.builder( shrinkWrap: true, itemCount: transactions_stat.length, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( childAspectRatio: 3 / 4, crossAxisSpacing: 15, mainAxisSpacing: 15, crossAxisCount: 3, ), itemBuilder: (ctx, i) { // our widgets } ),

As you can see there is a transactions_stat variable which is a List of type map that contains all of our stats container data. Inside global.dart we set this variable as follows:

List<Map<String, dynamic>> transactions_stat = [ { 'count': 73, 'color': Color(0xff1b4dfe), 'text': 'Waiting For Confirmation', 'text_color': Colors.white, }, { 'count': 49, 'color': Color(0xff112f5f), 'text': 'Be Pairing', 'text_color': Colors.white, }, { 'count': 9, 'color': Color(0xff1bc29f), 'text': 'In Progress', 'text_color': Colors.white, }, { 'count': 231, 'color': Colors.white, 'text': 'Completed', 'text_color': Colors.black, 'border' : Border.all(color: Colors.black54) }, { 'count': 0, 'color': Color(0xfff3e8d9), 'text': 'Objection or failure', 'text_color': Color(0xffe59f45), }, ];

Instead of a Map you can use a Class inside your models folder. (For simplicity sack I prefer using a Map).

StatesDetailContainer:

Each of the StatesDetailContainer widget has these variables:

Container border (If any): transactions_stat[i][‘border’]

Container color: transactions_stat[i][‘color’]

State count: transactions_stat[i][‘count’]

State detail text: transactions_stat[i][‘text’]

Text Color: transactions_stat[i][‘text_color’]

Here is the code for the widget itself:

class StatesDetailContainer extends StatelessWidget { final int i; const StatesDetailContainer({ Key key, this.i, }) : super(key: key); @override Widget build(BuildContext context) { return Container( padding: EdgeInsets.all(15.0), decoration: BoxDecoration( border: transactions_stat[i]['border'] ?? Border(), borderRadius: BorderRadius.circular(15.0), color: transactions_stat[i]['color'], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Expanded( child: Text( "${transactions_stat[i]['count']}", style: Theme.of(context).textTheme.headline.apply( color: transactions_stat[i]['text_color']), ), ), Expanded( child: Text( "${transactions_stat[i]['text']}", style: TextStyle( color: transactions_stat[i]['text_color'], ), ), ), ], ), ); } }

First thing to note is the border of our container, we use transactions_stat[i][‘border’] if it’s not null otherwise we use border() which gives us no border.

The second is that our Text widgets are wrapped inside Expanded that allows the text to break.

Transactions List:

Because we are inside a SingleChildScrollView we could use List.generate and it will build all of our TransactionContainer however since we don’t know how many of these transactions the user may have I prefer to use a ListView.builder.

Container( height: MediaQuery.of(context).size.height / 3, child: ListView.builder( itemCount: transactions.length, itemBuilder: (ctx, i) { // return TransactionContainer(); } ) ),

Transaction Container:

class TransactionContainer extends StatelessWidget { final int i; const TransactionContainer({ Key key, this.i, }) : super(key: key); @override Widget build(BuildContext context) { return Column( children: <Widget>[ Row( children: <Widget>[ Expanded( child: Text( "${transactions[i]['title']}", style: Theme.of(context) .textTheme .subhead .apply(color: darkBlue, fontWeightDelta: 2), ), ), SizedBox(width: 15), Container( padding: EdgeInsets.symmetric( horizontal: 9.0, vertical: 5.0), decoration: BoxDecoration( color: Color(0xffd5d7dc), borderRadius: BorderRadius.circular(15.0), ), child: Text("Pairing"), ) ], ), SizedBox(height: 11), Row( children: <Widget>[ Text("Originator: "), Text("${transactions[i]['originator']}") ], ), SizedBox(height: 5), Row( children: <Widget>[ Text("Transaction Number: "), Text("${transactions[i]['transaction_number']}") ], ), SizedBox(height: 5), Row( children: <Widget>[ Text("Type: "), Text("${transactions[i]['type']}") ], ), Row( mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[ RaisedButton( child: Text("Delete"), onPressed: () {}, color: Colors.white, elevation: 0, shape: RoundedRectangleBorder( borderRadius: new BorderRadius.circular(9.0), side: BorderSide(color: Colors.black54), ), ), SizedBox(width: 15.0), RaisedButton( child: Text( "Accept", style: TextStyle(color: Colors.white), ), onPressed: () {}, color: lightBlue, elevation: 0, shape: RoundedRectangleBorder( borderRadius: new BorderRadius.circular(9.0), ), ), ], ), Divider( height: 21, color: darkBlue, ), ], ); } }

What can I say about this widget? It is pretty simple.

One way for creating rounded buttons in flutter is by using the Shape property inside our RaisedButton widget.

As you can see the Shape property here takes a RoundedRectangleBorder, this allows us to have a border-radius as well as a border. For the case of the delete button which is a transparent button, we use the border value to imply that it is a clickable item.

The end.

I hope you followed along with me on this and you learned something new. If you did then share this tutorial!

Another nice thing you can do is donate on my BuyMeACoffee page, which allows me to pay for the hosting of this site.

Don’t miss other exciting tutorials that are coming soon, follow me on Twitter: @cybdom

Upcoming: