Intro to Flutter Composition and Stateless Widgets
- Widgets in Flutter
- Code along
- Composition and the widget tree
- Stateless widgets
- Other tips and tricks
- Key terms
Widgets in Flutter
A widget is “an immutable description of a part of a user interface” according to the (Flutter documentation). Widgets are the individual parts of the UI that provide structure, style, and layout on the screen. UIs in Flutter are composed of many widgets.
In Flutter, widgets are divided into two types: stateless and stateful. We will talk about the distinction later. In terms of what the widgets do, you can group Flutter widgets into through categories:
- structural - widgets that show information and provide behavior, buttons, menus, images, text.
- stylistic - widgets that fonts, colors, gradients and other styles that are usually applied to structural widgets
- layout - widgets whose primarily job is to arrange and organize other widgets in the UI
Flutter has dozens of built-in widgets for you to use and customize. We will start with some basic widgets so that you can become familiar with composition.
Code along
We are going to code a new Flutter project together in class to explain the basics of Flutter composition and use a few widgets.
-
Part 1: https://youtu.be/JhepdWNRYFA
-
Part 2: https://youtu.be/sW7vgfcd8vc
Structural widgets
- BuildContext - this parameter can tell you things about the parent Widgets about the one you are currently building. This may be useful in future weeks, but don’t worry about it now.
- MaterialApp - Provides support for app navigation menu (later), the title of the app for the launcher icon, applying color themes to all widgets, figuring out how long and wide the screen is (useful later), and some other things.
- AppBar - the bar at the top, referred to as either the “app bar” or the “action bar”. Room for a leading icon (a back button or a nav menu), the title, and then shortcut “actions” that you cutomize. More on actions later.
- Scaffold - a complex structural Widget that you can think of as the container for what you see on the screen. It tells where to draw the AppBar, an optional BottomAppBar, and FloatingActionButtons, as well as the “Body” content that is the focus of the screen. It also provides support for a “drawer”. It handles figuring out where on the screen a person is tapping. It handles things like the keyboard popping up, figuring out the size of the device and stretching itself appropriately.
- Text - a structural widget that renders text
- DecoratedBox draws a rectangluar box on the screen with a particular styling – usually coupled with BoxDecoration
- Icon - draws a vector icon on the screen. Flutter provides a number of ready-made icons. You can see what icons are available on the Google Fonts website.
- SizedBox - Useful for creating spaces of fixed width or height between widgets in a Column or Row. You can also give the SizedBox a child, which will force the child to be the size you specify.
- Container - a powerful, general purpose widget that represents a rectangular area on the screen. This rectangle can be decorated, padded, and positioned. This rectangle can also have a child widget (like a Row or Column), allowing you to provide a “visual grouping” of widgets through color and style.
Stylistic widgets
- ThemeData - More on this later in the semester. Allows you to customize a color scheme and font styles.
- BoxDecoration - a widget for styling a DecoratedBox
Layout widgets
- Center - a layout widget that centers its child within itself.
- Padding - a layout widget that provides whitespace around its child
- Column - a layout widget that can have multiple children arrayed in a vertical column. More on Columns later.
- Row - a layout widget that displays its children in a horizontal array. The complement of the Column widget.
Composition and the widget tree
Hopefully you are now starting to see how Flutter widgets are composed together. Composition is the combining of widgets together to create a UI. Each widget has one simple job. Composition in Flutter means that one widget will often “contain” another. Programmatically, this means that one object has a reference (a variable) holding another object. In the built-in Flutter widget classes, the child
variable usually holds the reference.
Widgets are composed into a widget tree. The widget tree usually has a StatelessWidget as its root, CollageApp
in our example. The CollageApp
defines the build()
method, which then returns a MaterialApp
. The MaterialApp
contains a Scaffold
, which contains an AppBar
and a Center
, which contains… And on and on.
Drawing the widget tree is a great way to help you understand how Flutter thinks of your app. The starting point, from a programming perspective, of a Flutter app is the runApp()
function call that takes a widget as a parameter. This widget is the root of the widget tree. Flutter then traverses the tree (remember data structures?) to figure out where to position widgets on the screen while taking into account the desired size of each widget against the amount of actual pixels available.
Think of the widget tree you compose with your Dart code as a blueprint for Flutter. Flutter uses your widget tree to figure out very specifically how to instruct your phone to draw things on the screen. There is a separate, parallel structure called the element tree that is in memory while your app is running that represents the actual widgets displayed on the screen at the given time.
Planning a UI in Flutter
When designing a UI, sketch out on paper a very simple drawing of what goes where on the screen. then, think about what widgets you will need to realize this and sketch out the widget tree. Your widget tree need not be perfect, but it will help you figure out how to compose the code. It is easy in Flutter to get confused with all the code nesting. Better to have a visual blueprint to refer to.
Stateless widgets
Flutter widgets are one of two types: stateless or stateful. All widget classes in Flutter inherit from either the StatelessWidget
or StatefulWidget
classes.
A stateless widget is one that does not change after it is first drawn on the screen. It is effectively immutable once rendered. Text
, Padding
, and BoxDecoration
are all examples of stateless widgets. You can configure their style and content in the code, but once they are showing on the screen, they are not able to be changed. Contrast this to a Checkbox or Search Box whose appearance will change when a user interacts with it. Checkboxes and Search Boxes are stateful.
Stateless widgets can have children, i.e., can refer to other widgets for composition. Flutter has many built-in stateless widgets, but you can also define your own widget (which will be composed of built-in widgets) by extending the StatelessWidget
class. This is great for when you have a particular style you want to create for a widget and use that same style in multiple places.
Just like Flutter’s built-in widgets, you can define a constructor on your StatelessWidget that will accept parameters to specify how your widget should behave. For example, you can define a StatelessWidget that displays text in a colorful “card”, and your constructor can take a parameter that specifies which string you want to show.
The build() method
Every subclass of StatelessWidget must override the build()
method. The build()
method is automatically called by Flutter when it is time to draw your widget on the screen. Remember, widgets are best thought of as blueprints or configurations for the app’s UI. Flutter will process your widgets and transform them into elements. An element is a widget that has been made real and mounted on the screen. Each Widget class has a corresponding Element class, e.g., StatelessWidget
has a StatelessElement
class.
So, when exactly is the build()
method called? What starts the process of building and creating the elements?
runApp
is the starting point. Whatever Widget is given to it, Flutter tries to create that Element to the height and width constraints that match the screen.runApp()
is called when the user launches your app on their device.- Flutter asks the Widget for an Element, and then puts that element on the element tree with a reference to the widget that created it. The element tree always represents what is actually displayed on the device at any given moment.
- Flutter then calls the widget’s build method to get any child elements. They are mounted on the element tree as well.
Other tips and tricks
- Is your Dart code getting messy? Right-click in the editor and select “Format Document”. The keyboard shortcut is Alt+Shift+F for Windows and Option-Shift-F for Mac.
- Use Control+LeftClick or Command+LeftClick over a class, method, or variable to navigate to its code definition.
- When calling a function with multiple parameters or specifying a list in Dart, you can either include or omit a trailing
,
after the last element. Both of the following are valid:Padding(padding: const EdgeInsets.all(8), child: Text(label))
Padding(padding: const EdgeInsets.all(8), child: Text(label),)
- In VSCode, you may see a warning on your custom widgets that extend
StatelessWidget
that says “Use key in widget constructors.” We can ignore this for now. Open the fileanalysis_options.yaml
and you will see a header labeledrules:
. Hit Enter to the next line, hit Tab, and paste the following:use_key_in_widget_constructors: false
- Official Flutter video that recaps StatelessWidgets: https://www.youtube.com/watch?v=wE7khGHVkYY&ab_channel=GoogleDevelopers
Key terms
- UI
- user interface
- page or screen
- We use these terms to refer to a collection of widgets that appear together and fill the screen. Different pages often have different logical purposes, like a “View Profile” page, “Upload Pictures” page, “Search for Items to Buy” page, etc. We will cover creating multiple pages and navigating between them soon.
- Widget
- a widget is an individual part of the UI that provides structure, style, and layout on the screen. Widgets are blueprints for what Flutter should draw on the screen.
- Element
- a widget made real on the screen. Flutter creates elements on the screen by examining your widgets, and does the hard work of telling the phone how to draw the elements on the screen while considering different screen sizes and other constraints.
- Composition
- Combining widgets together to create a UI. Each widget has one simple job. Composition in Flutter means that one widget will often “contain” another. Programmatically, this means that one object has a reference (a variable) holding another object. In the built-in Flutter widget classes, the
child
variable usually holds the reference. - Widget tree
- The composition of widgets that create the UI. You specify your widget tree by composing Flutter widgets in your code, and when the app runs, Flutter traverses your logical widget tree to figure out what and how to render the widgets into UI elements on the physical screen.
- Element tree
- A tree that represents what is actually displayed on the device at any given moment. It has a parallel structure to the widget tree.
- Material design
- You may see or hear the term “material” when watching videos or reading Flutter documentation. Material Design is Google’s design language that they use in all their services and apps. It is characterized by the metaphor that UI elements are “cards” or “materials” sitting on top of a surface.