UI _ Flutter_ex
UI _ Flutter_ex
Learn more
Building user
interfaces with Flutter
UI
Contents
Hello world
Basic widgets
Using Material Components
Handling gestures
Changing widgets in response to input
Bringing it all together
Responding to widget lifecycle events
Keys
Global keys
Flutter widgets are built using a modern framework that takes inspiration from React. The
central idea is that you build your UI out of widgets. Widgets describe what their view
should look like given their current configuration and state. When a widget's state
changes, the widget rebuilds its description, which the framework diffs against the
previous description in order to determine the minimal changes needed in the underlying
render tree to transition from one state to the next.
info Note
If you would like to become better acquainted with Flutter by diving into some code,
check out building layouts, and adding interactivity to your Flutter app.
Hello world
The minimal Flutter app simply calls the runApp() function with a widget:
The runApp() function takes the given Widget and makes it the root of the widget tree.
In this example, the widget tree consists of two widgets, the Center widget and its child,
the Text widget. The framework forces the root widget to cover the screen, which means
the text "Hello, world" ends up centered on screen. The text direction needs to be
specified in this instance; when the MaterialApp widget is used, this is taken care of for
you, as demonstrated later.
When writing an app, you'll commonly author new widgets that are subclasses of either
StatelessWidget or StatefulWidget , depending on whether your widget manages any
state. A widget's main job is to implement a build() function, which describes the
widget in terms of other, lower-level widgets. The framework builds those widgets in turn
until the process bottoms out in widgets that represent the underlying RenderObject ,
which computes and describes the geometry of the widget.
Basic widgets
Flutter comes with a suite of powerful basic widgets, of which the following are commonly
used:
Text
The Text widget lets you create a run of styled text within your application.
Row , Column
These flex widgets let you create flexible layouts in both the horizontal ( Row ) and
vertical ( Column ) directions. The design of these objects is based on the web's flexbox
layout model.
Stack
Instead of being linearly oriented (either horizontally or vertically), a Stack widget lets
you place widgets on top of each other in paint order. You can then use the
Positioned widget on children of a Stack to position them relative to the top, right,
bottom, or left edge of the stack. Stacks are based on the web's absolute positioning
layout model.
Container
The Container widget lets you create a rectangular visual element. A container can be
decorated with a BoxDecoration , such as a background, a border, or a shadow. A
Container can also have margins, padding, and constraints applied to its size. In
addition, a Container can be transformed in three-dimensional space using a matrix.
Below are some simple widgets that combine these and other widgets:
yaml
name: my_app
flutter:
uses-material-design: true
The MyScaffold widget organizes its children in a vertical column. At the top of the
column it places an instance of MyAppBar , passing the app bar a Text widget to use as
its title. Passing widgets as arguments to other widgets is a powerful technique that lets
you create generic widgets that can be reused in a wide variety of ways. Finally,
MyScaffold uses an Expanded to fill the remaining space with its body, which consists of
a centered message.
Now that the code has switched from MyAppBar and MyScaffold to the AppBar and
Scaffold widgets, and from material.dart , the app is starting to look a bit more
Material. For example, the app bar has a shadow and the title text inherits the correct
styling automatically. A floating action button is also added.
Notice that widgets are passed as arguments to other widgets. The Scaffold widget
takes a number of different widgets as named arguments, each of which are placed in the
Scaffold layout in the appropriate place. Similarly, the AppBar widget lets you pass in
widgets for the leading widget, and the actions of the title widget. This pattern
recurs throughout the framework and is something you might consider when designing
your own widgets.
Handling gestures
Most applications include some form of user interaction with the system. The first step in
building an interactive application is to detect input gestures. See how that works by
creating a simple button:
The GestureDetector widget doesn't have a visual representation but instead detects
gestures made by the user. When the user taps the Container , the GestureDetector
calls its onTap() callback, in this case printing a message to the console. You can use
GestureDetector to detect a variety of input gestures, including taps, drags, and scales.
Many widgets use a GestureDetector to provide optional callbacks for other widgets.
For example, the IconButton , ElevatedButton , and FloatingActionButton widgets
have onPressed() callbacks that are triggered when the user taps the widget.
You might wonder why StatefulWidget and State are separate objects. In Flutter,
these two types of objects have different life cycles. Widgets are temporary objects,
used to construct a presentation of the application in its current state. State objects, on
the other hand, are persistent between calls to build() , allowing them to remember
information.
The example above accepts user input and directly uses the result in its build() method.
In more complex applications, different parts of the widget hierarchy might be responsible
for different concerns; for example, one widget might present a complex user interface
with the goal of gathering specific information, such as a date or location, while another
widget might use that information to change the overall presentation.
In Flutter, change notifications flow "up" the widget hierarchy by way of callbacks, while
current state flows "down" to the stateless widgets that do presentation. The common
parent that redirects this flow is the State . The following slightly more complex example
shows how this works in practice:
Notice the creation of two new stateless widgets, cleanly separating the concerns of
displaying the counter ( CounterDisplay ) and changing the counter
( CounterIncrementor ). Although the net result is the same as the previous example, the
separation of responsibility allows greater complexity to be encapsulated in the individual
widgets, while maintaining simplicity in the parent.
StatefulWidget
setState()
The ShoppingListItem widget follows a common pattern for stateless widgets. It stores
the values it receives in its constructor in final member variables, which it then uses
during its build() function. For example, the inCart boolean toggles between two
visual appearances: one that uses the primary color from the current theme, and another
that uses gray.
When the user taps the list item, the widget doesn't modify its inCart value directly.
Instead, the widget calls the onCartChanged function it received from its parent widget.
This pattern lets you store state higher in the widget hierarchy, which causes the state to
persist for longer periods of time. In the extreme, the state stored on the widget passed to
runApp() persists for the lifetime of the application.
When the parent receives the onCartChanged callback, the parent updates its internal
state, which triggers the parent to rebuild and create a new instance of
ShoppingListItem with the new inCart value. Although the parent creates a new
instance of ShoppingListItem when it rebuilds, that operation is cheap because the
framework compares the newly built widgets with the previously built widgets and applies
only the differences to the underlying RenderObject .
To access properties of the current ShoppingList , the _ShoppingListState can use its
widget property. If the parent rebuilds and creates a new ShoppingList , the
_ShoppingListState rebuilds with the new widget value. If you wish to be notified when
the widget property changes, override the didUpdateWidget() function, which is
passed an oldWidget to let you compare the old widget with the current widget.
When a state object is no longer needed, the framework calls dispose() on the state
object. Override the dispose function to do cleanup work. For example, override
dispose to cancel timers or to unsubscribe from platform services. Implementations of
dispose typically end by calling super.dispose .
Keys
Use keys to control which widgets the framework matches up with other widgets when a
widget rebuilds. By default, the framework matches widgets in the current and previous
build according to their runtimeType and the order in which they appear. With keys, the
framework requires that the two widgets have the same key as well as the same
runtimeType .
Keys are most useful in widgets that build many instances of the same type of widget. For
example, the ShoppingList widget, which builds just enough ShoppingListItem
instances to fill its visible region:
Without keys, the first entry in the current build would always sync with the first
entry in the previous build, even if, semantically, the first entry in the list just scrolled
off screen and is no longer visible in the viewport.
By assigning each entry in the list a "semantic" key, the infinite list can be more
efficient because the framework syncs entries with matching semantic keys and
therefore similar (or identical) visual appearances. Moreover, syncing the entries
semantically means that state retained in stateful child widgets remains attached to
the same semantic entry rather than the entry in the same numerical position in the
viewport.
Global keys
Use global keys to uniquely identify child widgets. Global keys must be globally unique
across the entire widget hierarchy, unlike local keys which need only be unique among
siblings. Because they are globally unique, a global key can be used to retrieve the state
associated with a widget.
Except as otherwise noted, this site is licensed under a Creative Commons Attribution 4.0 International
License, and code samples are licensed under the 3-Clause BSD License.