赞
踩
3. 接着点击右上角‘编辑’按钮,新建响应内容,类型为Array,一次生成50条
4. 点击chat_list左侧添加按钮,新建chat_list中的数据内容,此时用到一个获取随机头像的网站.到该网站中,随机复制一个图片地址,假设为:https://randomuser.me/api/portraits/women/35.jpg.将数据填上,然后保存.
5.接下来,我们想让获取的图像是个随机值,那么参考Mock.js网站中的生成规则.
6.接着回到响应内容这里,通过设置初始值规则,生成随机的图片地址.
1. AppBar的actions就是我们需要添加操作的地方.
2. PopupMenuButton组件
- AppBar(
- //去除导航条黑线
- elevation: 0.0,
- backgroundColor: WeChatThemeColor,
- //设置标题默认居中、否则双端默认方式不一致
- centerTitle: true,
- title: const Text("微信", style: TextStyle(color: Colors.black),),
- actions: [
- Container(
- margin: EdgeInsets.only(right: 10),
- child: PopupMenuButton(
- onSelected: (item){
- print(item);
- },
- onCanceled: (){
- print('onCanceled');
- },
- //PopupMenuButton的背景颜色
- color: Colors.black,
- offset: Offset(0,60),
- child: Image(image: AssetImage('images/圆加.png'),width: 25,height: 25,),
- itemBuilder: (BuildContext context){
- return <PopupMenuItem>[
- _buildMenuItem('images/发起群聊.png','发起群聊'),
- _buildMenuItem('images/添加朋友.png','添加朋友'),
- _buildMenuItem('images/扫一扫1.png','扫一扫'),
- _buildMenuItem('images/收付款.png','收付款'),
- ];
- },
- ),
- )
- ],
- ),
3. 其中_buildMenuItem是我们封装的一个创建组件的方法,内部实现为
- PopupMenuItem _buildMenuItem(String imageName,String title){
- return PopupMenuItem(
- value: {
- 'imageName' : imageName,
- 'title' : title,
- },
- child: Row(
- children: [
- Image(image: AssetImage(imageName), width: 25,),
- SizedBox(width: 10,),
- Text(title, style: TextStyle(color: Colors.white),),
- ],
- )
- );
- }
4.关于PopupMenuButton背景颜色的设置.可以直接在其内部设置背景颜色.也可以在ThemeData中设置app的cardColor.但是优先级没有直接设置PopupMenuButton的高.如果不设置黑色背景颜色,弹出的视图显示均为白色,看不到UI效果.
3.在项目的pubspec.yaml中粘贴复制的包名.
4. 粘贴完之后需要Pub get获取一下,获取包对应的代码.
5.在chat_page.dart中导入http包并取别名
import 'package:http/http.dart' as http;
6.在渲染状态组件的时候发起网络请求,也就是在initState中发起网络请求.getData后采用async表示异步执行.async需要搭配await使用,await后面跟着的是耗时的代码,所以会异步执行调用.
- class _ChatPageState extends State<ChatPage> {
- .....
- @override
- void initState() {
- super.initState();
- getDatas();
- }
- getDatas() async {
- var response = await http.get(Uri.parse('http://rap2api.taobao.org/app/mock/311243/api/chat/list'),);
- print(response.statusCode);//200
- print(response.body);//这里就是我们自定义的网络数据了
- }
- .....
- }
7.处理返回数据
JSON和Map互相转换
- void initState() {
- super.initState();
- getDatas();
- final chat = {
- 'name': '张三',
- 'message': '在干嘛?',
- };
- //Map转JSON
- final jsonChat = json.encode(chat);
- print(jsonChat);
- //JSON转Map
- final mapChat = json.decode(jsonChat);
- print(mapChat);
- print(mapChat is Map);
- }
- flutter: {"name":"张三","message":"在干嘛?"}
- flutter: {name: 张三, message: 在干嘛?}
- flutter: true
- flutter: 200
8. 新建聊天模型
- class Chat {
- final String? name;
- final String? message;
- final String? imageUrl;
- Chat(this.name,this.message,this.imageUrl);
- //工厂方法,用来初始化对象.
- factory Chat.fromJson(Map json){
- return Chat(json['name'],json['message'],json['imageUrl']);
- }
- }
- //将json转为Chat模型
- final chatModule = Chat.fromJson(mapChat);
- print('name:${chatModule.name} message:${chatModule.message}');// name:张三 message:在干嘛?
9.处理响应的数据
Future<List<Chat>?> getDatas() async {}
- Future<List<Chat>?> getDatas() async {
- final response = await http.get(Uri.parse('http://rap2api.taobao.org/app/mock/311243/api/chat/list'));
- print(response.statusCode);
- if (response.statusCode == 200) {
-
- } else {
- throw Exception('statusCode: ${response.statusCode}');
- }
- }
- //获取响应数据,并且转换成Map类型
- final responseBody = json.decode(response.body);
- //转换模型数组
- responseBody['chat_list'].map(
- (item) {
- print(item);
- return item;
- }
- );
- flutter: {imageUrl: https://randomuser.mflutter: {imageUrl: https://randomuser.me/api/portraits/women/12.jpg, name: 黎超, message: 音和委起度明条部过们放省。们区以号还九保把王之候包与先件能议清。江知天能能五开比点别增石次米五平。极养提立手专把示低率号容眼组是石。离维照联子象派三热始受构参元离还。相电构次色影件力计面进东把。}
- flutter: {imageUrl: https://randomuser.me/api/portraits/women/23.jpg, name: 傅秀兰, message: 但保写太满果此力少合反压色生太个图。制社并更个构北不张需国些清不。没八你或况铁员三时划志有改题头感。值年改你要变程新但八传织。进化林号中不按亲天张原美多。}e/api/portraits/women/37.jpg, name: 李丽, message: 可组品且发铁直报表状传素安小全。器音天石别数业局装共习清。加然处进派变装你农速约部族利音次层。毛得理状主质所局等工型即天研走机段。}
- final responseBody = json.decode(response.body);
- //转换模型数组
- List<Chat>chatList = responseBody['chat_list'].map<Chat>(
- (item) {
- return Chat.fromJson(item);
- }
- ).toList();
- print(chatList);
- return chatList;
[Instance of 'Chat', Instance of 'Chat', Instance of 'Chat', Instance of 'Chat', Instance of 'Chat',...]
- List<Chat>chatList = responseBody['chat_list'].map<Chat>((item) => Chat.fromJson(item)).toList();
- return chatList;
10.处理网络请求的结果
- loadData(){
- getDatas().then((value) {
- print(value);
- });
- }
- flutter: data: null
- flutter: state:ConnectionState.waiting
- flutter: data: [Instance of 'Chat', Instance of 'Chat', Instance of 'Chat', ...]
- flutter: state:ConnectionState.done
2. 所以这个时候我们可以借助于ConnectionState状态来确定当前要渲染的界面.
- FutureBuilder(
- future: getDatas(),
- builder: (BuildContext context, AsyncSnapshot snapshot){
- //无数据时渲染默认界面,有数据时显示网络数据
- print('state:${snapshot.connectionState}');
- if (snapshot.connectionState == ConnectionState.waiting) {
- return Center(child: Text('Loading...'),);
- } else {
- return ListView(
- children: snapshot.data.map<Widget>((item){
- return ListTile(
- //右侧 标题
- title: Text(item.name),
- //右侧 子标题
- subtitle: Container(
- height: 20,width: 20,
- //TextOverflow.ellipsis 展示不下的时候省略号
- child: Text(item.message,overflow: TextOverflow.ellipsis,),
- ),
- //左侧:圆型头像
- leading: CircleAvatar(
- backgroundImage: NetworkImage(item.imageUrl),
- ),
- );
- }).toList(),
- );
- }
- },
- )
List<Chat> _datas = [];
2. 在loadData时处理返回的数据.
- loadData(){
- //当数据正常返回的时候
- getDatas().then((List<Chat>? datas) {
- setState(() {
- _datas = datas ?? [];
- });
- }).catchError((err){
- print(err);
- });
- }
3. 这个时候通过缓存的数据来渲染界面
- Container(
- child: _datas.length == 0 ?
- Center(child: Text('Loading...'),)
- : ListView.builder(
- itemCount: _datas.length,
- itemBuilder: (BuildContext context, int index) {
- return ListTile(
- title: Text(_datas[index].name ?? ""),
- subtitle: Container(height: 20,width: 20,child: Text(_datas[index].message ?? ""),),
- leading: CircleAvatar(
- backgroundImage: NetworkImage(_datas[index].imageUrl ?? ""),
- ),
- );
- }),
- ),
4. 关于Future请求结果的处理完善
- loadData(){
- //当数据正常返回的时候
- getDatas().then((List<Chat>? datas) {
- setState(() {
- _datas = datas ?? [];
- });
- }).catchError((err){
- print(err);
- }).whenComplete(() {
- //数据处理完毕
- print("完毕");
- }).timeout(Duration(milliseconds: 10)).catchError((timeout){
- print("加载超时 ${timeout}");//flutter: 加载超时 TimeoutException after 0:00:00.010000: Future not completed
- });
- }
5. 请求超时的异常处理/多次重复刷新
关于状态的保存,这个时候我们引入另一个概念: 混入(Mixins),用来给类增加功能,是多继承模式下的一种代码复用.使用with关键字来实现混入一个或多个类.
- //AutomaticKeepAliveClientMixin让当前界面保存状态
- class _ChatPageState extends State<ChatPage> with AutomaticKeepAliveClientMixin<ChatPage>{
- .....
- //保留setState状态
- @override
- bool get wantKeepAlive => true;
- @override
- Widget build(BuildContext context) {
- //6.3 重写父类渲染方法
- super.build(context);
- ...
- }
- ...
- }
3. 同理我们去保存通讯录界面的状态.
4. 这个时候我们去检测设置的效果,发现貌似并没有设置成功.还是会重新渲染对应的界面.是否是设置失效了呢?
5. 回到rootpage.dart中,我们发现在设置当前显示body的时候,采用的是从_pages数组中获取对应的界面.在iOS中,这样设置并没有什么问题.
MyApp => RootPage => Container => ChatPage/FriendsPage/DiscoverPage/MinePage
6. 回到我们在微信界面/通讯录界面混入AutomaticKeepAliveClientMixin这个类的目的:
7. PageController设置rootpage根视图界面.
- final PageController _controller = PageController(
- //初始显示的界面索引
- initialPage: 0,
- );
- PageView(
- onPageChanged: (int index ){
- _currentIndex = index;
- setState(() {
- });
- },
- //如果不设置这个属性,那么根视图事件可以左右滚动切换.
- physics: NeverScrollableScrollPhysics(),
- controller: _controller,
- children: [
- ChatPage(),
- FriendsPage(),
- DiscoverPage(),
- MinePage()
- ],
- ),
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。