×Закрыть

10 инструментов для облегчения работы с Flutter

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

1. FVM

FVM — аббревиатура от Flutter Version Manager. Аналогичный инструмент вы могли встречать в работе с Node.js — NVM. FVM позволяет вам гибко переключаться между версиями Flutter. Поддерживает загрузку как конкретных версий Flutter, так и каналов Master, Dev, Beta, Stable.

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

Хотя во Flutter релизы происходят ежеквартально, гарантий, как обычно, нет — могут и задержать. Бывают ситуации, что критический для бизнеса баг, если и починят довольно оперативно, то в релизную ветку он попадет не на следующий день. В этом случае FVM помогает мне быстро скачать обновления и переключиться на другую версию Flutter, проверить конкретный фикс. А если эта ветка не подходит, вернуться назад.

Я работаю над несколькими проектами, и не все мигрировали на последний Stable. FVM очень удобен, если нужно сделать мелкую правку, где не предполагается возня с обновлением версий фреймворка, библиотек, и повторное тестирование.

Если переключения делать вручную, каждая итерация занимает от пары минут до бесконечности в зависимости от мощности «железа», скорости интернет-канала и так далее. FVM, в свою очередь, позволяет переключиться между версиями в одну команду.

Устанавливаем: flutter pub global activate fvm.

Затем любую желаемую версию: fvm install master, или fvm install 1.17.0.

Дожидаемся скачивания. Потом в папке с проектом fvm use 1.17.0 выбираем желаемую версию.

Дальше все команды выполняем, как раньше, только с приставкой FVM. Внутри команды проксируются к нужной версии фреймворка.

Первый раз запускаем проект вручную, чтобы докачались все нужные для конкретной версии модули: fvm flutter run.

Потом можно поправить настройки IDE, чтобы конкретный проект использовал нужную вам версию Flutter.

2. VS Code

Добавить в settings.json:

"dart.flutterSdkPaths": [
    "fvm"
]

3. Android Studio

В настройках проекта нужно поменять путь к папке с фреймворком.

Заходим: Preferences -> Languages&Frameforks -> Flutter. Указываем путь к папке с версией фреймворка. На macOs это: /Users/[your_user_name]/fvm/versions/[verstion_name].

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

Существует альтернативный инструмент, но пока что с ним ознакомился. На текущий момент все мои потребности закрывает FVM. Возможно, по совокупности фич вам он покажется более полезным.

Для самых ленивых

4. flutter_launcher_icons

Этот пакет помогает мне в одну команду создать и сложить по своим местам иконки для приложений Android и iOs. Базовая настройка включает в себя всего несколько строчек в pubspec.yaml:

flutter_icons:
 image_path_android: "assets/icon/android.png"
 image_path_ios: "assets/icon/ios.png"
 android: true

Запускаем командой flutter pub run flutter_launcher_name:main и наслаждаемся результатом.

5. flutter_launcher_name

Позволяет в одну команду изменить имя приложения. Настройка лаконична — добавьте секцию в pubspec.yaml:  ios: true.

flutter_launcher_name:
 name: "Your app name"

C запуском справится даже начинающий: flutter pub run flutter_launcher_name:main.

Это не сложно и вручную сделать. Но название секции как бы намекает...

6. change_app_package_name

Изменяет название пакета для Android:

  • Обновляет AndroidManifest.xml для release, debug & profile.
  • Обновляет build.gradle.
  • Обновляет MainActivity (для Java и Kotlin).
  • Перемещает MainActivity в новую структуру папок.
  • Удаляет старую структуру папок.

Для получения результата в командной строке выполняем: flutter pub run change_app_package_name:main com.new.package.name

7. build_context

С помощью extension дает быстрый доступ к часто используемым методам контекста.

Вместо MediaQuery.of(context).orientation == Orientation.landscape пишем context.isLandscape.

Коротко и удобно — полный перечень сокращений в readme.

Для суровых, но все еще ленивых

Рассмотрим три библиотеки для кодогенерации: json_serializable, freezed, auto_route.

Каждая из них помогает или побороться с определенными недостатками экосистемы Flutter, или переложить рутинную работу по написанию бойлерплейт-кода на машину.

8. json_serializable

Большинство приложений общаются с бэкендом и получают в ответ старый добрый JSON. Для работы с ним Dart из коробки предлагает две функции — jsonEncode и jsonDecode. Если вы играете в песочнице — поле для фантазий бесконечно. Можно голый JSON передавать между компонентами или вручную написать несколько классов для сериализации/десериализации, как в этом примере.

Оба способа не подойдут вам вне песочницы. Передавать JSON «как есть» не удобно и не типизируемо. Писать все вручную долго, больно и чревато ошибками, особенно в случае периодических изменений в дизайне API.

Библиотека json_serializable помогает генерировать методы fromJSON и toJSON для моделей данных с бэкенда. Для примера возьмем jsonplaceholder.typicode.com и сделаем выборку постов и комментариев по ним.

