本节指标

  • GetConnect
  • StateMixin
  • GetController + Dio
  • SuperController

视频

https://www.bilibili.com/vide...

代码

https://github.com/ducafecat/...

参考

  • https://pub.flutter-io.cn/pac...

注释

GetConnect

  • 瞎聊设计模式

Provider 提供者模式 位于高层 由他来决定从哪里、提供什么

绝对应的有 Consumer 消费者模式

Repository 模式,这层有 OO 面向对象的意思,用来解决拉取数据细节,这样到 Controller 控制器 这一层只有解决业务就行,可不便测试

DAO 就是纯正的数据拜访层,没有 00 的概念

Service Model Entity ...

前端其实对数据加工、面向服务、畛域模型偏弱,更多的是组件拆分、款式、布局,这才是要关系的,就算是测试也是 E2E 偏重不同。

E2E(End To End)即端对端测试,属于黑盒测试,通过编写测试用例,自动化模仿用户操作,确保组件间通信失常,程序流数据传递如预期。

  • 封装 GetConnect

lib/common/utils/base_provider.dart

class BaseProvider extends GetConnect {  @override  void onInit() {    httpClient.baseUrl = SERVER_API_URL;    // 申请拦挡    httpClient.addRequestModifier<void>((request) {      request.headers['Authorization'] = '12345678';      return request;    });    // 响应拦挡    httpClient.addResponseModifier((request, response) {      return response;    });  }}
  • Provider

lib/pages/getConnect/provider.dart

abstract class INewsProvider {  Future<Response<NewsPageListResponseEntity>> getNews();}class NewsProvider extends BaseProvider implements INewsProvider {  // 新闻分页  // @override  // Future<Response<NewsPageListResponseEntity>> getNews() => get("/news");  @override  Future<Response<NewsPageListResponseEntity>> getNews() async {    var response = await get("/news");    var data = NewsPageListResponseEntity.fromJson(response.body);    return Response(      statusCode: response.statusCode,      statusText: response.statusText,      body: data,    );  }}
  • Repository

lib/pages/getConnect/repository.dart

abstract class INewsRepository {  Future<NewsPageListResponseEntity> getNews();}class NewsRepository implements INewsRepository {  NewsRepository({required this.provider});  final INewsProvider provider;  @override  Future<NewsPageListResponseEntity> getNews() async {    final response = await provider.getNews();    if (response.status.hasError) {      return Future.error(response.statusText!);    } else {      return response.body!;    }  }}
  • Controller

lib/pages/getConnect/controller.dart

class NewsController extends SuperController<NewsPageListResponseEntity> {  NewsController({required this.repository});  final INewsRepository repository;  @override  void onInit() {    super.onInit();    //Loading, Success, Error handle with 1 line of code    // append(() => repository.getNews);  }  // 拉取新闻列表  Future<void> getNewsPageList() async {    append(() => repository.getNews);  }  @override  void onReady() {    print('The build method is done. '        'Your controller is ready to call dialogs and snackbars');    super.onReady();  }  @override  void onClose() {    print('onClose called');    super.onClose();  }  @override  void didChangeMetrics() {    print('the window size did change');    super.didChangeMetrics();  }  @override  void didChangePlatformBrightness() {    print('platform change ThemeMode');    super.didChangePlatformBrightness();  }  @override  Future<bool> didPushRoute(String route) {    print('the route $route will be open');    return super.didPushRoute(route);  }  @override  Future<bool> didPopRoute() {    print('the current route will be closed');    return super.didPopRoute();  }  @override  void onDetached() {    print('onDetached called');  }  @override  void onInactive() {    print('onInative called');  }  @override  void onPaused() {    print('onPaused called');  }  @override  void onResumed() {    print('onResumed called');  }}
  • GetView

lib/pages/getConnect/view.dart

class NewsView extends GetView<NewsController> {  NewsView({Key? key}) : super(key: key);  _buildListView(NewsPageListResponseEntity? state) {    return ListView.separated(      itemCount: state != null ? state.items!.length : 0,      itemBuilder: (context, index) {        final NewsItem item = state!.items![index];        return ListTile(          onTap: () => null,          title: Text(item.title),          trailing: Text("分类 ${item.category}"),        );      },      separatorBuilder: (BuildContext context, int index) {        return Divider();      },    );  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: Text("GetConnect Page"),      ),      body: controller.obx(        (state) => _buildListView(state),        onEmpty: Text("onEmpty"),        onLoading: Center(          child: Column(            children: [              Text("没有数据"),              ElevatedButton(                onPressed: () {                  controller.getNewsPageList();                },                child: Text('拉取数据'),              ),            ],          ),        ),        onError: (err) => Text("onEmpty" + err.toString()),      ),    );  }}
  • Bindings

lib/pages/getConnect/bindings.dart

class NewsBinding implements Bindings {  @override  void dependencies() {    Get.lazyPut<INewsProvider>(() => NewsProvider());    Get.lazyPut<INewsRepository>(() => NewsRepository(provider: Get.find()));    Get.lazyPut(() => NewsController(repository: Get.find()));  }}
  • 路由

lib/common/routes/app_pages.dart

