Flutter简化认证流程插件easy_auth的使用

发布于 1周前 作者 zlyuanteng 来自 Flutter

Flutter简化认证流程插件easy_auth的使用

EasyAuth

very good analysis very good analysis License: MIT


Widgets和classes使得在任何Flutter应用中添加认证变得简单。它基于package:bloc架构构建,并且完全不依赖于任何特定的认证框架,但它为常用的框架如package:firebase_auth提供了一些即插即用的机制。

⚠️ 如果你喜欢这个仓库,请考虑帮助维护/改进/推广它!


使用方法 #

让我们看看如何将基本的Firebase Auth状态集成到你的应用中。其他例子可以查看examples文件夹。

首先,我们创建一个基本的MaterialApp(或者你可能使用的任何其他应用)并初始化默认的Firebase应用:

// -> main.dart
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  GestureBinding.instance!.resamplingEnabled = true;
  await Firebase.initializeApp();
  runApp(
    MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const _MyApp(),
    ),
  );
}

然后我们创建我们的<_MyApp>组件,该组件扩展了<AuthenticationBasedApp>:

// -> main.dart
class _MyApp extends AuthenticationBasedApp<UserData> {
  const _MyApp({Key? key}) : super(key: key);

  @override
  BasicFirebaseAuth<UserData> get repository => BasicFirebaseAuth<UserData>(
      transformer: (user) => UserData('1-1-1970', user.metadata.creationTime!, user.email));

  @override
  Widget buildState(BuildContext context, AuthStatus status, UserData? user) {
    switch (status) {
      case AuthStatus.uninitialized:
        return const SplashScreenView();
      case AuthStatus.authenticated:
        return const HomeView();
      case AuthStatus.newAccount:
        return const HomeView.newAccount();
      case AuthStatus.authenticating:
      case AuthStatus.unauthenticated:
        return const LoginView();
    }
  }
}

这样就完成了!现在你可以像使用<FirebaseAuth>一样使用<EasyAuth>进行登录、注销、创建账户等操作。

注意,我们在<AuthenticationBasedApp>中传递了<UserData>类作为泛型,让我们来讨论一下为什么。

EquatableUser #

这个类被用作用户的一个默认表示。它可以很容易地扩展以添加自己的参数。

由于它继承自Equatable,如果你想让某个属性成为==操作的一部分,你需要将其添加到props数组中。

// -> custom_user.dart
class CustomUser extends EquatableUser {
  const CustomUser({required this.birthday}) : super(id: '1', email: 'first@user.com');

  final String birthday;

  @override
  List<Object?> get props => [...super.props, birthday];
}

EasyAuth 方法 #

EasyAuth是一个工具类,允许你静态访问<AuthenticationRepository>上的方法。

ElevatedButton(
  child: const Text('Log in'),
  onPressed: () {
    final provider = EmailPasswordAuth('test@easyauth.com', 'some-password');
    EasyAuth.login(context, provider: provider);
  },
)

AuthenticationRepository #

AuthenticationRepository是一个抽象类,定义了添加自定义认证提供程序所需的所有方法。

注意:在覆盖此类中的任何方法时,你不需要处理错误!

abstract class AuthenticationRepository<T extends EquatableUser> {
  Future<void> login({required EasyAuthProvider provider});
  Future<void> register({required T user, required String password});
  Future<void> signOut();
  Future<void> deleteAccount();
  bool isUserNew(T user);

  T get currentUser;
  Stream<T> get user;

  Future<AuthException?> performSafeAuth(Future<void> future, AuthAction action) async {...}
}

唯一不需要重新实现的方法是performSafeAuth(...)。它用于处理执行认证操作时可能抛出的任何错误。

以下是BasicFirebaseAuth的一个示例:

class BasicFirebaseAuth<T extends EquatableUser> extends AuthenticationRepository<T> {
  final _firebaseAuth = FirebaseAuth.instance;

  @override
  Stream<T> get user => _firebaseAuth.authStateChanges().map<T>((user) {
        if (user == null) {
          return EquatableUser.empty;
        } else {
          return EquatableUser(
            id: user.uid,
            name: user.displayName,
            email: user.email,
            createdAt: user.metadata.creationTime,
          );
        }
      });

