1. Введение
| Компоненты 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
Откройте проект и запустите приложение.
- Откройте проект в выбранном вами редакторе.
- Следуйте инструкциям, чтобы запустить приложение в разделе « Начало работы: Тестовая версия » для выбранного вами редактора.
Успех! На вашем устройстве должна отобразиться страница входа в 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 с использованием цвета, формы, рельефа и шрифта , мы настроим стиль этих компонентов, чтобы выразить яркий, современный бренд.
