    GetPage(      name: AppRoutes.GetConnect,      binding: NewsBinding(),      page: () => NewsView(),    ),

StateMixin

雷同代码不再反复

  • 控制器 Mixin 如下

lib/pages/getConnect_stateMixin/controller.dart

class NewsStateMixinController extends GetxController    with StateMixin<NewsPageListResponseEntity> {  final NewsStateMixinProvider provider;  NewsStateMixinController({required this.provider});  // 拉取新闻列表  Future<void> getNewsPageList() async {    // 获取数据    final Response response = await provider.getNews();    // 判断,如果有谬误    if (response.hasError) {      // 扭转数据,传入谬误状态,在ui中会解决这些谬误      change(null, status: RxStatus.error(response.statusText));    } else {      // 否则,存储数据,扭转状态为胜利      var data = NewsPageListResponseEntity.fromJson(response.body);      change(data, status: RxStatus.success());    }  }}
这种形式的确简化了很多代码

GetController + Dio

这种形式就是之前 Flutter 新闻客户端 的写法,能复用原来的 dio 代码。

  • dio 根底类

lib/common/utils/http.dart

/*  * http 操作类  *  * 手册  * https://github.com/flutterchina/dio/blob/master/README-ZH.md  *  * 从 3 降级到 4  * https://github.com/flutterchina/dio/blob/master/migration_to_4.x.md*/class HttpUtil {  static HttpUtil _instance = HttpUtil._internal();  factory HttpUtil() => _instance;  late Dio dio;  HttpUtil._internal() {    // BaseOptions、Options、RequestOptions 都能够配置参数,优先级别顺次递增,且能够依据优先级别笼罩参数    BaseOptions options = new BaseOptions(      // 申请基地址,能够蕴含子门路      baseUrl: SERVER_API_URL,      // baseUrl: storage.read(key: STORAGE_KEY_APIURL) ?? SERVICE_API_BASEURL,      //连贯服务器超时工夫,单位是毫秒.      connectTimeout: 10000,      // 响应流上前后两次承受到数据的距离,单位为毫秒。      receiveTimeout: 5000,      // Http申请头.      headers: {},      /// 申请的Content-Type,默认值是"application/json; charset=utf-8".      /// 如果您想以"application/x-www-form-urlencoded"格局编码申请数据,      /// 能够设置此选项为 `Headers.formUrlEncodedContentType`,  这样[Dio]      /// 就会自动编码申请体.      contentType: 'application/json; charset=utf-8',      /// [responseType] 示意冀望以那种格局(形式)承受响应数据。      /// 目前 [ResponseType] 承受三种类型 `JSON`, `STREAM`, `PLAIN`.      ///      /// 默认值是 `JSON`, 当响应头中content-type为"application/json"时,dio 会主动将响应内容转化为json对象。      /// 如果想以二进制形式承受响应数据,如下载一个二进制文件,那么能够应用 `STREAM`.      ///      /// 如果想以文本(字符串)格局接管响应数据,请应用 `PLAIN`.      responseType: ResponseType.json,    );    dio = new Dio(options);    // Cookie治理    CookieJar cookieJar = CookieJar();    dio.interceptors.add(CookieManager(cookieJar));  }  /// restful get 操作  Future get(    String path, {    dynamic? queryParameters,    Options? options,  }) async {    var response = await dio.get(      path,      queryParameters: queryParameters,      options: options,    );    return response.data;  }}
  • api 定义

lib/common/apis/news.dart

/// 新闻class NewsAPI {  /// 翻页  static Future<NewsPageListResponseEntity> newsPageList(      {NewsRecommendRequestEntity? param}) async {    var response = await HttpUtil().get(      '/news',      queryParameters: param?.toJson(),    );    return NewsPageListResponseEntity.fromJson(response);  }}
  • 控制器

lib/pages/getController_dio/controller.dart

class NewsDioController extends GetxController {  var newsPageList =      Rx<NewsPageListResponseEntity>(NewsPageListResponseEntity());  @override  void onInit() {    super.onInit();    print("onInit");  }  @override  void onClose() {    super.onClose();    print("onClose");  }  getPageList() async {    newsPageList.value = await NewsAPI.newsPageList();  }}

© 猫哥

https://ducafecat.tech/

https://github.com/ducafecat

往期

开源

GetX Quick Start

https://github.com/ducafecat/...

新闻客户端

https://github.com/ducafecat/...

strapi 手册译文

https://getstrapi.cn

微信探讨群 ducafecat

系列汇合

Dart 编程语言根底

https://space.bilibili.com/40...

Flutter 零根底入门

https://space.bilibili.com/40...

Flutter 实战从零开始 新闻客户端

https://space.bilibili.com/40...

Flutter 组件开发

https://space.bilibili.com/40...

Flutter Bloc

https://space.bilibili.com/40...

Flutter Getx4

https://space.bilibili.com/40...

Docker Yapi

https://space.bilibili.com/40...