美文网首页需要近期研究的项目Flutter飞起
Flutter 学习 之 使用 Provider 实现 MVVM

Flutter 学习 之 使用 Provider 实现 MVVM

作者: 半城半离人 | 来源:发表于2022-04-05 23:56 被阅读0次

InheritedWidget 组件的上层封装,使其更易用,更易复用。地址→ provider

一.引入Provider

在 pubspec.yaml 文件下新增 provider(注意空格问题)

dependencies:
  provider: ^6.0.2

二. 创建MVVM架构文件

MVVM架构文件可以分为四个:

  1. Model 数据模型
  2. View 界面
  3. ViewModel model和view的数据绑定
  4. Repository 网络访问

1. ViewModel 的基类

  • 页面状态的枚举
enum ViewState {
  idle, //空闲的(执行完毕)
  error, //出错了
  empty, //返回空值
  busy, //繁忙的
}
  • 错误的枚举类型
///页面错误类型
enum StateErrorType {
  defaultError,
  networkTimeoutError, //网络错误
  unauthorizedError, //授权错误
  responseException, //响应异常
}
  • BaseViewModel 的封装
///基础的ViewModel
class BaseViewModel extends ChangeNotifier {
  ///页面状态
  ViewState _state;
  ///是否被销毁
  bool _disposed = false;

  ///页面错误详细信息
  ViewStateError? _viewStateError;

  BaseViewModel({ViewState? state}) : _state = state ?? ViewState.busy;

  ViewState get viewState => _state;

  ViewStateError? get viewStateError => _viewStateError;

  set viewState(ViewState state) {
    _viewStateError = null;
    _state = state;
    notifyListeners();
  }
  //判断是否是忙碌状态
  bool isBusy() => _state == ViewState.busy;
  //判断是否是获取到的是空
  bool isEmpty() => _state == ViewState.empty;
  //判断是否是出错
  bool isError() => _state == ViewState.error;
  //判断是否加载完成
  bool isIdly() => _state == ViewState.idle;
  //设置空闲状态
  void setIdle() => viewState = ViewState.idle;
  //设置为空状态
  void setEmpty() => viewState = ViewState.empty;
  //设置出错状态(主要是由Dio 接口返回的DioErrorType)
  void setError(e, stackTrace, {String? message}) {
    StateErrorType errorType =
        FormatUtil.dioErrorFormat(e).errorType ?? StateErrorType.defaultError;
    message = FormatUtil.dioErrorFormat(e).message ?? "出错鸟~~~";
    debugPrint(" errorType: $errorType, errorMsg: $message, errorInfo: ${e.toString()}");
    viewState = ViewState.error;
    _viewStateError = ViewStateError(
        errorType: errorType, errorMsg: message, errorInfo: e.toString());
  }
  //设置繁忙状态
  void setBusy() => viewState = ViewState.busy;

  @override
  void notifyListeners() {
    if (!_disposed) {
      super.notifyListeners();
    }
  }

  @override
  void dispose() {
    _disposed = true;
    super.dispose();
  }
}
  • 带列表的BaseViewModel
  // T为Model的泛型 M是Model中的List的类型
abstract class BaseRefreshListViewModel<T, M> extends BaseViewModel {
  // 设定当前页面角标
  int pageIndex = 1;
  // 设置页面大小
  int pageSize = SystemConfig.pageSize;
  //获取到的Model
  late T model;
  Model中的list
  List<M> lists = [];
 //当前是否正在加载 
  bool isLoading = false;
  //滚动控制器 用来实现上拉加载
  ScrollController controller = ScrollController();
  //添加监听
  addScrollListener() {
    controller.addListener(() {
      double fis =
          controller.position.maxScrollExtent - controller.position.pixels;
      //当前没有加载 且上拉一定距离并当列表高度不满屏幕高度时不执行加载更多
      if (fis < 300 && !isLoading && controller.position.maxScrollExtent != 0) {
        loadMore(loadMore: true);
      }
    });
  }

  scrollDispose() {
    controller.dispose();
  }

  initData() async {
    setBusy();
    await loadMore(loadMore: false);
  }