  @override
  bool isUserNew(T user) => user.createdAt?.isAfter(DateTime.now().subtract(const Duration(seconds: 5))) ?? false;

  @override
  T get currentUser {
    final _user = _firebaseAuth.currentUser!;
    return EquatableUser(id: _user.uid, name: _user.displayName, email: _user.email);
  }

  @override
  Future<void> login({required EasyAuthProvider provider}) async {
    if (provider is EmailPasswordAuth) {
      await _firebaseAuth.signInWithEmailAndPassword(email: provider.email, password: provider.password);
    } else if (provider is GoogleAuth) {
      //sign in with google
    }
  }

  @override
  Future<void> register({required T user, required String password}) async {
    if (user.email == null) throw FirebaseAuthException(code: 'no-email-registration');
    await _firebaseAuth.createUserWithEmailAndPassword(email: user.email!, password: password);
  }

  @override
  Future<void> signOut() => _firebaseAuth.signOut();

  @override
  Future<void> deleteAccount() => _firebaseAuth.currentUser!.delete();
}

EasyAuthProvider #

如果你需要添加比预包装的更多认证提供程序,只需覆盖<EasyAuthProvider>类。

EasyAuth Widgets #

AuthenticationBasedApp

AuthenticationBasedApp是一个抽象类,你需要扩展它以向你的应用添加认证响应性。

有一些你需要了解的方法:

/// 每当认证状态发生变化时,都会重建应用程序的状态。
/// 这是一个高效的方法,因为`T`扩展了`Equatable`,因此只有在必要时才会重建。
Widget buildState(BuildContext context, AuthStatus status, T user);

注意:此方法需要重写。

/// 当与认证相关的异常被抛出时调用。
/// 可以覆盖以提供自己的自定义错误处理逻辑(例如日志记录、自定义Snackbar等)。
void handleError(BuildContext context, AuthException exception) {...}

注意:此方法有一个默认实现,会打印执行的动作,当异常被抛出时,并显示以下来自package:another_flushbarFlushbar

Flushbar(
  icon: const Padding(padding: EdgeInsets.only(left: 14.0), child: Text('😱')),
  message: exception.message,
  backgroundColor: Theme.of(context).errorColor,
  margin: const EdgeInsets.all(8),
  borderRadius: BorderRadius.circular(8.0),
  flushbarPosition: FlushbarPosition.TOP,
  flushbarStyle: FlushbarStyle.FLOATING,
);

EasyAuthBuilder

EasyAuthBuilder是一个Flutter小部件,需要一个builder函数。EasyAuthBuilder处理响应新的认证状态而构建小部件。EasyAuthBuilderpackage:bloc中的<BlocBuilder>的一个简单包装器。builder函数可能会被多次调用,应该是一个返回小部件的纯函数,响应状态。

EasyAuthBuilder(
  builder: (context, status, user) {
    // 在这里返回基于当前AuthStatus和User的小部件
  }
)

更多关于Flutter简化认证流程插件easy_auth的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter简化认证流程插件easy_auth的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何使用Flutter插件easy_auth来简化认证流程的示例代码。easy_auth是一个假设的插件名称,用于说明目的,因为在实际Flutter生态系统中可能没有一个确切名为easy_auth的插件提供完整的认证流程简化功能。然而,我们可以根据常见的认证流程(如OAuth、Email/Password等)给出一个示例。

在实际应用中,你可能会使用如firebase_authgoogle_sign_influtter_facebook_login等插件来实现不同的认证方式。下面的代码将展示如何结合这些插件来简化认证流程。

1. 添加依赖

首先,在你的pubspec.yaml文件中添加所需的依赖:

dependencies:
  flutter:
    sdk: flutter
  firebase_auth: ^3.3.5  # 请检查最新版本
  google_sign_in: ^5.0.5  # 请检查最新版本
  flutter_facebook_login: ^3.0.0  # 假设有一个Facebook登录插件,请检查最新版本

2. 配置Firebase和Google Sign-In

按照Firebase和Google Sign-In的官方文档配置你的项目。

3. 创建认证服务

创建一个新的Dart文件,比如auth_service.dart,来封装认证逻辑:

import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:flutter_facebook_login/flutter_facebook_login.dart';

class AuthService {
  final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
  final GoogleSignIn _googleSignIn = GoogleSignIn();
  final FacebookLogin _facebookLogin = FacebookLogin();

