三、Flutter之StatefulWidget和网络请求

2019-02-25  本文已影响0人  夏_Leon

之前学习中使用的都是StatelessWidget,是不可变的,这意味着它们的属性不能改变。而当我们需要一个动态的属性可变的控件,就需要用到StatefulWidget

StatefulWidget持有的状态可能在 widget 生命周期中发生变化,实现一个 stateful widget 至少需要两个类:1)一个 StatefulWidget 类;2)一个 State 类,StatefulWidget 类本身是不变的,但是 State 类在 widget 生命周期中始终存在。

在这一步,你将添加一个 StatefulWidget-ListViewSample,它会创建自己的状态类-_ListViewSampleState,然后你需要将 ListViewSample 内嵌到已有的App里。

先导入网络请求的包:

import 'dart:io';
import 'dart:convert';

列表展示及网络请求核心代码:

//StatefulWidget申明
class ListViewSample extends StatefulWidget {
  @override
  State createState() {
    return new _ListViewSampleState();
  }
}

//StatefulWidget对应的State
class _ListViewSampleState extends State<ListViewSample> {
  List _listItems; //私有的list数据

  @override
  void initState() {
    super.initState();
    //初始化时即进行数据请求
    getNewMovie();
  }

  //请求豆瓣当前热门电影
  getNewMovie() async {
    var httpClient = new HttpClient();
    //网络请求,{}里可以添加参数
    var uri = new Uri.http('api.douban.com', '/v2/movie/in_theaters', {});
    //flutter是单线程,用httpclient进行网络请求时要用await关键字,并且此时返回的都是Future对象s
    var request = await httpClient.getUrl(uri);
    var response = await request.close();

    if (response.statusCode == 200) {
      var responseBody = await response.transform(utf8.decoder).join();
      //注意很多教程里json转换用的是JSON.decode,然而在18年底的一个版本中,把常量都改为小写了。现在都是用json.decode
      Map<String, dynamic> map = json.decode(responseBody);
      print('refresh movie list ');
      setState(() {
        _listItems = new List();
        for (dynamic movie in map['subjects']) {
          _listItems.add(movie);
        }
      });
    } else {
      print("http error ${response.statusCode}");
    }
  }

  @override
  Widget build(BuildContext context) {
    //还没请求到数据的时候显示加载中
    if (_listItems == null) {
      return new Center(
        heightFactor: 6,
        child: new Text('加载中...'),
      );
    }

    //请求到数据后显示列表
    return new Expanded(
      child: new ListView.builder(
        scrollDirection: Axis.vertical,
        itemCount: _listItems.length,
        itemBuilder: (context, index) {
          return movieItem(context, index);
        },
      ),
    );
  }

  //列表单元的UI封装
  Widget movieItem(BuildContext context, int index) {
    return new Column(
      children: <Widget>[
        //分割线
        new Divider(),

        //包裹一个GestureDetector,响应点击事件
        new GestureDetector(
          //点击item的时候也再请求数据
          onTap: getNewMovie,
          //item容器
          child: new Container(
            padding: const EdgeInsets.all(8.0),
            //一级Row布局
            child: new Row(
              //右边布局顶对齐
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                //显示电影图片
                new Image.network(
                  _listItems[index]['images']['small'],
                  width: 100,
                ),

                //右边布局
                new Column(
                  mainAxisAlignment: MainAxisAlignment.end,
                  children: <Widget>[
                    //电影名
                    new Container(
                      padding: EdgeInsets.all(8.0),
                      width: 200,
                      child: new Text(
                        _listItems[index]['title'],
                        style: new TextStyle(
                          color: Colors.blueAccent,
                        ),
                        overflow: TextOverflow.clip,
                        textAlign: TextAlign.left,
                      ),
                    ),

                    //评分
                    new Container(
                      padding: EdgeInsets.all(8.0),
                      width: 200,
                      child: new Text(
                        '评分:' +
                            _listItems[index]['rating']['average'].toString(),
                        style: new TextStyle(
                          color: Colors.orange,
                        ),
                        overflow: TextOverflow.clip,
                        textAlign: TextAlign.left,
                      ),
                    ),

                    //导演
                    new Container(
                      padding: EdgeInsets.all(8.0),
                      width: 200,
                      child: new Text(
                        '导演:' + _listItems[index]['directors'][0]['name'],
                        style: new TextStyle(
                          color: Colors.blueAccent,
                        ),
                        overflow: TextOverflow.clip,
                        textAlign: TextAlign.left,
                      ),
                    ),
                  ],
                ),
              ],
            ),
          ),
        ),
      ],
    );
  }
}

可以看到在Flutter中UI的嵌套层级非常深,而在Android中如果用约束布局,一两层就可以搞定了,这方面是很不习惯。但是代码量来说Flutter是远少于Android的,不需要单独写xml文件,网络请求库的使用非常简单,列表不需要写Adapter那么麻烦。
Item单元的代码中我加了GestureDetector,这是为了添加点击事件,在点击时刷新数据。这种在控件内部修改state的方式比较简单,如果是在控件外修改state就比较麻烦了。

这里的网络请求用的是自带的httpclient,代码比较简单,不需要进行一大堆设置就能先跑起来,要注意Dart是单线程的,所有耗时任务都要加入async关键词放入延时任务里,具体参考 异步async、await和Future的使用技巧

上一篇下一篇

猜你喜欢

热点阅读