Flutter实战-搭建微信项目(七)

「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战

回顾

上一篇我们已经拿到了数据,本章主要讲数据怎么显示。数据显示就不可避免的要提到异步加载以及数据的转换

json转换成字典

Flutter中json数据转换成字典,可以使用json.decode

import 'dart:convert';
json.decode(response.body);
复制代码

字典转换成模型

构造方法如果想要有返回值,此时需要使用工厂方法factory

class ChatModel {
  final String? imgUrl;
  final String? name;
  final String? message;
  ChatModel({this.imgUrl, this.name, this.message});
  factory ChatModel.fromMap(Map map) {
    return ChatModel(
        imgUrl: map['imgUrl'], name: map['name'], message: map['message']);
  }
}

复制代码

模型数据源

异步加载的数据,拿到所有的数据的返回值,可以使用Future

  Future<List<ChatModel>> getData() async {
    final url =
        Uri.parse('http://rap2api.taobao.org/app/mock/293759/home/chat/list');
    var response = await http.get(url);
    if (response.statusCode == 200) {
      // json转换成字典
      final responseBody = json.decode(response.body);
      return responseBody['data']
          .map<ChatModel>((item) => ChatModel.fromMap(item))
          .toList();
      //转模型
    } else {
      throw Exception('statusCode=${response.statusCode}');
    }
  }
}
复制代码

渲染

有一个专门用来渲染异步加载回来的数据FutureBuilder,有一个必传参数buildercommand点击进去文档是一个有返回值有参数的方法typedef AsyncWidgetBuilder<T> = Widget Function(BuildContext context, AsyncSnapshot<T> snapshot);

Container(
  child: FutureBuilder(
     builder: (BuildContext context, AsyncSnapshot snapshot) {
       print('${snapshot.data}');
       return Container();
    },
    future: getData(),
)),
复制代码

打印snapshot.data的时候,发现这里会走两次,一次页面刚加载的时候没有拿到数据这里打印的是null,第二次则是拿到数组之后再次刷新数据

image.png

snapshot.connectionState:waiting的时候没有数据,done的时候表明数据已经加载完了。

image.png 所以此时我们可以根据这个connectionState的状态来判断

if (snapshot.connectionState == ConnectionState.waiting) {
       return Center(child: Text('加载中...'));
}
复制代码

ListView的children可以使用snapshot.data.map<Widget>((ChatModel item)数组遍历生成,同时这里介绍一个新的Widget:ListTile包含比较简单常用的一些布局

const ListTile({
    Key? key,
    this.leading,
    this.title,
    this.subtitle,
    this.trailing,
    this.isThreeLine = false,
    this.dense,
    this.visualDensity,
    this.shape,
    this.contentPadding,
    this.enabled = true,
    this.onTap,
    this.onLongPress,
    this.mouseCursor,
    this.selected = false,
    this.focusColor,
    this.hoverColor,
    this.focusNode,
    this.autofocus = false,
    this.tileColor,
    this.selectedTileColor,
    this.enableFeedback,
    this.horizontalTitleGap,
    this.minVerticalPadding,
    this.minLeadingWidth,
  }) : assert(isThreeLine != null),
       assert(enabled != null),
       assert(selected != null),
       assert(autofocus != null),
       assert(!isThreeLine || subtitle != null),
       super(key: key);
复制代码

这样布局出来的效果就是这样的:

image.png

状态保留

上面有一个小问题,每次页面切出去再次切回来的时候,数据都会重新加载。这样的话状态就没有保留。如果想要保留的话需要:

  1. 继承AutomaticKeepAliveClientMixin

class _ChatPageState extends State<ChatPage> with AutomaticKeepAliveClientMixin

  1. 然后实现父类的方法
  @override
  bool get wantKeepAlive => true;
复制代码
  1. Widget build(BuildContext context)中实现super.build(context);

修改Widget树

尽管实现了上面的3部曲,但是切回来的时候还是会重新init,这个是因为在根控制器下每次的页面都是重新生成的,并没有在当前的Widget树中,我们回到RootPage页面中重新修改一下

  1. 声明一个final PageController _pageController = PageController();
  2. body使用PageView
body: PageView(
  children: _pages,
  controller: _pageController,
),
复制代码
  1. 切换页面的时候,_pageController同步跳转
setState(() {
   _currentIndex = index;
   _pageController.jumpToPage(_currentIndex);
});
复制代码

这样每次点击TabbarItem的时候就能保存住当前页面的状态。但是此时有个小问题,当前的页面可以滑动切屏,但是底部的按钮不会随之联动。这个小问题可以在PageView onPageChanged中解决:

onPageChanged: (index) {
  setState(() {
     _currentIndex = index;
   });
},
复制代码

或者设置不能拖拽切屏:physics: NeverScrollableScrollPhysics(),

Future

在上面网络请求的时候用到了Future,那么这个到底是什么?

String _temp = '0';

void main() {
  // operatorDemo();
  getData();
  print('循环之前');
}

getData() {
  print('开始了');
  Future(() {
    for (int i = 0; i < 100; i++) {}
    print('循环结束');
  });
}
复制代码

image.png 经过测试发现使用Future修饰的代码块会异步执行,不会卡住当前的线程。如果希望在这个异步任务执行完成之后再操作,需要在Future前面加上一个await

String _temp = '0';

void main() {
  // operatorDemo();
  getData();
  print('循环之前');
}

getData() async {
  print('开始了');
  await Future(() {
    for (int i = 0; i < 100; i++) {}
    print('循环结束');
  });
  print('await之后的代码');
}
复制代码

image.png

完整代码地址:

二维码.png

猜你喜欢

转载自juejin.im/post/7030386453964128286