Skip to main content

All About Flutter

FLUTTER

What is Flutter?

Flutter is an open-source mobile SDK developer can use to build native-looking Android and iOS applications from the same code base. Flutter has been around since 2015 when Google introduced it and remained in the beta stage before its official launch in December 2018. Since then, the buzz around Flutter has been growing stronger.

Widgets
The central idea behind Flutter is the use of widgets. It’s by combining different widgets that developers can build the entire UI. Each of these widgets defines a structural element (like a button or menu), a stylistic element (a font or color scheme), a layout aspect (like padding), and many others.
Flutter also provides developers with reactive-style views. To avoid performance issues deriving from using a compiled programming language to serve as the JavaScript bridge, Flutter uses Dart. It compiles Dart ahead of time (AOT) into the native code for multiple platforms.
Flutter also provides developers with reactive-style views. To avoid performance issues deriving from using a compiled programming language to serve as the JavaScript bridge, Flutter uses Dart. It compiles Dart ahead of time (AOT) into the native code for multiple platforms.
As you know in Flutter all the UI components are known as widgets. The widget which contains the code for a single screen of the app can be just of two types —
  1. Stateful
  1. Stateless

Stateless

Stateless widgets do not require a mutable state, i.e., it is immutable.
In simple words, Stateless widgets cannot change their state during the runtime of the app, which means the widgets cannot be redrawn while the app is in action.
The structure of a Stateless widget looks like this:
Stateless Widget
So, let’s understand what is there in this small code snippet.
The name of this Stateless Widget is “StartScreen”, inside which we have to override the “build” method. This build method takes in a “BuildContext” as the parameter and returns a widget. That’s why you can see that the return type of the build method is a widget. And this the place where you can design the UI of this screen, which is Stateless.
In Stateless widget, The “build” method can be called only ONCE while the app is in action, which is responsible for drawing the widgets on to the device screen.
If you want to redraw the Stateless Widget, then you will need to create a new instance of the widget.
TIPS: You can quickly build a Stateless Widget in VS Code or Android Studio by using the shortcut “stl”.
Some examples of Stateless widgets are as follows:
Now, let’s move on to the Stateful Widget.

Stateful

Stateful widgets have a mutable state, i.e., they are mutable and can be drawn multiple times within its lifetime.
They are the widgets which can change their state multiple times and can be redrawn on to the screen any number of times while the app is in action.
The structure of a Stateful widget looks like this:
Stateful Widget
The name of the widget is again “StartScreen”, but now it overrides the “createState” method, instead of the “build” method, which returns the instance of the class “_StartScreenState”.
The class “_StartScreenState” extends from State<> which takes “StartScreen” as a template input.
Now, this “_StartScreenState” overrides the “build” method and returns a widget. This is where you can define the UI of the app, which is Stateful. As it is a Stateful widget you can call the build method any number of times, which will redraw the widgets on the screen.

So, how can you call the build method?

It’s really easy, you can use the “setState” method to call the build method, which will, in turn, redraw the widgets. This is the most important method you will need to use with any Stateful widget, to really use the statefulness of the widget.
TIPS: You can quickly build a Stateful Widget in VS Code or Android Studio by using the shortcut “stf”.
Some examples of Stateful widgets are as follows:

Conclusion

We have come to the end of this short article. I hope you all have received a basic idea of Stateful and Stateless widgets and how do they differ from each other. These concepts would be more clear if you do some projects on your own and get the feel of how the app handles the state.

MaterialApp is the starting point of your app, it tells Flutter that you are going to use Material components and follow material design in your app.
What is Scaffold?
A Scaffold Widget provides a framework which implements the basic material design visual layout structure of the flutter app. It provides APIs for showing drawers, snack bars and bottom sheets. Have a look at its constructor and the properties it has. We are going to overview it’s parameters one by one.
//Constructor of Scaffold
const Scaffold({
Key key,
this.appBar,
this.body,
this.floatingActionButton,
this.floatingActionButtonLocation,
this.floatingActionButtonAnimator,
this.persistentFooterButtons,
this.drawer,
this.endDrawer,
this.bottomNavigationBar,
this.bottomSheet,
this.backgroundColor,
this.resizeToAvoidBottomPadding = true,
this.primary = true,
})
Scaffold contains various functionality from giving an appbar, a floating button, a drawer, background color, bottom navigation bar, footer buttons,body. Now, let’s explore them individually:
appBar
An appBar is to display at the top of the scaffold it’s one of the primary content of Scaffold without which a scaffold widget is incomplete. It defines what has to be displayed at the top of the screen. appBar uses the AppBar widget properties through Scaffold.appBar. This AppBar widget itself has various properties like title, elevation,brightness etc to name a few.
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Scaffold'),
),
Here, the title property of AppBar uses Text widget to display the text on the app bar.
Fig 1: Display an AppBar
body
body is the other primary and minimum required property of the scaffold which signifies the area below the app bar and behind the floatingActionButton and drawer. Any widget in the body of scaffold is positioned at the top-left corner by default.
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Scaffold'),
),
body: Center(
child: Text("This is Homepage",
style: TextStyle(
color: Colors.black,
fontSize: 40.0,
fontStyle: FontStyle.italic,
),
),
),
Here, the body property of the Scaffold is used to display a text “This is HomePage”. The Centre widget is used to align the text at the centre of the body and a Text widget is used to give the text along with it’s style property which gives the text a color, fontsize and fontstyle. Don’t worry this blog isn’t about the Centre or Text or TextStyle widgets, they will be covered later.
Fig 2: Text displayed inside the body
floatingActionButton
A floatingActionButton is a button displayed floating above body in the bottom right corner. It is a circular icon button that hovers over content to promote a primary action in the application. floatingActionButton uses the FloatingActionButton widget properties through Scaffold.floatingActionButton.
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Scaffold'),
),
body: Center(
child: Text(
"This is Homepage",
style: TextStyle(
color: Colors.black,
fontSize: 40.0,
fontStyle: FontStyle.italic,
),
),
),
floatingActionButton: FloatingActionButton(
elevation: 10.0,
child: Icon(Icons.add),
onPressed: (){
print('I am Floating button');
}
),
);
}
Here, the elevation property of the FloatingActionButton is used which gives shadow to the button and Icon widget is used to give an icon to the button. The onPressed property provides a callback that is called when the button is tapped, when you tap the button “ I am a Floating Button” gets printed on the console window refer Fig 4 below .
Fig 3: A floatingActionButton with an icon
Fig 4: onPressed callback is called when tapped
floatingActionButtonLocation
By default, the floatingActionButton is positioned at the bottom right corner of the screen so as the name says floatingActionButtonLocation defines the position of the floatingActionButton using the predefined contants like,
centerDocked
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
centerFloat
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
endDocked
floatingActionButtonLocation: FloatingActionButtonLocation.endDocked,
endFloat
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
Fig 5: floatingActionButtonLocation constants
drawer
drawer is a panel displayed to the side of the body, often hidden on mobile devices. One usually has to swipe left to right or right to left to access the drawer.
It uses the Drawer widget properties which is a material design panel that slides in horizontally from the edge of a Scaffold to show navigation links in an application and this widget is used in scaffold using Scaffold.drawer property.
drawer: Drawer(
elevation: 16.0,
child: Column(
children: <Widget>[
UserAccountsDrawerHeader(
accountName: Text("xyz"),
accountEmail: Text("xyz@gmail.com"),
currentAccountPicture: CircleAvatar(
backgroundColor: Colors.white,
child: Text("xyz"),
),
otherAccountsPictures: <Widget>[
CircleAvatar(
backgroundColor: Colors.white,
child: Text("abc"),
)
],
),
ListTile(
title: new Text("All Inboxes"),
leading: new Icon(Icons.mail),
),
Divider(
height: 0.1,
),
ListTile(
title: new Text("Primary"),
leading: new Icon(Icons.inbox),
),
ListTile(
title: new Text("Social"),
leading: new Icon(Icons.people),
),
ListTile(
title: new Text("Promotions"),
leading: new Icon(Icons.local_offer),
)
],
),
),
Here, the drawer property of scaffold is used to create a drawer. We used various other widget inside this to make a little attractive drawer but don’t worry about all the other widgets, as we’ll be covering in the series.
Fig 6: drawer
persistentFooterButtons
persistentFooterButtons is a list of buttons that are displayed at the bottom of the scaffold. These buttons are persistently visible, even if the body of the scaffold scrolls. They will be wrapped in a ButtonBar and are rendered above the bottomNavigationBar but below the body.
persistentFooterButtons: <Widget>[
RaisedButton(
elevation: 10.0,
onPressed: () {},
color: Colors.green,
child: Icon(
Icons.add,
color: Colors.white,
),
),
RaisedButton(
elevation: 10.0,
onPressed: () {},
color: Colors.blueGrey,
child: Icon(
Icons.clear,
color: Colors.white,
),
),
],
Here, persistentFooterButtons take a list of widgets or buttons. In this we have used the RaisedButton if you want you can use the FlatButton instead.
Fig 7: persistentFooterButtons
bottomNavigationBar
bottomNavigationBar is used to display a navigation bar at the bottom of the scaffold. It is rendered below the persistentFooterButtons and the body.
bottomNavigationBar: BottomNavigationBar(
currentIndex: 0,
fixedColor: Colors.teal,
items: [
BottomNavigationBarItem(
title: Text("Home"),
icon: Icon(Icons.home),
),
BottomNavigationBarItem(
title: Text("Search"),
icon: Icon(Icons.search),
),
BottomNavigationBarItem(
title: Text("Add"),
icon: Icon(Icons.add_box),
),
],
onTap: (int index){
setState(() {
_currentIndex = index;
});
},
),
Fig 8: bottomNavigationBar
endDrawer
enddrawer is like a drawer property but in enddrawer by default the drawer is display at the right side of the screen.
primary
Whether the scaffold is being displayed at the top of the screen. If true then the height of the appBar will be extended by the height of the screen’s status bar, i.e. the top padding for. The default value of this property is true.
Fig 9: Shows primary property of scaffold
backgroundColor
This property sets the background color of the scaffold.
backgroundColor: Colors.white,
resizeToAvoidBottomInset
If this property is true the body and the scaffold’s floating widgets should size themselves to avoid the onscreen keyboard whose height is defined by the bottom property.
For example, if there is an onscreen keyboard displayed above the scaffold, the body can be resized to avoid overlapping the keyboard, which prevents widgets inside the body from being obscured by the keyboard. Defaults to true.



