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:

Для отримання результату в командному рядку виконуємо: 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-канал , слідкуйте за оновленнями!

Опубліковано: 20/05/20 @ 10:00
Розділ Різне

Рекомендуємо:

Ні відповіді ні привіту. Чому рекрутери не відповідають після інтерв'ю і як це виправити
Розвинений IT-ринок і спокій. 6 причин жити і працювати в Миколаєві для IT-спеціаліста
DevOps дайджест #32: застосування Helm, Kustomize, ArgoCD і реліз Vitess
Як не PM нафакапить на новому проекті
Як нас накрутили конкуренти в Яндексі і що з цього вийшло