flutter之从零开始搭建(三)之 网络请求


项目还是在原来的基础上搭建,具体的可以看上面的连接

这次,我们来介绍下网络请求,并且将请求到的数据设置到ListView列表中。老规矩,先来看下效果图

image

页面看起来不错吧,在动手之前还是得说一下,首页数据来自wanandroid提供,毕竟用了别人的东西就得标明。

实战

flutter请求网络有两种,一种是http请求,一种是HttpClient请求,下面来分别来使用一下。

http方式

在使用http方式请求网络时,需要导入http包

//导入网络请求相关的包
import 'package:http/http.dart' as http; 

void _pullNet()  {
    http.get("http://www.wanandroid.com/project/list/1/json?cid=1")
        .then((http.Response response) {
             var convertDataToJson = JSON.decode(response.body);
             convertDataToJson = convertDataToJson["data"]["datas"];
             //打印请求的结果
             print(convertDataToJson);
             //更新数据
             setState(() {
                data = convertDataToJson;
             });
    });
 }

httpClient方式

需要导入httpClient包

import 'dart:io';

void _httpClient() async {
    var responseBody;

    var httpClient = new HttpClient();
    var request = await httpClient.getUrl(
        Uri.parse("http://www.wanandroid.com/project/list/1/json?cid=1"));

    var response = await request.close();
    //判断是否请求成功
    if (response.statusCode == 200) {
      //拿到请求的数据
      responseBody = await response.transform(utf8.decoder).join();
      //解析json,拿到对应的jsonArray数据
      var convertDataToJson = jsonDecode(responseBody)["data"]["datas"];
      //更新数据
      setState(() {
        data = convertDataToJson;
      });

    } else {
      print("error");
    }
  }

知道了网络请求的概念,那么,我们先来写下界面

ListView界面布局

打开HomePage,

import 'package:flutter/material.dart';

class HomePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return new HomeState();
  }
}

 @override
 Widget build(BuildContext context) {
    // TODO: implement build
    return new Scaffold(
      body: new ListView(
          children: <Widget>[
            _getItem2(),
            _getItem2()
          ]),
    );
  }


  Widget _getItem2() {
    return new Card(child: new Padding(
      padding: const EdgeInsets.all(10.0), child: _getRowWidget2(),),
      elevation: 3.0,
      margin: const EdgeInsets.all(10.0),);
  }


  Widget _getRowWidget2() {
    return new Row(children: <Widget>[
      new Flexible(
          flex: 1,
          fit: FlexFit.tight, //和android的weight=1效果一样
          child: new Stack(children: <Widget>[
            new Column(children: <Widget>[
              new Text("title".trim(),
                  style: new TextStyle(color: Colors.black, fontSize: 20.0,),
                  textAlign: TextAlign.left),
              new Text("desc", maxLines: 3,)
            ],)
          ],)
      ),
      new ClipRect(child: new FadeInImage.assetNetwork(
        placeholder: "images/ic_shop_normal.png",
        image: "images/ic_shop_normal.png",
        width: 50.0,
        height: 50.0,
        fit: BoxFit.fitWidth,),),
    ],);
  }

效果如下

image

ListView感觉看起来像是Android中的ScrollView+LineaLayout.vertical。

flutter的布局其实是个特别头疼的问题,widget特别多,没有android那么方便,也没有react-native的flex布局方便,迷之缩进更让人想删除widget都变得特别的困难,所以,在做布局这部分,我们尽可能的将widget做成分割出来,做成一个个的方法widget,然后组合起来。

数据填充

我们看到ListView接收的是一个widget数组,后台返回给我们jsonArray数据的时候,我们完全可以使用map来遍历数据,然后返回widget给Listview的children。

flutter是有生命周期的,大致生命周期可以分为
- initState : 初始化widget的时候调用,只会调用一次。
- build : 初始化之后开始绘制界面,当setState触发的时候会再次被调用
- didUpdateWidget : 当触发setState时,会被调用
- dispose : 页面销毁的时候调用

如果把他们和react-native进行分类看的话,下面是个对比

flutter react native
initState Mount等函数
didUpdateWidget update等函数
dispose Unmount等函数

所以,我们在请求网络的时候,将数据请求放在initState进行处理,下面贴出代码。

import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http; //导入网络请求相关的包


class HomePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return new HomeState();
  }
}

class HomeState extends State<HomePage> {
 //数据源
  List data;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _pullNet();
  }

  void _pullNet() async {
    await http.get("http://www.wanandroid.com/project/list/1/json?cid=1")
        .then((http.Response response) {
      var convertDataToJson = JSON.decode(response.body);
      convertDataToJson = convertDataToJson["data"]["datas"];

      print(convertDataToJson);

      setState(() {
        data = convertDataToJson;
      });

    });
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return new Scaffold(
      body: new ListView(
          children:  _getItem() ),
    );
  }

   List<Widget> _getItem() {
    return data.map((item) {
      return new Card(child: new Padding(
        padding: const EdgeInsets.all(10.0), child: _getRowWidget(item),),
        elevation: 3.0,
        margin: const EdgeInsets.all(10.0),);
    }).toList();
  }


  Widget _getRowWidget(item) {
    return new Row(children: <Widget>[
      new Flexible(
          flex: 1,
          fit: FlexFit.tight, //和android的weight=1效果一样
          child: new Stack(children: <Widget>[
            new Column(children: <Widget>[
              new Text("${item["title"]}".trim(),
                  style: new TextStyle(color: Colors.black, fontSize: 20.0,),
                  textAlign: TextAlign.left),
              new Text("${item["desc"]}", maxLines: 3,)
            ],)
          ],)
      ),
      new ClipRect(child: new FadeInImage.assetNetwork(
        placeholder: "images/ic_shop_normal.png",
        image: "${item['envelopePic']}",
        width: 50.0,
        height: 50.0,
        fit: BoxFit.fitWidth,),),
    ],);
  }

然后我们来看下效果图

image

相信大家看到了一闪而过的红色报警图,虽然不影响最后的显示效果,但是,我们必须得去处理。

在控制台中,我看到了这样的一句异常

The method 'map' was called on null

看到map我们这才焕然大悟,因为网络请求是异步的,当前界面因为要执行build界面的绘制,导致我们在_getItem中map遍历data数据时是个空值,然后再异步请求成功后,setState又重新给data赋了值,然后触发了界面重新绘制,这时候,map遍历是有值,然后就出现了一下会出现异常,然后又好了的原因。

我们得想个办法,并且能优雅的去解决这个问题,对了,我们只需要在ListView中对data进行判空处理不就行了吗,如果为空的话,我们给他设置一个预加载页面,如果不为空的话,直接就当前现在的这套流程。

ok,思路有了,开始干吧

直接看build方法进行更改

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return new Scaffold(
      body: new ListView(
          children: data != null ? _getItem() : _loading()),
    );
  }

  //预加载布局
  List<Widget> _loading() {
    return <Widget>[
      new Container(
        height: 300.0, child: new Center(child:
      new Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          new CircularProgressIndicator(
            strokeWidth: 1.0,),
          new Text("正在加载"),
        ],)),)
    ];
  }

然后我们再来看看效果图

image

gay gay gayhub

下次再见咯!

猜你喜欢

转载自blog.csdn.net/u013000152/article/details/80940690