From 5f4c7956953f5ac79399a9bb598cb88c789b86e4 Mon Sep 17 00:00:00 2001 From: Mozammal Hossain <152433210+mozammal-hossain@users.noreply.github.com> Date: Sat, 20 Jun 2026 03:11:27 +0600 Subject: [PATCH] =?UTF-8?q?[compass=5Fapp]=20Scope=20LogoutViewModel=20to?= =?UTF-8?q?=20route=20builder=20=E2=80=94=20fix=20#2604=20(#2819)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 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]. [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 --- CHANGELOG.md | 10 ++++++ compass_app/app/lib/routing/router.dart | 10 +++++- .../app/lib/ui/home/widgets/home_screen.dart | 13 ++++++-- .../app/lib/ui/home/widgets/home_title.dart | 15 ++++----- .../ui/home/widgets/home_screen_test.dart | 20 +++++------ cupertino_gallery/lib/widgets/sheet_page.dart | 33 ++++++++----------- 6 files changed, 61 insertions(+), 40 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..3bb4049a4 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +Notable changes to the samples in this repository are listed here. + +## [Unreleased] + +### compass_app + +* Scope `LogoutViewModel` to the home route so it is not recreated on every + `HomeHeader` rebuild ([#2604](https://github.com/flutter/samples/issues/2604)). diff --git a/compass_app/app/lib/routing/router.dart b/compass_app/app/lib/routing/router.dart index 1ed94ed02..284706850 100644 --- a/compass_app/app/lib/routing/router.dart +++ b/compass_app/app/lib/routing/router.dart @@ -11,6 +11,7 @@ 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'; @@ -46,7 +47,14 @@ GoRouter router(AuthRepository authRepository) => GoRouter( bookingRepository: context.read(), userRepository: context.read(), ); - return HomeScreen(viewModel: viewModel); + final logoutViewModel = LogoutViewModel( + authRepository: context.read(), + itineraryConfigRepository: context.read(), + ); + return HomeScreen( + viewModel: viewModel, + logoutViewModel: logoutViewModel, + ); }, routes: [ GoRoute( diff --git a/compass_app/app/lib/ui/home/widgets/home_screen.dart b/compass_app/app/lib/ui/home/widgets/home_screen.dart index fefe78003..09d1e49ba 100644 --- a/compass_app/app/lib/ui/home/widgets/home_screen.dart +++ b/compass_app/app/lib/ui/home/widgets/home_screen.dart @@ -7,6 +7,7 @@ import 'package:go_router/go_router.dart'; import '../../../domain/models/booking/booking_summary.dart'; import '../../../routing/routes.dart'; +import '../../auth/logout/view_models/logout_viewmodel.dart'; import '../../core/localization/applocalization.dart'; import '../../core/themes/colors.dart'; import '../../core/themes/dimens.dart'; @@ -18,9 +19,14 @@ import 'home_title.dart'; const String bookingButtonKey = 'booking-button'; class HomeScreen extends StatefulWidget { - const HomeScreen({super.key, required this.viewModel}); + const HomeScreen({ + super.key, + required this.viewModel, + required this.logoutViewModel, + }); final HomeViewModel viewModel; + final LogoutViewModel logoutViewModel; @override State createState() => _HomeScreenState(); @@ -88,7 +94,10 @@ class _HomeScreenState extends State { vertical: Dimens.of(context).paddingScreenVertical, horizontal: Dimens.of(context).paddingScreenHorizontal, ), - child: HomeHeader(viewModel: widget.viewModel), + child: HomeHeader( + viewModel: widget.viewModel, + logoutViewModel: widget.logoutViewModel, + ), ), ), SliverList.builder( diff --git a/compass_app/app/lib/ui/home/widgets/home_title.dart b/compass_app/app/lib/ui/home/widgets/home_title.dart index 165e7ffe5..16755793a 100644 --- a/compass_app/app/lib/ui/home/widgets/home_title.dart +++ b/compass_app/app/lib/ui/home/widgets/home_title.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; -import 'package:provider/provider.dart'; import '../../auth/logout/view_models/logout_viewmodel.dart'; import '../../auth/logout/widgets/logout_button.dart'; @@ -13,9 +12,14 @@ import '../../core/themes/dimens.dart'; import '../view_models/home_viewmodel.dart'; class HomeHeader extends StatelessWidget { - const HomeHeader({super.key, required this.viewModel}); + const HomeHeader({ + super.key, + required this.viewModel, + required this.logoutViewModel, + }); final HomeViewModel viewModel; + final LogoutViewModel logoutViewModel; @override Widget build(BuildContext context) { @@ -37,12 +41,7 @@ class HomeHeader extends StatelessWidget { height: Dimens.of(context).profilePictureSize, ), ), - LogoutButton( - viewModel: LogoutViewModel( - authRepository: context.read(), - itineraryConfigRepository: context.read(), - ), - ), + LogoutButton(viewModel: logoutViewModel), ], ), const SizedBox(height: Dimens.paddingVertical), diff --git a/compass_app/app/test/ui/home/widgets/home_screen_test.dart b/compass_app/app/test/ui/home/widgets/home_screen_test.dart index 24890be41..a99981100 100644 --- a/compass_app/app/test/ui/home/widgets/home_screen_test.dart +++ b/compass_app/app/test/ui/home/widgets/home_screen_test.dart @@ -2,16 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:compass_app/data/repositories/auth/auth_repository.dart'; -import 'package:compass_app/data/repositories/itinerary_config/itinerary_config_repository.dart'; import 'package:compass_app/routing/routes.dart'; +import 'package:compass_app/ui/auth/logout/view_models/logout_viewmodel.dart'; import 'package:compass_app/ui/home/view_models/home_viewmodel.dart'; import 'package:compass_app/ui/home/widgets/home_screen.dart'; import 'package:compass_app/utils/result.dart'; -import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:provider/provider.dart'; import '../../../../testing/app.dart'; import '../../../../testing/fakes/repositories/fake_auth_repository.dart'; @@ -24,6 +22,7 @@ import '../../../../testing/models/booking.dart'; void main() { group('HomeScreen tests', () { late HomeViewModel viewModel; + late LogoutViewModel logoutViewModel; late MockGoRouter goRouter; late FakeBookingRepository bookingRepository; @@ -33,6 +32,10 @@ void main() { bookingRepository: bookingRepository, userRepository: FakeUserRepository(), ); + logoutViewModel = LogoutViewModel( + authRepository: FakeAuthRepository(), + itineraryConfigRepository: FakeItineraryConfigRepository(), + ); goRouter = MockGoRouter(); when(() => goRouter.push(any())).thenAnswer((_) => Future.value(null)); }); @@ -40,12 +43,9 @@ void main() { Future loadWidget(WidgetTester tester) async { await testApp( tester, - ChangeNotifierProvider.value( - value: FakeAuthRepository() as AuthRepository, - child: Provider.value( - value: FakeItineraryConfigRepository() as ItineraryConfigRepository, - child: HomeScreen(viewModel: viewModel), - ), + HomeScreen( + viewModel: viewModel, + logoutViewModel: logoutViewModel, ), goRouter: goRouter, ); diff --git a/cupertino_gallery/lib/widgets/sheet_page.dart b/cupertino_gallery/lib/widgets/sheet_page.dart index 9d27c0bd2..103d1bdcc 100644 --- a/cupertino_gallery/lib/widgets/sheet_page.dart +++ b/cupertino_gallery/lib/widgets/sheet_page.dart @@ -13,25 +13,20 @@ class SheetPage extends StatelessWidget { onPressed: () { Navigator.of(context).push( CupertinoSheetRoute( - scrollableBuilder: - (BuildContext context, ScrollController controller) { - Widget widgetBuilder(BuildContext context) { - return CupertinoPageScaffold( - navigationBar: CupertinoNavigationBar( - middle: const Text('Sheet'), - trailing: GestureDetector( - child: const Icon(CupertinoIcons.xmark), - onTap: () { - Navigator.of(context).pop(); - }, - ), - ), - child: const Center(child: Text('This is a sheet')), - ); - } - - return widgetBuilder(context); - }, + builder: (BuildContext context) { + return CupertinoPageScaffold( + navigationBar: CupertinoNavigationBar( + middle: const Text('Sheet'), + trailing: GestureDetector( + child: const Icon(CupertinoIcons.xmark), + onTap: () { + Navigator.of(context).pop(); + }, + ), + ), + child: const Center(child: Text('This is a sheet')), + ); + }, ), ); },