36

I want to keep the user logged in after the user successfully logsin in flutter. I am using a REST API to retrieve the user name and password of the user. But I want to save those details so that the user can stay logged in. My current situation is i can successfully log the user in but when i restart the app i have to login again so i need to save the details of the user in a shared preference so that the user can stay logged for the entire session until logout.But i am unable to do that so please help me with it. Thanks in advance

This is the code i have for my login page. I have removed the UI contents which should be inside the listview as those are not that relevant.

Login.dart

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:restaurant_app/globalVar.dart';
import 'package:restaurant_app/homescreen.dart';
import 'package:restaurant_app/models/auth.dart';
import 'package:restaurant_app/signup.dart';
import 'package:http/http.dart' as http;
import 'package:restaurant_app/utils/authutils.dart';
import 'package:shared_preferences/shared_preferences.dart';

class SignIn extends StatefulWidget {

    SignIn({ Key key, this.post }): super(key: key);

@override
_SignInState createState() => _SignInState();
    }

class _SignInState extends State<SignIn> with SingleTickerProviderStateMixin 
    {
    TabController controller;
    TextEditingController _email = new TextEditingController();
    TextEditingController _password = new TextEditingController();
    bool loading;

    final GlobalKey < ScaffoldState > _scaffoldKey = new GlobalKey<ScaffoldState>
        ();

    @override
    void initState() {
        // TODO: implement initState
        super.initState();
        _fetchSessionAndNavigate();
        controller = new TabController(length: 2, vsync: this);
        loading = false;
    }

    @override
    void dispose() {
        // TODO: implement dispose
        super.dispose();
        controller.dispose();
        setState(() {
            loading = false;
        });
        _email.dispose();
        _password.dispose();
    }

    final GlobalKey < FormState > _formKey = GlobalKey<FormState>();
    bool _autoValidate = false;

    _login(username, password) async {
        setState(() {
            loading = true;
        });

        var body = json.encode({
            "username": username,
            "password": password,
        });

        Map < String, String > headers = {
            'Content-type': 'application/json',
                'Accept': 'application/json',
      };

        await http
            .post("${GlobalVar.Ip}/wp-json/jwt-auth/v1/token",
                body: body, headers: headers)
            .then((response) {
                var body = json.decode(response.body);
                //var response1;

                if (response.statusCode == 200) {
                    // TODO: you need to store body['token'] to use in some authentication
                    loading = false;
                    Navigator.pushReplacement(context,
                        MaterialPageRoute(builder: (BuildContext ctx) => HomePage()));
    } else {
        // TODO: alert message
        final snackBar = SnackBar(
            content: Text(body['message'].toString().trim()),
        );
        _scaffoldKey.currentState.showSnackBar(snackBar);
    }
    setState(() {
        loading = false;
    });
});
      }

@override
Widget build(BuildContext context) {
    return Scaffold(
        key: _scaffoldKey,
        resizeToAvoidBottomPadding: false,
        body: Container(
            decoration: BoxDecoration(
                image: DecorationImage(
                    image: AssetImage('images/art.png'),
                    fit: BoxFit.fill,
                    colorFilter: ColorFilter.mode(
                        Colors.white12.withOpacity(0.2), BlendMode.dstATop),
                ),
            ),
            child: ListView();
}
LIGHT
  • 5,604
  • 10
  • 35
  • 78
Bishal Das
  • 789
  • 2
  • 12
  • 15
  • 1
    That's a lot of code. I suggest you (1) reduce the code to the minimal relevant parts, and (2) explain what you're trying to do and what the problem is. See https://stackoverflow.com/help/how-to-ask – Edman Jan 26 '19 at 11:00
  • Thanks Edman...I have made the changes – Bishal Das Jan 26 '19 at 16:11

5 Answers5

94

You can navigate to the Login page if the user details are saved in the storage else to the Home page with the below code

  Future<void> main() async {
      WidgetsFlutterBinding.ensureInitialized();
      SharedPreferences prefs = await SharedPreferences.getInstance();
      var email = prefs.getString('email');
      print(email);
      runApp(MaterialApp(home: email == null ? Login() : Home()));
    }

Save the required user details after the successful login

class Login extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: RaisedButton(
          onPressed: () async {
            //after the login REST api call && response code ==200
            SharedPreferences prefs = await SharedPreferences.getInstance();
            prefs.setString('email', 'useremail@gmail.com');
            Navigator.pushReplacement(context,
                MaterialPageRoute(builder: (BuildContext ctx) => Home()));
          },
          child: Text('Login'),
        ),
      ),
    );
  }
}

