MDC-103 Flutter: тематика материала с указанием цвета, формы, высоты и типа

1. Введение

logo_comComponents_color_2x_web_96dp.png

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

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

В кодовых лабораториях MDC-101 и MDC-102 вы использовали Material Flutter для создания основ приложения под названием Shrine , приложения для электронной коммерции, которое продает одежду и товары для дома. Это приложение содержит пользовательский поток, который начинается с экрана входа в систему, а затем переносит пользователя на главный экран, на котором отображаются продукты.

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

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

  • Цвет
  • Типография
  • Высота
  • Форма
  • Макет

Андроид

iOS

Страница входа в храм, коричнево-розовая тематика

Страница входа в храм, коричнево-розовая тематика

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

Компоненты и подсистемы 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-102?

Если вы прошли MDC-102, ваш код должен быть готов к использованию в этой лаборатории. Перейдите к шагу: Измените цвета .

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

Загрузите начальное приложение Codelab.

Стартовое приложение находится в каталоге material-components-flutter-codelabs-103-starter_and_102-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 103-starter_and_102-complete

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

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

Успех! На вашем устройстве вы должны увидеть страницу входа в Shrine из предыдущих тестов кода.

Андроид

iOS

бестематическая страница входа в Храм

бестематическая страница входа в Храм

Нажмите «Далее», чтобы увидеть страницу продукта.

Андроид

iOS

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

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

4. Измените цвета

Была создана цветовая схема, представляющая бренд Shrine, и дизайнер хотел бы, чтобы вы реализовали эту цветовую схему во всем приложении Shrine.

Для начала давайте импортируем эти цвета в наш проект.

Создать colors.dart

Создайте новый файл dart в lib с именем colors.dart . Импортируйте material.dart и добавьте const Color :

import 'package:flutter/material.dart';

const kShrinePink50 = Color(0xFFFEEAE6);
const kShrinePink100 = Color(0xFFFEDBD0);
const kShrinePink300 = Color(0xFFFBB8AC);
const kShrinePink400 = Color(0xFFEAA4A4);

const kShrineBrown900 = Color(0xFF442B2D);

const kShrineErrorRed = Color(0xFFC5032B);

const kShrineSurfaceWhite = Color(0xFFFFFBFA);
const kShrineBackgroundWhite = Colors.white;

Пользовательская цветовая палитра

Эта цветовая тема была создана дизайнером с использованием нестандартных цветов (показано на изображении ниже). Он содержит цвета, выбранные из бренда Shrine и примененные к редактору тем материалов, который расширил их для создания более полной палитры. (Эти цвета не входят в цветовую палитру Material 2014 года .)

Редактор тем материалов сгруппировал их по оттенкам с числовой маркировкой, включая метки от 50, 100, 200 до 900 каждого цвета. Shrine использует только оттенки 50, 100 и 300 из розового образца и 900 из коричневого.

d0362cb45c565a8e.jpeg470b0e1c2669ae2.png

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

Теперь, когда у нас есть цвета, которые мы хотим использовать, мы можем применить их к пользовательскому интерфейсу. Мы сделаем это, установив значения виджета ThemeData , который мы применим к экземпляру MaterialApp в верхней части иерархии виджетов.

Настройте ThemeData.light()

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

Давайте импортируем colors.dart в app.dart.

import 'colors.dart';

Затем добавьте следующее в app.dart за пределами класса ShrineApp:

// TODO: Build a Shrine Theme (103)
final ThemeData _kShrineTheme = _buildShrineTheme();

ThemeData _buildShrineTheme() {
  final ThemeData base = ThemeData.light(useMaterial3: true);
  return base.copyWith(
    colorScheme: base.colorScheme.copyWith(
      primary: kShrinePink100,
      onPrimary: kShrineBrown900,
      secondary: kShrineBrown900,
      error: kShrineErrorRed,
    ),
    // TODO: Add the text themes (103)
    // TODO: Decorate the inputs (103)
  );
}

Теперь установите theme: в конце функции build() ShrineApp (в виджете MaterialApp), чтобы она стала нашей новой темой:

  // TODO: Customize the theme (103)
  theme: _kShrineTheme, // New code

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

Андроид

iOS

Страница входа в Храм в розово-коричневой тематике

Страница входа в Храм в розово-коричневой тематике

5. Измените типографику и стили меток.

