6
Using Common Widgets

WHAT YOU WILL LEARN IN THIS CHAPTER

  • How to use basic widgets such as Scaffold, AppBar, SafeArea, Container, Text, RichText, Column, and Row, as well as different types of buttons
  • How to nest the Column and Row widgets together to create different UI layouts
  • Ways to include images, icons, and decorators
  • How to use text field widgets to retrieve, validate, and manipulate data
  • How to check your app's orientation

In this chapter, you'll learn how to use the most common widgets. I call them our base building blocks for creating beautiful UIs and UXs. You'll learn how to load images locally or over the Web via a uniform resource locator (URL), use the included rich Material Components icons, and apply decorators to enhance the look and feel of widgets or use them as input guides to entry fields. You'll also explore how to take advantage of the Form widget to validate text field entry widgets as a group, not just individually. Additionally, to account for the variety of device sizes, you'll see how using the MediaQuery or OrientationBuilder widget is a great way to detect orientation—because using the device orientation and layout widgets accordingly based on portrait or landscape is extremely important. For example, if the device is in portrait mode, you can show a row of three images, but when the device is turned to landscape mode, you can show a row of five images since the width is a larger area than in portrait mode.

USING BASIC WIDGETS

When building a mobile app, you'll usually implement certain widgets for the base structure. Being familiar with them is necessary.

  • Scaffold As you learned in Chapter 4, “Creating a Starter Project Template,” the Scaffold widget implements the basic Material Design visual layout, allowing you to easily add various widgets such as AppBar, BottomAppBar, FloatingActionButton, Drawer, SnackBar, BottomSheet, and more.
  • AppBar The AppBar widget usually contains the standard title, toolbar, leading, and actions properties (along with buttons), as well as many customization options.
    • title The title property is typically implemented with a Text widget. You can customize it with other widgets such as a DropdownButton widget.
    • leading The leading property is displayed before the title property. Usually this is an IconButton or BackButton.
    • actions The actions property is displayed to the right of the title property. It's a list of widgets aligned to the upper right of an AppBar widget usually with an IconButton or PopupMenuButton.
    • flexibleSpace The flexibleSpace property is stacked behind the Toolbar or TabBar widget. The height is usually the same as the AppBar widget's height. A background image is commonly applied to the flexibleSpace property, but any widget, such as an Icon, could be used.
  • SafeArea The SafeArea widget is necessary for today's devices such as the iPhone X or Android devices with a notch (a partial cut‐out obscuring the screen usually located on the top portion of the device). The SafeArea widget automatically adds sufficient padding to the child widget to avoid intrusions by the operating system. You can optionally pass a minimum amount of padding or a Boolean value to not enforce padding on the top, bottom, left, or right.
  • Container The Container widget is a commonly used widget that allows customization of its child widget. You can easily add properties such as color, width, height, padding, margin, border, constraint, alignment, transform (such as rotating or sizing the widget), and many others. The child property is optional, and the Container widget can be used as an empty placeholder (invisible) to add space between widgets.
  • Text The Text widget is used to display a string of characters. The Text constructor takes the arguments string, style, maxLines, overflow, textAlign, and others. A constructor is how the arguments are passed to initialize and customize the Text widget.
  • RichText The RichText widget is a great way to display text using multiple styles. The RichText widget takes TextSpans as children to style different parts of the strings.
  • Column A Column widget displays its children vertically. It takes a children property containing an array of List<Widget>, meaning you can add multiple widgets. The children align vertically without taking up the full height of the screen. Each child widget can be embedded in an Expanded widget to fill the available space. CrossAxisAlignment, MainAxisAlignment, and MainAxisSize can be used to align and size how much space is occupied on the main axis.
  • Row A Row widget displays its children horizontally. It takes a children property containing an array of List<Widget>. The same properties that the Column contains are applied to the Row widget with the exception that the alignment is horizontal, not vertical.
  • Buttons There are a variety of buttons to choose from for different situations such as RaisedButton, FloatingActionButton, FlatButton, IconButton, PopupMenuButton, and ButtonBar.

In the next section, you'll learn how to customize the Scaffoldbody property by nesting widgets to build the page content.

SafeArea