clear the details on logout

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home'),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () async {
            SharedPreferences prefs = await SharedPreferences.getInstance();
            prefs.remove('email');
            Navigator.pushReplacement(context,
                MaterialPageRoute(builder: (BuildContext ctx) => Login()));
          },
          child: Text('Logout'),
        ),
      ),
    );
  }
}

Hope it helps!

Shyju M
  • 9,387
  • 4
  • 43
  • 48
  • I am getting confused..Can u please write something based on my code as i am new to rest api and shared preference so i am finding difficult to understand the code – Bishal Das Jan 27 '19 at 18:33
  • 3
    Here is your edited code https://gist.github.com/shyjuzz/054f783e8b7c3a2e852af326f0db375f – Shyju M Jan 28 '19 at 05:39
  • This is not working for me. SharedPreferences prefs = await SharedPreferences.getInstance(); is throwing an error for me. Completly confusing.... – NullPointer Aug 23 '19 at 12:27
  • @NullPointer What is the error you are getting ? Have you added the package in the pubspec file ? – Shyju M Aug 26 '19 at 06:42
  • make sure WidgetFlutterBinding.ensureInitialized() is the first line of main() https://stackoverflow.com/a/57775690/11535769 – Wilmer Feb 04 '20 at 14:09
  • I am getting: The getter 'email' was called on null. Receiver: null Tried calling: email – chazefate Mar 16 '20 at 11:15
  • Unless you also store password somewhere, how is this answering the question? – Ajay Gautam May 06 '20 at 03:31
  • I am not sure if I undestand why it is necessary to call WidgetFlutterBinding.ensureInitialized(), since none of the calls that follow actually require the binding to be initialized, or am I mistaken? – Qiong Wu May 22 '20 at 18:18
  • Please help me, I'm facing an issue with FlutterSession: https://stackoverflow.com/questions/66311232/flutter-session-stores-data-but-cant-make-decisions-based-off-it – CodeSadhu Feb 22 '21 at 08:39
6

Make sure WidgetFlutterBinding.ensureInitialized() is the first line of main()

import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter/material.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  SharedPreferences prefs = await SharedPreferences.getInstance();
  bool login = prefs.getBool("login");
  print("login:" + login.toString());
  runApp(MaterialApp(home: login == null ? LoginPage(title: 'My App') : HomePage()));
}

class LoginPage extends StatelessWidget { ...
Wilmer
  • 482
  • 8
  • 11
  • by impementing this example im getting the following error \n The following ScheduleParseException was thrown building Builder: FormatException: Unable to parse: */0 – Padmaja Rani Jul 05 '22 at 05:29
4

The above answers using SharedPreferences works (make sure you have WidgetsFlutterBinding.ensureInitiazed(); as your first line of main), but it will give you a null on re-start, ie, if you remove the app from recent and re-open it again, it will not re-direct you to the Home or Profile Page. I solved this issue by giving write external storage permission to your app because the shared preferences need to write the data somewhere in your device or emulator.

Just add the write and read external storage permissions in your Android Manifest file and you can use permission_handler plugin for flutter from pub.dev to get the required permissions from user at runtime when the app is opened for the first time and then Shared Preferences won't give you null.

Amir
  • 1,328
  • 2
  • 13
  • 27
Vaibhav Kamani
  • 357
  • 3
  • 2
1

Use user sessions instead. Check out Consession. The package adds user session support in Flutter and is easy to use.

// Store value to session
await Consession().set("token", myJWTToken);

// Retrieve item from session
dynamic token = await Consession().get("token");
Jhourlad Estrella
  • 3,545
  • 4
  • 37
  • 66
1

Concession is now deprecated in favour of flutter_session. We can now use flutter_session to manage user sessions seamlessly.

//Write values to the session: 
await FlutterSession().set("token", myJWTToken);

//Read values from the session: 
dynamic token = await FlutterSession().get("token");
Faizyy
  • 353
  • 2
  • 15
  • Hey! I'm having trouble with FlutterSession. Could you help me out? https://stackoverflow.com/questions/66311232/flutter-session-stores-data-but-cant-make-decisions-based-off-it – CodeSadhu Feb 22 '21 at 08:35
  • Sorry @CodeSadhu I am not an expert at flutter. I was trying something very specific and came across this problem, which I don't even seem to remember now :P – Faizyy Feb 22 '21 at 14:34