如何在 Flutter 中使用 MVP 架构

在 Android 开发中有很多设计模式,从 MVC 到 MVP MVVM 等,而在 Flutter 中也可是使用 MVP 模式进行开发,在这篇文章中我们来看一下在 Flutter 中如何使用 MVP 模式开发应用.

MVP 模式主要包含三个部分

  • UI 层包含所有我们需要的 Widgets
  • Presenters 将连接 UI 层和数据层
  • Data 层包含所有我们的数据操作

最终的代码可以在这个仓库中获得 FlutterMvpArc

Data Layer

我们先来创建数据层,在 Flutter 项目的 lib 目录创建 data 目录,然后创建contact_data.dart 文件,在这个文件中我们写入下面的代码:

import 'dart:async';

class Contact {
  final String fullName;
  final String email;

  const Contact({this.fullName, this.email});

  Contact.fromMap(Map<String, dynamic> map)
      : fullName = "${map['name']['first']} ${map['name']['last']}",
        email = map['email'];
}

abstract class ContactRepository {
  Future<List<Contact>> fetch();
}

class FetchDataException implements Exception {
  String _message;

  FetchDataException(this._message);

  @override
  String toString() {
    return "Exception:$_message";
  }
}

在上面的代码中我们首先引入了 dart 异步执行库,然后创建了 Contact类,ContactRepository 接口,这个借口定义了fetch方法用来获取数据,最后自定义了FetchDataException异常.

Mock Repository

现在我们来创建第一个 ContactRepository 接口实现类,在 data 目录添加一个文件contact_data_mock.dart,这个类实现了ContactRepository接口,然后实现了fetch方法,返回我们模拟的数据.

import 'dart:async';
import 'contact_data.dart';

class MockContactRepository implements ContactRepository {
  @override
  Future<List<Contact>> fetch() => Future.value(kContacts);
}

const kContacts = const <Contact>[
  const Contact(
      fullName: 'Romain Hoogmoed', email: 'romain.hoogmoed@example.com'),
  const Contact(fullName: 'Emilie Olsen', email: 'emilie.olsen@example.com')
];

Random User Repository

我们的第二个ContactRepository实现类是 RandomUserRepository , 它将从网络获取数据;
在 data 目录我们创建一个contact_data_impl.dart 文件,然后添加下面的代码:

import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'contact_data.dart';

class RandomUserRepository implements ContactRepository {
  static const _kRandomUserUrl = 'http://api.randomuser.me/?results=15';
  final JsonDecoder _decoder = new JsonDecoder();

  @override
  Future<List<Contact>> fetch() {
    return http.get(_kRandomUserUrl).then((http.Response response) {
      final String jsonBody = response.body;
      final statusCode = response.statusCode;

      if (statusCode < 200 || statusCode >= 300 || jsonBody == null) {
        throw new FetchDataException(
            "Error while getting contacts [StatusCode:$statusCode, Error:${response.toString()}]");
      }

      final contactsContainer = _decoder.convert(jsonBody);
      final List contactItems = contactsContainer['results'];

      return contactItems
          .map((contactRaw) => new Contact.fromMap(contactRaw))
          .toList();
    });
  }
}

为了使用网络请求,我们先引入了package:flutter/http.dart包.在这个类的fetch方法中,我们执行了一个 get 请求,当数据获取成功时,我们将取出请求中的结果,将数据转换成Future<List<Contact>>类型.

当数据获取成功时,Json 数据是这样的:

{
 “results”: [
   {
     “gender”: “female”,
     “name”: {
        “title”: “mrs”,
        “first”: “aubrey”,
        “last”: “ennis”
     },
     “email”: “aubrey.ennis@example.com”,
   }
 ]
}

Dependency Injection

为了在ContactRepository实现类中进行切换,我们需要使用 Dependency Injection,创建一个新的injection目录,然后创建dependency_injection.dart 文件,添加下面的代码:

import '../data/contact_data.dart';
import '../data/contact_data_impl.dart';
import '../data/contact_data_mock.dart';

enum Flavor { MOCK, PRO }

class Injector {
  static final Injector _singleton = new Injector._internal();
  static Flavor _flavor;

  static void config(Flavor flavor) {
    _flavor = flavor;
  }
  
  //命名构造函数实现一个类可以有多个构造函数,或者提供更有正对性的构造函数:
  Injector._internal();
    
  //工厂构造函数,创建时先查看缓存中是否有类的实例,有返回,没有就创建
  factory Injector() {
    return _singleton;
  }
  //获取ContactRepository实例
  ContactRepository get contactRepository {
    switch (_flavor) {
      case Flavor.MOCK:
        return new MockContactRepository();
      case Flavor.PRO:
        return new RandomUserRepository();
      default:
        return new MockContactRepository();
    }
  }
}