CONTAINER
The Container widget is used to contain a child widget with the ability to apply some styling properties.
If the Container widget has no child it will automatically fill the given area on the screen, otherwise it will wrap the height & width of the given child element.
NB: Container Widget should not render directly without any parent widget. You can use Center widget, Padding Widget, Column Widget, Row Widget or Scaffold Widget as parent.

Lets start with en empty container and apply a red color property. The container will fill all the screen.
Center(
child: Container(
color: Colors.green,
),
);

Let’s add a child for the container
Center(
child: Container(
color: Colors.green,
child: Text("Flutter CheatSheet."),
),
);
We can see that the container take the size of the child. (We will talk later on the Text Widget)

Color Property

You can use the color property to apply a background color for the container.
You will use the Color Class or Colors Class with the color property like below:
Colors Class
use Colors Class with the color name
Center(
child: Container(
color: Colors.green,
),
);
You can add a shade also
Center(
child: Container(
color: Colors.green[400],
),
);
OR
Center(
child: Container(
color: Colors.green.shade400,
),
);
Color Class
use Color Class with a full 8 hexadecimal digits not 6. If you only specify six, then the leading two digits are assumed to be zero, which means fully-transparent.
Color(0xFFFFFF); // fully transparent white (invisible)
Color(0xFFFFFFFF); // fully opaque white (visible)
You can use the .fromARGB (A = Alpha or opacity, R = Red, G = Green, B = Blue) with the color number or hexadecimal number
Color.fromARGB(0xFF, 0x42, 0xA5, 0xF5);
Color.fromARGB(255, 66, 165, 245);
And you can use the .fromRGBO (R = Red, G = Green, B = Blue, O = Opacity) with the color number
Color c = const Color.fromRGBO(66, 165, 245, 1.0);

Child Property

Provide a child widget to be contained by the container, the container will wrap the width & height of this child.
This widget can only have one child. To lay out multiple children, let this widget’s child be a widget such as Row, Column, or Stack, which have a children property, and then provide the children to that widget.
Center(
child: Container(
color: Color.fromARGB(255, 66, 165, 245),
child: new Text("Flutter Cheatsheet"),
),
);

Alignment Property

We use an Alignment Class with the alignment property to be applied for aligning the child widgets.
Alignment take 2 parameters x and y.
Alignment(0.0, 0.0) represents the center of the rectangle.
Center(
child: Container(
color: Color.fromARGB(255, 66, 165, 245),
child: new Text("Flutter Cheatsheet",
style: TextStyle(
fontSize: 10.0
),
),
alignment: Alignment(0.0, 0.0),
),
);
Alignment(-1.0, -1.0) represents the top left of the rectangle.
Alignment(1.0, 1.0) represents the bottom right of the rectangle.
Alignment(0.0, 3.0) represents a point that is horizontally centered with respect to the rectangle and vertically below the bottom of the rectangle by the height of the rectangle.
The below picture show the graph of X and Y
You can also use a constant name with the Alignment Class
Alignment.bottomCenter The center point along the bottom edge same as Alignment(0.0, 1.0)
Alignment.bottomLeft The bottom left corner same as Alignment(-1.0, 1.0)
Alignment.bottomRight The bottom right corner same as Alignment(1.0, 1.0)
Alignment.center The center point, both horizontally and vertically same as Alignment(0.0, 0.0)
Alignment.centerLeft The center point along the left edge same as Alignment(-1.0, 0.0)
Alignment.centerRight The center point along the right edge same as Alignment(1.0, 0.0)
Alignment.topCenter The center point along the top edge same as Alignment(0.0, -1.0)
Alignment.topLeft The top left corner same as Alignment(-1.0, -1.0)
Alignment.topRight The top right corner same as Alignment(1.0, -1.0)

Alse you can use FractionalOffset Class with the alignment property.
FractionalOffset and Alignment are two different representations of the same information: the location within a rectangle relative to the size of the rectangle. The difference between the two classes is in the coordinate system they use to represent the location.
FractionalOffset uses a coordinate system with an origin in the top-left corner of the rectangle whereas Alignment uses a coordinate system with an origin in the center of the rectangle.
Center(
child: Container(
color: Color.fromARGB(255, 66, 165, 245),
child: new Text("Flutter Cheatsheet",
style: TextStyle(
fontSize: 20.0
),
),
alignment: FractionalOffset(0.5, 0.5),
),
);
You can also use a constant name with the FractionalOffset Class
FractionalOffset.bottomCenter The center point along the bottom edge same as FractionalOffset(0.5, 1.0)
FractionalOffset.bottomLeft The bottom left corner same as FractionalOffset(0.0, 1.0)
FractionalOffset.bottomRight The bottom right corner same as FractionalOffset(1.0, 1.0)
FractionalOffset.center The center point, both horizontally and vertically same as FractionalOffset(0.5, 0.5)
FractionalOffset.centerLeft The center point along the left edge same as FractionalOffset(0.0, 0.5)
FractionalOffset.centerRight The center point along the right edge same as FractionalOffset(1.0, 0.5)
FractionalOffset.topCenter The center point along the top edge same as FractionalOffset(0.5, 0.0)
FractionalOffset.topLeft The top left corner same as FractionalOffset(0.0, 0.0)
FractionalOffset.topRight The top right corner same as FractionalOffset(1.0, 0.0)

Alse you can use AlignmentDirectional Class with the alignment property.
An offset that’s expressed as a fraction of a Size, but whose horizontal component is dependent on the writing direction.
This can be used to indicate an offset from the left in TextDirection.ltr text and an offset from the right in TextDirection.rtl text without having to be aware of the current text direction.
Center(
child: Container(
color: Color.fromARGB(255, 66, 165, 245),
child: new Text("Flutter Cheatsheet",
style: TextStyle(
fontSize: 20.0
),
),
alignment: AlignmentDirectional(0.0, 0.0),
),
);
You can also use a constant name with the AlignmentDirectional Class
AlignmentDirectional.bottomCenter The center point along the bottom edge same as AlignmentDirectional(0.0, 1.0)
AlignmentDirectional.bottomEnd The bottom corner on the “end” side same as AlignmentDirectional(1.0, 1.0)
AlignmentDirectional.bottomStart The bottom corner on the “start” side same as AlignmentDirectional(-1.0, 1.0)
AlignmentDirectional.center The center point, both horizontally and vertically same as AlignmentDirectional(0.0, 0.0)
AlignmentDirectional.centerEnd The center point along the “end” edge same as AlignmentDirectional(1.0, 0.0)
AlignmentDirectional.centerStart The center point along the “start” edge same as AlignmentDirectional(-1.0, 0.0)
AlignmentDirectional.topCenter The center point along the top edge same as AlignmentDirectional(0.0, -1.0)
AlignmentDirectional.topEnd The top corner on the “end” side same as AlignmentDirectional(1.0, -1.0)
AlignmentDirectional.topEnd The top corner on the “start” side same as AlignmentDirectional(-1.0, -1.0)

Constraints Property

A constraint is just a property specifying the size or space a widget can take up. Most of the widgets and UI can be build by using simple BoxConstraint.
A BoxConstraint only cares about minWidth, maxWidth, minHeight and maxHeight.
Center(
child: Container(
color: Color.fromARGB(255, 66, 165, 245),
alignment: AlignmentDirectional(0.0, 0.0),
child: Container(
color: Colors.green,
constraints: BoxConstraints(
maxHeight: 300.0,
maxWidth: 200.0,
minWidth: 150.0,
minHeight: 150.0
),
),
),
);
As we know before, if the container widget has no child it will automatically fill the given area on the screen, and because we have a max-width and max-height so the container will fill only the area size of the max-width and max-height.
Let’s add a Text widget to the container.
Center(
child: Container(
color: Color.fromARGB(255, 66, 165, 245),
alignment: AlignmentDirectional(0.0, 0.0),
child: Container(
color: Colors.green,
child: Text("Flutter"),
constraints: BoxConstraints(
maxHeight: 300.0,
maxWidth: 200.0,
minWidth: 150.0,
minHeight: 150.0
),
),
),
);
Because there is a child in the container, it will wrap the height & width of the given child element, and because we have a min-width and min-height, so it will take the size given in the BoxConstraints.
Lets try with a long text.
Center(
child: Container(
color: Color.fromARGB(255, 66, 165, 245),
alignment: AlignmentDirectional(0.0, 0.0),
child: Container(
color: Colors.green,
child: Text("Flutter Cheatsheet Flutter Cheatsheet"),
constraints: BoxConstraints(
maxHeight: 300.0,
maxWidth: 200.0,
minWidth: 150.0,
minHeight: 150.0
),
),
),
);
We can see in the screenshot that the container is not able to expend more than the max-width and the max-height.
If we need to expand our container to the maximum size even if it has a child we can use the BoxConstraints.expand()
Center(
child: Container(
color: Color.fromARGB(255, 66, 165, 245),
alignment: AlignmentDirectional(0.0, 0.0),
child: Container(
color: Colors.green,
child: Text("Flutter"),
constraints: BoxConstraints.expand(),
),
),
);
We can see that the the container has not wrapped the height & width of the given child element, it has expended to the maximum.
also you can send a width and height as parameter.
Center(
child: Container(
color: Color.fromARGB(255, 66, 165, 245),
alignment: AlignmentDirectional(0.0, 0.0),
child: Container(
color: Colors.green,
child: Text("Flutter"),
constraints: BoxConstraints.expand(
width: 350.0,
height: 400.0
),
),
),
);

Margin Property

A margin is just a property specifying to add empty space to surround of the container using an EdgeInsets constant value.
EdgeInsets.all()
if you need to add margin on all sides
Center(
child: Container(
color: Color.fromARGB(255, 66, 165, 245),
alignment: AlignmentDirectional(0.0, 0.0),
child: Container(
color: Colors.green,
margin: new EdgeInsets.all(20.0),
),
),
);
EdgeInsets.symmetric()
If you need to add margin with symmetrical vertical and/or horizontal offsets
Center(
child: Container(
color: Color.fromARGB(255, 66, 165, 245),
alignment: AlignmentDirectional(0.0, 0.0),
child: Container(
color: Colors.green,
margin: new EdgeInsets.symmetric(
vertical: 20.0,
horizontal: 50.0
),
),
),
);
EdgeInsets.fromLTRB()
If you need to add margin from offsets from the left, top, right, and bottom.
Center(
child: Container(
color: Color.fromARGB(255, 66, 165, 245),
alignment: AlignmentDirectional(0.0, 0.0),
child: Container(
color: Colors.green,
margin: new EdgeInsets.fromLTRB(20.0, 30.0, 40.0, 50.0),
),
),
);
EdgeInsets.only()
If you need to add margin with only the given values non-zero.
Center(
child: Container(
color: Color.fromARGB(255, 66, 165, 245),
alignment: AlignmentDirectional(0.0, 0.0),
child: Container(
color: Colors.green,
margin: new EdgeInsets.only(
left: 20.0,
bottom: 40.0,
top: 50.0
),
),
),
);

Padding Property

A padding is just a property specifying to add empty space to inscribe inside the container using an EdgeInsets constant value same as the margin.
Center(
child: Container(
color: Color.fromARGB(255, 66, 165, 245),
alignment: AlignmentDirectional(0.0, 0.0),
child: Container(
margin: new EdgeInsets.only(
left: 20.0,
bottom: 40.0,
top: 50.0
),
padding: new EdgeInsets.all(40.0),
color: Colors.green,
child: Text("Flutter Cheatsheet"),
constraints: BoxConstraints.expand(),
),
),
);

Decoration Property

A decoration property can be applied behind the given container.
The value can be
BoxDecoration Class
FlutterLogoDecoration Class
ShapeDecoration Class
UnderlineTabIndicator Class
We will talk about the above class later in a different article

ForegroundDecoration Property

A decoration property can be applied in front of the given container.
The value can be
BoxDecoration Class
FlutterLogoDecoration Class
ShapeDecoration Class
UnderlineTabIndicator Class
We will talk about the above class later in a different article

Transform Property

Perform a transformation on the container when it is laid out. We use Matrix Class as value.
Center(
child: Container(
color: Color.fromARGB(255, 66, 165, 245),
alignment: AlignmentDirectional(0.0, 0.0),
child: Container(
padding: new EdgeInsets.all(40.0),
color: Colors.green,
child: Text("Flutter Cheatsheet"),
transform: new Matrix4.rotationZ(0.5)
),
),
);
We will talk about the Matrix class later in a different article



Expanded
Helping you fill your rows and columns
The basics
If you have some widgets in a row or column, you can hug the children like this:
Row(
children: [
MyWidget(),
MyWidget(),
MyWidget(),
],
),
Or let them relax:
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
MyWidget(),
MyWidget(),
MyWidget(),
],
),
If you want one of the widgets to expand to fill the extra space in the row or column, you can wrap it with an Expanded widget:
Row(
children: [
MyWidget(),
Expanded(
child: MyWidget(),
),
MyWidget(),
],
),
If you have a couple widgets that you want to share the extra space (but not equally) you can set the flex factor:
children: [
MyWidget(),
Expanded(
flex: 2,
child: MyWidget(),
),
MyWidget(),
Expanded(
flex: 1,
child: MyWidget(),
),
],
),
The first and the third widgets have a fixed size. The second and fourth widget in the row both share the extra space because they are wrapped with Expanded. However, since the flex factors are being used, the second widget gets twice as much width as the fourth widget.

Going deeper

Here are a few points to keep in mind:
Extended has to be a descendant of a Flex widget. Otherwise your app will crash. Row and Column are both extend Flex widget so they count.
An Expanded widget is exactly the same thing as a Flexible widget with the fit set to FlexFit.tight. This means that the size is tightly constrained. That is, it must fill the available space. Using Flexible with FlexFit.loose would allow the children to choose their size.

Full code

If you want to play around with the code yourself, here it is:

import 'package:flutter/material.dart';



void main() => runApp(MyApp());



class MyApp extends StatelessWidget {

@override

Widget build(BuildContext context) {

return MaterialApp(

theme: ThemeData(

primarySwatch: Colors.blue,

),

home: Scaffold(

body: ExpandedWidgetDemo(),

),

);

}

}



class ExpandedWidgetDemo extends StatelessWidget {

@override

Widget build(BuildContext context) {

return Center(

child: Container(

color: Colors.blue,

margin: EdgeInsets.all(10),

padding: EdgeInsets.all(10),

child: Row(

children: [

MyWidget(),

Expanded(

flex: 2,

child: MyWidget(),

),

MyWidget(),

Expanded(

flex: 1,

child: MyWidget(),

),

],

),

),

);

}

}



class MyWidget extends StatelessWidget {

@override

Widget build(BuildContext context) {

return Container(

margin: EdgeInsets.all(10),

width: 50,

height: 50,

color: Colors.yellow.shade700,

);

}

}



Center
Suppose that we want to show the logo of our application, at the top and center of the page, like this:
At first, it might seem like an easy task, but there is a lot to learn from it!
Let’s start by implementing the main function, which will instantiate and show the LoginPage widget:
It’d be very likely that we want to add another widget below our logo, like two input boxes for username and password. Therefore, it’s a good idea to start our widget tree with a Column:
So far we’ve got an empty page with “Logo” on top of it, under the status bar:
I can barely see the “Logo” because it’s got hidden under the status bar. Is it possible to tell Flutter that the status bar area is not part of our application? Yes! By wrapping our Scaffold widget in the SafeArea widget:
A widget that insets its child by sufficient padding to avoid intrusions by the operating system. For example, this will indent the child by enough to avoid the status bar at the top of the screen. It will also indent the child by the amount necessary to avoid The Notch on the iPhone X, or other similar creative physical features of the display.
So let’s get it wrapped:
The result:
As you can see, now that the area under the status bar is not part of our application, my phone does not make the status bar transparent, because I don’t need to see what’s under it anymore!
Now let’s center the widget.
Question: We know that the children of a Column widget, are by default center aligned horizontally (along the cross axis). So why is not the “Logo” center aligned in the column?
Answer: It is! The point though is that our Column is as wide as its widest child, hence as wide as the “Logo” here.
It’s possible to see the boundaries of our widgets with a tool called “Flutter Inspector”. If you are using Android Studio, select the “Flutter Inspector” tab from the right edge of the IDE and then press the “Toggle Debug Paint” button:
The Inspector Tool
Now the widget boundaries will become visible to you. It can be easily seen that the Column widget is as wide as its widest child, and there is no doubt the Text widget is in the center of the Column horizontally:
There are several options here to center-align the text on the screen. Let us do it this way:
Wrap the Text widget in another widget that is as wide as the screen,
Align the Text widget in the center of its parent.
The Center widget is very useful here. Note the following point from the documentation:
A widget that centers its child within itself.
Do not misunderstand it:
The Center widget is NOT meant to be in the center of its parent
The child of the Center widget is meant to be placed in the center of the Center widget
Therefore, it would be meaningless for the Center widget to be the same size as its child! The Center widget will always try to expand itself!
Let’s see it in action:
The result with boundaries left visible:
As you can see, the Center widget has become as wide as the screen, and the Text widget is aligned in the center of it.
As I told you, the Center widget will try to be as big as possible. But why it only stretched horizontally? Why didn’t it expand vertically to the bottom of the page? The answer lies in the documentation:
This widget will be as big as possible if its dimensions are constrained and widthFactor and heightFactor are null. If a dimension is unconstrained and the corresponding size factor is null then the widget will match its child’s size in that dimension. If a size factor is non-null then the corresponding dimension of this widget will be the product of the child’s dimension and the size factor. For example if width Factor is 2.0 then the width of this widget will always be twice its child’s width.
Facts:
Constraints are given by parents to their children (these are called Incoming Constraints because they come from the parents!)
Columns are unconstrained or unbounded vertically. This means that Columns tell their children: “You are allowed to take as much as space you can in the vertical direction”.
Columns are constrained or bounded horizontally. This means that Columns tell their children: “You are not allowed to grow freely in the horizontal direction, there is a certain limit to that!”.
Most often, when the Incoming Constraint is unbounded in a given direction, the child widget decides to be as small as possible in that direction. i.e. it decides to shrink itself to match the size of its child. Here, the Column tells its children (including the Center widget), “You have no limitation to grow in the vertical direction, you can have any height you want my children!”. Therefore, the Center widget decides to shrink its height to the height of its child (the Text widget). That’s exactly what the documentation of the Center widget says: “If a dimension is unconstrained and the corresponding size factor is null then the widget will match its child’s size in that dimension.”.
In contrast, when the Incoming Constraint is bounded in a given direction, the child widget will try to be as big as possible in that direction. Here, the Column tells its children (including the Center widget), “You are limited to grow in the horizontal direction, you are only allowed to grow up to the edge of the screen, my children!”. Therefore, the Center widget decides to expand its width as much as the Incoming Constraint allows, up to the edges of the screen. That’s exactly what the documentation of the Center widget says: “This widget will be as big as possible if its dimensions are constrained and widthFactor and heightFactor are null.”.
Now let’s give some margin to the top of our “Logo”. I will do this three ways:
Wrap the Center widget in a Container, and set the top margin for the Container.
2. Add a “SizedBox” with a specific height before the Center widget:
3. Using the heightFactor of the Center widget! Let’s read the documentation of Center widget again:
If a size factor is non-null then the corresponding dimension of this widget will be the product of the child’s dimension and the size factor. For example if widthFactor is 2.0 then the width of this widget will always be twice its child’s width.
This means that if we set heightFactor to 10, the height of the Center widget will become “10 * height of its child” i.e. “10 * height of Text widget”. This will result in some margin above our “Logo”:
The result of which is:
As you can see, the boundaries of the Center widget show that the height of the Center widget is now ten times the height of the “Logo” Text widget. Of course, this is not a good way of adding a top margin because it will depend on the size of your Text. I used this method only to demonstrate the meaning of the “heightFactor” and “widthFactor” parameter.


