Flutter:在应用程序关闭并重新打开后让用户保持登录状态

Flutter: Keep user logged in after app closes and reopen

我是初学者,正在寻求帮助。我试图找到答案,但遗憾的是找不到我正在寻找的答案。我正在构建一个应用程序,用户可以在未登录时看到主页和应用程序的其他部分。这部分我正在工作,但只有当我关闭应用程序并重新启动它时,我才需要重新登录。这当然不理想。我试图寻找 SharedPreferences 但我能找到的所有示例都带有整个应用程序的登录功能,我无法弄清楚如何让它在我的应用程序中运行。下面是我到目前为止的代码。感谢所有帮助,并提前感谢大家!

(抱歉发布了这么多代码,但它可能会有所帮助!)

Main.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:reboo1/services/firebaseauthservice.dart';
import 'package:reboo1/services/users.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return StreamProvider<User>.value(
      value: AuthService().user,
           child: MaterialApp(
            title: 'Rèboo',
            theme: new ThemeData(
              primarySwatch: Colors.blue,
            ),
              home:HomePage(),
          ),
        );
      }
    } 

HomePage.dart

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:reboo1/authenticate/signin.dart';
import 'package:reboo1/authenticate/signout.dart';
import 'package:reboo1/models/drawerlogin.dart';
import 'package:reboo1/models/drawerlogout.dart';
import 'package:reboo1/services/firebaseauthservice.dart';
import 'package:reboo1/services/users.dart';

class DrawerItem {
  String title;
  IconData icon;
  DrawerItem(this.title, this.icon);
}

class HomePage extends StatefulWidget {
  final drawerItems = [
    new DrawerItem("Rèboo", Icons.home),
    new DrawerItem("Inbox", Icons.mail_outline),
    new DrawerItem("Profile", Icons.person_outline),
    new DrawerItem("Reservations", Icons.event_note),
    new DrawerItem("Favorites", Icons.favorite_border),
    new DrawerItem("Vouchers", Icons.card_giftcard),
    new DrawerItem("Invite Friends", Icons.person_add),
    new DrawerItem("Settings", Icons.settings),
    new DrawerItem("FAQ", Icons.help_outline),
    new DrawerItem("Terms & Conditions", Icons.assignment),
    new DrawerItem("Sign out", Icons.exit_to_app),
  ];

  @override
  State<StatefulWidget> createState() {
    return new HomePageState();
  }
}

class HomePageState extends State<HomePage> {

  final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
  int _selectedDrawerIndex = 0;
  var _setIcon = Icons.person;
  var drawer;
  String uid;

  _getDrawerItemWidget(int pos) {
    switch (pos) {
      case 0:
        return new Reboo();
      case 1:
        return new Inbox();
      case 2:
        return new Profile();
      case 3:
        return new Reservations();
      case 4:
        return new Favorites();
      case 5:
        return new Vouchers();
      case 6:
        return new InviteFriends();
      case 7:
        return new Settings();
      case 8:
        return new Faq();
      case 9:
        return new TermsAndConditions();
      case 10:
        return new SignOut();
      default:
        return new Text("Error");
    }
  }


  _onSelectItem(int index) {
    setState(() => _selectedDrawerIndex = index);
    if (_selectedDrawerIndex == 0) {
      setState(() => _setIcon = Icons.person);
    } else {
      setState(() => _setIcon = Icons.arrow_back);
    }
    Navigator.of(context).pop(); // close the drawer
  }

  @override
  Widget build(BuildContext context) {
    var drawerOptions = <Widget>[];
    for (var i = 0; i < widget.drawerItems.length; i++) {
      var d = widget.drawerItems[i];
      drawerOptions.add(new ListTile(
        leading: new Icon(
          d.icon,
          color: Color(0xFF008577),
        ),
        title: new Text(d.title),
        selected: i == _selectedDrawerIndex,
        onTap: () => _onSelectItem(i),
      ));
    }
    final auth = Provider.of<User>(context, listen: false);
    if (auth != null) {
      drawer = DrawerLogin();
    } else {
      drawer = DrawerLogout();
    }

    return Scaffold(
      key: _scaffoldKey,
      appBar: AppBar(
        leading: Builder(
          builder: (BuildContext context) {
            return IconButton(
              icon: new Icon(_setIcon),
              onPressed: () {
                if (_selectedDrawerIndex == 0) {
                  Scaffold.of(context).openDrawer();
                } else {
                  setState(() => _selectedDrawerIndex = 0);
                  setState(() => _setIcon = Icons.person);
                }
              },
              tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
            );
          },
        ),
        centerTitle: true,
        title: new Text(widget.drawerItems[_selectedDrawerIndex].title),
        backgroundColor: Color(0xFF008577),
        elevation: 0.0,
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.menu, color: Colors.white),
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => Company()),
              );
            },
          )
        ],
      ),
      drawer: new Drawer(
        child: SingleChildScrollView(
          child: new Column(
            children: <Widget>[drawer, new Column(children: drawerOptions)],
          ),
        ),
      ),
      body: _getDrawerItemWidget(_selectedDrawerIndex),
    );
  }
}

