1 通用库

(1) Entity

  1. /// 新闻分页 response
  2. class NewsPageListResponseEntity {
  3. int? counts;
  4. int? pagesize;
  5. int? pages;
  6. int? page;
  7. List<NewsItem>? items;
  8. NewsPageListResponseEntity({
  9. this.counts,
  10. this.pagesize,
  11. this.pages,
  12. this.page,
  13. this.items,
  14. });
  15. factory NewsPageListResponseEntity.fromJson(Map<String, dynamic> json) =>
  16. NewsPageListResponseEntity(
  17. counts: json["counts"],
  18. pagesize: json["pagesize"],
  19. pages: json["pages"],
  20. page: json["page"],
  21. items:
  22. List<NewsItem>.from(json["items"].map((x) => NewsItem.fromJson(x))),
  23. );
  24. Map<String, dynamic> toJson() => {
  25. "counts": counts,
  26. "pagesize": pagesize,
  27. "pages": pages,
  28. "page": page,
  29. "items": items != null
  30. ? List<dynamic>.from(items!.map((x) => x.toJson()))
  31. : [],
  32. };
  33. }
  34. class NewsItem {
  35. String id;
  36. String title;
  37. String category;
  38. String thumbnail;
  39. String author;
  40. DateTime addtime;
  41. String url;
  42. NewsItem({
  43. required this.id,
  44. required this.title,
  45. required this.category,
  46. required this.thumbnail,
  47. required this.author,
  48. required this.addtime,
  49. required this.url,
  50. });
  51. factory NewsItem.fromJson(Map<String, dynamic> json) => NewsItem(
  52. id: json["id"],
  53. title: json["title"],
  54. category: json["category"],
  55. thumbnail: json["thumbnail"],
  56. author: json["author"],
  57. addtime: DateTime.parse(json["addtime"]),
  58. url: json["url"],
  59. );
  60. Map<String, dynamic> toJson() => {
  61. "id": id,
  62. "title": title,
  63. "category": category,
  64. "thumbnail": thumbnail,
  65. "author": author,
  66. "addtime": addtime.toIso8601String(),
  67. "url": url,
  68. };
  69. }
  70. /// 新闻推荐 request
  71. class NewsRecommendRequestEntity {
  72. String categoryCode;
  73. String channelCode;
  74. String tag;
  75. String keyword;
  76. NewsRecommendRequestEntity({
  77. required this.categoryCode,
  78. required this.channelCode,
  79. required this.tag,
  80. required this.keyword,
  81. });
  82. Map<String, dynamic> toJson() => {
  83. "categoryCode": categoryCode,
  84. "channelCode": channelCode,
  85. "tag": tag,
  86. "keyword": keyword,
  87. };
  88. }

(2) BaseProvider

继承自GetConnect

  1. class BaseProvider extends GetConnect {
  2. static const SERVER_API_URL = "101.42.134.18:8000";
  3. @override
  4. void onInit() {
  5. httpClient.baseUrl = SERVER_API_URL;
  6. // 请求拦截
  7. httpClient.addRequestModifier<void>((request) {
  8. request.headers['token'] = '12345678';
  9. return request;
  10. });
  11. // 响应拦截
  12. httpClient.addResponseModifier((request, response) {
  13. return response;
  14. });
  15. }
  16. }

2 法一(最麻烦)

  • Provider 提供者模式, 由它决定从哪里、提供什么
  • Repository 用来处理拉取数据细节
  • Controller 控制器层 只要处理业务就行

image.png

(1) Provider

  1. import 'package:fltest/common/utils/base_provider.dart';
  2. import 'package:get/get.dart';
  3. abstract class INewsProvider {
  4. Future<Response<NewsPageListResponseEntity>> getNews();
  5. }
  6. class NewsProvider extends BaseProvider implements INewsProvider {
  7. // 新闻分页
  8. // @override
  9. // Future<Response<NewsPageListResponseEntity>> getNews() => get("/news");
  10. @override
  11. Future<Response<NewsPageListResponseEntity>> getNews() async {
  12. var response = await get("/news");
  13. var data = NewsPageListResponseEntity.fromJson(response.body);
  14. return Response(
  15. statusCode: response.statusCode,
  16. statusText: response.statusText,
  17. body: data,
  18. );
  19. }
  20. }