The SafeArea widget is a must for today's devices such as the iPhone X or Android devices with a notch (a partial cut‐out obscuring the screen usually located on the top portion of the device). The SafeArea widget automatically adds sufficient padding to the child widget to avoid intrusions by the operating system. You can optionally pass minimum padding or a Boolean value to not enforce padding on the top, bottom, left, or right.

Container

The Container widget has an optional child widget property and can be used as a decorated widget with a custom border, color, constraint, alignment, transform (such as rotating the widget), and more. This widget can be utilized as an empty placeholder (invisible), and if a child is omitted, it sizes to the full available screen size.

Text

You've already used the Text widget in the preceding examples; it's an easy widget to use but also customizable. The Text constructor takes the arguments string, style, maxLines, overflow, textAlign, and others.

Text(
 'Flutter World for Mobile',
 style: TextStyle(
  fontSize: 24.0,
  color: Colors.deepPurple,
  decoration: TextDecoration.underline,
  decorationColor: Colors.deepPurpleAccent,
  decorationStyle: TextDecorationStyle.dotted,
  fontStyle: FontStyle.italic,
  fontWeight: FontWeight.bold,
 ),
 maxLines: 4,
 overflow: TextOverflow.ellipsis,
 textAlign: TextAlign.justify,
),

RichText

The RichText widget is a great way to display text using multiple styles. The RichText widget takes TextSpan as children to style different parts of the strings (Figure 6.1).

Screenshot of RichText with TextSpan.

FIGURE 6.1: RichText with TextSpan

Column

A Column widget (Figures 6.2 and 6.3) displays its children vertically. It takes a children property containing an array of List<Widget>. The children align vertically without taking up the full height of the screen. Each child widget can be embedded in an Expanded widget to fill available space. You can use CrossAxisAlignment, MainAxisAlignment, and MainAxisSize to align and size how much space is occupied on the main axis.

Screenshot of column widget.

FIGURE 6.2: Column widget

Screenshot of column widget rendered in app.

FIGURE 6.3: Column widget rendered in app

Column(
 crossAxisAlignment: CrossAxisAlignment.center,
 mainAxisAlignment: MainAxisAlignment.spaceEvenly,
 mainAxisSize: MainAxisSize.max,
 children: <Widget>[
  Text('Column 1'),
  Divider(),
  Text('Column 2'),
  Divider(),
  Text('Column 3'),
 ],
), 

Row

A Row widget (Figures 6.4 and 6.5) displays its children horizontally. It takes a children property containing an array of List<Widget>. The same properties that the Column contains are applied to the Row widget with the exception that the alignment is horizontal, not vertical.

Screenshot of row widget.

FIGURE 6.4: Row widget

Screenshot of row widget rendered in app.

FIGURE 6.5: Row widget rendered in app

Row(
 crossAxisAlignment: CrossAxisAlignment.start,
 mainAxisAlignment: MainAxisAlignment.spaceEvenly,
 mainAxisSize: MainAxisSize.max,
 children: <Widget>[
  Row(
   children: <Widget>[
    Text('Row 1'),
    Padding(padding: EdgeInsets.all(16.0),),
    Text('Row 2'),
    Padding(padding: EdgeInsets.all(16.0),),
    Text('Row 3'),
   ],
  ),
 ],
), 

Column and Row Nesting

A great way to create unique layouts is to combine Column and Row widgets for individual needs. Imagine having a journal page with Text in a Column with a nested Row containing a list of images (Figures 6.6 and 6.7).

Screenshot of Column and Row nesting.

FIGURE 6.6: Column and Row nesting

Screenshot of Column and Row widgets rendered in app.

FIGURE 6.7: Column and Row widgets rendered in app

Add a Row widget inside the Column widget. Use mainAxisAlignment:MainAxisAlignment.spaceEvenly and add three Text widgets.

Column(
 crossAxisAlignment: CrossAxisAlignment.start,
 mainAxisAlignment: MainAxisAlignment.spaceEvenly,
 mainAxisSize: MainAxisSize.max,
 children: <Widget>[
  Text('Columns and Row Nesting 1',),
  Text('Columns and Row Nesting 2',),
  Text('Columns and Row Nesting 3',),
  Padding(padding: EdgeInsets.all(16.0),),
  Row(
   mainAxisAlignment: MainAxisAlignment.spaceEvenly,
   children: <Widget>[
    Text('Row Nesting 1'),
    Text('Row Nesting 2'),
    Text('Row Nesting 3'),
   ],
  ),
 ],
),

