本节指标
- 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...