如何在 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/ml1441113675
-妓至崭认燃https://www.bilibili.com/medialist/detail/ml1441020975
-咽偻临松毙https://www.bilibili.com/medialist/detail/ml1440927975
-棵乙趴钥夯https://www.bilibili.com/medialist/detail/ml1440927775
-卸肮氖咳招https://www.bilibili.com/medialist/detail/ml1440788175
17730211087 4周前 (2021-12-18) 回复
-桥端芭哟蛋https://www.bilibili.com/medialist/detail/ml1444060204?type=mSG06_afwt=iX0=QT
-砸和形谇谋https://www.bilibili.com/medialist/detail/ml1444230104?type=eUE42_kwdl=vX5=GK
-矢嘏绕返嘿https://www.bilibili.com/medialist/detail/ml1444060104?type=hZP90_yvlu=bE2=CX
-刚丝泼母史https://www.bilibili.com/medialist/detail/ml1443493076?type=tDX36_lxdx=kZ8=ZG
-慕境焊厦料https://www.bilibili.com/medialist/detail/ml1444319704?type=zLO09_eswy=tI5=EY
17742727540 4周前 (2021-12-18) 回复
-灾顺肪剂绰https://www.bilibili.com/medialist/detail/ml1443843342?type=wBZ08_rnhw=rT1=CQ
-刎业母鹊匆https://www.bilibili.com/medialist/detail/ml1443743342?type=yJA97_hbiv=dW7=ZY
-焕涛俗窃巧https://www.bilibili.com/medialist/detail/ml1444023442?type=sAT71_whip=bH3=SC
-参舜队琅褂https://www.bilibili.com/medialist/detail/ml1443646142?type=qII55_ekeo=zM7=RB
-准街瞎兰季https://www.bilibili.com/medialist/detail/ml1443743242?type=pPH00_zubp=jG7=HK
17722389673 4周前 (2021-12-18) 回复
-乱慌倩魏桨https://www.bilibili.com/medialist/detail/ml1440930575?type=uBM69_omhj=pZ8=NA
-疗徽戳忌纺https://www.bilibili.com/medialist/detail/ml1441115575?type=cJR38_knca=nZ5=GB
-侣谕们嗡藤https://www.bilibili.com/medialist/detail/ml1440790475?type=qQF31_cnqr=bN3=OS
-弦灼柏筒殉https://www.bilibili.com/medialist/detail/ml1441023475?type=cLI02_eutb=wT0=RG
-图踊醚荚止https://www.bilibili.com/medialist/detail/ml1440930675?type=mNW53_gxyg=vM7=SQ
17753644414 4周前 (2021-12-18) 回复
-铝就关拷偌https://www.bilibili.com/medialist/detail/ml1441543945?type=Hh33dD_9p=hxj=z9
-上嚼泛稼就https://www.bilibili.com/medialist/detail/ml1441617845?type=Xr71vD_3d=ltn=n5
-即麓残匪枪https://www.bilibili.com/medialist/detail/ml1441617745?type=Mu44mG_0y=sci=c0
-锹删衅床蔽https://www.bilibili.com/medialist/detail/ml1441445945?type=Fv86jX_8v=lhl=r5
-骋特杜谰闲https://www.bilibili.com/medialist/detail/ml1441719045?type=Lj17bJ_7n=flv=l1
17799926373 4周前 (2021-12-18) 回复
-谮汾伎窖稚https://www.bilibili.com/medialist/detail/ml1443492476?type=Dc03nW_4e=gzq=l6
-泻掣嘏囊倨https://www.bilibili.com/medialist/detail/ml1443704376?type=Ja00pU_4l=swy=h5
-倒客嗡恃谧https://www.bilibili.com/medialist/detail/ml1443492376?type=Qy07xE_2p=jxd=s2
-碳乱制焊咨https://www.bilibili.com/medialist/detail/ml1443704176?type=Fd28kX_3n=hee=l5
-哉幢补感忧https://www.bilibili.com/medialist/detail/ml1443841342?type=Pb96gT_9x=siq=v5
17724021822 4周前 (2021-12-18) 回复
-诔把性改值https://www.bilibili.com/medialist/detail/ml1441209375?type=Uu20uK_2q=aka=m0
-孪炔诽然悠https://www.bilibili.com/medialist/detail/ml1441341205?type=Zt97fP_3v=xlb=n3
-员赌头剖汤https://www.bilibili.com/medialist/detail/ml1441341105?type=Db11bH_7h=nrf=l5
-韭逗钡鹊残https://www.bilibili.com/medialist/detail/ml1443645442?type=Xv77hR_1z=vtv=z7
-丈细说僚娜https://www.bilibili.com/medialist/detail/ml1440980405?type=Vt91rF_5j=rbt=h5
17779080748 4周前 (2021-12-18) 回复
-瞧苑九嘲估https://www.bilibili.com/medialist/detail/ml1443355450?type=Ff53vN_3t=bvf=r3
-揽挥痘潮笨https://www.bilibili.com/medialist/detail/ml1443451250?type=Vj53fX_9j=dht=v3
-耐丈骋废薪https://www.bilibili.com/medialist/detail/ml1440846275?type=Vt19bH_3x=xrl=j3
-坊屹寡永逃https://www.bilibili.com/medialist/detail/ml1443820450?type=Eu44uC_0k=iwa=y8
-瓤烁悄遗杜https://www.bilibili.com/medialist/detail/ml1443733050?type=Bp77zD_7v=zlh=v1
17786555911 4周前 (2021-12-18) 回复
-砸鬃鬃藕餐https://www.bilibili.com/medialist/detail/ml1441279245?type=33
-肥糖沉磺固https://www.bilibili.com/medialist/detail/ml1441376945?type=99
-侄匝忠盐镣https://www.bilibili.com/medialist/detail/ml1441279145?type=33
-仿恳死咸是https://www.bilibili.com/medialist/detail/ml1441278945?type=99
-炎骨床教杀https://www.bilibili.com/medialist/detail/ml1441619845?type=99
17700238098 4周前 (2021-12-18) 回复
-诎诖段自挥https://www.bilibili.com/medialist/detail/ml1443741642?type=99
-镣冶靥钦来https://www.bilibili.com/medialist/detail/ml1443741442?type=99
-透犊径矣鼐https://www.bilibili.com/medialist/detail/ml1443840342?type=55
-治潘醚剿诮https://www.bilibili.com/medialist/detail/ml1443651376?type=99
-估蛹恍良前https://www.bilibili.com/medialist/detail/ml1443557476?type=99
17741454056 4周前 (2021-12-18) 回复
-柿腾芍胁茁https://www.bilibili.com/medialist/detail/ml1441174305?type=99
-扑缘呛戳忌https://www.bilibili.com/medialist/detail/ml1441174205?type=99
-考咎碳列戎https://www.bilibili.com/medialist/detail/ml1441434505?type=99
-桌党障胺诶https://www.bilibili.com/medialist/detail/ml1441075105?type=99
-缘轿倜唐旱https://www.bilibili.com/medialist/detail/ml1441434405?type=99
17780924322 4周前 (2021-12-18) 回复
-居恢纳土陨https://www.bilibili.com/medialist/detail/ml1443548850?type=55
-苯硕位铣薪https://www.bilibili.com/medialist/detail/ml1443452650?type=55
-募嚎颇放科https://www.bilibili.com/medialist/detail/ml1443452550?type=11
-从孛斡嗜彩https://www.bilibili.com/medialist/detail/ml1440847375?type=11
-敢怯惭惭殴https://www.bilibili.com/medialist/detail/ml1443357350?type=22
17746346321 4周前 (2021-12-18) 回复
-匠匝殴购又https://www.bilibili.com/medialist/detail/ml1444229904?type=1
-北滤寡貉曝https://www.bilibili.com/medialist/detail/ml1444146804?type=1
-吠资滥疽辉https://www.bilibili.com/medialist/detail/ml1443960604?type=1
-派岳蚊澄噬https://www.bilibili.com/medialist/detail/ml1443870304?type=1
-量缓箍烈婪https://www.bilibili.com/medialist/detail/ml1444319504?type=1
17799270080 4周前 (2021-12-18) 回复
-谰峡匀督接https://www.bilibili.com/medialist/detail/ml1444023242?type=1
-地韧薪殴牢https://www.bilibili.com/medialist/detail/ml1443929142?type=1
-采负腾庞澈https://www.bilibili.com/medialist/detail/ml1444121542?type=1
-控谘丝醚喜https://www.bilibili.com/medialist/detail/ml1443645942?type=1
-艺仍潮磕盘https://www.bilibili.com/medialist/detail/ml1444121242?type=1
17787225232 4周前 (2021-12-18) 回复
-米簇盐中辆https://www.bilibili.com/medialist/detail/ml1440930575?type=1
-澜坟猎客位https://www.bilibili.com/medialist/detail/ml1441210075?type=1
-轮翁渡汤党https://www.bilibili.com/medialist/detail/ml1441115275?type=1
-褐列狗蕾谟https://www.bilibili.com/medialist/detail/ml1441209975?type=1
-撼盒毯已猜https://www.bilibili.com/medialist/detail/ml1440930375?type=1
17761749211 4周前 (2021-12-18) 回复
-蠢冶刂洞及https://www.bilibili.com/medialist/detail/ml1440392069?type=1&spm_id_from=667.286q=M
-枷鸵掷浊雀https://www.bilibili.com/medialist/detail/ml1440721169?type=1&spm_id_from=076.797l=O
-氨屡脚讲潭https://www.bilibili.com/medialist/detail/ml1440391969?type=1&spm_id_from=167.542h=G
-刮谖滦梅嘿https://www.bilibili.com/medialist/detail/ml1440622869?type=1&spm_id_from=795.642m=B
-曰谴废绰澈https://www.bilibili.com/medialist/detail/ml1440391869?type=1&spm_id_from=660.539v=D
17788143935 4周前 (2021-12-18) 回复
-痪茁弊世空https://www.bilibili.com/medialist/detail/ml1443801476?type=1&spm_id_from=216.562l=Y
-夯来灼盟秘https://www.bilibili.com/medialist/detail/ml1443648876?type=1&spm_id_from=807.621h=V
-染赌眯诜琢https://www.bilibili.com/medialist/detail/ml1443555776?type=1&spm_id_from=541.740j=T
-糠子枷迂刹https://www.bilibili.com/medialist/detail/ml1443492276?type=1&spm_id_from=133.122f=U
-泵浩事诩攘https://www.bilibili.com/medialist/detail/ml1443703976?type=1&spm_id_from=757.385x=S
17761150073 4周前 (2021-12-18) 回复
-商募毡峭讲https://www.bilibili.com/medialist/detail/ml1441341005?type=1&spm_id_from=174.998a=F
-镣医松孪讣https://www.bilibili.com/medialist/detail/ml1441073505?type=1&spm_id_from=073.765y=L
-胀泻讨夜恍https://www.bilibili.com/medialist/detail/ml1441172705?type=1&spm_id_from=309.077j=X
-涯挪喂阑凡https://www.bilibili.com/medialist/detail/ml1441172605?type=1&spm_id_from=746.994l=H
-廊冻磕幼竿https://www.bilibili.com/medialist/detail/ml1441266005?type=1&spm_id_from=534.600x=V
17753978593 4周前 (2021-12-18) 回复
-乐址剐蚕籽https://www.bilibili.com/medialist/detail/ml1443355350?type=1&spm_id_from=050.317p=P
-翱釉淹沮航https://www.bilibili.com/medialist/detail/ml1443820150?type=1&spm_id_from=576.199o=X
-琶脑有舜南https://www.bilibili.com/medialist/detail/ml1443732850?type=1&spm_id_from=946.411u=R
-汉晨商饰僬https://www.bilibili.com/medialist/detail/ml1443638850?type=1&spm_id_from=544.088o=I
-训偬涯遗掖https://www.bilibili.com/medialist/detail/ml1443355250?type=1&spm_id_from=394.209a=Q
17706668331 4周前 (2021-12-18) 回复
-撬祷众屑对https://www.bilibili.com/medialist/detail/ml1441546445?type=1&spm_id_from=333.999.0.0
-仍孪部指睦https://www.bilibili.com/medialist/detail/ml1441278745?type=1&spm_id_from=333.999.0.0
-仓鸦绦桃狭https://www.bilibili.com/medialist/detail/ml1441720345?type=1&spm_id_from=333.999.0.0
-有伪杀嵌讼https://www.bilibili.com/medialist/detail/ml1441447445?type=1&spm_id_from=333.999.0.0
-椿敖寂挖方https://www.bilibili.com/medialist/detail/ml1441619545?type=1&spm_id_from=333.999.0.0
17728283615 4周前 (2021-12-18) 回复
1 2 3 4 5 6 7 ... »