Помимо изменения цвета, дизайнер также предоставил нам специальную типографику. ThemeData Flutter включает в себя 3 текстовые темы. Каждая текстовая тема представляет собой набор текстовых стилей, таких как «заголовок» и «заголовок». Мы будем использовать несколько стилей для нашего приложения и изменим некоторые значения.

Настройте текстовую тему

Чтобы импортировать шрифты в проект, их необходимо добавить в файл pubspec.yaml.

В pubspec.yaml добавьте следующее сразу после тега flutter:

  # TODO: Insert Fonts (103)
  fonts:
    - family: Rubik
      fonts:
        - asset: fonts/Rubik-Regular.ttf
        - asset: fonts/Rubik-Medium.ttf
          weight: 500

Теперь вы можете получить доступ к шрифту Rubik и использовать его.

Устранение неполадок с файлом pubspec

Вы можете получить ошибки при запуске pub get, если вы скопируете и вставите приведенное выше объявление. Если вы получаете ошибки, начните с удаления начальных пробелов и замены их пробелами, используя отступ в два пробела. (Два пробела перед

fonts:

, четыре пробела перед

family: Rubik

, и так далее.)

Если вы видите , что значения сопоставления здесь не разрешены , проверьте отступ строки, в которой возникла проблема, и отступ строк над ней.

В login.dart измените следующее внутри Column() :

Column(
  children: <Widget>[
    Image.asset('assets/diamond.png'),
    const SizedBox(height: 16.0),
    Text(
      'SHRINE',
      style: Theme.of(context).textTheme.headlineSmall,
    ),
  ],
)

В app.dart добавьте следующее после _buildShrineTheme() :

// TODO: Build a Shrine Text Theme (103)
TextTheme _buildShrineTextTheme(TextTheme base) {
  return base
      .copyWith(
        headlineSmall: base.headlineSmall!.copyWith(
          fontWeight: FontWeight.w500,
        ),
        titleLarge: base.titleLarge!.copyWith(
          fontSize: 18.0,
        ),
        bodySmall: base.bodySmall!.copyWith(
          fontWeight: FontWeight.w400,
          fontSize: 14.0,
        ),
        bodyLarge: base.bodyLarge!.copyWith(
          fontWeight: FontWeight.w500,
          fontSize: 16.0,
        ),
      )
      .apply(
        fontFamily: 'Rubik',
        displayColor: kShrineBrown900,
        bodyColor: kShrineBrown900,
      );
}

Это принимает TextTheme и изменяет внешний вид заголовков, заголовков и подписей.

Применение fontFamily таким образом применяет изменения только к значениям типографского масштаба, указанным в copyWith() (заголовок, заголовок, подпись).

Для некоторых шрифтов мы устанавливаем собственный шрифт FontWeight с шагом 100: w500 (вес 500) соответствует среднему, а w400 соответствует обычному.

Используйте новые текстовые темы

Добавьте следующие темы в _buildShrineTheme после ошибки:

// TODO: Add the text themes (103)
textTheme: _buildShrineTextTheme(base.textTheme),
textSelectionTheme: const TextSelectionThemeData(
  selectionColor: kShrinePink100,
),

Сохраните проект. На этот раз также перезапустите приложение (так называемый « Горячий перезапуск »), поскольку мы изменили шрифты.

Андроид

iOS

Страница сетки продуктов Shrine с примененными текстовыми темами

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

Сократить текст

Этикетки слишком большие.

В home.dart измените children: самого внутреннего столбца:

// TODO: Change innermost Column (103)
children: <Widget>[
// TODO: Handle overflowing labels (103)
  Text(
    product.name,
    style: theme.textTheme.button,
    softWrap: false,
    overflow: TextOverflow.ellipsis,
    maxLines: 1,
  ),
  const SizedBox(height: 4.0),
  Text(
    formatter.format(product.price),
    style: theme.textTheme.bodySmall,
  ),
  // End new code
],

Центрируйте и опустите текст

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

Переместите метки в конец (вниз) главной оси и измените их на центрирование::

  // TODO: Align labels to the bottom and center (103)
  mainAxisAlignment: MainAxisAlignment.end,
  crossAxisAlignment: CrossAxisAlignment.center,

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

Андроид

iOS

Страница сетки продуктов Shrine с различным выравниванием текста

Страница сетки продуктов Shrine с различным выравниванием текста

Это выглядит намного лучше.

