MDC-102 Flutter: структура и расположение материала

1. Введение

logo_components_color_2x_web_96dp.png

Компоненты Material (MDC) помогают разработчикам внедрять Material Design. Созданные командой инженеров и UX-дизайнеров Google, MDC включают в себя десятки красивых и функциональных компонентов пользовательского интерфейса и доступны для Android, iOS, веб-приложений и Flutter.material.io/develop

В практическом занятии MDC-101 вы использовали два компонента Material для создания страницы входа в систему: текстовые поля и кнопки с эффектом чернильной ряби. Теперь давайте расширим эту основу, добавив навигацию, структуру и данные.

Что вы построите

В этом практическом занятии вы создадите главный экран для приложения Shrine , приложения электронной коммерции, продающего одежду и товары для дома. Он будет содержать:

  • Панель приложений верхнего уровня
  • Список товаров в виде сетки

Android

iOS

Приложение для электронной коммерции с верхней панелью приложений и сеткой товаров.

Приложение для электронной коммерции с верхней панелью приложений и сеткой товаров.

Компоненты и подсистемы Material Flutter в этом практическом занятии.

  • Верхняя панель приложений
  • Сетки
  • Карты

Как бы вы оценили свой уровень опыта в разработке на Flutter?

Новичок Средний Профессионал

2. Настройте среду разработки Flutter.

Для выполнения этой лабораторной работы вам понадобятся два программных компонента: Flutter SDK и редактор .

Вы можете выполнить это практическое задание, используя любое из следующих устройств:

  • Физическое устройство Android или iOS , подключенное к компьютеру и настроенное на режим разработчика.
  • Симулятор iOS (требуется установка инструментов Xcode).
  • Эмулятор Android (требуется настройка в Android Studio).
  • Для работы требуется браузер (для отладки необходим Chrome).
  • Если вы разрабатываете настольное приложение для Windows , Linux или macOS , вам необходимо работать на той платформе, на которой вы планируете его развернуть. Таким образом, если вы хотите разработать настольное приложение для Windows, вам необходимо использовать Windows для доступа к соответствующей цепочке сборки. Существуют специфические требования к операционной системе, которые подробно описаны в документации docs.flutter.dev/desktop .

3. Скачайте стартовое приложение Codelab.

Продолжение темы MDC-101?

Если вы прошли курс MDC-101, ваш код должен быть готов для этого практического занятия. Перейдите к шагу: Добавьте верхнюю панель приложения .

Начинать с нуля?

Скачайте стартовое приложение Codelab.

Стартовое приложение находится в каталоге material-components-flutter-codelabs-102-starter_and_101-complete/mdc_100_series .

...или клонируйте его с GitHub

Чтобы клонировать этот код с GitHub, выполните следующие команды:

git clone https://github.com/material-components/material-components-flutter-codelabs.git
cd material-components-flutter-codelabs/mdc_100_series
git checkout 102-starter_and_101-complete

Откройте проект и запустите приложение.

  1. Откройте проект в выбранном вами редакторе.
  2. Следуйте инструкциям, чтобы запустить приложение в разделе « Начало работы: Тестовая версия » для выбранного вами редактора.

Успех! На вашем устройстве должна отобразиться страница входа в Shrine, созданная на основе кода MDC-101.

Android

iOS

Страница входа с полями для имени пользователя и пароля, кнопками «Отмена» и «Далее».

Страница входа с полями для имени пользователя и пароля, кнопками «Отмена» и «Далее».

Теперь, когда экран входа в систему выглядит хорошо, давайте добавим в приложение несколько товаров.

4. Добавьте верхнюю панель приложений.

Сейчас, если вы нажмете кнопку «Далее», вы увидите главный экран с надписью «Вы это сделали!». Это здорово! Но теперь пользователю не нужно ничего делать, и он не понимает, где находится в приложении. Чтобы помочь, пора добавить навигацию.

Material Design предлагает шаблоны навигации, обеспечивающие высокую степень удобства использования. Одним из наиболее заметных компонентов является верхняя панель приложений.

Для обеспечения навигации и быстрого доступа пользователей к другим действиям добавим верхнюю панель приложения.

Добавьте виджет AppBar

