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

1. Введение

logo_comComponents_color_2x_web_96dp.png

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

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

Что ты построишь

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

  • Верхняя панель приложений
  • Сетчатый список, полный продуктов

Андроид

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.

Андроид

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: 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)

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

Андроид

iOS

панель приложения с названием Shrine

панель приложения с названием Shrine

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

Добавьте ведущую кнопку IconButton

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

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

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

Андроид

iOS

панель приложения с названием Shrine и значком гамбургер-меню

панель приложения с названием Shrine и значком гамбургер-меню

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

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

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

Есть место еще для двух IconButtons.

Добавьте их в экземпляр 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');
    },
  ),
],

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

Андроид

iOS

панель приложения с заголовком Shrine и значком гамбургер-меню, а также завершающими значками поиска и настройки.

панель приложения с заголовком Shrine и значком гамбургер-меню, а также завершающими значками поиска и настройки.

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

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

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

Добавить GridView

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

Замените центр тела 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: обеспечивает пространство на всех 4 сторонах 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 решает, какую форму принимает изображение, независимо от того, какой тип изображения предоставляется.

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

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

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

Андроид

iOS

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

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

В этом предварительном просмотре вы можете видеть, что карточка вставлена ​​с края, с закругленными углами и тенью (которая выражает высоту карты). Вся форма в Material называется «контейнером». (Не путать с реальным классом виджета под названием 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
),

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

Андроид

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
),

Горячий перезапуск приложения.

Андроид

iOS

сетка товаров с изображением, названием продукта и ценой

сетка товаров с изображением, названием продукта и ценой

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

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

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

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

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

Андроид

iOS

сетка товаров с обрезанным изображением, названием продукта и ценой

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

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

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

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

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

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

Мне удалось завершить эту кодовую работу, потратив разумное количество времени и усилий.

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

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

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