(2) Repository

  1. import 'package:fltest/pages/get_connect/provider.dart';
  2. abstract class INewsRepository {
  3. Future<NewsPageListResponseEntity> getNews();
  4. }
  5. class NewsRepository implements INewsRepository {
  6. NewsRepository({required this.provider});
  7. final INewsProvider provider;
  8. @override
  9. Future<NewsPageListResponseEntity> getNews() async {
  10. final response = await provider.getNews();
  11. if (response.status.hasError) {
  12. return Future.error(response.statusText!);
  13. } else {
  14. return response.body!;
  15. }
  16. }
  17. }

(3) Controller

  1. class NewsController extends SuperController<NewsPageListResponseEntity> {
  2. NewsController({required this.repository});
  3. final INewsRepository repository;
  4. @override
  5. void onInit() {
  6. super.onInit();
  7. }
  8. // 拉取新闻列表
  9. Future<void> getNewsPageList() async {
  10. append(() => repository.getNews);
  11. }
  12. @override
  13. void onReady() {
  14. print('The build method is done. '
  15. 'Your controller is ready to call dialogs and snackbars');
  16. super.onReady();
  17. }
  18. @override
  19. void onClose() {
  20. print('onClose called');
  21. super.onClose();
  22. }
  23. @override
  24. void didChangeMetrics() {
  25. print('the window size did change');
  26. super.didChangeMetrics();
  27. }
  28. @override
  29. void didChangePlatformBrightness() {
  30. print('platform change ThemeMode');
  31. super.didChangePlatformBrightness();
  32. }
  33. @override
  34. Future<bool> didPushRoute(String route) {
  35. print('the route $route will be open');
  36. return super.didPushRoute(route);
  37. }
  38. @override
  39. Future<bool> didPopRoute() {
  40. print('the current route will be closed');
  41. return super.didPopRoute();
  42. }
  43. @override
  44. void onDetached() {
  45. print('onDetached called');
  46. }
  47. @override
  48. void onInactive() {
  49. print('onInative called');
  50. }
  51. @override
  52. void onPaused() {
  53. print('onPaused called');
  54. }
  55. @override
  56. void onResumed() {
  57. print('onResumed called');
  58. }
  59. }

(4) GetView

  1. class NewsView extends GetView<NewsController> {
  2. NewsView({Key? key}) : super(key: key);
  3. _buildListView(NewsPageListResponseEntity? state) {
  4. return ListView.separated(
  5. itemCount: state != null ? state.items!.length : 0,
  6. itemBuilder: (context, index) {
  7. final NewsItem item = state!.items![index];
  8. return ListTile(
  9. onTap: () => null,
  10. title: Text(item.title),
  11. trailing: Text("分类 ${item.category}"),
  12. );
  13. },
  14. separatorBuilder: (BuildContext context, int index) {
  15. return Divider();
  16. },
  17. );
  18. }
  19. @override
  20. Widget build(BuildContext context) {
  21. return Scaffold(
  22. appBar: AppBar(
  23. title: Text("GetConnect Page"),
  24. ),
  25. body: controller.obx(
  26. (state) => _buildListView(state),
  27. onEmpty: Text("onEmpty"),
  28. onLoading: Center(
  29. child: Column(
  30. children: [
  31. Text("没有数据"),
  32. ElevatedButton(
  33. onPressed: () {
  34. controller.getNewsPageList();
  35. },
  36. child: Text('拉取数据'),
  37. ),
  38. ],
  39. ),
  40. ),
  41. onError: (err) => Text("onEmpty" + err.toString()),
  42. ),
  43. );
  44. }
  45. }

(5) Bindings

  1. class NewsBinding implements Bindings {
  2. @override
  3. void dependencies() {
  4. Get.lazyPut<INewsProvider>(() => NewsProvider());
  5. Get.lazyPut<INewsRepository>(() => NewsRepository(provider: Get.find()));
  6. Get.lazyPut(() => NewsController(repository: Get.find()));
  7. }
  8. }

(6) Route

  1. GetPage(
  2. name: AppRoutes.GetConnect,
  3. binding: NewsBinding(),
  4. page: () => NewsView(),
  5. ),

3 法二(适中)

使用StateMixin可以节省一些代码, 不用写Repository

(1) provider

  1. import 'package:fltest/common/utils/base_provider.dart';
  2. import 'package:get/get.dart';
  3. abstract class INewsProvider {
  4. Future<Response> getNews();
  5. }
  6. class NewsStateMixinProvider extends BaseProvider implements INewsProvider {
  7. @override
  8. Future<Response> getNews() => get("/news");
  9. }

