08-📝Flutter核心知识|网络请求和Json转Model【网络处理封装、Json转Model、Json解析 、自动反序列化配置等】

3,757 阅读3分钟

一、前言

本系列文章旨在快速复习并上手Flutter开发,并在适当分享在项目实战过程中遇到的一些比较有价值的知识内容:

本系列文章内容篇幅如下:

  • 一、了解Flutter开发
      1. Flutter的特性与应用场景
      1. Flutter绘制原理
      1. 与Flutter相关的技术原理
      1. 搭建Flutter开发环境
      1. 创建Flutter项目的几种方式
  • 二、快速入门Flutter开发知识大纲
      1. Dart语言快速入门
      1. Flutter的Widget
  • 三、常见应用功能模块与开源项目
      1. 常见应用功能模块
      1. 不错的开源项目

二、网络请求

Flutter 三方库 搜索地址: pub.dev/

1. 引入网络框架dio

  • pubspec.yaml 引入 dio 框架,并 执行 flutter pub get:
    dependencies:
      dio: ^x.x.x #请使用pub上的最新版本
    
  • 配置网络请求工具:
    • config.dart
      class HttpConfig {
        static const String baseURL = "http://127.0.0.1:3000";
        static const int connectTimeout = 5;// 请求超时时间
        static const int receiveTimeout = 5;// 响应超时时间
      }
      
      class HomeConfig {
        static const int movieCount = 20;
      }
      
    • http_request.dart
      import 'package:dio/dio.dart';
      import 'config.dart';
      
      class HttpRequest {
        // 1. 请求配置
        static final BaseOptions baseOptions = BaseOptions(
          baseUrl: HttpConfig.baseURL,
          connectTimeout: Duration(seconds: HttpConfig.connectTimeout),
          receiveTimeout: Duration(seconds: HttpConfig.receiveTimeout),
        );
      
        static final dioNetworkRequest = Dio(baseOptions);
        // 2. 网络拦截器
        static final InterceptorsWrapper dInter = InterceptorsWrapper(
            onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
              // 如果你想完成请求并返回一些自定义数据,你可以使用 `handler.resolve(response)`。
              // 如果你想终止请求并触发一个错误,你可以使用 `handler.reject(error)`。
              print("path:${options.path}");
              if (options.path == "/" ){
                print("拦截了一个请求:${options.path}");
              }
      
              return handler.next(options);
            },
            onResponse: (Response response, ResponseInterceptorHandler handler) {
              // 如果你想终止请求并触发一个错误,你可以使用 `handler.reject(error)`。
              if (response.statusCode == "200" ){
                print("拦截了一个请求:${response.statusCode}");
              }
      
              // print(response.data);
      
              return handler.next(response);
            },
            onError: (DioException error, ErrorInterceptorHandler handler) {
              // 如果你想完成请求并返回一些自定义数据,你可以使用 `handler.resolve(response)`。
              print(error);
              return handler.next(error);
          });
      
        // 3. 基本网络请求
        static Future<T> request<T>(String url, {
            String method = "get",
            Map<String, dynamic>? params,
            Interceptor? inter}) async {
            // 1.创建单独配置
            final options = Options(method: method);
            // 全局拦截器
            // 创建默认的全局拦截器
      
            if (!dioNetworkRequest.interceptors.contains(dInter)){
              List<Interceptor> inters = [dInter];
              // 请求单独拦截器
              if (inter != null) {
                inters.add(inter);
              }
              // 统一添加到拦截器中
              dioNetworkRequest.interceptors.addAll(inters);
            }
      
            // 2.发送网络请求
            try {
              Response response = await dioNetworkRequest.request(url, queryParameters: params, options: options);
              return response.data;
            } on DioError catch(e) {
              return Future.error(e);
            }
        }
      
        // 4. get请求
        static Future<T> getRequest<T>(String url, {
          Map<String, dynamic>? params}) async {
            return HttpRequest.request(url,params: params);
        }
      
        // 5. post请求
        static Future<T> postRequest<T>(String url, {
          Map<String, dynamic>? params}) async {
          return HttpRequest.request(url,method: "post",params: params);
        }
      }
      
    • home模块的网络配置:home_request.dart
      import 'http_request.dart';
      import '../models/home_models.dart';
      import 'config.dart';
      
      class HomeRequest {
            static Future<List<MovieItem>> requestMovieList(int start) async{
                  // 1.构建URL
                  final movieURL = "/movie/top250?start=$start&count=${HomeConfig.movieCount}";
      
                  // 2.发送网络请求获取结果
                  final result = await HttpRequest.getRequest(movieURL);
                  final subjects = result["subjects"];
      
                  // 3.将Map转成Model
                  List<MovieItem> movies = [];
                  for (var sub in subjects) {
                    movies.add(MovieItem.fromJson(sub));
                  }
                  return movies;
            }
      }
      

2. 配置网络处理工具

三、JSON解析

1. 加载Json文件/网络请求获取Json报文

  • 网络请求获取Json报文 的 方式 在第二节 已说明

