1. Introduction
What are Material Design and the Material Flutter library?
Material Design
is a system for building bold and beautiful digital products. By uniting style, branding, interaction, and motion under a consistent set of principles and components, product teams can realize their greatest design potential.
The
Material Flutter library
includes Flutter widgets which implement the designs of Material Design components (
MDC
for short) to create a consistent user experience across apps and platforms. As the Material Design system evolves, these components are updated to ensure consistent pixel-perfect implementation, adhering to Google's front-end development standards.
In this codelab, you'll build a login page using several of Material Flutter's components.
What you'll build
This codelab is the first of four codelabs that will guide you through building an app called
Shrine
, an e-commerce app that sells clothing and home goods. It will demonstrate how you can customize components to reflect any brand or style using Material Flutter.
In this codelab, you'll build a login page for Shrine that contains:
- An image of Shrine's logo
- The name of the app (Shrine)
- Two text fields, one for entering a username and the other for a password
- Two buttons
Android
| iOS
|
|
|
Material Flutter components and subsystems in this codelab
- Text field
- Button
- Ink ripple (a visual form of feedback for touch events)
How would you rate your level of experience with Flutter development?
Novice
Intermediate
Proficient
3. Download the codelab starter app
The starter project is located in the
material-components-flutter-codelabs-101-starter/mdc_100_series
directory.
...or clone it from GitHub
To clone this codelab from GitHub, run the following commands:
git clone https://github.com/material-components/material-components-flutter-codelabs.git
cd material-components-flutter-codelabs/mdc_100_series
git checkout 101-starter
Open the project and run the app
- Open the project in your editor of choice.
- Follow the instructions to "Run the app" in
Get Started: Test drive
for your chosen editor.
Success! The starter code for Shrine's login page should be running on your device. You should see the Shrine logo and the name "Shrine" just below it.
Android
| iOS
|
|
|
Let's look at the code.
Open up
login.dart
. It should contain this:
import 'package:flutter/material.dart';
class LoginPage extends StatefulWidget {
const LoginPage({Key? key}) : super(key: key);
@override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
// TODO: Add text editing controllers (101)
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: ListView(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
children: <Widget>[
const SizedBox(height: 80.0),
Column(
children: <Widget>[
Image.asset('assets/diamond.png'),
const SizedBox(height: 16.0),
const Text('SHRINE'),
],
),
const SizedBox(height: 120.0),
// TODO: Remove filled: true values (103)
// TODO: Add TextField widgets (101)
// TODO: Add button bar (101)
],
),
),
);
}
}
Notice that it contains an
import
statement and two new classes:
- The
import
statement allows using the Material library in this file.
- The
LoginPage
class represents the entire page displayed in the simulator.
- The
_LoginPageState
class's
build()
function controls how all the widgets in our UI are created.
4. Add TextField widgets
To begin, we'll add two text fields to our login page, where users enter their username and password. We'll use the TextField widget, which displays a floating label and activates a touch ripple.
This page is structured primarily with a
ListView
, which arranges its children in a scrollable column. Let's put text fields below.
Add the TextField widgets
Add two new text fields and a spacer after
const SizedBox(height: 120.0)
.
// TODO: Add TextField widgets (101)
// [Name]
TextField(
decoration: const InputDecoration(
filled: true,
labelText: 'Username',
),
),
// spacer
const SizedBox(height: 120.0),
// [Password]
TextField(
decoration: const InputDecoration(
filled: true,
labelText: 'Password',
),
obscureText: true,
),
The text fields each have a
decoration:
field that takes an
InputDecoration
widget. The
filled:
field means the background of the text field is lightly filled in to help people recognize the tap or touch target area of the text field. The second text field's
obscureText: true
value automatically replaces the input that the user types with bullets, which is appropriate for passwords.
Save your project (with the keystroke: command + s) which performs a
Hot Reload
.
You should now see a page with two text fields for Username and Password! Check out the floating label animation:
Android
| iOS
|
|
|
5. Add buttons
Next, we'll add two buttons to our login page: "Cancel" and "Next." We'll use two kinds of button widgets: the
TextButton
and the
ElevatedButton
.
Add the OverflowBar
After the text fields, add the
OverflowBar
to the
ListView
's children:
// TODO: Add button bar (101)
OverflowBar(
alignment: MainAxisAlignment.end,
// TODO: Add a beveled rectangular border to CANCEL (103)
children: <Widget>[
// TODO: Add buttons (101)
],
),
The
OverflowBar
arranges its children in a row.
Then add two buttons to the
OverflowBar
's list of
children
:
// TODO: Add buttons (101)
TextButton(
child: const Text('CANCEL'),
onPressed: () {
// TODO: Clear the text fields (101)
},
),
// TODO: Add an elevation to NEXT (103)
// TODO: Add a beveled rectangular border to NEXT (103)
ElevatedButton(
child: const Text('NEXT'),
onPressed: () {
// TODO: Show the next page (101)
},
),
Save your project. Under the last text field, you should see two buttons appear:
Android
| iOS
|
|
|
The
OverflowBar
handles the layout work for you. It positions the buttons horizontally, so they appear side by side.
Touching a button initiates an ink ripple animation, without causing anything else to happen. Let's add functionality into the anonymous
onPressed:
functions, so that the cancel button clears the text fields, and the next button dismisses the screen:
Add TextEditingControllers
To make it possible to clear the text fields' values, we'll add
TextEditingControllers
to control their text.
Right under the
_LoginPageState
class's declaration, add the controllers as
final
variables.
// TODO: Add text editing controllers (101)
final _usernameController = TextEditingController();
final _passwordController = TextEditingController();
On the first text field's
controller:
field, set the
_usernameController
:
// TODO: Add TextField widgets (101)
// [Name]
TextField(
controller: _usernameController,
On the second text field's
controller:
field, now set the
_passwordController
:
// TODO: Add TextField widgets (101)
// [Password]
TextField(
controller: _passwordController,
Edit onPressed
Add a command to clear each controller in the TextButton's
onPressed:
function:
// TODO: Clear the text fields (101)
_usernameController.clear();
_passwordController.clear();
Save your project. Now when you type something into the text fields, hitting cancel clears each field again.
This login form is in good shape! Let's advance our users to the rest of the Shrine app.
Pop
To dismiss this view, we want to
pop
(or remove) this page (which Flutter calls a
route
) off the navigation stack.
In the ElevatedButton's
onPressed:
function, pop the most recent route from the Navigator:
// TODO: Show the next page (101)
Navigator.pop(context);
Lastly, open up
home.dart
and set
resizeToAvoidBottomInset
to
false
in the
Scaffold
:
return Scaffold(
// TODO: Add app bar (102)
// TODO: Add a grid view (102)
body: Center(
child: Text('You did it!'),
),
// TODO: Set resizeToAvoidBottomInset (101)
resizeToAvoidBottomInset: false,
);
Doing this ensures that the keyboard's appearance does not alter the size of the home page or its widgets.
That's it! Save your project. Go ahead and click "Next."
You did it!
Android
| iOS
|
|
|
This screen is the starting point for our next codelab, which you'll work on in MDC-102.