6
votes

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()
    },
    );
  }
}
2
Basically, I can't get access to BuildiContext in the active checkActiveUser method. - Robert Benedetto
Not sure why you changed the login screen part to throw an exception in your updated code, but it's not a good idea to throw an exception to handle something like that; just put that code in the else block like it was before. - Herohtar
Did it as I was getting an error. Length was throwing an error as no data had been provided (no data saved in persistent state, thus null), so a try catch seems to be a good thing to add. Would't really do error handling using if else. - Robert Benedetto
If user or user.usr has the potential of being null it's better to actually check for that case first instead of leaving it to throw an exception. - Herohtar
Will consider that. Have any feedback on the actual problem? Would be greatly appreciated. - Robert Benedetto

2 Answers

7
votes

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.

1
votes

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);
}