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 authenticate({
    @required String username,
    @required String password,
  }) async {
    await Future.delayed(Duration(seconds: 1));
    return "token";
  }

  /// 删除Token
  Future deleteToToken() async {
    await Future.delayed(Duration(seconds: 1));
    return ;
  }

  /// 保存Token
  /// @param token 令牌
  Future persistToken(String token) async {
    // 保存
    await Future.delayed(Duration(seconds: 1));
    return ;
  }

  /// 判断是否有Token
  /// @return true: 有; false: 没有Token
  Future 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 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 get props => [];

}

/// APP 启动事件
class AppStart extends AuthenticationEvent {}

/// APP 登录事件
class LoginIn extends AuthenticationEvent {
  
  final String token;

  const LoginIn({@required this.token});

  @override
  List get props => [token];

  @override
  String toString() => "LoggedIn { token: $token }";
}

/// APP 退出登录事件
class LoginOut extends AuthenticationEvent {}
 
 
5. AuthenticationBloc
  1. 实现构造方法
  2. 实现initialState方法
  3. 实现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 {
  // 用户仓库
  final UserRepository userRepository;

  AuthenticationBloc({@required this.userRepository}): assert(userRepository != null);

  @override
  AuthenticationState get initialState => AuthenticationUninitialized();

  @override
  Stream 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(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 get props => [];
}
  
/// 登录初始化状态
class LoginInitial extends LoginState {}

/// 正在登录中状态
class LoginLoading extends LoginState {}

/// 登录失败状态
class LoginFailure extends LoginState {
  final String error;

  const LoginFailure({@required this.error});

  @override
  List 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 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 {
  /// 用户信息仓库
  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 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 createState() => _LoginFormState();
}

/// 登录状态表单
class _LoginFormState extends State {
  /// 用户名控制器
  final _usernameController = TextEditingController();

  /// 密码控制器
  final _passwordController = TextEditingController();
  @override
  Widget build(BuildContext context) {
    _onLoginPressed() {
      BlocProvider.of(context).add(LoginPressed(
          username: _usernameController.text,
          password: _passwordController.text));
    }

    return BlocListener(
      listener: (ctx, state) {
        if (state is LoginFailure) {
          Scaffold.of(context).showSnackBar(SnackBar(
            content: Text('${state.error}'),
            backgroundColor: Colors.red,
          ));
        }
      },
      child: BlocBuilder(
        builder: (ctx, state) {
          return Form(
            child: Column(
              children: [
                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(
      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(
        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(),
      ),
    );
  }
}

效果图:

Flutter 状态管理 Bloc 之登录示例_第1张图片
效果图.gif

你可能感兴趣的:(Flutter 状态管理 Bloc 之登录示例)