Flutter状态管理Bloc之登录示例

本文实例为大家分享了Flutter状态管理Bloc之登录的具体代码,供大家参考,具体内容如下

1. 依赖

dependencies:
  flutter_bloc: ^2.1.1
  equatable: ^1.0.1

2. UserRepository 用于管理用户数据

提供认证方法,删除Token,保存Token,是否包含Token四个方法。

import 'package:flutter/material.dart';
 
/// 用户数据仓库
class UserRepository {
 
  /// 认证
  /// @param username 用户名
  /// @param password 密码
  /// @return 返回认证信息
  Future<String> authenticate({
    @required String username,
    @required String password,
  }) async {
    await Future.delayed(Duration(seconds: 1));
    return "token";
  }
 
  /// 删除Token
  Future<void> deleteToToken() async {
    await Future.delayed(Duration(seconds: 1));
    return ;
  }
 
  /// 保存Token
  /// @param token 令牌
  Future<void> persistToken(String token) async {
    // 保存
    await Future.delayed(Duration(seconds: 1));
    return ;
  }
 
  /// 判断是否有Token
  /// @return true: 有; false: 没有Token
  Future<bool> hasToken() async {
    // 读取Token
    await Future.delayed(Duration(seconds: 1));
    return false;
  }
}

3. AuthenticateState

  • uninitialized - 身份验证未初始化
  • loading - 等待保存/删除Token
  • authenticated - 认证成功
  • unauthenticated - 未认证
import 'package:equatable/equatable.dart';
 
/// 认证状态
abstract class AuthenticationState extends Equatable {
  @override
  List<Object> get props => [];
}
 
/// - uninitialized - 身份验证未初始化
class AuthenticationUninitialized extends AuthenticationState {}
/// - loading - 等待保存/删除Token
class AuthenticationLoading extends AuthenticationState {}
/// - authenticated - 认证成功
class AuthenticationAuthenticated extends AuthenticationState {}
/// - unauthenticated - 未认证
class AuthenticationUnauthenticated extends AuthenticationState {}

4. 认证事件

  • AppStarted - App 启动事件
  • LoggedIn - 登录事件
  • LoggedOut - 退出登录事件
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
 
/// 认证事件
abstract class AuthenticationEvent extends Equatable {
 
  const AuthenticationEvent();
 
  @override
  List<Object> get props => [];
 
}
 
/// APP 启动事件
class AppStart extends AuthenticationEvent {}
 
/// APP 登录事件
class LoginIn extends AuthenticationEvent {
  
  final String token;
 
  const LoginIn({@required this.token});
 
  @override
  List<Object> get props => [token];
 
  @override
  String toString() => "LoggedIn { token: $token }";
}
 
/// APP 退出登录事件
class LoginOut extends AuthenticationEvent {}

5. AuthenticationBloc

  • 实现构造方法
  • 实现initialState方法
  • 实现mapEventToState方法
import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart';
import './bloc.dart';
import 'package:state_manage/login/user_repository.dart';
 
/// 认证Bloc
class AuthenticationBloc extends Bloc<AuthenticationEvent, AuthenticationState> {
  // 用户仓库
  final UserRepository userRepository;
 
  AuthenticationBloc({@required this.userRepository}): assert(userRepository != null);
 
  @override
  AuthenticationState get initialState => AuthenticationUninitialized();
 
  @override
  Stream<AuthenticationState> mapEventToState(AuthenticationEvent event) async* {
    if (event is AppStarted) {
      // 判断是否有Token
      final bool hasToken = await userRepository.hasToken();
      if (hasToken) {
        yield AuthenticationAuthenticated();
      } else {
        yield AuthenticationUnauthenticated();
      }
    } else if (event is LoggedIn) {
      yield AuthenticationLoading();
      await userRepository.persistToken(event.token);
      yield AuthenticationAuthenticated();
    } else if (event is LoggedOut) {
      yield AuthenticationLoading();
      await userRepository.deleteToToken();
      yield AuthenticationUnauthenticated();
    }
  }
}

6. SplashPage 启动页

import 'package:flutter/material.dart';
 
/// 启动页
class SplashPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text('Splash Screen'),
      ),
    );
  }
}

7. HomePage 主页

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:state_manage/login/bloc/bloc.dart';
 
