Saturday, June 16, 2018

Flutter - Design user interface with Flutter layouts.

Before start building layouts for Flutter applications. It’s important to understand how the framework handles the creation of these layouts. The Flutter framework builds its layout via the composition of widgets, everything that you construct programmatically is a widget and these are compiled together to create the user interface. 


I have already created a post for Flutter widget that explains the fundamentals of Flutter building a Flutter app. So I’m going to assume you’ve followed one of those already and instead call out interesting elements of the app that highlight strengths of Flutter as a mobile toolkit.

Let’s take a look at the TabLayout that is a common view component used within mobile applications:


If we manually build this layout within a Flutter application, then the structure of the layout file looks:


                                                                         


Container is a widget that contains child widgets. It gives the ability to add styling properties to the children that are contained within it, this can be things such as padding, margins, background color etc.

In the case of above tablayout component, our whole layout is placed within a container so that the layout is encapsulated into its own separate container. 

For design such kind of layout, Flutter gives us various component that's we going to discuss here.
  • Row and Column
  • Stack
  • Expanded
  • ConstrainedBox
  • Container
  • SizeBox
  • SafeArea

Row and Column
In Flutter, a common way of laying out widgets is composing them into groups of columns and row. This allows us to easily layout our components in a vertical/horizontal manner. When you wish to layout components in a Horizontal manner, then you will use a Row and for the Vertical layout, you will use a Column

As you can see above tab layout, we only need one Row for our parent container, as all of the child components are composed into this single row. If we move into this Row to the child components, we can see that four Columns have been used. Because we have four tab layout options that we wish to distribute within this column.


Within each of these columns, we have placed child widgets in the form of some text. This Text and Container component are then contained within our defined Column which is used to display these widgets in their vertical manner.




Properties of Row and Column. 

MainAxisAlignment
We can use it to align the widget in row and column.
   
Example-1
MainAxisAlignment.start
body: new Row( mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[ Container( color: Colors.orange, child: FlutterLogo( size: 60.0, ), ), new Padding( padding: const EdgeInsets.only(left: 5.0), child: new Text("Hello") ), new Icon(Icons.delete, size: 50.0), new Icon(Icons.edit, size: 50.0), ], ),
// Just replace row with column

Example-2
MainAxisAlignment.baseline
new Row( crossAxisAlignment: CrossAxisAlignment.baseline, textBaseline: TextBaseline.alphabetic, children: <Widget>[ new Text( 'Baseline', style: Theme.of(context).textTheme.display3, ), new Text( 'Baseline', style: Theme.of(context).textTheme.body1, ), ], ),



Stack
If you are an Android developer, you definitely know about Framelayout. In Flutter, stack behaves the same as FrameLayout. You can use the stack if you want to overlap several children in a simple way like having some text and an image, overlaid with a gradient and a button attached to the bottom. 

The stack paints its children in order with the first child being at the bottom. If you want to change the order in which the children paint, you can rebuild the stack with the children in the new order. If you reorder the children in this way, consider giving the children non-null keys. These keys will cause the framework to move the underlying objects for the children to their new locations rather than recreate them at their new location. As you can see below, an overlap banner on the delete icon. 


implementation of the above UI.
Stack
@override Widget build(BuildContext context) { return new Scaffold( appBar: AppBar(title: Text('Stack')), body: Stack( fit: StackFit.expand, children: <Widget>[ Material(color: Colors.blueGrey), Positioned( top: 0.0, left: 0.0, child: Icon(Icons.delete, size: 50.0), ), Positioned( top: 340.0, left: 250.0, child: Icon(Icons.add_a_photo, size: 50.0), ), Banner( message: "Top Start", location: BannerLocation.topStart, ), Banner( message: "Top End", location: BannerLocation.topEnd, ), Banner( message: "Bottom Start", location: BannerLocation.bottomStart, ), Banner( message: "Bottom End", location: BannerLocation.bottomEnd, ), ], ), ); }

Expanded
Expanded works with Row, Column, and Flex layout. It can distribute space between multiple items. With an Expanded widget makes a child of a Row, Column, and Flex expands to fill the available space in the main axis (e.g., horizontally for a Row or vertically for a Column). If multiple children are expanded, the available space is divided among them according to the flex factor.
Expanded
new Row( children: <Widget>[ new Expanded( child: new Container( decoration: const BoxDecoration(color: Colors.green), ), flex: 3, ), new Expanded( child: new Container( decoration: const BoxDecoration(color: Colors.red), ), flex: 2, ), new Expanded( child: new Container( decoration: const BoxDecoration(color: Colors.blue), ), flex: 1, ), ], ),