В home.dart добавьте AppBar в Scaffold и удалите выделенную const :

return const Scaffold(
  // TODO: Add app bar (102)
  appBar: AppBar(
    // TODO: Add buttons and title (102)
  ),

Добавление AppBar в поле appBar appBar: элемента Scaffold позволяет получить идеальную компоновку совершенно бесплатно, размещая AppBar вверху страницы, а основную часть — под ним.

Добавить текстовый виджет

В home.dart добавьте заголовок к AppBar:

// TODO: Add app bar (102)
  appBar: AppBar(
    // TODO: Add buttons and title (102)
    title: const Text('SHRINE'),
    // TODO: Add trailing buttons (102)

Сохраните свой проект.

Android

iOS

Панель приложений с заголовком «Святилище»

Панель приложений с заголовком «Святилище»

Во многих панелях приложений рядом с заголовком есть кнопка. Давайте добавим в наше приложение значок меню.

Добавить кнопку-иконку перед кнопкой

Оставаясь в файле home.dart , установите IconButton для поля leading: панели AppBar. (Разместите его перед полем title: чтобы имитировать порядок от leading: к end:):

    // TODO: Add buttons and title (102)
    leading: IconButton(
      icon: const Icon(
        Icons.menu,
        semanticLabel: 'menu',
      ),
      onPressed: () {
        print('Menu button');
      },
    ),

Сохраните свой проект.

Android

iOS

Панель приложений с заголовком «Святилище» и иконкой меню-гамбургера.

Панель приложений с заголовком «Святилище» и иконкой меню-гамбургера.

Значок меню (также известный как «гамбургер») появляется именно там, где вы ожидаете его увидеть.

Также можно добавить кнопки в конце заголовка. Во Flutter они называются «действиями».

Добавить действия

Здесь достаточно места ещё для двух кнопок-иконок.

Добавьте их в экземпляр AppBar после заголовка:

// TODO: Add trailing buttons (102)
actions: <Widget>[
  IconButton(
    icon: const Icon(
      Icons.search,
      semanticLabel: 'search',
    ),
    onPressed: () {
      print('Search button');
    },
  ),
  IconButton(
    icon: const Icon(
      Icons.tune,
      semanticLabel: 'filter',
    ),
    onPressed: () {
      print('Filter button');
    },
  ),
],

Сохраните свой проект. Ваш главный экран должен выглядеть примерно так:

Android

iOS

Панель приложений с заголовком «Святилище», иконкой меню-гамбургера и иконками поиска и настройки.

Панель приложений с заголовком «Святилище», иконкой меню-гамбургера и иконками поиска и настройки.

Теперь приложение имеет кнопку в начале, заголовок и два действия справа. Панель приложения также отображает эффект возвышения с помощью едва заметной тени, которая показывает, что оно находится на другом слое, чем контент.

5. Добавьте карточку в сетку.

Теперь, когда наше приложение имеет некоторую структуру, давайте организуем контент, разместив его в виде карточек.

Добавить GridView

Начнём с добавления одной карточки под верхней панелью приложения. Сам по себе виджет Card не содержит достаточно информации, чтобы отобразить её на видном месте, поэтому нам нужно будет поместить его в виджет GridView .

Замените элемент Center в теле Scaffold на GridView:

// TODO: Add a grid view (102)
body: GridView.count(
  crossAxisCount: 2,
  padding: const EdgeInsets.all(16.0),
  childAspectRatio: 8.0 / 9.0,
  // TODO: Build a grid of cards (102)
  children: <Widget>[Card()],
),

Давайте разберем этот код. GridView вызывает конструктор count() поскольку количество отображаемых элементов является подсчитываемым, а не бесконечным. Но ему требуется дополнительная информация для определения своей компоновки.

crossAxisCount: указывает, сколько элементов нужно разместить по горизонтали. Нам нужно 2 столбца.

Поле padding: ` задаёт пространство со всех четырёх сторон GridView. Конечно, вы не увидите отступы с нижних или боковых сторон, потому что рядом с ними ещё нет дочерних элементов GridView.

Поле childAspectRatio: определяет размер элементов на основе соотношения сторон (ширина/высота).

По умолчанию GridView создает плитки одинакового размера.

У нас есть одна карточка, но она пустая. Давайте добавим дочерние виджеты к нашей карточке.

Расположите содержимое

На карточках должны быть области для изображения, заголовка и дополнительного текста.

Обновите дочерние элементы GridView:

// TODO: Build a grid of cards (102)
children: <Widget>[
  Card(
    clipBehavior: Clip.antiAlias,
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        AspectRatio(
          aspectRatio: 18.0 / 11.0,
          child: Image.asset('assets/diamond.png'),
        ),
        Padding(
          padding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 8.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Text('Title'),
              const SizedBox(height: 8.0),
              Text('Secondary Text'),
            ],
          ),
        ),
      ],
    ),
  )
],

Этот код добавляет виджет «Колонка», используемый для вертикального размещения дочерних виджетов.

crossAxisAlignment: field задает значение CrossAxisAlignment.start , что означает «выровнять текст по переднему краю».

Виджет AspectRatio определяет форму изображения независимо от его типа.

Отступы немного выдвигают текст сбоку.

Два текстовых виджета расположены вертикально друг над другом с 8 точками пустого пространства между ними ( SizedBox ). Мы создаём ещё одну колонку , чтобы разместить их внутри отступа (Padding).

Сохраните свой проект.

Android

iOS

Один элемент, содержащий изображение, заголовок и дополнительный текст.

Один элемент, содержащий изображение, заголовок и дополнительный текст.

На этом предварительном просмотре вы можете увидеть, что карточка отстоит от края, имеет закругленные углы и тень (которая подчеркивает возвышение карточки). Вся эта форма называется «контейнером» в Material Design. (Не путать с классом виджета под названием Container .)

Карты обычно выставляются в коллекции вместе с другими картами. Давайте разложим их в виде сетки.

6. Соберите коллекцию карточек.

Если на экране присутствует несколько карточек, они группируются в одну или несколько коллекций. Карточки в коллекции лежат в одной плоскости, то есть имеют одинаковую высоту в состоянии покоя (если только карточки не подняты или не перетащены, но мы не будем этого делать).

Умножьте карты на коллекцию

Сейчас наша карточка создается непосредственно в поле ` children: ` элемента `GridView`. Это большой объем вложенного кода, который сложно читать. Давайте вынесем его в функцию, которая сможет генерировать столько пустых карточек, сколько нам нужно, и возвращать список карточек.

Создайте новую приватную функцию выше функции build() (помните, что функции, начинающиеся с подчеркивания, являются приватными API):

// TODO: Make a collection of cards (102)
List<Card> _buildGridCards(int count) {
  List<Card> cards = List.generate(
    count,
    (int index) {
      return Card(
        clipBehavior: Clip.antiAlias,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            AspectRatio(
              aspectRatio: 18.0 / 11.0,
              child: Image.asset('assets/diamond.png'),
            ),
            Padding(
              padding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 8.0),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: const <Widget>[
                  Text('Title'),
                  SizedBox(height: 8.0),
                  Text('Secondary Text'),
                ],
              ),
            ),
          ],
        ),
      );
    },
  );
  return cards;
}

Присвойте сгенерированные карточки children полям GridView. Не забудьте заменить весь код в GridView следующим новым кодом :

// TODO: Add a grid view (102)
body: GridView.count(
  crossAxisCount: 2,
  padding: const EdgeInsets.all(16.0),
  childAspectRatio: 8.0 / 9.0,
  children: _buildGridCards(10) // Replace
),

Сохраните свой проект.

Android

iOS

сетка элементов, содержащая изображение, заголовок и дополнительный текст.

сетка элементов, содержащая изображение, заголовок и дополнительный текст.

Карточки есть, но пока ничего не отображаются. Сейчас самое время добавить данные о товаре.

Добавить данные о товаре

В приложении есть несколько товаров с изображениями, названиями и ценами. Давайте добавим это к виджетам, которые у нас уже есть в карточке.

Затем в home.dart импортируйте новый пакет и несколько файлов, которые мы предоставили для модели данных:

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';

import 'model/product.dart';
import 'model/products_repository.dart';

Наконец, измените функцию _buildGridCards() так, чтобы она получала информацию о товаре, и используйте эти данные в карточках:

// TODO: Make a collection of cards (102)

// Replace this entire method
List<Card> _buildGridCards(BuildContext context) {
  List<Product> products = ProductsRepository.loadProducts(Category.all);

  if (products.isEmpty) {
    return const <Card>[];
  }

  final ThemeData theme = Theme.of(context);
  final NumberFormat formatter = NumberFormat.simpleCurrency(
      locale: Localizations.localeOf(context).toString());

  return products.map((product) {
    return Card(
      clipBehavior: Clip.antiAlias,
      // TODO: Adjust card heights (103)
      child: Column(
        // TODO: Center items on the card (103)
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          AspectRatio(
            aspectRatio: 18 / 11,
            child: Image.asset(
              product.assetName,
              package: product.assetPackage,
             // TODO: Adjust the box size (102)
            ),
          ),
          Expanded(
            child: Padding(
              padding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 8.0),
              child: Column(
               // TODO: Align labels to the bottom and center (103)
               crossAxisAlignment: CrossAxisAlignment.start,
                // TODO: Change innermost Column (103)
                children: <Widget>[
                 // TODO: Handle overflowing labels (103)
                 Text(
                    product.name,
                    style: theme.textTheme.titleLarge,
                    maxLines: 1,
                  ),
                  const SizedBox(height: 8.0),
                  Text(
                    formatter.format(product.price),
                    style: theme.textTheme.titleSmall,
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }).toList();
}

Примечание: Пока что компиляция и запуск невозможны. У нас есть ещё одно изменение.

Кроме того, измените функцию build() таким образом, чтобы она передавала BuildContext в _buildGridCards() перед попыткой компиляции:

// TODO: Add a grid view (102)
body: GridView.count(
  crossAxisCount: 2,
  padding: const EdgeInsets.all(16.0),
  childAspectRatio: 8.0 / 9.0,
  children: _buildGridCards(context) // Changed code
),

Перезапустите приложение.

Android

iOS

Сетка из элементов, содержащая изображение, название товара и цену.

Сетка из элементов, содержащая изображение, название товара и цену.

Вы можете заметить, что мы не добавляем вертикального пространства между карточками. Это потому, что по умолчанию у них есть отступы в 4 пункта сверху и снизу.

Сохраните свой проект.

Данные о товаре отображаются, но вокруг изображений остается лишнее пространство. По умолчанию изображения отрисовываются с параметром BoxFit , равным .scaleDown (в данном случае). Давайте изменим это на .fitWidth , чтобы они немного увеличились в масштабе и убрали лишнее пустое пространство.

Добавьте к изображению поле ` fit: ` со значением ` BoxFit.fitWidth :

  // TODO: Adjust the box size (102)
  fit: BoxFit.fitWidth,

Android

iOS

Сетка элементов, содержащая обрезанное изображение, название товара и цену.

Теперь наши товары отображаются в приложении безупречно!

7. Поздравляем!

Наше приложение имеет простую структуру, которая ведет пользователя от экрана входа в систему к главному экрану, где можно просматривать товары. Всего несколькими строками кода мы добавили верхнюю панель приложения (с заголовком и тремя кнопками) и карточки (для отображения контента приложения). Теперь наш главный экран прост и функционален, имеет базовую структуру и содержит интерактивный контент.

Следующие шаги

Для верхней панели приложения, карточки, текстового поля и кнопки мы использовали четыре основных компонента из библиотеки Material Flutter! Вы можете узнать больше, посетив каталог виджетов Material Components .

Хотя наше приложение полностью функционально, оно пока не выражает какой-либо конкретный бренд или точку зрения. В курсе MDC-103: Тематическое оформление в стиле Material Design с использованием цвета, формы, рельефа и шрифта , мы настроим стиль этих компонентов, чтобы выразить яркий, современный бренд.

Мне удалось выполнить это практическое задание за разумное время и с разумными затратами усилий.

Полностью согласен Соглашаться Нейтральный Не согласен Категорически не согласен

Я хотел бы и в будущем продолжать использовать компоненты Material.

Полностью согласен Соглашаться Нейтральный Не согласен Категорически не согласен