前言:近来换了一份工作。在新工作中,由于公司产品是使用bloc开发的,那么没办法了,只能学了一下。在学习bloc的过程中,比较顺畅吧。感觉他跟provider(仅仅使用的区别,不是源码还有他跟provider适用场景的区别!!!!再次提示,勿喷)的区别就是,provider做一个封装过InheritWidget的特殊顶层变量它里面镶嵌这widget,而bloc是把自己的数据层,事件,状态,全部分割出来,当然你开始会觉得他很乱,要是用明白之后,发现其实这个框架开发产品挺舒服的。毕竟逻辑清晰。
好了有人问我为什么要用equatable这个库呢,其实这个库很好用的,enmm怎么说呢,榨干Flutter的最后性能。这个库会将我们暴露出来的对象进行比较,如果发生了变化,他就会指导bloc进行重构,反之。。。
1.我们创建属于我们的bloc文件夹,这样方便以后我们进行管理。
不要管这个sinple_bloc_delegate.dart是什么,用也行不用也行,就是我们可以做的一个全局捕捉,个人感觉没啥用啊!!!!
2.创建我们正常需要的bloc通用文件
在这里我说一下,login_bloc是我们组装的bloc的用法,login_state是我们每个操作返回的页面state状态,login_event是我们的操作动作,就是我们的事件,login_repository是我们的数据层。
不说别的了上代码:
login_state是记录我们页面的state状态,用它来判断我们页面的显示
import 'package:equatable/equatable.dart';
import 'package:flutterbloccart/model/user_model.dart';
abstract class LoginState extends Equatable{
const LoginState();
@override
List
login_event是我们的事件,用来判断bloc返回哪一个state
import 'package:equatable/equatable.dart';
abstract class LoginEvent extends Equatable{
const LoginEvent();
@override
List get props => [];
}
class LoginPressEvent extends LoginEvent{
final String name;
final String pwd;
LoginPressEvent(this.name, this.pwd);
@override
List get props => [name,pwd];
}
login_bloc组合login_state和login_event
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutterbloccart/bloc/login_event.dart';
import 'package:flutterbloccart/bloc/login_repository.dart';
import 'package:flutterbloccart/bloc/login_state.dart';
import 'package:flutterbloccart/model/user_model.dart';
class LoginBloc extends Bloc{
@override
LoginState get initialState => LoginInitialState();
@override
Stream mapEventToState(LoginEvent event) async* {
try{
if(event is LoginPressEvent){
UserModel model = UserModel.init();
yield LoginInProgressState();
final currentEvent = event;
model = await LoginRepository.login(currentEvent.name.trim(), currentEvent.pwd.trim());
yield LoginSuccessState(model);
}
}catch(e){
final errMsg = '登录错误';
yield LoginFailureState(errMsg);
}
}
}
login_repositor是与网络交互的数据层,这里我是模拟了一下过程。
import 'package:flutterbloccart/model/user_model.dart';
class LoginRepository{
static Future login(String name,String pwd) async {
if(name.length >= 6 && pwd.length >= 6){
await Future.delayed(Duration(seconds: 2));
return UserModel(name: name, pwd: pwd);
}else{
throw Exception('登录错误');
}
}
}
还有我们建立的UserModel数据模型,这是我们存储在bloc实例上的数据
import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';
class UserModel extends Equatable{
final String name;
final String pwd;
UserModel({@required this.name, @required this.pwd}) :assert(name != null,pwd != null);
factory UserModel.init(){
return UserModel(name: '',pwd: '');
}
@override
List get props => [name,pwd];
}
然后看我们的页面布局
login_page
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutterbloccart/bloc/login_bloc.dart';
import 'package:flutterbloccart/bloc/login_event.dart';
import 'package:flutterbloccart/bloc/login_state.dart';
import 'package:flutterbloccart/home_page.dart';
class LoginPage extends StatefulWidget {
@override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State {
final nameCtr = new TextEditingController();
final pwdCtr = new TextEditingController();
@override
void initState() {
super.initState();
nameCtr.addListener(() {
print('name输入框的实时变化::::${nameCtr.text}');
});
pwdCtr.addListener(() {
print('pwd输入框的实时变化::::${pwdCtr.text}');
});
}
@override
void dispose() {
super.dispose();
nameCtr?.removeListener(() { });
nameCtr?.dispose();
pwdCtr?.removeListener(() { });
pwdCtr?.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('登录'),
centerTitle: true,
),
body: BlocBuilder(
builder: (context,state){
if(state is LoginInitialState){
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
nameTF(),
pwdTF(),
loginBtn(context,(){
BlocProvider.of(context).add(LoginPressEvent(nameCtr.text.trim(), pwdCtr.text.trim()));
}),
],
);
}
if(state is LoginInProgressState){
return Center(
child: CircularProgressIndicator(),
);
}
if(state is LoginSuccessState){
return SuccessDialog();
}
if(state is LoginFailureState){
final currentState = state;
return Center(
child: Text(currentState.errMsg,style: TextStyle(fontSize: 18,color: Colors.black),),
);
}
return Container();
},
),
);
}
Widget nameTF(){
return Container(
height: 60,
margin: EdgeInsets.symmetric(horizontal: 50),
width: double.infinity,
child: TextField(
controller: nameCtr,
decoration: InputDecoration(
fillColor: Color(0x30cccccc),
filled: true,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color(0x00FF0000)),
borderRadius: BorderRadius.circular(100)
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color(0x00FF0000)),
borderRadius: BorderRadius.circular(100)
),
hintText: '请输入用户名'
),
),
);
}
Widget pwdTF(){
return Container(
height: 60,
margin: EdgeInsets.symmetric(horizontal: 50,vertical: 10),
width: double.infinity,
child: TextField(
controller: pwdCtr,
decoration: InputDecoration(
fillColor: Color(0x30cccccc),
filled: true,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color(0x00FF0000)),
borderRadius: BorderRadius.circular(100)
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color(0x00FF0000)),
borderRadius: BorderRadius.circular(100)
),
hintText: '请输入密码',
),
obscureText: true,
),
);
}
Widget loginBtn(context,onPressed){
return Container(
height: 50,
margin: EdgeInsets.symmetric(horizontal: 50),
width: double.infinity,
child: RaisedButton(
child: Text('登录',style: TextStyle(fontSize: 16,color: Colors.white),),
color: Theme.of(context).accentColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(100),
),
onPressed: onPressed,
),
);
}
}
class SuccessDialog extends StatefulWidget {
@override
_SuccessDialogState createState() => _SuccessDialogState();
}
class _SuccessDialogState extends State {
Future waitFuture() async{
await Future.delayed(Duration(milliseconds: 500));
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => HomePage()
));
}
@override
void initState() {
super.initState();
waitFuture();
}
@override
Widget build(BuildContext context) {
return Center(
child: Text('登录成功',style: TextStyle(fontSize: 18,color: Colors.black),),
);
}
}
home_page
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutterbloccart/bloc/login_bloc.dart';
import 'package:flutterbloccart/bloc/login_state.dart';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State {
LoginBloc loginBloc;
@override
void initState() {
super.initState();
loginBloc = BlocProvider.of(context);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('登录成功的页面'),
centerTitle: true,
),
body: BlocBuilder(
builder: (context,state){
if(state is LoginSuccessState){
final currentState = state;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('登录成功',style: TextStyle(fontSize: 24,color: Colors.black),),
SizedBox(height: 10,),
Text('登录账号::::${state.model.name}',style: TextStyle(fontSize: 18,color: Colors.black),),
SizedBox(height: 10,),
Text('登录密码::::${state.model.pwd}',style: TextStyle(fontSize: 18,color: Colors.black),),
],
);
}
return Container();
},
),
);
}
}
然后我们需要将这个实例注入到MaterialApp上面做最底层的数据共享
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutterbloccart/bloc/login_bloc.dart';
import 'package:flutterbloccart/login_page.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => LoginBloc(),
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: LoginPage(),
),
);
}
}
好了,我们使用BLoc设计的登录功能已经完成了。
我也不想多解释什么了,其实很明白了。就是我们在LoginBloc中重写的mapEventToState方法,至少每个事件要yield一个state对象,这个对象就是我们的login_state需要用它来判断当前页面的显示。
我也不放图了。代码copy下来就能运行。这就是一个最简单的bloc功能的设计。当然还有其他的bloc的方法。就需要你们自己去研究了。。。