Flutter的VIPER架構(gòu)

Flutter項(xiàng)目模塊化架構(gòu)搭建這篇文章里有朋友對(duì)flutter的viper感興趣,搞了個(gè)demo出來。

其實(shí)viper也就是在mvp基礎(chǔ)上把m繼續(xù)拆分為i和e,把router也抽出來統(tǒng)一處理。
感覺最重要的思想是面向接口編程。
所以會(huì)有個(gè)protocol文件來定義該模塊的方法,讓各層實(shí)現(xiàn),協(xié)議接口想叫啥都行項(xiàng)目里統(tǒng)一就好。
在頁面開始先寫這個(gè)協(xié)議,然后各層implements實(shí)現(xiàn)協(xié)議,根據(jù)AS的爆紅提示直接點(diǎn)擊override方法,非常清晰。

V(view,頁面)
I(interactor,接口,后臺(tái)網(wǎng)絡(luò)請(qǐng)求)
P(presenter,業(yè)務(wù)邏輯處理、跳轉(zhuǎn)等)
E(entity,就是純數(shù)據(jù)模型,負(fù)責(zé)接口字段對(duì)應(yīng)而已)
R(router,對(duì)外跳轉(zhuǎn)接口,這個(gè)每個(gè)小業(yè)務(wù)不用再實(shí)現(xiàn)了,由整個(gè)模塊的Router負(fù)責(zé))

Don't bb, show me the code.

下面看下面這個(gè)很簡單的demo,1個(gè)頁面,2個(gè)模型,2個(gè)接口,3個(gè)展示數(shù)據(jù)用的Text。
我用到了getx做數(shù)據(jù)綁定,用到的地方我都有標(biāo)注釋,不想看刪掉就能更簡潔。

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);
  /// 獲取設(shè)置成功
  void p2vGetSettingDataSuccess();
  /// 獲取設(shè)置失敗
  void p2vGetSettingDataFail(Error error);
}

abstract class MineHomePresenterProtocol {

  // 這里數(shù)據(jù)持有有2種方式,一種是模型,一種是使用GetxController的StateMixin
  MineDetailEntity get detailEntity;
  MineSettingEntity get settingEntity;
  // 使用StateMixin
  MineSettingEntityController get settingController;

  /// 獲取詳情
  void v2pGetDetailData();
  /// 獲取設(shè)置
  void v2pGetSettingData();
  /// 獲取詳情成功
  void m2pGetDetailDataSuccess(MineDetailEntity detailEntity);
  /// 獲取詳情失敗
  void m2pGetDetailDataFail(Error error);
}

abstract class MineHomeInteractorProtocol {
  // 這里接口的實(shí)現(xiàn)有2種方式,一種是把成功和失敗結(jié)果分別返回到p層,一種是把Future直接返回到p層處理
  // 第一種方法會(huì)比較多,但是適合數(shù)據(jù)處理比較麻煩的接口,各有優(yōu)劣吧
  /// 獲取詳情
  void p2iGetDetailData();
  /// 獲取設(shè)置
  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 ? '開' : '關(guān)',
                style: TextStyle(
                  color: Colors.blue,
                ),
              );
            }),
            SizedBox(height: 50),
            // 使用StateMixin
            iPresenter.settingController.obx(
              (value) {
                return Text(
                  value?.noticeType == 1 ? '開' : '關(guān)',
                  style: TextStyle(
                    color: Colors.green,
                  ),
                );
              },
              onLoading: const Center(child: CircularProgressIndicator()),
              onEmpty: const Text('暫無數(shù)據(jù)'),
              onError: (error) {
                return Text(error ?? '未知錯(cuò)誤');
              },
            ),
          ],
        ),
      ),
    );
  }

  @override
  void p2vGetDetailDataFail(Error error) {
    // 彈窗提示,錯(cuò)誤展示
  }

  @override
  void p2vGetDetailDataSuccess() {
    // 如果用StatefulWidget可以setState
  }

  @override
  void p2vGetSettingDataFail(Error error) {
    // 彈窗提示,錯(cuò)誤展示
  }

  @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());

  // 構(gòu)造函數(shù)綁定v層i層
  MineHomePresenter(MineHomeViewProtocol view) {
    iView = view;
    iInteractor = MineHomeInteractor(this);
  }

  @override
  void v2pGetDetailData() {
    iInteractor.p2iGetDetailData();
  }

  @override
  void m2pGetDetailDataSuccess(MineDetailEntity detailEntity) {
    _detailObs.value = detailEntity;
    // 用getx監(jiān)聽更新就可以不用主動(dòng)告訴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監(jiān)聽更新就可以不用主動(dòng)告訴view層
      //iView.p2vGetSettingDataSuccess();
    }).catchError((error) {
      _settingController.change(null, status: RxStatus.error('獲取設(shè)置信息失敗'));
      // 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;

  // 構(gòu)造函數(shù)綁定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;
  /// 電話號(hào)碼
  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 {
  /// 通知開關(guān)
  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(),
  };
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容