(2) controller

  1. class NewsStateMixinController extends GetxController
  2. with StateMixin<NewsPageListResponseEntity> {
  3. final NewsStateMixinProvider provider;
  4. NewsStateMixinController({required this.provider});
  5. // 拉取新闻列表
  6. Future<void> getNewsPageList() async {
  7. // 获取数据
  8. final Response response = await provider.getNews();
  9. // 判断,如果有错误
  10. if (response.hasError) {
  11. // 改变数据,传入错误状态,在ui中会处理这些错误
  12. change(null, status: RxStatus.error(response.statusText));
  13. } else {
  14. // 否则,存储数据,改变状态为成功
  15. var data = NewsPageListResponseEntity.fromJson(response.body);
  16. change(data, status: RxStatus.success());
  17. }
  18. }
  19. }

3 法三(最简单)

(1) http

  1. import 'package:cookie_jar/cookie_jar.dart';
  2. import 'package:dio/dio.dart';
  3. import 'package:dio_cookie_manager/dio_cookie_manager.dart';
  4. const SERVER_API_URL = "101.42.134.18:8000";
  5. class HttpUtil {
  6. static final HttpUtil _instance = HttpUtil._internal();
  7. factory HttpUtil() => _instance;
  8. late Dio dio;
  9. HttpUtil._internal() {
  10. // BaseOptions、Options、RequestOptions 都可以配置参数,优先级别依次递增,且可以根据优先级别覆盖参数
  11. BaseOptions options = BaseOptions(
  12. // 请求基地址,可以包含子路径
  13. baseUrl: SERVER_API_URL,
  14. // baseUrl: storage.read(key: STORAGE_KEY_APIURL) ?? SERVICE_API_BASEURL,
  15. //连接服务器超时时间,单位是毫秒.
  16. connectTimeout: 10000,
  17. // 响应流上前后两次接受到数据的间隔,单位为毫秒。
  18. receiveTimeout: 5000,
  19. // Http请求头.
  20. headers: {},
  21. // 请求的Content-Type,默认值是"application/json; charset=utf-8".
  22. // 如果您想以"application/x-www-form-urlencoded"格式编码请求数据,
  23. // 可以设置此选项为 `Headers.formUrlEncodedContentType`, 这样[Dio]
  24. // 就会自动编码请求体.
  25. contentType: 'application/json; charset=utf-8',
  26. // [responseType] 表示期望以那种格式(方式)接受响应数据。
  27. // 目前 [ResponseType] 接受三种类型 `JSON`, `STREAM`, `PLAIN`.
  28. //
  29. // 默认值是 `JSON`, 当响应头中content-type为"application/json"时,dio 会自动将响应内容转化为json对象。
  30. // 如果想以二进制方式接受响应数据,如下载一个二进制文件,那么可以使用 `STREAM`.
  31. //
  32. // 如果想以文本(字符串)格式接收响应数据,请使用 `PLAIN`.
  33. responseType: ResponseType.json,
  34. );
  35. dio = Dio(options);
  36. // Cookie管理
  37. CookieJar cookieJar = CookieJar();
  38. dio.interceptors.add(CookieManager(cookieJar));
  39. }
  40. // restful get 操作
  41. Future get(
  42. String path, {
  43. dynamic queryParameters,
  44. Options? options,
  45. }) async {
  46. var response = await dio.get(
  47. path,
  48. queryParameters: queryParameters,
  49. options: options,
  50. );
  51. return response.data;
  52. }
  53. }

(2) api

  1. /// 新闻
  2. class NewsAPI {
  3. /// 翻页
  4. static Future<NewsPageListResponseEntity> newsPageList(
  5. {NewsRecommendRequestEntity? param}) async {
  6. var response = await HttpUtil().get(
  7. '/news',
  8. queryParameters: param?.toJson(),
  9. );
  10. return NewsPageListResponseEntity.fromJson(response);
  11. }
  12. }

(3) controller

  1. class NewsDioController extends GetxController {
  2. var newsPageList =
  3. Rx<NewsPageListResponseEntity>(NewsPageListResponseEntity());
  4. @override
  5. void onInit() {
  6. super.onInit();
  7. print("onInit");
  8. }
  9. @override
  10. void onClose() {
  11. super.onClose();
  12. print("onClose");
  13. }
  14. getPageList() async {
  15. newsPageList.value = await NewsAPI.newsPageList();
  16. }
  17. }