繁体   English   中英

如何在 Flutter 中管理 Firebase 身份验证 state?

[英]How to manage Firebase Authentication state in Flutter?

我有一个WelcomeScreen ,其中包含注册和登录,以及我想在用户登录后重定向的HomeScreen 。为了管理身份验证数据,我创建了一个auth.dartstatic属性和方法,以便我可以在所有页面上访问它们相同的数据。

import 'package:firebase_auth/firebase_auth.dart';

class Auth {

  static final auth = FirebaseAuth.instance;

  static Future<void> logout() async {
    await auth.signOut();
  }

  static Future<void> loginUser(String userEmail, String userPassword) async {
    await auth.signInWithEmailAndPassword(email: userEmail, password: userPassword);
  }

  static Future<FirebaseUser> getCurrentUser() async {
    return await auth.currentUser();
  }
}

main.dart文件中,我使用StreamBuilder根据更改的身份验证数据更改当前屏幕。 我从这个答案中得到了这个StreamBuilder代码。

home: StreamBuilder<FirebaseUser>(
  stream: Auth.auth.onAuthStateChanged,
  builder: (context, snapshot) {
    if (snapshot.hasData) {
      return HomeScreen();
    } else {
      return WelcomeScreen();
    }
  },
),

在我的登录屏幕中,我使用以下代码触发登录:

Future<void> login() async {
    ...

    try {
      await Auth.loginUser(userEmail, userPassword);
      var user =  await Auth.getCurrentUser();
      print(user.displayName); // This works
    } catch (error) {
      print(error.message);
    }
  }

我不知道我使用的 static 方法是否是处理 Firebase 身份验证的正确方法,但它似乎有效。 登录后,我可以显示登录用户的名称,但StreamBuilder中的main.dart没有反映更新的身份验证数据,即没有更改页面。

是因为 static 方法还是StreamBuilder的实现有问题?

Null安全码(使用provider

在此处输入图像描述


完整代码:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();

  runApp(
    ChangeNotifierProvider<AuthModel>(
      create: (_) => AuthModel(),
      child: MaterialApp(
        home: Consumer<AuthModel>(
          builder: (_, auth, __) => auth.isSignedIn ? HomePage() : WelcomePage(),
        ),
      ),
    ),
  );
}

class WelcomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('WelcomePage')),
      body: Center(
        child: ElevatedButton(
          onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (_) => LoginPage())),
          child: Text('Go to Login Page'),
        ),
      ),
    );
  }
}

class LoginPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Login Page')),
      body: Center(
        child: ElevatedButton(
          onPressed: () async {
            final model = context.read<AuthModel>();
            await model.signIn(email: 'test@test.com', password: 'test1234');
            Navigator.pop(context);
          },
          child: Text('Login'),
        ),
      ),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('HomePage')),
      body: Center(
        child: FloatingActionButton.extended(
          onPressed: () async {
            final model = context.read<AuthModel>();
            await model.signOut();
          },
          label: Text('Log out'),
        ),
      ),
    );
  }
}

class AuthModel extends ChangeNotifier {
  final FirebaseAuth _auth = FirebaseAuth.instance;

  bool get isSignedIn => _auth.currentUser != null;

  Future<void> signIn({required String email, required String password}) async {
    await _auth.signInWithEmailAndPassword(email: email, password: password);
    notifyListeners();
  }

  Future<void> signOut() async {
    await _auth.signOut();
    notifyListeners();
  }
}

在我看来,在 flutter 中管理 firebase 身份验证的最佳方法是使用提供程序 package。 您的 Auth class 缺少一件重要的事情,即 onAuthStateChnaged 方法。 您可以创建 stream 作为 Auth class 内的 onAuthStateChanged 的 getter。 Auth class 将扩展 ChangeNotifier class。 ChangeNotifier class 是 flutter api 的一部分。

class Auth extends ChangeNotifier {

    final FirebaseAuth _auth = FirebaseAuth.instance;

    // create a getter stream
    Stream<FirebaseUser> get onAuthStateChanged => _auth.onAuthStateChanged;

    //Sign in async functions here ..

}

用 ChangeNotifierProvider(提供程序包的一部分)包装您的 MaterialApp,并在 create 方法中返回 Auth class 的实例,如下所示:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => Auth(),
      child: new MaterialApp(
      home: Landing(),
      ),
    );
  }
}

现在将登录页面创建为无状态小部件。 使用 Consumer 或 Provider.of(context) 和 stream 构建器来监听 auth 更改并根据需要呈现登录页面或主页。

