Friday, 6 September, 2019 UTC


Summary

As Flutter quickly becomes one of the leading technologies in app development, it is important for even someone in the web dev environment to check out. Coming from a background of HTML, CSS and JavaScript, the way Flutter handled everything seemed extremely foreign to me. So we’re going to be try and simplify it to just the essentials of structuring the elements in our app to create a simple layout using the Material Components.
Prerequisites
Already having Flutter installed and an emulator set up is necessary, we covered getting started in Flutter here. Becoming familiar with the official docs is also always a plus.
Scaffold
The Scaffold instantiates our main structure, usually whatever is consistent across our app like our appbar or navigation, then we’ll set our body to the more interesting part of our app, abstracting it out of runApp will also allow us to use hot reload.
main.dart
import 'package:flutter/material.dart'; void main() { runApp( MaterialApp( home: Scaffold( appBar: AppBar( centerTitle: true, title: Text('I\'m an App'), backgroundColor: Colors.red[600], ), body: App(), ), ), ); } class App extends StatelessWidget { @override Widget build(BuildContext context) { return Container(); } } 
And here’s what this looks like:
Containers
Like with HTML div’s, we can wrap our containers to give us more control over our elements when we can’t manipulate them themselves, since not every widget has properties like width or padding. Containers have some of the same properties from CSS like height, width, padding, and margin. By default, they will take up the maximum space of their children, empty containers try to take up the maximum amount of space of their parent.

Margin and Padding

Controlling the spacing can be a bit weird, instead of directly setting padding or margin to a number of pixels, we need to set it to a property on EdgeInsets and pass in our value in pixels. There is also the Padding widget that you can wrap your elements in, but you still need to pass-in padding the same as before so it’s really just to be explicit.
  • EdgeInsets.all()
  • EdgeInsets.only(left: 0, top: 0, right: 0, bottom: 0)
  • EdgeInsets.symmetric(vertical: 0, horizontal: 0)
  • EdgeInsets.fromLTRB(left, top, right, bottom) Just takes the values without them be explicitly assigned.
main.dart
class App extends StatelessWidget { @override Widget build(BuildContext context) { return Container( color: Colors.blue, margin: EdgeInsets.only(top: 20, right: 50), child: Container( color: Colors.green, // Add 200px on top and bottom margin: EdgeInsets.symmetric(vertical: 200), child: Container( color: Colors.yellow, margin: EdgeInsets.fromLTRB(0, 20, 200, 20), ), ), ); } } 
And now we have a bunch of containers:
Columns and Rows
The problem with containers is that we can only have one child in each. The column and row widgets let us use a more flexible version of containers while controlling the direction of our elements. We can’t just pass child to them for each element, we need to set children to an array of the type <Widget>, then pass in each of our elements.
If we put a container inside of an Expanded widget, it will then take up the maximum amount of space of its column or row.
Similar to how flexbox works on the web, we can use mainAxisAlignment and crossAxisAlignment to do things the same as justify-content and align-items. We even have the same options of start, center, end, spaceEvenly, spaceAround, spaceBetween, and stretch.
main.dart
class App extends StatelessWidget { @override Widget build(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[ // Row 1 Row( children: <Widget>[ Container( color: Colors.blue, height: 40, width: 40, child: Text('1')), Container( color: Colors.blue, height: 40, width: 40, child: Text('2')), Container( color: Colors.blue, height: 40, width: 40, child: Text('3')), ], ), // Row 2 Row( children: <Widget>[ Container( color: Colors.blue, height: 40, width: 40, child: Text('1')), //Will expand to fill all remaining space Expanded( child: Container( color: Colors.green, height: 40, width: 40, child: Text('2'))), Container( color: Colors.blue, height: 40, width: 40, child: Text('3')), ], ), //Row 3 Container( height: 100, child: Row( //Stretches to vertically fill its parent container crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[ Container( color: Colors.blue, height: 40, width: 40, child: Text('1')), Expanded( child: Container( color: Colors.green, height: 40, width: 40, child: Text('2'))), Container( color: Colors.blue, height: 40, width: 40, child: Text('3')), ], )), // Row 4 Row( //Creates even space between each item and their parent container mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[ Container( color: Colors.blue, height: 40, width: 40, child: Text('1')), Container( color: Colors.blue, height: 40, width: 40, child: Text('1')), Container( color: Colors.blue, height: 40, width: 40, child: Text('3')), ], ) ]); } } 
And here’s a screenshot of our rows and columns:
Conclusion
While working with layouts in Flutter may seem a bit more clunky and verbose that on the web at first glance, it’s good to keep in mind that we also don’t have to deal with the complexity of dynamic grids or bloated media queries for every screen size. At times, layouts in Flutter may seem less powerful than the CSS you’re used to, because it really doesn’t have to be. With just a few containers, columns, rows, and spacing you can get most of the structures than you’ll ever need.