FirebaseAuthService.dart

import 'package:firebase_auth/firebase_auth.dart';
import 'package:reboo1/services/users.dart';
import 'package:reboo1/services/database.dart';

class AuthService {

  final FirebaseAuth _auth = FirebaseAuth.instance;

  // create user obj based on FirebaseUser
  User _userFromFirebaseUser(FirebaseUser user){
    return user != null ? User(uid: user.uid) : null;
  }


  // auth change user stream
  Stream<User> get user {
    return _auth.onAuthStateChanged
      .map(_userFromFirebaseUser);
  }


  // sign with email & password

Future signInWithEmailAndPassword(String email, String password) async {
  try {
    AuthResult result = await _auth.signInWithEmailAndPassword(email: email, password: password); 
    FirebaseUser user = result.user;
    return _userFromFirebaseUser(user);
  }catch(e){
    print(e.toString());
    return null;
  }
}

  // register with email & password

Future registerForm(String email, String password, String birthday, String address, String fullName, String phone) async {
  try {
    AuthResult result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
    FirebaseUser user = result.user;

    // creat a new document in the database for the user with the uid
    await DatabaseService(uid: user.uid).updateUserData(email, birthday, address, fullName, phone);

    return _userFromFirebaseUser(user);
  }catch(e){
    print(e.toString());
    return null;
  }
}

  Stream<String> get onAuthStateChanged =>
      _auth.onAuthStateChanged.map(
            (FirebaseUser user) => user?.uid,
      );
  // GET UID
  Future<String> getCurrentUID() async {
    return (await _auth.currentUser()).uid;
  }
  Future getCurrentUser() async {
    return await _auth.currentUser();
  }

  //sign out
  Future signOut() async{
    try{
      return await _auth.signOut();
    }catch(e){
      print(e.toString());
      return null;
    }
  }

}

SignIn.dart

import 'package:flutter/material.dart';
import 'package:reboo1/authenticate/register.dart';
import 'package:reboo1/home/homepage.dart';
import 'package:reboo1/services/constants.dart';
import 'package:reboo1/services/loading.dart';
import 'package:reboo1/services/firebaseauthservice.dart';
import 'package:shared_preferences/shared_preferences.dart';

class SignIn extends StatefulWidget {
  final Function toggleView;
  SignIn({this.toggleView});

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

class _SignInState extends State<SignIn> {
  final AuthService _auth = AuthService();
  final _formKey = GlobalKey<FormState>(); //global key
  bool loading = false;

  // text field state
  String email = '';
  String password = '';
  String error = '';

  @override
  Widget build(BuildContext context) {
    return loading
        ? Loading()
        : Scaffold(
            backgroundColor: Colors.brown[50],
            appBar: AppBar(
              backgroundColor: Color(0xFF008577),
              elevation: 0.0,
              title: Text('Sign in to Rèboo'),
              actions: <Widget>[
                FlatButton.icon(
                  textColor: Colors.white,
                  icon: Icon(Icons.person),
                  label: Text('Register'),
                  onPressed: () {
                    Navigator.push(
                      context,
                      MaterialPageRoute(builder: (context) => Register()),
                    );
                  },
                )
              ],
            ),
            body: Container(
              padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 50.0),
              child: Form(
                key: _formKey, //global key
                child: Column(children: <Widget>[
                  SizedBox(height: 20.0),
                  TextFormField(
                      decoration: textInputDecoration.copyWith(
                          hintText: 'Email address'),
                      validator: (val) =>
                          val.isEmpty ? 'Enter an email address' : null,
                      onChanged: (val) {
                        setState(() => email = val);
                      }),
                  SizedBox(height: 20.0),
                  TextFormField(
                    decoration:
                        textInputDecoration.copyWith(hintText: 'Password'),
                    validator: (val) =>
                        val.length < 8 ? 'Enter valid password' : null,
                    obscureText: true,
                    onChanged: (val) {
                      setState(() => password = val);
                    },
                  ),
                  SizedBox(height: 20.0),
                  RaisedButton(
                    color: Colors.purple[400],
                    child:
                        Text('Sign in', style: TextStyle(color: Colors.white)),
                    onPressed: () async {
                      if (_formKey.currentState.validate()) {
                        setState(() => loading = true);
                        dynamic result = await _auth.signInWithEmailAndPassword(
                            email, password);
                        if (result == null) {
                          setState(() {
                            error =
                                'Could not sign in with those credentials. Please register an account.';
                            setState(() => loading = false);
                          });
                        } else {

                          setState(() => loading = false);
                          Navigator.push(
                            context,
                            MaterialPageRoute(builder: (context) => HomePage()),
                          );
                        }
                      }
                    },
                  ),
                  SizedBox(height: 12.0),
                  Text(
                    error,
                    style: TextStyle(color: Colors.red, fontSize: 14.0),
                  )
                ]),
              ),
            ),
          );
  }
}