Hello All,
Wish you a happy new year.
This is my first post in 2019, this article will explain to you how to use LayoutBuilder in your app.

What is Layout Builder

LayoutBuilder is a widget which provides the dimensions of its parent so we can know how much space we have for the widget and can build it our child accordingly
LayoutBuilder widget's builder method provides us BuildContext and BoxConstraints

LayoutBuilder(


builder: (context, constraints) {



},

)

this is how our basic LayoutBuilder widget looks like
BoxConstraints provides us with various options it will give us the maxWidth and maxHeight available for us

constraints.maxWidth; // get Maximum available width


constraints.maxHeight; // get Maximum available height

constraints.minWidth; // get Minimum available width

constraints.minHeight; // get Minimum available height

In order to learn more about BoxConstraints visit here
based on maxWidth we can build our layout
LayoutBuilder(


builder: (context, constraints) {

if(constraints.maxWidth > 600) {

getWideLayout();

} else {

getNormalLayout();

}

},

)



Layouts in Flutter

What's the point?


  • Widgets are classes used to build UIs.
  • Widgets are used for both layout and UI elements.
  • Compose simple widgets to build complex widgets.
The core of Flutter’s layout mechanism is widgets. In Flutter, almost everything is a widget—even layout models are widgets. The images, icons, and text that you see in a Flutter app are all widgets. But things you don’t see are also widgets, such as the rows, columns, and grids that arrange, constrain, and align the visible widgets.
You create a layout by composing widgets to build more complex widgets. For example, the first screenshot below shows 3 icons with a label under each one:
 
The second screenshot displays the visual layout, showing a row of 3 columns where each column contains an icon and a label.
 Note: Most of the screenshots in this tutorial are displayed with debugPaintSizeEnabled set to true so you can see the visual layout. For more information, see Debugging layout issues visually, a section in Using the Flutter inspector.
Here’s a diagram of the widget tree for this UI:
Most of this should look as you might expect, but you might be wondering about the containers (shown in pink). Container is a widget class that allows you to customize its child widget. Use a Container when you want to add padding, margins, borders, or background color, to name some of its capabilities.
In this example, each Text widget is placed in a Container to add margins. The entire Row is also placed in a Container to add padding around the row.
The rest of the UI in this example is controlled by properties. Set an Icon’s color using its color property. Use the Text.style property to set the font, its color, weight, and so on. Columns and rows have properties that allow you to specify how their children are aligned vertically or horizontally, and how much space the children should occupy.

Lay out a widget

How do you layout a single widget in Flutter? This section shows you how to create and display a simple widget. It also shows the entire code for a simple Hello World app.
In Flutter, it takes only a few steps to put text, an icon, or an image on the screen.

1. Select a layout widget

Choose from a variety of layout widgets based on how you want to align or constrain the visible widget, as these characteristics are typically passed on to the contained widget.
This example uses Center which centers its content horizontally and vertically.

2. Create a visible widget

For example, create a Text widget:
Text('Hello World'),
Create an Image widget:
Image.asset(
'images/lake.jpg',
fit: BoxFit.cover,),
Create an Icon widget:
Icon(
Icons.star,
color: Colors.red[500],),

3. Add the visible widget to the layout widget

All layout widgets have either of the following:
  • child property if they take a single child – for example, Center or Container
  • children property if they take a list of widgets – for example, RowColumnListView, or Stack.
Add the Text widget to the Center widget:
Center(
child: Text('Hello World'),),

4. Add the layout widget to the page

A Flutter app is itself a widget, and most widgets have a build() method. Instantiating and returning a widget in the app’s build() method displays the widget.

Material apps

For a Material app, you can use a Scaffold widget; it provides a default banner, background color, and has API for adding drawers, snack bars, and bottom sheets. Then you can add the Center widget directly to the body property for the home page.
lib/main.dart (MyApp)
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter layout demo',
home: Scaffold(
appBar: AppBar(
title: Text('Flutter layout demo'),
),
body: Center(
child: Text('Hello World'),
),
),
);
}}
 Note: The Material library implements widgets that follow Material Design principles. When designing your UI, you can exclusively use widgets from the standard widgets library, or you can use widgets from the Material library. You can mix widgets from both libraries, you can customize existing widgets, or you can build your own set of custom widgets.

Non-Material apps

For a non-Material app, you can add the Center widget to the app’s build() method:
lib/main.dart (MyApp)
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(color: Colors.white),
child: Center(
child: Text(
'Hello World',
textDirection: TextDirection.ltr,
style: TextStyle(
fontSize: 32,
color: Colors.black87,
),
),
),
);
}}
By default a non-Material app doesn’t include an AppBar, title, or background color. If you want these features in a non-Material app, you have to build them yourself. This app changes the background color to white and the text to dark grey to mimic a Material app.
That’s it! When you run the app, you should see Hello World.
App source code:

Lay out multiple widgets vertically and horizontally

One of the most common layout patterns is to arrange widgets vertically or horizontally. You can use a Row widget to arrange widgets horizontally, and a Column widget to arrange widgets vertically.

What's the point?

  • Row and Column are two of the most commonly used layout patterns.
  • Row and Column each take a list of child widgets.
  • A child widget can itself be a Row, Column, or other complex widget.
  • You can specify how a Row or Column aligns its children, both vertically and horizontally.
  • You can stretch or constrain specific child widgets.
  • You can specify how child widgets use the Row’s or Column’s available space.
To create a row or column in Flutter, you add a list of children widgets to a Row or Column widget. In turn, each child can itself be a row or column, and so on. The following example shows how it is possible to nest rows or columns inside of rows or columns.
This layout is organized as a Row. The row contains two children: a column on the left, and an image on the right:
The left column’s widget tree nests rows and columns.
You’ll implement some of Pavlova’s layout code in Nesting rows and columns.
 Note: Row and Column are basic primitive widgets for horizontal and vertical layouts—these low-level widgets allow for maximum customization. Flutter also offers specialized, higher level widgets that might be sufficient for your needs. For example, instead of Row you might prefer ListTile, an easy-to-use widget with properties for leading and trailing icons, and up to 3 lines of text. Instead of Column, you might prefer ListView, a column-like layout that automatically scrolls if its content is too long to fit the available space. For more information, see Common layout widgets.

Aligning widgets

You control how a row or column aligns its children using the mainAxisAlignment and crossAxisAlignment properties. For a row, the main axis runs horizontally and the cross axis runs vertically. For a column, the main axis runs vertically and the cross axis runs horizontally.
 
The MainAxisAlignment and CrossAxisAlignment classes offer a variety of constants for controlling alignment.
 Note: When you add images to your project, you need to update the pubspec.yaml file to access them—this example uses Image.asset to display the images. For more information, see this example’s pubspec.yaml file, or Adding Assets and Images in Flutter. You don’t need to do this if you’re referencing online images using Image.network.
In the following example, each of the 3 images is 100 pixels wide. The render box (in this case, the entire screen) is more than 300 pixels wide, so setting the main axis alignment to spaceEvenly divides the free horizontal space evenly between, before, and after each image.
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset('images/pic1.jpg'),
Image.asset('images/pic2.jpg'),
Image.asset('images/pic3.jpg'),
],);
App source: row_column
Columns work the same way as rows. The following example shows a column of 3 images, each is 100 pixels high. The height of the render box (in this case, the entire screen) is more than 300 pixels, so setting the main axis alignment to spaceEvenly divides the free vertical space evenly between, above, and below each image.
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Image.asset('images/pic1.jpg'),
Image.asset('images/pic2.jpg'),
Image.asset('images/pic3.jpg'),
],);
App source: row_column

Sizing widgets

When a layout is too large to fit a device, a yellow and black striped pattern appears along the affected edge. Here is an example of a row that is too wide:
Widgets can be sized to fit within a row or column by using the Expanded widget. To fix the previous example where the row of images is too wide for its render box, wrap each image with an Expanded widget.
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Image.asset('images/pic1.jpg'),
),
Expanded(
child: Image.asset('images/pic2.jpg'),
),
Expanded(
child: Image.asset('images/pic3.jpg'),
),
],);
App source: sizing
Perhaps you want a widget to occupy twice as much space as its siblings. For this, use the Expanded widget flex property, an integer that determines the flex factor for a widget. The default flex factor is 1. The following code sets the flex factor of the middle image to 2:
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Image.asset('images/pic1.jpg'),
),
Expanded(
flex: 2,
child: Image.asset('images/pic2.jpg'),
),
Expanded(
child: Image.asset('images/pic3.jpg'),
),
],);
App source: sizing

Packing widgets

By default, a row or column occupies as much space along its main axis as possible, but if you want to pack the children closely together, set its mainAxisSize to MainAxisSize.min. The following example uses this property to pack the star icons together.
Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.black),
Icon(Icons.star, color: Colors.black),
],)
App source: pavlova

Nesting rows and columns

The layout framework allows you to nest rows and columns inside of rows and columns as deeply as you need. Let’s look at the code for the outlined section of the following layout:
The outlined section is implemented as two rows. The ratings row contains five stars and the number of reviews. The icons row contains three columns of icons and text.
The widget tree for the ratings row:
The ratings variable creates a row containing a smaller row of 5 star icons, and text:
var stars = Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.black),
Icon(Icons.star, color: Colors.black),
],);

final ratings = Container(
padding: EdgeInsets.all(20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
stars,
Text(
'170 Reviews',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w800,
fontFamily: 'Roboto',
letterSpacing: 0.5,
fontSize: 20,
),
),
],
),);
 Tip: To minimize the visual confusion that can result from heavily nested layout code, implement pieces of the UI in variables and functions.
The icons row, below the ratings row, contains 3 columns; each column contains an icon and two lines of text, as you can see in its widget tree:
The iconList variable defines the icons row:
final descTextStyle = TextStyle(
color: Colors.black,
fontWeight: FontWeight.w800,
fontFamily: 'Roboto',
letterSpacing: 0.5,
fontSize: 18,
height: 2,);