/// 主页
class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home'),
      ),
      body: Container(
        child: Center(
          child: RaisedButton(
            child: Text('logout'),
            onPressed: () => BlocProvider.of<AuthenticationBloc>(context).add(LoggedOut())
          ),
        ),
      ),
    );
  }
}

8. LoginState 登录状态

  • LoginInitial - 初始化状态
  • LoginLoading - 登录中状态
  • LoginFailure - 登录失败状态
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
 
/// 登录状态
@immutable
abstract class LoginState extends Equatable {
  const LoginState();
 
  @override
  List<Object> get props => [];
}
  
/// 登录初始化状态
class LoginInitial extends LoginState {}
 
/// 正在登录中状态
class LoginLoading extends LoginState {}
 
/// 登录失败状态
class LoginFailure extends LoginState {
  final String error;
 
  const LoginFailure({@required this.error});
 
  @override
  List<Object> get props => [error];
 
  @override
  String toString() => "LoginFailure { error: $error }";
}

9. LoginEvent 登录事件

import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
 
/// 登录事件
@immutable
abstract class LoginEvent extends Equatable{}
 
/// 登录事件
class LoginPressed extends LoginEvent {
  /// 用户名
  final String username;
  /// 密码
  final String password;
 
  LoginPressed({
    @required this.username,
    @required this.password
  });
 
  @override
  List<Object> get props => [username, password];
 
  @override
  String toString() => "LoginPressed { username: $username, password: $password }";
}

10. LoginBloc 实现

import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:flutter/cupertino.dart';
import 'package:state_manage/login/user_repository.dart';
import './bloc.dart';
 
class LoginBloc extends Bloc<LoginEvent, LoginState> {
  /// 用户信息仓库
  final UserRepository userRepository;
 
  /// 认证Bloc
  final AuthenticationBloc authenticationBloc;
 
  LoginBloc({@required this.userRepository, @required this.authenticationBloc})
      : assert(userRepository != null),
        assert(authenticationBloc != null);
 
  @override
  LoginState get initialState => LoginInitial();
 
  @override
  Stream<LoginState> mapEventToState(
    LoginEvent event,
  ) async* {
    if (event is LoginPressed) {
      yield LoginLoading();
 
      try {
        final token = await userRepository.authenticate(
          username: event.username,
          password: event.password
        );
        authenticationBloc.add(LoggedIn(token: token));
        yield LoginInitial();
      } catch (error) {
        yield LoginFailure(error: error);
      }
    }
  }
}

11. 登录页面

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:state_manage/login/bloc/bloc.dart';
import 'package:state_manage/login/user_repository.dart';
 
/// 登录页面
class LoginPage extends StatelessWidget {
  /// 用户信息仓库
  final UserRepository userRepository;
 
  LoginPage({Key key, @required this.userRepository})
      : assert(userRepository != null),
        super(key: key);
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Login'),
      ),
      body: BlocProvider(
        create: (ctx) => LoginBloc(
            authenticationBloc: BlocProvider.of(context),
            userRepository: userRepository),
        child: LoginForm(),
      ),
    );
  }
}
 
/// 登录表单
class LoginForm extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _LoginFormState();
}
 
/// 登录状态表单
class _LoginFormState extends State<LoginForm> {
  /// 用户名控制器
  final _usernameController = TextEditingController();
 
  /// 密码控制器
  final _passwordController = TextEditingController();
  @override
  Widget build(BuildContext context) {
    _onLoginPressed() {
      BlocProvider.of<LoginBloc>(context).add(LoginPressed(
          username: _usernameController.text,
          password: _passwordController.text));
    }
 
    return BlocListener<LoginBloc, LoginState>(
      listener: (ctx, state) {
        if (state is LoginFailure) {
          Scaffold.of(context).showSnackBar(SnackBar(
            content: Text('${state.error}'),
            backgroundColor: Colors.red,
          ));
        }
      },
      child: BlocBuilder<LoginBloc, LoginState>(
        builder: (ctx, state) {
          return Form(
            child: Column(
              children: <Widget>[
                TextFormField(
                  decoration: InputDecoration(labelText: 'username'),
                  controller: _usernameController,
                ),
                TextFormField(
                  decoration: InputDecoration(labelText: 'password'),
                  controller: _passwordController,
                ),
                RaisedButton(
                  onPressed: state is LoginLoading ? _onLoginPressed : null,
                  child: Text('Login'),
                ),
                Container(
                  child: state is LoginLoading
                      ? CircularProgressIndicator()
                      : null,
                )
              ],
            ),
          );
        },
      ),
    );
  }
}

