Slide 1

Slide 1 text

Tokyo, Japan Daichi Furiya ( Wasabeef ) CyberAgent, Inc. What it Takes to be a Flutter Developer

Slide 2

Slide 2 text

Flutter を書き始める時に役に立つ知識

Slide 3

Slide 3 text

02. Android エンジニアが Flutter 始める時に 01. Flutter のこれまでの振り返りと 2023 に向けて 03. まとめ

Slide 4

Slide 4 text

02. Android エンジニアが Flutter 始める時に 01. Flutter のこれまでの振り返りと 2023 に向けて 03. まとめ

Slide 5

Slide 5 text

History of Flutter

Slide 6

Slide 6 text

1. Windows 2. macOS 3. Linux 〜〜〜〜〜〜〜〜〜〜〜〜 4. Foldable Phone 5. Web 1. FFI 2. Null Safety 3. Impeller Devices Tools 1. Game Engine (Flame) 2. Firebase (FlutterFire) 3. Material 3 4. FlutterFlow Codes

Slide 7

Slide 7 text

iOS の描画パフォーマンスの課題

Slide 8

Slide 8 text

iOS の描画パフォーマンス問題(Shader compilation jank) 現行の Skia ではアプリ初回起動時にシェーダーコンパイルが行われ、初 回起動時は数十フレームの欠落することがあるが、それ以降はキャッシュさ れ滑らかなアニメーションになる。 しかし、Android はそれが可能だが iOS の場合はアプリを最初から開くた びに行われてしまう。 (キャッシュファイルを埋め込むこともできるが)

Slide 9

Slide 9 text

新しいグラフィックエンジンの 「Impeller」 Skia のパフォーマンス問題を解決するために現在グラフィックエンジンの書 き換えが行われている。それは以下が行われ解決するもの → シェーダーコンパイルをアプリビルド時にオフラインで実行 → アニメーションをキャプチャし、ディスクに永続化 → Metal や Vulkan などのモダン API に対応 → 並行処理を効果的に利用

Slide 10

Slide 10 text

Try Impeller FLTEnableImpeller $ flutter run --enable-impeller iOS: Info.plist Android: AndroidManifest.xml https://github.com/flutter/flutter/wiki/Impeller Master channel

Slide 11

Slide 11 text

Wonderous https://flutter.gskinner.com/wonderous/

Slide 12

Slide 12 text

02. Android エンジニアが Flutter 始める時に 01. Flutter のこれまでの振り返りと 2023 に向けて 03. まとめ

Slide 13

Slide 13 text

android/architecture-templates

Slide 14

Slide 14 text

https://github.com/android/architecture-templates android/architecture-templates

Slide 15

Slide 15 text

android/architecture-templates 構成要素 1. Editor, IDE 2. KTS Gradle files 3. Version catalog 0. App Architecture 1. Room 2. DI with Hilt 3. Navigation 4. Retrofit, OkHttp 5. Coroutines and Flow 6. Unit tests Build UI Architecture 1. Jetpack Compose

Slide 16

Slide 16 text

Build 1. Editor, IDE 2. Flutter Version Management 3. Project Configuration 3.1. Multi-Module Project 3.2. Assets Management

Slide 17

Slide 17 text

Editor, IDE

Slide 18

Slide 18 text

Android Studio || VSCode || Vim

Slide 19

Slide 19 text

海外からのアウトプットを見る限りでは VSCode を使ってる人が多そうな印象

Slide 20

Slide 20 text

Flutter Version Management

Slide 21

Slide 21 text

Flutter Version Management 2.10 3.0 3.3 チーム開発で各個人環境で実行 バージョンに差異があると挙動に 問題起きやすい

Slide 22

Slide 22 text

# FVM Install $ brew tap leoafarias/fvm $ brew install fvm # Run with FVM $ fvm flutter pub get Flutter Version Management # asdf Install $ brew install asdf # Flutter plugin Install $ asdf plugin add flutter # Run with asdf $ flutter pub get FVM asdf Flutter 専用のバージョン管理ツール (rbenvのようなイメージ) Flutter に限らずマルチランタイムのバージョン管理ツール (anyenvのようにオールインワンのイメージ)

Slide 23

Slide 23 text

Project Configuration

Slide 24

Slide 24 text

# Create a new Flutter app $ flutter create my_app $ cd my_app # To launch the app in the Simulator $ flutter run Create a Flutter project これだけで flutter コマンドでプロジェクトを作成しシミュレータ等で実行します。

Slide 25

Slide 25 text

