refactor: Replaces Home page with Reception page

Removes the dedicated `HomePage` and its associated model files.

Configures `ReceptionPage` as the new default landing page after successful login and product form submission. Updates navigation and state management dependencies to direct users to the primary operational interface upon entry.
This commit is contained in:
mandreshope 2025-07-23 17:19:37 +03:00
parent f01c34d10c
commit 855770b428
12 changed files with 255 additions and 244 deletions

View File

@ -1,6 +1,6 @@
import 'package:barcode_scanner/backend/schema/user/user_struct.dart';
import 'package:barcode_scanner/pages/home/home_page_model.dart';
import 'package:barcode_scanner/pages/login/login_page_model.dart';
import 'package:barcode_scanner/pages/operation/reception/reception_page_model.dart';
import 'package:barcode_scanner/router/go_router_builder.dart';
import 'package:barcode_scanner/router/go_secure_router_builder.dart';
import 'package:barcode_scanner/themes/app_theme.dart';
@ -52,7 +52,7 @@ class _DrawerComponentState extends ConsumerState<DrawerComponent> {
// === Profil utilisateur
Consumer(
builder: (context, ref, child) {
final state = ref.watch(homePageModelProvider);
final state = ref.watch(receptionPageModelProvider);
return ListTile(
onTap: () => ProfileRoute().push(context),
contentPadding: EdgeInsets.zero,

View File

@ -1,152 +0,0 @@
import 'package:barcode_scanner/components/components.dart';
import 'package:barcode_scanner/pages/home/home_page_model.dart';
import 'package:barcode_scanner/router/go_secure_router_builder.dart';
import 'package:barcode_scanner/themes/app_theme.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class HomePage extends ConsumerStatefulWidget {
const HomePage({super.key});
@override
ConsumerState<ConsumerStatefulWidget> createState() => _HomePageState();
}
class _HomePageState extends ConsumerState<HomePage> {
@override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((_) {
ref.read(homePageModelProvider.notifier).getUserConnected();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
drawer: DrawerComponent(isOperationExpanded: true),
backgroundColor: AppTheme.of(context).primaryBackground,
appBar: AppBar(
title: Text('Barcode Scanner', style: AppTheme.of(context).titleLarge),
centerTitle: true,
backgroundColor: AppTheme.of(context).primaryBackground,
actions: [],
),
body: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
/// HEADER ICON
Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: AppTheme.of(context).primary,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Color(0x225F3DFF),
blurRadius: 20,
offset: Offset(0, 12),
),
],
),
child: const Center(
child: Icon(
Icons.qr_code_scanner_rounded,
color: Colors.white,
size: 50,
),
),
),
const SizedBox(height: 24),
/// TITLE
const Text(
'Barcode Scanner',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 12),
/// SUBTITLE
const Text(
'Scannez facilement tous vos code barre\nen quelques secondes',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 16, color: Colors.grey),
),
const SizedBox(height: 36),
/// SCANNER PREVIEW AREA (placeholder)
Container(
width: 260,
height: 200,
padding: const EdgeInsets.all(40),
decoration: BoxDecoration(
color: AppTheme.of(context).alternate,
borderRadius: BorderRadius.circular(24),
boxShadow: const [
BoxShadow(
color: Color(0x11000000),
blurRadius: 10,
offset: Offset(0, 8),
),
],
),
child: Icon(
Icons.qr_code_2_rounded,
size: 60,
color: AppTheme.of(context).primaryText,
),
),
const SizedBox(height: 16),
const Text(
'Pointez votre caméra vers le code\nbarre',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.grey),
),
const SizedBox(height: 40),
/// START BUTTON
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: () {
ScannerRoute().push(context);
},
style: ElevatedButton.styleFrom(
backgroundColor: AppTheme.of(context).primary,
padding: const EdgeInsets.symmetric(vertical: 18),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
),
icon: const Icon(
Icons.qr_code_scanner_rounded,
color: Colors.white,
size: 30,
),
label: const Text(
'Commencer le scan',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
),
],
),
),
),
);
}
}

View File

@ -0,0 +1 @@
export 'reception/reception_page.dart';

View File