Тема для текстовых полей

Вы также можете оформить тематическое оформление текстовых полей с помощью InputDecorationTheme .

В app.dart в методе _buildShrineTheme() укажите значение inputDecorationTheme:

// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
  border: OutlineInputBorder(),
),

Прямо сейчас текстовые поля имеют filled оформление. Давайте удалим это. Удаление filled и указание inputDecorationTheme придаст текстовым полям стиль контура.

В login.dart удалите filled: true значения:

// Remove filled: true values (103)
TextField(
  controller: _usernameController,
  decoration: const InputDecoration(
    // Removed filled: true
    labelText: 'Username',
  ),
),
const SizedBox(height: 12.0),
TextField(
  controller: _passwordController,
  decoration: const InputDecoration(
    // Removed filled: true
    labelText: 'Password',
  ),
  obscureText: true,
),

Горячий перезапуск. Ваш экран входа в систему должен выглядеть следующим образом, когда поле «Имя пользователя» активно (когда вы его вводите):

Андроид

iOS

Страница входа в храм с фокусом на поле имени пользователя

Страница входа в храм с фокусом на поле имени пользователя

Введите текст в текстовое поле — границы и плавающие метки отображаются в основном цвете. Но нам не так легко это увидеть. Он недоступен для людей, у которых есть проблемы с различением пикселей с недостаточно высоким цветовым контрастом. (Дополнительную информацию см. в статье «Рекомендации по использованию материалов: цвет и доступность ».)

В app.dart укажите focusedBorder: под inputDecorationTheme: :

// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
  border: OutlineInputBorder(),
  focusedBorder: OutlineInputBorder(
    borderSide: BorderSide(
      width: 2.0,
      color: kShrineBrown900,
    ),
  ),
),

Затем укажите floatingLabelStyle: в поле inputDecorationTheme:

// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
  border: OutlineInputBorder(),
  focusedBorder: OutlineInputBorder(
    borderSide: BorderSide(
      width: 2.0,
      color: kShrineBrown900,
    ),
  ),
  floatingLabelStyle: TextStyle(
    color: kShrineBrown900,
  ),
),

Наконец, давайте сделаем так, чтобы кнопка «Отмена» использовала вторичный цвет, а не основной, для повышения контрастности.

TextButton(
  child: const Text('CANCEL'),
  onPressed: () {
    _usernameController.clear();
    _passwordController.clear();
  },
  style: TextButton.styleFrom(
    primary: Theme.of(context).colorScheme.secondary,
  ),
),

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

Андроид

iOS

Страница входа в храм с доступной кнопкой ОТМЕНА

Страница входа в храм с доступной кнопкой ОТМЕНА

6. Отрегулируйте высоту

Теперь, когда вы придали странице определенный цвет и типографику, соответствующую Shrine, давайте отрегулируем высоту.

Измените высоту кнопки ДАЛЕЕ

По умолчанию для ElevatedButton установлено значение 2. Давайте поднимем его выше.

В login.dart добавьте значение style: к NEXT ElevatedButton:

ElevatedButton(
  child: const Text('NEXT'),
  onPressed: () {
    Navigator.pop(context);
  },
  style: ElevatedButton.styleFrom(
    foregroundColor: kShrineBrown900,
    backgroundColor: kShrinePink100,
    elevation: 8.0,
  ),
),

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

Андроид

iOS

Страница входа в храм с приподнятой кнопкой ДАЛЕЕ

Страница входа в храм с приподнятой кнопкой ДАЛЕЕ

Отрегулируйте высоту карты

Прямо сейчас карточки лежат на белой поверхности рядом с навигацией сайта.

В home.dart добавьте значение elevation: к Cards:

// TODO: Adjust card heights (103)
elevation: 0.0,

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

Андроид

iOS

Страница сетки продуктов Shrine без высоты для каждой карточки

Страница сетки продуктов Shrine без высоты для каждой карточки

Вы удалили тень под картами.

7. Добавляем фигуру

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

Изменение формы текстового поля на экране входа в систему

В app.dart импортируйте следующий файл:

import 'supplemental/cut_corners_border.dart';

По-прежнему в app.dart измените тему оформления текстового поля, чтобы использовать рамку со срезанными углами:

// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
  border: CutCornersBorder(),
  focusedBorder: CutCornersBorder(
    borderSide: BorderSide(
      width: 2.0,
      color: kShrineBrown900,
    ),
  ), 
  floatingLabelStyle: TextStyle(
    color: kShrineBrown900,
  ),
),

Изменение формы кнопок на экране входа в систему

В login.dart добавьте скошенную прямоугольную рамку к кнопке ОТМЕНА :

TextButton(
  child: const Text('CANCEL'),
  onPressed: () {
    _usernameController.clear();
    _passwordController.clear();
  },
  style: TextButton.styleFrom(
    foregroundColor: kShrineBrown900,
    shape: const BeveledRectangleBorder(
      borderRadius: BorderRadius.all(Radius.circular(7.0)),
    ),
  ),
),

TextButton не имеет видимой формы, так зачем добавлять форму границы? Таким образом, анимация пульсации при прикосновении привязана к одной и той же форме.

Теперь добавьте ту же форму к кнопке ДАЛЕЕ:

ElevatedButton(
  child: const Text('NEXT'),
  onPressed: () {
    Navigator.pop(context);
  },
  style: ElevatedButton.styleFrom(
    foregroundColor: kShrineBrown900,
    backgroundColor: kShrinePink100,
    elevation: 8.0,
    shape: const BeveledRectangleBorder(
      borderRadius: BorderRadius.all(Radius.circular(7.0)),
    ),
  ),
),

Чтобы изменить форму всех кнопок, мы также можем использовать elevatedButtonTheme или textButtonTheme в app.dart . Это остается вызовом для ученика!

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

Андроид

iOS

Страница входа в храм с примененной темой фигуры

Страница входа в храм с примененной темой фигуры

8. Измените макет

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

Замените GridView на AsymmetricView.

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

В home.dart добавьте следующий импорт:

import 'supplemental/asymmetric_view.dart';

Удалите _buildGridCards и замените body :

body: AsymmetricView(
  products: ProductsRepository.loadProducts(Category.all),
),

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

Андроид

iOS

Страница продукта Shrine с асимметричным макетом с горизонтальной прокруткой

Страница продукта Shrine с асимметричным макетом с горизонтальной прокруткой

Теперь товары прокручиваются горизонтально в тканом узоре.

9. Попробуйте другую тему (необязательно).

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

Изменить цвета

В colors.dart добавьте следующий цвет:

const kShrinePurple = Color(0xFF5D1049);

В app.dart измените функцию _buildShrineTheme() на следующее:

ThemeData _buildShrineTheme() {
  final ThemeData base = ThemeData.light();
  return base.copyWith(
    colorScheme: base.colorScheme.copyWith(
      primary: kShrinePurple,
      secondary: kShrinePurple,
      error: kShrineErrorRed,
    ),
    scaffoldBackgroundColor: kShrineSurfaceWhite,
    textSelectionTheme: const TextSelectionThemeData(
      selectionColor: kShrinePurple,
    ),
    appBarTheme: const AppBarTheme(
      foregroundColor: kShrineBrown900,
      backgroundColor: kShrinePink100,
    ),

    inputDecorationTheme: const InputDecorationTheme(
      border: CutCornersBorder(),
      focusedBorder: CutCornersBorder(
        borderSide: BorderSide(
          width: 2.0,
          color: kShrinePurple,
        ),
      ),
      floatingLabelStyle: TextStyle(
        color: kShrinePurple,
      ),
    ),
  );
}

Горячий перезапуск. Теперь должна появиться новая тема.

Андроид

iOS

Страница входа в храм в фиолетово-розовой теме

Страница входа в храм в фиолетово-розовой теме

Андроид

iOS

Страница продукта Shrine с розовой темой

Страница продукта Shrine с розовой темой

Результат совсем другой! Давайте вернем _buildShrineTheme app.dart's к тому состоянию, которое было до этого шага. Или загрузите стартовый код 104.

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

К этому моменту вы создали приложение, которое соответствует спецификациям вашего дизайнера.

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

Теперь вы использовали следующий Material Flutter: тему, типографику, высоту и форму. Вы можете изучить больше компонентов и подсистем в библиотеке Material Flutter.

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

Что делать, если запланированный дизайн вашего приложения содержит элементы, у которых нет компонентов в библиотеке? В MDC-104: Расширенные компоненты материалов мы показываем, как создавать пользовательские компоненты с использованием библиотеки Material Flutter для достижения желаемого вида.

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

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

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

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