When building Flutter apps, most of the time you are dealing with widgets. This chapter provides basic background information about widgets in Flutter. It also covers several basic widgets that display texts, images, icons, buttons, and placeholders.
4.1 Understanding Widgets
Problem
You want to know how to use components in Flutter.
Solution
Widgets are everywhere in Flutter.
Discussion
If you have been involved in development of user interface, you should be familiar with concepts like widgets or components. These concepts represent reusable building blocks to create user interface. A good user interface library should have a large number of high-quality and easy-to-use components. Buttons, icons, images, menus, dialogs, and form inputs are all examples of components. Components can be big or small. Complicated components are usually composed of small components. You can create your own components by following the component model. You can also choose to share your components to the community. A good eco-system of components is a key factor for a user interface library to be successful.
Flutter uses widgets to describe reusable building blocks in the user interface. Comparing to other libraries, widget in Flutter is a much broader concept. Not only common components like buttons and form inputs are widgets, layout constraints are also expressed as widgets in Flutter. For example, if you want to place a widget in the center of a box, you simply wrap the widget into a Center widget. Widgets are also used to retrieve context data. For example, DefaultTextStyle widget gets the TextStyle applies to un-styled Text widgets.
Widget in Flutter is an immutable description of a part of the user interface. All fields of a widget class are final and set in the constructor. Widget constructors only have named parameters. A widget can have one or many widgets as the children. Widgets of a Flutter app creates a tree-like hierarchy. The main() method of a Flutter app’s entry point file uses the runApp() method to start the app. The only parameter of runApp() is a Widget object. This Widget object is the root of the app’s widgets tree. Widgets are only static configurations that describe how to configure a subtree in the hierarchy. To actually run the app, we need a way to manage instantiation of widgets.
Flutter uses Element to represent an instantiation of a Widget at a particular location in the tree. A Widget can be instantiated zero or many times. The process to turn Widgets to Elements is called inflation. Widget class has a createElement() method to inflate the widget to a concrete instance of Element. Flutter framework is responsible for managing the lifecycle of elements. The widget associated with an element may change over time. The framework updates the element to use the new configuration.
4.2 Understanding BuildContext
Problem
You want to access information related to a widget in the widgets tree.
Solution
WidgetBuilder functions have a BuildContext parameter to access information related to a widget in the widgets tree. You can see BuildContext in StatelessWidget.build() and State.build() methods .
Discussion
Methods of BuildContext
Name | Description |
---|---|
ancestorInheritedElementForWidgetOfExactType | Get the InheritedElement corresponding to the nearest ancestor widget of the given type of InheritedWidget. |
ancestorRenderObjectOfType | Get the RenderObject of the nearest ancestor RenderObjectWidget widget. |
ancestorStateOfType | Get the State object of the nearest ancestor StatefulWidget widget. |
rootAncestorStateOfType | Get the State object of the furthest ancestor StatefulWidget widget. |
ancestorWidgetOfExactType | Get the nearest ancestor Widget. |
findRenderObject | Get the current RenderObject for the widget. |
inheritFromElement | Register this BuildContext with the given ancestor InheritedElement such that this BuildContext is rebuilt when the ancestor’s widget changes. |
inheritFromWidgetOfExactType | Get the nearest InheritedWidget of the given type and register this BuildContext such that this BuildContext is rebuilt when the widget changes. |
visitAncestorElements | Visit ancestor elements. |
visitChildElements | Visit children elements. |
Use BuildContext
4.3 Understanding Stateless Widget
Problem
You want to create a widget that has no mutable state.
Solution
Extend from StatelessWidget class.
Discussion
Example of StatelessWidget
4.4 Understanding Stateful Widget
Problem
You want to create a widget that has mutable state.
Solution
Extend from StatefulWidget class.
Discussion
Example of StatefulWidget
4.5 Understanding Inherited Widget
Problem
You want to propagate data down the widgets tree.
Solution
Extend from InheritedWidget class .
Discussion
When building a subtree of widgets, you may need to propagate data down the widgets tree. For example, your root widget of a subtree may define some context data, for example, configuration data retrieved from the server. Other widgets in the subtree may also need to access the context data. One possible way is to add the context data to a widget’s constructor, then propagate the data as constructor parameter of children widgets. The major drawback of this solution is that you need to add the constructor parameter to all widgets in the subtree. Even though some widgets may not actually need the data, they still need to have the data to pass to their children widgets.
A better approach is to use InheritedWidget class. BuildContext class has an inheritFromWidgetOfExactType() method to get the nearest instance of a particular type of InheritedWidget. With InheritedWidget, you can store the context data in an InheritedWiget instance. If a widget needs to access the context data, you can use inheritFromWidgetOfExactType() method to get the instance and access the data. If an inherited widget changes state, it will cause its consumers to rebuild.
Example of InheritedWidget
Use of ConfigWidget
Complete example
4.6 Displaying Text
Problem
You want to display some text.
Solution
Use the Text and RichText widgets .
Discussion
Almost all apps need to display some text to the end users. Flutter provides several classes related to text. Text and RichText are the two widgets to display text. In fact, Text uses RichText internally. The build() method of Text widget returns a RichText instance. The difference between Text and RichText is that Text uses the style from the closest enclosing DefaultTextStyle object, while RichText requires explicit style.
Text
Named parameters of Text() and Text.rich()
Name | Type | Description |
---|---|---|
style | TextStyle | Style of the text. |
textAlign | TextAlign | How text should be aligned horizontally. |
textDirection | TextDirection | Direction of text. |
locale | Locale | Locale to select font based on Unicode. |
softWrap | bool | Whether to break text at soft line breaks. |
overflow | TextOverflow | How to handle text overflow. |
textScaleFactor | double | The factor to scale the text. |
maxLines | int | The maximum number of lines. If the text exceeds the limit, it will be truncated according to the strategy specified in overflow. |
semanticsLabel | String | Semantics label for the text. |
TextAlign values
Name | Description |
---|---|
left | Align text on the left edge of its container. |
right | Align text on the right edge of its container. |
center | Align text in the center of its container. |
justify | For lines of text end with soft line breaks, stretch these lines to fill the width of the container; for lines of text end with hard line breaks, align them toward the start edge. |
start | Align text on the leading edge of its container. The leading edge is the left edge for left-to-right text, while it’s the right edge for right-to-left text. |
end | Align text on the trailing edge of its container. The trailing edge is the opposite of the leading edge. |
TextOverflow values
Name | Description |
---|---|
clip | Clip the overflowing text. |
fade | Fade the overflowing text to be transparent. |
ellipsis | Add an ellipsis after the overflowing text. |
Examples of Text
TextSpan
Named parameters of TextSpan()
Name | Type | Description |
---|---|---|
style | TextStyle | Style of the text and children. |
text | String | Text in the span. |
children | List<TextSpan> | TextSpans as children of this span. |
recognizer | GestureRecognizer | A gesture recognizer to receive events. |
Example of Text.rich()
RichText
RichText always uses TextSpan objects to represent text and styles. RichText() constructor has a required named parameter text of the type TextSpan. It also has optional named parameters textAlign, textDirection, softWrap, overflow, textScaleFactor, maxLines, and locale. These optional named parameters have the same meaning as Text() constructor shown in Table 4-2.
Example of RichText
4.7 Applying Styles to Text
Problem
You want the displayed text to have different styles.
Solution
Use TextStyle to describe styles.
Discussion
Named parameters of TextStyle()
Name | Type | Description |
---|---|---|
color | Color | Color of the text. |
fontSize | Double | Size of font. |
fontWeight | FontWeight | Typeface thickness. |
fontStyle | FontStyle | Typeface variant. |
letterSpacing | Double | Space between each letter. |
wordSpacing | Double | Space between each word. |
textBaseLine | TextBaseLine | Common baseline to align this text span and its parent span. |
height | Double | Height of the text. |
locale | Locale | Locale to select region-specific glyphs. |
foreground | Paint | Foreground for the text. |
background | Paint | Background for the text. |
shadows | List<Shadow> | Shadows painted underneath the text. |
decoration | TextDecoration | Decoration of the text. |
decorationColor | Color | Color of text decorations. |
decorationStyle | TextDecorationStyle | Style of text decorations. |
debugLabel | String | Description of the style for debugging. |
fontFamily | String | Name of the font. |
package | String | Use with fontFamily if the font is defined in a package. |
FontWeight class defines values w100, w200, w300, w400, w500, w600, w700, w800, and w900. FontWeight.w100 is the thinnest, while w900 is the thickest. FontWeight.bold is an alias of FontWeight.w700, while FontWeight.normal is an alias of FontWeight.w400. FontStyle is an enum type with two values italic and normal. TextBaseline is an enum type with values alphabetic and ideographic.
TextDecoration constants
Name | Description |
---|---|
none | No decoration. |
underline | Draw a line underneath text. |
overline | Draw a line above text. |
lineThrough | Draw a line through text. |
TextDecorationStyle values
Name | Description |
---|---|
solid | Draw a solid line. |
double | Draw two lines. |
dotted | Draw a dotted line. |
dashed | Draw a dashed line. |
wavy | Draw a sinusoidal line. |
Example of using TextDecoration and TextDecorationStyle
Update TextStyle
4.8 Displaying Images
Problem
You want to display images loaded from network.
Solution
Use Image.network() with the image URL to load and display an image.
Discussion
Example of Image.network()
All downloaded images are cached regardless of HTTP headers. This means that all HTTP cache control headers will be ignored. You can use cache buster to force cached images to refresh. For example, you can add a random string to the image URL.
If extra HTTP headers are required to load the image, you can specify the headers parameter of type Map<String, String> to provide these headers. A typical use case is to load protected images that require HTTP headers for authentication.
ImageRepeat values
Name | Description |
---|---|
Repeat | Repeat in both x and y directions. |
repeatX | Repeat only in the x direction. |
repeatY | Repeat only in the y direction. |
noRepeat | No repeat. The uncovered area will be transparent. |
Repeated images
4.9 Displaying Icons
Problem
You want to use icons.
Solution
Use Icon to show icons from Material Design or icon packs from community.
Discussion
Icons are used extensively in mobile apps. Comparing to text, icons take less screen estate to express the same semantics. Icons can be created from font glyphs or images. The Icon widget is drawn with a font glyph. A font glyph is described with IconData class. To create an IconData instance, the Unicode code point of this icon in the font is required.
Icons class has a number of predefined IconData constants for icons in Material Design (https://material.io/tools/icons/). For example, Icons.call is the IconData constant for the icon named “call”. If the app uses Material Design, then these icons can be used out of box. CupertinoIcons class has a number of predefined IconData constants for iOS-style icons.
Example of Icon()
Use Font Awesome icon
4.10 Using Buttons with Text
Problem
You want to use buttons with text.
Solution
Use button widgets FlatButton, RaisedButton, OutlineButton, and CupertinoButton.
Discussion
A FlatButton has zero elevation and no visible borders. It reacts to touches by filling with color specified by highlightColor.
A RaisedButton has elevation and is filled with color. It reacts to touches by increasing elevation to highlightElevation.
An OutlineButton has borders, an initial elevation of 0.0, and transparent background. It reacts to touches by making its background opaque with the color and increasing its elevation to highlightElevation.
FlatButtons should be used on toolbars, in dialogs, in cards, or inline with other content where there is enough space to make buttons’ presence obvious. RaisedButtons should be used where using space is not enough to make the buttons stand out. OutlineButton is the cross between RaisedButton and FlatButton. OutlineButtons can be used when neither FlatButtons nor RaisedButtons are appropriate.
Different types of buttons
4.11 Using Buttons with Icons
Problem
You want to use buttons with icons.
Solution
Use IconButton widget, FlatButton.icon(), RaisedButton.icon(), and OutlineButton.icon().
Discussion
There are two ways to create a button with an icon. If only the icon is enough, use IconButton widget. If both the icon and text are required, use constructors FlatButton.icon() , RaisedButton.icon() , or OutlineButton.icon() .
Examples of IconButton() and RaisedButton.icon()
4.12 Adding Placeholders
Problem
You want to add placeholders to represent widgets that will be added later.
Solution
Use Placeholder.
Discussion
Before implementing the interface of an app, you usually have a basic idea about how the app looks like. You can start by breaking down the interface into many widgets. You can use placeholders to represent unfinished widgets during development, so you can test the layout of other widgets. For example, if you need to create two widgets, one displays at the top, while the other one displays at the bottom. If you choose to create the bottom widget first and use a placeholder for the top widget, you can see the bottom widget in its desired position.
Example of Placeholder
4.13 Summary
Widgets are everywhere in Flutter apps. This chapter provides basic introduction of widgets in Flutter, including StatelessWidget, StatefulWidget, and InheritedWidget. This chapter also covers usage of common basic widgets to display text, images, icons, buttons, and placeholders. The next chapter will discuss layout in Flutter.