$30 off During Our Annual Pro Sale. View Details »

What it Takes to be a Flutter Developer

What it Takes to be a Flutter Developer

DevFest & Android Dev Summit Japan 2022

Daichi Furiya (Wasabeef)

January 24, 2023
Tweet

More Decks by Daichi Furiya (Wasabeef)

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  5. History of Flutter

    View Slide

  6. 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  10. Try Impeller
    FLTEnableImpeller

    android:name="io.flutter.embedding.android.EnableImpeller"
    android:value="true" />
    $ flutter run --enable-impeller
    iOS: Info.plist
    Android: AndroidManifest.xml
    https://github.com/flutter/flutter/wiki/Impeller
    Master channel

    View Slide

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

    View Slide

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

    View Slide

  13. android/architecture-templates

    View Slide

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

    View Slide

  15. 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

    View Slide

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

    View Slide

  17. Editor, IDE

    View Slide

  18. Android Studio || VSCode || Vim

    View Slide

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

    View Slide

  20. Flutter Version Management

    View Slide

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

    View Slide

  22. # 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のようにオールインワンのイメージ)

    View Slide

  23. Project Configuration

    View Slide

  24. # 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 コマンドでプロジェクトを作成しシミュレータ等で実行します。

    View Slide

  25. # 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 のバージョン指定、依存
    パッケージ、アセットの設定などを指定しま
    す。

    View Slide

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

    View Slide

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

    View Slide

  28. $ 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 コマンド実行

    View Slide

  29. Multi-Module Project

    View Slide

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

    View Slide

  31. # 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 の置き換えと考えると便利
    なものになっています。

    View Slide

  32. # 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 の置き換えと考えると便利
    なものになっています。

    View Slide

  33. 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

    View Slide

  34. Assets Management

    View Slide

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

    View Slide

  36. // 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 では公式に提
    供されていません。

    View Slide

  37. 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 を使うこ
    とで安全にリソースを指定す
    ることが出来る。

    View Slide

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

    View Slide

  39. Widgets

    View Slide

  40. 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 フレームワークなので似た
    ような感覚での記述が可能です。

    View Slide

  41. 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

    View Slide

  42. 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

    View Slide

  43. 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

    View Slide

  44. Side-effects

    View Slide

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

    View Slide

  46. 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)
    }
    }

    View Slide

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

    View Slide

  48. Local State management

    View Slide

  49. // 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}")
    }
    }

    View Slide

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

    View Slide

  51. App Architecture

    View Slide

  52. App Architecture ?

    View Slide

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

    View Slide

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

    View Slide

  55. Dependency Injection

    View Slide

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

    View Slide

  57. ここでは簡単なサンプルです
    が、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}'),
    );
    }

    View Slide

  58. Networking

    View Slide

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

    View Slide

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

    View Slide

  61. @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

    View Slide

  62. Unit test

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  67. 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

    View Slide

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

    View Slide