[Перевод] Как написать «Пятнашки» на Flutter

0683b405339b714f4c2c7bf5c0191f22.png

Возможно, вы уже читали о конкурсе Flutter Puzzle Hack и думаете о том, как проявить максимум творческих способностей. И мы вам в этом поможем, рассказав о том, как структурирована кодовая база нашего примера головоломки. Подробностями делимся к старту авторского курса по веб-разработке на Python.

Архитектура

В представленном исходном коде многослойная архитектура для управления состоянием реализуется с помощью flutter_bloc. Блоки помогают управлять всем — от игровой логики до настройки тем. 

Все состояния управляются согласованно: чтобы изменить логику головоломки, нужно только найти и обновить соответствующий блок. Кнопки сброса и перемешивания, таймера и обратного отсчёта — тоже отдельные блоки, которые можно расположить по вашей задумке: может быть, сделать песочные часы для таймера? Или шикарно стилизовать перемешивание?

Обратите внимание: вся игровая логика находится в одном блоке PuzzleBloc, который изменяется по таким событиям, как нажатие на один из 15 квадратиков и его перемещение с помощью события TileTapped, а также полный сброс головоломки по событию PuzzleReset. Состояние головоломки изменяется при каждом изменении в игре:

class PuzzleBloc extends Bloc {
  PuzzleBloc(this._size, {this.random}) : super(const PuzzleState() {
    on(_onPuzzleInitialized);
    on(_onTileTapped);
    on(_onPuzzleReset);
  }
  void _onPuzzleInitialized(
    PuzzleInitialized event,
    Emitter emit,
  ) {...}
  void _onTileTapped(
    TileTapped event,
    Emitter emit,
  ) {...}
  void _onPuzzleReset(
    PuzzleReset event,
    Emitter emit,
  ) {...}
}

Настройка темы

В примере кода головоломки есть две темы: Simple и Dashatar. Их можно взять за основу для собственных настроек или начать с нуля — реализация зависит от вас! Творческий подход можно проявить в настройке тем головоломки.

ddf53d54a9340bc10b4209eeaf8eea18.png

В демо всё происходит в верхней части PuzzlePage. Достаточно поменять элементы темы в одном месте, и изменения будут отражены везде. В обеих темах определяется ряд параметров: фон экрана, меню, логотип, кнопки, цвет текста, отображение таймера (есть в Dashatar, но нет в Simple) и другие. Они находятся в корня репозитория, в директориях dashatar и simple:

/// {@template simple_theme}
/// The simple puzzle theme.
/// {@endtemplate}
class SimpleTheme extends PuzzleTheme {
  /// {@macro simple_theme}
  const SimpleTheme() : super();
  @override
  Color get backgroundColor => PuzzleColors.white;
  @override
  Color get buttonColor => PuzzleColors.primary6;
  @override
  Color get hoverColor => PuzzleColors.primary3;
  @override
  Color get pressedColor => PuzzleColors.primary7;
  ...
}

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

Для дизайна сложнее можно настроить всю тему LayoutDelegate. Например, чтобы создать пользовательский фон, отображаемый только на большом экране, можно переопределить backgroundBuilder:

@override
Widget backgroundBuilder(PuzzleState state) {
  return Positioned(
    bottom: 74,
    right: 50,
    child: ResponsiveLayoutBuilder(
      small: (_, child) => const SizedBox(),
      medium: (_, child) => const SizedBox(),
      large: (_, child) => const DashatarThemePicker(),
    ),
  );
}

Анимация

Анимация — прекрасный элемент головоломки для изучения. В простой теме Simple нет анимации, зато несколько поэтапных анимаций реализуется в коде Dashatar. Эти анимации управляются одним контроллером, в котором есть Interval для настройки задержки анимации и Tween для определения диапазона значений анимации. 

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

70198eda70515c737dcbcc0a091bca31.png

Большинство анимаций в теме Dashatar неявные: нет необходимости писать всю анимацию самостоятельно — изменения свойств анимируются самими виджетами. Например, виджетом DashatarPuzzleTile анимируется перемещение квадратиков по нажатию на них. Благодаря неявно анимированному AnimatedAlign текущее положение квадрата меняется согласно заданному movementDuration:

class DashatarPuzzleTile extends StatelessWidget {
  ... 
  
  final Tile tile;
  
  @override
  Widget build(BuildContext context) {
    return AnimatedAlign(
      alignment: FractionalOffset(
        (tile.currentPosition.x - 1) / (size - 1),
        (tile.currentPosition.y - 1) / (size - 1),
      ),
      duration: movementDuration,
      curve: Curves.easeInOut,
      child: ResponsiveLayoutBuilder(...),
    );
  }
}

Головоломка для веба

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

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

Когда пользователь попадает в игру-головоломку по умолчанию — Simple, файлы для версии Dashatar загружаются в фоновом режиме. Аналогичный подход мы использовали при загрузке всех свойств для I/O Photo Booth (фотобудки ввода-вывода). Так мы гарантируем, что при переходе к теме Dashatar большинство файлов уже загружены.

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

Доступность

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

С приложением можно взаимодействовать через программы чтения с экрана. Это делается с помощью семантических меток. Для некоторых действий есть всплывающие подсказки: используется виджет Tooltip. 

Если вы пишете «Пятнашки» с нуля, настоятельно рекомендуем сделать головоломку доступной для всех пользователей (применяйте стратегии, аналогичные описанным выше).

Другие идеи

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

Из примеров Simple и Dashatar можно позаимствовать идеи настроек или чего-то интересного, что можно сделать самостоятельно. Одна интересная идея — проецировать на квадратики головоломки созданную Феликсом Блашке Flutter Plasma.

Эффект плазмы достигается применением виджета CustomPaint, обёрнутого в виджет Transform и анимированного с помощью AnimationController. Вот пример отрисовки плазмы из демо:

5ca54966efc73ecd25541b9c1a199929.png

Ещё одна идея — получать изображения или другие данные от API. Например, чтобы сделать «Пятнашки», можно использовать Google Photos API, взяв фотографии одного из альбомов Google Photos. В этом конкурсе нет предела совершенству!

Посмотрите пример кода головоломки здесь. Поделитесь с нами своими творениями в Twitter с помощью хештега #FlutterPuzzleHack. Ждём с нетерпением!

Лицензия кода — MIT, так что его легко использовать в ваших «Пятнашках». А продолжить погружение в IT и прокачать ваши навыки вы сможете на наших курсах:

Выбрать другую востребованную профессию.

822dbdd4a4593813d6c9339471b4f037.pngКраткий каталог курсов и профессий

Data Science и Machine Learning

Python, веб-разработка

Мобильная разработка

Java и C#

От основ — в глубину

А также

© Habrahabr.ru