Skip to content

Commit

Permalink
Merge branch 'master' into feature/1068-replace-flutter-siren-2
Browse files Browse the repository at this point in the history
  • Loading branch information
XavierPaquet-Rapold authored Oct 31, 2024
2 parents 574fa30 + da5cd74 commit f6ffa62
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 21 deletions.
32 changes: 32 additions & 0 deletions lib/features/app/navigation/navigation_history_observer.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import 'package:built_collection/built_collection.dart';
import 'package:flutter/widgets.dart';

class NavigationHistoryObserver extends NavigatorObserver {
final List<Route<dynamic>?> _history = <Route<dynamic>?>[];

/// Gets a clone of the navigation history as an immutable list.
BuiltList<Route<dynamic>> get history =>
BuiltList<Route<dynamic>>.from(_history);

/// Implements a singleton pattern for NavigationHistoryObserver.
static final NavigationHistoryObserver _singleton = NavigationHistoryObserver._internal();
factory NavigationHistoryObserver() {
return _singleton;
}
NavigationHistoryObserver._internal();

@override
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
_history.removeLast();
}

@override
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
_history.add(route);
}

@override
void didRemove(Route<dynamic> route, Route<dynamic>? previousRoute) {
_history.remove(route);
}
}
30 changes: 29 additions & 1 deletion lib/features/app/navigation/navigation_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
// Project imports:
import 'package:notredame/features/app/analytics/analytics_service.dart';
import 'package:notredame/features/app/analytics/remote_config_service.dart';
import 'package:notredame/features/app/navigation/navigation_history_observer.dart';
import 'package:notredame/features/app/navigation/router_paths.dart';
import 'package:notredame/utils/locator.dart';