Buttons

There are a variety of buttons to choose from, depending on the situation, such as FloatingActionButton, FlatButton, IconButton, RaisedButton, PopupMenuButton, and ButtonBar.

FloatingActionButton

The FloatingActionButton widget is usually placed on the bottom right or center of the main screen in the ScaffoldfloatingActionButton property. Use the FloatingActionButtonLocation widget to either dock (notch) or float above the navigation bar. To dock a button to the navigation bar, use the BottomAppBar widget. By default, it's a circular button but can be customized to a stadium shape by using the named constructor FloatingActionButton.extended. In the example code, I commented out the stadium shape button for you to test.

floatingActionButtonLocation: FloatingActionButtonLocation.endDocked,
floatingActionButton: FloatingActionButton(
 onPressed: () {},
 child: Icon(Icons.play_arrow),
 backgroundColor: Colors.lightGreen.shade100,
),
// or 
// This creates a Stadium Shape FloatingActionButton 
// floatingActionButton: FloatingActionButton.extended(
//  onPressed: () {},
//  icon: Icon(Icons.play_arrow),
//  label: Text('Play'),
// ),
bottomNavigationBar: BottomAppBar(
 hasNotch: true,
 color: Colors.lightGreen.shade100,
 child: Row(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: <Widget>[
   Icon(Icons.pause),
   Icon(Icons.stop),
   Icon(Icons.access_time),
   Padding(
    padding: EdgeInsets.all(32.0),
   ),
  ],
 ),
),

Figure 6.8 shows the FloatingActionButton widget on the bottom right of the screen with the notch enabled.

Screenshot of FloatingActionButton with notch.

FIGURE 6.8: FloatingActionButton with notch

FlatButton

The FlatButton widget is the most minimalist button used; it displays a text label without any borders or elevation (shadow). Since the text label is a widget, you could use an Icon widget instead or another widget to customize the button. color, highlightColor, splashColor, textColor, and other properties can be customized.

// Default - left button
FlatButton(
 onPressed: () {},
 child: Text('Flag'),
),

// Customize - right button
FlatButton(
 onPressed: () {},
 child: Icon(Icons.flag),
 color: Colors.lightGreen,
 textColor: Colors.white,
),

Figure 6.9 shows the default FlatButton widget on the left and the customized FlatButton widget on the right.

Screenshot of FlatButton.

FIGURE 6.9: FlatButton

RaisedButton

The RaisedButton widget adds a dimension, and the elevation (shadow) increases when the user presses the button.

// Default - left button
RaisedButton(
 onPressed: () {},
 child: Text('Save'),
),

// Customize - right button
RaisedButton(
 onPressed: () {},
 child: Icon(Icons.save),
 color: Colors.lightGreen,
),

Figure 6.10 shows the default RaisedButton widget on the left and the customized RaisedButton widget on the right.

Screenshot of RaisedButton.

FIGURE 6.10: RaisedButton

IconButton

The IconButton widget uses an Icon widget on a Material Component widget that reacts to touches by filling with color (ink). The combination creates a nice tap effect, giving the user feedback that an action has started.

// Default - left button
IconButton(
 onPressed: () {},
 icon: Icon(Icons.flight),
),

// Customize - right button
IconButton(
 onPressed: () {},
 icon: Icon(Icons.flight),
 iconSize: 42.0,
 color: Colors.white,
 tooltip: 'Flight',
),

Figure 6.11 shows the default IconButton widget on the left and the customized IconButton widget on the right.

Screenshot of IconButton.

FIGURE 6.11: IconButton

PopupMenuButton

The PopupMenuButton widget displays a list of menu items. When a menu item is pressed, the value passes to the onSelected property. A common use of this widget is placing it on the top right of the AppBar widget for the user to select different menu options. Another example is to place the PopupMenuButton widget in the middle of the AppBar widget showing a list of search filters.

ButtonBar

The ButtonBar widget (Figure 6.12) aligns buttons horizontally. In this example, the ButtonBar widget is a child of a Container widget to give it a background color.

Screenshot of ButtonBar.

FIGURE 6.12: ButtonBar

