📘

[Flutter] go_routerとfirebase_authとflutterfire_uiとriverpodでAuth

2022/09/10に公開

半年ぶり?位にFirebaseを使ってみたのですが、めちゃくちゃ簡単に出来たのでメモ。

初期設定

Firebaseの設定(firebase cli , flutterfire cli利用)

Firebase CLIの追加

公式ドキュメントを確認したほうがいいかも。

yarn add global firebase-tools

ログイン

firebase login

FlutterFire CLIの追加

dart pub global activate flutterfire_cli

初期設定

flutterfire configure

色々聞かれるのでよしなに設定してください。

Core

flutter pub add firebase_core

Auth

flutter pub add firebase_auth

Firestore

flutter pub add cloud_firestore

最後にCLIを走らせる

flutterfire configure

必要なDependencyの追加

Visual Studio Codeを使うとめちゃくちゃ簡単なのでオススメ!

Control + Shift + P → add dep まで打つと出てくるやつでdependencies と dev dependenciesが追加出来ます。めっちゃ簡単。

画像

go_router と hooks_riverpod と flutterfire_uiを追加してください。

あとはコード。

コード

ポイントはuser_provider.dartでStreamProviderを利用することと、router.dart部分でFutureBuilderProviderScopeOverridesを使うこと。

そうすることでログイン以下のページで簡単にcurrentUserが取得できます。

パスワードを忘れたとかはGoogle Sign In オンリーにして回避します。

main.dart

import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

import 'firebase_options.dart';
import 'router.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  GoRouter.setUrlPathStrategy(UrlPathStrategy.path);
  runApp(const ProviderScope(child: MyApp()));
}

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

  
  Widget build(BuildContext context, WidgetRef ref) {
    final router = ref.watch(routerProvider);

    return MaterialApp.router(
      title: 'SampleApp',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      routeInformationProvider: router.routeInformationProvider,
      routeInformationParser: router.routeInformationParser,
      routerDelegate: router.routerDelegate,
    );
  }
}

router.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:petty_cash_recorder/providers/user_provider.dart';

import 'screens/selection_screen.dart';
import 'screens/sign_in_screen.dart';

final routerProvider = Provider<GoRouter>(
  (ref) {
    final _user = ref.watch(userProvider);
    return GoRouter(
      routes: [
        GoRoute(
            name: 'top',
            path: '/',
            builder: (context, state) => _user.when(
                data: (user) => ProviderScope(
                    overrides: [currentUserId.overrideWithValue(user!.uid)],
                    child: SelectionPage()),
                error: (_, __) => Text(_.toString()),
                loading: () => const CircularProgressIndicator())),
        GoRoute(
          name: 'signin',
          path: '/signin',
          builder: (context, state) => const SignInPage(),
        ),
      ],
      redirect: (state) {
        final loggingIn = state.subloc == '/signin';
        if (_user.value == null) {
          return loggingIn ? null : '/signin';
        }
        if (loggingIn) {
          return '/';
        }
        return null;
      },
    );
  },
);
user_provider.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

final currentUser = Provider<User>((ref) => throw UnimplementedError());

final userProvider = StreamProvider<User?>((ref) async* {
  final auth = FirebaseAuth.instance;
  final userStream = auth.authStateChanges();
  await for (final user in userStream) {
    yield user;
  }
});
sign_in_screen.dart
import 'package:flutter/material.dart';
import 'package:flutterfire_ui/auth.dart';

class SignInPage extends StatelessWidget {
  const SignInPage({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return const SignInScreen(
      providerConfigs: [
        GoogleProviderConfiguration(
            clientId:
                "YOUR_CLIENT_ID")
      ],
    );
  }
}

selection_page.dart
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:petty_cash_recorder/providers/user_provider.dart';

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

  
  Widget build(BuildContext context, WidgetRef ref) {
    final user = ref.watch(currentUser);
    return Scaffold(
    appBar: AppBar(
        title: Text('SampleApp'),
        actions: [
          IconButton(
            icon: Icon(Icons.logout),
            onPressed: () async {
              await FirebaseAuth.instance.signOut();
            },
          )
        ],
      ),
      body: Center(child: Text('user is ${user.uid}')),
    );
  }
}

Tips

currentUserを他のProviderで利用するときは、そのプロバイダーの末尾にdependenciesを追加してください。
例えばfirestoreでUserを監視したいときなど。

firestoreCollection.dart
final yourModelRowsRef = Provider<CollectionReference<YourModel>>((ref) {
  final uid = ref.watch(currentUserId);
  final db = FirebaseFirestore.instance;

  final pettyCashRow = db.collection("user/${uid}/YourModel").withConverter(
      fromFirestore: YourModel.fromFireStore,
      toFirestore: ((YourModel yourModel, options) => yourModel.toFireStore()));
  return yourModelRow;
}, dependencies: [currentUserId]);

みたいな感じ。

Discussion