美文网首页
Flutter的VIPER架构

Flutter的VIPER架构

作者: oldSix_Zhu | 来源:发表于2023-09-04 15:32 被阅读0次

    Flutter项目模块化架构搭建这篇文章里有朋友对flutter的viper感兴趣,搞了个demo出来。

    其实viper也就是在mvp基础上把m继续拆分为i和e,把router也抽出来统一处理。
    感觉最重要的思想是面向接口编程。
    所以会有个protocol文件来定义该模块的方法,让各层实现,协议接口想叫啥都行项目里统一就好。
    在页面开始先写这个协议,然后各层implements实现协议,根据AS的爆红提示直接点击override方法,非常清晰。

    V(view,页面)
    I(interactor,接口,后台网络请求)
    P(presenter,业务逻辑处理、跳转等)
    E(entity,就是纯数据模型,负责接口字段对应而已)
    R(router,对外跳转接口,这个每个小业务不用再实现了,由整个模块的Router负责)

    Don't bb, show me the code.

    下面看下面这个很简单的demo,1个页面,2个模型,2个接口,3个展示数据用的Text。
    我用到了getx做数据绑定,用到的地方我都有标注释,不想看删掉就能更简洁。

    mine_home_protocol.dart

    import 'model/mine_detail_entity.dart';
    import 'model/mine_setting_entity.dart';
    import 'package:get/get.dart';
    
    // 使用StateMixin
    class MineSettingEntityController extends GetxController with StateMixin<MineSettingEntity> {
    
    }
    
    abstract class MineHomeViewProtocol {
      /// 获取详情成功
      void p2vGetDetailDataSuccess();
      /// 获取详情失败
      void p2vGetDetailDataFail(Error error);
      /// 获取设置成功
      void p2vGetSettingDataSuccess();
      /// 获取设置失败
      void p2vGetSettingDataFail(Error error);
    }
    
    abstract class MineHomePresenterProtocol {
    
      // 这里数据持有有2种方式,一种是模型,一种是使用GetxController的StateMixin
      MineDetailEntity get detailEntity;
      MineSettingEntity get settingEntity;
      // 使用StateMixin
      MineSettingEntityController get settingController;
    
      /// 获取详情
      void v2pGetDetailData();
      /// 获取设置
      void v2pGetSettingData();
      /// 获取详情成功
      void m2pGetDetailDataSuccess(MineDetailEntity detailEntity);
      /// 获取详情失败
      void m2pGetDetailDataFail(Error error);
    }
    
    abstract class MineHomeInteractorProtocol {
      // 这里接口的实现有2种方式,一种是把成功和失败结果分别返回到p层,一种是把Future直接返回到p层处理
      // 第一种方法会比较多,但是适合数据处理比较麻烦的接口,各有优劣吧
      /// 获取详情
      void p2iGetDetailData();
      /// 获取设置
      Future<Map<String, dynamic>> p2iGetSettingData();
    }
    

    mine_home_page.dart

    import 'package:flutter/material.dart';
    import '../mine_home_protocol.dart';
    import '../presenter/mine_home_presenter.dart';
    import 'package:get/get.dart';
    
    
    class MineHomePage extends StatelessWidget implements MineHomeViewProtocol {
    
      late MineHomePresenterProtocol iPresenter;
    
      @override
      Widget build(BuildContext context) {
        // 入口绑定p层,如果用StatefulWidget在initState中做绑定
        iPresenter = MineHomePresenter(this);
        iPresenter.v2pGetDetailData();
        iPresenter.v2pGetSettingData();
    
        return Scaffold(
          appBar: AppBar(
            title: Text("我的"),
          ),
          body: Center(
            child: Column(
              children: [
                SizedBox(height: 100),
                Obx(() {
                  return Text(iPresenter.detailEntity.accountName ?? '- -');
                }),
                SizedBox(height: 50),
                Obx(() {
                  return Text(
                    iPresenter.settingEntity.noticeType == 1 ? '开' : '关',
                    style: TextStyle(
                      color: Colors.blue,
                    ),
                  );
                }),
                SizedBox(height: 50),
                // 使用StateMixin
                iPresenter.settingController.obx(
                  (value) {
                    return Text(
                      value?.noticeType == 1 ? '开' : '关',
                      style: TextStyle(
                        color: Colors.green,
                      ),
                    );
                  },
                  onLoading: const Center(child: CircularProgressIndicator()),
                  onEmpty: const Text('暂无数据'),
                  onError: (error) {
                    return Text(error ?? '未知错误');
                  },
                ),
              ],
            ),
          ),
        );
      }
    
      @override
      void p2vGetDetailDataFail(Error error) {
        // 弹窗提示,错误展示
      }
    
      @override
      void p2vGetDetailDataSuccess() {
        // 如果用StatefulWidget可以setState
      }
    
      @override
      void p2vGetSettingDataFail(Error error) {
        // 弹窗提示,错误展示
      }
    
      @override
      void p2vGetSettingDataSuccess() {
        // 如果用StatefulWidget可以setState
      }
    
    }
    

    mine_home_presenter.dart

    import '../mine_home_protocol.dart';
    import '../model/mine_home_interactor.dart';
    import '../model/mine_setting_entity.dart';
    import '../model/mine_detail_entity.dart';
    import 'package:get/get.dart';
    
    class MineHomePresenter implements MineHomePresenterProtocol {
    
      late MineHomeInteractorProtocol iInteractor;
      late MineHomeViewProtocol iView;
    
      final _detailObs = MineDetailEntity().obs;
      final _settingObs = MineSettingEntity().obs;
      // 使用StateMixin
      final MineSettingEntityController _settingController = Get.put(MineSettingEntityController());
    
      // 构造函数绑定v层i层
      MineHomePresenter(MineHomeViewProtocol view) {
        iView = view;
        iInteractor = MineHomeInteractor(this);
      }
    
      @override
      void v2pGetDetailData() {
        iInteractor.p2iGetDetailData();
      }
    
      @override
      void m2pGetDetailDataSuccess(MineDetailEntity detailEntity) {
        _detailObs.value = detailEntity;
        // 用getx监听更新就可以不用主动告诉view层
        //iView.p2vGetDetailDataSuccess();
      }
    
      @override
      void m2pGetDetailDataFail(Error error) {
        iView.p2vGetDetailDataFail(error);
      }
    
      @override
      void v2pGetSettingData() {
        _settingController.change(null, status: RxStatus.loading());
        iInteractor.p2iGetSettingData().then((value) {
          MineSettingEntity settingEntity = MineSettingEntity.fromJson(value);
          _settingObs.value = settingEntity;
          // 使用StateMixin
          _settingController.change(settingEntity, status: RxStatus.success());
          // 用getx监听更新就可以不用主动告诉view层
          //iView.p2vGetSettingDataSuccess();
        }).catchError((error) {
          _settingController.change(null, status: RxStatus.error('获取设置信息失败'));
          // iView.p2vGetSettingDataFail(error);
        });
      }
    
      @override
      MineDetailEntity get detailEntity => _detailObs.value;
    
      @override
      MineSettingEntity get settingEntity => _settingObs.value;
    
      @override
      MineSettingEntityController get settingController => _settingController;
    
    }
    

    mine_home_interactor.dart

    import 'mine_detail_entity.dart';
    import '../mine_home_protocol.dart';
    
    class MineHomeInteractor implements MineHomeInteractorProtocol {
    
      late MineHomePresenterProtocol iPresenter;
    
      // 构造函数绑定p层
      MineHomeInteractor(this.iPresenter);
    
      @override
      void p2iGetDetailData() {
        Future.delayed(Duration(seconds: 3),(){
          var map = <String, dynamic>{};
          map["accountId"] = '111111';
          map['accountPhone'] = '18812345678';
          map["accountGender"] = 1;
          map["accountName"] = '哈哈哈';
          MineDetailEntity detailEntity = MineDetailEntity.fromJson(map);
          iPresenter.m2pGetDetailDataSuccess(detailEntity);
        });
      }
    
      @override
      Future<Map<String, dynamic>> p2iGetSettingData() async {
        var map = <String, dynamic>{};
        await Future.delayed(Duration(seconds: 3),(){
          map["noticeType"] = 1;
          map["languageType"] = 1;
          map["themeType"] = 1;
        });
        return map;
      }
    }
    

    mine_detail_entity.dart

    
    class MineDetailEntity {
      /// id
      String? accountId;
      /// 电话号码
      String? accountPhone;
      /// 性别
      int? accountGender;
      /// 姓名
      String? accountName;
    
      MineDetailEntity({
        this.accountId,
        this.accountPhone,
        this.accountGender,
        this.accountName,
      });
    
      MineDetailEntity.fromJson(Map<String, dynamic> json) {
        accountId = json['accountId'];
        accountPhone = json['accountPhone'];
        accountGender = json['accountGender'];
        accountName = json['accountName'];
      }
      
      Map<String, dynamic> toJson() {
        var map = <String, dynamic>{};
        map["accountId"] = accountId;
        map['accountPhone'] = accountPhone;
        map["accountGender"] = accountGender;
        map["accountName"] = accountName;
        return map;
      }
    }
    

    mine_setting_entity.dart

    class MineSettingEntity {
      /// 通知开关
      int? noticeType;
      /// 语言
      int? languageType;
      /// 主题
      int? themeType;
    
      MineSettingEntity({
        this.noticeType,
        this.languageType,
        this.themeType,
      });
      
      MineSettingEntity.fromJson(Map<String, dynamic> json) {
        noticeType = json['noticeType'];
        languageType = json['languageType'];
        themeType = json['themeType'];
      }
    
      Map<String, dynamic> toJson() {
        var map = <String, dynamic>{};
        map["noticeType"] = noticeType;
        map["languageType"] = languageType;
        map["themeType"] = themeType;
        return map;
      }
    }
    

    mine_router.dart

    import 'package:flutter/cupertino.dart';
    import 'home/view/mine_home_page.dart';
    
    class MineRouter {
      static const ROUTE_MINE_HOME = '/demo_mine/mine_home';
    
      static Map<String, WidgetBuilder> routes = {
        ROUTE_MINE_HOME : (context) => MineHomePage(),
      };
    }
    

    相关文章

      网友评论

          本文标题:Flutter的VIPER架构

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