Flutter 佈局聊天列表頁及網路資料處理
這是我參與11月更文挑戰的第8天,活動詳情檢視:2021最後一次更文挑戰
json 轉模型
``` class ChatModel { final String? name; final String? message; final String? imageUrl; ChatModel({this.name, this.message, this.imageUrl});
//工廠構造方法 factory ChatModel.fromMap(Map map) { return ChatModel( name: map['name'], message: map['message'], imageUrl: map['imageUrl'], ); } } ```
``` final chatMap = { 'name' : 'ChenXi', 'message' : 'Hello!', }; //Map 轉 json final chatJson = json.encode(chatMap); print(chatJson);
// json 轉 Map
final newChatMap = json.decode(chatJson);
print(chatJson);
final chatModel = ChatModel.fromMap(newChatMap as Map);
print(chatModel);
```
這裡我們簡單定義了一個 map
物件,程式碼示例中給出裡 json
與 Map
的相互轉換,及 Map
轉模型。我們定義了一個 ChatModel
的模型,添加了 fromMap
方法,由外部傳入一個 Map
型別的物件。開源的也有一些轉模型的框架,這裡我們先自己實現。
Future 使用
void initState() {
super.initState();
//獲取網路資料
_getDatas().then((value) {
print('$value');
});
}
Future<List<ChatModel>> _getDatas() async {
//url 連結
final url = Uri.parse('http://rap2api.taobao.org/app/mock/294394/api/chat/list');
//傳送請求
final response = await http.get(url);
if (response.statusCode == 200) {
//獲取響應資料,並且把 json 轉成 Map
final bodyMap = json.decode(response.body);
// 取出 bodyMap 中的 chat_list 陣列,通過 map 方法進行遍歷並轉為模型,通過 toList 返回一個模型陣列
final chatList = (bodyMap['chat_list'] as List).map((item) => ChatModel.fromMap(item)).toList();
return chatList;
} else {
throw Exception('statusCode:${response.statusCode}');
}
這裡 Future
代表未來的資料,Future
需要跟非同步方法結合使用,我們對返回的資料 Future<List<ChatModel>>
進行包裝,Future
有一個 then
方法,then
有一個外部傳入一個閉包屬性,當資料請求完成會呼叫閉包,這裡我們可以拿到 value
的值,也就是模型陣列。
FutureBuilder 非同步渲染
body: Container(
child: FutureBuilder(
future: _getDatas(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
//正在載入
if (snapshot.connectionState == ConnectionState.waiting) {
return Container(
child: Text('正在載入!'),
);
}
//載入完成
return ListView(
children: snapshot.data.map<Widget>((ChatModel item) {
return ListTile(
title: Text(item.name as String),
subtitle: Container(
alignment: Alignment.bottomCenter,
height: 25,
child: Text(item.message as String, overflow: TextOverflow.ellipsis,),
),
leading: Container(
width: 44,
height: 44,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0),
image: DecorationImage(image: NetworkImage(item.imageUrl as String)),
),
),
);
}).toList(),
);
},
)
)
這裡我們通過 FutureBuilder
部件實現網路資料的載入,FutureBuilder
部件支援非同步渲染,future
屬性是 Future
型別的資料。在每次進入微信頁面的時候 builder
方法會被呼叫兩次,ConnectionState.waiting
代表資料正在載入,在這裡我們可以做一些空頁面展示的處理。ConnectionState.done
代表資料載入完成,snapshot.data
就是 _getDatas
方法返回的列表資料,這裡可以進行相關邏輯的處理, 這裡我們展示聊天列表資料。ListView
中我們用 ListTile
部件來作為 cell
,ListTile
包含主標題 title
、副標題 subtitle
、頭像 leading
等屬性,用起來很方便。
網路請求資料處理
```
//模型陣列
List
void initState() { super.initState(); //獲取網路資料 _getDatas().then((value) { if (!_cancelConnect) { setState(() { _datas = value; }); } }).catchError((e) { _cancelConnect = true; //獲取資料失敗 print(e); }).whenComplete(() { print('資料請求結束'); }).timeout(Duration(seconds: 5)).catchError((timeout) { _cancelConnect = true; print('請求超時 ! $timeout'); }); } ```
這裡我們建立了一個外部成員變數 _datas
,用來儲存網路請求的資料,網路請求成功之後呼叫 setState
方法,定義了一個屬性 _cancelConnect
標識網路請求是否取消。catchError
代表請求失敗,whenComplete
代表請求結束,timeout
可以設定超時時間,這裡我們可以用來做一下 loading
頁面的展示及錯誤頁面的展示,
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 as String),
subtitle: Container(
alignment: Alignment.bottomCenter,
height: 25,
child: Text(_datas[index].message as String, overflow: TextOverflow.ellipsis,),
),
leading: Container(
width: 44,
height: 44,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0),
image: DecorationImage(image: NetworkImage(_datas[index].imageUrl as String)),
),
),
);
}),
)
對於列表的展示我們這裡換回了 ListView
,使用 FutureBuilder
,資料量比較大的話會影響載入問題。
頁面保持狀態
class _ChatPageState extends State<ChatPage> with AutomaticKeepAliveClientMixin<ChatPage>
Widget build(BuildContext context) {
super.build(context);
}
class _RootPageState extends State<RootPage> {
int _currentIndex = 0;
List <Widget>_pages = [ChatPage(), FriendsPage(), DiscoverPage(), MinePage()];
final PageController _controller = PageController();
@override
Widget build(BuildContext context) {
return Container(
child: Scaffold(
body: PageView(
//禁止頁面拖拽
physics: NeverScrollableScrollPhysics(),
onPageChanged: (int index) {
setState(() {
_currentIndex = index;
});
},
controller: _controller,
children: _pages,
),
當我們切換底部 tabBar
的時候,每次進入頁面都會重新載入,這裡我們採用 AutomaticKeepAliveClientMixin
來保持狀態,讓頁面只會被載入一次,以聊天頁面為例,_ChatPageState
後面加上 with AutomaticKeepAliveClientMixin<ChatPage>
,並在 build
方法中呼叫 super.build(context)
。在 RootPage
中,用 _pages
陣列來儲存底部子頁面,body
使用 PageView
部件,controller
賦值為我們定義的 _controller
,children
賦值為 _pages
。
- Swift - LeetCode - 學生出勤記錄 I
- Swift - LeetCode - 字串中的第一個唯一字元
- Swift - LeetCode - 猜數字大小
- Swift - LeetCode - 翻轉二叉樹
- Swift - LeetCode - 二叉樹的後序遍歷
- Swift - LeetCode - 二叉樹的前序遍歷
- Swift String、Moya 原始碼解析及高階函式
- Flutter 中 key 的原理及作用
- Flutter 生命週期及渲染原理
- Flutter 仿寫微信搜尋頁
- Flutter 網路請求類封裝及搜尋框實現
- Flutter 佈局聊天列表頁及網路資料處理
- Flutter 通訊錄索引條完善及聊天資料配置
- Flutter 仿寫微信通訊錄頁面
- Flutter 仿寫微信發現、我的頁面
- Flutter 專案搭建及工程配置
- 常用 Widget 部件介紹及 Flutter 佈局方式
- Flutter 之 Widget 部件體驗
- Dart 基礎語法
- 記憶體管理-弱引用分析