加载Json文件

  • pubspec.yaml添加json文件的路径:(如下示例代码,assets: 部分)
    name: network_request
    description: "A new Flutter project."
    version: 1.0.0+1
    
    environment:
      sdk: '>=3.3.1 <4.0.0'
    dependencies:
      flutter:
        sdk: flutter
      dio: ^5.4.1
      json_annotation: ^4.8.0
      cupertino_icons: ^1.0.6
    
    dev_dependencies:
      build_runner: ^2.3.3
      json_serializable: ^6.7.1
      flutter_test:
        sdk: flutter
    flutter:
      uses-material-design: true
      assets:
        - assets/json/category.json  #json文件路径
        - assets/json/meal.json  #json文件路径
    
  • 执行 flutter pub get引入资源文件

2. 加载JSON,JSON转Model

  • 编写加载代码:
    import 'dart:convert';
    import 'package:flutter/services.dart';
    import '../models/home_models.dart';
    
    class JsonParse {
      static Future<List<CategoryModel>> getCategoryData() async {
        // 1.加载json文件
        final jsonString = await rootBundle.loadString("assets/json/category.json");
    
        // 2.将jsonString转成Map/List
        final result =  json.decode(jsonString);
    
        // 3.将Map中的内容转成一个个对象
        final resultList = result["category"];
        List<CategoryModel> categories = [];
        for (var json in resultList) {
          categories.add(CategoryModel.fromJson(json));
        }
    
        return categories;
      }
    }
    

四、自动反序列化配置

1. 在.yaml文件中引入以下依赖,在终端中运行 flutter pub get 命令获取依赖

dependencies:
  flutter:
   sdk: flutter 
   #使用JsonSerializable生成代码的必须要在需要生成代码的实体类前添加注解@JsonSerializable()
   #使用这个注解我们必须引入json_annotation
  json_annotation: ^4.0.0
dev_dependencies:
  build_runner: ^2.0.0 #dart团队提供的一个生成dart代码文件的外部包
  json_serializable: ^6.0.0 #json自动反序列化

2. 基本使用:

  • 先创建Model类
  • 再添加属性
  • 给类添加默认的构造器:
  • 引入头文件和要生成的模型文件的描述
    • 头文件: import 'package:json_annotation/json_annotation.dart';
    • 要生成的模型文件的描述: part 'xxx.g.dart';
      • 注意:文件名,要小写
      • xxx为当前文件的名字,例如:
        • image.png
  • 在这个类上添加 @JsonSerializable() 注解,并添加 fromJson 和 toJson 方法。
    • json转对象固定写法:_${类名}FromJson(json)
    • 对象转json固定写法:_${类名}ToJson(json)
  • 在终端 执行 生成 模型文件指令:flutter packages pub run build_runner build
import "package:json_annotation/json_annotation.dart";
part 'vip_open_record_model.g.dart';//注意,文件名,都要小写

@JsonSerializable() // 添加注解
class VipOpenRecordModel { 
    final int? orderId; 
    final String? orderSn;
    final String? skuName;
    final String? skuCode; 
    final int? memberType;
    final String? startTime;
    final String? endTime;
    final String? payTime;
    final num? price;
    final String? payType; 
    final int? days; 
    // 默认的构造器:
    VipOpenRecordModel({ this.orderId, 
        this.orderSn,
        this.skuName,
        this.skuCode,
        this.memberType,
        this.startTime,
        this.endTime, 
        this.payTime, 
        this.price,
        this.payType,
        this.days,
    });

factory VipOpenRecordModel.fromJson(Map<String, dynamic> json) => _$VipOpenRecordModelFromJson(json); // _${类名}FromJson(json) json转对象固定写法 
Map<String, dynamic> toJson() => _$VipOpenRecordModelToJson(this); // _${类名}ToJson(json)对象转json固定写法 }

3. 自动生成反序列文件,控制台输入指令:

flutter packages pub run build_runner build

下面是自动生成的文件,注意:自动生成的内容不可以手动修改,如果需要增加字段,修改模型之后重新执行指令即可

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'vip_open_record_model.dart';

// **************************************************************************

// JsonSerializableGenerator

// **************************************************************************

VipOpenRecordModel _$VipOpenRecordModelFromJson(Map<String, dynamic> json) =>

VipOpenRecordModel(

    orderId: json['orderId'] as int?,

    orderSn: json['orderSn'] as String?,

    skuName: json['skuName'] as String?,

    skuCode: json['skuCode'] as String?,

    memberType: json['memberType'] as int?,

    startTime: json['startTime'] as String?,

    endTime: json['endTime'] as String?,

    payTime: json['payTime'] as String?,

    price: json['price'] as num?,

    payType: json['payType'] as String?,

    days: json['days'] as int?,

);

Map<String, dynamic> _$VipOpenRecordModelToJson(VipOpenRecordModel instance) =>

<String, dynamic>{
    'orderId': instance.orderId,

    'orderSn': instance.orderSn,

    'skuName': instance.skuName,

    'skuCode': instance.skuCode,

    'memberType': instance.memberType,

    'startTime': instance.startTime,

    'endTime': instance.endTime,

    'payTime': instance.payTime,

    'price': instance.price,

    'payType': instance.payType,

    'days': instance.days,
};

4.然后就可以正常使用了:

List dataList = data;

for (var element in dataList) {
    VipOpenRecordModel model = VipOpenRecordModel.fromJson(element);
    openModelList.add(model);
}

使用自动反序列工具可以避免浪费时间在没有意义的工作上,同时也会避免因为书写失误造成的数据解析失败的问题,从而提高开发效率。