在 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
中调用 presenter
的loadContacts
方法加载数据,当数据获取成功时候,Presenter
层会调用 View
层的onLoadContactsComplete
方法,获取时候时会调用onLoadContactsError
方法,在获取数据成功后我们调用setState
方法来重新绘制界面.
Final result
今天的文章在 Flutter 中使用 MVP 架构分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/18496.html