12. 测试页面

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:state_manage/login/bloc/bloc.dart';
import 'package:state_manage/login/home_page.dart';
import 'package:state_manage/login/login_page.dart';
import 'package:state_manage/login/splash_page.dart';
import 'package:state_manage/login/user_repository.dart';
 
/// 登录测试页面
class LoginTest extends StatelessWidget {
  // 数据仓库
  final userRepository = UserRepository();
 
  @override
  Widget build(BuildContext context) {
    return BlocProvider<AuthenticationBloc>(
      create: (context) =>
          AuthenticationBloc(userRepository: userRepository)..add(AppStarted()),
      child: App(userRepository: userRepository),
    );
  }
}
 
/// 应用页
class App extends StatelessWidget{
  // 数据仓库
  final UserRepository userRepository;
 
  App({Key key, @required this.userRepository}) : super(key: key);
 
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
        builder: (context, state) {
          if (state is AuthenticationAuthenticated) {
            return HomePage();
          } else if (state is AuthenticationUnauthenticated) {
            return LoginPage(userRepository: userRepository);
          } else if (state is AuthenticationLoading) {
            return LoadingIndicator();
          } else {
            if (state is AuthenticationUninitialized) {
              return SplashPage();
            } else {
              return SplashPage();
            }
          }
        },
      ),
    );
  }
 
}
 
/// 加载状态
class LoadingIndicator extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: Center(
        child: CircularProgressIndicator(),
      ),
    );
  }
}