ConstrainedBox
By default, most of the widgets will use as little space as possible.
Without ConstrainedBox
@override Widget build(BuildContext context) { return new Scaffold( body: new Center( child: new Card(child: const Text('Hello World!'), color: Colors.green), ), ); }
constrainedBox can acquire available space.

ConstrainedBox
@override
Widget build(BuildContext context) { return new Scaffold( appBar: AppBar(title: Text('ConstrainedBox')), body: Stack( children: <Widget>[ Material(color: Colors.blueGrey), new ConstrainedBox( constraints: new BoxConstraints.expand(), child: const Card( child: const Text('ConstrainedBox'), color: Colors.green, ), ), ], ), ); }



Container
The Container widget is used to contain child widgets whilst also providing the ability to apply some basic styling properties on itself to be applied when laid out on screen.


If the container has no children then it will automatically fill the given area on the screen (dependant on constraints), otherwise, it will wrap the height & width of the given child elements.

When you don’t specify the height and the width of the Container, it will match its child’s size
Container match child size
Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Container as a layout')), body: Container( color: Colors.yellowAccent, child: Text("Hi"), ), ); }

If you want to stretch the Container to match its parent, use double.infinity for the height and width properties
Match to parent
Widget build(BuildContext context) {
return Scaffold( appBar: AppBar(title: Text('Container as a layout')), body: Container( height: double.infinity, width: double.infinity, color: Colors.red, child: Text("Hi"), ), ); }

Container has following properties. 
  • transform:-  If you don’t want to use Transform widget to change your layout. You can use transform property straight from the Container
Container Transform
@override Widget build(BuildContext context) { return new Scaffold( appBar: AppBar(title: Text('Container')), body: Container( height: 300.0, width: 300.0, transform: Matrix4.rotationZ(45.0), decoration: BoxDecoration(color: Colors.red), child: Text( "Hi", textAlign: TextAlign.center, ), ), ); }
  • alignment:- Provide a FractionalOffset to be applied for aligning the child widgets. This could include bottomCenter, bottomLeft, bottomRight, center, centerLeft, centerRight, topCenter, topLeft and topRight:
alignment
alignment: FractionalOffset.center
  • constraints:- Constraints to be applied to the container widget.
constraints
constraints: new BoxConstraints.expand(
width: 200.0, )
  • decoration:- Apply a decoration to the given child.
    decoration
    decoration: new BoxDecoration( color: Colors.blue.shade100 )
  • foregroundDecoration :- Apply a decoration to applied in front of the given child.
    foregroundDecorataion
    foregroundDecoration: new BoxDecoration( color: Colors.amber.shade400 )
  • margin :- Apply a margin to the container using an EdgeInsets constant value.
    margin
    margin: const EdgeInsets.all(16.0)
  • padding:- Apply to pad to the container using an EdgeInsets constant value.
    padding
    padding: const EdgeInsets.all(16.0)
  • color:- A background color which is to be applied to the container:
    color
    color: Colors.amber.shade400



SizedBox
A box with a specified size. If given a child, this widget forces its child to have a specific width and height (assuming values are permitted by this widget's parent). If either the width or height is null, this widget will size itself to match the child's size in that dimension. If not given a child, this widget will size itself to the given width and height, treating nulls as zero.

padding with SizedBox 
If you want to add padding or margin, you might choose Padding or Container widgets. But they can be more verbose and less readable than adding a Sizedbox.

SizedBox
@override Widget build(BuildContext context) { return new Scaffold( appBar: AppBar(title: Text('Sizebox')), body: new Column( children: <Widget>[ Icon(Icons.access_time, size: 50.0), const SizedBox(height: 100.0,), Icon(Icons.access_time, size: 50.0), Icon(Icons.access_time, size: 50.0) ], ), ); }

SizedBox as an Invisible Object
Many time you would like to hide/show a widget depending on a bool.

SizedBox hide/show widget
Widget build(BuildContext context) { bool isVisible = true; //true/false return Scaffold( appBar: AppBar( title: Text('isVisible = $isVisible'), ), body: isVisible ? Icon(Icons.adb, size: 150.0) : const SizedBox(), ); }


SafeArea
Android and Ios Platforms have some special areas like Status Bar on Android or the Notch on iPhone X that we might avoid drawing under.
SafeArea
Widget build(BuildContext context) { return Material( color: Colors.blue, child: SafeArea( child: SizedBox.expand( child: Card(color: Colors.red), ), ), );




So, These are a layout that I've explored. If you facing any issue to implement it. please feel free to clarify it in the comment section below.




Share:

Get it on Google Play

React Native - Start Development with Typescript

React Native is a popular framework for building mobile apps for both Android and iOS. It allows developers to write JavaScript code that ca...