# pubspec.yaml name: material_3_demo description: A Flutter project. publish_to: "none" version: 1.0.0+1 environment: sdk: ">=2.17.0-0 <3.0.0" dependencies: flutter: sdk: flutter cupertino_icons: ^1.0.2 dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^2.0.1 flutter: uses-material-design: true pubspec.yaml Flutter Project を作成した際に、プロジェク トルートに pubspec.yaml ファイルが生成さ れているので SDK のバージョン指定、依存 パッケージ、アセットの設定などを指定しま す。

Slide 26

Slide 26 text

https://pub.dev/ Maven Central のように Google が公式で提供しているパッケージリポジトリ

Slide 27

Slide 27 text

https://pub.dev/ 同サイトから Google が推奨する パッケージとしてリス ト化している Flutter Favorites や人気な パッケージも検索で きる

Slide 28

Slide 28 text

$ dart pub publish Publish to https://pub.dev/ Maven Central への公開 ・pom.xml, doc.jar, sources.jar 生成 ・GPU PG Key 生成 ・JIRA のアカウント作成 ・JIRA のチケット作成 ・Maven Central でチェック待ち ・リリース(Drop) pub.dev への公開 ・pub.dev ログイン ・publish コマンド実行

Slide 29

Slide 29 text

Multi-Module Project

Slide 30

Slide 30 text

Multi-Module Project Kotlin for Android ではビルド時間の短縮で あったりクラス間の依存関係の明確化を目的に マルチモジュールにすることがありますが Flutter ではマルチモジュール(マルチプラグイ ン)にビルド時間の短縮のような効果が得られる わけではありませんし、アプリ開発ではマルチモ ジュールという概念は浸透していません。

Slide 31

Slide 31 text

# Melos Install $ dart pub global activate melos # Run analyze $ melos run analyze # Run a script $ melos run Select a script to run in this workspace: 1) analyze 2) format 3) build_runner 4) build 5) test Melos --> Multi-Module Project Flutter では Melos を使うことでマル チモジュール(マルチプラグイン)の 構成管理が便利になるツールも有志 によって公開されています。 Firebase, AWS, Stripe でも採用され ているので実績があります。 Makefile の置き換えと考えると便利 なものになっています。

Slide 32

Slide 32 text

# melos.yaml name: App packages: - packages/** scripts: analyze: dart analyze format: dart format --set-exit-if-changed . build_runner: run: | melos exec -- dart pub run build_runner build select-package: depends-on: 'build_runner' build: run: | melos exec -- flutter build apk test: run: melos exec -- dart test select-package: dir-exists: test Melos --> Multi-Module Project Flutter では Melos を使うことでマル チモジュール(マルチプラグイン)の 構成管理が便利になるツールも有志 によって公開されています。 Firebase, AWS, Stripe でも採用され ているので実績があります。 Makefile の置き換えと考えると便利 なものになっています。

Slide 33

Slide 33 text

Melos --> Multi-Module Project Gradle Version Catalog のように各モ ジュールでバージョンを合わせるような機 能は Flutter SDK や Melos でも提供され ていないので、注意が必要である。 pubspec.yaml retrofit: 3.0.0 pubspec.yaml retrofit: 3.3.1 pubspec.yaml retrofit: 3.0.0 module1 module2 pubspec.yaml retrofit: 3.3.1 module3 home pubspec.yaml retrofit: 3.3.1 login

Slide 34

Slide 34 text

Assets Management

Slide 35

Slide 35 text

R.txt (runtime_symbol_list) Android では `res/` に画像等のリソースを配置 することで AGP がリソースにアクセスすることが 可能ですが、この仕組みを Flutter では公式に提 供されていません。

Slide 36

Slide 36 text

// R.txt int string app_name 0x7f0b0001 int string close_drawer 0x7f0b0002 int string close_sheet 0x7f0b0003 int animator fragment_close_enter 0x7f020000 int animator fragment_close_exit 0x7f020001 int animator fragment_fade_enter 0x7f020002 int animator fragment_fade_exit 0x7f020003 int animator fragment_open_enter 0x7f020004 int animator fragment_open_exit 0x7f020005 int attr action 0x7f030000 int attr alpha 0x7f030001 int attr argType 0x7f030002 int attr data 0x7f030003 int attr dataPattern 0x7f030004 int attr destination 0x7f030005 int attr enterAnim 0x7f030006 int attr exitAnim 0x7f030007 int attr font 0x7f030008 ................ R.txt (runtime_symbol_list) Android では `res/` に画像等のリソースを配置 することで AGP がリソースにアクセスすることが 可能ですが、この仕組みを Flutter では公式に提 供されていません。

Slide 37

Slide 37 text

FlutterGen --> R.txt # pubspec.yaml flutter: assets: - assets/images/profile.jpg Widget build(BuildContext context) { return Image.asset('assets/images/profile.jpeg'); } // The following assertion was thrown resolving an image codec: // Unable to load asset: assets/images/profile.jpeg Widget build(BuildContext context) { return Assets.images.profile.image(); } Flutter では pubspec.yaml で リソースファイルの指定を行 い、Widget 側で相対パスの 文字列で指定をする必要が あるが、FlutterGen を使うこ とで安全にリソースを指定す ることが出来る。

