I've got an app with working user registration, and using a user provider am able to set the user during registration and retrieve it on the following screen without issue. What I'm having trouble with is trying to properly implement the ability to set the stored currently logged in user on app launch and proceed to the dashboard rather than going through the registration process without throwing the below assertion.
I can get the user from the SharedPreferences
no problem however I'm not sure how to properly set this user in my provider so that it can be accessed when the dashboard screen loads. Below is my main()
function and MyApp class.
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => AuthProvider()),
ChangeNotifierProvider(create: (context) => UserProvider()),
],
child: MyApp(),
));
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
Future<User> getUserData() => UserPreferences().getUser();
return MaterialApp(
title: 'My App',
theme: ThemeData(
primarySwatch: Colors.red,
accentColor: Colors.black,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: FutureBuilder(
future: getUserData().then((value) => {
Provider.of<UserProvider>(context, listen: false)
.setUser(value)
}),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return CircularProgressIndicator();
default:
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
else if (snapshot.data.token == null)
return Login();
else if (snapshot.data.token != null) {
return Dashboard();
} else {
UserPreferences().removeUser();
return Login();
}
}
}),
routes: {
'/dashboard': (context) => Dashboard(),
'/login': (context) => Login(),
'/register': (context) => Register(),
'/username': (context) => Username(),
});
}
}
In the FutureBuilder I tried chaining a then on the getUserData()
call to set the user however this of course returns the following assertion:
════════ Exception caught by foundation library ════════════════════════════════
The following assertion was thrown while dispatching notifications for UserProvider:
setState() or markNeedsBuild() called during build.
This _InheritedProviderScope<UserProvider> widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
From what I've read this is because there is a call to notifyListeners()
in the user provider which will call markNeedsBuild()
. That said, I've tried moving things around and several suggestions online and can't figure out what the best practice and best way to implement this is.
User Provider:
class UserProvider with ChangeNotifier {
User _user = new User();
User get user => _user;
void setUser(User user) {
_user = user;
notifyListeners();
}
}
How can I properly set the user when the app is loading up if someone is already logged in without running into the above assertion? Thanks!
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…