Learning a new UI engine is always tricky. Thankfully, the Flutter team has highlighted the basic widgets. For this code tutorial, we will focus on Row and Column, which lay out a list of child widgets in the horizontal and vertical direction respectively.
First off, my favourite thing about them is that, even though you may mistake them for Android SDK’s LinearLayout at first, they are actually far more flexible, yet easy to understand.
To figure out how Row and Column work, we will start from 3 different mock-ups and lay them out using them. We will do this as part of an app with 3 screens (one screen per example).
For each example, we will have a rough sketch of what we want, with notes to describe how each part of the layout behaves. Why rough hand drawn sketches and not fancy mock ups done in Photoshop? Because I want to show you a simple process that you can apply to your own mock ups. Oh, and also because pen and paper is a very useful tool (see Tip 3 of 4 tips to boost your software development career).
A few basic rules to remember
The documentation is rich in explanations about how Flutter lays things out, but, when you’re starting out, this is a bit daunting. So, I’ve extracted a few basic Flutter UI rules that will allow you to build many layouts.
- if you want to split your screen horizontally, ie several elements to the right or left of each other, use a Row
- if you want to split your screen vertically, ie several elements above or below each other, use a Column
- if you want to specify padding, a background colour, or a fixed height or width for an item, wrap it inside a Container
- if you want a child element of a Row or Column to expand to fill in the available space, wrap it inside an Expanded
- elements have sensible names and the search feature on the website is pretty good, so use it! For example, if you search for text, you will see both the Text widget and a page listing the different types of available Text widgets.
Code tutorial setup
To follow the code tutorial, create a new app as follows.
1 |
flutter create rowcolumnexample |
If you’re unsure how to set up a Flutter app, check out Getting started with Flutter official tutorial.
We will set up the app to have 3 screens, namely Example1Page, Example2Page, and Example3Page.
So let’s amend main.dart to launch the app on the first example screen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import 'package:flutter/material.dart'; import 'example1_page.dart'; void main() { runApp(new MyApp()); } class MyApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return new MaterialApp( title: 'Row & Column Example', theme: new ThemeData( primaryColor: const Color(0xFF43a047), accentColor: const Color(0xFFffcc00), primaryColorBrightness: Brightness.dark, ), home: new Example1Page(), ); } } |
Now, we create example1_page.dart, example2_page.dart and example3_page.dart, with a simple navigation from 1 to 2 and from 1 to 3 via buttons in the toolbar.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
import 'package:flutter/material.dart'; import 'example2_page.dart'; import 'example3_page.dart'; // Note: for simplicity, this is a stateless widget but, in a real app, // a full screen is likely to be a stateful widget. class Example1Page extends StatelessWidget { @override Widget build(BuildContext context) { List<Widget> menu = <Widget>[ new IconButton( icon: new Icon(Icons.send), tooltip: 'To Example 2', onPressed: () => _toExample2(context), ), new IconButton( icon: new Icon(Icons.help), tooltip: 'To Example 3', onPressed: () => _toExample3(context), ) ]; return new Scaffold( appBar: new AppBar( title: new Text("Example 1 Page"), actions: menu, ), body: new Padding( padding: new EdgeInsets.symmetric(vertical: 0.0, horizontal: 0.0), child: new Text('Example Page 1 content'), ), ); } void _toExample2(BuildContext context) { Navigator.of(context).push(new MaterialPageRoute<dynamic>( builder: (BuildContext context) { return new Example2Page(); }, )); } void _toExample3(BuildContext context) { Navigator.of(context).push(new MaterialPageRoute<dynamic>( builder: (BuildContext context) { return new Example3Page(); }, )); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import 'package:flutter/material.dart'; // Note: for simplicity, this is a stateless widget but, in a real app, // a full screen is likely to be a stateful widget. class Example2Page extends StatelessWidget { @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text("Example 2 Page"), ), body: new Padding( padding: new EdgeInsets.symmetric(vertical: 0.0, horizontal: 0.0), child: new Text('Example Page 2 content'), ), ); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import 'package:flutter/material.dart'; // Note: for simplicity, this is a stateless widget but, in a real app, // a full screen is likely to be a stateful widget. class Example3Page extends StatelessWidget { @override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text("Example 3 Page"), ), body: new Padding( padding: new EdgeInsets.symmetric(vertical: 0.0, horizontal: 0.0), child: new Text('Example Page 3 content'), ), ); } } |
Example 1
For this example, we will create a screen with a subtitle (below the toolbar) which is always shown, a call to action button at the bottom, which is also always shown, and some other content in between the two.
From mock to Flutter widgets
Let’s start with a sketch of the screen, annotated with descriptions in plain English. Then, we convert the descriptions into Flutter widgets.

