在 Flutter 中使用 MVP 架构
source link: http://caimuhao.com/2018/08/29/Flutter-MVP-Architecture/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
在 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: '[email protected]'), const Contact(fullName: 'Emilie Olsen', email: '[email protected]') ];
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”: “[email protected]”, } ] }
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
中调用 presenter
的 loadContacts
方法加载数据,当数据获取成功时候, Presenter
层会调用 View
层的 onLoadContactsComplete
方法,获取时候时会调用 onLoadContactsError
方法,在获取数据成功后我们调用 setState
方法来重新绘制界面.
Recommend
-
0
10 Tips for Building a Successful MVP with Flutter10 Tips for Building a Successful MVP with FlutterAugust 21st 2023 New Story7...
-
5
6. MVP示例 6.1 添加依赖 compile 'cn.finalteam:okhttpfinal:2.0.7' 6.2 初始化okhttpfinal public class ...
-
4
层次架构风格从之前的两层C/S到三层C/S,然后演化为三层B/S架构,三层B/S架构之后仍然在往后面演化,我们来看一下层次架构演化过程中都有了哪些演化的架构风格呢?而我们先简单了解一下之前的层次架构风格中分层的各个层次的作用。
-
38
-
71
Android MVP升级路(二)时尚版 Original JesseBraveMan...
-
54
关闭
-
112
Notice! This is an old research repo. No active work is being done here. Efforts in the direction of production-ready MVP plasma chain (MoreVP, ERC20, audits) are in ht...
-
76
人之所以能,是相信能。 说到MVP,大家应该都不陌生了,由于其高度解等等优点,越来越多的项目使用这个设计模式。然而,优点虽在,缺点也不少,其中一个就是类多了很多,而且V与P直接要项目通信,那么P就得持有V得实例,但如果活动挂掉了,如果没有对V进行释放,还...
-
89
目录 MVP那些事儿(1)……用场景说话 MVP那些事儿(2)……MVC架构初探 MVP那些事儿(3)……在Android中使用MVC(上) MVP那些事儿(4)……在Android中使用MVC(下) MVP那些事儿(5)……中介者模式与MVP的关系 MVP那
-
38
MVP Logo Generator
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK