Fixed bug where homescreen would load twice
Moved AccountMeta provider into <User> context and migrated to using a FutureProvider to perform the network request NOTE: Bug was caused by AccountMeta? being loaded late, causing the root provider to be reloaded.
This commit is contained in:
parent
1af8d6f068
commit
ecccec7950
3 changed files with 172 additions and 161 deletions
325
lib/main.dart
325
lib/main.dart
|
@ -24,9 +24,9 @@ void main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
final GlobalKey<NavigatorState> _rootNavigatorKey =
|
final GlobalKey<NavigatorState> _rootNavigatorKey =
|
||||||
GlobalKey<NavigatorState>(debugLabel: 'root');
|
GlobalKey<NavigatorState>(debugLabel: 'root');
|
||||||
final GlobalKey<NavigatorState> _userShellNavigatorKey =
|
final GlobalKey<NavigatorState> _userShellNavigatorKey =
|
||||||
GlobalKey<NavigatorState>(debugLabel: 'user');
|
GlobalKey<NavigatorState>(debugLabel: 'user');
|
||||||
|
|
||||||
class OutbagApp extends StatefulWidget {
|
class OutbagApp extends StatefulWidget {
|
||||||
const OutbagApp({super.key});
|
const OutbagApp({super.key});
|
||||||
|
@ -39,7 +39,6 @@ class _OutbagAppState extends State {
|
||||||
// assume user is logged in
|
// assume user is logged in
|
||||||
// unless not userdata is found
|
// unless not userdata is found
|
||||||
// or the userdata turns out to be wrong
|
// or the userdata turns out to be wrong
|
||||||
AccountMeta? info;
|
|
||||||
User? user;
|
User? user;
|
||||||
|
|
||||||
AppTheme theme = AppTheme.auto;
|
AppTheme theme = AppTheme.auto;
|
||||||
|
@ -49,7 +48,7 @@ class _OutbagAppState extends State {
|
||||||
try {
|
try {
|
||||||
final theme = await AppTheme.fromDisk();
|
final theme = await AppTheme.fromDisk();
|
||||||
setState(() {
|
setState(() {
|
||||||
this.theme = theme;
|
this.theme = theme;
|
||||||
});
|
});
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
}
|
}
|
||||||
|
@ -59,15 +58,14 @@ class _OutbagAppState extends State {
|
||||||
try {
|
try {
|
||||||
final user = await User.fromDisk();
|
final user = await User.fromDisk();
|
||||||
setState(() {
|
setState(() {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
});
|
});
|
||||||
fetchInfo(user);
|
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
// user unavailable
|
// user unavailable
|
||||||
// invalid credentials
|
// invalid credentials
|
||||||
// log out
|
// log out
|
||||||
setState(() {
|
setState(() {
|
||||||
user = null;
|
user = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,177 +75,186 @@ class _OutbagAppState extends State {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
loadTheme();
|
loadTheme();
|
||||||
loadUser();
|
loadUser();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void fetchInfo(User user) async {
|
Future<AccountMeta?> fetchInfo(User user) async {
|
||||||
|
AccountMeta? info;
|
||||||
// try to obtain user account information
|
// try to obtain user account information
|
||||||
// with existing details
|
// with existing details
|
||||||
// NOTE: also functions as a way to verify ther data
|
// NOTE: also functions as a way to verify ther data
|
||||||
doNetworkRequest(null,
|
await doNetworkRequest(null,
|
||||||
req: () => postWithCreadentials(
|
req: () => postWithCreadentials(
|
||||||
target: user.server,
|
target: user.server,
|
||||||
path: 'getMyAccount',
|
path: 'getMyAccount',
|
||||||
credentials: user,
|
credentials: user,
|
||||||
body: {}),
|
body: {}),
|
||||||
onOK: (body) {
|
onOK: (body) async {
|
||||||
final info = AccountMeta.fromJSON(body['data']);
|
final i = AccountMeta.fromJSON(body['data']);
|
||||||
setState(() {
|
info = i;
|
||||||
this.info = info;
|
},
|
||||||
});
|
onServerErr: (_) {
|
||||||
},
|
info = null;
|
||||||
onServerErr: (_) {
|
|
||||||
// credentials are wrong
|
|
||||||
// log out
|
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
info = null;
|
|
||||||
this.user = null;
|
this.user = null;
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
onNetworkErr: () {
|
||||||
|
info = null;
|
||||||
|
// user is currently offline
|
||||||
|
// approve login,
|
||||||
|
// until user goes back offline
|
||||||
|
// NOTE TODO: check user data once online
|
||||||
|
return true;
|
||||||
});
|
});
|
||||||
return true;
|
|
||||||
},
|
return info;
|
||||||
onNetworkErr: () {
|
|
||||||
// user is currently offline
|
|
||||||
// approve login,
|
|
||||||
// until user goes back offline
|
|
||||||
// NOTE TODO: check user data once online
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MultiProvider(
|
return MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
Provider<AccountMeta?>.value(
|
Provider<AppTheme>.value(value: theme),
|
||||||
value: info,
|
|
||||||
),
|
|
||||||
Provider<AppTheme>.value(value: theme),
|
|
||||||
],
|
|
||||||
child: MaterialApp.router(
|
|
||||||
title: "Outbag",
|
|
||||||
localizationsDelegates: const [
|
|
||||||
GlobalMaterialLocalizations.delegate,
|
|
||||||
GlobalWidgetsLocalizations.delegate,
|
|
||||||
GlobalCupertinoLocalizations.delegate,
|
|
||||||
AppLocalizations.delegate
|
|
||||||
],
|
],
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
child: MaterialApp.router(
|
||||||
themeMode: theme.mode,
|
title: "Outbag",
|
||||||
theme: ThemeData(useMaterial3: true, brightness: Brightness.light),
|
localizationsDelegates: const [
|
||||||
darkTheme: ThemeData(useMaterial3: true, brightness: Brightness.dark),
|
GlobalMaterialLocalizations.delegate,
|
||||||
routerConfig: GoRouter(
|
GlobalWidgetsLocalizations.delegate,
|
||||||
navigatorKey: _rootNavigatorKey,
|
GlobalCupertinoLocalizations.delegate,
|
||||||
initialLocation: '/',
|
AppLocalizations.delegate
|
||||||
redirect: (context, state) async {
|
],
|
||||||
if (user == null) {
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
// prelogin
|
themeMode: theme.mode,
|
||||||
if (!state.subloc.startsWith('/welcome')) {
|
theme: ThemeData(useMaterial3: true, brightness: Brightness.light),
|
||||||
// prevent unauthorized user from accessing home
|
darkTheme: ThemeData(useMaterial3: true, brightness: Brightness.dark),
|
||||||
return '/welcome';
|
routerConfig: GoRouter(
|
||||||
}
|
navigatorKey: _rootNavigatorKey,
|
||||||
} else {
|
initialLocation: '/',
|
||||||
// post login
|
redirect: (context, state) async {
|
||||||
if (state.subloc.startsWith('/welcome')) {
|
if (user == null) {
|
||||||
// prevent authorized user from accessing /welcome
|
// prelogin
|
||||||
return '/';
|
if (!state.subloc.startsWith('/welcome')) {
|
||||||
}
|
// prevent unauthorized user from accessing home
|
||||||
}
|
return '/welcome';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// post login
|
||||||
|
if (state.subloc.startsWith('/welcome')) {
|
||||||
|
// prevent authorized user from accessing /welcome
|
||||||
|
return '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
routes: <RouteBase>[
|
|
||||||
// unauthorized routes
|
|
||||||
GoRoute(
|
|
||||||
name: 'welcome',
|
|
||||||
path: '/welcome',
|
|
||||||
builder: (context, state) => const WelcomePage(),
|
|
||||||
routes: <RouteBase>[
|
routes: <RouteBase>[
|
||||||
|
// unauthorized routes
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: 'signin',
|
name: 'welcome',
|
||||||
path: 'signin',
|
path: '/welcome',
|
||||||
builder: (context, state) => AuthPage(mode: Mode.signin, refresh: loadUser),
|
builder: (context, state) => const WelcomePage(),
|
||||||
),
|
routes: <RouteBase>[
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: 'signup',
|
name: 'signin',
|
||||||
path: 'signup',
|
path: 'signin',
|
||||||
builder: (context, state) => AuthPage(mode: Mode.signup, refresh: loadUser),
|
builder: (context, state) =>
|
||||||
),
|
AuthPage(mode: Mode.signin, refresh: loadUser),
|
||||||
GoRoute(
|
),
|
||||||
name: 'signup-ota',
|
GoRoute(
|
||||||
path: 'signup-ota',
|
name: 'signup',
|
||||||
builder: (context, state) => AuthPage(mode: Mode.signupOTA, refresh: loadUser),
|
path: 'signup',
|
||||||
),
|
builder: (context, state) =>
|
||||||
]),
|
AuthPage(mode: Mode.signup, refresh: loadUser),
|
||||||
|
),
|
||||||
// authorized routes
|
GoRoute(
|
||||||
ShellRoute(
|
name: 'signup-ota',
|
||||||
navigatorKey: _userShellNavigatorKey,
|
path: 'signup-ota',
|
||||||
builder: (context, state, child) =>
|
builder: (context, state) =>
|
||||||
Provider.value(value: user!, child: child),
|
AuthPage(mode: Mode.signupOTA, refresh: loadUser),
|
||||||
routes: <RouteBase>[
|
),
|
||||||
GoRoute(
|
|
||||||
path: '/',
|
|
||||||
name: 'home',
|
|
||||||
builder: (context, state) => const HomePage(),
|
|
||||||
routes: [
|
|
||||||
GoRoute(
|
|
||||||
name: 'settings',
|
|
||||||
path: 'settings',
|
|
||||||
builder: (context, state) => SettingsPage(refreshTheme: loadTheme)),
|
|
||||||
GoRoute(
|
|
||||||
path: 'join-room',
|
|
||||||
name: 'add-room',
|
|
||||||
builder: (context, state) =>
|
|
||||||
const JoinRoomPage(),
|
|
||||||
routes: <RouteBase>[
|
|
||||||
GoRoute(
|
|
||||||
path: 'new',
|
|
||||||
name: 'new-room',
|
|
||||||
builder: (context, state) =>
|
|
||||||
const NewRoomPage()),
|
|
||||||
]),
|
]),
|
||||||
GoRoute(
|
|
||||||
name: 'room',
|
|
||||||
path: 'r/:server/:id',
|
|
||||||
builder: (context, state) => RoomPage(
|
|
||||||
state.params['server'] ?? '',
|
|
||||||
state.params['id'] ?? ''),
|
|
||||||
routes: <RouteBase>[
|
|
||||||
GoRoute(
|
|
||||||
name: 'edit-room',
|
|
||||||
path: 'edit',
|
|
||||||
builder: (context, state) => EditRoomPage(
|
|
||||||
state.params['server'] ?? '',
|
|
||||||
state.params['id'] ?? '')),
|
|
||||||
GoRoute(
|
|
||||||
name: 'room-members',
|
|
||||||
path: 'members',
|
|
||||||
builder: (context, state) =>
|
|
||||||
ManageRoomMembersPage(
|
|
||||||
state.params['server'] ?? '',
|
|
||||||
state.params['id'] ?? '')),
|
|
||||||
GoRoute(
|
|
||||||
name: 'room-permissions',
|
|
||||||
path: 'roles',
|
|
||||||
builder: (context, state) =>
|
|
||||||
EditRoomPermissionSetPage(
|
|
||||||
state.params['server'] ?? '',
|
|
||||||
state.params['id'] ?? '')),
|
|
||||||
])
|
|
||||||
]),
|
|
||||||
]),
|
|
||||||
|
|
||||||
// routes that can be accessed
|
// authorized routes
|
||||||
// with and without an account
|
ShellRoute(
|
||||||
// i.e the about screen
|
navigatorKey: _userShellNavigatorKey,
|
||||||
GoRoute(
|
builder: (context, state, child) => Provider.value(
|
||||||
path: '/about',
|
value: user!,
|
||||||
name: 'about',
|
child: FutureProvider(
|
||||||
builder: (context, state) => const Text('About'))
|
initialData: null,
|
||||||
]),
|
child: child,
|
||||||
));
|
create: (context)=>fetchInfo(context.read<User>()),
|
||||||
|
)),
|
||||||
|
routes: <RouteBase>[
|
||||||
|
GoRoute(
|
||||||
|
path: '/',
|
||||||
|
name: 'home',
|
||||||
|
builder: (context, state) => const HomePage(),
|
||||||
|
routes: [
|
||||||
|
GoRoute(
|
||||||
|
name: 'settings',
|
||||||
|
path: 'settings',
|
||||||
|
builder: (context, state) =>
|
||||||
|
SettingsPage(
|
||||||
|
refreshTheme: loadTheme,
|
||||||
|
refreshUser: loadUser
|
||||||
|
)),
|
||||||
|
GoRoute(
|
||||||
|
path: 'join-room',
|
||||||
|
name: 'add-room',
|
||||||
|
builder: (context, state) =>
|
||||||
|
const JoinRoomPage(),
|
||||||
|
routes: <RouteBase>[
|
||||||
|
GoRoute(
|
||||||
|
path: 'new',
|
||||||
|
name: 'new-room',
|
||||||
|
builder: (context, state) =>
|
||||||
|
const NewRoomPage()),
|
||||||
|
]),
|
||||||
|
GoRoute(
|
||||||
|
name: 'room',
|
||||||
|
path: 'r/:server/:id',
|
||||||
|
builder: (context, state) => RoomPage(
|
||||||
|
state.params['server'] ?? '',
|
||||||
|
state.params['id'] ?? ''),
|
||||||
|
routes: <RouteBase>[
|
||||||
|
GoRoute(
|
||||||
|
name: 'edit-room',
|
||||||
|
path: 'edit',
|
||||||
|
builder: (context, state) => EditRoomPage(
|
||||||
|
state.params['server'] ?? '',
|
||||||
|
state.params['id'] ?? '')),
|
||||||
|
GoRoute(
|
||||||
|
name: 'room-members',
|
||||||
|
path: 'members',
|
||||||
|
builder: (context, state) =>
|
||||||
|
ManageRoomMembersPage(
|
||||||
|
state.params['server'] ?? '',
|
||||||
|
state.params['id'] ?? '')),
|
||||||
|
GoRoute(
|
||||||
|
name: 'room-permissions',
|
||||||
|
path: 'roles',
|
||||||
|
builder: (context, state) =>
|
||||||
|
EditRoomPermissionSetPage(
|
||||||
|
state.params['server'] ?? '',
|
||||||
|
state.params['id'] ?? '')),
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
|
||||||
|
// routes that can be accessed
|
||||||
|
// with and without an account
|
||||||
|
// i.e the about screen
|
||||||
|
GoRoute(
|
||||||
|
path: '/about',
|
||||||
|
name: 'about',
|
||||||
|
builder: (context, state) => const Text('About'))
|
||||||
|
]),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,9 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
class SettingsPage extends StatefulWidget {
|
class SettingsPage extends StatefulWidget {
|
||||||
Function refreshTheme;
|
Function refreshTheme;
|
||||||
SettingsPage({super.key, required this.refreshTheme});
|
Function refreshUser;
|
||||||
|
SettingsPage(
|
||||||
|
{super.key, required this.refreshTheme, required this.refreshUser});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<StatefulWidget> createState() => _SettingsPageState();
|
State<StatefulWidget> createState() => _SettingsPageState();
|
||||||
|
@ -204,6 +206,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||||
|
|
||||||
// go back home
|
// go back home
|
||||||
router.pushReplacementNamed('home');
|
router.pushReplacementNamed('home');
|
||||||
|
widget.refreshUser();
|
||||||
},
|
},
|
||||||
after: () {
|
after: () {
|
||||||
// close popup
|
// close popup
|
||||||
|
@ -254,6 +257,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||||
|
|
||||||
// go back home
|
// go back home
|
||||||
router.pushReplacementNamed('home');
|
router.pushReplacementNamed('home');
|
||||||
|
widget.refreshUser();
|
||||||
},
|
},
|
||||||
child: Text(AppLocalizations.of(context)!.yes),
|
child: Text(AppLocalizations.of(context)!.yes),
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,7 +4,7 @@ import 'package:outbag_app/backend/request.dart';
|
||||||
import 'package:outbag_app/tools/snackbar.dart';
|
import 'package:outbag_app/tools/snackbar.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
void doNetworkRequest(ScaffoldMessengerState? sm,
|
Future<void> doNetworkRequest(ScaffoldMessengerState? sm,
|
||||||
{required Future<Response> Function() req,
|
{required Future<Response> Function() req,
|
||||||
Function(Map<String, dynamic> body)? onOK,
|
Function(Map<String, dynamic> body)? onOK,
|
||||||
bool Function()? onNetworkErr,
|
bool Function()? onNetworkErr,
|
||||||
|
|
Loading…
Reference in a new issue