// DefaultTextStyle.merge() allows you to create a default text// style that is inherited by its child and all subsequent children.final iconList = DefaultTextStyle.merge(
style: descTextStyle,
child: Container(
padding: EdgeInsets.all(20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [
Icon(Icons.kitchen, color: Colors.green[500]),
Text('PREP:'),
Text('25 min'),
],
),
Column(
children: [
Icon(Icons.timer, color: Colors.green[500]),
Text('COOK:'),
Text('1 hr'),
],
),
Column(
children: [
Icon(Icons.restaurant, color: Colors.green[500]),
Text('FEEDS:'),
Text('4-6'),
],
),
],
),
),);
The leftColumn variable contains the ratings and icons rows, as well as the title and text that describes the Pavlova:
final leftColumn = Container(
padding: EdgeInsets.fromLTRB(20, 30, 20, 20),
child: Column(
children: [
titleText,
subTitle,
ratings,
iconList,
],
),);
The left column is placed in a Container to constrain its width. Finally, the UI is constructed with the entire row (containing the left column and the image) inside a Card.
The Pavlova image is from Pixabay. You can embed an image from the net using Image.network() but, for this example, the image is saved to an images directory in the project, added to the pubspec file, and accessed using Images.asset(). For more information, see Adding assets and images.
body: Center(
child: Container(
margin: EdgeInsets.fromLTRB(0, 40, 0, 30),
height: 600,
child: Card(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 440,
child: leftColumn,
),
mainImage,
],
),
),
),),
 Tip: The Pavlova example runs best horizontally on a wide device, such as a tablet. If you are running this example in the iOS simulator, you can select a different device using the Hardware > Device menu. For this example, we recommend the iPad Pro. You can change its orientation to landscape mode using Hardware > Rotate. You can also change the size of the simulator window (without changing the number of logical pixels) using Window > Scale.
App source: pavlova

Common layout widgets

Flutter has a rich library of layout widgets. Here are a few of those most commonly used. The intent is to get you up and running as quickly as possible, rather than overwhelm you with a complete list. For information on other available widgets, refer to the Widget catalog, or use the Search box in the API reference docs. Also, the widget pages in the API docs often make suggestions about similar widgets that might better suit your needs.
The following widgets fall into two categories: standard widgets from the widgets library, and specialized widgets from the Material library. Any app can use the widgets library but only Material apps can use the Material Components library.

Standard widgets

  • Container: Adds padding, margins, borders, background color, or other decorations to a widget.
  • GridView: Lays widgets out as a scrollable grid.
  • ListView: Lays widgets out as a scrollable list.
  • Stack: Overlaps a widget on top of another.

Material widgets

  • Card: Organizes related info into a box with rounded corners and a drop shadow.
  • ListTile: Organizes up to 3 lines of text, and optional leading and trailing icons, into a row.

Container

Many layouts make liberal use of Containers to separate widgets using padding, or to add borders or margins. You can change the device’s background by placing the entire layout into a Container and changing its background color or image.

Summary (Container)

  • Add padding, margins, borders
  • Change background color or image
  • Contains a single child widget, but that child can be a Row, Column, or even the root of a widget tree

Examples (Container)

This layout consists of a column with two rows, each containing 2 images. A Container is used to change the background color of the column to a lighter grey.
Widget _buildImageColumn() => Container(
decoration: BoxDecoration(
color: Colors.black26,
),
child: Column(
children: [
_buildImageRow(1),
_buildImageRow(3),
],
),
);
A Container is also used to add a rounded border and margins to each image:
Widget _buildDecoratedImage(int imageIndex) => Expanded(
child: Container(
decoration: BoxDecoration(
border: Border.all(width: 10, color: Colors.black38),
borderRadius: const BorderRadius.all(const Radius.circular(8)),
),
margin: const EdgeInsets.all(4),
child: Image.asset('images/pic$imageIndex.jpg'),
),
);

Widget _buildImageRow(int imageIndex) => Row(
children: [
_buildDecoratedImage(imageIndex),
_buildDecoratedImage(imageIndex + 1),
],
);
You can find more Container examples in the tutorial and the Flutter Gallery.
App source: container

GridView

Use GridView to lay widgets out as a two-dimensional list. GridView provides two pre-fabricated lists, or you can build your own custom grid. When a GridView detects that its contents are too long to fit the render box, it automatically scrolls.

Summary (GridView)

  • Lays widgets out in a grid
  • Detects when the column content exceeds the render box and automatically provides scrolling
  • Build your own custom grid, or use one of the provided grids:
    • GridView.count allows you to specify the number of columns
    • GridView.extent allows you to specify the maximum pixel width of a tile
 Note: When displaying a two-dimensional list where it’s important which row and column a cell occupies (for example, it’s the entry in the “calorie” column for the “avocado” row), use Table or DataTable.

Examples (GridView)

Uses GridView.extent to create a grid with tiles a maximum 150 pixels wide.
App source: grid_and_list
Uses GridView.count to create a grid that’s 2 tiles wide in portrait mode, and 3 tiles wide in landscape mode. The titles are created by setting the footer property for each GridTile.
Dart code: grid_list_demo.dart from the Flutter Gallery
Widget _buildGrid() => GridView.extent(
maxCrossAxisExtent: 150,
padding: const EdgeInsets.all(4),
mainAxisSpacing: 4,
crossAxisSpacing: 4,
children: _buildGridTileList(30));

// The images are saved with names pic0.jpg, pic1.jpg...pic29.jpg.// The List.generate() constructor allows an easy way to create// a list when objects have a predictable naming pattern.List<Container> _buildGridTileList(int count) => List.generate(
count, (i) => Container(child: Image.asset('images/pic$i.jpg')));

ListView

ListView, a column-like widget, automatically provides scrolling when its content is too long for its render box.

Summary (ListView)

  • A specialized Column for organizing a list of boxes
  • Can be laid out horizontally or vertically
  • Detects when its content won’t fit and provides scrolling
  • Less configurable than Column, but easier to use and supports scrolling

Examples (ListView)

Uses ListView to display a list of businesses using ListTiles. A Divider separates the theaters from the restaurants.
App source: grid_and_list
Uses ListView to display the Colors from the Material Design palette for a particular color family.
Dart code: colors_demo.dart from the Flutter Gallery
Widget _buildList() => ListView(
children: [
_tile('CineArts at the Empire', '85 W Portal Ave', Icons.theaters),
_tile('The Castro Theater', '429 Castro St', Icons.theaters),
_tile('Alamo Drafthouse Cinema', '2550 Mission St', Icons.theaters),
_tile('Roxie Theater', '3117 16th St', Icons.theaters),
_tile('United Artists Stonestown Twin', '501 Buckingham Way',
Icons.theaters),
_tile('AMC Metreon 16', '135 4th St #3000', Icons.theaters),
Divider(),
_tile('Kescaped_code#39;s Kitchen', '757 Monterey Blvd', Icons.restaurant),
_tile('Emmyescaped_code#39;s Restaurant', '1923 Ocean Ave', Icons.restaurant),
_tile(
'Chaiya Thai Restaurant', '272 Claremont Blvd', Icons.restaurant),
_tile('La Ciccia', '291 30th St', Icons.restaurant),
],
);

ListTile _tile(String title, String subtitle, IconData icon) => ListTile(
title: Text(title,
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 20,
)),
subtitle: Text(subtitle),
leading: Icon(
icon,
color: Colors.blue[500],
),
);

Stack

Use Stack to arrange widgets on top of a base widget—often an image. The widgets can completely or partially overlap the base widget.

Summary (Stack)

  • Use for widgets that overlap another widget
  • The first widget in the list of children is the base widget; subsequent children are overlaid on top of that base widget
  • Stack’s content can’t scroll
  • You can choose to clip children that exceed the render box

Examples (Stack)

Uses Stack to overlay a Container (that displays its Text on a translucent black background) on top of a CircleAvatar. The Stack offsets the text using the alignment property and Alignments.
App source: card_and_stack
Uses Stack to overlay a gradient to the top of the image. The gradient ensures that the toolbar’s icons are distinct against the image.
Dart code: contacts_demo.dart from the Flutter Gallery
Widget _buildStack() => Stack(
alignment: const Alignment(0.6, 0.6),
children: [
CircleAvatar(
backgroundImage: AssetImage('images/pic.jpg'),
radius: 100,
),
Container(
decoration: BoxDecoration(
color: Colors.black45,
),
child: Text(
'Mia B',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
],
);

Card

A Card, from the Material library, contains related nuggets of information and can be composed from almost any widget, but is often used with ListTile. Card has a single child, but its child can be a column, row, list, grid, or other widget that supports multiple children. By default, a Card shrinks its size to 0 by 0 pixels. You can use SizedBox to constrain the size of a card.
In Flutter, a Card features slightly rounded corners and a drop shadow, giving it a 3D effect. Changing a Card’s elevation property allows you to control the drop shadow effect. Setting the elevation to 24, for example, visually lifts the Card further from the surface and causes the shadow to become more dispersed. For a list of supported elevation values, see Elevation in the Material guidelines. Specifying an unsupported value disables the drop shadow entirely.

Summary (Card)

  • Implements a Material card
  • Used for presenting related nuggets of information
  • Accepts a single child, but that child can be a RowColumn, or other widget that holds a list of children
  • Displayed with rounded corners and a drop shadow
  • Card’s content can’t scroll

Examples (Card)

A Card containing 3 ListTiles and sized by wrapping it with a SizedBox. A Divider separates the first and second ListTiles.
App source: card_and_stack
A Card containing an image and text.
Dart code: cards_demo.dart from the Flutter Gallery
Widget _buildCard() => SizedBox(
height: 210,
child: Card(
child: Column(
children: [
ListTile(
title: Text('1625 Main Street',
style: TextStyle(fontWeight: FontWeight.w500)),
subtitle: Text('My City, CA 99984'),
leading: Icon(
Icons.restaurant_menu,
color: Colors.blue[500],
),
),
Divider(),
ListTile(
title: Text('(408) 555-1212',
style: TextStyle(fontWeight: FontWeight.w500)),
leading: Icon(
Icons.contact_phone,
color: Colors.blue[500],
),
),
ListTile(
title: Text('costa@example.com'),
leading: Icon(
Icons.contact_mail,
color: Colors.blue[500],
),
),
],
),
),
);

ListTile

Use ListTile, a specialized row widget from the Material library, for an easy way to create a row containing up to 3 lines of text and optional leading and trailing icons. ListTile is most commonly used in Card or ListView, but can be used elsewhere.

Summary (ListTile)

  • A specialized row that contains up to 3 lines of text and optional icons
  • Less configurable than Row, but easier to use

Examples (ListTile)

A Card containing 3 ListTiles.
App source: card_and_stack

Uses ListTile to list 3 drop down button types.
Dart code: buttons_demo.dart from the Flutter Gallery


Table of Contents

Row and Column
IntrinsicWidth and IntrinsicHeight
Stack
Expanded
ConstrainedBox
Container
decoration: BoxDecoration
• image: DecorationImage
• border: Border
• borderRadius: BorderRadius
• shape: BoxShape
• boxShadow: List<BoxShadow>
• gradient: RadialGradient
• backgroundBlendMode: BlendMode
Material
• shape: BeveledRectangleBorder
Slivers
• SliverFillRemaining
SizedBox
SafeArea

Row and Column

MainAxisAlignment

Row /*or Column*/(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 50),
],
),

Row /*or Column*/(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 50),
],
),

Row /*or Column*/(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 50),
],
),

Row /*or Column*/(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 50),
],
),

Row /*or Column*/(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 50),
],
),

Row /*or Column*/(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 50),
],
),

You should use CrossAxisAlignment.baseline if you require for the baseline of different text be aligned.
Row(
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
children: <Widget>[
Text(
'Baseline',
style: Theme.of(context).textTheme.display3,
),
Text(
'Baseline',
style: Theme.of(context).textTheme.body1,
),
],
),

CrossAxisAlignment

Row /*or Column*/(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 200),
Icon(Icons.star, size: 50),
],
),

Row /*or Column*/(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 200),
Icon(Icons.star, size: 50),
],
),

Row /*or Column*/(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 200),
Icon(Icons.star, size: 50),
],
),

Row /*or Column*/(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 200),
Icon(Icons.star, size: 50),
],
),

MainAxisSize

Row /*or Column*/(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 50),
],
),

Row /*or Column*/(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 50),
],
),

IntrinsicWidth and IntrinsicHeight

Want all the widgets inside Row or Column to be as tall/wide as the tallest/widest widget? Search no more!
In case you have this kind of layout:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('IntrinsicWidth')),
body: Center(
child: Column(
children: <Widget>[
RaisedButton(
onPressed: () {},
child: Text('Short'),
),
RaisedButton(
onPressed: () {},
child: Text('A bit Longer'),
),
RaisedButton(
onPressed: () {},
child: Text('The Longest text button'),
),
],
),
),
);
}
But you would like to have all buttons as wide as the widest, just use IntrinsicWidth :
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('IntrinsicWidth')),
body: Center(
child: IntrinsicWidth(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
RaisedButton(
onPressed: () {},
child: Text('Short'),
),
RaisedButton(
onPressed: () {},
child: Text('A bit Longer'),
),
RaisedButton(
onPressed: () {},
child: Text('The Longest text button'),
),
],
),
),
),
);
}
In case you have a similar problem but you would like to have all the widgets as tall as the tallest just use a combination of IntrinsicHeight and Row widgets.

Stack

Perfect for overlaying Widgets on top of each other
@override
Widget build(BuildContext context) {
Widget main = Scaffold(
appBar: AppBar(title: Text('Stack')),
);

return Stack(
fit: StackFit.expand,
children: <Widget>[
main,
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,
),
],
);
}

With your own Widgets, you need to place them in Positioned Widget
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Stack')),
body: Stack(
fit: StackFit.expand,
children: <Widget>[
Material(color: Colors.yellowAccent),
Positioned(
top: 0,
left: 0,
child: Icon(Icons.star, size: 50),
),
Positioned(
top: 340,
left: 250,
child: Icon(Icons.call, size: 50),
),
],
),
);
}

If you don’t want to guess the top/bottom values you can use LayoutBuilder to retrieve them
Widget build(BuildContext context) {
const iconSize = 50;
return Scaffold(
appBar: AppBar(title: Text('Stack with LayoutBuilder')),
body: LayoutBuilder(
builder: (context, constraints) =>
Stack(
fit: StackFit.expand,
children: <Widget>[
Material(color: Colors.yellowAccent),
Positioned(
top: 0,
child: Icon(Icons.star, size: iconSize),
),
Positioned(
top: constraints.maxHeight - iconSize,
left: constraints.maxWidth - iconSize,
child: Icon(Icons.call, size: iconSize),
),
],
),
),
);
}

Expanded

Expanded works with Flex\Flexbox layout and is great for distributing space between multiple items.
Row(
children: <Widget>[
Expanded(
child: Container(
decoration: const BoxDecoration(color: Colors.red),
),
flex: 3,
),
Expanded(
child: Container(
decoration: const BoxDecoration(color: Colors.green),
),
flex: 2,
),
Expanded(
child: Container(
decoration: const BoxDecoration(color: Colors.blue),
),
flex: 1,
),
],
),

ConstrainedBox

By default, most of the widgets will use as little space as possible:
Card(child: const Text('Hello World!'), color: Colors.yellow)

ConstrainedBox allows a widget to use the remaining space as desired.
ConstrainedBox(
constraints: BoxConstraints.expand(),
child: const Card(
child: const Text('Hello World!'),
color: Colors.yellow,
),
),

Using BoxConstraints you specify how much space a widget can have — you specify min/max of height/width.
BoxConstraints.expand uses infinite (all the available) amount of space unless specified:
ConstrainedBox(
constraints: BoxConstraints.expand(height: 300),
child: const Card(
child: const Text('Hello World!'),
color: Colors.yellow,
),
),
And it’s the same as:
ConstrainedBox(
constraints: BoxConstraints(
minWidth: double.infinity,
maxWidth: double.infinity,
minHeight: 300,
maxHeight: 300,
),
child: const Card(
child: const Text('Hello World!'),
color: Colors.yellow,
),
),

Container

One of the most used Widgets — and for good reasons:

Container as a layout tool

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

Container as decoration

You can use color property to affect Container’s background but decoration and foregroundDecoration. (With those two properties, you can completely change how Container looks like but I will be talking about different decorations later as it quite a big topic)
decoration is always placed behind the child, whereas foregroundDecoration is on top of the child
decoration
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Container.decoration')),
body: Container(
height: double.infinity,
width: double.infinity,
decoration: BoxDecoration(color: Colors.yellowAccent),
child: Text("Hi"),
),
);
}

decoration and foregroundDecoration
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Container.foregroundDecoration')),
body: Container(
height: double.infinity,
width: double.infinity,
decoration: BoxDecoration(color: Colors.yellowAccent),
foregroundDecoration: BoxDecoration(
color: Colors.red.withOpacity(0.5),
),
child: Text("Hi"),
),
);
}

Container as Transform

If you don’t want to use Transform widget to change your layout, you can use transform property straight from the Container
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Container.transform')),
body: Container(
height: 300,
width: 300,
transform: Matrix4.rotationZ(pi / 4),
decoration: BoxDecoration(color: Colors.yellowAccent),
child: Text(
"Hi",
textAlign: TextAlign.center,
),
),
);
}

BoxDecoration

The decoration is usually used on a Container widget to change how the container looks.

image: DecorationImage

Puts an image as a background:
Scaffold(
appBar: AppBar(title: Text('image: DecorationImage')),
body: Center(
child: Container(
height: 200,
width: 200,
decoration: BoxDecoration(
color: Colors.yellow,
image: DecorationImage(
fit: BoxFit.fitWidth,
image: NetworkImage(
),
),
),
),
),
);

border: Border

Specifies how should the border of the Container look like.
Scaffold(
appBar: AppBar(title: Text('border: Border')),
body: Center(
child: Container(
height: 200,
width: 200,
decoration: BoxDecoration(
color: Colors.yellow,
border: Border.all(color: Colors.black, width: 3),
),
),
),
);

borderRadius: BorderRadius

Enables border corners to be rounded.
borderRadius does not work if the shape of the decoration is BoxShape.circle
Scaffold(
appBar: AppBar(title: Text('borderRadius: BorderRadius')),
body: Center(
child: Container(
height: 200,
width: 200,
decoration: BoxDecoration(
color: Colors.yellow,
border: Border.all(color: Colors.black, width: 3),
borderRadius: BorderRadius.all(Radius.circular(18)),
),
),
),
);

shape: BoxShape

Box decoration can be either a rectangle/square or an ellipse/circle.
For any other shape, you can use ShapeDecoration instead of BoxDecoration
Scaffold(
appBar: AppBar(title: Text('shape: BoxShape')),
body: Center(
child: Container(
height: 200,
width: 200,
decoration: BoxDecoration(
color: Colors.yellow,
shape: BoxShape.circle,
),
),
),
);

boxShadow: List<BoxShadow>

Adds shadow to the Container.
This parameter is a list because you can specify multiple different shadows and merge them together.
Scaffold(
appBar: AppBar(title: Text('boxShadow: List<BoxShadow>')),
body: Center(
child: Container(
height: 200,
width: 200,
decoration: BoxDecoration(
color: Colors.yellow,
boxShadow: const [
BoxShadow(blurRadius: 10),
],
),
),
),
);

gradient

There are three types of gradients: LinearGradient, RadialGradient and SweepGradient.
LinearGradient
Scaffold(
appBar: AppBar(title: Text('gradient: LinearGradient')),
body: Center(
child: Container(
height: 200,
width: 200,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: const [
Colors.red,
Colors.blue,
],
),
),
),
),
);

RadialGradient
Scaffold(
appBar: AppBar(title: Text('gradient: RadialGradient')),
body: Center(
child: Container(
height: 200,
width: 200,
decoration: BoxDecoration(
gradient: RadialGradient(
colors: const [Colors.yellow, Colors.blue],
stops: const [0.4, 1.0],
),
),
),
),
);

SweepGradient
Scaffold(
appBar: AppBar(title: Text('gradient: SweepGradient')),
body: Center(
child: Container(
height: 200,
width: 200,
decoration: BoxDecoration(
gradient: SweepGradient(
colors: const [
Colors.blue,
Colors.green,
Colors.yellow,
Colors.red,
Colors.blue,
],
stops: const [0.0, 0.25, 0.5, 0.75, 1.0],
),
),
),
),
);

backgroundBlendMode

backgroundBlendMode is the most complex property of BoxDecoration.
It’s responsible for mixing together colors/gradients of BoxDecoration and whatever BoxDecoration is on top of.
With backgroundBlendMode you can use a long list of algorithms specified in BlendMode enum.
First, let’s set BoxDecoration as foregroundDecoration which is drawn on top of Container’s child (whereas decoration is drawn behind the child).
Scaffold(
appBar: AppBar(title: Text('backgroundBlendMode')),
body: Center(
child: Container(
height: 200,
width: 200,
foregroundDecoration: BoxDecoration(
backgroundBlendMode: BlendMode.exclusion,
gradient: LinearGradient(
colors: const [
Colors.red,
Colors.blue,
],
),
),
),
),
),
);
backgroundBlendMode does not affect only the Container it’s located in.

backgroundBlendMode changes the color of anything that is up the widget tree from the Container.
The following code has a parent Container that draws an image and child Container that uses backgroundBlendMode. Still, you would get the same effect as previously.
Scaffold(
appBar: AppBar(title: Text('backgroundBlendMode')),
body: Center(
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
),
),
),
child: Container(
height: 200,
width: 200,
foregroundDecoration: BoxDecoration(
backgroundBlendMode: BlendMode.exclusion,
gradient: LinearGradient(
colors: const [
Colors.red,
Colors.blue,
],
),
),
),
),
),
);

Material

Border with cut corners
Scaffold(
appBar: AppBar(title: Text('shape: BeveledRectangleBorder')),
body: Center(
child: Material(
shape: const BeveledRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(20)),
side: BorderSide(color: Colors.black, width: 4),
),
color: Colors.yellow,
child: Container(
height: 200,
width: 200,
),
),
),
);

Slivers

SliverFillRemaining

This Widget is irreplaceable when you want to center your content even if there is not enough space for it. Interactive example
Enough vertical space
Scaffold(
appBar: AppBar(title: Text('SliverFillRemaining')),
body: CustomScrollView(
slivers: [
SliverFillRemaining(
hasScrollBody: false,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
FlutterLogo(size: 200),
Text(
'This is some longest text that should be centered'
'together with the logo',
textAlign: TextAlign.center,
),
],
),
),
],
),
);
In case there is no enough space for the centred content, SliverFillRemaining will become scrollable:
Not enough vertical space
If it was not for SliverFillRemaining , the content would overflow like this:
Not enough space without SliverFillRemaining

Filling the remaining space

Apart from being useful for centering your content, SliverFillRemaining will fill the remainings viewport’s free space. To do that this widget has to be placed in CustomScrollView and needs to be the last sliver
In case there is not enough space, the widget becomes scrollable:
Scaffold(
appBar: AppBar(title: Text('SliverFillRemaining')),
body: CustomScrollView(
slivers: [
SliverList(
delegate: SliverChildListDelegate(const [
ListTile(title: Text('First item')),
ListTile(title: Text('Second item')),
ListTile(title: Text('Third item')),
ListTile(title: Text('Fourth item')),
]),
),
SliverFillRemaining(
hasScrollBody: false,
child: Container(
color: Colors.yellowAccent,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
FlutterLogo(size: 200),
Text(
'This is some longest text that should be centered'
'together with the logo',
textAlign: TextAlign.center,
),
],
),
),
),
],
),
);

SizedBox

It’s one of the simplest but most useful Widgets

SizedBox as ConstrainedBox

SizedBox can work in a similar fashion as ConstrainedBox
SizedBox.expand(
child: Card(
child: Text('Hello World!'),
color: Colors.yellowAccent,
),
),

SizedBox as padding

When in need of adding padding or margin, you might choose Padding or Container widgets. But they can be more verbose and less readable than adding a Sizedbox
Column(
children: <Widget>[
Icon(Icons.star, size: 50),
const SizedBox(height: 100),
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 50),
],
),

SizedBox as an Invisible Object

Many time you would like to hide/show a widget depending on a bool
Widget build(BuildContext context) {
bool isVisible = ...
return Scaffold(
appBar: AppBar(
title: Text('isVisible = $isVisible'),
),
body: isVisible
? Icon(Icons.star, size: 150)
: const SizedBox(),
);
}
Because SizedBox has a const constructor, using const SizedBox() is really cheap**.
** One cheaper solution would be to use Opacity widget and change the opacity value to 0.0 . The drawback of this solution is that the given widget would be only invisible, still would occupy the space.

SafeArea

On different platforms, there are special areas like Status Bar on Android or the Notch on iPhone X that we might avoid drawing under.
The solution to this problem is SafeArea widget (example without/with SafeArea)
Widget build(BuildContext context) {
return Material(
color: Colors.blue,
child: SafeArea(
child: SizedBox.expand(
child: Card(color: Colors.yellowAccent),
),
),
);
}



Adding interactivity to your Flutter app

What you’ll learn


  • How to respond to taps.
  • How to create a custom widget.
  • The difference between stateless and stateful widgets.
How do you modify your app to make it react to user input? In this tutorial, you’ll add interactivity to an app that contains only non-interactive widgets. Specifically, you’ll modify an icon to make it tappable by creating a custom stateful widget that manages two stateless widgets.
Layout tutorial showed you how to create the layout for the following screenshot.
The layout tutorial app
When the app first launches, the star is solid red, indicating that this lake has previously been favorited. The number next to the star indicates that 41 people have favorited this lake. After completing this tutorial, tapping the star removes its favorited status, replacing the solid star with an outline and decreasing the count. Tapping again favorites the lake, drawing a solid star and increasing the count.
To accomplish this, you’ll create a single custom widget that includes both the star and the count, which are themselves widgets. Tapping the star changes state for both widgets, so the same widget should manage both.
You can get right to touching the code in Step 2: Subclass StatefulWidget. If you want to try different ways of managing state, skip to Managing state.

Stateful and stateless widgets

A widget is either stateful or stateless. If a widget can change—when a user interacts with it, for example—it’s stateful.
A stateless widget never changes. Icon, IconButton, and Text are examples of stateless widgets. Stateless widgets subclass StatelessWidget.
A stateful widget is dynamic: for example, it can change its appearance in response to events triggered by user interactions or when it receives data. Checkbox, Radio, Slider, InkWell, Form, and TextField are examples of stateful widgets. Stateful widgets subclass StatefulWidget.
A widget’s state is stored in a State object, separating the widget’s state from its appearance. The state consists of values that can change, like a slider’s current value or whether a checkbox is checked. When the widget’s state changes, the state object calls setState(), telling the framework to redraw the widget.

Creating a stateful widget

What's the point?

  • A stateful widget is implemented by two classes: a subclass of StatefulWidget and a subclass of State.
  • The state class contains the widget’s mutable state and the widget’s build() method.
  • When the widget’s state changes, the state object calls setState(), telling the framework to redraw the widget.
In this section, you’ll create a custom stateful widget. You’ll replace two stateless widgets—the solid red star and the numeric count next to it—with a single custom stateful widget that manages a row with two children widgets: an IconButton and Text.
Implementing a custom stateful widget requires creating two classes:
  • A subclass of StatefulWidget that defines the widget.
  • A subclass of State that contains the state for that widget and defines the widget’s build() method.
This section shows you how to build a stateful widget, called FavoriteWidget, for the lakes app. After setting up, your first step is choosing how state is managed for FavoriteWidget.

Step 0: Get ready

If you’ve already built the app in Layout tutorial (step 6), skip to the next section.
  1. Make sure you’ve set up your environment.
  2. Replace the lib/main.dart file with main.dart.
  3. Replace the pubspec.yaml file with pubspec.yaml.
  4. Create an images directory in your project, and add lake.jpg.
Once you have a connected and enabled device, or you’ve launched the iOS simulator (part of the Flutter install), you are good to go!

Step 1: Decide which object manages the widget’s state

A widget’s state can be managed in several ways, but in our example the widget itself, FavoriteWidget, will manage its own state. In this example, toggling the star is an isolated action that doesn’t affect the parent widget or the rest of the UI, so the widget can handle its state internally.
Learn more about the separation of widget and state, and how state might be managed, in Managing state.

Step 2: Subclass StatefulWidget

The FavoriteWidget class manages its own state, so it overrides createState() to create a State object. The framework calls createState() when it wants to build the widget. In this example, createState() returns an instance of _FavoriteWidgetState, which you’ll implement in the next step.
lib/main.dart (FavoriteWidget)
class FavoriteWidget extends StatefulWidget {
@override
_FavoriteWidgetState createState() => _FavoriteWidgetState();}
 Note: Members or classes that start with an underscore (_) are private. For more information, see Libraries and visibility, a section in the Dart language tour.

Step 3: Subclass State

The _FavoriteWidgetState class stores the mutable data that can change over the lifetime of the widget. When the app first launches, the UI displays a solid red star, indicating that the lake has “favorite” status, along with 41 likes. These values are stored in the _isFavorited and _favoriteCount fields:
lib/main.dart (_FavoriteWidgetState fields)
class _FavoriteWidgetState extends State<FavoriteWidget> {
bool _isFavorited = true;
int _favoriteCount = 41;
// ···}
The class also defines a build() method, which creates a row containing a red IconButton, and Text. You use IconButton (instead of Icon) because it has an onPressed property that defines the callback function (_toggleFavorite) for handling a tap. You’ll define the callback function next.
lib/main.dart (_FavoriteWidgetState build)
class _FavoriteWidgetState extends State<FavoriteWidget> {
// ···
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: EdgeInsets.all(0),
child: IconButton(
icon: (_isFavorited ? Icon(Icons.star) : Icon(Icons.star_border)),
color: Colors.red[500],
onPressed: _toggleFavorite,
),
),
SizedBox(
width: 18,
child: Container(
child: Text('$_favoriteCount'),
),
),
],
);
}}
 Tip: Placing the Text in a SizedBox and setting its width prevents a discernible “jump” when the text changes between the values of 40 and 41 — a jump would otherwise occur because those values have different widths.