  // Google Sign In
  Future<UserCredential?> signInWithGoogle() async {
    final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();
    if (googleUser == null) return null;

    final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
    final AuthCredential credential = GoogleAuthProvider.credential(
      idToken: googleAuth.idToken,
      accessToken: googleAuth.accessToken,
    );

    return await _firebaseAuth.signInWithCredential(credential);
  }

  // Facebook Login
  Future<UserCredential?> signInWithFacebook() async {
    final AccessToken? accessToken = await _facebookLogin.logIn();
    if (accessToken == null) return null;

    final AuthCredential credential = FacebookAuthProvider.credential(accessToken.token);
    return await _firebaseAuth.signInWithCredential(credential);
  }

  // Email/Password Sign In
  Future<UserCredential?> signInWithEmailAndPassword(String email, String password) async {
    return await _firebaseAuth.signInWithEmailAndPassword(email: email, password: password);
  }

  // Sign Out
  Future<void> signOut() async {
    await _firebaseAuth.signOut();
    await _googleSignIn.signOut();
    await _facebookLogin.logOut();
  }
}

4. 使用认证服务

在你的主应用文件中(如main.dart),使用上述认证服务:

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final AuthService _authService = AuthService();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Auth Demo'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: () => _authService.signInWithGoogle().then((credential) {
                  if (credential != null) {
                    // Handle successful sign-in
                    print('Google Sign-In Successful');
                  }
                }),
                child: Text('Sign In with Google'),
              ),
              ElevatedButton(
                onPressed: () => _authService.signInWithFacebook().then((credential) {
                  if (credential != null) {
                    // Handle successful sign-in
                    print('Facebook Sign-In Successful');
                  }
                }),
                child: Text('Sign In with Facebook'),
              ),
              ElevatedButton(
                onPressed: () {
                  // Show a dialog for Email/Password input and then call signInWithEmailAndPassword
                  showDialog(
                    context: context,
                    builder: (context) {
                      return AlertDialog(
                        title: Text('Sign In'),
                        content: SingleChildScrollView(
                          child: ListBody(
                            children: [
                              TextField(
                                decoration: InputDecoration(labelText: 'Email'),
                                onSubmitted: (email) {
                                  TextEditingController passwordController = TextEditingController();
                                  showDialog(
                                    context: context,
                                    builder: (context) {
                                      return AlertDialog(
                                        title: Text('Enter Password'),
                                        content: TextField(
                                          controller: passwordController,
                                          decoration: InputDecoration(labelText: 'Password'),
                                          obscureText: true,
                                        ),
                                        actions: [
                                          TextButton(
                                            onPressed: () => Navigator.of(context).pop(),
                                            child: Text('Cancel'),
                                          ),
                                          TextButton(
                                            onPressed: () async {
                                              String password = passwordController.text;
                                              await _authService.signInWithEmailAndPassword(email, password).then((credential) {
                                                if (credential != null) {
                                                  Navigator.of(context).pop();
                                                  Navigator.of(context).pop();
                                                  print('Email/Password Sign-In Successful');
                                                }
                                              });
                                            },
                                            child: Text('Sign In'),
                                          ),
                                        ],
                                      );
                                    },
                                  );
                                },
                              ),
                            ],
                          ),
                        ),
                        actions: [
                          TextButton(
                            onPressed: () => Navigator.of(context).pop(),
                            child: Text('Cancel'),
                          ),
                        ],
                      );
                    },
                  );
                },
                child: Text('Sign In with Email/Password'),
              ),
              ElevatedButton(
                onPressed: () => _authService.signOut(),
                child: Text('Sign Out'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

注意事项

  1. 实际项目中的错误处理:在实际项目中,你需要添加更多的错误处理逻辑,比如处理网络错误、认证失败等。
  2. UI/UX优化:上述示例中的UI/UX非常基础,你可能需要根据你的应用需求进行优化。
  3. 安全性:确保你的应用遵循最佳的安全实践,特别是在处理用户凭证时。

这个示例展示了如何使用Flutter和相关的认证插件来简化认证流程。虽然示例中使用了firebase_authgoogle_sign_in和假设的flutter_facebook_login插件,但你可以根据实际需求替换为其他插件或添加更多的认证方式。

回到顶部