栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Flutter 状态管理之Bloc下

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Flutter 状态管理之Bloc下

这篇是使用 Bloc 来实现业务逻辑与UI分离。主要就是慕课网课程列表的网络请求并且展示。

首先定义一个基础事件的类,如下:

abstract class LessonEvent {}

然后我定义了3种事件:

  1. 发起网络请求事件:
class FetchDataEvent extends LessonEvent {}
  1. 网络请求成功事件:
class FetchDataSuccessEvent extends LessonEvent {}
  1. 网络请求失败的事件:
class FetchDataFailedEvent extends LessonEvent {}

然后定义一个枚举类型,来表示这3种事件,这样就可以在 BlockBuild 中根据枚举类型来展示不同的 UI 了。

enum LoadStatus { loading, success, failed }

课程的数据结构如下:

import 'package:json_annotation/json_annotation.dart';

part 'LessonBean.g.dart';

@JsonSerializable()
class LessonBean {
  int status;
  String msg;
  List data;

  LessonBean({required this.status, required this.msg, required this.data});

  factory LessonBean.fromJson(Map srcJson) =>
      _$LessonBeanFromJson(srcJson);

  Map toJson() => _$LessonBeanToJson(this);

  @override
  bool operator ==(Object other) {
    if (identical(other, this)) return true;

    if (other is LessonBean) {
      return other.runtimeType == this.runtimeType &&
          other.status == this.status &&
          other.msg == this.msg &&
          other.data == this.data;
    } else {
      return false;
    }
  }

  @override
  int get hashCode {
    int result = 17;
    result = 37 * result + status.hashCode;
    result = 37 * result + msg.hashCode;
    result = 37 * result + data.hashCode;
    return result;
  }
}

@JsonSerializable()
class LessonDetail {
  final int id;
  final String name;
  final String picSmall;
  final String picBig;
  final String description;

  LessonDetail(
      {required this.id,
      required this.name,
      required this.picSmall,
      required this.picBig,
      required this.description});

  factory LessonDetail.fromJson(Map srcJson) =>
      _$LessonDetailFromJson(srcJson);

  Map toJson() => _$LessonDetailToJson(this);

  @override
  bool operator ==(Object other) {
    if (identical(other, this)) {
      return true;
    } else if (other is LessonDetail) {
      return other.runtimeType == this.runtimeType &&
          other.id == this.id &&
          other.name == this.name &&
          other.picSmall == this.picSmall &&
          other.picBig == this.picBig &&
          other.description == this.description;
    } else {
      return false;
    }
  }

  @override
  int get hashCode {
    int result = 17;
    result = 37 * result + id.hashCode;
    result = 37 * result + name.hashCode;
    result = 37 * result + picSmall.hashCode;
    result = 37 * result + picBig.hashCode;
    result = 37 * result + description.hashCode;
    return result;
  }
}

重写了其中的 hashcode 和重载了运算符 ==。

然后再定义一个课程的包装类,包含了 LessonBean 和 枚举类型 LoadStatus,如下:

class LessonWrap {
  LessonBean? lessonBean;
  LoadStatus status;

  LessonWrap({required this.lessonBean, required this.status});
}

这个包装类型 LessonWrap 将作为自定义 Bloc 的输出,然后 BlockBuilder中就会观察到数据的改变。

LessonBloc 的代码如下:

class LessonBloc extends Bloc {
  LessonBean? _lessonBean;

  LessonBloc(LessonWrap initial) : super(initial) {
    on((event, emit) {
      _requestData();
    });

    on((event, emit) {
      emit(LessonWrap(lessonBean: _lessonBean, status: LoadStatus.success));
    });

    on((event, emit) {
      emit(LessonWrap(lessonBean: null, status: LoadStatus.failed));
    });

    /// 初始化 bloc 的时候就去请求数据
    add(FetchDataEvent());
  }

  void _requestData() async {
    _lessonBean = await MukeService().getPersonalLesson(7);

    /// 接口请求太快了,看不到loading圈,所以加个延时
    Timer(Duration(seconds: 2), () {
      if (_lessonBean == null) {
        add(FetchDataFailedEvent());
      } else {
        add(FetchDataSuccessEvent());
      }
    });
  }
}

页面展示的逻辑如下:

class BlocInstanceWidget extends StatelessWidget {
  Widget getRow(LessonDetail detail) {
    return GestureDetector(
      child: Container(
        color: Colors.blueGrey,
        width: double.infinity,
        padding: EdgeInsets.only(bottom: 10, left: 10, top: 10),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          mainAxisSize: MainAxisSize.min,
          children: [
            Text("Row >>>  ${detail.name}"),
            SizedBox(
              height: 5,
            ),
            Container(
              child: ClipRRect(
                // 圆角图片
                borderRadius: BorderRadius.circular(8),
                child: Image(
                  image: NetworkImage(detail.picSmall),
                  width: 60,
                  height: 60,
                  fit: BoxFit.fitHeight,
                ),
              ),
            ),
            SizedBox(
              height: 6,
            ),
            Text(
              detail.description,
              style: const TextStyle(
                  color: Colors.brown,
                  fontWeight: FontWeight.bold,
                  fontStyle: FontStyle.italic),
            ),
          ],
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    LessonBloc lessonBloc = BlocProvider.of(context);
    return Scaffold(
      appBar: AppBar(
        title: Text('Bloc Instance'),
      ),
      body: Container(
        child: Column(
          children: [
            BlocBuilder(
              builder: (context, wrap) {
                if (wrap.status == LoadStatus.loading) {
                  return Container(
                      margin: EdgeInsets.only(top: 300),
                      // color: Color(0xffFFFFFF),
                      alignment: Alignment.center,
                      child: CircularProgressIndicator(
                        color: const Color(0xff2A9DFF).withOpacity(0.5),
                      ));
                } else if (wrap.status == LoadStatus.success) {
                  /// 因为 在 Column 中嵌套 ListView ,所以需要加上 Expanded ,
                  /// 否则报 Vertical viewport was given unbounded height
                  return Expanded(
                      child: ListView.separated(
                    shrinkWrap: true,
                    itemCount: wrap.lessonBean!.data.length,
                    itemBuilder: (BuildContext context, int position) {
                      return getRow(wrap.lessonBean!.data[position]);
                    },
                    separatorBuilder: (context, index) {
                      return Divider(
                          height: 1.0, indent: 10, color: Colors.black);
                    },
                  ));
                } else {
                  return Text("请求数据出错!!!!");
                }
              },
              buildWhen: (previous, next) {
                /// 过滤条件,只有2次数据不一致时才刷新
                return previous.lessonBean != next.lessonBean;
              },
            ),
            ElevatedButton(
                onPressed: () {
                  /// 由于我加了过滤条件,即上面的 buildWhen,
                  /// 因为2次刷新的数据是一样的(我重写了 LessonBean 的 hashcode 和 '=='),所以不会刷新界面
                  lessonBloc.add(FetchDataEvent());
                },
                child: Text('刷新数据')),
          ],
        ),
      ),
    );
  }
}

入口代码如下:

void main() {
  runApp(MaterialApp(
      home: BlocProvider(
          create: (context) => LessonBloc(
              LessonWrap(lessonBean: null, status: LoadStatus.loading)),
          child: BlocInstanceWidget())));
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1035500.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号