fix: v1.1.6+8 - time filter / signup button / friends sync

This commit is contained in:
Ubuntu 2026-02-17 19:30:49 +08:00
parent df047ad64a
commit 5849b5f1f6
6 changed files with 179 additions and 42 deletions

View File

@ -13,6 +13,7 @@ class ActivityDetailScreen extends StatefulWidget {
class _ActivityDetailScreenState extends State<ActivityDetailScreen> { class _ActivityDetailScreenState extends State<ActivityDetailScreen> {
final TextEditingController _commentController = TextEditingController(); final TextEditingController _commentController = TextEditingController();
bool _isLiked = false; bool _isLiked = false;
bool _isJoined = false;
int _likeCount = 0; int _likeCount = 0;
int _currentImageIndex = 0; int _currentImageIndex = 0;
@ -454,7 +455,12 @@ class _ActivityDetailScreenState extends State<ActivityDetailScreen> {
// //
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: _isJoined
? null
: () {
setState(() {
_isJoined = true;
});
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
content: Text('报名成功!'), content: Text('报名成功!'),
@ -470,7 +476,7 @@ class _ActivityDetailScreenState extends State<ActivityDetailScreen> {
), ),
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 10), padding: EdgeInsets.symmetric(horizontal: 24, vertical: 10),
), ),
child: Text('报名'), child: Text(_isJoined ? '已报名' : '报名'),
), ),
], ],
), ),

View File

