1126 字
6 分钟
第三方库导入与网络数据异步请求与展示
前言
在 iOS 开发中 我们经常使用 CocoaPods 等工具进行包管理。那么在 flutter 开发中又该如何进行呢?
一、 http
这是官方提供来一个网络库:http
点击复制

1.1 修改 pubspec.yaml 配置文件

1.2 更新依赖库

1.3 在使用的地方导入
import 'package:http/http.dart' as http;二、 异步函数
2.1 简单汇总Service
简单汇总一下Service,便于后续使用。
import 'package:flutter/material.dart';
class ServiceURL { /// 会话列表 static Uri conversationList = Uri.parse('xxxxxx');}2.2 发送请求
这里简单选择在 initState 中进行网络请求,调用移步方法
@override void initState() { super.initState(); _loadConversation(); }
/// 加载会话列表 void _loadConversation() async { var response = await http.get(ServiceURL.conversationList); print(response.body); }这里可以发现,和我们在 iOS 开发中的网络请求的调用形式有很大差别。iOS开发中我们都是通过回调的形式接收请求结果的。这里使用 await。
三、 数据模型转换
接收到网络数据后我们自然需要进行数据模型的转换。
3.1 定义数据模型
import 'package:flutter/material.dart';
class ConversationData { final String nick; final String avatar; final String lastestMessage;
ConversationData({ required this.nick, required this.avatar, required this.lastestMessage, });
factory ConversationData.formMap(Map map) { return ConversationData( nick: map['nick'], avatar: map['avatar'], lastestMessage: map['lastest_message'], ); }}3.2 Json 转 Map
List _conversations = []; bool _isLoading = false;
/// 加载会话列表 void _loadConversation() async { _isLoading = true; var response = await http.get(ServiceURL.conversationList); _isLoading = false; if (response.statusCode == 200) { final bodyMap = json.decode(response.body); final result = bodyMap['lists'].map((item) { return ConversationData.formMap(item); }).toList();
setState(() { _conversations = result; }); } else { throw Exception('请求失败:${response.statusCode}'); } }3.3 构建 Cell
- Text
- 样式设置
- 缩略方式设置
- 设置最大宽度
- Container
- 设置图片圆角
class ConversationCell extends StatelessWidget { ConversationCell({required this.conversation});
final ConversationData conversation;
@override Widget build(BuildContext context) { return Container( height: 60, color: Colors.white, child: Stack( children: [ Align( alignment: Alignment.center, child: Container( child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ const SizedBox( width: 12, ), // 圆角 Container( width: 48, height: 48, decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), image: DecorationImage( // 网络图片 image: NetworkImage(conversation.avatar), ), ), ), const SizedBox( width: 12, ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 12), Text( conversation.nick, maxLines: 1, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ), Container( // 限制宽度 width: UIConfig.screenWidth(context) * 0.7, child: Text( conversation.lastestMessage, maxLines: 1, // 缩略样式 overflow: TextOverflow.ellipsis, style: const TextStyle( fontSize: 12, fontWeight: FontWeight.w400, ), ), ) ], ), ], ), ), ), // 分割线 Align( alignment: Alignment.bottomCenter, child: Container( margin: const EdgeInsets.only(left: 70), height: 0.5, color: Colors.grey, ), ), ], ), ); }}3.4 构建 ListView
Widget _itemForRow(BuildContext context, int index) { return ConversationCell(conversation: _conversations[index]); }
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(...), body: Container( color: Colors.yellow, child: _isLoading ? Center( child: Text('Loading'), ) : ListView.builder( itemBuilder: _itemForRow, itemCount: _conversations.length, ), ), ); }3.5 效果

四、 Future & FutureBuilder
除了按照上面这种我们在 iOS 开发中一样常规的思路进行请求和展示外,再介绍另一种可以处理这种场景。
4.1 请求方法修改
之前的加载方法:
/// 加载会话列表 void _loadConversation() async { var response = await http.get(ServiceURL.conversationList); if (response.statusCode == 200) { final bodyMap = json.decode(response.body);
_conversations = bodyMap['lists'] .map((item) { return ConversationData.formMap(item); }) .toList();
} else { throw Exception('请求失败:${response.statusCode}'); } }修改为:
/// 加载会话列表 Future<List> _loadConversation() async { var response = await http.get(ServiceURL.conversationList); if (response.statusCode == 200) { final bodyMap = json.decode(response.body); return bodyMap['lists'].map((item) { return ConversationData.formMap(item); }).toList(); } else { throw Exception('请求失败:${response.statusCode}'); } }4.2 ListView 修改
AsyncSnapshot
包含有绑定的 future 回调的数据,也包含异步任务的状态。我们可以用来判断是否展示加载页面。
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(...), body: Container( color: Colors.yellow, child: FutureBuilder( // 传入 Future future: _loadConversation(), builder: (context, AsyncSnapshot snap){ // 判断请求状态 if (snap.connectionState != ConnectionState.done) { return const Center( child: Text('Loading'), ); } // 根据结果处理数据展示 return ListView( children: snap.data.map<Widget>((item){ return ConversationCell(conversation: item); }).toList(), ); }, ), ), ); }4.3 看看效果

五、 结合上面两种方式
结合上面两种方式,这里使用 Future 调整加载数据的函数。
5.1 请求函数修改
/// 加载会话列表 Future<List> _loadConversation() async { _isLoading = true; var response = await http.get(ServiceURL.conversationList); _isLoading = false; if (response.statusCode == 200) { final bodyMap = json.decode(response.body); final result = bodyMap['lists'].map((item) { return ConversationData.formMap(item); }).toList();
return result; } else { throw Exception('请求失败:${response.statusCode}'); } }5.2 回调处理
@override void initState() { super.initState(); _loadConversation().then((value) { // value 就是 Future 里 return 的结果 setState(() { _conversations = value; }); }); }5.3 异常处理
通过 Future 我们也可以捕捉异常。
@override void initState() { super.initState(); _loadConversation().then((value) { setState(() { _conversations = value; }); }).catchError((error) { print(error); }); }5.4 设置超时
@override void initState() { super.initState(); _loadConversation().then((value) { setState(() { _conversations = value; }); }).catchError((error) { print(error); }).whenComplete(() { print('Done'); }).timeout(Duration(seconds: 10)); }六、 保留数据
总结
- 第一种方式更加接近我们 iOS 开发中的思路
- 第二种方式 Future & FutureBuilder 更加充分利用 Future 特性
- 而结合二者的方式则对我这个iOS刚入门Flutter的新手而言,更加熟悉同时也有一些新的体验