Code
Now, we implement the design in example1_page.dart.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
@override Widget build(BuildContext context) { List<Widget> menu = <Widget>[ new IconButton( icon: new Icon(Icons.send), tooltip: 'To Example 2', onPressed: () => _toExample2(context), ), new IconButton( icon: new Icon(Icons.help), tooltip: 'To Example 3', onPressed: () => _toExample3(context), ) ]; Widget subtitle = new Container ( padding: new EdgeInsets.all(8.0), color: new Color(0X33000000), child: new Text('Subtitle'), ); Widget middleSection = new Expanded( child: new Container ( padding: new EdgeInsets.all(8.0), color: new Color(0X9900CC00), child: new Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[ new Text('Some data here'), new Text('More stuff here'), ], ), ), ); Widget bottomBanner = new Container ( padding: new EdgeInsets.all(8.0), color: new Color(0X99CC0000), height: 48.0, child: new Center( child: new Text('Bottom Banner'), ), ); Widget body = new Column( // This makes each child fill the full width of the screen crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, children: <Widget>[ subtitle, middleSection, bottomBanner, ], ); return new Scaffold( appBar: new AppBar( title: new Text("Example 1 Page"), actions: menu, ), body: new Padding( padding: new EdgeInsets.symmetric(vertical: 0.0, horizontal: 0.0), child: body, ), ); } |
Voila!

Example 2
For this example, we will create a screen with a subtitle (below the toolbar) which is always shown, a list which takes up 2 / 3 of the space below the subtitle, and a grid which takes up 1/ 3 of the space below the subtitle.
From mock to Flutter widgets
Let’s start with a sketch of the screen, annotated with descriptions in plain English. Then, we convert the descriptions into Flutter widgets.

Code
Now, we implement the design in example2_page.dart.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
@override Widget build(BuildContext context) { Widget subtitle = new Container ( padding: new EdgeInsets.all(8.0), color: new Color(0X33000000), child: new Text('Subtitle'), ); Widget listSection = new Expanded( flex: 2, child: new ListView( scrollDirection: Axis.vertical, shrinkWrap: true, children: _generateListItems().map((String value) { return _displayListItem(value); }).toList()), ); Widget gridSection = new Expanded( flex: 1, child: new GridView.count( crossAxisCount: 4, childAspectRatio: 1.0, mainAxisSpacing: 4.0, crossAxisSpacing: 4.0, children: _generateGridItems().map((String value) { return _displayGridItem(value); }).toList()), ); Widget body = new Column( // This makes each child fill the full width of the screen crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, children: <Widget>[ subtitle, listSection, gridSection, ], ); return new Scaffold( appBar: new AppBar( title: new Text("Example 2 Page"), ), body: new Padding( padding: new EdgeInsets.symmetric(vertical: 0.0, horizontal: 0.0), child: body, ), ); } Widget _displayListItem(String value) { return new Container ( padding: new EdgeInsets.all(8.0), color: new Color(0X9900CCCC), child: new Text(value), ); } Widget _displayGridItem(String value) { return new Container ( padding: new EdgeInsets.all(8.0), color: new Color(0X99CCCC00), child: new Text(value), ); } // Note: Placeholder method to generate list data. List<String> _generateListItems() { List<String> listItems = new List<String>(); for (int i = 0; i < 20; i++) { listItems.add('List Item ' + i.toString() + ' title and description'); } return listItems; } // Note: Placeholder method to generate grid data List<String> _generateGridItems() { List<String> gridItems = new List<String>(); for (int i = 0; i < 24; i++) { gridItems.add('GI ' + i.toString()); } return gridItems; } |
Voila!

Example 3
For this example, we will create a widget that would most likely be used in a list. It has an icon on the left and one on the right, a title which itself is made up of an image, and 2 separate pieces of text (one of those to be truncated as required), and a description to be restricted to 2 lines.
From mock to Flutter widgets
Let’s start with a sketch of the screen, annotated with descriptions in plain English. Then, we convert the descriptions into Flutter widgets.

Code
Now, we implement the design in example3_page.dart.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
@override Widget build(BuildContext context) { Widget titleRow = new Row( children: <Widget>[ new Icon(Icons.people), new Expanded( child: new Container( padding: new EdgeInsets.symmetric(horizontal: 4.0), child: new Text('This is quite a long name that will be cut off', overflow: TextOverflow.ellipsis, maxLines: 1, ), ), ), new Text('0111 222 333'), ], ); Widget textSection = new Container( color: new Color(0X3300CC00), child: new Column( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, children: <Widget>[ titleRow, new Text('We have a description here. It may fit on 2 lines, ' 'or it may fit on 1 line. Or will require more lines than 2, but' ' we only show 2 lines maximum f f f f f f f f f f f f f f f .', overflow: TextOverflow.ellipsis, maxLines: 2,) ], ), ); Widget body = new Container( color: new Color(0X33000000), child: new Row( children: <Widget>[ new IconButton( onPressed: null, // Not implemented in this code tutorial icon: new Icon(Icons.send), ), new Expanded( child: textSection), new IconButton( onPressed: null, // Not implemented in this code tutorial icon: new Icon(Icons.edit), ), ], ), ); return new Scaffold( appBar: new AppBar( title: new Text("Example 3 Page"), ), body: new Padding( padding: new EdgeInsets.symmetric(vertical: 0.0, horizontal: 0.0), child: body, ), ); } |
Voila!

What next?
From one of your favourite apps, or an app you are working on now, choose a screen, and work out how you would lay it out in Flutter.

Mobile app developer with 12 years experience. Started with the Android SDK in 2010 and switched to Flutter in 2017. Past apps range from start ups to large tech (Google) and non tech (British Airways) companies, in various sectors (transport, commercial operations, e-commerce).
Great post. There is not much documentation on how to use the layout widgets together so this is very helpful.
One anomaly in your first design. If there is an entry form in the middle, the bottom footer slides up with the keyboard. Do you know a way to get the keyboard to cover the bottom footer instead?
Hi CodeGrue, I ran into this earlier: https://gist.github.com/slightfoot/2baa44e8a35f5226cd4c9f41caf81d5f
It seems to address the keyboard pushing content up issue.
Wow thanks very much for this tutorial it helps
Excellent post, layout was tricky for me since I’m not a native dev — this helped a bunch.
Combination of useful tips, sketches and clearly laid out code is effective. I ‘got it’ really quickly. Thanks
Hi Natalie
Remember me? We worked together in ubicabs. I was working remotely as android dev then.
I was searching for flutter layouts and found your blog. Great work and nice blog. (y)
regards
hashir
Hi Hashir, yes, I do! Sent you an email 😉