diff --git a/lib/screens/activity_detail_screen.dart b/lib/screens/activity_detail_screen.dart index 4c4bc3b..d885016 100644 --- a/lib/screens/activity_detail_screen.dart +++ b/lib/screens/activity_detail_screen.dart @@ -13,6 +13,7 @@ class ActivityDetailScreen extends StatefulWidget { class _ActivityDetailScreenState extends State { final TextEditingController _commentController = TextEditingController(); bool _isLiked = false; + bool _isJoined = false; int _likeCount = 0; int _currentImageIndex = 0; @@ -454,14 +455,19 @@ class _ActivityDetailScreenState extends State { // 报名按钮 ElevatedButton( - onPressed: () { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('报名成功!'), - backgroundColor: Color(0xFFFF6B35), - ), - ); - }, + onPressed: _isJoined + ? null + : () { + setState(() { + _isJoined = true; + }); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('报名成功!'), + backgroundColor: Color(0xFFFF6B35), + ), + ); + }, style: ElevatedButton.styleFrom( backgroundColor: Color(0xFFFF6B35), foregroundColor: Colors.white, @@ -470,7 +476,7 @@ class _ActivityDetailScreenState extends State { ), padding: EdgeInsets.symmetric(horizontal: 24, vertical: 10), ), - child: Text('报名'), + child: Text(_isJoined ? '已报名' : '报名'), ), ], ), diff --git a/lib/screens/activity_list_screen.dart b/lib/screens/activity_list_screen.dart index b41c821..a6ccf08 100644 --- a/lib/screens/activity_list_screen.dart +++ b/lib/screens/activity_list_screen.dart @@ -14,11 +14,13 @@ class ActivityListScreen extends StatefulWidget { class _ActivityListScreenState extends State { int _selectedIndex = 0; final _feedKey = GlobalKey(); + String? _selectedTime; // 模拟活动数据 final List> activities = [ { 'title': '周末公园太极拳', + 'date': '今天 07:30', 'location': '人民公园', 'participants': 12, 'image': 'https://picsum.photos/seed/1/400/300', @@ -27,6 +29,7 @@ class _ActivityListScreenState extends State { }, { 'title': '社区书法班开课啦', + 'date': '明天 09:00', 'location': '社区活动中心', 'participants': 8, 'image': 'https://picsum.photos/seed/2/400/500', @@ -35,6 +38,7 @@ class _ActivityListScreenState extends State { }, { 'title': '广场舞队招新', + 'date': '本周三 19:00', 'location': '中央广场', 'participants': 25, 'image': 'https://picsum.photos/seed/3/400/400', @@ -43,6 +47,7 @@ class _ActivityListScreenState extends State { }, { 'title': '老年摄影兴趣小组', + 'date': '本周四 14:00', 'location': '文化馆', 'participants': 15, 'image': 'https://picsum.photos/seed/4/400/350', @@ -51,6 +56,7 @@ class _ActivityListScreenState extends State { }, { 'title': '周三棋牌活动', + 'date': '本周五 16:00', 'location': '社区会所', 'participants': 20, 'image': 'https://picsum.photos/seed/5/400/450', @@ -59,6 +65,7 @@ class _ActivityListScreenState extends State { }, { 'title': '健康养生讲座', + 'date': '${DateTime.now().month}月${DateTime.now().day}日 10:30', 'location': '图书馆', 'participants': 30, 'image': 'https://picsum.photos/seed/6/400/380', @@ -147,24 +154,80 @@ class _ActivityListScreenState extends State { } Widget _buildFeed() { + final filteredActivities = _getFilteredActivities(); return RefreshIndicator( key: _feedKey, onRefresh: _refreshActivities, - child: Padding( - padding: EdgeInsets.all(12), - child: MasonryGridView.count( - crossAxisCount: 2, - mainAxisSpacing: 10, - crossAxisSpacing: 10, - itemCount: activities.length, - itemBuilder: (context, index) { - return _buildActivityCard(activities[index], index); - }, - ), + child: ListView( + physics: const AlwaysScrollableScrollPhysics(), + padding: const EdgeInsets.all(12), + children: [ + _buildTimeFilterChips(), + const SizedBox(height: 12), + MasonryGridView.count( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + crossAxisCount: 2, + mainAxisSpacing: 10, + crossAxisSpacing: 10, + itemCount: filteredActivities.length, + itemBuilder: (context, index) { + return _buildActivityCard(filteredActivities[index], index); + }, + ), + ], ), ); } + Widget _buildTimeFilterChips() { + const timeOptions = ['今天', '明天', '本周']; + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: timeOptions.map((time) { + final isSelected = _selectedTime == time; + return Padding( + padding: const EdgeInsets.only(right: 8), + child: ChoiceChip( + label: Text(time), + selected: isSelected, + onSelected: (_) { + setState(() { + _selectedTime = isSelected ? null : time; + }); + }, + selectedColor: const Color(0xFFFF6B35), + labelStyle: TextStyle( + color: isSelected ? Colors.white : const Color(0xFF666666), + fontWeight: FontWeight.w600, + ), + backgroundColor: const Color(0xFFFFEFE3), + ), + ); + }).toList(), + ), + ); + } + + List> _getFilteredActivities() { + if (_selectedTime == null || _selectedTime == '本周') { + return activities; + } + + final todayLabel = '${DateTime.now().month}月${DateTime.now().day}日'; + return activities.where((activity) { + final dateText = (activity['date'] ?? '').toString(); + if (_selectedTime == '今天') { + return dateText.contains('今天') || dateText.contains(todayLabel); + } + if (_selectedTime == '明天') { + return dateText.contains('明天'); + } + return true; + }).toList(); + } + Widget _buildActivityCard(Map activity, int index) { final imageHeight = index.isOdd ? 160.0 : 210.0; final coverUrl = @@ -281,6 +344,28 @@ class _ActivityListScreenState extends State { ), ], ), + SizedBox(height: 6), + Row( + children: [ + Icon( + Icons.access_time, + size: 13, + color: Color(0xFF999999), + ), + SizedBox(width: 4), + Expanded( + child: Text( + activity['date'] ?? '', + style: TextStyle( + fontSize: 12, + color: Color(0xFF999999), + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), ], ), ), diff --git a/lib/screens/friends_screen.dart b/lib/screens/friends_screen.dart index bff2f34..2c25148 100644 --- a/lib/screens/friends_screen.dart +++ b/lib/screens/friends_screen.dart @@ -9,26 +9,41 @@ class FriendsScreen extends StatefulWidget { class _FriendsScreenState extends State { final List> _followingUsers = [ - {'name': '林阿姨', 'avatar': 3, 'isFollowing': true}, - {'name': '黄叔叔', 'avatar': 7, 'isFollowing': true}, - {'name': '周老师', 'avatar': 12, 'isFollowing': true}, - {'name': '何姐', 'avatar': 18, 'isFollowing': true}, - {'name': '苏大哥', 'avatar': 24, 'isFollowing': false}, - {'name': '陶阿姨', 'avatar': 31, 'isFollowing': true}, + {'id': 'linayi', 'name': '林阿姨', 'avatar': 3, 'isFollowing': true}, + {'id': 'huangshu', 'name': '黄叔叔', 'avatar': 7, 'isFollowing': true}, + {'id': 'zhoulaoshi', 'name': '周老师', 'avatar': 12, 'isFollowing': true}, + {'id': 'hejie', 'name': '何姐', 'avatar': 18, 'isFollowing': true}, + {'id': 'sudage', 'name': '苏大哥', 'avatar': 24, 'isFollowing': false}, + {'id': 'taoayi', 'name': '陶阿姨', 'avatar': 31, 'isFollowing': true}, ]; final List> _fansUsers = [ - {'name': '白阿姨', 'avatar': 5, 'isFollowing': false}, - {'name': '邓叔', 'avatar': 14, 'isFollowing': true}, - {'name': '贺老师', 'avatar': 19, 'isFollowing': false}, - {'name': '任姐', 'avatar': 26, 'isFollowing': true}, - {'name': '罗阿姨', 'avatar': 38, 'isFollowing': false}, - {'name': '章叔叔', 'avatar': 46, 'isFollowing': true}, + {'id': 'baiayi', 'name': '白阿姨', 'avatar': 5, 'isFollowing': false}, + {'id': 'dengshu', 'name': '邓叔', 'avatar': 14, 'isFollowing': true}, + {'id': 'helaoshi', 'name': '贺老师', 'avatar': 19, 'isFollowing': false}, + {'id': 'renjie', 'name': '任姐', 'avatar': 26, 'isFollowing': true}, + {'id': 'luoayi', 'name': '罗阿姨', 'avatar': 38, 'isFollowing': false}, + {'id': 'zhangshu', 'name': '章叔叔', 'avatar': 46, 'isFollowing': true}, ]; - void _toggleFollow(List> users, int index) { + late final Set _following; + + @override + void initState() { + super.initState(); + _following = { + ..._followingUsers.where((u) => u['isFollowing'] as bool).map((u) => u['id'] as String), + ..._fansUsers.where((u) => u['isFollowing'] as bool).map((u) => u['id'] as String), + }; + } + + void _toggleFollow(String userId) { setState(() { - users[index]['isFollowing'] = !(users[index]['isFollowing'] as bool); + if (_following.contains(userId)) { + _following.remove(userId); + } else { + _following.add(userId); + } }); } @@ -67,7 +82,8 @@ class _FriendsScreenState extends State { separatorBuilder: (_, __) => const SizedBox(height: 10), itemBuilder: (context, index) { final user = users[index]; - final isFollowing = user['isFollowing'] as bool; + final userId = user['id'] as String; + final isFollowing = _following.contains(userId); return Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), decoration: BoxDecoration( @@ -95,7 +111,7 @@ class _FriendsScreenState extends State { SizedBox( height: 34, child: TextButton( - onPressed: () => _toggleFollow(users, index), + onPressed: () => _toggleFollow(userId), style: TextButton.styleFrom( backgroundColor: isFollowing ? Colors.white : const Color(0xFFFF8A3D), foregroundColor: isFollowing ? const Color(0xFFFF8A3D) : Colors.white, diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 69da42a..0ec1d04 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -33,6 +33,13 @@ class _HomeScreenState extends State { List get _filteredActivities { return _activities.where((a) { if (_selectedCategory != '全部' && a.category != _selectedCategory) return false; + if (_timeFilter == '今天') { + return DateUtils.isSameDay(a.time, DateTime.now()); + } + if (_timeFilter == '明天') { + final tomorrow = DateTime.now().add(const Duration(days: 1)); + return DateUtils.isSameDay(a.time, tomorrow); + } return true; }).toList(); } @@ -123,6 +130,7 @@ class _HomeScreenState extends State { SliverList( delegate: SliverChildBuilderDelegate( (ctx, i) => _ActivityCard( + key: ValueKey(_filteredActivities[i].id), activity: _filteredActivities[i], onTap: () => Navigator.push(context, MaterialPageRoute( builder: (_) => ActivityDetailScreen(activity: _filteredActivities[i]), @@ -146,22 +154,44 @@ class _HomeScreenState extends State { } } -class _ActivityCard extends StatelessWidget { +class _ActivityCard extends StatefulWidget { final Activity activity; final VoidCallback onTap; - const _ActivityCard({required this.activity, required this.onTap}); + const _ActivityCard({super.key, required this.activity, required this.onTap}); + + @override + State<_ActivityCard> createState() => _ActivityCardState(); +} + +class _ActivityCardState extends State<_ActivityCard> { + bool _isJoined = false; String _categoryEmoji(String cat) { const map = {'太极':'🧘','茶话会':'🍵','书法':'✍️','摄影':'📷','舞蹈':'💃','户外徒步':'🥾','手工':'🎨','唱歌':'🎵','棋牌':'♟️','读书':'📚','晨练':'🏃'}; return map[cat] ?? '🎯'; } + void _joinActivity() { + setState(() { + _isJoined = true; + }); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('报名成功!')), + ); + } + @override Widget build(BuildContext context) { + final activity = widget.activity; + final canJoin = !activity.isFull && !_isJoined; + final buttonText = _isJoined + ? '已报名' + : (activity.isFull ? '已满员' : '报名'); + return Card( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), child: InkWell( - onTap: onTap, + onTap: widget.onTap, borderRadius: BorderRadius.circular(16), child: Padding( padding: const EdgeInsets.all(16), @@ -203,12 +233,12 @@ class _ActivityCard extends StatelessWidget { SizedBox( height: 40, child: ElevatedButton( - onPressed: activity.isFull ? null : () {}, + onPressed: canJoin ? _joinActivity : null, style: ElevatedButton.styleFrom( minimumSize: const Size(80, 40), padding: const EdgeInsets.symmetric(horizontal: 20), ), - child: Text(activity.isFull ? '已满员' : '报名', style: const TextStyle(fontSize: 16)), + child: Text(buttonText, style: const TextStyle(fontSize: 16)), ), ), ], diff --git a/pubspec.yaml b/pubspec.yaml index 18391c2..c9102be 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: banxiang_app description: 伴享 -version: 1.1.5+7 +version: 1.1.6+8 environment: sdk: '>=3.0.0 <4.0.0' diff --git a/releases/banxiang-v1.1.6+8.apk b/releases/banxiang-v1.1.6+8.apk new file mode 100644 index 0000000..b63659a Binary files /dev/null and b/releases/banxiang-v1.1.6+8.apk differ