feat(flutter): GoRouter shell + version gate + auth-aware redirects
Cold launch flow: no server-url -> /server-url. URL set, no token -> /login. Token present -> /home with shell. VersionGate runs once when the URL changes, hits /healthz, blocks if min_client_version > package version. /home is a placeholder until Task 14. Replaces Navigator.pushReplacementNamed in server_url_screen + login_screen with context.go now that go_router owns the routes.
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../auth/auth_provider.dart';
|
||||
import '../auth/login_screen.dart';
|
||||
import '../auth/server_url_screen.dart';
|
||||
import 'widgets/version_gate.dart';
|
||||
|
||||
GoRouter buildRouter(Ref ref) {
|
||||
return GoRouter(
|
||||
initialLocation: '/',
|
||||
redirect: (ctx, state) async {
|
||||
final url = await ref.read(serverUrlProvider.future);
|
||||
if (url == null) return '/server-url';
|
||||
final user = await ref.read(authControllerProvider.future);
|
||||
final loc = state.matchedLocation;
|
||||
if (user == null && loc != '/login' && loc != '/server-url') {
|
||||
return '/login';
|
||||
}
|
||||
if (user != null && (loc == '/login' || loc == '/server-url')) {
|
||||
return '/home';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
routes: [
|
||||
GoRoute(path: '/', redirect: (_, __) => '/home'),
|
||||
GoRoute(path: '/server-url', builder: (_, __) => const ServerUrlScreen()),
|
||||
GoRoute(path: '/login', builder: (_, __) => const LoginScreen()),
|
||||
ShellRoute(
|
||||
builder: (ctx, state, child) => VersionGate(child: _ShellWithPlayerBar(child: child)),
|
||||
routes: [
|
||||
GoRoute(path: '/home', builder: (_, __) => const _HomePlaceholder()),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
class _ShellWithPlayerBar extends StatelessWidget {
|
||||
const _ShellWithPlayerBar({required this.child});
|
||||
final Widget child;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// PlayerBar is wired in Task 18; this placeholder leaves the shell slot.
|
||||
return Column(
|
||||
children: [
|
||||
Expanded(child: child),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _HomePlaceholder extends StatelessWidget {
|
||||
const _HomePlaceholder();
|
||||
@override
|
||||
Widget build(BuildContext context) =>
|
||||
const Scaffold(body: Center(child: Text('Home — coming in Task 14')));
|
||||
}
|
||||
Reference in New Issue
Block a user