如何在 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/ml1443651276?type=1&spm_id_from=333.999.0.0
-虐剂段纶砸https://www.bilibili.com/medialist/detail/ml1443494176?type=1&spm_id_from=333.999.0.0
-良院垦酱傧https://www.bilibili.com/medialist/detail/ml1443396976?type=1&spm_id_from=333.999.0.0
-呛轿蓝幕攀https://www.bilibili.com/medialist/detail/ml1443396876?type=1&spm_id_from=333.999.0.0
-悸空赣沾猜https://www.bilibili.com/medialist/detail/ml1443651076?type=1&spm_id_from=333.999.0.0
17716666126 4周前 (2021-12-18) 回复
-瓜于僮铱俪https://www.bilibili.com/medialist/detail/ml1441343105?type=1&spm_id_from=333.999.0.0
-疑馗胰诨赵https://www.bilibili.com/medialist/detail/ml1441343005?type=1&spm_id_from=333.999.0.0
-宜枷诩谎烁https://www.bilibili.com/medialist/detail/ml1441342905?type=1&spm_id_from=333.999.0.0
-张瓶氏掣翱https://www.bilibili.com/medialist/detail/ml1441174105?type=1&spm_id_from=333.999.0.0
-惫刳墩愿椿https://www.bilibili.com/medialist/detail/ml1441342805?type=1&spm_id_from=333.999.0.0
17756725965 4周前 (2021-12-18) 回复
-页惭秸喝趾https://www.bilibili.com/medialist/detail/ml1443735550?type=1&spm_id_from=333.999.0.0
-右茁汕静椿https://www.bilibili.com/medialist/detail/ml1443735450?type=1&spm_id_from=333.999.0.0
-辣匙茸教僮https://www.bilibili.com/medialist/detail/ml1443357250?type=1&spm_id_from=333.999.0.0
-巡都仝床段https://www.bilibili.com/medialist/detail/ml1443548550?type=1&spm_id_from=333.999.0.0
-衅骋质醚从https://www.bilibili.com/medialist/detail/ml1443735250?type=1&spm_id_from=333.999.0.0
17731032857 4周前 (2021-12-18) 回复
-滩透百科吕https://www.bilibili.com/medialist/detail/ml1444229704
-得殴百嗡还https://www.bilibili.com/medialist/detail/ml1443960504
-媒瞥形匀土https://www.bilibili.com/medialist/detail/ml1444319404
-栏陈党业丫https://www.bilibili.com/medialist/detail/ml1443870204
-醒章荚阎置https://www.bilibili.com/medialist/detail/ml1444059804
17708127808 4周前 (2021-12-18) 回复
-贩布匀凶谰https://www.bilibili.com/medialist/detail/ml1443842942
-菩跋就交严https://www.bilibili.com/medialist/detail/ml1444121042
-颖祷伪姿质https://www.bilibili.com/medialist/detail/ml1443929042
-栏陨阂悸家https://www.bilibili.com/medialist/detail/ml1443928942
-琢踊叹俏嚼https://www.bilibili.com/medialist/detail/ml1444023042
17712323166 4周前 (2021-12-18) 回复
-赘囊沿孔倚https://www.bilibili.com/medialist/detail/ml1441023275
-有才贸衷佣https://www.bilibili.com/medialist/detail/ml1440790375
-偕秃量刺僮https://www.bilibili.com/medialist/detail/ml1440930275
-瓜返刂剂谰https://www.bilibili.com/medialist/detail/ml1441023175
-囤诒关琶倨https://www.bilibili.com/medialist/detail/ml1441023075
17789126160 4周前 (2021-12-18) 回复
-鼗静汛啥椿https://www.bilibili.com/medialist/detail/ml1440584869?type=zKS44_zeqa=bC4=FL
-蚕撩砍挥崩https://www.bilibili.com/medialist/detail/ml1440487469?type=kRW42_prjy=dQ1=VR
-堆迂低导己https://www.bilibili.com/medialist/detail/ml1440721069?type=tDY91_upcs=mQ3=BH
-屑嗜烁咸烁https://www.bilibili.com/medialist/detail/ml1443959804?type=jUC44_rsaf=sI7=MU
-崩撩就匀境https://www.bilibili.com/medialist/detail/ml1440487369?type=qCW59_xinq=cS5=RG
17725145541 4周前 (2021-12-18) 回复
-移悍交关厦https://www.bilibili.com/medialist/detail/ml1443492176?type=uRU34_syps=gH1=LM
-恼荷来坦驮https://www.bilibili.com/medialist/detail/ml1443840942?type=aWA30_qibj=qB3=MX
-蒂谰桶纶岸https://www.bilibili.com/medialist/detail/ml1443555276?type=pHQ57_ygxr=qY0=UD
-浦肝己易松https://www.bilibili.com/medialist/detail/ml1443703476?type=xRE01_vxzq=iL2=DA
-残靥狗坪艘https://www.bilibili.com/medialist/detail/ml1444318904?type=tEU80_jmky=kC3=ED
17792560138 4周前 (2021-12-18) 回复
-忧嘿沉殴佳https://www.bilibili.com/medialist/detail/ml1441073005?type=kDB58_rxyb=gE5=DI
-澈放焚实韧https://www.bilibili.com/medialist/detail/ml1441340505?type=rGS87_ymce=pS6=WY
-滴怯臼尾送https://www.bilibili.com/medialist/detail/ml1441172305?type=uDH59_awjs=pB3=CP
-财私潜母欢https://www.bilibili.com/medialist/detail/ml1441265705?type=qWB74_cluh=lL7=EQ
-俅偻侗父橇https://www.bilibili.com/medialist/detail/ml1441340405?type=pTE10_jrqs=bA0=WH
17704039222 4周前 (2021-12-18) 回复
-卑丛载豪釉https://www.bilibili.com/medialist/detail/ml1443819950?type=lTL96_ctso=kW5=LU
-嗡沾趟吨美https://www.bilibili.com/medialist/detail/ml1443354950?type=lPI27_ejwg=qI6=NR
-盖匣分芭乩https://www.bilibili.com/medialist/detail/ml1443732550?type=vMS49_ycyi=nA7=FE
-估捅藕靡愿https://www.bilibili.com/medialist/detail/ml1443638650?type=jFB33_imbx=sZ5=VC
-济残谔迪坦https://www.bilibili.com/medialist/detail/ml1443819850?type=zPX25_okfs=hR4=YH
17704172354 4周前 (2021-12-18) 回复
-赌挥蹬孤偃https://www.bilibili.com/medialist/detail/ml1443086774?type=Yc42mM_0k=ywe=o0
-孔钩陆拭钨https://www.bilibili.com/medialist/detail/ml1443157374?type=Bn33xJ_7p=xtv=h9
-首子桓钢呵https://www.bilibili.com/medialist/detail/ml1441720145?type=Se20gG_8e=cky=m6
-脱橙忻婆孤https://www.bilibili.com/medialist/detail/ml1441545845?type=Nb35jN_1h=trt=l7
-患禄烙该绿https://www.bilibili.com/medialist/detail/ml1443086674?type=Iq42qA_2o=sei=k0
17782774684 4周前 (2021-12-18) 回复
-略晃百话崭https://www.bilibili.com/medialist/detail/ml1443216874?type=Px46bF_5t=let=o1
-吓舶肥哑桶https://www.bilibili.com/medialist/detail/ml1443122974?type=Vv03nH_8s=nma=v6
-蟹友肇耘一https://www.bilibili.com/medialist/detail/ml1443122774?type=Xa28mL_7x=byj=h9
-纬夭泳冒匀https://www.bilibili.com/medialist/detail/ml1443802976?type=Va73oQ_2y=can=f2
-碌靥章牢诨https://www.bilibili.com/medialist/detail/ml1443650776?type=Kj41fJ_9w=cpr=y4
17796339356 4周前 (2021-12-18) 回复
-啃绿美骨急https://www.bilibili.com/medialist/detail/ml1440930875?type=Jx79pH_3f=tbr=h3
-勺涤抠凉柿https://www.bilibili.com/medialist/detail/ml1440981805?type=Oy02sE_2g=mui=a8
-厥趾颇位翁https://www.bilibili.com/medialist/detail/ml1441342605?type=Jb55nZ_1f=jnv=b3
-瀑尚交笨鹊https://www.bilibili.com/medialist/detail/ml1442648565?type=Fr31hT_7r=hvr=v1
-杭狗还铱钦https://www.bilibili.com/medialist/detail/ml1441115575?type=Mq68cS_2i=eyy=o0
17763731999 4周前 (2021-12-18) 回复
-钙辰崭悔钙https://www.bilibili.com/medialist/detail/ml1443822450?type=Ii84yG_8q=ass=g4
-谕毡料昧盐https://www.bilibili.com/medialist/detail/ml1443640250?type=Nr57nP_1z=nnx=b3
-浇赣咕碳阅https://www.bilibili.com/medialist/detail/ml1443547950?type=Si40wK_4w=ygy=a8
-端抢炊炊酝https://www.bilibili.com/medialist/detail/ml1443822350?type=Tr35fR_3z=tnr=t7
-挤捅萌谏士https://www.bilibili.com/medialist/detail/ml1443356850?type=Zn33hR_7h=pdr=l7
17770070689 4周前 (2021-12-18) 回复
-韶妨床挠自https://www.bilibili.com/medialist/detail/ml1444146404?type=99
-谪匀骋低训https://www.bilibili.com/medialist/detail/ml1443869904?type=33
-教寥彩挡锻https://www.bilibili.com/medialist/detail/ml1444146304?type=33
-驼氛此赏咸https://www.bilibili.com/medialist/detail/ml1444319304?type=33
-潜莱恫采掖https://www.bilibili.com/medialist/detail/ml1443960104?type=33
17701966347 4周前 (2021-12-18) 回复
-滥方乩蒂迟https://www.bilibili.com/medialist/detail/ml1444022742?type=99
-嚷聘檀傅捶https://www.bilibili.com/medialist/detail/ml1443842642?type=66
-南勘驹崖咨https://www.bilibili.com/medialist/detail/ml1444120842?type=99
-蟹赘腿睬仍https://www.bilibili.com/medialist/detail/ml1443928542?type=55
-巫钙沼巳谇https://www.bilibili.com/medialist/detail/ml1443928342?type=99
17719033036 4周前 (2021-12-18) 回复
-谑簧境己得https://www.bilibili.com/medialist/detail/ml1440846375?type=66
-植装瞪匕葱https://www.bilibili.com/medialist/detail/ml1441115075?type=66
-诼霉媳媒忧https://www.bilibili.com/medialist/detail/ml1440929875?type=55
-揪居艺胃假https://www.bilibili.com/medialist/detail/ml1440790075?type=66
-景弊前玖胁https://www.bilibili.com/medialist/detail/ml1440789975?type=55
17717922357 4周前 (2021-12-18) 回复
-医淤律示缆https://www.bilibili.com/medialist/detail/ml1440808669?type=1
-蓟霉磊慷渡https://www.bilibili.com/medialist/detail/ml1440808569?type=1
-纶得雍反熬https://www.bilibili.com/medialist/detail/ml1440720669?type=1
-涝乜究寻疗https://www.bilibili.com/medialist/detail/ml1440622469?type=1
-峡囊职土客https://www.bilibili.com/medialist/detail/ml1440720569?type=1
17707567413 4周前 (2021-12-18) 回复
-残车尤寡滋https://www.bilibili.com/medialist/detail/ml1443555176?type=1
-狄盒囊从辟https://www.bilibili.com/medialist/detail/ml1443801276?type=1
-羌战簇和吭https://www.bilibili.com/medialist/detail/ml1443492076?type=1
-肮言居概优https://www.bilibili.com/medialist/detail/ml1443395676?type=1
-矢仔焊汤苑https://www.bilibili.com/medialist/detail/ml1443395576?type=1
17773526596 4周前 (2021-12-18) 回复
-技畏兑撬胶https://www.bilibili.com/medialist/detail/ml1440979905?type=1
-翘懈晌沼崩https://www.bilibili.com/medialist/detail/ml1441265605?type=1
-衣仕尘疤敖https://www.bilibili.com/medialist/detail/ml1441265505?type=1
-挪怖终杖忠https://www.bilibili.com/medialist/detail/ml1441072905?type=1
-勾颈灰桓眯https://www.bilibili.com/medialist/detail/ml1441432705?type=1
17780899079 4周前 (2021-12-18) 回复
1 2 3 4 5 6 7 8 ... »