Flutter 如何使用第三方库http、json转模型

1,508 阅读3分钟

和谐学习!不急不躁!!我是你们的老朋友小青龙~

  • Flutter常用的网络库,以httpdio库为多,本文以http为例。

  • json转模型的两种方法:

如果想了解dio的使用,请前往

1、如何集合第三方库http

image.png

image.png

点击它会复制库名+版本号

image.png

来到我们的工程目录,找到并打开pubspec.yaml

image.png

2、如何使用第三方库http呢?

在你需要使用网络的地方导入

import 'package:http/http.dart' as http;

然后在initState

class HomePage extends StatefulWidget {
    @override
    _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {

    @override
    Widget build(BuildContext context) {
    // 此处省略无关代码
    ...
    }

    @override
    void initState() {
      super.initState();
      //发送一个get请求
      sendHttp();
    }

    void sendHttp() async {
      var url = Uri.parse('http://rap2api.taobao.org/app/mock/293606/api/chat/list');
      var response = await http.get(url);
      print('打印看看 reasonPhrase ->${response.reasonPhrase}');
      print('打印看看 request-> ${response.request}');
      print('打印看看 statusCode-> ${response.statusCode}');
      print('打印看看 contentLength-> ${response.contentLength}');
      print('打印看看 headers-> ${response.headers}');
      print('打印看看 isRedirect-> ${response.isRedirect}');
      print('打印看看 persistentConnection-> ${response.persistentConnection}');
      print('打印看看 body->${response.body}');
    }
}

打印结果:

image.png

3、Json字符串转数据模型(json_annotation

前面,我们顺利的拿到了网络请求返回的Json字符串数据,那么如何转化成我们定义好的模型类呢?

3.1、配置pubspec.yaml

这里我们需要用到几个库

  • json_annotation

  • build_runner

  • json_serializable

打开pubspec.yaml,开始配置、加载第三方库

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^1.0.2
  http: ^0.13.4
  json_annotation: ^4.3.0


dev_dependencies:
  flutter_test:
    sdk: flutter

  
  flutter_lints: ^1.0.0
  json_serializable: ^6.0.1
  build_runner: ^1.8.0

image.png

3.2、配置数据模型

创建一个student.dart文件,并按照下图模版填好,以后其他模型数据也可以这么来写

import 'package:json_annotation/json_annotation.dart';

part 'student.g.dart';

@JsonSerializable()
class StudentRoot {
  String code;
  List<Student> list;
  StudentRoot({required this.code, required this.list});

  factory StudentRoot.fromJson(Map<String, dynamic> json) =>
      _$StudentRootFromJson(json);

  Map<String, dynamic> toJson() => _$StudentRootToJson(this);
}

@JsonSerializable()
class Student {
  String studentName;
  int studentAge;
  int studentId;
  Student(
      {required this.studentName,
      required this.studentAge,
      required this.studentId});

  factory Student.fromJson(Map<String, dynamic> json) =>
      _$StudentFromJson(json);

  // Map<String, dynamic> toJson(Student instance) => _$StudentToJson(instance);
  Map<String, dynamic> toJson() => _$StudentToJson(this);
}

image.png

我们发现part 'student.g.dart';这行报错了,先不用管后面会生成的

3.3、准备测试接口

http://rap2api.taobao.org/app/mock/293606/api/flutter/jsonmodel

3.4、窗口执行命令

打开Android studio 左下角自带Terminal窗口

image.png

输入以下命令并按下回车

flutter pub run build_runner build

执行完是这样的

image.png

当然有的小伙伴可能发现执行完:

  • part 'student.g.dart';这行依旧报错

  • student.g.dart这个文件也没有生成

小编也遇到过这个问题,不妨试试以下几个方法:

  • 关闭那几个文件创建,重新运行项目

  • 重启Android studio

3.5、使用

前面的几步都是铺垫,接下来才是我们的重头戏。

使用方法

void sendHttp() async {
  var url = Uri.parse(
      'http://rap2api.taobao.org/app/mock/293606/api/flutter/jsonmodel');
  var response = await http.get(url);
  print('JsonPage打印看看 body->${response.body}');

  // Json转StudentRoot
  Map mapJson = jsonDecode(response.body);
  String data = utf8.decode(response.bodyBytes);
  StudentRoot studentRoot = StudentRoot.fromJson(json.decode(data));

  // StudentRoot转Json
  String jsonAfter = jsonEncode(studentRoot);

  // 下面是测试代码~~
  print('查看请求状态码 ---${studentRoot.code}');
  print('查看list ---${studentRoot.list}');
  for (var listChild in studentRoot.list) {
    print('查看list子元素 ---${listChild.studentName}');
  }

  print('StudentRoot转Json打印 $jsonAfter');
}

运行结果:

image.png

如何使用在线工具quicktype

image.png 上面截图忘记标注了,类名可以在左上角StuRoot处改动

复制代码,新建模型文件stu.dart,粘贴即可

image.png

来到我们的json_page.dart测试文件, 导入头文件

import 'package:ssj_wechat_demo/models/stu.dart';

使用方法也很简单~

// StudentRoot studentRoot = StudentRoot.fromJson(json.decode(data));
// 注释上面这行,改用下面这行,其它不动
StuRoot studentRoot = stuRootFromJson(response.body);

image.png

Future的使用

有时候,我们希望在异步任务结束之后再主动做一些事情,这个时候可以考虑用官网提供的Future

Future带有一个then属性,then是一个带有value参数的方法,即在返回Future之后会主动调用then里的方法。

具体使用如下:

void initState() {
  super.initState();
  print('JsonPage - 开始请求');
  sendHttp().then((value) {
    print('then代码块开始执行->$value');
  });
  print('JsonPage - 请求执行完毕');
}

Future<List<ListElement>> sendHttp() async {
  var url = Uri.parse(
      'http://rap2api.taobao.org/app/mock/293606/api/flutter/jsonmodel');
  var response = await http.get(url);
  if (response.statusCode == 200) {
    // Json转StudentRoot
    Map mapJson = jsonDecode(response.body);
    String data = utf8.decode(response.bodyBytes);
    // StudentRoot studentRoot = StudentRoot.fromJson(json.decode(data));
    // 注释上面这行,改用下面这行,其它不动
    StuRoot studentRoot = stuRootFromJson(response.body);
    // StudentRoot转Json
    String jsonAfter = jsonEncode(studentRoot);

    return studentRoot.list;
  } else {
    print('请求失败');
    throw Exception('请求失败,code:${response.statusCode}');
  }
}

运行效果:

image.png

从打印结果看,then代码块确实是最后执行的,value参数则是Future包含的返回值

FutureBuilder

方法都可以通过返回Future来实现异步执行任务,那么我们的widget组件是否也有类似的东西呢,还真别说,官网为我们提供了这么一个组件:FutureBuilder

FutureBuilder有两个属性

  • future
  • builder

首先,我们来看下怎么用:

Widget build(BuildContext context) {
  return Container(
    child: FutureBuilder(
        future: sendHttp(),
        builder: (BuildContext context, AsyncSnapshot snapshot) {
          print('snapshot:$snapshot');
          return Container(
            child: const Text('我加载完了~'),
          );
        }),
  );
}

运行效果:

image.png

我们发现,builder方法被执行了两次,分别是Future返回前后各执行一次。利用这个特性,我们可以对一些耗时请求,添加友好提示,比如“加载ing~”等字眼。

说干就干:

// sendHttp()跟前面一样
Widget build(BuildContext context) {
  return Container(
    child: FutureBuilder(
        future: sendHttp(),
        builder: (BuildContext context, AsyncSnapshot snapshot) {
          print('snapshot:$snapshot');
          if (snapshot.data == null) {
            return Container(
              color: Colors.red,
              child: const Center(
                child: Text(
                  '加载ing~',
                  textAlign: TextAlign.center,
                ),
              ),
            );
          } else {
          // else代码块可以根据实际需求进行修改
            return Container(
              color: Colors.yellow,
              child: const Center(
                child: Text(
                  '我加载完了~',
                  textAlign: TextAlign.center,
                ),
              ),
            );
          }
        }),
  );
}

效果图:

IMB_CfSBdl.GIF

另外, FutureBuilder最适用的是数据一次就获取,不需要频繁获取数据,改变页面内容的情况下,因为future的方法不能够重新执行,所以要刷新页面可能需要刷新整个widget。