@ -14,11 +14,13 @@ class ActivityListScreen extends StatefulWidget {
class _ActivityListScreenState extends State<ActivityListScreen> { class _ActivityListScreenState extends State<ActivityListScreen> {
int _selectedIndex = 0; int _selectedIndex = 0;
final _feedKey = GlobalKey<RefreshIndicatorState>(); final _feedKey = GlobalKey<RefreshIndicatorState>();
String? _selectedTime;
// //
final List<Map<String, dynamic>> activities = [ final List<Map<String, dynamic>> activities = [
{ {
'title': '周末公园太极拳', 'title': '周末公园太极拳',
'date': '今天 07:30',
'location': '人民公园', 'location': '人民公园',
'participants': 12, 'participants': 12,
'image': 'https://picsum.photos/seed/1/400/300', 'image': 'https://picsum.photos/seed/1/400/300',
@ -27,6 +29,7 @@ class _ActivityListScreenState extends State<ActivityListScreen> {
}, },
{ {
'title': '社区书法班开课啦', 'title': '社区书法班开课啦',
'date': '明天 09:00',
'location': '社区活动中心', 'location': '社区活动中心',
'participants': 8, 'participants': 8,
'image': 'https://picsum.photos/seed/2/400/500', 'image': 'https://picsum.photos/seed/2/400/500',
@ -35,6 +38,7 @@ class _ActivityListScreenState extends State<ActivityListScreen> {
}, },
{ {
'title': '广场舞队招新', 'title': '广场舞队招新',
'date': '本周三 19:00',
'location': '中央广场', 'location': '中央广场',
'participants': 25, 'participants': 25,
'image': 'https://picsum.photos/seed/3/400/400', 'image': 'https://picsum.photos/seed/3/400/400',
@ -43,6 +47,7 @@ class _ActivityListScreenState extends State<ActivityListScreen> {
}, },
{ {
'title': '老年摄影兴趣小组', 'title': '老年摄影兴趣小组',
'date': '本周四 14:00',
'location': '文化馆', 'location': '文化馆',
'participants': 15, 'participants': 15,
'image': 'https://picsum.photos/seed/4/400/350', 'image': 'https://picsum.photos/seed/4/400/350',
@ -51,6 +56,7 @@ class _ActivityListScreenState extends State<ActivityListScreen> {
}, },
{ {
'title': '周三棋牌活动', 'title': '周三棋牌活动',
'date': '本周五 16:00',
'location': '社区会所', 'location': '社区会所',
'participants': 20, 'participants': 20,
'image': 'https://picsum.photos/seed/5/400/450', 'image': 'https://picsum.photos/seed/5/400/450',
@ -59,6 +65,7 @@ class _ActivityListScreenState extends State<ActivityListScreen> {
}, },
{ {
'title': '健康养生讲座', 'title': '健康养生讲座',
'date': '${DateTime.now().month}${DateTime.now().day}日 10:30',
'location': '图书馆', 'location': '图书馆',
'participants': 30, 'participants': 30,
'image': 'https://picsum.photos/seed/6/400/380', 'image': 'https://picsum.photos/seed/6/400/380',
@ -147,24 +154,80 @@ class _ActivityListScreenState extends State<ActivityListScreen> {
} }
Widget _buildFeed() { Widget _buildFeed() {
final filteredActivities = _getFilteredActivities();
return RefreshIndicator( return RefreshIndicator(
key: _feedKey, key: _feedKey,
onRefresh: _refreshActivities, onRefresh: _refreshActivities,
child: Padding( child: ListView(
padding: EdgeInsets.all(12), physics: const AlwaysScrollableScrollPhysics(),
child: MasonryGridView.count( padding: const EdgeInsets.all(12),
children: [
_buildTimeFilterChips(),
const SizedBox(height: 12),
MasonryGridView.count(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
crossAxisCount: 2, crossAxisCount: 2,
mainAxisSpacing: 10, mainAxisSpacing: 10,
crossAxisSpacing: 10, crossAxisSpacing: 10,
itemCount: activities.length, itemCount: filteredActivities.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return _buildActivityCard(activities[index], 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<Map<String, dynamic>> _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<String, dynamic> activity, int index) { Widget _buildActivityCard(Map<String, dynamic> activity, int index) {
final imageHeight = index.isOdd ? 160.0 : 210.0; final imageHeight = index.isOdd ? 160.0 : 210.0;
final coverUrl = final coverUrl =
@ -281,6 +344,28 @@ class _ActivityListScreenState extends State<ActivityListScreen> {
), ),
], ],
), ),
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,
),
),
],
),
], ],
), ),
), ),

View File

@ -9,26 +9,41 @@ class FriendsScreen extends StatefulWidget {
class _FriendsScreenState extends State<FriendsScreen> { class _FriendsScreenState extends State<FriendsScreen> {
final List<Map<String, dynamic>> _followingUsers = [ final List<Map<String, dynamic>> _followingUsers = [
{'name': '林阿姨', 'avatar': 3, 'isFollowing': true}, {'id': 'linayi', 'name': '林阿姨', 'avatar': 3, 'isFollowing': true},
{'name': '黄叔叔', 'avatar': 7, 'isFollowing': true}, {'id': 'huangshu', 'name': '黄叔叔', 'avatar': 7, 'isFollowing': true},
{'name': '周老师', 'avatar': 12, 'isFollowing': true}, {'id': 'zhoulaoshi', 'name': '周老师', 'avatar': 12, 'isFollowing': true},
{'name': '何姐', 'avatar': 18, 'isFollowing': true}, {'id': 'hejie', 'name': '何姐', 'avatar': 18, 'isFollowing': true},
{'name': '苏大哥', 'avatar': 24, 'isFollowing': false}, {'id': 'sudage', 'name': '苏大哥', 'avatar': 24, 'isFollowing': false},
{'name': '陶阿姨', 'avatar': 31, 'isFollowing': true}, {'id': 'taoayi', 'name': '陶阿姨', 'avatar': 31, 'isFollowing': true},
]; ];
final List<Map<String, dynamic>> _fansUsers = [ final List<Map<String, dynamic>> _fansUsers = [
{'name': '白阿姨', 'avatar': 5, 'isFollowing': false}, {'id': 'baiayi', 'name': '白阿姨', 'avatar': 5, 'isFollowing': false},
{'name': '邓叔', 'avatar': 14, 'isFollowing': true}, {'id': 'dengshu', 'name': '邓叔', 'avatar': 14, 'isFollowing': true},
{'name': '贺老师', 'avatar': 19, 'isFollowing': false}, {'id': 'helaoshi', 'name': '贺老师', 'avatar': 19, 'isFollowing': false},
{'name': '任姐', 'avatar': 26, 'isFollowing': true}, {'id': 'renjie', 'name': '任姐', 'avatar': 26, 'isFollowing': true},
{'name': '罗阿姨', 'avatar': 38, 'isFollowing': false}, {'id': 'luoayi', 'name': '罗阿姨', 'avatar': 38, 'isFollowing': false},
{'name': '章叔叔', 'avatar': 46, 'isFollowing': true}, {'id': 'zhangshu', 'name': '章叔叔', 'avatar': 46, 'isFollowing': true},
]; ];
void _toggleFollow(List<Map<String, dynamic>> users, int index) { late final Set<String> _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(() { 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<FriendsScreen> {
separatorBuilder: (_, __) => const SizedBox(height: 10), separatorBuilder: (_, __) => const SizedBox(height: 10),
itemBuilder: (context, index) { itemBuilder: (context, index) {
final user = users[index]; final user = users[index];
final isFollowing = user['isFollowing'] as bool; final userId = user['id'] as String;
final isFollowing = _following.contains(userId);
return Container( return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
decoration: BoxDecoration( decoration: BoxDecoration(
@ -95,7 +111,7 @@ class _FriendsScreenState extends State<FriendsScreen> {
SizedBox( SizedBox(
height: 34, height: 34,
child: TextButton( child: TextButton(
onPressed: () => _toggleFollow(users, index), onPressed: () => _toggleFollow(userId),
style: TextButton.styleFrom( style: TextButton.styleFrom(
backgroundColor: isFollowing ? Colors.white : const Color(0xFFFF8A3D), backgroundColor: isFollowing ? Colors.white : const Color(0xFFFF8A3D),
foregroundColor: isFollowing ? const Color(0xFFFF8A3D) : Colors.white, foregroundColor: isFollowing ? const Color(0xFFFF8A3D) : Colors.white,

View File

@ -33,6 +33,13 @@ class _HomeScreenState extends State<HomeScreen> {
List<Activity> get _filteredActivities { List<Activity> get _filteredActivities {
return _activities.where((a) { return _activities.where((a) {
if (_selectedCategory != '全部' && a.category != _selectedCategory) return false; 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; return true;
}).toList(); }).toList();
} }
@ -123,6 +130,7 @@ class _HomeScreenState extends State<HomeScreen> {
SliverList( SliverList(
delegate: SliverChildBuilderDelegate( delegate: SliverChildBuilderDelegate(
(ctx, i) => _ActivityCard( (ctx, i) => _ActivityCard(
key: ValueKey(_filteredActivities[i].id),
activity: _filteredActivities[i], activity: _filteredActivities[i],
onTap: () => Navigator.push(context, MaterialPageRoute( onTap: () => Navigator.push(context, MaterialPageRoute(
builder: (_) => ActivityDetailScreen(activity: _filteredActivities[i]), builder: (_) => ActivityDetailScreen(activity: _filteredActivities[i]),
@ -146,22 +154,44 @@ class _HomeScreenState extends State<HomeScreen> {
} }
} }
class _ActivityCard extends StatelessWidget { class _ActivityCard extends StatefulWidget {
final Activity activity; final Activity activity;
final VoidCallback onTap; 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) { String _categoryEmoji(String cat) {
const map = {'太极':'🧘','茶话会':'🍵','书法':'✍️','摄影':'📷','舞蹈':'💃','户外徒步':'🥾','手工':'🎨','唱歌':'🎵','棋牌':'♟️','读书':'📚','晨练':'🏃'}; const map = {'太极':'🧘','茶话会':'🍵','书法':'✍️','摄影':'📷','舞蹈':'💃','户外徒步':'🥾','手工':'🎨','唱歌':'🎵','棋牌':'♟️','读书':'📚','晨练':'🏃'};
return map[cat] ?? '🎯'; return map[cat] ?? '🎯';
} }
void _joinActivity() {
setState(() {
_isJoined = true;
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('报名成功!')),
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final activity = widget.activity;
final canJoin = !activity.isFull && !_isJoined;
final buttonText = _isJoined
? '已报名'
: (activity.isFull ? '已满员' : '报名');
return Card( return Card(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
child: InkWell( child: InkWell(
onTap: onTap, onTap: widget.onTap,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
child: Padding( child: Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
@ -203,12 +233,12 @@ class _ActivityCard extends StatelessWidget {
SizedBox( SizedBox(
height: 40, height: 40,
child: ElevatedButton( child: ElevatedButton(
onPressed: activity.isFull ? null : () {}, onPressed: canJoin ? _joinActivity : null,
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
minimumSize: const Size(80, 40), minimumSize: const Size(80, 40),
padding: const EdgeInsets.symmetric(horizontal: 20), padding: const EdgeInsets.symmetric(horizontal: 20),
), ),
child: Text(activity.isFull ? '已满员' : '报名', style: const TextStyle(fontSize: 16)), child: Text(buttonText, style: const TextStyle(fontSize: 16)),
), ),
), ),
], ],

View File

@ -1,6 +1,6 @@
name: banxiang_app name: banxiang_app
description: 伴享 description: 伴享
version: 1.1.5+7 version: 1.1.6+8
environment: environment:
sdk: '>=3.0.0 <4.0.0' sdk: '>=3.0.0 <4.0.0'

Binary file not shown.