Letzter Roadmap-Punkt von V3. Foto-basierte Lichtverhältnis-Analyse pro Stellplatz (Cloud Function analyzeLocation) und darauf aufbauende, rein textbasierte Eignungs-Bewertung einer Pflanze (assessPlantFit) — beides ausschließlich auf Abruf, nicht automatisch (Kostenkontrolle, eigener Anthropic-Key). Neue Stellplatz-Detailseite, Verlinkung im Pflanzen-Detail, Erkennung veralteter Bewertungen bei Umzug/Neuanalyse. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
124 lines
4.2 KiB
Dart
124 lines
4.2 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
|
|
import '../../features/auth/presentation/login_screen.dart';
|
|
import '../../features/household/presentation/household_screen.dart';
|
|
import '../../features/locations/presentation/location_detail_screen.dart';
|
|
import '../../features/locations/presentation/locations_screen.dart';
|
|
import '../../features/plants/domain/plant.dart';
|
|
import '../../features/plants/presentation/plant_detail_screen.dart';
|
|
import '../../features/plants/presentation/plant_diagnoses_screen.dart';
|
|
import '../../features/plants/presentation/plant_form_screen.dart';
|
|
import '../../features/plants/presentation/plants_screen.dart';
|
|
import '../../features/settings/presentation/settings_screen.dart';
|
|
import '../../features/today/presentation/today_screen.dart';
|
|
import '../firebase/firebase_providers.dart';
|
|
|
|
/// Zentrale Routen-Definition. Die Pfade werden auch vom Drawer genutzt,
|
|
/// deshalb hier als Konstanten.
|
|
abstract final class AppRoutes {
|
|
static const today = '/';
|
|
static const login = '/login';
|
|
static const plants = '/plants';
|
|
static const plantAdd = '/plants/add';
|
|
static const plantEdit = '/plants/edit';
|
|
static const locations = '/locations';
|
|
static const household = '/household';
|
|
static const settings = '/settings';
|
|
|
|
/// Untersuchungs-Seite einer Pflanze.
|
|
static String plantDiagnoses(String plantId) =>
|
|
'/plants/$plantId/diagnoses';
|
|
|
|
/// Detailseite eines Stellplatzes (Foto, Standort-Analyse, Eignung).
|
|
static String locationDetail(String locationId) => '/locations/$locationId';
|
|
}
|
|
|
|
/// Übersetzt einen Stream in ein Listenable, damit der Router bei
|
|
/// Login/Logout sofort neu auswertet, wohin umgeleitet werden muss.
|
|
class _StreamListenable extends ChangeNotifier {
|
|
_StreamListenable(Stream<dynamic> stream) {
|
|
_subscription = stream.listen((_) => notifyListeners());
|
|
}
|
|
|
|
late final StreamSubscription<dynamic> _subscription;
|
|
|
|
@override
|
|
void dispose() {
|
|
_subscription.cancel();
|
|
super.dispose();
|
|
}
|
|
}
|
|
|
|
final appRouterProvider = Provider<GoRouter>((ref) {
|
|
final auth = ref.watch(firebaseAuthProvider);
|
|
final refreshListenable = _StreamListenable(auth.authStateChanges());
|
|
ref.onDispose(refreshListenable.dispose);
|
|
|
|
final router = GoRouter(
|
|
initialLocation: AppRoutes.today,
|
|
refreshListenable: refreshListenable,
|
|
redirect: (context, state) {
|
|
final loggedIn = auth.currentUser != null;
|
|
final onLoginScreen = state.matchedLocation == AppRoutes.login;
|
|
if (!loggedIn) return onLoginScreen ? null : AppRoutes.login;
|
|
if (onLoginScreen) return AppRoutes.today;
|
|
return null;
|
|
},
|
|
routes: [
|
|
GoRoute(
|
|
path: AppRoutes.login,
|
|
builder: (context, state) => const LoginScreen(),
|
|
),
|
|
GoRoute(
|
|
path: AppRoutes.today,
|
|
builder: (context, state) => const TodayScreen(),
|
|
),
|
|
GoRoute(
|
|
path: AppRoutes.plants,
|
|
builder: (context, state) => const PlantsScreen(),
|
|
),
|
|
GoRoute(
|
|
path: AppRoutes.plantAdd,
|
|
builder: (context, state) => const PlantFormScreen(),
|
|
),
|
|
GoRoute(
|
|
path: AppRoutes.plantEdit,
|
|
builder: (context, state) =>
|
|
PlantFormScreen(existing: state.extra as Plant?),
|
|
),
|
|
GoRoute(
|
|
path: '/plants/:id',
|
|
builder: (context, state) =>
|
|
PlantDetailScreen(plantId: state.pathParameters['id']!),
|
|
),
|
|
GoRoute(
|
|
path: '/plants/:id/diagnoses',
|
|
builder: (context, state) =>
|
|
PlantDiagnosesScreen(plantId: state.pathParameters['id']!),
|
|
),
|
|
GoRoute(
|
|
path: AppRoutes.locations,
|
|
builder: (context, state) => const LocationsScreen(),
|
|
),
|
|
GoRoute(
|
|
path: '/locations/:id',
|
|
builder: (context, state) =>
|
|
LocationDetailScreen(locationId: state.pathParameters['id']!),
|
|
),
|
|
GoRoute(
|
|
path: AppRoutes.household,
|
|
builder: (context, state) => const HouseholdScreen(),
|
|
),
|
|
GoRoute(
|
|
path: AppRoutes.settings,
|
|
builder: (context, state) => const SettingsScreen(),
|
|
),
|
|
],
|
|
);
|
|
ref.onDispose(router.dispose);
|
|
return router;
|
|
});
|