Container(
 color: Colors.white70,
 child: ButtonBar(
  alignment: MainAxisAlignment.spaceEvenly,
  children: <Widget>[
   IconButton(
    icon: Icon(Icons.map),
    onPressed: () {},
   ),
   IconButton(
    icon: Icon(Icons.airport_shuttle),
    onPressed: () {},
   ),
   IconButton(
    icon: Icon(Icons.brush),
    onPressed: () {},
   ),
  ],
 ),
),  

USING IMAGES AND ICONS

Images can make an app look tremendous or ugly depending on the quality of the artwork. Images, icons, and other resources are commonly embedded in an app.

AssetBundle

The AssetBundle class provides access to custom resources such as images, fonts, audio, data files, and more. Before a Flutter app can use a resource, you must declare it in the pubspec.yaml file.

// pubspec.yaml file to edit
# To add assets to your application, add an assets section, like this:
assets:
 —assets/images/logo.png
 —assets/images/work.png
 —assets/data/seed.json

Instead of declaring each asset, which can get very long, you can declare all the assets in each directory. Make sure you end the directory name with a forward slash, /. Throughout the book, I'll use this approach when adding assets to the projects.

// pubspec.yaml file to edit
# To add assets to your application, add an assets section, like this:
assets:
 —assets/images/
 —assets/data/

Image

The Image widget displays an image from a local or URL (web) source. To load an Image widget, there are a few different constructors to use.

  • Image()—Retrieves image from an ImageProvider class
  • Image.asset()—Retrieves image from an AssetBundle class using a key
  • Image.file()—Retrieves image from a File class
  • Image.memory()—Retrieves image from a Uint8List class
  • Image.network()—Retrieves image from a URL path

Press Ctrl+Spacebar to invoke the code completion for the available options (Figure 6.13).

Screenshot of image code completion.

FIGURE 6.13: Image code completion

As a side note, the Image widget also supports animated GIFs.

The following sample uses the default Image constructor to initialize the image and fit arguments. The image argument is set by using the AssetImage() constructor with the default bundle location of the logo.png file. You can use the fit argument to size the Image widget with the BoxFit options, such as contain, cover, fill, fitHeight, fitWidth, or none (Figure 6.14).

Illustration of images loaded locally and from network (web)

FIGURE 6.14: Images loaded locally and from network (web)

// Image - on the left side
Image(
 image: AssetImage("assets/images/logo.png"),
 fit: BoxFit.cover,
),

// Image from a URL - on the right side
Image.network(
'https://flutter.io/images/catalog-widget-placeholder.png',
),

If you add color to the image, it colorizes the image portion and leaves any transparencies alone, giving a silhouette look (Figure 6.15).

Illustration of silhouette-style image.

FIGURE 6.15: Silhouette‐style image

// Image
 Image(
 image: AssetImage("assets/images/logo.png"),
 color: Colors.deepOrange,
 fit: BoxFit.cover,
),

Icon

The Icon widget is drawn with a glyph from a font described in IconData. Flutter's icons.dart file has the full list of icons available from the font MaterialIcons. A great way to add custom icons is to add to the AssetBundle fonts containing glyphs. Once example is Font Awesome, which has a high‐quality list of icons and a Flutter package. Of course, there are many other high‐quality icons available from other sources.

The Icon widget allows you to change the Icon widget's color, size, and other properties (Figure 6.16).

Illustration of icons with custom sizes.

FIGURE 6.16: Icons with custom sizes

Icon(
 Icons.brush,
 color: Colors.lightBlue,
 size: 48.0,
),

USING DECORATORS

Decorators help to convey a message depending on the user's action or customize the look and feel of a widget. There are different types of decorators for each task.

  • Decoration—The base class to define other decorations.
  • BoxDecoration—Provides many ways to draw a box with border, body, and boxShadow.
  • InputDecoration—Used in TextField and TextFormField to customize the border, label, icon, and styles. This is a great way to give the user feedback on data entry, specifying a hint, an error, an alert icon, and more.

A BoxDecoration class (Figure 6.17) is a great way to customize a Container widget to create shapes by setting the borderRadius, color, gradient, and boxShadow properties.

Illustration of BoxDecoration applied to a Container.

FIGURE 6.17: BoxDecoration applied to a Container