效果图:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Flutter组件状态管理的3种方法

    前言 前面讲了Flutter布局,布局只是静态的.在Flutter中,组件分为StatelesslWidget和StatefulWidget. StatelesslWidget 没有状态,是一成不变的.比如图标,文字,按钮等 StatefulWidget 有状态的组件,页面数据是动态的,或者会随着用户操作变化,比如多选框,文本输入框等. 有状态组件 重点来了,如何使用实现一个有状态的组件呢? 有状态的组件一般由两个类构成,一个StatefulWidget子类和一个State子类. State类包

  • Flutter状态管理Bloc之定时器示例

    本文实例为大家分享了Flutter状态管理Bloc之定时器的具体代码,供大家参考,具体内容如下 1. 依赖 dependencies:   flutter_bloc: ^2.1.1   equatable: ^1.0.1   wave: ^0.0.8 2. Ticker Ticker 用于产生定时器的数据流. /// 定时器数据源 class Ticker {      /// 定时器数据源   /// @param ticks 时间   Stream<int> tick({int ticks

  • Flutter 状态管理的实现

    一.什么是状态管理 大到整个app的状态,用户使用app是登录状态,还是游客状态:小到一个按钮的状态,按钮是点击选中状态还是未点击状态等等,这些都是状态管理. 二.命令式编程和声明式编程状态管理的区别 iOS是如何管理状态的,一般都是获取这个控件然后设置你想要的状态 当你的 Flutter 应用的状态发生改变时(例如,用户在设置界面中点击了一个开关选项)你改变了状态,这将会触发用户界面的重绘.去改变用户界面本身是没有必要的(例如 widget.setText )-你改变了状态,那么用户界面将重新

  • Flutter部件内部状态管理小结之实现Vue的v-model功能

    Flutter部件内部状态管理 本文是 Flutter 部件内部状态管理的小结,从部件的基础开始,到部件的状态管理,并且在过程中实现一个类似 Vue 的 v-model 的功能. widget 基础 widget(部件) 如 React 里万物皆组件, Java 里万物皆对象, Flutter 里,能看到的一切都是 widget(部件),如按钮.文本框等等. Flutter 内部已经为我们做了一些基础的 widget ,例如: Text : 这个就是一个文本部件,里面用于放置文本 Row , C

  • Flutter状态管理Bloc之登录示例

    本文实例为大家分享了Flutter状态管理Bloc之登录的具体代码,供大家参考,具体内容如下 1. 依赖 dependencies:   flutter_bloc: ^2.1.1   equatable: ^1.0.1 2. UserRepository 用于管理用户数据 提供认证方法,删除Token,保存Token,是否包含Token四个方法. import 'package:flutter/material.dart';   /// 用户数据仓库 class UserRepository {

  • Flutter状态管理Bloc使用示例详解

    目录 前言 两种使用模式 Cubit模式 最后 前言 目前Flutter三大主流状态管理框架分别是provider.flutter_bloc.getx,三大状态管理框架各有优劣,本篇文章将介绍其中的flutter_bloc框架的使用,他是bloc设计思想模式在flutter上的实现,bloc全程全称 business logic ,业务逻辑的意思,核心思想就是最大程度的将页面ui与数据逻辑的解耦,使我们的项目可读性.可维护性.健壮性增强. 两种使用模式 首先第一步引入插件: flutter_bl

  • Flutter状态管理Provider的使用示例详解

    目录 前言 计数器 全局状态 总结 前言 Provider是三大主流状态管理框架官方推荐使用的框架,它是基于官方数据共享组件InheritedWidget实现的,通过数据改变调用生命周期中的didChangeDependencies()方法,来实现状态的通知改变. InheritedWidget的使用可以参考我之前的这篇Flutter中几种数据传递的应用总结. 计数器 还是以计数器为例,这次通过Provider实现,provider相较于bloc并没有那么强制性分层,所以这里我们自己分为数据层(

  • 封装flutter状态管理工具示例详解

    目录 引言 RxBinder 代码实现 Demo 完美运行 引言 关于 Flutter 状态管理,公司项目使用的是Bloc方案.Bloc 其实本质上是 provider 的封装扩展库,整体通过 InheritedWidget .Notifier 外加 Stream中转实现状态变更通知. 关于 Bloc 实现原理,有兴趣的同学可以观看这篇文章 Bloc原理解析 RxBinder 撇开Bloc内部实现策略,小轰尝试基于数据驱动模型,自定义一套状态管理工具.构思如下: 主要成员如下: RxBinder

  • Flutter状态管理Provider示例解析

    目录 什么是状态管理 常见的状态管理框架有哪些 Provider Redux GetX Provider 使用 添加依赖 导入应用 定义需要共享的数据 在应用程序入口初始化 使用共享数据 状态管理的好处 结束语 什么是状态管理 状态管理是一个十分广泛的概念,因为状态无处不在.从广义角度讲状态管理就是页面跟代码.跟服务器进行数据同步.例如:你在某购物应用内,添加了一件商品,你需要在购物车页面进行展示.这就是一种状态.相反的,你在商品页面点击了添加到购物车,却在购物车页面没有发现或者过段时间它才展示

  • React 状态管理工具优劣势示例分析

    目录 什么是状态管理? React 状态管理方案 方案介绍 方案对比 Source 相关建议 什么是状态管理? “状态”是描述应用程序当前行为的任何数据.这可能包括诸如“从服务器获取的对象列表”.“当前选择的项目”.“当前登录用户的名称”和“此模式是否打开?”等值. 众所周知,我们在研发一个复杂应用的过程中,一套好的状态管理方案是必不可少的,既能提升研发效率,又能降低研发维护成本,那么状态管理方案那么多,它们有什么不同,我们又该如何选择适合当前应用的方案呢? 本期将主要就 react 的常用状态

  • flutter状态管理Provider的使用学习

    目录 1. provider的使用 2. 控制Widget的刷新颗粒度 3. 小结 本文主要介绍flutter中状态管理组件provider,provider: ^6.0.3主要是用于我们系统InheritedWidge的封装,用于数据的传递和管理. 1. provider的使用 网上也有很多关于provider说明,也可以看下官方的provider的 README.这里我记录一下我自己学习.我们对于简单的数据共享可以设置参数,之后子页面进行数据方法回调,从而完成数据间的通信.但是比较麻烦,下面

  • vuex实现登录状态的存储,未登录状态不允许浏览的方法

    基础思路就是使用vuex状态管理来存储登录状态(其实就是存一个值,例如token),然后在路由跳转前进行登录状态的判断,可以使用vue-router的全局前置守卫beforeEach,也可以使用路由独享的守卫beforeEnter. 导航守卫 正如其名,vue-router``` 提供的导航守卫主要用来通过跳转或取消的方式守卫导航.有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的. 记住参数或查询的改变并不会触发进入/离开的导航守卫.你可以通过观察 $route 对象来应对

随机推荐