ARTICLE
All About Flutter Widgets
From Flutter in Action by Eric Windmill
This article is a crash course on widgets in Flutter.
__________________________________________________________________
Take 37% off Flutter in Action. Just enter fccwindmill into the discount code box at checkout at manning.com.
__________________________________________________________________
Widgets: The widget tree, widget types and the State object
In general, Flutter is made of a handful of widget types. The two which we care about right now are the base of all other widget types — StatelessWidget
and StatefulWidget
.
The general goal when developing UI in a Flutter app is to compose a ton of widgets together to build the widget tree. A Flutter app is represented by a widget tree, similar to the DOM on the browser, which is also a tree-structure.
Here’s a simple visual representation of the widget tree for the counter app:
The process of composing widgets together is done by telling widgets that their child (or children) are more widgets. A simple example is styling some text:
return Container( child: Padding( ①
padding: EdgeInsets.all(8.0),
child: Text("Padded Text") ②
),
);
① The container widget has a property called child
, which takes another widget.
② The Padding widget has a property called child
also, which takes a widget.
Other common properties in Flutter that allow you to pass widgets into widgets are children
and builder
.
Stateless Widget
The difference between a StatefulWidget
and a StatelessWidget
is right in the name. A StatefulWidget
tracks its own internal state. A StatelessWidget
is a “dumb” widget. It doesn’t care about its configuration or what data it’s displaying. It could be passed a configuration from its parent, or the configuration could be defined within the widget, but it can’t change its own configuration. A stateless widget’s immutable.
When it comes to widget jargon, you’ll see the word configuration often.
NOTE: it’s vague, but it encapsulates everything: namely the variables passed in and its size constraints (enforced by its parent).
Imagine a button in your app. Perhaps it always says “Submit”:
class SubmitButton extends StatelessWidget { Widget build(context) {
return Button(
child: Text('Submit');
);
}
}
Or, perhaps you want the button to say “Submit” in some cases and “Update” in others.
Listing 1. A widget with configuration
class SubmitButton extends StatelessWidget { final String buttonText; ①
SubmitButton(this.buttonText); ②
Widget build(context) {
return Button(
child: Text(buttonText); ③
);
}
}
① Any piece of data passed to the widget is part of its configuration.
② You can omit useless constructors in Dart, but it’s needed now.
③ Pass in a variable, rather than a string literal. Flutter now knows to change this widget whenever configuration changes.
Either way, this widget is ‘dumb’ because it doesn’t update itself. It doesn’t care what the button says. Its configuration relies on parent widgets.
Also, importantly, StatelessWidgets
are destroyed entirely when Flutter removes them from the widget tree. It’s important to understand that a Stateless
widget shouldn’t be responsible for any data you don’t want to lose. Once it’s gone, it’s gone.
Stateful Widget
A StatefulWidget
has internal state and can manage that state. All StatefulWidgets
have corresponding State
objects. This is the anatomy of every StatefulWidget
.
Listing 2. Anatomy of a Stateful Widget
class MyHomePage extends StatefulWidget { ①
@override ②
_MyHomePageState createState() => new _MyHomePageState(); ③
}
class _MyHomePageState extends State<MyHomePage> { ④
@override
Widget build(BuildContext context) { ⑤
// ..
}
}
① Inherit from StatefulWidget
② Override the super class method createState
③ Every StatefulWidget
must have a createState
method which returns a State
object
④ Your state class inherits from the Flutter State
object
⑤ The StatefulWidget
required build
method.
If you remember earlier, I said that every widget class must have a build method. As you can see above, the StatefulWidget
class, in fact, doesn’t have a build method. Every StatefulWidget
has an associated State
object, which has a build
method. You can think of the pair of StatefulWidget
and State
as the same entity. In fact, Stateful
widgets are dumb and immutable (like a StatelessWidget
), but their associated State
objects are smart and mutable.
MyHomePage
is a StatefulWidget
, because it manages the state of the counter in the center of the app. When you tap that button, it fires a method called _incrementCounter
.
void _incrementCounter() { setState(() { ①
_counter++;
});
}
① setState
is one of the methods that a Flutter State
object uses to manage internal state.
setState
setState
is the third important Flutter method that you have to know, after build
and createState
. It only exists on the State
object. It more or less says “Hey Flutter, execute the code in this callback, (in this case, increase the counter variable by one), and then repaint all the widgets that rely on this state for configuration (in this case, the number on the screen in the middle of the app).” This method takes one argument, a void callback.
In our example, the State
object lives in the _MyHomePageState
widget, and its children interact and rely on the state. When you press the button, it calls a method passed to it from _MyHomePageState
. That method calls setState
, which in turn calls the _MyHomePageState.build
method again, repainting the widgets whose configurations have changed.
This covers setState
, but it’s worth noting that setState
can’t execute async code. Any async work should be done before calling setState
, because you don’t want Flutter to repaint something before the data it’s meant to display has resolved. For example, if you’re fetching a gif from a gif API on the internet, you don’t want call setState
before the image is ready to displayed.
That’s all for now. If you want to learn more about the book, check it out on liveBook here and see this slide deck.
For more free content from Manning’s books and video courses, check out our Free Content Center.