Slide 38

Slide 38 text

UI 1. Widgets 2. Side-effects 3. Local State management

Slide 39

Slide 39 text

Widgets

Slide 40

Slide 40 text

class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Testing Sample'), actions: [ TextButton.icon( onPressed: () { ... }, icon: const Icon(Icons.favorite_border), label: const Text('Favorites'), ), ], ), body: ListView.builder( itemCount: 100, controller: ScrollController(), padding: const EdgeInsets.symmetric(vertical: 16), itemBuilder: (context, index) => ItemTile(index), ), ); } } Widgets Jetpack Compose と同様に宣言 的 UI フレームワークなので似た ような感覚での記述が可能です。

Slide 41

Slide 41 text

class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Testing Sample'), actions: [ TextButton.icon( onPressed: () { ... }, icon: const Icon(Icons.favorite_border), label: const Text('Favorites'), ), ], ), body: ListView.builder( itemCount: 100, controller: ScrollController(), padding: const EdgeInsets.symmetric(vertical: 16), itemBuilder: (context, index) => ItemTile(index), ), ); } } Widgets Jetpack Compose と同様に宣言 的 UI フレームワークなので似た ような感覚での記述が可能です。 AppBar --> TopAppBar

Slide 42

Slide 42 text

class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Testing Sample'), actions: [ TextButton.icon( onPressed: () { ... }, icon: const Icon(Icons.favorite_border), label: const Text('Favorites'), ), ], ), body: ListView.builder( itemCount: 100, controller: ScrollController(), padding: const EdgeInsets.symmetric(vertical: 16), itemBuilder: (context, index) => ItemTile(index), ), ); } } Widgets Jetpack Compose と同様に宣言 的 UI フレームワークなので似た ような感覚での記述が可能です。 TextButton --> Button

Slide 43

Slide 43 text

class HomePage extends StatelessWidget { const HomePage({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Testing Sample'), actions: [ TextButton.icon( onPressed: () { ... }, icon: const Icon(Icons.favorite_border), label: const Text('Favorites'), ), ], ), body: ListView.builder( itemCount: 100, controller: ScrollController(), padding: const EdgeInsets.symmetric(vertical: 16), itemBuilder: (context, index) => ItemTile(index), ), ); } } Widgets Jetpack Compose と同様に宣言 的 UI フレームワークなので似た ような感覚での記述が可能です。 ListView --> LazyColumn

Slide 44

Slide 44 text

Side-effects

Slide 45

Slide 45 text

Flutter Hooks (Local State Management) React Hooks の Flutter 版で文法も使い方もほぼ一緒。 ステートの初期化(initState)や破棄 (dispose) をウィジェットと分離ができる。 単純にクラスの定義を減らすシ ンタックスシュガー的な恩恵もある。

Slide 46

Slide 46 text

useEffect --> DisposableEffect // Flutter useEffect(() => { final subscription = source.subscribe(id); return () => { // 破棄される時に実行 subscription.unsubscribe(id); }; }, [id]); 当該の Widget だけで利用するようなものは Flutter Hooks でローカルに状態管理す ることでコードがスッキリする場合があります。 // Jetpack Compose DisposableEffect(id) { val subscription = source.subscribe(id) onDispose { // 破棄される時に実行(Activity破棄など) subscription.unsubscribe(id) } }

Slide 47

Slide 47 text

// Jetpack Compose LaunchedEffect(id) { // ID が変更されるたび } SideEffect { // 毎回 } LaunchedEffect(Unit) { // 最初の一回 } // Flutter useEffect(() => { // ID が変更されるたび }, [id]); useEffect(() => { // 毎回 }); useEffect(() => { // 最初の一回 }, []); useEffect --> LaunchedEffect, SideEffect React Hooks や Flutter Hooks は useEffect の指定の仕方で実行回数がわかれてい る が Jetpack Compose のほうが後発に出来たこともあってか読み取りやすい。

Slide 48

Slide 48 text

Local State management

Slide 49

Slide 49 text

// Flutter class CountPage extends HookWidget { @override Widget build(BuildContext context) { final count = useState(0); return TextButton( onPressed: () { count.value++; }, child: Text("Count: ${count.value}"), ); } } Flutter Hooks’s useState --> mutableStateOf 当該の Widget だけで利用するようなものは Flutter Hooks でローカルに状態管理す ることでコードがスッキリする場合があります。 // Jetpack Compose @Composable fun Count() { val count = remember { mutableStateOf(0) } Button(onClick = { count.value++ }) { Text("Count: ${count.value}") } }

Slide 50

Slide 50 text

Architecture 0. App Architecture 1. Dependency injection 2. Networking 3. Unit tests

Slide 51

Slide 51 text

App Architecture

Slide 52

Slide 52 text

App Architecture ?

Slide 53

Slide 53 text

App Architecture ? Flutter で推奨されているアプリアーキテクチャは何ですか?

Slide 54

Slide 54 text

App Architecture ? Flutter で推奨されているアプリアーキテクチャは何ですか? Flutter で推奨されているアプリアーキテクチャは、状態管理パターンとして Model-View-Update (MVU) のアーキテクチャがあります。 MVU は、状態と ビューのみを管理するシンプルなアーキテクチャで、アプリのロジックを分離 し、テストしやすくすることができます。 MVU のアーキテクチャは、非同期プロ グラミングや React のフレームワークで使われる Redux のアーキテクチャに 似ています。Flutter では、MVU のアーキテクチャを推奨していますが、他の アーキテクチャを使用することも可能です。

Slide 55

Slide 55 text

Dependency Injection

Slide 56

Slide 56 text

Riverpod --> Hilt Riverpodは、Dart で状態管理を行うためのライブラリで。容易かつ効率的にアプリケーションの状態管理を行 うことができます。

Slide 57

Slide 57 text

ここでは簡単なサンプルです が、Riverpod の中心的な機能 に Provider というものあり以下 のような機能が備わっていま す。 ・グローバルの状態管理 ・キャッシング ・テスト向けに値のオーバーライド final configurationsProvider = FutureProvider((ref) async { final uri = Uri.parse('configs.json'); final rawJson = await File.fromUri(uri).readAsString(); return Configuration.fromJson(json.decode(rawJson)); }); Riverpod --> Hilt class Example extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final configs = ref.watch(configurationsProvider); return configs.when( loading: () => const CircularProgressIndicator(), error: (err, stack) => Text('Error $err'), data: (configs) => Text('data: ${configs.host}'), ); }

Slide 58

Slide 58 text

Networking

Slide 59

Slide 59 text

Dio や dart-lang/http は、クライアント/サーバ通信のための Dart 向け HTTP クライアントライブラリです。 HTTPリクエストを容易かつ効率的に処理することができます。 Dio || dart-lang/http --> OkHttp

Slide 60

Slide 60 text

Retrofit For Dart は Android アプリ開発でよく使われている Retrofit を参考にして作られた Dio 用のクライア ントコード生成ライブラリです。 Retrofit For Dart

Slide 61

Slide 61 text

@RestApi(baseUrl: "https://wasabeef.jp/api/v1/") abstract class RestClient { factory RestClient(Dio dio, {String baseUrl}) = _RestClient; @GET("/tasks") Future> getTasks(); @GET("/tasks/{id}") Future getTask(@Path("id") String id); @PATCH("/tasks/{id}") Future updateTaskPart( @Path() String id, @Body() Map map); } @JsonSerializable() class Task { ... } アノテーションでのパス・パラメー タ指定が出来るので、似たような 感覚で記述ができる。 Retrofit For Dart

Slide 62

Slide 62 text

Unit test

Slide 63

Slide 63 text

dart-lang/Mockito --> Mockito, Mockk dart-lang/Mockito は Android アプリ開発でよく使われている Java 用の Mockito を参考にして作られたモッ クフレームです。

Slide 64

Slide 64 text

02. Android エンジニアが Flutter 始める時に 01. Flutter のこれまでの振り返りと 2023 に向けて 03. まとめ

Slide 65

Slide 65 text

今回の主題である既に Kotlin で Android を作ってる人であればモバイル開発の前提知識 のショートカットが出来つつ Flutter アプリ開発に参入することができます。 また、React を書いている Web エンジニアの方々も Flutter に興味もってもらえるとこれま での Kotlin Android に比べて比較的簡単にモバイルアプリ開発に参入することができま す。

Slide 66

Slide 66 text

Flutter コミュニティの発展のために ブログや Qiita などに投稿された内容に対して強い言葉で全否定の 意見をぶつける → アーキテクチャ等の全否定 ⇨ ブログ数の減少 ⇨ 初学者が敬遠

Slide 67

Slide 67 text

Online Communities r/FlutterDev https://discord.com/invite/N7Yshp4 Google Developers Online https://t.co/qXZdmipQpt FlutterDev https://www.reddit.com/r/FlutterDev/ Flutter Community https://fluttercommunity.slack.com/ Flutter https://twitter.com/i/communities/1472249315724771329

Slide 68

Slide 68 text

Daichi Furiya @wasabeef_jp Thank you Twitter: @wasabeef_jp GitHub: wasabeef #DevFest2022 Connect