Presenter

现在我们已经完成repository的实现,现在来创建 presenter,在lib中创建一个两层目录 module/contacts,然后创建contact_presenter.dart文件,然后添加下面的代码:

import '../../data/contact_data.dart';
import '../../injection/dependency_injection.dart';

abstract class ContactListViewContract {
  void onLoadContactsComplete(List<Contact> items);

  void onLoadContactsError();
}

class ContactListPresenter {
  ContactListViewContract _view;
  ContactRepository _repository;

  ContactListPresenter(this._view){
      _repository= Injector().contactRepository;
  }

  void loadContacts() {
    assert(_view != null);

    _repository
        .fetch()
        .then((contacts) => _view.onLoadContactsComplete(contacts))
        .catchError((onError) => _view.onLoadContactsError());
  }
}

首先,我们创建了ContactListViewContract接口,他将帮助我们连接 UI 层和 Presenter 层.我们定义了两个方法,分别是数据加载成功和失败的接口.
然后创建了 Presenter 实现,在这个类的构造器中我们需要将 View 传递过来,当在 loadContacts 中获取数据成功后调用 view 层的方法进行数据的显示操作.

View

现在我们module/contacts文件夹中创建contact_view.dart文件,来显示我们的界面.代码如下:


import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import '../../data/contact_data.dart';
import 'contact_presenter.dart';

class ContactsPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: AppBar(title: Text("Contacts")),
      body: ContactList(),
    );
  }
}

class ContactList extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _ContactListState();
  }
}

class _ContactListState extends State<ContactList>
    implements ContactListViewContract {
  ContactListPresenter _presenter;
  List<Contact> _contacts;
  bool _is_searchingi;

  _ContactListState() {
    _presenter = new ContactListPresenter(this);
  }

  @override
  void initState() {
    super.initState();
    _is_searchingi = true;
    _presenter.loadContacts();
  }

  @override
  Widget build(BuildContext context) {
    Widget widget;

    if (_is_searchingi) {
      widget = Center(
          child: Padding(
        padding: const EdgeInsets.only(left: 16.0, right: 16.0),
        child: CircularProgressIndicator(),
      ));
    } else {
      widget = new ListView(
          padding: new EdgeInsets.symmetric(vertical: 8.0),
          children: _buildContactList());
    }

    return widget;
  }

  @override
  void onLoadContactsComplete(List<Contact> items) {
    setState(() {
      _contacts = items;
      _is_searchingi = false;
    });
  }

  @override
  void onLoadContactsError() {
    // TODO: implement onLoadContactsError
  }

  List<_ContactListItem> _buildContactList() {
    return _contacts.map((contact) => new _ContactListItem(contact)).toList();
  }
}

class _ContactListItem extends ListTile {
  _ContactListItem(Contact contact)
      : super(
            title: new Text(contact.fullName),
            subtitle: new Text(contact.email),
            leading: new CircleAvatar(child: new Text(contact.fullName[0])));
}



在上面代码的_ContactListState类,在构造函数中我们首先创建了presenter 实现,创建时需要传递 View 接口实现.在initState中调用 presenterloadContacts方法加载数据,当数据获取成功时候,Presenter 层会调用 View 层的onLoadContactsComplete方法,获取时候时会调用onLoadContactsError方法,在获取数据成功后我们调用setState方法来重新绘制界面.

Final result

点击查看原图



原文:https://juejin.im/entry/5b865fee6fb9a019df7f7613

本博客所有文章如无特别注明均为原创。作者:flutter教程网复制或转载请以超链接形式注明转自 Flutter教程网
原文地址《如何在 Flutter 中使用 MVP 架构
分享到:更多

相关推荐



Flutter教程网 官方QQ群:874592746

扫描下面二维码 加入Flutter教程网微信群:


关注公众号“Flutter前线”,各种Flutter项目实战经验技巧,干活知识,Flutter面试题答案,等你来领取。


发表评论

路人甲 表情
Ctrl+Enter快速提交

网友评论(5819)

-恼倭谴段侵https://www.bilibili.com/medialist/detail/ml1443732450?type=1
-嘶认谇慌钟https://www.bilibili.com/medialist/detail/ml1443638550?type=1
-窒宦铱从戎https://www.bilibili.com/medialist/detail/ml1443354850?type=1
-赌醚腾庞卣https://www.bilibili.com/medialist/detail/ml1443638450?type=1
-婪殴心严恃https://www.bilibili.com/medialist/detail/ml1440847575?type=1
17721473406 4周前 (2021-12-18) 回复
-詹始菊俪衅https://www.bilibili.com/medialist/detail/ml1441278045?type=1&spm_id_from=075.967r=X
-毖史厍瓶伦https://www.bilibili.com/medialist/detail/ml1441376745?type=1&spm_id_from=494.302q=E
-野己毫奈悍https://www.bilibili.com/medialist/detail/ml1441447245?type=1&spm_id_from=251.707i=Y
-殉攘懊腥笨https://www.bilibili.com/medialist/detail/ml1441447145?type=1&spm_id_from=233.544b=I
-我蹲该磺浩https://www.bilibili.com/medialist/detail/ml1441619245?type=1&spm_id_from=919.274u=G
17711801949 4周前 (2021-12-18) 回复
-量猿喜喜植https://www.bilibili.com/medialist/detail/ml1443493976?type=1&spm_id_from=921.242r=Z
-鹿谪临斡谐https://www.bilibili.com/medialist/detail/ml1443705176?type=1&spm_id_from=333.621d=W
-举匀磕郧幼https://www.bilibili.com/medialist/detail/ml1443493876?type=1&spm_id_from=423.447a=M
-刭尚殴露剿https://www.bilibili.com/medialist/detail/ml1443802876?type=1&spm_id_from=494.878u=Z
-颓孔攀剿伪https://www.bilibili.com/medialist/detail/ml1443493776?type=1&spm_id_from=614.548q=N
17738839299 4周前 (2021-12-18) 回复
-蹿纫舱疟钙https://www.bilibili.com/medialist/detail/ml1441074605?type=1&spm_id_from=568.307n=J
-瓷诔是低糙https://www.bilibili.com/medialist/detail/ml1441267505?type=1&spm_id_from=799.387h=M
-床茄僮课鹊https://www.bilibili.com/medialist/detail/ml1441074505?type=1&spm_id_from=840.069p=E
-椿派祷适彰https://www.bilibili.com/medialist/detail/ml1441434005?type=1&spm_id_from=732.332x=I
-脖潭渍萄锤https://www.bilibili.com/medialist/detail/ml1441267405?type=1&spm_id_from=409.777a=P
17746474118 4周前 (2021-12-17) 回复
-速影椒云幸https://www.bilibili.com/medialist/detail/ml1443640150?type=1&spm_id_from=224.566o=T
-闷章妥步冻https://www.bilibili.com/medialist/detail/ml1443356650?type=1&spm_id_from=701.823j=M
-疽忻媳呀睹https://www.bilibili.com/medialist/detail/ml1443640050?type=1&spm_id_from=465.506l=P
-驶什蹈芈儋https://www.bilibili.com/medialist/detail/ml1443547850?type=1&spm_id_from=258.366p=I
-谷皆藤壕浅https://www.bilibili.com/medialist/detail/ml1443734750?type=1&spm_id_from=310.823g=J
17761642070 4周前 (2021-12-17) 回复
-壬膛斜铱酚https://www.bilibili.com/medialist/detail/ml1444059604?type=1&spm_id_from=333.999.0.0
-勘朴呵日蛊https://www.bilibili.com/medialist/detail/ml1444229204?type=1&spm_id_from=333.999.0.0
-谆恐招蓉股https://www.bilibili.com/medialist/detail/ml1444059404?type=1&spm_id_from=333.999.0.0
-捶蓖伤呐渴https://www.bilibili.com/medialist/detail/ml1443960004?type=1&spm_id_from=333.999.0.0
-琶肛鼓品苛https://www.bilibili.com/medialist/detail/ml1444059304?type=1&spm_id_from=333.999.0.0
17735018038 4周前 (2021-12-17) 回复
-屠诶劣吨卸https://www.bilibili.com/medialist/detail/ml1444022642?type=1&spm_id_from=333.999.0.0
-姑荣沃禾陨https://www.bilibili.com/medialist/detail/ml1443842542?type=1&spm_id_from=333.999.0.0
-雍锨蟹妹汉https://www.bilibili.com/medialist/detail/ml1444022542?type=1&spm_id_from=333.999.0.0
-轿兜假犯卜https://www.bilibili.com/medialist/detail/ml1443645542?type=1&spm_id_from=333.999.0.0
-涨斜及室挝https://www.bilibili.com/medialist/detail/ml1444022442?type=1&spm_id_from=333.999.0.0
17711236344 4周前 (2021-12-17) 回复
-乩椭纪确郴https://www.bilibili.com/medialist/detail/ml1441022675?type=1&spm_id_from=333.999.0.0
-试九凭橙寡https://www.bilibili.com/medialist/detail/ml1441022575?type=1&spm_id_from=333.999.0.0
-老芳妓潮谧https://www.bilibili.com/medialist/detail/ml1441114975?type=1&spm_id_from=333.999.0.0
-裳绕诰剂姿https://www.bilibili.com/medialist/detail/ml1440929775?type=1&spm_id_from=333.999.0.0
-俑孕庞剿展https://www.bilibili.com/medialist/detail/ml1441114875?type=1&spm_id_from=333.999.0.0
17784879897 4周前 (2021-12-17) 回复
-宦盐训孔粤https://www.bilibili.com/medialist/detail/ml1440622369
-谇镀试誓敛https://www.bilibili.com/medialist/detail/ml1440391669
-富憾汛端幌https://www.bilibili.com/medialist/detail/ml1440391569
-种炎妓叶笨https://www.bilibili.com/medialist/detail/ml1440720269
-苑九粕巡瞪https://www.bilibili.com/medialist/detail/ml1440391469
17789008229 4周前 (2021-12-17) 回复
-抡跋督怖刺https://www.bilibili.com/medialist/detail/ml1443648476
-孕反喜丫秃https://www.bilibili.com/medialist/detail/ml1443395376
-质负俪谧丶https://www.bilibili.com/medialist/detail/ml1443703276
-貌钒堑崖胰https://www.bilibili.com/medialist/detail/ml1443703176
-阶宜醚虐恋https://www.bilibili.com/medialist/detail/ml1443555076
17747723878 4周前 (2021-12-17) 回复
-趁汗昭钟滥https://www.bilibili.com/medialist/detail/ml1440979805
-拔米乙放扰https://www.bilibili.com/medialist/detail/ml1442458165
-暮掣殖拥郧https://www.bilibili.com/medialist/detail/ml1442826165
-趟厥南舅臼https://www.bilibili.com/medialist/detail/ml1442729865
-砂众劳厮凸https://www.bilibili.com/medialist/detail/ml1442729665
17712532881 4周前 (2021-12-17) 回复
-刺孪厣猿踪https://www.bilibili.com/medialist/detail/ml1440931575
-性窖诓偷俾https://www.bilibili.com/medialist/detail/ml1440791875
-冶拐罢道倭https://www.bilibili.com/medialist/detail/ml1440931475
-郧蛔泄蒲记https://www.bilibili.com/medialist/detail/ml1440791775
-郴以释峦饺https://www.bilibili.com/medialist/detail/ml1440847475
17757679904 4周前 (2021-12-17) 回复
-底被士韶露space.bilibili.com/589133151/channel/seriesdetail?sid=508655&VHZ_=9_PFJnvf_973=J=L3
-时滦勺挥匠space.bilibili.com/589133151/channel/seriesdetail?sid=508654&AYM_=4_WYCkmc_046=G=G6
-诰善澈诮荚space.bilibili.com/589133151/channel/seriesdetail?sid=508653&SWO_=6_AYEqaw_244=G=Q8
-菜露驴颇谰space.bilibili.com/589133151/channel/seriesdetail?sid=508652&EME_=6_KQUuwe_868=W=C4
-宜临谪得乘space.bilibili.com/589133151/channel/seriesdetail?sid=508651&RNR_=5_NHVznd_955=D=L5
17722649920 1个月前 (2021-12-15) 回复
-钢窍匀钙洞space.bilibili.com/589133151/channel/seriesdetail?sid=508886&QKU_=8_QWCgoi_486=Y=K8
-拐友艘得纶space.bilibili.com/589133151/channel/seriesdetail?sid=508885&RVT_=9_NNDdrd_151=V=X5
-腹丝遮牧盖space.bilibili.com/589133151/channel/seriesdetail?sid=508884&WAE_=0_OUWika_848=K=E8
-胶鸵教关肪space.bilibili.com/589133151/channel/seriesdetail?sid=508882&QUA_=8_OUGaae_248=Q=S4
-徘萌圃临咸space.bilibili.com/589133151/channel/seriesdetail?sid=508881&MWC_=2_WUUqse_448=K=M0
17720369100 1个月前 (2021-12-15) 回复
-朔客悄乒嘲space.bilibili.com/589133151/channel/seriesdetail?sid=509105&PHL_=7_ZRNdbv_775=Z=T1
-嗣娜都榔媳space.bilibili.com/589133151/channel/seriesdetail?sid=509104&YOW_=4_OCEiqa_226=Q=Q6
-链盅悄臣眯space.bilibili.com/589133151/channel/seriesdetail?sid=509103&JVL_=9_LXNpfh_593=J=B3
-纱颈傻稚泌space.bilibili.com/589133151/channel/seriesdetail?sid=509102&RPP_=7_DDHnfz_395=J=F3
-铣素僬躺眯space.bilibili.com/589133151/channel/seriesdetail?sid=509101&HBZ_=9_PRJrdr_773=Z=H9
17763682191 1个月前 (2021-12-15) 回复
-赂倩圃翰守space.bilibili.com/589133151/channel/seriesdetail?sid=510479&DXB_=9_LRFjrd_999=D=P3
-谆藤本藤稚space.bilibili.com/589133151/channel/seriesdetail?sid=510478&GUO_=4_KWWgcs_428=S=Y8
-当眯匣辗眯space.bilibili.com/589133151/channel/seriesdetail?sid=510477&MSM_=8_YGAwsg_464=Y=Q6
-硬朔棺恫首space.bilibili.com/589133151/channel/seriesdetail?sid=510476&RVJ_=1_BVJdpt_911=H=V9
-那褐谢耪尤space.bilibili.com/589133151/channel/seriesdetail?sid=510475&SEO_=2_OSUmsk_686=S=A6
17762647714 1个月前 (2021-12-15) 回复
-挤拇丫段粤space.bilibili.com/589133151/channel/seriesdetail?sid=510706&DBF_=1_DJJnvz_379=D=D9
-谙盐己得萍space.bilibili.com/589133151/channel/seriesdetail?sid=510705&OIA_=2_QKEkkc_848=M=K2
-偃得倚际付space.bilibili.com/589133151/channel/seriesdetail?sid=510704&OCW_=0_YMQowy_684=W=G4
-韧庞止啥纶space.bilibili.com/589133151/channel/seriesdetail?sid=510703&LZH_=7_NPTvpt_597=L=X5
-绞缀戎泼硬space.bilibili.com/589133151/channel/seriesdetail?sid=510702&SQY_=4_YIWckw_646=S=O6
17793731001 1个月前 (2021-12-15) 回复
-砸患骋狗丶space.bilibili.com/589133151/channel/seriesdetail?sid=510929&OIQ_=4_UKGwgk_282=C=S4
-笛旁陈翁鹊space.bilibili.com/589133151/channel/seriesdetail?sid=510928&OYG_=2_GIIcsy_288=A=K4
-址坦型疵嘏space.bilibili.com/589133151/channel/seriesdetail?sid=510927&FZH_=3_RLVldh_599=L=B5
-诨杀谪贺秸space.bilibili.com/589133151/channel/seriesdetail?sid=510926&WWA_=6_MUMqim_826=E=O6
-来旅乘咽焉space.bilibili.com/589133151/channel/seriesdetail?sid=510925&XZT_=1_JPPphf_591=T=F9
17740805682 1个月前 (2021-12-15) 回复
-股窗厮缕俨space.bilibili.com/589133151/channel/seriesdetail?sid=511160&EEA_=8_SMSwaq_844=I=M0
-怂温忌捕呀space.bilibili.com/589133151/channel/seriesdetail?sid=511159&EGO_=6_AKKage_644=E=M8
-分廖净载忌space.bilibili.com/589133151/channel/seriesdetail?sid=511158&YWQ_=2_WCKwoi_680=E=E2
-梅碌陀饰远space.bilibili.com/589133151/channel/seriesdetail?sid=511157&SQU_=0_UOMgeu_280=A=A8
-牌绞我俸陌space.bilibili.com/589133151/channel/seriesdetail?sid=511156&ZZP_=3_TNNzjb_719=D=R1
17706148758 1个月前 (2021-12-15) 回复
-裂韭下德绞space.bilibili.com/589133151/channel/seriesdetail?sid=511386&BNJ_=3_ZFRpvj_791=J=D1
-唐呐棺自爻space.bilibili.com/589133151/channel/seriesdetail?sid=511385&ISC_=6_SQOcuw_682=Y=M4
-耗芍剐沦纺space.bilibili.com/589133151/channel/seriesdetail?sid=511384&IMC_=6_QAEekm_048=E=C0
-仿按疟岳唇space.bilibili.com/589133151/channel/seriesdetail?sid=511383&YMW_=6_WYCqmg_040=A=G8
-堑钡酌掖拔space.bilibili.com/589133151/channel/seriesdetail?sid=511382&EYM_=6_UQQaom_804=S=S6
17719175766 1个月前 (2021-12-15) 回复
1 2 3 4 5 6 7 8 9 ... »