Accessing currentUser data in firebase_auth plugin version 0.2.0 - dart

In my app, I have a drawer with a UserAccountsDrawerHeader, which I feed its properties by simply getting the x property from FirebaseAuth.instance.currentUser.x
In the latest firebase_auth 0.2.0 version , where currentUser() is async.
I have been trying for several hours to store the information of the currently logged user and have not yet reached the correct way to do this.
I understand that I can access them by something like the following:
Future<String> _getCurrentUserName() async {
FirebaseUser user = await FirebaseAuth.instance.currentUser();
return user.displayName;
}
...
new UserAccountsDrawerHeader(accountName: new Text(_getCurrentUserName()))
I understand that these code snippets will give type mismatch, but I am just trying to illustrate what I am trying to do.
What am I missing exactly that is preventing me from reaching a solution?
Update
class _MyTabsState extends State<MyTabs> with TickerProviderStateMixin {
TabController controller;
Pages _page;
String _currentUserName;
String _currentUserEmail;
String _currentUserPhoto;
#override
void initState() {
super.initState();
_states();
controller = new TabController(length: 5, vsync: this);
controller.addListener(_select);
_page = pages[0];
}
My method
I just coupled the auth state with my previously implemented TabBar state
_states() async{
var user = await FirebaseAuth.instance.currentUser();
var name = user.displayName;
var email = user.email;
var photoUrl = user.photoUrl;
setState(() {
this._currentUserName=name;
this._currentUserEmail=email;
this._currentUserPhoto=photoUrl;
_page = pages[controller.index];
});
}
My Drawer
drawer: new Drawer(
child: new ListView(
children: <Widget>[
new UserAccountsDrawerHeader(accountName: new Text(_currentUserName) ,
accountEmail: new Text (_currentUserEmail),
currentAccountPicture: new CircleAvatar(
backgroundImage: new NetworkImage(_currentUserPhoto),
),
Here is the exception I get from the debug console
I/flutter (14926): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (14926): The following assertion was thrown building MyTabs(dirty, state: _MyTabsState#f49aa(tickers:
I/flutter (14926): tracking 1 ticker)):
I/flutter (14926): 'package:flutter/src/widgets/text.dart': Failed assertion: line 207 pos 15: 'data != null': is not
I/flutter (14926): true.
I/flutter (14926): Either the assertion indicates an error in the framework itself, or we should provide substantially
Update 2:
This is how I modified the google sign in function from the firebase examples:
Future <FirebaseUser> _testSignInWithGoogle() async {
final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
//checking if there is a current user
var check = await FirebaseAuth.instance.currentUser();
if (check!=null){
final FirebaseUser user = check;
return user;
}
else{
final FirebaseUser user = await _auth.signInWithGoogle(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
assert(user.email != null);
assert(user.displayName != null);
assert(!user.isAnonymous);
assert(await user.getToken() != null);
return user;
}
}
Update 3:
My main function
void main() {
runApp(
new MaterialApp(
home: new SignIn(),
routes: <String, WidgetBuilder>{
"/SignUp":(BuildContext context)=> new SignUp(),
"/Login": (BuildContext context)=> new SignIn(),
"/MyTabs": (BuildContext context)=> new MyTabs()},
));
}
And then my SignIn contains a google button that when pressed:
onPressed: () { _testSignInWithGoogle(). //async returns FirebaseUser
whenComplete(()=>Navigator.of(context).pushNamed("/MyTabs")
);
}
and the Drawer from update 1 is included within MyTabs build.

There are several possibilities.
First : Use a stateful widget
Override the initState method like this :
class Test extends StatefulWidget {
#override
_TestState createState() => new _TestState();
}
class _TestState extends State<Test> {
String _currentUserName;
#override
initState() {
super.initState();
doAsyncStuff();
}
doAsyncStuff() async {
var name = await _getCurrentUserName();
setState(() {
this._currentUserName = name;
});
}
#override
Widget build(BuildContext context) {
if (_currentUserName == null)
return new Container();
return new Text(_currentUserName);
}
}
Second : Use the FutureBuilder widget
Basically, it's a wrapper for those who don't want to use a stateful widget. It does the same in the end.
But you won't be able to reuse your future somewhere else.
class Test extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new FutureBuilder(
future: _getCurrentUserName(),
builder: (context, AsyncSnapshot<int> snapshot) {
if (snapshot.hasData)
return new Text(snapshot.data.toString());
else
return new Container();
},
);
}
}
Explanation :
Your getCurrentUserName is asynchronous.
You can't just directly mix it with other synchronous functions.
Asynchronous functions are quite useful. But if you want to use them, just remember two things :
Inside another async function, you can var x = await myFuture, which will wait until myFuture finish to get it's result.
But you can't use await inside a sync function.
Instead, you can use
myFuture.then(myFunction) or myFuture.whenComplete(myFunction). myFunction will be called when the future is finished. And they both .then and .whenComplete will pass the result of your future as parameter to your myFunction.

"How to properly implement authentification" ?
You should definitely not do it this way. You'll have tons of code duplication.
The most ideal way to organise layers such as Authentification is like this :
runApp(new Configuration.fromFile("confs.json",
child: new Authentification(
child: new MaterialApp(
home: new Column(
children: <Widget>[
new Text("Hello"),
new AuthentifiedBuilder(
inRoles: [UserRole.admin],
builder: (context, user) {
return new Text(user.name);
}
),
],
),
),
),
));
And then, when you need a configuration or the current user inside a widget, you'd do this :
#override
Widget build(BuildContext context) {
var user = Authentification.of(context).user;
var host = Configuration.of(context).host;
// do stuff with host and the user
return new Container();
}
There are so many advantages about doing this, that there's no reason not to do it.
Such as "Code once, use everywhere". Or the ability to have a generic value and override it for a specific widget.
You'll realise that a lot of Flutter widgets are following this idea.
Such as Navigator, Scaffold, Theme, ...
But "How to do this ??"
It's all thanks to the BuildContext context parameter. Which provides a few helpers to do it.
For exemple, the code of Authentification.of(context) would be the following :
class Authentification extends StatefulWidget {
final Widget child;
static AuthentificationData of(BuildContext context) {
final AuthentificationData auth = context.inheritFromWidgetOfExactType(AuthentificationData);
assert(auth != null);
return auth;
}
Authentification({this.child});
#override
AuthentificationState createState() => new AuthentificationState();
}

Related

How to fix FutureBuilder open multiple times error?

these my two classes(two pages). these two classes open multiple times.
I put debug point in futurebuilder in two classes.
debug point running,
MainCategory page and got to the next page
SubCategory page and again running MainCategory page(previous page) futurebuilder and again running MainCategory page futurebuilder
navigate subcategory page to third page running subcategory page and main category page
I upload my two classes to GitHub and please let me know what the issue is.
MainCategory code: https://github.com/bhanuka96/ios_login/blob/master/MainCategory.dart
SubCategory code: https://github.com/bhanuka96/ios_login/blob/master/subCategory.dart
As stated in the documentation, you should not fetch the Future for the Futurebuilder during the widget's build event.
https://docs.flutter.io/flutter/widgets/FutureBuilder-class.html
The future must have been obtained earlier, e.g. during
State.initState, State.didUpdateConfig, or
State.didChangeDependencies. It must not be created during the
State.build or StatelessWidget.build method call when constructing the
FutureBuilder. If the future is created at the same time as the
FutureBuilder, then every time the FutureBuilder's parent is rebuilt,
the asynchronous task will be restarted.
So, try to move your call to getRegister method outside the build method and replace it with the returned Future value.
For example, below I have a class that returns a Future value which will be consumed with the help of FutureBuilder.
class MyApiHelper{
static Future<List<String>> getMyList() async {
// your implementation to make server calls
return List<String>();
}
}
Now, inside your widget, you will have something like this:
class _MyHomePageState extends State<MyHomePage> {
Future<List<String>> _myList;
#override
void initState() {
super.initState();
_myList = MyApiHelper.getMyList();
}
#override
Widget build(BuildContext context) {
return Scaffold(body: FutureBuilder(
future: _myList,
builder: (_, AsyncSnapshot<List<String>> snapLs) {
if(!snapLs.hasData) return CircularProgressIndicator();
return ListView.builder(
itemCount: snapLs.data.length,
itemBuilder: (_, index) {
//show your list item row here...
},
);
},
));
}
}
As shown above, the Future is fetched in the initState function and used inside the build method and used by FutureBuilder.
I hope this was helpful.
Thanks.
If you happen to use Provider, here's (in my opinion) a clearer alternative based on your question:
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FutureProvider<List<String>>(
create: (_) => MyApiHelper.getMyList(),
child: Consumer<List<String>>(
builder: (_, list, __) {
if (list == null) return CircularProgressIndicator();
return ListView.builder(
itemCount: list.length,
itemBuilder: (_, index) {
//show your list item row here...
},
);
};
),
);
}
}
This can also be achieved of course as a StatefulWidget as suggested by the other answer, or even with flutter_hooks as explained in Why is my Future/Async Called Multiple Times?
You can create new Widget and pass Function to
returnFuture as
() {
return YourFuture;
}
import 'dart:developer';
import 'package:flutter/material.dart';
class MyFutureBuilder<T> extends StatefulWidget {
final Future<T> Function() returnFuture;
final AsyncWidgetBuilder<T> builder;
final T initialData;
MyFutureBuilder({
this.returnFuture,
#required this.builder,
this.initialData,
Key key,
}) : super(key: key);
#override
_MyFutureBuilderState<T> createState() => _MyFutureBuilderState<T>();
}
class _MyFutureBuilderState<T> extends State<MyFutureBuilder<T>> {
bool isLoading = false;
Future<T> future;
#override
void initState() {
super.initState();
future = widget.returnFuture();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
builder: widget.builder,
initialData: widget.initialData,
future: future,
);
}
}
Example
MyFutureBuilder<List<User>>(
returnFuture: () {
return moderatorUserProvider
.getExecutorsAsModeratorByIds(val.users,
save: true);
},
builder: (cont, asyncData) {
if (asyncData.connectionState !=
ConnectionState.done) {
return Center(
child: MyCircularProgressIndicator(
color: ModeratorColor.executors.color,
),
);
}
return Column(
children: asyncData.data
.map(
(singlExecutor) =>
ChooseInfoButton(
title:
'${singlExecutor.firstName} ${singlExecutor.secondName}',
subTitle: 'Business analyst',
middleText: '4.000 NOK',
subMiddleText: 'full time',
label: 'test period',
subLabel: '1.5 month',
imageUrl:
assetsUrl + 'download.jpeg',
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) =>
ModeratorExecutorEditPage(),
),
);
},
),
)
.toList());
},
)
```

Bloc pattern does not show newly added user to sqflite on screen

I am using sqflite database to save user list.
I have user list screen, which shows list of user and it has a fab button,
on click of fab button, user is redirected to next screen where he can add new user to database.
The new user is properly inserted to the database
but when user presses back button and go backs to user list screen,
the newly added user is not visible on the screen.
I have to close the app and reopen it,then the newly added user is visible on the screen.
I am using bloc pattern and following is my code to show user list
class _UserListState extends State<UserList> {
UserBloc userBloc;
#override
void initState() {
super.initState();
userBloc = BlocProvider.of<UserBloc>(context);
userBloc.fetchUser();
}
#override
void dispose() {
userBloc?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.of(context).pushNamed("/detail");
},
child: Icon(Icons.add),
),
body: StreamBuilder(
stream: userBloc.users,
builder: (context, AsyncSnapshot<List<User>> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.data != null) {
return ListView.builder(
itemBuilder: (context, index) {
return Dismissible(
key: Key(snapshot.data[index].id.toString()),
direction: DismissDirection.endToStart,
onDismissed: (direction) {
userBloc.deleteParticularUser(snapshot.data[index]);
},
child: ListTile(
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => UserDetail(
user: snapshot.data[index],
)));
},
title: Text(snapshot.data[index].name),
subtitle:
Text("Mobile Number ${snapshot.data[index].userId}"),
trailing:
Text("User Id ${snapshot.data[index].mobileNumber}"),
),
);
},
itemCount: snapshot.data.length,
);
}
},
),
);
}
}
Following is my bloc code
class UserBloc implements BlocBase {
final _users = BehaviorSubject<List<User>>();
Observable<List<User>> get users => _users.stream;
fetchUser() async {
await userRepository.initializeDatabase();
final users = await userRepository.getUserList();
_users.sink.add(users);
}
insertUser(String name,int id,int phoneNumber) async {
userRepository.insertUser(User(id, name, phoneNumber));
fetchUser();
}
updateUser(User user) async {
userRepository.updateUser(user);
}
deleteParticularUser(User user) async {
userRepository.deleteParticularUser(user);
}
deleteAllUser() {
return userRepository.deleteAllUsers();
}
#override
void dispose() {
_users.close();
}
}
As Remi posted answer saying i should try BehaviorSubject and ReplaySubject which i tried but it does not help. I have also called fetchUser(); inside insertUser() as pointed in comments
Following is the link of the full example
https://github.com/pritsawa/sqflite_example
Follow up from the comments, it seems you don't have a single instance of your UsersBloc in those two pages. Both the HomePage and UserDetails return a BlocProvider which instantiate a UsersBloc instance. Because you have two blocs instances(which you shouldn't have) you don't update the streams properly.
The solution is to remove the BlocProvider from those two pages(HomePage and UserDetail) and wrap the MaterialApp widget in it to make the same instance available to both pages.
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocProvider(
bloc: UserBloc(),
child:MaterialApp(...
The HomePage will be:
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return UserList(),
);
}
}
Remove the BlocProvider from UserDetail as well.
In the UsersBloc then call fetchUser() inside the insertUser() method after the user insertion, to requery the database and update the users stream.
Also as Rémi Rousselet said, use one of the subjects that return previous values.
The issue is that you're using a PublishSubject.
When a new listener subscribes to a PublishSubject, it does not receive the previously sent value and will only receive the next events.
The easiest solution is to use a BehaviorSubject or a ReplaySubject instead. These two will directly call their listener with the latest values.
#override
void initState() {
super.initState();
userBloc = BlocProvider.of<UserBloc>(context);
userBloc.fetchUser();
}
The problem is that you have called the userBloc.fetchUser() function in the initState of the page.
Bloc stream emits whenever a new data is added to it and the userBloc.fetchUser() function does exactly that, it adds the userList that you fetch from the Sqflite database.
Whenever you come back to the userlist screen from add user screen, init function is NOT called. It is only called when the userlist screen is created, that is, whenever you push it to the navigation stack.
The workaround is to call userBloc.fetchUser() whenever your StreamBuilder's snapshot data is null.
...
if (!snapshot.hasData) {
userBloc.fetchUser();
return Center(
child: CircularProgressIndicator(),
);
}
...

How to prevent passing down BuildContext?

Currently I get the BuildContext from the build method in HomeScreen, and then I have to pass it down to _gridSliver then down to _storeCard.
How can I write the code so that I don't need to pass the context down?
Maybe I can create a new private StatelessWidget called _StoreCard that will have its own build method and thus its own BuildContext?
class HomeScreen extends StatelessWidget {
HomeScreen({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return StoreConnector<AppState, List<MyStore.Store>>(
converter: (Store<AppState> store) => store.state.home.stores,
builder: (BuildContext context, List<MyStore.Store> stores) =>
CustomScrollView(slivers: <Widget>[_gridSliver(stores, context)]));
}
Widget _gridSliver(stores, context) {
return SliverGrid(
delegate: SliverChildListDelegate(List<Widget>.from(stores.map(_storeCard, context))));
}
Widget _storeCard(MyStore.Store store, BuildContext context) {
return InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (_) => StoreScreen(storeId: store.id)),
);
},
child: Container(child: Text(store.name))
);
}
}
Another instance of this problem is I navigate on a child function.
#override
Widget build(BuildContext context) {
return Column(
children: [
WhiteButton(text: "Login with Facebook", onPressed: _loginWithFacebook),
WhiteButton(text: "Login with Google", onPressed: _loginWithGoogle),
])
)
}
_loginWithFacebook(context) async {
...
var user = User.fromFacebook(result.accessToken.token, json.decode(graphResponse.body));
await _login(user, context);
}
}
_loginWithGoogle(context) async {
...
GoogleSignInAccount googleUser = await _googleSignIn.signIn();
await _login(User.fromGoogle(googleUser), context);
}
_login(user, context) async {
var fetchedUser = await MeService.getUser(user);
if (fetchedUser != null) {
loginSuccess(fetchedUser);
Navigator.popUntil(context, ModalRoute.withName(MainRoutes.root));
} else {
Navigator.push(
context,
MaterialPageRoute(builder: (_) => RegisterScreen(user: user)),
);
}
}
To get a new BuildContext, you have two main solutions:
Extract part of the subtree into a new widget, typically StatelessWidget. And then use it's BuildContext from the build method
Use Builder widget, which is basically a reusable widget made to obtain a BuildContext:
Example:
#override
Widget build(BuildContext context) {
return Builder(
builder: (context) {
// do something with this new context
},
);
}
You have to use a Bloc pattern that uses an Inherited Widget, but still you'll have to pass context, but in a more straight forward way. I recommend using this app by Stephen Grider, to figure out how the whole thing works. He explains in his tutorial how to put the whole thing together but I can't link you to that because that would be advertising.
The idea is, you first create a file Bloc.dart that is going to contain your logic, then you create what is called a Provider, in a Provider.dart.
Provider.dart:
class Provider extends InheritedWidget {
final bloc = Bloc();
Provider({Key key, Widget child}) : super(key: key, child: child);
bool updateShouldNotify(_) => true;
static Bloc of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(Provider) as Provider).bloc;
}
}
In your file that contains the Material App, you wrap the material App with the provider:
Widget build(BuildContext context) {
return Provider(
child: MaterialApp(
And then you use the provider in every other class down the three of widgets.
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
final bloc = Provider.of(context); // this is where you insert the provider
return StoreConnector<AppState, List<MyStore.Store>>(
converter: (Store<AppState> store) => store.state.home.stores,
builder: (BuildContext context, List<MyStore.Store> stores) =>
CustomScrollView(slivers: <Widget>[_gridSliver(stores, context)]));
}
Widget _gridSliver(stores) {
final bloc = Provider.of(context);
return SliverGrid(
delegate: SliverChildListDelegate(List<Widget>.from(stores.map(_storeCard, context))));
}
Widget _storeCard(MyStore.Store store) {
final bloc = Provider.of(context);
return InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (_) => StoreScreen(storeId: store.id)),
);
},
child: Container(child: Text(store.name))
);
}
}
I'm a total noob with flutter and take everything with grain of salt, but this is what I would use. Hope it helps.

Flutter Future and setstate

I am trying to add a Image widget when I get the results of an API call. My code is:
class AnimalDetailsPage extends StatefulWidget {
final selection;
_AnimalDetailsPage createState() => new _AnimalDetailsPage();
AnimalDetailsPage({Key key, this.selection}) : super(key: key);
}
class _AnimalDetailsPage extends State<AnimalDetailsPage> {
Future<List> getphotos(horseId) async {
http.Response response = await http.get(
Uri.encodeFull(
"http://myhorses.com/api/getHorsePhotos?horse_id=" +
horseId));
return JSON.decode(response.body);
}
#override
Widget build(BuildContext context) {
final menu = new MyMenuBar();
List<Widget> bodyContent = [menu];
dynamic body = new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: bodyContent,
);
if (widget.selection != null) {
final horse = widget.selection;
getphotos(horse['id'].toString()).then((res) {
setState(() {
bodyContent.add(new Image.network(res[0]['image']));
});
});
}
return Scaffold(
body: body,
);
}
}
What I can't manage to understand is that the setState does not updates the view. If I move the setState out of the then statement and hard code the image src, then it works fine.
bodyContent is declared inside build()
#override
Widget build(BuildContext context) {
final menu = new MyMenuBar();
List<Widget> bodyContent = [menu];
...
bodyContent.add(new Image.network(res[0]['image']));
...
and setState() causes build to be executed again, which means the bodyContent that holds the image is discarded an a new one created.
Move List<Widget> bodyContent = [menu]; out of the build() method and make it a class-level field and you should get the desired result.

flutter: route into different pages according to different status

Trying to do a redirect depending on user status in my app (logged in or not), but it won't work as I want it to as I am not sure how to get the BuildContext inside the method.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:t2/helpers/currentuser.dart';
import 'screens/dashboard.dart';
import 'screens/login.dart';
void main() => runApp(new MyApp());
CurrentUser user = new CurrentUser();
Future checkActiveUser() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
user.usr = prefs.get('usr');
user.pwd = prefs.get('pwd');
if (user.usr.length == 0 && user.pwd.length == 0) {
user.isLoggedIn = false;
Navigator.of(x).pushNamedAndRemoveUntil('/dashboard', (Route<dynamic> route) => false);
} else {
// Send to login screen
user.isLoggedIn = false;
Navigator.of(x).pushNamedAndRemoveUntil('/login', (Route<dynamic> route) => false);
}
return user.isLoggedIn;
/*
// How to read/write to local storage
int counter = (prefs.getInt('counter') ?? 0) + 1;
print('Pressed $counter times.');
prefs.setInt('counter', counter);
*/
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or press Run > Flutter Hot Reload in IntelliJ). Notice that the
// counter didn't reset back to zero; the application is not restarted.
primarySwatch: Colors.blue,
),
home: new MyHomePage(),
routes: <String, WidgetBuilder>{
'/dashboard': (BuildContext context) => new Dashboard(),
'/login': (BuildContext context) => new Login()
});
}
}
class MyHomePage extends StatelessWidget {
var isLoggedIn = checkActiveUser();
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Demo Building'),
),
body: new Container(
child: new Center(
child: new Column(
children: <Widget>[new Text('DASHBOARD')],
),
)));
}
}
If you have suggestions for a different approach, I'm all ears! I basically want to run this check on app load and redirect accordingly.
Regards, Bob
UPDATED CODE: Tried the suggestion from Hadrien, and got a step closer. It now runs and I get contact access but, get the following error:
'Navigator operation requested with a context that does not include a Navigator. The context used to push or pop routes from the Navigator must be that of a widget that is a descendant of a Navigator widget.'
This is the updated code:
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:t2/helpers/currentuser.dart';
import 'screens/dashboard.dart';
import 'screens/login.dart';
void main() => runApp(new MyApp());
CurrentUser user = new CurrentUser();
checkActiveUser(BuildContext context) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
try {
user.usr = prefs.get('usr');
user.pwd = prefs.get('pwd');
if (user.usr.length == 0 && user.usr.length == 0) {
user.isLoggedIn = false;
Navigator
.of(context)
.pushNamedAndRemoveUntil('/dashboard', (Route<dynamic> route) => false);
} else {
throw new Exception('No user data found');
}
} catch (e) {
// Send to login screen
user.isLoggedIn = false;
Navigator
.of(context)
.pushNamedAndRemoveUntil('/login', (Route<dynamic> route) => false);
}
/*
// How to read/write to local storage
int counter = (prefs.getInt('counter') ?? 0) + 1;
print('Pressed $counter times.');
prefs.setInt('counter', counter);
*/
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
void initState() {
super.initState();
checkActiveUser(context);
}
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Welcome to Flutter',
home: new Scaffold(
appBar: new AppBar(
title: new Text('CADSYS'),
),
body: new Center(
child: new Text('Loading...'),
),
),
routes: <String, WidgetBuilder> {
'/dashboard': (BuildContext context) => new Dashboard(),
'/login': (BuildContext context) => new Login()
},
);
}
}
I would probably do it a little differently... instead of pushing a route inside a function, set the login state inside your StatefulWidget and then set the body based on that.
body: user.isLoggedIn ? new Dashboard() : new Login(),
then elsewhere in your code you'll need to check the active user and do setState((){ user.isLoggedIn = true; }); (or false).
When the login state changes, your view will automatically update with the new Widget.
The Navigator come with the MaterialApp widget, so you can only access it from the routes you defined in it and from their child. (Login, Dashboard, MyHomePage).
if you transform your MyHomePage widget into a stateful widget. you will be able to call your checkActiveUser() function inside initState
initState() async {
super.initState();
checkActiveUser(context);
}

Resources