Русский
Русский
English
Статистика
Реклама

Storybook Flutter storybook_flutter

Всем привет! В этой статье я буду бессовестно пиарить рассказывать о своей библиотеке для Flutter'а, которая позволяет создавать истории из изолированных виджетов и/или экранов. Что-то типа Storybook из мира React. Собственно, она так и называется: storybook_flutter.

Зачем она нужна?

Во-первых, быстрее делать UI. Конечно, у Flutter'а и так есть hot reload, но если виджет зарыт где-то в дебрях приложения, то до него еще надо добраться. А если этот виджет показывается только при определенных условиях, то эти условия надо воспроизвести. Кроме того, hot reload работает не во всех случаях. Поэтому удобнее изолировать виджет, вынести его в отдельную историю, и работать с этой историей. При этом вам придется задуматься над тем, как бы убрать лишние зависимости из этого виджета, так что код в итоге будет чище.

Во-вторых, демонстрация виджетов/экранов. Например, мы делаем свою дизайн-библиотеку для Flutter'а, и в документацию мы бы хотели встраивать интерактивную песочницу с виджетами, тем более, что Flutter for Web уже в стабильной ветке.

В-третьих, в будущем я хочу добавить (мне эту идею подсказали в issues) возможность автоматически генерировать golden tests для виджетов с разными комбинациями параметров.

Может, взять что-то готовое?

Наверное, можно. Но, во-первых, какого-то явного фаворита в сообществе пока нет. Во-вторых, синдром NIH никто не отменял. В-третьих, я хочу иметь возможность быстро добавлять те фичи, которые нам нужны.

Как она выглядит?

Как-то так:

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

Что она умеет?

  • Навигация по историям с разбивкой по категориям.

  • Параметры (knobs) виджетов.

  • Переключатель светлой/темной темы.

  • Показ истории в отдельном окне без всех элементов интерфейса (в вебе удобно встраивать такую полноэкранную историю в iframe).

  • Кастомизация.

  • Различные рамки (спасибо пакету device_frame) в превью версии.

  • Плагины тоже в превью.

Как с ней работать?

Добавляем в pubspec.yaml(я использую превью-версию, плагины и рамки пока есть только в ней):

storybook_flutter: ^0.5.0-dev.0

Создаем историю (хорошо звучит). В самом простом случае будет что-то такое:

import 'package:flutter/material.dart';import 'package:storybook_flutter/storybook_flutter.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({Key? key}) : super(key: key);  @override  Widget build(BuildContext context) => Storybook(        children: [          Story.simple(            name: 'Button',            child: ElevatedButton(              onPressed: () {},              child: const Text('Push me'),            ),          ),        ],      );}

Запускаем, смотрим, радуемся:

Добавим несколько параметров. Для этого меняем simple конструктор на обычный, и используем builder вместо child:

Story(  name: 'Button',  builder: (context, k) => ElevatedButton(    onPressed:        k.boolean(label: 'Enabled', initial: true) ? () {} : null,    child: Text(k.text(label: 'Text', initial: 'Push me')),  ),),

Запускаем, радуемся еще больше:

Если надо добавить секцию, просто добавляем параметр section:

Story(  name: 'Button',  section: 'Buttons',  builder: (context, k) => ElevatedButton(    onPressed:        k.boolean(label: 'Enabled', initial: true) ? () {} : null,    child: Text(k.text(label: 'Text', initial: 'Push me')),  ),),

Все истории с одинаковым параметром section будут автоматически сгруппированы.

Как кастомизировать?

У каждой Story есть параметры paddingи background отвечающие, как ни странно, за отступы и фоновый цвет каждой истории:

Story(  name: 'Button',  section: 'Buttons',  padding: const EdgeInsets.all(8),  background: Colors.red,  builder: (context, k) => ElevatedButton(    onPressed:        k.boolean(label: 'Enabled', initial: true) ? () {} : null,    child: Text(k.text(label: 'Text', initial: 'Push me')),  ),),

Но это слишком просто. Гораздо интереснее использовать параметр wrapperBuilder у Story, который позволяет обернуть каждую историю в кастомный виджет:

Story(  name: 'Button',  section: 'Buttons',  wrapperBuilder: (context, story, child) => Container(    decoration: BoxDecoration(border: Border.all()),    margin: const EdgeInsets.all(16),    child: Center(child: child),  ),  builder: (context, k) => ElevatedButton(    onPressed:        k.boolean(label: 'Enabled', initial: true) ? () {} : null,    child: Text(k.text(label: 'Text', initial: 'Push me')),  ),),

Этот же билдер можно передать в качестве параметра storyWrapperBuilder в Storybook, тогда каждая история будет обернута в этот виджет.

Нужно больше кастомизаций!

Если же одних билдеров, врапперов и параметров недостаточно, можно взять CustomStorybook и сделать все своими руками:

class MyApp extends StatelessWidget {  const MyApp({Key? key}) : super(key: key);  @override  Widget build(BuildContext context) {    final decoration = BoxDecoration(      border: Border(        right: BorderSide(color: Theme.of(context).dividerColor),        left: BorderSide(color: Theme.of(context).dividerColor),      ),      color: Theme.of(context).cardColor,    );    return MaterialApp(      debugShowCheckedModeBanner: false,      home: Scaffold(        body: CustomStorybook(          builder: (context) => Row(            children: [              Container(                width: 200,                decoration: decoration,                child: const Contents(),              ),              const Expanded(child: CurrentStory()),              Container(                width: 200,                decoration: decoration,                child: const KnobPanel(),              ),            ],          ),          children: [            Story(              name: 'Button',              builder: (context, k) => ElevatedButton(                onPressed:                    k.boolean(label: 'Enabled', initial: true) ? () {} : null,                child: Text(k.text(label: 'Text', initial: 'Push me')),              ),            )          ],        ),      ),    );  }}

При этом вы можете использовать встроенные виджеты Contents, CurrentStory и KnobPanel (думаю, вы догадались, что они делают). Получим вот такую минималистичную картину:

Один из возможных юзкейсов для CustomStorybook вот этот плагин, который добавляет Storybook в другую библиотеку, device_preview, с поддержкой оглавления и настроек. Получается вот так:

Что там с плагинами?

Как я уже говорил, в превью версии появилась поддержка плагинов и первый 1st party плагин: DeviceFramePlugin:

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

Про создание плагинов, если будет интересно, расскажу в другой статье.

Какие платформы поддерживаются?

Никакой особой магии под капотом нет, так что теоретически должно работать на всех платформах, которые поддерживаются Flutter'ом. Я проверял на Android, iOS, Web и macOS.

Что дальше?

Дальше в планах устаканить API плагинов, подумать, какие плагины еще нужны из коробки (ну и написать их).

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


На этом все. Буду рад замечаниям, предложениям и баг-репортам (ну и лайкам/звездочкам, конечно же, чего уж греха таить).

Источник: habr.com
К списку статей
Опубликовано: 29.03.2021 18:20:06
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Я пиарюсь

Разработка мобильных приложений

Flutter

Dart

Storybook

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru