本节目标
- GetConnect
- StateMixin
- GetController + Dio
- SuperController
视频
https://www.bilibili.com/video/BV17B4y1c7eF
代码
https://github.com/ducafecat/getx_quick_start
参考
正文
GetConnect
Provider
提供者模式 位于高层 由他来决定从哪里、提供什么
相对应的有 Consumer
消费者模式
Repository
模式,这层有 OO
面向对象的意思,用来处理拉取数据细节,这样到 Controller
控制器 这一层只要处理业务就行,可方便测试
DAO
就是纯粹的数据访问层,没有 00
的概念
Service
Model
Entity
…
前端其实对数据加工、面向服务、领域模型偏弱,更多的是组件拆分、样式、布局,这才是要关系的,就算是测试也是 E2E
侧重不同。
E2E
(End To End)即端对端测试,属于黑盒测试,通过编写测试用例,自动化模拟用户操作,确保组件间通信正常,程序流数据传递如预期。
lib/common/utils/base_provider.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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; }); } }
|
lib/pages/getConnect/provider.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| abstract class INewsProvider { Future<Response<NewsPageListResponseEntity>> getNews(); }
class NewsProvider extends BaseProvider implements INewsProvider { @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, ); } }
|
lib/pages/getConnect/repository.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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!; } } }
|
lib/pages/getConnect/controller.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| class NewsController extends SuperController<NewsPageListResponseEntity> { NewsController({required this.repository});
final INewsRepository repository;
@override void onInit() { super.onInit();
}
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'); } }
|
lib/pages/getConnect/view.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| 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()), ), ); } }
|
lib/pages/getConnect/bindings.dart
1 2 3 4 5 6 7 8
| 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
1 2 3 4 5
| GetPage( name: AppRoutes.GetConnect, binding: NewsBinding(), page: () => NewsView(), ),
|
StateMixin
雷同代码不再重复
lib/pages/getConnect_stateMixin/controller.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 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) { change(null, status: RxStatus.error(response.statusText)); } else { var data = NewsPageListResponseEntity.fromJson(response.body); change(data, status: RxStatus.success()); } } }
|
这种方式确实简化了很多代码
GetController + Dio
这种方式就是之前 Flutter 新闻客户端 的写法,能复用原来的 dio 代码。
lib/common/utils/http.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
|
class HttpUtil { static HttpUtil _instance = HttpUtil._internal(); factory HttpUtil() => _instance;
late Dio dio;
HttpUtil._internal() { BaseOptions options = new BaseOptions( baseUrl: SERVER_API_URL,
connectTimeout: 10000,
receiveTimeout: 5000,
headers: {},
contentType: 'application/json; charset=utf-8',
responseType: ResponseType.json, );
dio = new Dio(options);
CookieJar cookieJar = CookieJar(); dio.interceptors.add(CookieManager(cookieJar)); }
Future get( String path, { dynamic? queryParameters, Options? options, }) async { var response = await dio.get( path, queryParameters: queryParameters, options: options, ); return response.data; } }
|
lib/common/apis/news.dart
1 2 3 4 5 6 7 8 9 10 11 12
| 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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/getx_quick_start
新闻客户端
https://github.com/ducafecat/flutter_learn_news
strapi 手册译文
https://getstrapi.cn
微信讨论群 ducafecat
系列集合
Dart 编程语言基础
https://space.bilibili.com/404904528/channel/detail?cid=111585
Flutter 零基础入门
https://space.bilibili.com/404904528/channel/detail?cid=123470
Flutter 实战从零开始 新闻客户端
https://space.bilibili.com/404904528/channel/detail?cid=106755
Flutter 组件开发
https://space.bilibili.com/404904528/channel/detail?cid=144262
Flutter Bloc
https://space.bilibili.com/404904528/channel/detail?cid=177519
Flutter Getx4
https://space.bilibili.com/404904528/channel/detail?cid=177514
Docker Yapi
https://space.bilibili.com/404904528/channel/detail?cid=130578
邮箱 ducafecat@gmail.com / 微信 ducafecat / 留言板 disqus