// BoxDecoration
Container(
 height: 100.0,
 width: 100.0,
 decoration: BoxDecoration(
  borderRadius: BorderRadius.all(Radius.circular(20.0)),
  color: Colors.orange,
  boxShadow: [
   BoxShadow(
    color: Colors.grey,
    blurRadius: 10.0,
    offset: Offset(0.0, 10.0),
   )
  ],
 ),
),

The InputDecoration class (Figure 6.18) is used with a TextField or TextFormField to specify labels, borders, icons, hints, errors, and styles. This is helpful in communicating with the user as they enter data. For the border property shown here, I am implementing two ways to customize it, with UnderlineInputBorder and with OutlineInputBorder:

Illustration of InputDecoration with OutlineInputBorder and default border.

FIGURE 6.18: InputDecoration with OutlineInputBorder and default border

// TextField
TextField(
 keyboardType: TextInputType.text,
 style: TextStyle(
  color: Colors.grey.shade800,
  fontSize: 16.0,
 ),
 decoration: InputDecoration(
  labelText: "Notes",
  labelStyle: TextStyle(color: Colors.purple),
  //border: UnderlineInputBorder(),
  border: OutlineInputBorder(),
    
 ),
),

// TextFormField
TextFormField(
 decoration: InputDecoration(
  labelText: 'Enter your notes',
 ),
),

USING THE FORM WIDGET TO VALIDATE TEXT FIELDS

There are different ways to use text field widgets to retrieve, validate, and manipulate data. The Form widget is optional, but the benefits of using a Form widget are to validate each text field as a group. You can group TextFormField widgets to manually or automatically validate them. The TextFormField widget wraps a TextField widget to provide validation when enclosed in a Form widget.

If all text fields pass the FormStatevalidate method, then it returns true. If any text fields contain errors, it displays the appropriate error message for each text field, and the FormStatevalidate method returns false. This process gives you the ability to use FormState to check for any validation errors instead of checking each text field for errors and not allowing the posting of invalid data.

The Form widget needs a unique key to identify it and is created by using GlobalKey. This GlobalKey value is unique across the entire app.

In the next example, you'll create a form with two TextFormFields (Figure 6.19) to enter an item and quantity to order. You'll create an Order class to hold the item and quantity and fill the order once the validation passes.

Screenshot of the Form and TextFormField layout.

FIGURE 6.19: The Form and TextFormField layout

CHECKING ORIENTATION

Under certain scenarios, knowing the device orientation helps in laying out the appropriate UI. There are two ways to figure out orientation, MediaQuery.of(context).orientation and OrientationBuilder.

A huge note on OrientationBuilder: it returns the amount of space available to the parent to figure out orientation. This means it does not guarantee the actual device orientation. I prefer using MediaQuery to obtain the actual device orientation because of its accuracy.

SUMMARY

In this chapter, you learned about the most commonly used (basic) widgets. These basic widgets are the building blocks to designing mobile apps. You also explored different types of buttons to choose depending on the situation. You learned how to add assets to your app via AssetBundle by listing items in the pubspec.yaml file. You used the Image widget to load images from the local device or a web server through a URL string. You saw how the Icon widget gives you the ability to load icons by using the MaterialIcons font library.

To modify the appearance of widgets, you learned how to use BoxDecoration. To improve giving users feedback on data entry, you implemented InputDecoration. Validating multiple text field data entries can be cumbersome, but you can use the Form widget to manually or automatically validate them. Lastly, using MediaQuery to find out the current device orientation is extremely powerful in any mobile app to lay out widgets depending on the orientation.

In the next chapter, you'll learn how to use animations. You'll start by using widgets such as AnimatedContainer, AnimatedCrossFade, and AnimatedOpacity and finish with the powerful AnimationController for custom animation.

image WHAT YOU LEARNED IN THIS CHAPTER

TOPIC KEY CONCEPTS
Using basic widgets You learned to use Scaffold, SafeArea, AppBar, Container, Text, RichText, Column, Row, Column and RowNesting, Buttons, FloatingActionButton, FlatButton, RaisedButton, IconButton, PopupMenuButton, and ButtonBar.
Using images You learned to use AssetBundle, Image, and Icon.
Using decorators You learned to use Decoration, BoxDecoration, and InputDecoration.
Using forms for text field validation You learned to use the Form widget to validate each TextFormField as a group.
Detecting orientation You learned to use MediaQuery.of(context).orientation and OrientationBuilder to detect device orientation.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset