Open14

Flutter 勉強ログ

syunyosyunyo

wasabeef さんが書いている blueprint を動作させながら勉強してみる
https://zenn.dev/syunyo/scraps/cf6f3f5ba9c5cd

FirebaseCrashAnalytics や、Dio がWebに対応しておらず中々動作しない。
とはいえ、MVVM, RiverPod や fvm などは参考になった。

今後は Flutter の勉強をしながら簡単なモックを作ってみる。

syunyosyunyo

AutoRouter の導入

https://pub.dev/packages/auto_route/versions/2.0.0

変更点

# pubspec.yaml
dependencies:
  auto_route: ^2.0.0 

dev_dependencies:
  auto_route_generator: ^2.0.0
  build_runner:
// app/route/route.dart
import 'package:auto_route/auto_route.dart';
import 'package:app/pages/signIn/sign_in_page.dart';

(
  replaceInRouteName: 'Page,Route',
  routes: <AutoRoute>[
    AutoRoute(page: SignInPage, initial: true),
  ],
)
class $AppRouter {}

反映

以下を実行。app/route/route.gr.dart にスクリプトが生成される

fvm flutter packages pub run build_runner build
syunyosyunyo

Firebase Hosting 連携
https://zenn.dev/pressedkonbu/articles/deploy-flutter-web-app-with-firebase-hosting

GithubActions は flutter install とキャッシュの箇所を書き直した

name: Deploy to Firebase Hosting on merge
'on':
  push:
    branches:
      - master
jobs:
  build_and_deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: setup cache
        uses: actions/cache@v1
        with:
          path: /Users/runner/hostedtoolcache/flutter
          key: ${{ runner.OS }}-flutter-install-cache
      - name: install flutter
        uses: subosito/flutter-action@v1
        with:
          flutter-version: '2.2.2' # .fvm と合わせておく
          channel: 'stable'
      - name: flutter dependencies install
        run: flutter pub get
      - run: flutter build web
      - uses: FirebaseExtended/action-hosting-deploy@v0
        with:
          repoToken: '${{ secrets.GITHUB_TOKEN }}'
          firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_TITI_FLUTTER }}'
          channelId: live
          projectId: titi-flutter
        env:
          FIREBASE_CLI_PREVIEWS: hostingchannels
syunyosyunyo

テーマ管理

標準のやりかた
https://qiita.com/sekitaka_1214/items/e25f2f3b282b0ff382cf
https://itome.team/blog/2019/12/flutter-advent-calendar-day12/

dynamic_theme プラグインの使用
https://ichi.pro/kitsune-no-yona-flutter-apuri-de-te-ma-o-kirikaeru-116934619192907

wasabeef さんの Riverpod を使ったテーマ管理
https://github.com/wasabeef/flutter-architecture-blueprints/blob/main/lib/ui/theme/app_theme.dart

難しいことはせず、シンプルに普通に標準で用意されているテーマ管理でいいと思う。

syunyosyunyo

RouteGuard

Firebase 認証情報を確認し、認証済みでなければログイン画面に遷移する。

https://dev.to/ruizalexandre/flutter-firebase-authentication-dynamic-routing-by-authstatechanges-9k0
https://github.com/Milad-Akarie/auto_route_library/issues/433

Issue に上がっているのは iOS, Android 想定(直接そのページに遷移できない前提)のものが多い。
Webの場合URLを指定すれば直接そのページに飛べるため、ページにアクセスする度に認証が必要となる。

AutoRoute の AutoRouteGuard を継承すれば実装可能。
Firebase連携では、HotReload と 手動のページ更新 で挙動が異なるので気をつける。
HotReload では fsAuthStateChange が呼び出されなかったり
手動更新では、user情報の取得に時間がかかり、user が null で読み込まれる場合がある。

以下コードの一部

class AuthGuard extends AutoRouteGuard {
  void _navigateUser(NavigationResolver resolver, StackRouter router) async {
    final userState = getCurrentUserState(router);
    print('AuthGuard navigateUser: $userState');
    switch (userState?.state) {
      case UserAuthState.unAuth:
        // 未認証時はログインページへ
        router.push(LogInRoute(onResult: (user) {
          _navigateUser(resolver, router);
        }));
        break;
      case UserAuthState.unVerifyEmail:
        // メールアドレス認証待ちページへ
        router.push(ConfirmMailRoute(onResult: (user) {
          _navigateUser(resolver, router);
        }));
        break;
      case UserAuthState.verified:
        // 認証済み
        resolver.next(true);
        break;
      case UserAuthState.loading:
        await new Future.delayed(new Duration(seconds: 1));
        _navigateUser(resolver, router);
        break;
      default:
    }
  }

  UserState? getCurrentUserState(StackRouter router) {
    // riverpod の Provider から取得
    final context = router.navigatorKey.currentContext;
    final userState = context!.read(authControllerProvider);
    return userState;
  }

  
  void onNavigation(NavigationResolver resolver, StackRouter router) {
    _navigateUser(resolver, router);
  }
}

(
  replaceInRouteName: 'Page,Route',
  routes: <AutoRoute>[
    AutoRoute(page: SignUpPage, path: '/signup'),
    AutoRoute(page: LogInPage, path: '/login'),
    AutoRoute(page: ConfirmMailPage, path: '/confirm-mail'),
    AutoRoute(
        page: ChatPage, path: '/chat', guards: [AuthGuard], initial: true),
  ],
)
class $AppRouter {}