在 Flutter 中使用 MVP 架构

在 Flutter 中使用 MVP 架构在 Android 开发中有很多设计模式,从 MVC 到 MVP MVVM 等,而在 Flutter 中也可是使用 MVP 模式进行开发,在这篇文章中我们来看一下在 Flutter 中如何使用 MVP 模式开发应用. MVP 模式主要包含三个部分 UI 层包含所有我们需要的 W…

  1. Data Layer
  2. Mock Repository
  3. Random User Repository
  4. Dependency Injection
  5. Presenter
  6. View
  7. Final result

在 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

contactscontacts

今天的文章在 Flutter 中使用 MVP 架构分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/18496.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注