Создадим файл post.dart и две модели Post и Posts.

Posts

class Posts {
  final List<Post> posts;

  Posts({this.posts});
}

Posts

class Post {
  final int userId;
  final int id;
  final String title;
  final String body;

  Post({this.userId, this.id, this.title, this.body});
}

Добавим импорты и аннотации.

Импорты

import 'package:json_annotation/json_annotation.dart';

part 'post.g.dart'; // файл, который будет сгенерирован

Аннотации

Указывают, что мы хотим сгенерировать методы fromJson и toJson для аннотированных классов:

@JsonSerializable()
class Posts {...

@JsonSerializable()
class Post {...

Добавляем методы fromJson и toJson в классы:

@JsonSerializable()
class Posts {
  ...

  factory Posts.fromJson(Map<String, dynamic> json) => _$PostsFromJson(json);
  Map<String, dynamic> toJson() => _$PostsToJson(this);
}

@JsonSerializable()
class Post {
  …


  factory Post.fromJson(Map<String, dynamic> json) => _$PostFromJson(json);
  Map<String, dynamic> toJson() => _$PostToJson(this);
}

Запускаем генерацию: flutter pub run build_runner build.

По окончанию генерации появится новый файл post.g.dart. Проделываем те же манипуляции для комментариев.

В результате получаем готовые методы для сериализации/десериализации данных с бэкенда. Как использовать, можно посмотреть здесь.

9. freezed

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

Из коробки можно создавать const-объекты классов. Это может быть выходом, но ограничивает вас созданием объектов только на этапе компиляции. Существует пакет meta с набором аннотаций для статического анализа. Он предлагает директиву @immutable, которая в dart analyzer подскажет, что не все поля в классе типа final, но не сильно воспрепятствует на этапе написания кода.

Остается два варианта: писать и следить за иммутабельностью вручную или прибегнуть к кодогенерации.

Пакет freezed помогает добавить иммутабельность объектам и сделать количество рукописного кода еще меньше, чем в предыдущем примере.

Модифицируем post.dart, чтобы сгенерировать необходимый код.

Добавляем импорты:

import 'package:freezed_annotation/freezed_annotation.dart';
part 'post.freezed.dart';// файл который будет сгенерирован

Правим аннотации:

@freezed JsonSerializable()
class Posts {...

@freezed JsonSerializable()
class Post {...

Убираем лишний код: пакет freezed отлично интегрируется с json_serialaizable, но нужно немного поправить код класса.

Обновленный класс Posts:

@freezed
abstract class Posts with _$Posts {
  factory Posts({List<Post> posts}) = _Posts;

  factory Posts.fromJson(Map<String, dynamic> json) => _$PostsFromJson(json);
}

Обновленный класс Post:

@freezed
abstract class Post with _$Post {
  factory Post({int userId, int id, String title, String body}) = _Post;

  factory Post.fromJson(Map<String, dynamic> json) => _$PostFromJson(json);
}

Запускаем генерацию: flutter pub run build_runner build

По окончанию генерации появится новый файл post.freezed.dart.

Проделываем те же манипуляции для комментариев.

10. auto_route

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

Используем библиотеку для добавления роутинга между экранами со списком постов и комментариями к ним.

Создадим route.dart.

Добавим импорты:

import 'package:auto_route/auto_route_annotations.dart';
import 'package:doutoolbox/comments_view.dart';
import 'package:doutoolbox/posts_view.dart';

Определим класс роутинга: @initial — указывает на то, что postsView будет стартовым.

@MaterialAutoRouter()
class $Router {
  @initial
  PostsView postsView;

  CommentsView commentsView;
}

Запускаем генерацию: flutter pub run build_runner build.

Сгенерируется route.gr.dart.

Поправим main.dart, добавив туда роутинг:

MaterialApp(
      // ...
      builder: (context, nativeNavigator) =>
          ExtendedNavigator<Router>(router: Router()),
    );

По клику на пост откроем страницу с комментариями:

onTap: () => ExtendedNavigator.of(context).pushNamed(
    Routes.commentsView,
    arguments: CommentsViewArguments(
        postId: snapshot.data.posts[index].id)),

В итоге получим типизированную передачу аргументов в роут и сгенерированные имена роутов.

Посмотреть, как это все работает, можно в репозитории.

Подписывайтесь на наш Telegram-канал, следите за обновлениями!

LinkedIn

4 комментария

Подписаться на комментарииОтписаться от комментариев Комментарии могут оставлять только пользователи с подтвержденными аккаунтами.

Для своїх потреб теж використовував json_serializable, але воно працює лише в пласких ієрархіях класів, часто плуталося, який саме клас згенерувати в fromJson/toJson методах. Після постійних виправлянь (після кожного регенерування часткових класів), приходилося руками правити помилки. Задовбало і написав сам собі ці всі методи :)

Пройдемте)

це- універсальний інструмент, до Флатера не має відношення

Подписаться на комментарии