d86c694cde
1. lib/app.dart + lib/shared/routing.dart — buildRouter takes a Ref but was being called from a ConsumerWidget's build() with a WidgetRef. Add a routerProvider; the widget watches it instead of constructing the router from its own ref. Real bug — would have crashed compile in slice 1, just never compiled until CI ran. 2. lib/player/audio_handler.dart — override of AudioHandler.seek used `p` for the Duration; rename to `position` to match the base class (avoid_renaming_method_parameters lint). 3. lib/theme/theme_extension.dart — fromTokens() returned a non-const constructor; all inputs are const so make the call const too (prefer_const_constructors). 4. test/library/home_screen_test.dart — same const-constructor lint on the HomeData test fixture. 5. test/smoke_test.dart — drop the unused `import 'package:flutter/material.dart'`. These are exactly the kind of issues the no-in-task-tests rule shifted to CI; this is the first run that actually exercises the analyzer against the slice 1 code.
74 lines
2.5 KiB
Dart
74 lines
2.5 KiB
Dart
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 '../library/album_detail_screen.dart';
|
|
import '../library/artist_detail_screen.dart';
|
|
import '../library/home_screen.dart';
|
|
import '../player/now_playing_screen.dart';
|
|
import '../player/player_bar.dart';
|
|
import 'widgets/version_gate.dart';
|
|
|
|
/// Exposed as a Provider so its single argument is a real `Ref` (the
|
|
/// constructor's redirect closures use `ref.read`). Widgets consume the
|
|
/// router via `ref.watch(routerProvider)` instead of constructing it
|
|
/// themselves with a `WidgetRef`, which can't satisfy the `Ref` type.
|
|
final routerProvider = Provider<GoRouter>((ref) => buildRouter(ref));
|
|
|
|
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 HomeScreen()),
|
|
GoRoute(
|
|
path: '/artists/:id',
|
|
builder: (_, s) => ArtistDetailScreen(id: s.pathParameters['id']!),
|
|
),
|
|
GoRoute(
|
|
path: '/albums/:id',
|
|
builder: (_, s) => AlbumDetailScreen(id: s.pathParameters['id']!),
|
|
),
|
|
GoRoute(path: '/now-playing', builder: (_, __) => const NowPlayingScreen()),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
class _ShellWithPlayerBar extends StatelessWidget {
|
|
const _ShellWithPlayerBar({required this.child});
|
|
final Widget child;
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Column(
|
|
children: [
|
|
Expanded(child: child),
|
|
const PlayerBar(),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|