您可以将某种数据保存到共享首选项中,然后当用户打开应用程序时,您可以从共享首选项中检索该数据以确定用户是否应该已经登录。例如,该数据可以是用户不记名令牌和到​​期日期。这可能看起来像这样:

将数据保存到共享首选项:

Future storeTokenPropertiesToPreferences(
  TokenProperties accessTokenProperties, refreshTokenProperties) async {
final preferences = await SharedPreferences.getInstance();
preferences.setString(
  _preferencesLocation,
  json.encode(
    {
      'accessToken': accessTokenProperties?.value,
      'refreshToken': refreshTokenProperties?.value,
      'accessTokenExpiresAt':
          accessTokenProperties?.expiresAt?.toIso8601String(),
      'refreshTokenExpiresAt':
          refreshTokenProperties?.expiresAt?.toIso8601String(),
    },
  ),
);

检索数据:

  Future<TokenProperties> _tokenPropertiesFromPreferences(
  String tokenName) async {
final _userData = await _getPreferences();

try {
  final value = _userData[tokenName];
  final expiresAt = DateTime.parse(_userData['${tokenName}ExpiresAt']);
  final lifeTime = expiresAt.difference(DateTime.now()).inSeconds;

  return (value == null || expiresAt == null)
      ? null
      : TokenProperties(value, lifeTime);
} catch (e) {
  await _loggingService.log(LoggingMethod.ERROR,
      'Could not get token properties from preferences for token $tokenName. Got exception: $e');
  return null;
}


  Future<Map<String, Object>> _getPreferences() async {
final preferences = await SharedPreferences.getInstance();
if (!preferences.containsKey(_preferencesLocation)) return null;

return json.decode(preferences.getString(_preferencesLocation))
    as Map<String, Object>;
}

您显然可以将对象属性更改为您需要的任何内容,这只是一个示例。

我不认为你需要搞乱本地存储来保存用户的 detail.As Firebase 本身将用户的详细信息存储在本地 storage.You 只需要调用 FirebaseAuth 的 .currentUser() 方法 class 在您的应用程序启动时对象。如果当前用户为空,则加载登录页面,否则加载您的主页。就这样;

Future loadUser() async {
    FirebaseUser _tempUser = await _auth.currentUser();
    if (_tempUser != null)
    //  goto MainPage(mainUser:_tempUser);
    } else
    //  goto LoginPage;
  }

提供了一个虚拟实现,但这是解决这个问题的合法方法。如果您想在 google 上搜索,请搜索关键字 firebase onAuthChanged,然后阅读相关信息。无需本地存储,以最简单、最安全的方式处理您的需求。

StreamBuilder<FirebaseUser>(
          stream: FirebaseAuth.instance.onAuthStateChanged,
          builder: (BuildContext context, snapshot) {

            if (snapshot.connectionState == ConnectionState.waiting) {
              return print("getting data");
            } else if (snapshot.connectionState == ConnectionState.active &&
                !snapshot.hasData) {
              return print("no user found");
            } else if (snapshot.connectionState == ConnectionState.active &&
                snapshot.hasData)
              {
                return print(snapshot.data.currentuser);
              }
            else
              return null;
          },

感谢@UTKARSH Sharma,我发现用户没有注销,但问题出在我自己的代码中。我发现以下代码帮助我解决了问题:

  @override
  void initState() {
    super.initState();
    var auth = FirebaseAuth.instance;
    auth.onAuthStateChanged.listen((user) {
      if (user != null) {
        print("user is logged in");
        //navigate to home page using Navigator Widget
      } else {
        print("user is not logged in");
         //navigate to sign in page using Navigator Widget
      }
    });
  }

谢谢大家的帮助!