class Landing extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    Auth auth = Provider.of<Auth>(context);
    return StreamBuilder<FirebaseUser>(
      stream: auth.onAuthStateChanged,
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.active) {
          FirebaseUser user = snapshot.data;
          if (user == null) {
            return LogIn();
          }
          return Home();
        } else {
          return Scaffold(
            body: Center(
              child: CircularProgressIndicator(),
            ),
          );
        }
      },
    );
  }
}

您可以从官方 flutter 文档中阅读有关 state 管理与提供程序的更多信息。 按照这个链接: https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple

截屏:

在此处输入图像描述


我不确定你是怎么做的,所以我添加了一个最小的工作代码,我没有对你的Auth class 进行任何更改。 虽然使用Provider是个好主意,但您也可以使用static方法完成任务。

编辑代码:

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MaterialApp(home: MyApp()));
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StreamBuilder<FirebaseUser>(
      stream: Auth.auth.onAuthStateChanged,
      builder: (context, snapshot) {
        if (snapshot.hasData) return HomeScreen();
         else return WelcomeScreen();
      },
    );
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Home Screen')),
      floatingActionButton: FloatingActionButton.extended(
        label: Text('Sign out'),
        onPressed: Auth.logout,
      ),
    );
  }
}

class WelcomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Welcome Screen')),
      body: Center(
        child: RaisedButton(
          onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (_) => LoginPage())),
          child: Text('Go to Login Page'),
        ),
      ),
    );
  }
}

class LoginPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Login Page')),
      body: Center(
        child: RaisedButton(
          onPressed: () async {
            await Auth.loginUser('test@test.com', 'test1234');
            await Navigator.pushAndRemoveUntil(context, MaterialPageRoute(builder: (_) => MyApp()), (_) => false);
          },
          child: Text('Login'),
        ),
      ),
    );
  }
}

我制作了一个视频 ( https://youtu.be/iqy7xareuAI ) 讨论这个赏金并带您完成实现所需应用程序的步骤。 它所需要的只是一个简单的StreamBuilder和一个FutureBuilder

更复杂的工具,如providersingleton pattern (您试图通过 static 类实现的目标)可以应用于更复杂的应用程序,但这里不需要。

  1. 我们将 WelcomeScreen 视为在 LoginSignupScreen 和 HomeScreen 之间做出决定的屏幕
  2. 我们将 StreamBuilder 用于 WelcomeScreen
  3. 我们将 FutureBuilder 用于 HomeScreen。

这是 WelcomeScreen 的代码:

import 'package:ctfultterfireexperiments/src/screens/home_screen.dart';
import 'package:ctfultterfireexperiments/src/screens/login_signup_screen.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';

class WelcomeScreen extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<FirebaseUser>(
      stream: FirebaseAuth.instance.onAuthStateChanged,
      builder: (BuildContext _, AsyncSnapshot<FirebaseUser> snapshot) {
        //if the snapshot is null, or not has data it is signed out
        if(! snapshot.hasData) return LoginSignupScreen();
        // if the snapshot is having data it is signed in, show the homescreen
        return HomeScreen();
      },
    );
  }
}

这是 HomeScreen.dart 的代码

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

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Container(
          child: Center(
              child: FutureBuilder(
            builder: (BuildContext context, AsyncSnapshot<FirebaseUser> snapshot) {
              if(!snapshot.hasData) return LinearProgressIndicator();
              return Text("Home Screen: ${snapshot.data.displayName}");
            },
            future: FirebaseAuth.instance.currentUser(),
          )),
        ),
        Spacer(),
        RaisedButton(onPressed: () {FirebaseAuth.instance.signOut();})
      ],
    );
  }
}

这是 LoginSignupScreen.dart 的代码:

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';

class LoginSignupScreen extends StatelessWidget {

  login() async{
    final GoogleSignIn _googleSignIn = GoogleSignIn();
    final _auth = FirebaseAuth.instance;
    final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
    final GoogleSignInAuthentication googleAuth = await googleUser.authentication;

    final AuthCredential credential = GoogleAuthProvider.getCredential(
      accessToken: googleAuth.accessToken,
      idToken: googleAuth.idToken,
    );

    final FirebaseUser user = (await _auth.signInWithCredential(credential)).user;
    print("signed in " + user.displayName);
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Spacer(flex: 1,),
        Text("Login/Signup Screen"),
        Spacer(flex: 2,),
        RaisedButton(onPressed: login)
      ],
    );
  }
}

这将作为一个最小的工作示例。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM