「这是我参与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
,有一个必传参数builder
,command
点击进去文档是一个有返回值有参数的办法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
,第二次则是拿到数组之后再次刷新数据
snapshot.connectionState
:waiting的时分没有数据,done的时分表明数据现已加载完了。
所以此刻咱们能够依据这个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);
这样布局出来的作用就是这样的:
状况保存
上面有一个小问题,每次页面切出去再次切回来的时分,数据都会从头加载。这样的话状况就没有保存。假如想要保存的话需要:
- 继承
AutomaticKeepAliveClientMixin
class _ChatPageState extends State<ChatPage> with AutomaticKeepAliveClientMixin
- 然后完成父类的办法
@override
bool get wantKeepAlive => true;
- 在
Widget build(BuildContext context)
中完成super.build(context);
修正Widget树
虽然完成了上面的3部曲,可是切回来的时分仍是会从头init,这个是因为在根控制器下每次的页面都是从头生成的,并没有在当时的Widget树中,咱们回到RootPage
页面中从头修正一下
- 声明一个
final PageController _pageController = PageController();
-
body
运用PageView
body: PageView(
children: _pages,
controller: _pageController,
),
- 切换页面的时分,
_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('循环结束');
});
}
经过测验发现运用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之后的代码');
}
完好代码地址: