今天我们就来完成微信项目中的最后一个页面聊天列表页
微信聊天列表页
在开始之前我们先准备一些数据
创建获取数据的接口
我们可以通过这个网址来构建我们的测试接口
点击新建街口,就可以自定义我们需要数据格式的接口了
现在接口有了,根据我们的页面我们还差显示的数据
获取数据
https://randomuser.me 数据地址
http://mockjs.com/ 使上面的数据随机出现
现在我们的数据已经接口都已经准备好了,我们开始构建页面吧
分析UI
我们先来分析整个UI,整个页面由3个元素所组成
- 1.导航栏右边菜单按钮,已经菜单
- 2.导航栏下方的搜索框
- 3.聊天列表
微信聊天列表页
①导航栏
class ChatPage extends StatefulWidget {
const ChatPage({Key? key}) : super(key: key);
@override
State<ChatPage> createState() => _ChatPageState();
}
class _ChatPageState extends State<ChatPage> {
//下拉菜单Itme
PopupMenuItem<String> _menuItem(String imageName, String text) {
return PopupMenuItem(
child: Row(
children: [
//图标
Image(
image: AssetImage(imageName),
width: 25,
),
SizedBox(width: 20),
//文字
Text(
text,
style: const TextStyle(
color: Colors.white
),
),
],
)
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: themeColor,
elevation: 0.0,
title: const Text(
'微信',
style: TextStyle(
color: Colors.black,
),
),
actions: [
Container(
margin: EdgeInsets.only(right: 10),
child: PopupMenuButton(
//调整展开位置
offset: const Offset(0, 60),
color: const Color.fromRGBO(1, 1, 1, 0.65),
itemBuilder: (BuildContext context) {
return <PopupMenuItem<String>>[
_menuItem('images/icon_chat_white.png', '发起群聊'),
_menuItem('images/icon_add_friends.png', '添加朋友'),
_menuItem('images/icon_scanning_white.png', '扫一扫'),
_menuItem('images/icon_payment.png', '收付款'),
];
},
child: const Image(
width: 25,
image: AssetImage('images/icon_add.png'),
),
),
),
],
),
body: Container(),
);
}
}
-
PopupMenuButton
下拉菜单列表
②获取数据
网络请求这里用了http
三方库
pubspec.yaml
文件中
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
http: ^0.13.5
获取数据
List<Chat> _datas = [];
@override
void initState() {
super.initState();
_getDatas()
.then((List<Chat> datas) {
debugPrint('数据来了!');
if (!_cancelConnect) {
debugPrint('更新数据!');
setState(() {
_datas = datas;
});
}
})
.catchError((e) {
debugPrint(e);
})
.whenComplete(() => debugPrint('完毕!'))
.timeout(const Duration(seconds: 6))
.catchError((timeout) {
_cancelConnect = true;
debugPrint('超出时间:$timeout');
});
}
Future<List<Chat>> _getDatas() async {
//发送网络请求
final url = Uri.parse('http://rap2api.taobao.org/app/mock/307918/api/chat/list');
final response = await http.get(url);
if (response.statusCode == 200) {
Map responseBody = json.decode(response.body);
List list = responseBody['chat_list'];
List<Chat> chatList = list.map((e) => Chat.fromJson(e)).toList();
return chatList;
}else {
throw Exception('statusCode:${response.statusCode}');
}
}
Future<List<Chat>> _getDatas() async
异步不是多线程
数据获取下来并保存在_datas
中了,我们开始完成列表
class ChatPage extends StatefulWidget {
const ChatPage({Key? key}) : super(key: key);
@override
State<ChatPage> createState() => _ChatPageState();
}
class _ChatPageState extends State<ChatPage> {
bool _cancelConnect = false;
List<Chat> _datas = [];
@override
void initState() {
super.initState();
_getDatas()
.then((List<Chat> datas) {
debugPrint('数据来了!');
if (!_cancelConnect) {
debugPrint('更新数据!');
setState(() {
_datas = datas;
});
}
})
.catchError((e) {
debugPrint(e);
})
.whenComplete(() => debugPrint('完毕!'))
.timeout(const Duration(seconds: 6))
.catchError((timeout) {
_cancelConnect = true;
debugPrint('超出时间:$timeout');
});
}
Future<List<Chat>> _getDatas() async {
//发送网络请求
final url = Uri.parse('http://rap2api.taobao.org/app/mock/307918/api/chat/list');
final response = await http.get(url);
if (response.statusCode == 200) {
Map responseBody = json.decode(response.body);
List list = responseBody['chat_list'];
List<Chat> chatList = list.map((e) => Chat.fromJson(e)).toList();
return chatList;
}else {
throw Exception('statusCode:${response.statusCode}');
}
}
PopupMenuItem<String> _menuItem(String imageName, String text) {
return PopupMenuItem(
child: Row(
children: [
//图标
Image(
image: AssetImage(imageName),
width: 25,
),
SizedBox(width: 20),
//文字
Text(
text,
style: const TextStyle(
color: Colors.white
),
),
],
)
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: themeColor,
elevation: 0.0,
title: const Text(
'微信',
style: TextStyle(
color: Colors.black,
),
),
actions: [
Container(
margin: EdgeInsets.only(right: 10),
child: PopupMenuButton(
//调整展开位置
offset: const Offset(0, 60),
color: const Color.fromRGBO(1, 1, 1, 0.65),
itemBuilder: (BuildContext context) {
return <PopupMenuItem<String>>[
_menuItem('images/icon_chat_white.png', '发起群聊'),
_menuItem('images/icon_add_friends.png', '添加朋友'),
_menuItem('images/icon_scanning_white.png', '扫一扫'),
_menuItem('images/icon_payment.png', '收付款'),
];
},
child: const Image(
width: 25,
image: AssetImage('images/icon_add.png'),
),
),
),
],
),
body: Container(
child: _datas.isEmpty
? const Center(
child: Text('Loading...'),
)
: ListView.builder(
itemCount: _datas.length,
itemBuilder: _itemForRow
),
),
);
}
Widget _itemForRow(BuildContext context, int index) {
return ListTile(
title: Text(_datas[index].name),
subtitle: Container(
padding: const EdgeInsets.only(right: 10),
height: 20,
child: Text(
_datas[index].message,
overflow: TextOverflow.ellipsis,
),
),
leading: Container(
width: 44,
height: 44,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0),
image: DecorationImage(
image: NetworkImage(_datas[index].imageName),
)
),
),
);
}
}
③搜索框
class SearchCell extends StatelessWidget {
final List<Chat> datas;
const SearchCell({Key? key, required this.datas}) : super(key: key);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (BuildContext context) => SearchPage(datas: datas)));
},
child: Container(
color: themeColor,
height: 44,
padding: const EdgeInsets.all(5),
child: Stack(
alignment: Alignment.center,
children: [
//白色底
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(6.0),
),
),
//图片+文字
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
//图片
Image(
image: AssetImage('images/icon_magnifier_black.png'),
color: Colors.grey,
width: 15,
),
//文字
Text(
'搜索',
style: TextStyle(
color: Colors.grey,
fontSize: 15,
),
)
],
),
],
),
),
);
}
}
最后我们的页面就基本完成,我们来看下最终的效果
聊天页效果