Flutter第6天--异步-IO+网络+json

作者: e4e52c116681 | 来源:发表于2018-12-21 19:43 被阅读8次

    Flutter七日游第六天:2018-12-21 天气:雨-阴

    零、前言

    对于掘金的首页已经无力吐槽,也引发了我的一些思考,导致今天的心情不太好
    毕竟我Flutter系列的文章都没上过首页(自认为比驳来驳去的有意义很多),
    虽然说并不在乎,但多少有些不平。对于那条沸点更是问的...好吧,与我无关,进文
    对于问我怎么学习的人,空口白牙说的是鸡汤,我不喜欢喝也不喜欢做。
    文中根据实际情况,分享一些个人的编程心得,自己参考一下,取长补短


    一、单线程模型下的异步操作

    • 为什么强调是单线程:Dart是单线程模型,单线程模型,单线程模型!!!

    什么是单线程:就是你是一个人在战斗
    什么是异步: 比如你要烧水(耗时操作),并不需要傻傻地等着水开才能去做下一件事(扫地)
    只要开火(方法调用),然后你就可以去扫地(执行异步任务下面的方法),水烧开鸣叫(回调), 去冲水(处理异步任务结果)。

    • Dart异步编程的方式:Future和Stream

    Future相当于40米大砍刀,Stream相当于一捆40米大砍刀
    dart提供了关键字async(异步)await(延迟执行),相当于普通的便捷的小匕首


    1.asyncawait的简单使用

    感觉网上一些教程上来就告诉你什么样是错的,然后一步步纠正...最后都没有完整代码总结一下
    我想最起码应该先给个正确的示范吧...然后再说错误情况

    1.1:最简单的文件读取
    //根据名称读取文件
    readFile(name) {
      //创建文件对象
      var file = File(name);
      return file.readAsString();
    }
    
    //读取文件成功
    readOk() async{
      var result = await readFile(r"C:\Users\Administrator\Desktop\应龙.txt");
      print(result);
    }
    
    main()  {
      readOk();
      print("我是第几?");
    }
    
    async+await.png 文件读取.png

    函数执行过程中看到了async(烧水)会先去执行下面的操作(扫地),水烧开await放行,print(result);(冲水)


    1.2.asyncawait的分析

    也许你就问,不加async或await会怎么样?不同时加又会怎么样?
    不加async或await:就像平常代码一样顺序执行
    加async不加await:然并卵
    不加async加await:报错


    2.去拿我40米大砍刀:Future

    可以看出:file.readAsString()返回的是:Future<String>,

    Future.png
    main() {
      var file = File(r"C:\Users\Administrator\Desktop\应龙.txt");
      Future<String> re = file.readAsString();
      re.then((result) {
        print(result);
      });
      print("我是第几?");
    }
    

    这样操作也能达到异步的效果,具体就不深入说了
    有时间打算写一篇:基于Java,Python,JavaScript(ES6+),Dart,node(都是我曾涉及过的)
    综合讨论一下单线程,多线程,同步,异步,毕竟这几个词让我挺烦心


    二、Dart中的IO操作

    1.文件操作的API测试构造函数
    构造函数.png
    File(文件路径)
    File.fromUri(Uri资源路径标识符)
    File.fromRawPath(Uint8List rawPath)
    

    [番外]:如何去认识一个类:Uri为例

    也许看到File.fromUri(Uri uri)你会说Uri我不会,然后就不管了,如果有空就看两眼呗,又不会吃亏
    我的经验是先看它的构造方法,然后再看字段,再总览一下方法名(Ctr+F12)
    如果你对这个类一无所知,还是先看粗略瞄一下文档注释,至少知道干嘛的
    一般都会有一句简洁的话介绍它(英文不会,词典查一下,读原文档:这道坎早晚要过的)
    Android中对Uri有一定的认识,知道它是一个资源定位的标志,就像门牌号吧。

    ---->[注释第一句]------------
    A parsed URI, such as a URL.
    url我们都知道:http://192.168.43.60:8089/file/springboot/data.json
    -------------------------------------------------------------
    它是由一下部分构成的:
    http(协议名)://+ 192.168.43.60(域名) + :8089(端口) + file/springboot/data.json(资源地址)
    
    比如我是资源,你要找我:
    中国://安徽:合肥/瑶海区/XXX路/XXX小区/张风捷特烈
    -------------------------------------------------------------
    
    ---->[Uri核心属性]------------
    factory Uri(
          {String scheme,
          String userInfo,
          String host,
          int port,
          String path,
          Iterable<String> pathSegments,
          String query,
          Map<String, dynamic /*String|Iterable<String>*/ > queryParameters,
          String fragment}) 
    
    不知道的东西去试试呗,反正跑一下又不要钱
    var base = Uri.base;
    print(base);//打印了跟路径--file:///I:/Java/Android/FlutterUnit/toly/
    
    
    ----->[.parse方法测试]
    static Uri parse(String uri, [int start = 0, int end])
    //既然Uri.parse返回一个Uri对象,那么它应该有Uri的相应属性
     var parse = Uri.parse("http://192.168.43.60:8089/file/springboot/data.json");
      print("host=${parse.host}");//192.168.43.60
      print("port=${parse.port}");//8089
      print("path=${parse.path}");//file/springboot/data.json
      print("query=${parse.query}");//
      print("fragment=${parse.fragment}");//
    
    ----->[.http方法测试]
    Uri.http(String authority, String unencodedPath,[Map<String, String> queryParameters]) 
    ----->[.http方法测试,它的注释都写成这样了,你还不会用吗?]-------
    //Creates a new `http` URI from authority, path and query
    //http://example.org/path?q=dart.
    //new Uri.http("example.org", "/path", { "q" : "dart" });
    
    
    如果用File开一个网络的Uri会怎么样:

    学会分析bug不要轻易否定
    首先保证网址是正确的

    url请求.png
    var file = File.fromUri(new Uri.http("192.168.43.60:8089", "/file/springboot/data.json"));
    
    Unhandled exception:
    Unsupported operation: Cannot extract a file path from a http URI
    #0      _Uri.toFilePath (dart:core/uri.dart:2617:7)
    #1      new File.fromUri (dart:io/file.dart:265:49)
    #2      readFile (file:///I:/Java/Android/FlutterUnit/toly/test/base/8_io.dart:11:19)
    

    也许你到走了,会想(当然我也是这样):什么鬼,老子看半天,TM不能用,浪费时间!
    也许你会愤然而去,而我则会去分析错误的原因(这就是面对错误的不同选择)
    前者可能永远也不知道原因,而后者即使最后无果,路上也会有所收获(打字的现在,我还未去分析)


    所以,一起去看看吧
    ---->[bug的链接处:]
     String toFilePath({bool windows}) {
        if (scheme != "" && scheme != "file") {
          throw new UnsupportedError(
              "Cannot extract a file path from a $scheme URI");
        }
        
    // 原来是scheme的锅------那scheme是什么呢?
      var uri = new Uri.http("192.168.43.60:8089", "/file/springboot/data.json");
      print(uri.scheme);//http
      
    //可见-- new Uri.http的scheme是http,而这里不是file所以报错
     至少你的知识库中多收录了一条信息:File.fromUri()不能访问非file类型的Uri
     也知道了scheme大概是什么东西,知识库就是这样一点一点自己累积的
     
    ---->[这么重要的限制,方法上能不注明吗?]-------
      /**
       * Create a File object from a URI.
       *
       * If [uri] cannot reference a file this throws [UnsupportedError].
       如果uri未涉及 file会报错: UnsupportedError
       */
      factory File.fromUri(Uri uri) => new File(uri.toFilePath());
    
    好吧,是一开始没注意,到此一个错误就可以画上句号了  
    
    错误不可怕,可怕的是你不知道为什么而导致以后还会犯,总之踩的坑多了,就会知道坑在那里
    也许别人给说你那有坑,第一次小心的过去了,下一次没人提醒你,你可能就掉下去  
    

    file的Uri是什么鬼?

    也许你不知道,文件拖到浏览器里,也是能打开的,你所见的就是feil类型的Uri

    file.png
      //源码上面还有很多注释自己看....
    * // file:///C:/xxx/yyy
     * new Uri.file(r"C:\xxx\yyy", windows: true);
     */
    factory Uri.file(String path, {bool windows})
    
    然后你就会用File.fromUri了:
    var file = File.fromUri(Uri.parse("file:///F:/SpringBootFiles/file/springboot/data.json"));
    
    uri访问资源.png

    从一个小的API开始,让自己尽可能去多认识一些事物,并不是说你要把源码都理得很清楚
    在自己接受范围的150%之内可以去尝试,失败了没有关系,总比看那些驳来驳去的文章有意义

    如果你想提高自己(这句话也是自勉):
    不要让自己总走在平坦的路上,有时登高望远方能窥见美景,也不要一心只走险峰,小心失足。
    今天心情不佳,废话有点多,听得进去的就听,听不进去的就无视,如果要驳我,请在评论区!!

    [番外结束]

    2.File和Directory的常见Api

    Java里文件夹也是File对象,Dart里区分了出来
    很有意思,File和Directory的Api基本上都是同步,异步成对出现

    FileSystemEntity.png
    2.1:递归创建文件夹

    默认recursive是false,只能创建下一级

    递归创建文件夹.png
    main() {
      var dir = Directory(r"C:\Users\Administrator\Desktop\dart\test\all\li");
      dir.createSync(recursive: true);
    }
    

    2.2:列出所有的文件
    main() async {
      var dir = Directory(r"E:\Material\MyUI");
      var list = dir.list();//默认非递归及只列出一级
      list.forEach((fs){
        print(fs.path);
      });
    }
    
    目录列表.png
    main() async {
      var dir = Directory(r"E:\Material\MyUI");
      var list = dir.list(recursive: true);//递归列出所有文件
      list.forEach((fs){
        print(fs.path);
      });
    }
    

    目录下所有文件都列出来,就不贴图了


    2.3:重命名

    看一下就行了

      var dir = Directory(r"C:\Users\Administrator\Desktop\dart\test\all\li");
      dir.rename(r"C:\Users\Administrator\Desktop\dart\test\all\hello");
    

    3.File对象的常用操作:
    //根据名称读取文件
    readFile(name) async {
      //创建文件对象
      var file = File(name);
      try {
        //判断是否存在
        bool exists = await file.exists();
        if (exists) {
          //如果存在
          print(await file.length()); //文件大小(字节)---137
          print(await file.lastModified()); //最后修改时间---2018-12-21 13:49:35.000
          print(file.parent.path); //获取父文件夹的路径---C:\Users\Administrator\Desktop\dart
          return await file.readAsString(); //读取文件并返回
        } else {
          await file.create(recursive: true); //不存在则创建文件
          return "未发现文件,已为您创建!Dart机器人:2333";
        }
      } catch (e) {
        //异常处理
        print(e);
      }
    }
    

    另外还有几种不同的打开方式,基本上Java都包含了,看名字也知道是什么


    4.文件的写入:

    和java一样,默认全换:想要追加:参数加mode: FileMode.append

    main() async {
      wirte(r"C:\Users\Administrator\Desktop\dart\应龙.txt");
    }
    
    wirte(name) async{
      var file = File(name);
      file.writeAsString("海的彼岸有我未曾见证的风采");
    }
    
    默认全部替换.png

    三、关于移动端的文件读取问题

    1.路径问题

    path_provider: ^0.4.1:提供了三个路径,勉强用用吧

    localPath() async {
      try {
        print('临时目录: ' + (await getTemporaryDirectory()).path);
        //----/data/user/0/com.toly1994.toly/cache
        print('文档目录: ' + (await getApplicationDocumentsDirectory()).path);
        //----/data/user/0/com.toly1994.toly/app_flutter
        print('sd卡目录: ' + (await getExternalStorageDirectory()).path);
        //----/storage/emulated/0
      } catch (err) {
        print(err);
      }
    }
    

    2.动态权限申请问题

    simple_permissions: ^0.1.9:提供了动态权限申请

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    
    readFormSD() async {
      try {
        var perm =
            SimplePermissions.requestPermission(Permission.ReadExternalStorage);
        var sdPath = getExternalStorageDirectory();
        sdPath.then((file) {
          perm.then((v) async {
            var res = await readFile(file.path + "/应龙.txt");
            print(res);
          });
        });
      } catch (err) {
        print(err);
      }
    }
    
    权限+读取.png

    好了,这样知识就对接完毕


    3.小测试:列出sd卡的文件

    比较基础,就是读取文件夹下的内容,设置给ListView的Item

    列出sd卡的文件.png
    import 'package:flutter/cupertino.dart';
    import 'package:flutter/material.dart';
    import 'package:path_provider/path_provider.dart';
    import 'package:simple_permissions/simple_permissions.dart';
    
    class ListFilePage extends StatefulWidget {
      @override
      _ListFilePageState createState() => _ListFilePageState();
    }
    
    class _ListFilePageState extends State<ListFilePage>
        with SingleTickerProviderStateMixin {
      List<String> _files = [];
    
      @override
      void initState() {
        super.initState();
        localPath();
      }
    
      @override
      Widget build(BuildContext context) {
        //生成listView
        var listview = ListView.builder(
          itemCount: _files.length,
          itemBuilder: (BuildContext context, int index) {
            return Column(
              children: <Widget>[
                Container(
                    color: Colors.white,
                    padding: EdgeInsets.all(15),
                    child: renderItem(index))
              ],
            );
          },
        );
    
        return Scaffold(
          appBar: AppBar(
            title: Text("张风捷特烈"),
          ),
          body: listview,
        );
      }
    
    // 添加所有SD卡文件名称
      localPath() {
        try {
          var perm =
              SimplePermissions.requestPermission(Permission.ReadExternalStorage);
          var sdPath = getExternalStorageDirectory();
          sdPath.then((file) {
            perm.then((v) {
              file.list().forEach((i) {
                _files.add(i.path);
              });
              setState(() {});
            });
          });
        } catch (err) {
          print(err);
        }
      }
    
      //渲染单条目
      renderItem(index) {
        return Row(
          children: <Widget>[
            Icon(
              Icons.extension,
              color: Colors.blue,
            ),
            Expanded(
                child: Padding(
              padding: EdgeInsets.only(left: 20),
              child: Text(
                _files[index],
                style: TextStyle(fontSize: 18),
              ),
            )),
            Icon(Icons.arrow_forward),
            Divider(height: 1)
          ],
        );
      }
    }
    
    

    三、Dart中的网络请求操作:

    0.添加依赖:在pubspec.yaml的dependencies下
    http: ^0.11.3+17
    

    我的服务器上提供了一些网络请求的Api,如果你想自己搭建服务器接口,请看这篇
    来回顾一下接口的api:

    查询接口:GET请求--------------------------------------------
    ----查询所有:
    http://www.toly1994.com:8089/api/android/note
    ----查询偏移12条,查询12条(即12条为一页的第2页):
    http://www.toly1994.com:8089/api/android/note/12/12
    ----按区域查询(A为Android数据,SB为SpringBoot数据,Re为React数据)
    http://www.toly1994.com:8089/api/android/note/area/A
    http://www.toly1994.com:8089/api/android/note/area/A/12/12
    ----按部分名称查询
    http://www.toly1994.com:8089/api/android/note/name/材料
    http://www.toly1994.com:8089/api/android/note/name/材料/2/2
    ----按类型名称查询(类型定义表见第一篇)
    http://www.toly1994.com:8089/api/android/note/name/ABCS
    http://www.toly1994.com:8089/api/android/note/name/ABCS/2/2
    ----按id名称查
    http://www.toly1994.com:8089/api/android/note/12
    
    添改删接口---------------------------------------------------------------
    添-POST请求:http://www.toly1994.com:8089/api/android/note
    更新-PUT请求:http://www.toly1994.com:8089/api/android/note/1
    删-DELETE请求:http://www.toly1994.com:8089/api/android/note/1
    

    1.get请求

    注:client你随便取什么名字都行,客户端访问服务端,所以我用client

    import 'package:http/http.dart' as client;
    
    main() {
      getData((data) {
        print(data);
      });
    }
    
    getData(cbk) async {
      var api = 'http://www.toly1994.com:8089/api/android/note/100';
      try {
        final response = await client.get(api);
        if (response.statusCode == 200) {
          cbk(response.body);
        }
      } catch (e) {
        print(e);
      }
    }
    
    访问成功.png

    如果你觉得回调有点low,也完全可以用Future(用什么不是重点,怎么简洁怎么来)

    main() {
      getData().then((data){
        print(data);
      });
    }
    
    Future<String> getData() async {
      try {
        final response = await client.get('http://www.toly1994.com:8089/api/android/note/100');
        if (response.statusCode == 200) {
          return response.body;
        }
      } catch (e) {
        print(e);
      }
    }
    

    2.post请求:插入数据
    main() {
      add((data) {
        print(data);
      });
    }
    
    add(cbk) async {
      var api = 'http://www.toly1994.com:8089/api/android/note';
      var item = {
        "type": "C",
        "name": "插入测试",
        "localPath": "null",
        "jianshuUrl": "https://www.jianshu.com/p/12f8ab32591a",
        "juejinUrl": "null",
        "imgUrl":
            "http://toly1994.com:8089/imgs/android/c3af376135a7abe0655c908195b271db.png",
        "createTime": "2018-09-06",
        "info": "null",
        "area": "A"
      };
      try {
        final response = await client.post(api, body: item);
        if (response.statusCode == 200) {
          cbk(response.body);
        }
      } catch (e) {
        print(e);
      }
    }
    
    插入操作.png
    3.put请求:更新数据
    main() {
      set((data) {
        print(data);
      });
    }
    
    set(cbk) async {
      var api = 'http://www.toly1994.com:8089/api/android/note/199';
      var item = {
        "type": "C",
        "name": "修改测试",
        "localPath": "null",
        "jianshuUrl": "https://www.jianshu.com/p/12f8ab32591a",
        "juejinUrl": "null",
        "imgUrl":
        "http://toly1994.com:8089/imgs/android/c3af376135a7abe0655c908195b271db.png",
        "createTime": "2018-09-06",
        "info": "null",
        "area": "A"
      };
    
      try {
        final response = await client.put(api, body: item);
        if (response.statusCode == 200) {
          cbk(response.body);
        }
      } catch (e) {
        print(e);
      }
    }
    
    修改测试.png
    4.delete请求:删除操作
    main() {
      delete((data) {
        print(data);
      });
    }
    
    delete(cbk) async {
      var api = 'http://www.toly1994.com:8089/api/android/note/199';
    
      try {
        final response = await client.delete(api);
    
        if (response.statusCode == 200) {
          cbk(response.body);
        }
      } catch (e) {
        print(e);
      }
    }
    
    删除成功.png

    四、关于Json

    一般都是解析服务器端传来的json,非后端基本不用生产json

    1.将json转化为对象
    {
        "id": 100,
        "type": "绘图相关",
        "name": "D5-Android绘图之让图形动起来",
        "localPath": "null",
        "jianshuUrl": "https://www.jianshu.com/p/12f8ab32591a",
        "juejinUrl": "null",
        "imgUrl": "http://toly1994.com:8089/imgs/android/c3af376135a7abe0655c908195b271db.png",
        "createTime": "2018-09-06",
        "info": "以前在Html利用js控制SVG或canvas进行运动模拟。浏览器自带window.requestAnimationFrame能不断执行渲染在这...",
        "area": "A"
      }
    
    1.1:创建实体类,创建构造方法
    class NoteBean {
      int id;
      String type;
      String name;
      String localPath;
      String jianshuUrl;
      String juejinUrl;
      String imgUrl;
      String createTime;
      String info;
      String area;
    
      NoteBean.fromJson(Map<String, dynamic> map)
          : id = map['id'],
            name = map['name'],
            localPath = map['localPath'],
            jianshuUrl = map['jianshuUrl'],
            juejinUrl = map['juejinUrl'],
            imgUrl = map['imgUrl'],
            createTime = map['createTime'],
            info = map['info'],
            area = map['area'];
    }
    
        var j =
            '{"id":100,"type":"绘图相关","name":"D5-Android绘图之让图形动起来","localPath":"null","jianshuUrl":"https://www.jianshu.com/p/12f8ab32591a","juejinUrl":"null","imgUrl":"http://toly1994.com:8089/imgs/android/c3af376135a7abe0655c908195b271db.png","createTime":"2018-09-06","info":"以前在Html利用js控制SVG或canvas进行运动模拟。浏览器自带window.requestAnimationFrame能不断执行渲染在这...","area":"A"}';
        var noteBean = NoteBean.fromJson(json.decode(j));
        print(noteBean.name);//D5-Android绘图之让图形动起来
    

    2.复对的Json转化(也就是Json里套Json)
    2.1:待处理的Json字符串
    {
      "code": 200,
      "msg": "操作成功",
      "data": {
        "id": 100,
        "type": "绘图相关",
        "name": "D5-Android绘图之让图形动起来",
        "localPath": "null",
        "jianshuUrl": "https://www.jianshu.com/p/12f8ab32591a",
        "juejinUrl": "null",
        "imgUrl": "http://toly1994.com:8089/imgs/android/c3af376135a7abe0655c908195b271db.png",
        "createTime": "2018-09-06",
        "info": "以前在Html利用js控制SVG或canvas进行运动模拟。浏览器自带window.requestAnimationFrame能不断执行渲染在这...",
        "area": "A"
      }
    }
    

    2.2:增加实体类ResultBean
    class ResultBean {
      String msg;
      int code;
      NoteBean data;
    
      ResultBean.fromJson(Map<String, dynamic> map)
          : msg = map['msg'],
            code = map['code'],
            data = NoteBean.fromJson(map['data']);
    }
    

    2.3:使用:
      var j =
          '{"code":200,"msg":"操作成功","data":{"id":100,"type":"绘图相关","name":"D5-Android绘图之让图形动起来","localPath":"null","jianshuUrl":"https://www.jianshu.com/p/12f8ab32591a","juejinUrl":"null","imgUrl":"http://toly1994.com:8089/imgs/android/c3af376135a7abe0655c908195b271db.png","createTime":"2018-09-06","info":"以前在Html利用js控制SVG或canvas进行运动模拟。浏览器自带window.requestAnimationFrame能不断执行渲染在这...","area":"A"}}';
    
      var result = ResultBean.fromJson(json.decode(j));
      print(result.data.name);//D5-Android绘图之让图形动起来
    

    3.关于Json的内嵌数组

    这里data是一个json的数组,这样访问的服务端接口的数据处理就搞定了

    {
      "code": 200,
      "msg": "操作成功",
      "data": [
        {
          "id": 198,
          "type": "绘图相关",
          "name": "",
          "localPath": "---",
          "jianshuUrl": "",
          "juejinUrl": "---",
          "imgUrl": "http://toly1994.com:8089/imgs/android/8a11d27d58f4c1fa4488cf39fdf68e76.png",
          "createTime": "2021-02-18",
          "info": "hh",
          "area": "A"
        },
        {
          "id": 200,
          "type": "绘图相关",
          "name": "",
          "localPath": "---",
          "jianshuUrl": "",
          "juejinUrl": "---",
          "imgUrl": "http://toly1994.com:8089/imgs/android/8a11d27d58f4c1fa4488cf39fdf68e76.png",
          "createTime": "2018-12-21",
          "info": "hh",
          "area": "A"
        }
      ]
    
    class ResultBean {
      String msg;
      int code;
      var data;
    
      ResultBean.fromJson(Map<String, dynamic> map)
          : msg = map['msg'],
            code = map['code'],
            data = map['data'];
    }
    
    class NoteBean {
      int id;
      String type;
      String name;
      String localPath;
      String jianshuUrl;
      String juejinUrl;
      String imgUrl;
      String createTime;
      String info;
      String area;
    
      NoteBean.fromJson(Map<String, dynamic> map)
          : id = map['id'],
            name = map['name'],
            localPath = map['localPath'],
            jianshuUrl = map['jianshuUrl'],
            juejinUrl = map['juejinUrl'],
            imgUrl = map['imgUrl'],
            createTime = map['createTime'],
            info = map['info'],
            area = map['area'];
    }
    
      var j ='{"code":200,"msg":"操作成功","data":[{"id":198,"type":"绘图相关","name":"","localPath":"---","jianshuUrl":"","juejinUrl":"---","imgUrl":"http://toly1994.com:8089/imgs/android/8a11d27d58f4c1fa4488cf39fdf68e76.png","createTime":"2021-02-18","info":"hh","area":"A"},{"id":200,"type":"绘图相关","name":"","localPath":"---","jianshuUrl":"","juejinUrl":"---","imgUrl":"http://toly1994.com:8089/imgs/android/8a11d27d58f4c1fa4488cf39fdf68e76.png","createTime":"2018-12-21","info":"hh","area":"A"}]}';
      var result = ResultBean.fromJson(json.decode(j));
      print(NoteBean.fromJson(result.data[1]).imgUrl);//http://toly1994.com:8089/imgs/android/8a11d27d58f4c1fa4488cf39fdf68e76.png
    

    好了,今天就到这里,明天最后一天,敬请期待


    后记:捷文规范

    1.本文成长记录及勘误表
    项目源码 日期 备注
    V0.1-github 2018-12-21 Flutter第6天--异步-IO+网络+json
    2.更多关于我
    笔名 QQ 微信 爱好
    张风捷特烈 1981462002 zdl1994328 语言
    我的github 我的简书 我的掘金 个人网站
    3.声明

    1----本文由张风捷特烈原创,转载请注明
    2----欢迎广大编程爱好者共同交流
    3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正
    4----看到这里,我在此感谢你的喜欢与支持


    icon_wx_200.png

    相关文章

      网友评论

        本文标题:Flutter第6天--异步-IO+网络+json

        本文链接:https://www.haomeiwen.com/subject/ehbvkqtx.html