@ -0,0 +1,166 @@
import 'package:barcode_scanner/components/drawer_component.dart';
import 'package:barcode_scanner/pages/operation/reception/reception_page_model.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter/material.dart';
class ReceptionPage extends ConsumerStatefulWidget {
const ReceptionPage({super.key});
@override
ConsumerState<ReceptionPage> createState() => _ReceptionPageState();
}
class _ReceptionPageState extends ConsumerState<ReceptionPage> {
@override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((_) {
ref.read(receptionPageModelProvider.notifier).getUserConnected();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
drawer: DrawerComponent(isOperationExpanded: true),
appBar: AppBar(
title: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Gestionnaire d'Inventaire", style: TextStyle(fontSize: 18)),
Text("Opérations d'Entrepôt", style: TextStyle(fontSize: 12)),
],
),
toolbarHeight: 60,
backgroundColor: Colors.blue,
elevation: 4,
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: ListView(
children: [
Row(
children: [
const Icon(Icons.arrow_back),
const SizedBox(width: 8),
const Text(
'Réceptions',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
],
),
const SizedBox(height: 16),
Card(
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(color: Colors.grey.shade300),
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Text(
'Actions Rapides',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const Spacer(),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 4,
),
decoration: BoxDecoration(
color: Colors.grey.shade200,
borderRadius: BorderRadius.circular(20),
),
child: const Text(
'3 en attente',
style: TextStyle(fontSize: 12),
),
),
],
),
const SizedBox(height: 16),
ElevatedButton.icon(
icon: const Icon(Icons.add),
label: const Text('Nouvelle Réception'),
onPressed: () {},
style: ElevatedButton.styleFrom(
minimumSize: const Size.fromHeight(50),
),
),
const SizedBox(height: 12),
OutlinedButton.icon(
icon: const Icon(Icons.qr_code_scanner),
label: const Text('Scanner Code-Barres'),
onPressed: () {},
style: OutlinedButton.styleFrom(
minimumSize: const Size.fromHeight(50),
),
),
const SizedBox(height: 12),
OutlinedButton.icon(
icon: const Icon(Icons.search),
label: const Text('Rechercher Existant'),
onPressed: () {},
style: OutlinedButton.styleFrom(
minimumSize: const Size.fromHeight(50),
),
),
],
),
),
),
const SizedBox(height: 16),
Card(
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(color: Colors.grey.shade300),
),
child: const Padding(
padding: EdgeInsets.all(16),
child: Text(
'Activité Récente',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
),
),
],
),
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: 1, // Opérations sélectionné
selectedItemColor: Colors.blue,
unselectedItemColor: Colors.grey,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home_outlined),
label: 'Accueil',
),
BottomNavigationBarItem(icon: Icon(Icons.inbox), label: 'Opérations'),
BottomNavigationBarItem(
icon: Icon(Icons.insert_chart),
label: 'Produits',
),
BottomNavigationBarItem(
icon: Icon(Icons.history),
label: 'Historique',
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: 'Paramètres',
),
],
),
);
}
}

View File

