Skip to content

Instantly share code, notes, and snippets.

@SuperPenguin
Created March 25, 2023 14:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SuperPenguin/0dd0ca98d7e507d278a1290438e3a528 to your computer and use it in GitHub Desktop.
Save SuperPenguin/0dd0ca98d7e507d278a1290438e3a528 to your computer and use it in GitHub Desktop.
GoRouter and Riverpod
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
Future<void> main() async {
User? initialUser;
runApp(
ProviderScope(
overrides: [
userProvider.overrideWith(
(ref) => UserNotifier(initialUser),
),
],
child: const App(),
),
);
}
class App extends ConsumerStatefulWidget {
const App({super.key});
@override
ConsumerState<App> createState() => _AppState();
}
class _AppState extends ConsumerState<App> {
late final GoRouter _goRouter;
@override
void initState() {
super.initState();
final initialUser = ref.read(userProvider);
_goRouter = GoRouter(
initialLocation: initialUser == null ? '/login' : '/home',
routes: [
GoRoute(
path: '/login',
redirect: (context, state) {
final user = ref.read(userProvider);
if (user != null) return '/home';
return null;
},
builder: (context, state) => const LoginScreen(),
),
GoRoute(
path: '/home',
redirect: (context, state) {
final user = ref.read(userProvider);
if (user == null) return '/login';
return null;
},
builder: (context, state) => const HomeScreen(),
),
],
);
}
@override
void dispose() {
_goRouter.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
ref.listen<User?>(userProvider, (previous, next) {
if (previous != next) {
_goRouter.refresh();
}
});
return MaterialApp.router(
debugShowCheckedModeBanner: false,
routerConfig: _goRouter,
);
}
}
class LoginScreen extends ConsumerStatefulWidget {
const LoginScreen({super.key});
@override
ConsumerState<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends ConsumerState<LoginScreen> {
final TextEditingController _emailController = TextEditingController();
Future<void>? _loginFuture;
void _login() {
final email = _emailController.text;
final notifier = ref.read(userProvider.notifier);
setState(() {
_loginFuture = notifier.login(email: email);
});
}
@override
void dispose() {
_emailController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return FutureBuilder<void>(
future: _loginFuture,
builder: (context, snapshot) {
final isLoading = snapshot.connectionState == ConnectionState.waiting;
final error = snapshot.error;
return Stack(
fit: StackFit.expand,
children: [
Scaffold(
appBar: AppBar(
title: const Text('Login'),
),
body: SafeArea(
minimum: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextField(
controller: _emailController,
),
const Spacer(),
if (error != null) Text(error.toString()),
const SizedBox(height: 8.0),
ElevatedButton(
onPressed: _login,
child: const Text('Login'),
),
],
),
),
),
if (isLoading)
const Material(
color: Colors.black54,
child: Center(
child: CircularProgressIndicator.adaptive(
backgroundColor: Colors.white,
),
),
),
],
);
},
);
}
}
class HomeScreen extends ConsumerStatefulWidget {
const HomeScreen({super.key});
@override
ConsumerState<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends ConsumerState<HomeScreen> {
Future<void>? _logoutFuture;
Future<void> _logout() async {
final notifier = ref.read(userProvider.notifier);
final future = notifier.logout();
setState(() {
_logoutFuture = future;
});
try {
await future;
} catch (error) {
if (!mounted) return;
showDialog<void>(
context: context,
builder: (context) => AlertDialog(
title: const Text('Error'),
content: Text(error.toString()),
actions: [
TextButton(
onPressed: () => Navigator.of(context).maybePop(),
child: const Text('OK'),
),
],
),
);
}
}
@override
Widget build(BuildContext context) {
final user = ref.watch(userProvider);
return FutureBuilder<void>(
future: _logoutFuture,
builder: (context, snapshot) {
final isLoading = snapshot.connectionState == ConnectionState.waiting;
return Stack(
children: [
Scaffold(
appBar: AppBar(
title: const Text('Home'),
actions: [
IconButton(
onPressed: _logout,
icon: const Icon(Icons.logout),
),
],
),
body: SafeArea(
minimum: const EdgeInsets.all(16.0),
child: Text(user?.email ?? ''),
),
),
if (isLoading)
const Material(
color: Colors.black54,
child: Center(
child: CircularProgressIndicator.adaptive(
backgroundColor: Colors.white,
),
),
),
],
);
},
);
}
}
final userProvider = StateNotifierProvider<UserNotifier, User?>(
(ref) => throw UnimplementedError(),
);
class UserNotifier extends StateNotifier<User?> {
UserNotifier(super.state);
Future<void> login({required String email}) async {
await Future.delayed(const Duration(seconds: 2));
state = User(email: email);
}
Future<void> logout() async {
await Future.delayed(const Duration(seconds: 2));
state = null;
}
}
class User {
User({
required this.email,
});
final String email;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment