feat:更新页面颜色
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:liquid_glass_widgets/liquid_glass_widgets.dart';
|
||||
import 'package:sweet_chat_app/pages/contacts_page.dart';
|
||||
import 'package:sweet_chat_app/pages/explore_page.dart';
|
||||
import 'package:sweet_chat_app/pages/messages_page.dart';
|
||||
|
||||
void main() async {
|
||||
@@ -19,6 +20,7 @@ class MyApp extends StatelessWidget {
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: ThemeData(
|
||||
scaffoldBackgroundColor: const Color(0xFF120D25),
|
||||
brightness: Brightness.light,
|
||||
useMaterial3: true,
|
||||
colorScheme: ColorScheme.light(
|
||||
@@ -43,28 +45,21 @@ class _HomePageState extends State<HomePage> {
|
||||
int _index = 0;
|
||||
|
||||
final _pages = const [
|
||||
Center(child: MessagePage()),
|
||||
Center(child: ContactsPage()),
|
||||
Center(child: Text('⚙ Settings', style: TextStyle(fontSize: 26, color: Colors.white))),
|
||||
Center(child: Text('⚙ 我的', style: TextStyle(fontSize: 26, color: Colors.white))),
|
||||
MessagePage(),
|
||||
ContactsPage(),
|
||||
ExplorePage(),
|
||||
Center(child: Text('⚙ 我的', style: TextStyle(fontSize: 26))),
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GlassScaffold(
|
||||
background: Container(
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [Color(0xFFFFFFFF), Color(0xFFF5F5F7)],
|
||||
),
|
||||
),
|
||||
),
|
||||
statusBarStyle: GlassStatusBarStyle.auto,
|
||||
bottomBar: GlassBottomBar(
|
||||
selectedIndex: _index,
|
||||
onTabSelected: (i) => setState(() => _index = i),
|
||||
selectedIconColor: Colors.purple.shade200,
|
||||
unselectedIconColor: Colors.white,
|
||||
tabs: const [
|
||||
GlassBottomBarTab(
|
||||
icon: Icon(Icons.chat_bubble_outline),
|
||||
@@ -91,4 +86,4 @@ class _HomePageState extends State<HomePage> {
|
||||
body: _pages[_index],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
178
lib/pages/chat_page.dart
Normal file
178
lib/pages/chat_page.dart
Normal file
@@ -0,0 +1,178 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:liquid_glass_widgets/liquid_glass_widgets.dart';
|
||||
|
||||
class ChatMessage {
|
||||
final String text;
|
||||
final bool isMe;
|
||||
final String? avatar;
|
||||
|
||||
ChatMessage({required this.text, required this.isMe, this.avatar});
|
||||
}
|
||||
|
||||
class ChatPage extends StatefulWidget {
|
||||
final String contactName;
|
||||
final String avatarUrl;
|
||||
|
||||
const ChatPage({
|
||||
super.key,
|
||||
required this.contactName,
|
||||
required this.avatarUrl,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ChatPage> createState() => _ChatPageState();
|
||||
}
|
||||
|
||||
class _ChatPageState extends State<ChatPage> {
|
||||
final TextEditingController _controller = TextEditingController();
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
|
||||
late List<ChatMessage> _messages;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_messages = [
|
||||
ChatMessage(text: '在吗?', isMe: false, avatar: widget.avatarUrl),
|
||||
ChatMessage(text: '在的,怎么啦 😊', isMe: true),
|
||||
ChatMessage(text: '今晚一起吃饭吗?', isMe: false, avatar: widget.avatarUrl),
|
||||
];
|
||||
}
|
||||
|
||||
void _sendMessage() {
|
||||
final text = _controller.text.trim();
|
||||
if (text.isEmpty) return;
|
||||
|
||||
setState(() {
|
||||
_messages.add(ChatMessage(text: text, isMe: true));
|
||||
});
|
||||
|
||||
_controller.clear();
|
||||
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
_scrollController.animateTo(
|
||||
_scrollController.position.maxScrollExtent,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeOut,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildAvatar(String? url) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 8, top: 4),
|
||||
child: CircleAvatar(
|
||||
radius: 18,
|
||||
backgroundColor: Colors.grey[200],
|
||||
backgroundImage: url != null ? NetworkImage(url) : null,
|
||||
child:
|
||||
url == null
|
||||
? const Icon(Icons.person, size: 18, color: Colors.grey)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: _buildAppBar(),
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(child: _buildMessage()),
|
||||
SizedBox(height: 10),
|
||||
_buildInput(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
PreferredSizeWidget _buildAppBar() {
|
||||
return AppBar(
|
||||
backgroundColor: Color(0xFF120D25),
|
||||
foregroundColor: Colors.white,
|
||||
title: Text(
|
||||
widget.contactName,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
centerTitle: true,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMessage() {
|
||||
return GlassCard(
|
||||
child: ListView.separated(
|
||||
controller: _scrollController,
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
return SizedBox(height: 10);
|
||||
},
|
||||
itemCount: _messages.length,
|
||||
itemBuilder: (context, index) {
|
||||
final msg = _messages[index];
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment:
|
||||
msg.isMe ? MainAxisAlignment.end : MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (!msg.isMe) _buildAvatar(msg.avatar),
|
||||
Flexible(
|
||||
child: GlassCard(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 10,
|
||||
),
|
||||
child: Text(
|
||||
msg.text,
|
||||
style: const TextStyle(fontSize: 16, color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
if (msg.isMe) _buildAvatar("https://picsum.photos/id/1027/200"),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInput() {
|
||||
return SafeArea(
|
||||
child: GlassContainer(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _controller,
|
||||
onSubmitted: (_) => _sendMessage(),
|
||||
style: const TextStyle(color: Colors.white),
|
||||
decoration: const InputDecoration(
|
||||
hintText: '输入消息...',
|
||||
hintStyle: TextStyle(color: Colors.white),
|
||||
border: InputBorder.none,
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: _sendMessage,
|
||||
icon: const Icon(Icons.send, color: Colors.green),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -40,86 +40,26 @@ class _ContactsPageState extends State<ContactsPage> {
|
||||
|
||||
void _loadFakeData() {
|
||||
contactList = [
|
||||
ContactInfo(
|
||||
name: '张三',
|
||||
avatar: 'https://randomuser.me/api/portraits/men/11.jpg',
|
||||
),
|
||||
ContactInfo(
|
||||
name: '李四',
|
||||
avatar: 'https://randomuser.me/api/portraits/men/12.jpg',
|
||||
),
|
||||
ContactInfo(
|
||||
name: '王五',
|
||||
avatar: 'https://randomuser.me/api/portraits/men/13.jpg',
|
||||
),
|
||||
ContactInfo(
|
||||
name: '赵六',
|
||||
avatar: 'https://randomuser.me/api/portraits/women/14.jpg',
|
||||
),
|
||||
ContactInfo(
|
||||
name: '陈七',
|
||||
avatar: 'https://randomuser.me/api/portraits/women/15.jpg',
|
||||
),
|
||||
ContactInfo(
|
||||
name: '刘八',
|
||||
avatar: 'https://randomuser.me/api/portraits/men/16.jpg',
|
||||
),
|
||||
ContactInfo(
|
||||
name: '周九',
|
||||
avatar: 'https://randomuser.me/api/portraits/men/17.jpg',
|
||||
),
|
||||
ContactInfo(
|
||||
name: '吴十',
|
||||
avatar: 'https://randomuser.me/api/portraits/women/18.jpg',
|
||||
),
|
||||
ContactInfo(
|
||||
name: '欧阳峰',
|
||||
avatar: 'https://randomuser.me/api/portraits/men/21.jpg',
|
||||
),
|
||||
ContactInfo(
|
||||
name: '诸葛青',
|
||||
avatar: 'https://randomuser.me/api/portraits/men/22.jpg',
|
||||
),
|
||||
ContactInfo(
|
||||
name: '司马光',
|
||||
avatar: 'https://randomuser.me/api/portraits/men/23.jpg',
|
||||
),
|
||||
ContactInfo(
|
||||
name: '慕容复',
|
||||
avatar: 'https://randomuser.me/api/portraits/women/24.jpg',
|
||||
),
|
||||
ContactInfo(
|
||||
name: '安琪拉',
|
||||
avatar: 'https://randomuser.me/api/portraits/women/31.jpg',
|
||||
),
|
||||
ContactInfo(
|
||||
name: '亚瑟',
|
||||
avatar: 'https://randomuser.me/api/portraits/men/32.jpg',
|
||||
),
|
||||
ContactInfo(
|
||||
name: '李白',
|
||||
avatar: 'https://randomuser.me/api/portraits/men/33.jpg',
|
||||
),
|
||||
ContactInfo(
|
||||
name: '韩信',
|
||||
avatar: 'https://randomuser.me/api/portraits/men/34.jpg',
|
||||
),
|
||||
ContactInfo(
|
||||
name: '孙尚香',
|
||||
avatar: 'https://randomuser.me/api/portraits/women/35.jpg',
|
||||
),
|
||||
ContactInfo(
|
||||
name: '鲁班',
|
||||
avatar: 'https://randomuser.me/api/portraits/men/36.jpg',
|
||||
),
|
||||
ContactInfo(
|
||||
name: '妲己',
|
||||
avatar: 'https://randomuser.me/api/portraits/women/37.jpg',
|
||||
),
|
||||
ContactInfo(
|
||||
name: '甄姬',
|
||||
avatar: 'https://randomuser.me/api/portraits/women/38.jpg',
|
||||
),
|
||||
ContactInfo(name: '张三', avatar: 'https://picsum.photos/id/1011/200'),
|
||||
ContactInfo(name: '李四', avatar: 'https://picsum.photos/id/1012/200'),
|
||||
ContactInfo(name: '王五', avatar: 'https://picsum.photos/id/1013/200'),
|
||||
ContactInfo(name: '赵六', avatar: 'https://picsum.photos/id/1014/200'),
|
||||
ContactInfo(name: '陈七', avatar: 'https://picsum.photos/id/1015/200'),
|
||||
ContactInfo(name: '刘八', avatar: 'https://picsum.photos/id/1016/200'),
|
||||
ContactInfo(name: '周九', avatar: 'https://picsum.photos/id/1017/200'),
|
||||
ContactInfo(name: '吴十', avatar: 'https://picsum.photos/id/1018/200'),
|
||||
ContactInfo(name: '欧阳峰', avatar: 'https://picsum.photos/id/1021/200'),
|
||||
ContactInfo(name: '诸葛青', avatar: 'https://picsum.photos/id/1022/200'),
|
||||
ContactInfo(name: '司马光', avatar: 'https://picsum.photos/id/1023/200'),
|
||||
ContactInfo(name: '慕容复', avatar: 'https://picsum.photos/id/1024/200'),
|
||||
ContactInfo(name: '安琪拉', avatar: 'https://picsum.photos/id/1031/200'),
|
||||
ContactInfo(name: '亚瑟', avatar: 'https://picsum.photos/id/1032/200'),
|
||||
ContactInfo(name: '李白', avatar: 'https://picsum.photos/id/1033/200'),
|
||||
ContactInfo(name: '韩信', avatar: 'https://picsum.photos/id/1034/200'),
|
||||
ContactInfo(name: '孙尚香', avatar: 'https://picsum.photos/id/1035/200'),
|
||||
ContactInfo(name: '鲁班', avatar: 'https://picsum.photos/id/1036/200'),
|
||||
ContactInfo(name: '妲己', avatar: 'https://picsum.photos/id/1037/200'),
|
||||
ContactInfo(name: '甄姬', avatar: 'https://picsum.photos/id/1038/200'),
|
||||
];
|
||||
|
||||
_handleList(contactList);
|
||||
@@ -142,27 +82,31 @@ class _ContactsPageState extends State<ContactsPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFF2F2F7),
|
||||
appBar: AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: Colors.grey.shade200,
|
||||
elevation: 0,
|
||||
centerTitle: true,
|
||||
title: const Text(
|
||||
'通讯录',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF1C1C1E),
|
||||
),
|
||||
),
|
||||
),
|
||||
backgroundColor: Colors.transparent,
|
||||
appBar: _buildAppBar(),
|
||||
body: Column(
|
||||
children: [_buildSearchBar(), Expanded(child: _buildAzListView())],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
PreferredSizeWidget _buildAppBar() {
|
||||
return AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: Color(0xFF120D25),
|
||||
elevation: 0,
|
||||
centerTitle: true,
|
||||
title: const Text(
|
||||
'通讯录',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAzListView() {
|
||||
return AzListView(
|
||||
data: contactList,
|
||||
@@ -170,7 +114,6 @@ class _ContactsPageState extends State<ContactsPage> {
|
||||
itemBuilder: (_, index) {
|
||||
final c = contactList[index];
|
||||
return Container(
|
||||
color: Colors.white,
|
||||
child: ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundImage: NetworkImage(c.avatar),
|
||||
@@ -178,7 +121,7 @@ class _ContactsPageState extends State<ContactsPage> {
|
||||
),
|
||||
title: Text(
|
||||
c.name,
|
||||
style: const TextStyle(fontSize: 16, color: Color(0xFF1C1C1E)),
|
||||
style: const TextStyle(fontSize: 16, color: Colors.white),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -189,10 +132,10 @@ class _ContactsPageState extends State<ContactsPage> {
|
||||
height: 28,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
alignment: Alignment.centerLeft,
|
||||
color: const Color(0xFFF2F2F7),
|
||||
color: const Color(0xFF2B2744),
|
||||
child: Text(
|
||||
tag,
|
||||
style: const TextStyle(fontSize: 13, color: Color(0xFF8E8E93)),
|
||||
style: const TextStyle(fontSize: 13, color: Colors.grey),
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -210,21 +153,16 @@ class _ContactsPageState extends State<ContactsPage> {
|
||||
Widget _buildSearchBar() {
|
||||
return Container(
|
||||
height: 44,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
),
|
||||
decoration: BoxDecoration(color: Color(0xFF2F2A47)),
|
||||
child: TextField(
|
||||
textAlignVertical: TextAlignVertical.center,
|
||||
style: const TextStyle(color: Colors.white),
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
hintText: '搜索联系人',
|
||||
hintStyle: const TextStyle(fontSize: 15, color: Color(0xFF8E8E93)),
|
||||
prefixIcon: const Icon(
|
||||
Icons.search,
|
||||
size: 20,
|
||||
color: Color(0xFF8E8E93),
|
||||
),
|
||||
hintStyle: const TextStyle(fontSize: 15, color: Colors.white),
|
||||
prefixIcon: const Icon(Icons.search, size: 20, color: Colors.white),
|
||||
border: InputBorder.none,
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 12),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
277
lib/pages/explore_page.dart
Normal file
277
lib/pages/explore_page.dart
Normal file
@@ -0,0 +1,277 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:liquid_glass_widgets/liquid_glass_widgets.dart';
|
||||
|
||||
class Comment {
|
||||
final String nickname;
|
||||
final String content;
|
||||
|
||||
Comment({required this.nickname, required this.content});
|
||||
}
|
||||
|
||||
class Moment {
|
||||
final String avatar;
|
||||
final String nickname;
|
||||
final String content;
|
||||
final List<String> images;
|
||||
|
||||
bool liked;
|
||||
int likeCount;
|
||||
List<Comment> comments;
|
||||
|
||||
Moment({
|
||||
required this.avatar,
|
||||
required this.nickname,
|
||||
required this.content,
|
||||
this.images = const [],
|
||||
this.liked = false,
|
||||
this.likeCount = 0,
|
||||
this.comments = const [],
|
||||
});
|
||||
}
|
||||
|
||||
/// ================= Explore Page =================
|
||||
|
||||
class ExplorePage extends StatefulWidget {
|
||||
const ExplorePage({super.key});
|
||||
|
||||
@override
|
||||
State<ExplorePage> createState() => _ExplorePageState();
|
||||
}
|
||||
|
||||
class _ExplorePageState extends State<ExplorePage> {
|
||||
final List<Moment> _moments = [
|
||||
Moment(
|
||||
avatar: 'https://picsum.photos/100?random=1',
|
||||
nickname: '张三',
|
||||
content: '今天天气真不错 ☀️',
|
||||
images: [
|
||||
'https://picsum.photos/300?random=1',
|
||||
'https://picsum.photos/300?random=2',
|
||||
'https://picsum.photos/300?random=3',
|
||||
'https://picsum.photos/300?random=4',
|
||||
'https://picsum.photos/300?random=5',
|
||||
],
|
||||
likeCount: 12,
|
||||
comments: [
|
||||
Comment(nickname: '李四', content: '确实不错'),
|
||||
Comment(nickname: '王五', content: '想去旅游'),
|
||||
],
|
||||
),
|
||||
Moment(
|
||||
avatar: 'https://picsum.photos/100?random=2',
|
||||
nickname: '李四',
|
||||
content: 'Flutter 真香 🚀',
|
||||
images: ['https://picsum.photos/300?random=6'],
|
||||
likeCount: 8,
|
||||
comments: [],
|
||||
),
|
||||
];
|
||||
|
||||
void _handleLike(int index) {
|
||||
setState(() {
|
||||
final m = _moments[index];
|
||||
m.liked ? m.likeCount-- : m.likeCount++;
|
||||
m.liked = !m.liked;
|
||||
});
|
||||
}
|
||||
|
||||
void _handleComment(int index, String text) {
|
||||
setState(() {
|
||||
_moments[index].comments.add(Comment(nickname: '我', content: text));
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.transparent,
|
||||
appBar: _buildAppBar(),
|
||||
body: Padding(padding: const EdgeInsets.all(10), child: _buildMoment()),
|
||||
);
|
||||
}
|
||||
|
||||
PreferredSizeWidget _buildAppBar() {
|
||||
return AppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
backgroundColor: Color(0xFF120D25),
|
||||
elevation: 0,
|
||||
centerTitle: true,
|
||||
title: const Text(
|
||||
'朋友圈',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.add, size: 22, color: Colors.white),
|
||||
splashRadius: 20,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMoment() {
|
||||
return ListView.separated(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context).padding.bottom + 80,
|
||||
),
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
return SizedBox(height: 10);
|
||||
},
|
||||
itemCount: _moments.length,
|
||||
itemBuilder: (context, index) {
|
||||
return MomentItem(
|
||||
moment: _moments[index],
|
||||
onLike: () => _handleLike(index),
|
||||
onComment: (text) => _handleComment(index, text),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// ================= Moment Item =================
|
||||
|
||||
class MomentItem extends StatefulWidget {
|
||||
final Moment moment;
|
||||
final VoidCallback onLike;
|
||||
final Function(String) onComment;
|
||||
|
||||
const MomentItem({
|
||||
super.key,
|
||||
required this.moment,
|
||||
required this.onLike,
|
||||
required this.onComment,
|
||||
});
|
||||
|
||||
@override
|
||||
State<MomentItem> createState() => _MomentItemState();
|
||||
}
|
||||
|
||||
class _MomentItemState extends State<MomentItem> {
|
||||
final TextEditingController _controller = TextEditingController();
|
||||
|
||||
void _submit() {
|
||||
if (_controller.text.trim().isEmpty) return;
|
||||
widget.onComment(_controller.text.trim());
|
||||
_controller.clear();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final m = widget.moment;
|
||||
|
||||
return GlassCard(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
CircleAvatar(backgroundImage: NetworkImage(m.avatar)),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
m.nickname,
|
||||
style: const TextStyle(
|
||||
color: Colors.purple,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(m.content, style: const TextStyle(color: Colors.white)),
|
||||
|
||||
if (m.images.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final isSingle = m.images.length == 1;
|
||||
final itemWidth =
|
||||
isSingle ? 180.0 : (constraints.maxWidth - 8) / 3;
|
||||
|
||||
return Wrap(
|
||||
spacing: 4,
|
||||
runSpacing: 4,
|
||||
children:
|
||||
m.images.map((url) {
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
child: Image.network(
|
||||
url,
|
||||
width: itemWidth,
|
||||
height: itemWidth,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
children: [
|
||||
Badge(
|
||||
label: Text('${m.likeCount}'),
|
||||
isLabelVisible: m.likeCount > 0,
|
||||
offset: const Offset(0, 0),
|
||||
child: IconButton(
|
||||
onPressed: widget.onLike,
|
||||
icon: Icon(
|
||||
m.liked ? Icons.favorite : Icons.favorite_border,
|
||||
color: m.liked ? Colors.red : Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
Badge(
|
||||
label: Text('${m.comments.length}'),
|
||||
isLabelVisible: m.comments.length > 0,
|
||||
offset: const Offset(0, 0),
|
||||
child: IconButton(
|
||||
onPressed: () => {},
|
||||
icon: Icon(Icons.comment, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
if (m.comments.isNotEmpty)
|
||||
Container(
|
||||
width: double.infinity,
|
||||
margin: const EdgeInsets.only(top: 6),
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFF120D25),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children:
|
||||
m.comments
|
||||
.map(
|
||||
(c) => Text(
|
||||
'${c.nickname}:${c.content}',
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:liquid_glass_widgets/liquid_glass_widgets.dart';
|
||||
|
||||
import 'chat_page.dart';
|
||||
|
||||
class MessagePage extends StatelessWidget {
|
||||
const MessagePage({super.key});
|
||||
@@ -6,20 +9,20 @@ class MessagePage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFFF5F5F7),
|
||||
backgroundColor: Colors.transparent,
|
||||
appBar: _buildAppBar(),
|
||||
body: _buildMessageList(),
|
||||
body: _buildMessageList(context),
|
||||
);
|
||||
}
|
||||
|
||||
PreferredSizeWidget _buildAppBar() {
|
||||
return AppBar(
|
||||
backgroundColor: Colors.grey.shade200,
|
||||
backgroundColor: Color(0xFF120D25),
|
||||
elevation: 0,
|
||||
centerTitle: true,
|
||||
leading: IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.menu, size: 22),
|
||||
icon: const Icon(Icons.menu, size: 22, color: Colors.white),
|
||||
splashRadius: 20,
|
||||
),
|
||||
title: const Text(
|
||||
@@ -27,113 +30,106 @@ class MessagePage extends StatelessWidget {
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF1C1C1E),
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.search, size: 22),
|
||||
icon: const Icon(Icons.search, size: 22, color: Colors.white),
|
||||
splashRadius: 20,
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.add_circle_outline, size: 22),
|
||||
icon: const Icon(Icons.add, size: 22, color: Colors.white),
|
||||
splashRadius: 20,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMessageList() {
|
||||
Widget _buildMessageList(BuildContext context) {
|
||||
return ListView.separated(
|
||||
padding: EdgeInsets.only(
|
||||
left: 10,
|
||||
right: 10,
|
||||
top: 10,
|
||||
bottom: MediaQuery.of(context).padding.bottom + 80,
|
||||
),
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
return SizedBox(height: 10);
|
||||
},
|
||||
itemCount: _messages.length,
|
||||
separatorBuilder: (_, __) => const SizedBox(height: 0),
|
||||
itemBuilder: (context, index) {
|
||||
final msg = _messages[index];
|
||||
return Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: Color(0xFFE5E5EA),
|
||||
width: 0.6,
|
||||
),
|
||||
),
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 0),
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: Colors.transparent,
|
||||
backgroundImage: NetworkImage(msg['avatar']!),
|
||||
),
|
||||
title: Text(
|
||||
msg['name'].toString(),
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF1C1C1E),
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
msg['lastMsg'].toString(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
color: Color(0xFF8E8E93),
|
||||
),
|
||||
),
|
||||
trailing: Text(
|
||||
msg['time'].toString(),
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Color(0xFF8E8E93),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
return _buildMessageItem(context, msg);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMessageItem(BuildContext context, Message msg) {
|
||||
return GlassCard(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 0),
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
onTap:
|
||||
() => {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder:
|
||||
(_) => const ChatPage(
|
||||
contactName: '张三',
|
||||
avatarUrl: 'https://picsum.photos/id/1012/200',
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: Colors.transparent,
|
||||
backgroundImage: NetworkImage(msg.avatar),
|
||||
),
|
||||
title: Text(
|
||||
msg.name,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
msg.lastMsg,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontSize: 13, color: Color(0xFFAEAEB2)),
|
||||
),
|
||||
trailing: Text(
|
||||
msg.time,
|
||||
style: const TextStyle(fontSize: 12, color: Color(0xFF8E8E93)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// ✅ 假数据
|
||||
const List<Map<String, String>> _messages = [
|
||||
{
|
||||
'avatar': 'https://randomuser.me/api/portraits/men/32.jpg',
|
||||
'name': '小刘',
|
||||
'lastMsg': '今晚一起吃饭吗?',
|
||||
'time': '12:30',
|
||||
},
|
||||
{
|
||||
'avatar': 'https://randomuser.me/api/portraits/women/44.jpg',
|
||||
'name': 'AI 助手',
|
||||
'lastMsg': '已为你生成总结',
|
||||
'time': '11:20',
|
||||
},
|
||||
{
|
||||
'avatar': 'https://randomuser.me/api/portraits/men/65.jpg',
|
||||
'name': '产品经理',
|
||||
'lastMsg': '新版本评审定在周五',
|
||||
'time': '昨天',
|
||||
},
|
||||
{
|
||||
'avatar': 'https://randomuser.me/api/portraits/men/32.jpg',
|
||||
'name': '小刘',
|
||||
'lastMsg': '今晚一起吃饭吗?',
|
||||
'time': '12:30',
|
||||
},
|
||||
{
|
||||
'avatar': 'https://randomuser.me/api/portraits/women/44.jpg',
|
||||
'name': 'AI 助手',
|
||||
'lastMsg': '已为你生成总结',
|
||||
'time': '11:20',
|
||||
},
|
||||
{
|
||||
'avatar': 'https://randomuser.me/api/portraits/men/65.jpg',
|
||||
'name': '产品经理',
|
||||
'lastMsg': '新版本评审定在周五',
|
||||
'time': '昨天',
|
||||
},
|
||||
];
|
||||
class Message {
|
||||
final String name;
|
||||
final String avatar;
|
||||
final String lastMsg;
|
||||
final String time;
|
||||
|
||||
Message({
|
||||
required this.name,
|
||||
required this.avatar,
|
||||
required this.lastMsg,
|
||||
required this.time,
|
||||
});
|
||||
}
|
||||
|
||||
List<Message> _messages = [
|
||||
Message(
|
||||
avatar: 'https://picsum.photos/id/1012/200',
|
||||
name: '小刘',
|
||||
lastMsg: '今晚一起吃饭吗?',
|
||||
time: '12:30',
|
||||
),
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user