MDC-101 Flutter: Material Components (MDC) Basics

1. Introduction

What are Material Design and Material Components for Flutter?

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.

Material Components for Flutter (MDC-Flutter) unite design and engineering with a library of components that 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. MDC is also available for Android, iOS, and the web.

In this codelab, you'll build a login page using several of MDC 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 MDC-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

Shrine login page on Android

Shrine login page on iOS

MDC-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

2. Set up your Flutter development environment

Download and install the Flutter SDK

To install Flutter, see Get Started: Install. To complete this codelab, you'll only need to complete:

  • Get the Flutter SDK
  • Platform setup for either Android or iOS

Don't worry about warnings from flutter doctor unless they stop you from progressing in the codelab. If you encounter any issues or annoyances, please file an issue on GitHub.

Set up your preferred editor

To set up an editor, see Get Started: Set up an editor.

Choose a device

You'll need a device to run the code one. Choose one of the following options:

  • Create an Android virtual device in Android Studio
  • Use an iOS simulator (requires a Mac with Xcode),
  • Use a physical device

We recommend using a virtual device or simulator.

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

  1. Open the project in your editor of choice.
  2. 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

Shrine logo

Shrine logo

Let's look at the code.

Widgets in login.dart

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 brings Material Components into 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 place text fields at the bottom.

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: 12.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

Shrine logo with username and password fields

Shrine logo with username and password fields

5. Add buttons

Next, we'll add two buttons to our login page: "Cancel" and "Next." We'll use two kinds of MDC 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.

Add the buttons

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

Shrine logo with username and password fields, cancel and next buttons

Shrine logo with username and password fields, cancel and next buttons

The OverflowBar handles the layout work for you. It positions the buttons horizontally, so they appear next to one another.

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

screen that says "you did it"

screen that says "you did it"

This screen is the starting point for our next codelab, which you'll work on in MDC-102.

6. All done

We added text fields and buttons and hardly had to consider layout code. Material Components for Flutter come with a lot of style and can be placed on screen almost effortlessly.

Next steps

Text fields and buttons are two core components in the Material System, but there are many more! You can also explore the rest of the widgets in Flutter's Material Components library.

Alternatively, head over to MDC-102: Material Design Structure and Layout to learn about the components covered in MDC-102 for Flutter.

I was able to complete this codelab with a reasonable amount of time and effort

Strongly agree Agree Neutral Disagree Strongly disagree

I would like to continue using Material Components in the future

Strongly agree Agree Neutral Disagree Strongly disagree