The _toggleFavorite() method, which is called when the IconButton is pressed, calls setState(). Calling setState() is critical, because this tells the framework that the widget’s state has changed and that the widget should be redrawn. The function argument to setState() toggles the UI between these two states:
  • star icon and the number 41
  • star_border icon and the number 40
void _toggleFavorite() {
setState(() {
if (_isFavorited) {
_favoriteCount -= 1;
_isFavorited = false;
} else {
_favoriteCount += 1;
_isFavorited = true;
}
});}

Step 4: Plug the stateful widget into the widget tree

Add your custom stateful widget to the widget tree in the app’s build() method. First, locate the code that creates the Icon and Text, and delete it. In the same location, create the stateful widget:
layout/lakes/{step6 → interactive}/lib/main.dart


@@ -10,2 +5,2 @@
10
5
  class MyApp extends StatelessWidget {
11
6
   @override

@@ -38,11 +33,7 @@
38
33
   ],
39
34
   ),
40
35
   ),
41
-  Icon(
36
+  FavoriteWidget(),
42
-  Icons.star,
43
-  color: Colors.red[500],
44
-  ),
45
-  Text('41'),
46
37
   ],
47
38
   ),
48
39
   );

@@ -114,3 +105,5 @@
114
105
   ),
115
106
   ),
116
107
   ],
108
+  );
109
+  }

That’s it! When you hot reload the app, the star icon should now respond to taps.

Problems?

If you can’t get your code to run, look in your IDE for possible errors. Debugging Flutter Apps might help. If you still can’t find the problem, check your code against the interactive lakes example on GitHub.
If you still have questions, refer to any one of the developer community channels.

The rest of this page covers several ways a widget’s state can be managed, and lists other available interactive widgets.

Managing state

What's the point?

  • There are different approaches for managing state.
  • You, as the widget designer, choose which approach to use.
  • If in doubt, start by managing state in the parent widget.
Who manages the stateful widget’s state? The widget itself? The parent widget? Both? Another object? The answer is… it depends. There are several valid ways to make your widget interactive. You, as the widget designer, make the decision based on how you expect your widget to be used. Here are the most common ways to manage state:
How do you decide which approach to use? The following principles should help you decide:
  • If the state in question is user data, for example the checked or unchecked mode of a checkbox, or the position of a slider, then the state is best managed by the parent widget.
  • If the state in question is aesthetic, for example an animation, then the state is best managed by the widget itself.
If in doubt, start by managing state in the parent widget.
We’ll give examples of the different ways of managing state by creating three simple examples: TapboxA, TapboxB, and TapboxC. The examples all work similarly—each creates a container that, when tapped, toggles between a green or grey box. The _active boolean determines the color: green for active or grey for inactive.
 
These examples use GestureDetector to capture activity on the Container.

The widget manages its own state

Sometimes it makes the most sense for the widget to manage its state internally. For example, ListView automatically scrolls when its content exceeds the render box. Most developers using ListView don’t want to manage ListView’s scrolling behavior, so ListView itself manages its scroll offset.
The _TapboxAState class:
  • Manages state for TapboxA.
  • Defines the _active boolean which determines the box’s current color.
  • Defines the _handleTap() function, which updates _active when the box is tapped and calls the setState() function to update the UI.
  • Implements all interactive behavior for the widget.
// TapboxA manages its own state.

//------------------------- TapboxA ----------------------------------

class TapboxA extends StatefulWidget {
TapboxA({Key key}) : super(key: key);

@override
_TapboxAState createState() => _TapboxAState();}

class _TapboxAState extends State<TapboxA> {
bool _active = false;

void _handleTap() {
setState(() {
_active = !_active;
});
}

Widget build(BuildContext context) {
return GestureDetector(
onTap: _handleTap,
child: Container(
child: Center(
child: Text(
_active ? 'Active' : 'Inactive',
style: TextStyle(fontSize: 32.0, color: Colors.white),
),
),
width: 200.0,
height: 200.0,
decoration: BoxDecoration(
color: _active ? Colors.lightGreen[700] : Colors.grey[600],
),
),
);
}}

//------------------------- MyApp ----------------------------------

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: Text('Flutter Demo'),
),
body: Center(
child: TapboxA(),
),
),
);
}}

The parent widget manages the widget’s state

Often it makes the most sense for the parent widget to manage the state and tell its child widget when to update. For example, IconButton allows you to treat an icon as a tappable button. IconButton is a stateless widget because we decided that the parent widget needs to know whether the button has been tapped, so it can take appropriate action.
In the following example, TapboxB exports its state to its parent through a callback. Because TapboxB doesn’t manage any state, it subclasses StatelessWidget.
The ParentWidgetState class:
  • Manages the _active state for TapboxB.
  • Implements _handleTapboxChanged(), the method called when the box is tapped.
  • When the state changes, calls setState() to update the UI.
The TapboxB class:
  • Extends StatelessWidget because all state is handled by its parent.
  • When a tap is detected, it notifies the parent.
// ParentWidget manages the state for TapboxB.

//------------------------ ParentWidget --------------------------------

class ParentWidget extends StatefulWidget {
@override
_ParentWidgetState createState() => _ParentWidgetState();}

class _ParentWidgetState extends State<ParentWidget> {
bool _active = false;

void _handleTapboxChanged(bool newValue) {
setState(() {
_active = newValue;
});
}

@override
Widget build(BuildContext context) {
return Container(
child: TapboxB(
active: _active,
onChanged: _handleTapboxChanged,
),
);
}}

//------------------------- TapboxB ----------------------------------

class TapboxB extends StatelessWidget {
TapboxB({Key key, this.active: false, @required this.onChanged})
: super(key: key);

final bool active;
final ValueChanged<bool> onChanged;

void _handleTap() {
onChanged(!active);
}

Widget build(BuildContext context) {
return GestureDetector(
onTap: _handleTap,
child: Container(
child: Center(
child: Text(
active ? 'Active' : 'Inactive',
style: TextStyle(fontSize: 32.0, color: Colors.white),
),
),
width: 200.0,
height: 200.0,
decoration: BoxDecoration(
color: active ? Colors.lightGreen[700] : Colors.grey[600],
),
),
);
}}
 Tip: When creating API, consider using the @required annotation for any parameters that your code relies on. To use @required, import the foundation library (which re-exports Dart’s meta.dart library):
import 'package:flutter/foundation.dart';

A mix-and-match approach

For some widgets, a mix-and-match approach makes the most sense. In this scenario, the stateful widget manages some of the state, and the parent widget manages other aspects of the state.
In the TapboxC example, on tap down, a dark green border appears around the box. On tap up, the border disappears and the box’s color changes. TapboxC exports its _active state to its parent but manages its _highlight state internally. This example has two State objects, _ParentWidgetState and _TapboxCState.
The _ParentWidgetState object:
  • Manages the _active state.
  • Implements _handleTapboxChanged(), the method called when the box is tapped.
  • Calls setState() to update the UI when a tap occurs and the _active state changes.
The _TapboxCState object:
  • Manages the _highlight state.
  • The GestureDetector listens to all tap events. As the user taps down, it adds the highlight (implemented as a dark green border). As the user releases the tap, it removes the highlight.
  • Calls setState() to update the UI on tap down, tap up, or tap cancel, and the _highlight state changes.
  • On a tap event, passes that state change to the parent widget to take appropriate action using the widget property.
//---------------------------- ParentWidget ----------------------------

class ParentWidget extends StatefulWidget {
@override
_ParentWidgetState createState() => _ParentWidgetState();}

class _ParentWidgetState extends State<ParentWidget> {
bool _active = false;

void _handleTapboxChanged(bool newValue) {
setState(() {
_active = newValue;
});
}

@override
Widget build(BuildContext context) {
return Container(
child: TapboxC(
active: _active,
onChanged: _handleTapboxChanged,
),
);
}}

//----------------------------- TapboxC ------------------------------

class TapboxC extends StatefulWidget {
TapboxC({Key key, this.active: false, @required this.onChanged})
: super(key: key);

final bool active;
final ValueChanged<bool> onChanged;

_TapboxCState createState() => _TapboxCState();}

class _TapboxCState extends State<TapboxC> {
bool _highlight = false;

void _handleTapDown(TapDownDetails details) {
setState(() {
_highlight = true;
});
}

void _handleTapUp(TapUpDetails details) {
setState(() {
_highlight = false;
});
}

void _handleTapCancel() {
setState(() {
_highlight = false;
});
}

void _handleTap() {
widget.onChanged(!widget.active);
}

Widget build(BuildContext context) {
// This example adds a green border on tap down.
// On tap up, the square changes to the opposite state.
return GestureDetector(
onTapDown: _handleTapDown, // Handle the tap events in the order that
onTapUp: _handleTapUp, // they occur: down, up, tap, cancel
onTap: _handleTap,
onTapCancel: _handleTapCancel,
child: Container(
child: Center(
child: Text(widget.active ? 'Active' : 'Inactive',
style: TextStyle(fontSize: 32.0, color: Colors.white)),
),
width: 200.0,
height: 200.0,
decoration: BoxDecoration(
color:
widget.active ? Colors.lightGreen[700] : Colors.grey[600],
border: _highlight
? Border.all(
color: Colors.teal[700],
width: 10.0,
)
: null,
),
),
);
}}
An alternate implementation might have exported the highlight state to the parent while keeping the active state internal, but if you asked someone to use that tap box, they’d probably complain that it doesn’t make much sense. The developer cares whether the box is active. The developer probably doesn’t care how the highlighting is managed, and prefers that the tap box handles those details.

Other interactive widgets

Flutter offers a variety of buttons and similar interactive widgets. Most of these widgets implement the Material Design guidelines, which define a set of components with an opinionated UI.
If you prefer, you can use GestureDetector to build interactivity into any custom widget. You can find examples of GestureDetector in Managing state, and in the Flutter Gallery.
 Tip: Flutter also provides a set of iOS-style widgets called Cupertino.
When you need interactivity, it’s easiest to use one of the prefabricated widgets. Here’s a partial list:

Standard widgets

Material Components

Comments