mvp 在 flutter 中的应用
在 Android 應(yīng)用程序開(kāi)發(fā)過(guò)程中,我們經(jīng)常會(huì)用到一些所謂的架構(gòu)方法,如:mvp,mvvm,clean等。之所以這些方法會(huì)被推崇是因?yàn)樗麄兛梢源蟠蟮慕怦钗覀兊拇a的功能模塊,讓我們的代碼在項(xiàng)目中后期更容易擴(kuò)展和維護(hù)。
我個(gè)人比較推薦 mvp,主要是因?yàn)槠湎鄬?duì)比較簡(jiǎn)單且易上手,這次我將給大家介紹如何在 Flutter 中使用 mvp 來(lái)組織項(xiàng)目的功能模塊。為了演示方便,我選擇了一個(gè)比較簡(jiǎn)單的通訊錄列表來(lái)為大家做演示。
MVP
首先需要準(zhǔn)備 mvp 鼎鼎有名的兩個(gè)類(lèi):IView和IPrensenter,其中 IView 用于約束視圖的行為,IPresenter 則用于與 IView 進(jìn)行交互,為其提供除了 UI 行為的其他邏輯處理,如網(wǎng)絡(luò)請(qǐng)求,數(shù)據(jù)庫(kù)查詢(xún)等操作。
這里我們首先使用 IntelliJ 新建一個(gè)名為 flutter_mvp 的項(xiàng)目,接著在 lib 目錄下新建 mvp.dart 文件,文件內(nèi)容如下:
abstract class IView<T> {setPresenter(T presenter); }abstract class IPresenter{init(); }對(duì),這兩個(gè)類(lèi)就是如此簡(jiǎn)單。
數(shù)據(jù)源
首先我們不急著寫(xiě) UI 代碼,先保持 main.dart 文件不變。我們首先要定義一個(gè) Contact 類(lèi),用于表示通訊錄中的每一項(xiàng),接著還要定義一個(gè)數(shù)據(jù)倉(cāng)庫(kù)接口 ContactRepository ,用于獲取數(shù)據(jù),代碼如下:
import 'dart:async';class Contact {final String fullName;final String email;const Contact({this.fullName,this.email}); }abstract class ContactRepository{Future<List<Contact>> fetch(); }其中 Contact 有兩個(gè)字段 fullName 和 email 。ContactRepository 有一個(gè) fetch 方法,用于獲取通訊錄列表。
既然定義了 ContactRepository 接口,接下來(lái)編寫(xiě)它的實(shí)現(xiàn)類(lèi) MockContactRepository ,新建文件 contact_data_impl.dart ,其內(nèi)容如下:
import 'dart:async'; import 'contact_data.dart'; import 'package:flutter/services.dart'; import 'dart:convert'; class MockContactRepository implements ContactRepository{@overrideFuture<List<Contact>> fetch() {return new Future.value(kContacts);} }const kContacts = const<Contact>[const Contact(fullName: "Li bai",email: "libai@live.com"),const Contact(fullName: "Cheng yaojin",email: "chengyaojin@live.com"),const Contact(fullName: "Mi yue",email: "miyue@live.com"),const Contact(fullName: "A ke",email: "ake@live.com"),const Contact(fullName: "Lu ban",email: "luban@live.com"),const Contact(fullName: "Da qiao",email: "daqiao@live.com"),const Contact(fullName: "Hou yi",email: "houyi@live.com"),const Contact(fullName: "Liu bei",email: "liubei@live.com"),const Contact(fullName: "Wang zhaojun",email: "wangzhaoju@live.com"),];MockContactRepository 的功能就是在前期提供測(cè)試的假數(shù)據(jù)。
約束
接著是比較重要的環(huán)節(jié),為通訊錄功能編寫(xiě)約束,約束的內(nèi)容為 IView 和 IPresenter。新建 contract.dart 文件,內(nèi)容如下:
import 'package:flutter_mvp/mvp.dart'; import 'package:flutter_mvp/contact/data/contact_data.dart';abstract class Presenter implements IPresenter{loadContacts(); }abstract class View implements IView<Presenter>{void onLoadContactsComplete(List<Contact> items);void onLoadContactsError(); }這里我們給我們的通訊錄定義了屬于自己的兩個(gè)約束 Presenter 和 View,其中 Presenter 提供一個(gè) loadContacts 方法,用于加載數(shù)據(jù)。View 提供了 onLoadContactsComplete 方法,用于更新界面;onLoadContactsError 用于界面的錯(cuò)誤處理。
Presenter 的實(shí)現(xiàn)
接下來(lái)我們首先實(shí)現(xiàn) Presenter 接口,新建文件 contact_presenter.dart文件,文件內(nèi)容如下:
import 'package:flutter_mvp/contact/contract.dart'; import 'package:flutter_mvp/contact/data/contact_data.dart'; import 'package:flutter_mvp/contact/data/contact_data_impl.dart';class ContactPresenter implements Presenter{View _view;ContactRepository _repository;ContactPresenter(this._view){_view.setPresenter(this);}@overridevoid loadContacts(){assert(_view!= null);_repository.fetch().then((contacts){_view.onLoadContactsComplete(contacts);}).catchError((error){print(error);_view.onLoadContactsError();});}@overrideinit() {_repository = new MockContactRepository();} }該 Presenter 在構(gòu)造方法中初始化自己的 _view 字段,并且調(diào)用 _view 的 setPresenter 方法,為其注入了 presenter 對(duì)象。這樣一來(lái) View 和 Presenter 兩者就綁定到了一起。接著在 init 方法中初始化了 _repository 對(duì)象。
這里的重點(diǎn)是 loadContacts 方法,它會(huì)調(diào)用 _repository 的 fetch 方法來(lái)獲取數(shù)據(jù),當(dāng)拿到數(shù)據(jù)后調(diào)用 _view 的 onLoadContactsComplete 方法來(lái)更新 UI。
View 的實(shí)現(xiàn)
最后就是我們的 UI 部分了,這里新建文件 contact_page.dart ,其內(nèi)容如下:
import 'package:flutter/material.dart'; import 'package:flutter_mvp/contact/data/contact_data.dart'; import 'package:flutter_mvp/contact/contact_presenter.dart'; import 'package:flutter_mvp/contact/contract.dart'; class ContactsPage extends StatelessWidget{@overrideWidget build(BuildContext context) {return new Scaffold(appBar: new AppBar(title: new Text("Contacts"),),body: new ContactList());} }class ContactList extends StatefulWidget{ContactList({ Key key }) : super(key: key);@override_ContactListState createState(){_ContactListState view = new _ContactListState();ContactPresenter presenter = new ContactPresenter(view);presenter.init();return view ;} }class _ContactListState extends State<ContactList> implements View {List<Contact> contacts = [];ContactPresenter _presenter;@overridevoid initState() {super.initState();_presenter.loadContacts();}Widget buildListTile(BuildContext context, Contact contact) {return new MergeSemantics(child: new ListTile(isThreeLine: true,dense: false,leading: new ExcludeSemantics(child: new CircleAvatar(child: new Text(contact.fullName.substring(0,1)))) ,title: new Text(contact.fullName),subtitle: new Text(contact.email),),);}@overrideWidget build(BuildContext context) {Widget widget ;widget = new ListView.builder(padding: new EdgeInsets.symmetric(vertical: 8.0),itemBuilder: (BuildContext context, int index){return buildListTile(context,contacts[index]);},itemCount: contacts.length,);return widget;}@overridevoid onLoadContactsComplete(List<Contact> items) {setState((){contacts = items;print(" contacts size ${contacts.length}");});}@overridevoid onLoadContactsError() {}@overridesetPresenter(Presenter presenter) {_presenter = presenter;} }這段代碼有些長(zhǎng),我們分段來(lái)看。
首先是類(lèi) ContactsPage ,它主要用于提供 UI 上的 AppBar 和 body。其中 body 為 ContactList 就是我們的通訊錄列表。
接著看 ContactList ,其 createState 方法如下:
@override_ContactListState createState(){_ContactListState view = new _ContactListState();ContactPresenter presenter = new ContactPresenter(view);presenter.init();return view ;}首先是初始化了通訊錄的 UI 類(lèi) _ContactListState,接著初始化了 ContactPresenter ,并將 _ContactListState 傳入其中。最后調(diào)用了 Presenter 的 init 方法來(lái)初始化 Presenter。
接下來(lái)就是 _ContactListState 類(lèi)了,通訊錄列表就是由它構(gòu)建的。UI 相關(guān)代碼不多說(shuō),這里主要看 initState 方法,在其中調(diào)用了 Presenter 的 loadContacts 方法來(lái)加載數(shù)據(jù)。當(dāng) Presenter 加載完數(shù)據(jù)后會(huì)調(diào)用 _ContactListState 的 onLoadContactsComplete 方法來(lái)更新 UI 。
最后運(yùn)行結(jié)果如下:
使用真是數(shù)據(jù)
在上面我們使用的是 MockContactRepository 提供的假數(shù)據(jù),接著我們定義一個(gè) HttpContactRepository 來(lái)從網(wǎng)絡(luò)上加載數(shù)據(jù),在 contact_data_impl 添加 HttpContactRepository 類(lèi),
const String kContactsUrl = "http://o6p4e1uhv.bkt.clouddn.com/contacts.json";class HttpContactRepository implements ContactRepository{@overrideFuture<List<Contact>> fetch() async{var httpClient = createHttpClient();var response = await httpClient.get(kContactsUrl);var body = response.body;List<Map> contacts = JSON.decode(body)['contacts'];return contacts.map((contact){return new Contact(fullName: contact['fullname'],email: contact['email']);}).toList();} }為了 HttpContactRepository 和 MockContactRepository 切換翻遍,另外增加 RepositoryType 和 Injector 兩個(gè)類(lèi),
enum RepositoryType{mock,http }class Injector{ContactRepository getContactRepository(RepositoryType type){switch(type){case RepositoryType.mock:return new MockContactRepository();default:return new HttpContactRepository();}}}其中 Injector 用于管理外界對(duì) ContactRepository 的依賴(lài)。
最終 contact_data_impl 文件內(nèi)容如下:
import 'dart:async'; import 'contact_data.dart'; import 'package:flutter/services.dart'; import 'dart:convert'; class MockContactRepository implements ContactRepository{@overrideFuture<List<Contact>> fetch() {return new Future.value(kContacts);} }class HttpContactRepository implements ContactRepository{@overrideFuture<List<Contact>> fetch() async{var httpClient = createHttpClient();var response = await httpClient.get(kContactsUrl);var body = response.body;List<Map> contacts = JSON.decode(body)['contacts'];return contacts.map((contact){return new Contact(fullName: contact['fullname'],email: contact['email']);}).toList();} }enum RepositoryType{mock,http }class Injector{ContactRepository getContactRepository(RepositoryType type){switch(type){case RepositoryType.mock:return new MockContactRepository();default:return new HttpContactRepository();}}}const String kContactsUrl = "http://o6p4e1uhv.bkt.clouddn.com/contacts.json";const kContacts = const<Contact>[const Contact(fullName: "Li bai",email: "libai@live.com"),const Contact(fullName: "Cheng yaojin",email: "chengyaojin@live.com"),const Contact(fullName: "Mi yue",email: "miyue@live.com"),const Contact(fullName: "A ke",email: "ake@live.com"),const Contact(fullName: "Lu ban",email: "luban@live.com"),const Contact(fullName: "Da qiao",email: "daqiao@live.com"),const Contact(fullName: "Hou yi",email: "houyi@live.com"),const Contact(fullName: "Liu bei",email: "liubei@live.com"),const Contact(fullName: "Wang zhaojun",email: "wangzhaoju@live.com"),];最后需要改動(dòng)的地方是 ContactPresenter 類(lèi)的 init 方法,
@overrideinit() {_repository = new Injector().getContactRepository(RepositoryType.mock);}這樣就能方便對(duì)真是數(shù)據(jù)和測(cè)試數(shù)據(jù)做切換了。
總結(jié)
看到這,是不是覺(jué)得 mvp 還是比較簡(jiǎn)單的,其關(guān)鍵就是對(duì) View 和Presenter 的定義和實(shí)現(xiàn)。另外如果對(duì) mvp 還是不很熟悉的可以多在網(wǎng)上找些資料。
如果需要上述代碼,可以在https://github.com/flutter-dev/flutter-mvp 下載。
最后做一下廣告,我們的 Flutter 中文開(kāi)發(fā)者論壇已經(jīng)上線(xiàn)了,如果你對(duì) Flutter 感興趣的話(huà)可以前往 flutter-dev.cn/bbs 或 flutter-dev.com/bbs 與大家一起討論和學(xué)習(xí) 。
轉(zhuǎn)載于:https://www.cnblogs.com/zhujiabin/p/10120641.html
總結(jié)
以上是生活随笔為你收集整理的mvp 在 flutter 中的应用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: nginx安装和基础代理配置
- 下一篇: hive建表映射到hbase