Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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. 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
  2. Try Impeller <key>FLTEnableImpeller</key> <true/> <meta-data 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
  3. 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
  4. Build 1. Editor, IDE 2. Flutter Version Management 3. Project

    Configuration 3.1. Multi-Module Project 3.2. Assets Management
  5. # 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のようにオールインワンのイメージ)
  6. # 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 コマンドでプロジェクトを作成しシミュレータ等で実行します。
  7. # 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 のバージョン指定、依存 パッケージ、アセットの設定などを指定しま す。
  8. $ 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 コマンド実行
  9. Multi-Module Project Kotlin for Android ではビルド時間の短縮で あったりクラス間の依存関係の明確化を目的に マルチモジュールにすることがありますが Flutter ではマルチモジュール(マルチプラグイ

    ン)にビルド時間の短縮のような効果が得られる わけではありませんし、アプリ開発ではマルチモ ジュールという概念は浸透していません。
  10. # 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 の置き換えと考えると便利 なものになっています。
  11. # 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 の置き換えと考えると便利 なものになっています。
  12. 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
  13. // 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 では公式に提 供されていません。
  14. 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 を使うこ とで安全にリソースを指定す ることが出来る。
  15. 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 フレームワークなので似た ような感覚での記述が可能です。
  16. 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
  17. 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
  18. 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
  19. Flutter Hooks (Local State Management) React Hooks の Flutter 版で文法も使い方もほぼ一緒。

    ステートの初期化(initState)や破棄 (dispose) をウィジェットと分離ができる。 単純にクラスの定義を減らすシ ンタックスシュガー的な恩恵もある。
  20. 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) } }
  21. // Jetpack Compose LaunchedEffect(id) { // ID が変更されるたび } SideEffect

    { // 毎回 } LaunchedEffect(Unit) { // 最初の一回 } // Flutter useEffect(() => { // ID が変更されるたび }, [id]); useEffect(() => { // 毎回 }); useEffect(() => { // 最初の一回 }, []); useEffect --> LaunchedEffect, SideEffect React Hooks や Flutter Hooks は useEffect の指定の仕方で実行回数がわかれてい る が Jetpack Compose のほうが後発に出来たこともあってか読み取りやすい。
  22. // 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}") } }
  23. App Architecture ? Flutter で推奨されているアプリアーキテクチャは何ですか? Flutter で推奨されているアプリアーキテクチャは、状態管理パターンとして Model-View-Update (MVU) のアーキテクチャがあります。

    MVU は、状態と ビューのみを管理するシンプルなアーキテクチャで、アプリのロジックを分離 し、テストしやすくすることができます。 MVU のアーキテクチャは、非同期プロ グラミングや React のフレームワークで使われる Redux のアーキテクチャに 似ています。Flutter では、MVU のアーキテクチャを推奨していますが、他の アーキテクチャを使用することも可能です。
  24. ここでは簡単なサンプルです が、Riverpod の中心的な機能 に Provider というものあり以下 のような機能が備わっていま す。 ・グローバルの状態管理 ・キャッシング

    ・テスト向けに値のオーバーライド final configurationsProvider = FutureProvider<Configuration>((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}'), ); }
  25. @RestApi(baseUrl: "https://wasabeef.jp/api/v1/") abstract class RestClient { factory RestClient(Dio dio, {String

    baseUrl}) = _RestClient; @GET("/tasks") Future<List<Task>> getTasks(); @GET("/tasks/{id}") Future<Task> getTask(@Path("id") String id); @PATCH("/tasks/{id}") Future<Task> updateTaskPart( @Path() String id, @Body() Map<String, dynamic> map); } @JsonSerializable() class Task { ... } アノテーションでのパス・パラメー タ指定が出来るので、似たような 感覚で記述ができる。 Retrofit For Dart
  26. 今回の主題である既に Kotlin で Android を作ってる人であればモバイル開発の前提知識 のショートカットが出来つつ Flutter アプリ開発に参入することができます。 また、React を書いている

    Web エンジニアの方々も Flutter に興味もってもらえるとこれま での Kotlin Android に比べて比較的簡単にモバイルアプリ開発に参入することができま す。
  27. 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