How would you go about doing that? Ponder that for a minute. Go ahead, we’ll wait.
- 1.
You’d get a vision of what you’re building. Figure the whole thing out.
- 2.
Realize that the entire project is too complex to build at once.
- 3.
Break the project into sections (legs, left arm, right arm, torso, left sword, right sword, helmet, cape, head).
- 4.
Realize that each of them is still too complex.
- 5.
For each section, you break it into sub-sections.
- 6.
Repeat steps 4 and 5 until you’ve got simple enough components that each is easy to understand, build, and maintain – for you and for any teammates that you may have.
- 7.
Create each simple component.
- 8.
Combine simple components to form the larger, more complex components.
- 9.
Repeat steps 7 and 8 until you’ve got your entire project created.
This process has a name: componentization, and is exactly the thought process we’ll go through with our Flutter projects.
Componentization is not something new. In fact, it was proposed as far back as 1968.1 But the technique has recently exploded in popularity thanks to web frameworks like Angular, React, Vue, Polymer, and native web components. Seems like all the cool kids are doing software components these days. The idea of recursively breaking down the complex bits into simpler bits is called decomposition. And the act of putting the written pieces back together into larger components is called composition.
In the world of Flutter, components are referred to as widgets.
- 1.
Behavior – What the software does. All of the business logic goes here: the data reading, writing, and processing.
- 2.
Presentation – How the software looks. The user interface. The buttons, textboxes, labels.
Only Flutter combines these into one language instead of two.
UI as code
Only Flutter uses the same language for presentation and behavior
Framework | Behavior expressed in ... | UI expressed in ... |
---|---|---|
Xamarin | C# | XAML |
React Native | JavaScript | JSX |
NativeScript | JavaScript | XML |
Flutter | Dart | Dart |
And that’s all you need to create a “Hello world” in Flutter.
But wait ... what is this Text() thing? It’s a built-in Flutter widget. Since these built-in widgets are so important, we need to take a look at them.
Built-in Flutter widgets
Flutter’s foundational widgets are the building blocks of everything we create and there are tons of them – about 160 at last count.3 This is a lot of widgets for you and I to keep track. But if you mentally organize them, it becomes much more manageable.
Value widgets
Layout widgets
Navigation widgets
Other widgets
Note
These are not Flutter’s official list of categories. Their 14 categories are listed here: https://flutter.dev/docs/development/ui/widgets. We just felt that reorganizing them helps to keep them straight.
We’ll take a brief look at each of these categories with an example or two, and then we’ll do some deep dives in later chapters. Let’s start with value widgets.
Value widgets
Certain widgets hold a value, maybe values that came from local storage, a service on the Internet, or from the user themselves. These are used to display values to the user and to get values from the user into the app. The seminal example is the Text widget which displays a little bit of text. Another is the Image widget which displays a .jpg, .png, or another picture.
Checkbox CircularProgressIndicator Date & Time Pickers DataTable DropdownButton FlatButton FloatingActionButton FlutterLogo Form | FormField Icon IconButton Image LinearProgressIndicator PopupMenuButton Radio RaisedButton RawImage | RefreshIndicator RichText Slider Switch Text TextField Tooltip |
We’ll explore value widgets in more detail in the next chapter.
Layout widgets
Align AppBar AspectRatio Baseline BottomSheet ButtonBar Card Center Column ConstrainedBox Container CustomMultiChildLayout Divider Expanded ExpansionPanel | FittedBox Flow FractionallySizedBox GridView IndexedStack IntrinsicHeight IntrinsicWidth LayoutBuilder LimitedBox ListBody ListTile ListView MediaQuery NestedScrollview OverflowBox | Padding PageView Placeholder Row Scaffold Scrollable Scrollbar SingleChildScrollView SizedBox SizedOverflowBox SliverAppBar SnackBar Stack Table Wrap |
This is a huge topic which we’ve given its own chapter, Chapter 6, “Laying Out Your Widgets.”
Navigation widgets
AlertDialog BottomNavigationBar Drawer | MaterialApp Navigator SimpleDialog | TabBar TabBarView |
We’ll learn how they work in Chapter 7, “Navigation and Routing.”
Other widgets
GestureDetector Dismissible | Cupertino Theme | Transitions Transforms |
Many of these miscellaneous widgets are covered throughout the book where they fit naturally. GestureDetector is crucial enough that it gets its own chapter, Chapter 5, “Responding to Gestures.”
How to create your own stateless widgets
So we know that we will be composing these built-in widgets to form our own custom widgets which will then be composed with other built-in widgets to eventually form an app.
Widgets are masterfully designed because each widget is easy to understand and therefore easy to maintain. Widgets are abstract from the outside while being logical and predictable on the inside. They are a dream to work with.
This is how your build method will always work. It will return a single, massive, nested expression. It is widgets inside widgets inside widgets that enable you to create your own elaborate custom widget.
Widgets have keys
You may hear about a virtual DOM when other developers talk about Flutter. This comes from the world of React. (Remember that Flutter borrowed heavily from React’s excellent architecture.) Well, strictly speaking, Flutter doesn’t have a DOM, but it does maintain something resembling it – the element tree. The element tree is a tiny copy of all the widgets on the screen. Flutter maintains a current element tree and one with batched changes applied.
You see, Flutter might be really slow if it applied every tiny change to the screen and then tried to re-render it hundreds of times per second. Instead, Flutter applies all of those changes to a copy of the element tree. It then periodically “diffs” the current element tree with the modified one and decides what truly needs to be re-rendered. It only re-renders those parts that need it. This is much, much faster.
But occasionally Flutter gets confused when matching the widgets in the element trees. You’ll know to programmatically assign keys if your data changes and widgets get drawn in the wrong location, the data isn’t updated on the screen, or your scroll position isn’t preserved.
- 1.
Keys exist and why Flutter may need them.
- 2.
If your widgets aren’t being redrawn as you might expect when data changes, keys may solve problems.
- 3.
You have the opportunity to assign keys to certain widgets.
If that’s not enough to satisfy you for now, the great Emily Fortuna has recorded a super ten-minute video on keys.5
Passing a value into your widget
In other words, as the data in your app changes, the screen will change accordingly. And you, the developer, get to decide how that data is presented as you write a build method in your widgets. It is a foundational concept of Flutter.
- 1.
The widget can be re-rendered with new data passed from outside.
- 2.
Data can be maintained within certain widgets.
This is Dart syntax. Note three things. First, you’ll list the input parameter in the constructor (“this.firstName” in the preceding example). Second, make sure you put “this.” in front of it. The “this.” matches it to a class-level property rather than a parameter that is local to the constructor function. And third, mark the corresponding class property as final.
Do you see the difference? It’s subtle. There are now curly braces around the constructor parameters. This makes them optional and named.
Tip
Note that in all three of the preceding examples, we are using a Person class that might have been defined in the same dart file where you’re using it. But a better practice is to create each class in a separate dart file and import it into other dart files where it is used.
import 'Person.dart';
Stateless and Stateful widgets
So far we’ve been going out of our way to create stateless widgets. So you probably guessed that there’s also a stateful widget. You were right. A stateless widget is one that doesn’t maintain its own state. A stateful widget does.
“State” in this context refers to data within the widget that can change during its lifetime. Think about our Person widget from earlier. If it’s a widget that just displays the person’s information, it should be stateless. But if it is a person maintenance widget where we allow the user to change the data by typing into a TextField, then we’d need a StatefulWidget.
There’s a whole chapter on stateful widgets later. If you just can’t wait to know more about them, you can read Chapter 9, “Managing State,” later in this book. Then come back here.
So which one should I create?
The short answer is create a stateless widget. Never use a stateful widget until you must. Assume all widgets you make will be stateless and start them out that way. Refactor them into stateful widgets when you’re sure you really do need state. But recognize that state can be avoided more often than developers think. Avoid it when you can to make widgets simpler and therefore easier to write, to maintain, and to extend. Your team members will thank you for it.
Note
There is actually a third type of widget, the InheritedWidget. You set a value in your InheritedWidget and any descendent can reach back up through the tree and ask for that data directly. It is kind of an advanced topic, but Rémi Rousselet would have had my head if I hadn’t mentioned it. You can read more about it in Chapter 9, “Managing State,” or watch Emily Fortuna’s concise overview of InheritedWidget here: http://bit.ly/inheritedWidget.
Conclusion
So now we know that Flutter apps are all about widgets. You’ll compose your own custom Stateless or Stateful widgets that have a build method which will render a tree of built-in Flutter widgets. So clearly we need to know about the built-in Flutter widgets which we’ll learn beginning in the next chapter.