📘
[Flutter] go_routerとfirebase_authとflutterfire_uiとriverpodでAuth
半年ぶり?位に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
部分でFutureBuilder
とProviderScope
のOverrides
を使うこと。
そうすることでログイン以下のページで簡単に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