@ -6,22 +6,20 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'home_page_model.freezed.dart';
part 'reception_page_model.freezed.dart';
/// The provider for the AuthViewModel, using Riverpod's StateNotifierProvider
/// with autoDispose to manage the lifecycle of the view model.
final homePageModelProvider =
StateNotifierProvider<HomePageModel, HomePageState>((ref) {
return HomePageModel(
final receptionPageModelProvider =
StateNotifierProvider<ReceptionPageModel, ReceptionPageState>((ref) {
return ReceptionPageModel(
secureStorage: ref.read(sharedPrefsProvider),
tokenProvider: ref.read(tokenProvider),
);
});
class HomePageModel extends StateNotifier<HomePageState> {
class ReceptionPageModel extends StateNotifier<ReceptionPageState> {
/// Constructor initializes the TaskRepository using the provider reference.
HomePageModel({required this.secureStorage, required this.tokenProvider})
: super(const HomePageState());
ReceptionPageModel({required this.secureStorage, required this.tokenProvider})
: super(const ReceptionPageState());
late FlutterSecureStorage secureStorage;
late TokenProvider tokenProvider;
@ -34,9 +32,9 @@ class HomePageModel extends StateNotifier<HomePageState> {
}
@freezed
abstract class HomePageState with _$HomePageState {
const factory HomePageState({
abstract class ReceptionPageState with _$ReceptionPageState {
const factory ReceptionPageState({
UserStruct? user,
@Default(false) bool loading,
}) = _HomePageState;
}) = _ReceptionPageState;
}

View File

@ -4,7 +4,7 @@
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'home_page_model.dart';
part of 'reception_page_model.dart';
// **************************************************************************
// FreezedGenerator
@ -13,26 +13,26 @@ part of 'home_page_model.dart';
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$HomePageState implements DiagnosticableTreeMixin {
mixin _$ReceptionPageState implements DiagnosticableTreeMixin {
UserStruct? get user; bool get loading;
/// Create a copy of HomePageState
/// Create a copy of ReceptionPageState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$HomePageStateCopyWith<HomePageState> get copyWith => _$HomePageStateCopyWithImpl<HomePageState>(this as HomePageState, _$identity);
$ReceptionPageStateCopyWith<ReceptionPageState> get copyWith => _$ReceptionPageStateCopyWithImpl<ReceptionPageState>(this as ReceptionPageState, _$identity);
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
properties
..add(DiagnosticsProperty('type', 'HomePageState'))
..add(DiagnosticsProperty('type', 'ReceptionPageState'))
..add(DiagnosticsProperty('user', user))..add(DiagnosticsProperty('loading', loading));
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is HomePageState&&(identical(other.user, user) || other.user == user)&&(identical(other.loading, loading) || other.loading == loading));
return identical(this, other) || (other.runtimeType == runtimeType&&other is ReceptionPageState&&(identical(other.user, user) || other.user == user)&&(identical(other.loading, loading) || other.loading == loading));
}
@ -41,15 +41,15 @@ int get hashCode => Object.hash(runtimeType,user,loading);
@override
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
return 'HomePageState(user: $user, loading: $loading)';
return 'ReceptionPageState(user: $user, loading: $loading)';
}
}
/// @nodoc
abstract mixin class $HomePageStateCopyWith<$Res> {
factory $HomePageStateCopyWith(HomePageState value, $Res Function(HomePageState) _then) = _$HomePageStateCopyWithImpl;
abstract mixin class $ReceptionPageStateCopyWith<$Res> {
factory $ReceptionPageStateCopyWith(ReceptionPageState value, $Res Function(ReceptionPageState) _then) = _$ReceptionPageStateCopyWithImpl;
@useResult
$Res call({
UserStruct? user, bool loading
@ -60,14 +60,14 @@ $UserStructCopyWith<$Res>? get user;
}
/// @nodoc
class _$HomePageStateCopyWithImpl<$Res>
implements $HomePageStateCopyWith<$Res> {
_$HomePageStateCopyWithImpl(this._self, this._then);
class _$ReceptionPageStateCopyWithImpl<$Res>
implements $ReceptionPageStateCopyWith<$Res> {
_$ReceptionPageStateCopyWithImpl(this._self, this._then);
final HomePageState _self;
final $Res Function(HomePageState) _then;
final ReceptionPageState _self;
final $Res Function(ReceptionPageState) _then;
/// Create a copy of HomePageState
/// Create a copy of ReceptionPageState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? user = freezed,Object? loading = null,}) {
return _then(_self.copyWith(
@ -76,7 +76,7 @@ as UserStruct?,loading: null == loading ? _self.loading : loading // ignore: cas
as bool,
));
}
/// Create a copy of HomePageState
/// Create a copy of ReceptionPageState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
@ -95,30 +95,30 @@ $UserStructCopyWith<$Res>? get user {
/// @nodoc
class _HomePageState with DiagnosticableTreeMixin implements HomePageState {
const _HomePageState({this.user, this.loading = false});
class _ReceptionPageState with DiagnosticableTreeMixin implements ReceptionPageState {
const _ReceptionPageState({this.user, this.loading = false});
@override final UserStruct? user;
@override@JsonKey() final bool loading;
/// Create a copy of HomePageState
/// Create a copy of ReceptionPageState
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$HomePageStateCopyWith<_HomePageState> get copyWith => __$HomePageStateCopyWithImpl<_HomePageState>(this, _$identity);
_$ReceptionPageStateCopyWith<_ReceptionPageState> get copyWith => __$ReceptionPageStateCopyWithImpl<_ReceptionPageState>(this, _$identity);
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
properties
..add(DiagnosticsProperty('type', 'HomePageState'))
..add(DiagnosticsProperty('type', 'ReceptionPageState'))
..add(DiagnosticsProperty('user', user))..add(DiagnosticsProperty('loading', loading));
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _HomePageState&&(identical(other.user, user) || other.user == user)&&(identical(other.loading, loading) || other.loading == loading));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ReceptionPageState&&(identical(other.user, user) || other.user == user)&&(identical(other.loading, loading) || other.loading == loading));
}
@ -127,15 +127,15 @@ int get hashCode => Object.hash(runtimeType,user,loading);
@override
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
return 'HomePageState(user: $user, loading: $loading)';
return 'ReceptionPageState(user: $user, loading: $loading)';
}
}
/// @nodoc
abstract mixin class _$HomePageStateCopyWith<$Res> implements $HomePageStateCopyWith<$Res> {
factory _$HomePageStateCopyWith(_HomePageState value, $Res Function(_HomePageState) _then) = __$HomePageStateCopyWithImpl;
abstract mixin class _$ReceptionPageStateCopyWith<$Res> implements $ReceptionPageStateCopyWith<$Res> {
factory _$ReceptionPageStateCopyWith(_ReceptionPageState value, $Res Function(_ReceptionPageState) _then) = __$ReceptionPageStateCopyWithImpl;
@override @useResult
$Res call({
UserStruct? user, bool loading
@ -146,24 +146,24 @@ $Res call({
}
/// @nodoc
class __$HomePageStateCopyWithImpl<$Res>
implements _$HomePageStateCopyWith<$Res> {
__$HomePageStateCopyWithImpl(this._self, this._then);
class __$ReceptionPageStateCopyWithImpl<$Res>
implements _$ReceptionPageStateCopyWith<$Res> {
__$ReceptionPageStateCopyWithImpl(this._self, this._then);
final _HomePageState _self;
final $Res Function(_HomePageState) _then;
final _ReceptionPageState _self;
final $Res Function(_ReceptionPageState) _then;
/// Create a copy of HomePageState
/// Create a copy of ReceptionPageState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? user = freezed,Object? loading = null,}) {
return _then(_HomePageState(
return _then(_ReceptionPageState(
user: freezed == user ? _self.user : user // ignore: cast_nullable_to_non_nullable
as UserStruct?,loading: null == loading ? _self.loading : loading // ignore: cast_nullable_to_non_nullable
as bool,
));
}
/// Create a copy of HomePageState
/// Create a copy of ReceptionPageState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')

View File

@ -1,7 +1,7 @@
export 'home/home_page.dart';
export 'login/login_page.dart';
export 'product/product_form/product_form_page.dart';
export 'scanner/scanner_page.dart';
export 'splash/splash_page.dart';
export 'product/product_list/product_list_page.dart';
export 'profile/profile_page.dart';
export 'operation/operation_page.dart';

View File

@ -139,7 +139,7 @@ class _ProductFormPageState extends ConsumerState<ProductFormPage> {
debugPrint(
"Produit : ${name.text}, Code : ${code.text}",
);
HomeRoute().go(context);
ReceptionRoute().go(context);
}
},
text: 'Sauvegarder',

View File

@ -15,7 +15,7 @@ class SplashPage extends ConsumerWidget {
final authViewModel = ref.watch(loginPageModelProvider);
if (authViewModel.status.isLogged) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
HomeRoute().go(context);
ReceptionRoute().go(context);
});
} else if (authViewModel.status.isLogOut) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {

View File

@ -8,22 +8,15 @@ part 'go_secure_router_builder.g.dart';
final appSecureRoutes = $appRoutes;
const String _securePage = 'SecurePage';
const String _homePage = 'HomePage';
const String _scannerPage = 'ScannerPage';
const String _productFormPage = 'ProductFormPage';
const String _productListPage = 'ProductListPage';
const String _profilePage = "ProfilePage";
// Groupe des routes sécurisées
@TypedGoRoute<SecureRoute>(
path: '/$_securePage',
path: '/SecurePage',
routes: [
TypedGoRoute<HomeRoute>(path: _homePage),
TypedGoRoute<ScannerRoute>(path: _scannerPage),
TypedGoRoute<ProductFormRoute>(path: _productFormPage),
TypedGoRoute<ProductListRoute>(path: _productListPage),
TypedGoRoute<ProfileRoute>(path: _profilePage),
TypedGoRoute<ScannerRoute>(path: 'ScannerPage'),
TypedGoRoute<ProductFormRoute>(path: 'ProductFormPage'),
TypedGoRoute<ProductListRoute>(path: 'ProductListPage'),
TypedGoRoute<ProfileRoute>(path: 'ProfilePage'),
TypedGoRoute<ReceptionRoute>(path: 'ReceptionRoute'),
],
)
class SecureRoute extends GoRouteData with _$SecureRoute {
@ -43,13 +36,6 @@ class SecureRoute extends GoRouteData with _$SecureRoute {
}
}
class HomeRoute extends GoRouteData with _$HomeRoute {
const HomeRoute();
@override
Widget build(BuildContext context, GoRouterState state) => HomePage();
}
class ScannerRoute extends GoRouteData with _$ScannerRoute {
const ScannerRoute();
@ -79,3 +65,10 @@ class ProfileRoute extends GoRouteData with _$ProfileRoute {
@override
Widget build(BuildContext context, GoRouterState state) => ProfilePage();
}
class ReceptionRoute extends GoRouteData with _$ReceptionRoute {
const ReceptionRoute();
@override
Widget build(BuildContext context, GoRouterState state) => ReceptionPage();
}

View File

@ -13,7 +13,6 @@ RouteBase get $secureRoute => GoRouteData.$route(
factory: _$SecureRoute._fromState,
routes: [
GoRouteData.$route(path: 'HomePage', factory: _$HomeRoute._fromState),
GoRouteData.$route(path: 'ScannerPage', factory: _$ScannerRoute._fromState),
GoRouteData.$route(
path: 'ProductFormPage',
@ -26,6 +25,11 @@ RouteBase get $secureRoute => GoRouteData.$route(
factory: _$ProductListRoute._fromState,
),
GoRouteData.$route(path: 'ProfilePage', factory: _$ProfileRoute._fromState),
GoRouteData.$route(
path: 'ReceptionRoute',
factory: _$ReceptionRoute._fromState,
),
],
);
@ -49,26 +53,6 @@ mixin _$SecureRoute on GoRouteData {
void replace(BuildContext context) => context.replace(location);
}
mixin _$HomeRoute on GoRouteData {
static HomeRoute _fromState(GoRouterState state) => const HomeRoute();
@override
String get location => GoRouteData.$location('/SecurePage/HomePage');
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
mixin _$ScannerRoute on GoRouteData {
static ScannerRoute _fromState(GoRouterState state) => const ScannerRoute();
@ -155,3 +139,24 @@ mixin _$ProfileRoute on GoRouteData {
@override
void replace(BuildContext context) => context.replace(location);
}
mixin _$ReceptionRoute on GoRouteData {
static ReceptionRoute _fromState(GoRouterState state) =>
const ReceptionRoute();
@override
String get location => GoRouteData.$location('/SecurePage/ReceptionRoute');
@override
void go(BuildContext context) => context.go(location);
@override
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}

View File

@ -19,7 +19,7 @@ final routerProvider = Provider<GoRouter>(
routes: [...appRoutes, ...appSecureRoutes],
observers: [AppNavigatorObserver(ref: ref)],
navigatorKey: navigatorKey,
initialLocation: const HomeRoute().location,
initialLocation: const ReceptionRoute().location,
errorBuilder: (context, state) {
// using a post frame callback to perform the navigation after
// the build frame has finished