Flutter小实战----我们使用BLoc来实现一个登录的功能

前言:近来换了一份工作。在新工作中,由于公司产品是使用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<Object> get props => [];
}

class LoginInitialState extends LoginState{}

class LoginInProgressState extends LoginState{}

class LoginSuccessState extends LoginState{
  final UserModel model;

  LoginSuccessState(this.model);

  @override
  List<Object> get props => [model];
}

class LoginFailureState extends LoginState{
  final String errMsg;

  LoginFailureState(this.errMsg);

  @override
  List<Object> get props => [errMsg];
}

login_event是我们的事件,用来判断bloc返回哪一个state


import 'package:equatable/equatable.dart';

abstract class LoginEvent extends Equatable{
  const LoginEvent();

  @override
  List<Object> get props => [];
}

class LoginPressEvent extends LoginEvent{
  final String name;
  final String pwd;

  LoginPressEvent(this.name, this.pwd);

  @override
  List<Object> 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<LoginEvent,LoginState>{

  @override
  LoginState get initialState => LoginInitialState();

  @override
  Stream<LoginState> 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<Object> 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<LoginPage> {
  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<LoginBloc,LoginState>(
        builder: (context,state){
          if(state is LoginInitialState){
            return Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: <Widget>[
                nameTF(),
                pwdTF(),
                loginBtn(context,(){
                  BlocProvider.of<LoginBloc>(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<SuccessDialog> {

  Future<void> 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<HomePage> {
  LoginBloc loginBloc;

  @override
  void initState() {
    super.initState();
    loginBloc =  BlocProvider.of<LoginBloc>(context);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('登录成功的页面'),
        centerTitle: true,
      ),
      body: BlocBuilder<LoginBloc,LoginState>(
        builder: (context,state){
          if(state is LoginSuccessState){
            final currentState = state;
            return Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                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的方法。就需要你们自己去研究了。。。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
很高兴为您解答问题!关于Flutter实战视频-移动电商-48.详细页_详情和评论的切换,我可以提供以下解答: 在Flutter实战视频-移动电商-48中,详细页的详情和评论的切换是通过一个TabBar实现的。具体来说,通过在AppBar中添加一个TabBar来实现不同页面之间的切换。在这个TabBar中,我们可以添加两个Tab,分别为“详情”和“评论”,并分别对应两个页面。当用户点击不同的Tab时,就可以切换到不同的页面。 在Flutter中,TabBar可以使用DefaultTabController和TabBarView来实现。DefaultTabController是一个Widget,它可以管理TabBar和TabBarView之间的关系。TabBarView则是一个Widget,它可以显示不同的页面。 具体实现方法如下: ```dart class DetailPage extends StatefulWidget { @override _DetailPageState createState() => _DetailPageState(); } class _DetailPageState extends State<DetailPage> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('商品详情'), bottom: TabBar( tabs: <Widget>[ Tab( text: '详情', ), Tab( text: '评论', ), ], ), ), body: DefaultTabController( length: 2, child: TabBarView( children: <Widget>[ // 详情页面 DetailWidget(), // 评论页面 CommentWidget(), ], ), ), ); } } ``` 在上面的代码中,我们首先创建了一个AppBar,并在其中添加了一个TabBar。然后,在Scaffold的body中,我们创建了一个DefaultTabController,并将其length设为2,即有两个页面。最后,我们在TabBarView中添加了两个Widget,即DetailWidget和CommentWidget,分别对应详情页面和评论页面。 希望这个解答能够对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值