  loadMore({bool loadMore = false}) async {
    if (isLoading) {
      return;
    }
    isLoading = true;

    try {
    //获取当前model
      model=(await loadData(loadMore: loadMore)) as T;
      //获取当前List
      List<M> data = getList();
      if (data.isEmpty) {
        if (loadMore) {
          ToastUtil().showBottomToast("没有更多数据了");
        } else {
          lists.clear();
          setEmpty();
        }
      } else {
        if (loadMore) {
          lists .addAll(data);
          pageIndex++;
        } else {
          lists.clear();
          lists = List.of(data);
        }
        isLoading = false;
        setIdle();
      }
    } catch (e,s) {
      if (!loadMore) {
        lists.clear();
        setError(e, s);
      }
      ToastUtil().showCenterToast(e.toString());
    }
  }

  Future<T>? loadData({loadMore = false});


  List<M> getList();
}

2. View 的基类

class ProviderWidget<T extends BaseViewModel> extends StatefulWidget {
   // ChangeNotifierProvider需要的model
  final T model;
   // Consumer需要的builder
  final ValueWidgetBuilder<T> builder;
  final Function(T model) onModelReady;
  //Consumer需要的child 这里chil是指不需要notify的组件
  final Widget? child;
  final bool autoDispose;

  const ProviderWidget(
      {Key? key,
      required this.model,
      required this.onModelReady,
      required this.builder,
      this.child,
      this.autoDispose = false})
      : super(key: key);

  @override
  _ProviderWidgetState createState() => _ProviderWidgetState<T>();
}

class _ProviderWidgetState<T extends BaseViewModel>
    extends State<ProviderWidget<T>> {
  late final T model;

  @override
  void initState() {
    model = widget.model;
    widget.onModelReady(model);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (BuildContext context) => model,
      child: Consumer(
        builder: widget.builder,
        child: widget.child,
      ),
    );
  }
  @override
  void dispose() {
    super.dispose();
  }
}

3.model的定义

class UserModel {

  ///昵称
  String? nikeName;
  ///性别
  int? sex;
  ///年龄
  int? age;
  ///真实姓名
  String? realName;
  ///邮箱
  String? email;
  ///手机
  String? mobile;
  ///头像
  String? imageUrl;
  ///用户token
  String? userToken;
  ///学校姓名
  String? school;
  ///uid
  String? uid;
  ///签名
  String? signature;
  ///过期时间
  int? expirationDate;
    //构造方法
  UserModel({
    this.nikeName,
    this.sex,
    this.age,
    this.realName,
    this.email,
    this.mobile,
    this.imageUrl,
    this.userToken,
    this.school,
    this.uid,
    this.signature,
    this.expirationDate,
  });
    //将Model转成Json 的方法
  Map<String, dynamic> toJson() {
    Map<String, dynamic> data = {};
    data["realName"] = realName;
    data["nikeName"] = nikeName;
    data["sex"] = sex;
    data["age"] = age;
    data["email"] = email;
    data["mobile"] = mobile;
    data["imageUrl"] = imageUrl;
    data["userToken"] = userToken;
    data["school"] = school;
    data["uid"] = uid;
    data["signature"] = signature;
    data["expirationDate"] = expirationDate;
    return data;
  }
   //将Json转成Model的方法
  UserModel.fromJson(Map<String, dynamic> json) {
    realName = json["realName"];
    nikeName = json["nikeName"];
    sex = json["sex"];
    age = json["age"];
    email = json["email"];
    mobile = json["imageUrl"];
    email = json["imageUrl"];
    userToken = json["userToken"];
    school = json["school"];
    uid = json["uid"];
    signature = json["signature"];
    expirationDate = json["expirationDate"];
  }
}

4. Repository

我一般写的时候会把网络请求单独放在一个文件中,这样到时候修改接口也不至于动到其他地方...

 //定义一个User的网络访问文件,存储登录 登录需要用的方法
class UserRepository {
   Future<dynamic> login(Map<String, dynamic> param) async {
    Response<dynamic> response =
        await DioClient().doPost(Api.login, queryParameters: param);
    return response;
  }

   Future<void> logout() async {
    Response<dynamic> response = await DioClient().doGet(Api.logout);
  }
}

相关文章

网友评论

    本文标题:Flutter 学习 之 使用 Provider 实现 MVVM

    本文链接:https://www.haomeiwen.com/subject/rdbcsrtx.html