Expand All @@ -24,6 +25,9 @@ class NavigationService {
/// Will be used to report event and error.
final AnalyticsService _analyticsService = locator<AnalyticsService>();

/// Will be used to remove duplicate routes.
final NavigationHistoryObserver _navigationHistoryObserver = NavigationHistoryObserver();

GlobalKey<NavigatorState> get navigatorKey => _navigatorKey;

/// Pop the last route of the navigator if possible
Expand All @@ -37,7 +41,7 @@ class NavigationService {
return false;
}

/// Push a named route ([routeName] onto the navigator.
/// Push a named route [routeName] onto the navigator.
Future<dynamic> pushNamed(String routeName, {dynamic arguments}) {
final currentState = _navigatorKey.currentState;

Expand All @@ -53,6 +57,30 @@ class NavigationService {
return currentState.pushNamed(routeName, arguments: arguments);
}

/// Push a named route [routeName] onto the navigator
/// and remove existing routes with the same [routeName]
Future<dynamic> pushNamedAndRemoveDuplicates(String routeName, {dynamic arguments}) {
final currentState = _navigatorKey.currentState;

if (currentState == null) {
_analyticsService.logError(tag, "Navigator state is null");
return Future.error("Navigator state is null");
}

if (remoteConfigService.outage) {
return currentState.pushNamedAndRemoveUntil(
RouterPaths.serviceOutage, (route) => false);
}

final route = _navigationHistoryObserver.history
.where((r) => r.settings.name == routeName).firstOrNull;

if (route != null) {
currentState.removeRoute(route);
}
return currentState.pushNamed(routeName, arguments: arguments);
}

/// Replace the current route of the navigator by pushing the route named
/// [routeName] and then delete the stack of previous routes
Future<dynamic> pushNamedAndRemoveUntil(String routeName,
Expand Down
10 changes: 5 additions & 5 deletions lib/features/app/widgets/bottom_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,19 @@ class _BottomBarState extends State<BottomBar> {

switch (index) {
case BottomBar.dashboardView:
_navigationService.pushNamedAndRemoveUntil(RouterPaths.dashboard);
_navigationService.pushNamedAndRemoveDuplicates(RouterPaths.dashboard);
_analyticsService.logEvent("BottomBar", "DashboardView clicked");
case BottomBar.scheduleView:
_navigationService.pushNamedAndRemoveUntil(RouterPaths.schedule);
_navigationService.pushNamedAndRemoveDuplicates(RouterPaths.schedule);
_analyticsService.logEvent("BottomBar", "ScheduleView clicked");
case BottomBar.studentView:
_navigationService.pushNamedAndRemoveUntil(RouterPaths.student);
_navigationService.pushNamedAndRemoveDuplicates(RouterPaths.student);
_analyticsService.logEvent("BottomBar", "StudentView clicked");
case BottomBar.etsView:
_navigationService.pushNamedAndRemoveUntil(RouterPaths.ets);
_navigationService.pushNamedAndRemoveDuplicates(RouterPaths.ets);
_analyticsService.logEvent("BottomBar", "EtsView clicked");
case BottomBar.moreView:
_navigationService.pushNamedAndRemoveUntil(RouterPaths.more);
_navigationService.pushNamedAndRemoveDuplicates(RouterPaths.more);
_analyticsService.logEvent("BottomBar", "MoreView clicked");
}
_currentView = index;
Expand Down
10 changes: 5 additions & 5 deletions lib/features/app/widgets/navigation_rail.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,19 +68,19 @@ class _NavRailState extends State<NavRail> {

switch (index) {
case NavRail.dashboardView:
_navigationService.pushNamedAndRemoveUntil(RouterPaths.dashboard);
_navigationService.pushNamedAndRemoveDuplicates(RouterPaths.dashboard);
_analyticsService.logEvent("BottomBar", "DashboardView clicked");
case NavRail.scheduleView:
_navigationService.pushNamedAndRemoveUntil(RouterPaths.schedule);
_navigationService.pushNamedAndRemoveDuplicates(RouterPaths.schedule);
_analyticsService.logEvent("BottomBar", "ScheduleView clicked");
case NavRail.studentView:
_navigationService.pushNamedAndRemoveUntil(RouterPaths.student);
_navigationService.pushNamedAndRemoveDuplicates(RouterPaths.student);
_analyticsService.logEvent("BottomBar", "StudentView clicked");
case NavRail.etsView:
_navigationService.pushNamedAndRemoveUntil(RouterPaths.ets);
_navigationService.pushNamedAndRemoveDuplicates(RouterPaths.ets);
_analyticsService.logEvent("BottomBar", "EtsView clicked");
case NavRail.moreView:
_navigationService.pushNamedAndRemoveUntil(RouterPaths.more);
_navigationService.pushNamedAndRemoveDuplicates(RouterPaths.more);
_analyticsService.logEvent("BottomBar", "MoreView clicked");
}
_currentView = index;
Expand Down
2 changes: 2 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import 'package:notredame/features/app/error/outage/outage_view.dart';
import 'package:notredame/features/app/integration/firebase_options.dart';
import 'package:notredame/features/app/navigation/navigation_service.dart';
import 'package:notredame/features/app/navigation/router.dart';
import 'package:notredame/features/app/navigation/navigation_history_observer.dart';
import 'package:notredame/features/app/startup/startup_view.dart';
import 'package:notredame/features/ets/events/api-client/hello_api_client.dart';
import 'package:notredame/features/more/feedback/models/custom_feedback_localization.dart';
Expand Down Expand Up @@ -105,6 +106,7 @@ class ETSMobile extends StatelessWidget {
navigatorKey: locator<NavigationService>().navigatorKey,
navigatorObservers: [
locator<AnalyticsService>().getAnalyticsObserver(),
NavigationHistoryObserver(),
],
home: outage ? OutageView() : StartUpView(),
onGenerateRoute: generateRoute,
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,4 @@ flutter:
- assets/

flutter_intl:
enabled: true
enabled: true
15 changes: 6 additions & 9 deletions test/features/app/widgets/bottom_bar_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ void main() {
await tester.tap(find.byIcon(Icons.school_outlined));
await tester.tap(find.byIcon(Icons.school_outlined));

verify(navigationServiceMock.pushNamedAndRemoveUntil(RouterPaths.student))
verify(navigationServiceMock.pushNamedAndRemoveDuplicates(RouterPaths.student))
.called(1);
});

Expand All @@ -70,8 +70,7 @@ void main() {
await tester.tap(find.byIcon(Icons.schedule_outlined));
await tester.tap(find.byIcon(Icons.dashboard_outlined));

verify(navigationServiceMock
.pushNamedAndRemoveUntil(RouterPaths.dashboard));
verify(navigationServiceMock.pushNamedAndRemoveDuplicates(RouterPaths.dashboard));
});

testWidgets('schedule', (WidgetTester tester) async {
Expand All @@ -81,8 +80,7 @@ void main() {

await tester.tap(find.byIcon(Icons.schedule_outlined));

verify(navigationServiceMock
.pushNamedAndRemoveUntil(RouterPaths.schedule));
verify(navigationServiceMock.pushNamedAndRemoveDuplicates(RouterPaths.schedule));
});

testWidgets('student', (WidgetTester tester) async {
Expand All @@ -92,8 +90,7 @@ void main() {

await tester.tap(find.byIcon(Icons.school_outlined));

verify(
navigationServiceMock.pushNamedAndRemoveUntil(RouterPaths.student));
verify(navigationServiceMock.pushNamedAndRemoveDuplicates(RouterPaths.student));
});

testWidgets('ets', (WidgetTester tester) async {
Expand All @@ -103,7 +100,7 @@ void main() {

await tester.tap(find.byIcon(Icons.account_balance_outlined));

verify(navigationServiceMock.pushNamedAndRemoveUntil(RouterPaths.ets));
verify(navigationServiceMock.pushNamedAndRemoveDuplicates(RouterPaths.ets));
});

testWidgets('more', (WidgetTester tester) async {
Expand All @@ -113,7 +110,7 @@ void main() {

await tester.tap(find.byIcon(Icons.menu_outlined));

verify(navigationServiceMock.pushNamedAndRemoveUntil(RouterPaths.more));
verify(navigationServiceMock.pushNamedAndRemoveDuplicates(RouterPaths.more));
});
});
});
Expand Down

0 comments on commit f6ffa62

Please sign in to comment.