1
0
mirror of https://github.com/flutter/samples.git synced 2026-06-25 07:38:26 +00:00
Files
samples/compass_app/app/lib/routing/router.dart
Mozammal Hossain 5f4c795695 [compass_app] Scope LogoutViewModel to route builder — fix #2604 (#2819)
## Description

**Problem:** `LogoutViewModel` was built inside `HomeHeader.build()`, so
each rebuild could create a new instance and a new `logout` command.
That does not match how other Compass view models are scoped (created in
the GoRouter route `builder` and passed in), and it can affect an
in-flight logout when the home screen rebuilds.

**Change:** Create `LogoutViewModel` once in the `/home` route next to
`HomeViewModel`, pass it through `HomeScreen` → `HomeHeader` →
`LogoutButton`, and remove inline construction in `home_title.dart`.
Update `home_screen_test.dart` to build `LogoutViewModel` with fakes and
remove `Provider` wrappers that only supported `context.read()` in the
header.

**Result:** Logout view model lifetime aligns with the home route; no
visual or copy changes (screenshots not needed).

Fixes https://github.com/flutter/samples/issues/2604

---

## Pre-launch Checklist

- [x] I read the [Flutter Style Guide] _recently_, and have followed its
advice.
- [x] I signed the [CLA].
- [x] I read the [Contributors Guide].
- [x] I have added sample code updates to the [changelog].
- [x] I updated/added relevant documentation (doc comments with `///`).

If you need help, consider asking for advice on the #hackers-devrel
channel on [Discord].

<!-- Links -->
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Style-guide-for-Flutter-repo.md
[CLA]: https://cla.developers.google.com/
[Discord]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Chat.md
[Contributors Guide]:
https://github.com/flutter/samples/blob/main/CONTRIBUTING.md
[changelog]: ../CHANGELOG.md

---------

Co-authored-by: Eric Windmill <eric@ericwindmill.com>
2026-06-19 14:11:27 -07:00

150 lines
5.1 KiB
Dart

// Copyright 2024 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/cupertino.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
import '../data/repositories/auth/auth_repository.dart';
import '../ui/activities/view_models/activities_viewmodel.dart';
import '../ui/activities/widgets/activities_screen.dart';
import '../ui/auth/login/view_models/login_viewmodel.dart';
import '../ui/auth/login/widgets/login_screen.dart';
import '../ui/auth/logout/view_models/logout_viewmodel.dart';
import '../ui/booking/view_models/booking_viewmodel.dart';
import '../ui/booking/widgets/booking_screen.dart';
import '../ui/home/view_models/home_viewmodel.dart';
import '../ui/home/widgets/home_screen.dart';
import '../ui/results/view_models/results_viewmodel.dart';
import '../ui/results/widgets/results_screen.dart';
import '../ui/search_form/view_models/search_form_viewmodel.dart';
import '../ui/search_form/widgets/search_form_screen.dart';
import 'routes.dart';
/// Top go_router entry point.
///
/// Listens to changes in [AuthTokenRepository] to redirect the user
/// to /login when the user logs out.
GoRouter router(AuthRepository authRepository) => GoRouter(
initialLocation: Routes.home,
debugLogDiagnostics: true,
redirect: _redirect,
refreshListenable: authRepository,
routes: [
GoRoute(
path: Routes.login,
builder: (context, state) {
return LoginScreen(
viewModel: LoginViewModel(authRepository: context.read()),
);
},
),
GoRoute(
path: Routes.home,
builder: (context, state) {
final viewModel = HomeViewModel(
bookingRepository: context.read(),
userRepository: context.read(),
);
final logoutViewModel = LogoutViewModel(
authRepository: context.read(),
itineraryConfigRepository: context.read(),
);
return HomeScreen(
viewModel: viewModel,
logoutViewModel: logoutViewModel,
);
},
routes: [
GoRoute(
path: Routes.searchRelative,
builder: (context, state) {
final viewModel = SearchFormViewModel(
continentRepository: context.read(),
itineraryConfigRepository: context.read(),
);
return SearchFormScreen(viewModel: viewModel);
},
),
GoRoute(
path: Routes.resultsRelative,
builder: (context, state) {
final viewModel = ResultsViewModel(
destinationRepository: context.read(),
itineraryConfigRepository: context.read(),
);
return ResultsScreen(viewModel: viewModel);
},
),
GoRoute(
path: Routes.activitiesRelative,
builder: (context, state) {
final viewModel = ActivitiesViewModel(
activityRepository: context.read(),
itineraryConfigRepository: context.read(),
);
return ActivitiesScreen(viewModel: viewModel);
},
),
GoRoute(
path: Routes.bookingRelative,
builder: (context, state) {
final viewModel = BookingViewModel(
itineraryConfigRepository: context.read(),
createBookingUseCase: context.read(),
shareBookingUseCase: context.read(),
bookingRepository: context.read(),
);
// When opening the booking screen directly
// create a new booking from the stored ItineraryConfig.
viewModel.createBooking.execute();
return BookingScreen(viewModel: viewModel);
},
routes: [
GoRoute(
path: ':id',
builder: (context, state) {
final id = int.parse(state.pathParameters['id']!);
final viewModel = BookingViewModel(
itineraryConfigRepository: context.read(),
createBookingUseCase: context.read(),
shareBookingUseCase: context.read(),
bookingRepository: context.read(),
);
// When opening the booking screen with an existing id
// load and display that booking.
viewModel.loadBooking.execute(id);
return BookingScreen(viewModel: viewModel);
},
),
],
),
],
),
],
);
// From https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/redirection.dart
Future<String?> _redirect(BuildContext context, GoRouterState state) async {
// if the user is not logged in, they need to login
final loggedIn = await context.read<AuthRepository>().isAuthenticated;
final loggingIn = state.matchedLocation == Routes.login;
if (!loggedIn) {
return Routes.login;
}
// if the user is logged in but still on the login page, send them to
// the home page
if (loggingIn) {
return Routes.home;
}
// no need to redirect at all
return null;
}