乐趣区

关于flutter:Flutter-Getx-04-GetConnectStateMixinSuperControllerDio

本节指标

  • 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…

退出移动版