🚀

[Flutter] firebase_ui_authとriverpod_generator , go_routerでAuth

2023/04/27に公開
1

はじめに

Flutterfire_uiがdeprecatedになるらしく、同じinvertase社がメンテしているfirebase_ui_authを使ってみました。以前と少し使い方が異なっていたため、こちらに記述していきます。

また、こちらではriverpod_generatorを使用しています。少し約束事が増えますが、関数的にプロバイダを定義できるのでとても使いやすいです。

コード

main.dart
main.dart

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  FirebaseUIAuth.configureProviders([
    EmailAuthProvider(),
    GoogleProvider(
        clientId:
            "YOURCLIENTID"),
  ]);
  runApp(const ProviderScope(child: MyApp()));
}

class MyApp extends ConsumerWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  
  Widget build(BuildContext context, WidgetRef ref) {
    final router = ref.watch(routerProvider);
    return MaterialApp.router(
      localizationsDelegates: const [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate
      ],
      supportedLocales: const [Locale("en"), Locale("ja")],
      theme: lightTheme,
      darkTheme: darkTheme,
      routeInformationProvider: router.routeInformationProvider,
      routeInformationParser: router.routeInformationParser,
      routerDelegate: router.routerDelegate,
    );
  }
}

エントリポイント。最初に FirebaseUIAuth.configureProvidersでログインプロバイダを設定する。

router.dart
router.dart

part 'router.g.dart';


GoRouter Router(RouterRef ref) {
  final userStream = ref.watch(userStreamProvider);
  return GoRouter(
    routes: [
      GoRoute(
          path: MainPage.path,
          name: MainPage.path,
          builder: (context, state) {
            return MainPage(
              user: userStream.value!,
            );
          },
          ),
      GoRoute(
        path: SignInPage.path,
        name: SignInPage.path,
        builder: (context, state) => const SignInPage(),
      ),
    ],
    redirect: (context, state) async {
      print('redirecting...');
      //state.sublocが使えなくなったのでstate.matchedLocationを使う
      final loggingIn = state.matchedLocation == SignInPage.path;
      // print(state.subloc);
      if (userStream.value == null) {
        return loggingIn ? null : SignInPage.path;
      }
      // print('proceeding');
      if (loggingIn) {
        // print('auth success');
        return '/';
      }
      return null;
    },
  );
}

ここがキモで、ProviderScopeのoverridesを利用してしまうとGoroute間の値の受け渡し時にエラーになってしまったので、各RouteにコンストラクタとしてuserStream.value!を渡すことにしています。
usrStream.value!がそもそもないときはredirectでサインインページにredirectします。

sign_in_screen
sign_in_screen.dart
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';


class SignInPage extends ConsumerWidget {
  const SignInPage({Key? key}) : super(key: key);
  static const path = '/sign-in';

  
  Widget build(BuildContext context, WidgetRef ref) {
    return SignInScreen(
      auth: FirebaseAuth.instance,
      actions: [
        AuthStateChangeAction<SignedIn>((context, state) {
          debugPrint("Signed in");
          context.go(MainPage.path);
        }),
      ],
    );
  }
}

firebase_ui_authをこちらで使います。

user.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'user.g.dart';


Stream<User?> UserStream(UserStreamRef ref) async* {
  await for (final user in FirebaseAuth.instance.authStateChanges()) {
    yield user;
  }
}

Discussion

草野洋平草野洋平

TypedShellRoute使ってProtectedPageを定義するとProviderScopeのOverridesが使える。
こっちのが便利かも。