fix: v1.1.6+8 - time filter / signup button / friends sync
This commit is contained in:
parent
df047ad64a
commit
5849b5f1f6
@ -13,6 +13,7 @@ class ActivityDetailScreen extends StatefulWidget {
|
||||
class _ActivityDetailScreenState extends State<ActivityDetailScreen> {
|
||||
final TextEditingController _commentController = TextEditingController();
|
||||
bool _isLiked = false;
|
||||
bool _isJoined = false;
|
||||
int _likeCount = 0;
|
||||
int _currentImageIndex = 0;
|
||||
|
||||
@ -454,14 +455,19 @@ class _ActivityDetailScreenState extends State<ActivityDetailScreen> {
|
||||
|
||||
// 报名按钮
|
||||
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<ActivityDetailScreen> {
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 10),
|
||||
),
|
||||
child: Text('报名'),
|
||||
child: Text(_isJoined ? '已报名' : '报名'),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@ -14,11 +14,13 @@ class ActivityListScreen extends StatefulWidget {
|
||||
class _ActivityListScreenState extends State<ActivityListScreen> {
|
||||
int _selectedIndex = 0;
|
||||
final _feedKey = GlobalKey<RefreshIndicatorState>();
|
||||
String? _selectedTime;
|
||||
|
||||
// 模拟活动数据
|
||||
final List<Map<String, dynamic>> activities = [
|
||||
{
|
||||
'title': '周末公园太极拳',
|
||||
'date': '今天 07:30',
|
||||
'location': '人民公园',
|
||||
'participants': 12,
|
||||
'image': 'https://picsum.photos/seed/1/400/300',
|
||||
@ -27,6 +29,7 @@ class _ActivityListScreenState extends State<ActivityListScreen> {
|
||||
},
|
||||
{
|
||||
'title': '社区书法班开课啦',
|
||||
'date': '明天 09:00',
|
||||
'location': '社区活动中心',
|
||||
'participants': 8,
|
||||
'image': 'https://picsum.photos/seed/2/400/500',
|
||||
@ -35,6 +38,7 @@ class _ActivityListScreenState extends State<ActivityListScreen> {
|
||||
},
|
||||
{
|
||||
'title': '广场舞队招新',
|
||||
'date': '本周三 19:00',
|
||||
'location': '中央广场',
|
||||
'participants': 25,
|
||||
'image': 'https://picsum.photos/seed/3/400/400',
|
||||
@ -43,6 +47,7 @@ class _ActivityListScreenState extends State<ActivityListScreen> {
|
||||
},
|
||||
{
|
||||
'title': '老年摄影兴趣小组',
|
||||
'date': '本周四 14:00',
|
||||
'location': '文化馆',
|
||||
'participants': 15,
|
||||
'image': 'https://picsum.photos/seed/4/400/350',
|
||||
@ -51,6 +56,7 @@ class _ActivityListScreenState extends State<ActivityListScreen> {
|
||||
},
|
||||
{
|
||||
'title': '周三棋牌活动',
|
||||
'date': '本周五 16:00',
|
||||
'location': '社区会所',
|
||||
'participants': 20,
|
||||
'image': 'https://picsum.photos/seed/5/400/450',
|
||||
@ -59,6 +65,7 @@ class _ActivityListScreenState extends State<ActivityListScreen> {
|
||||
},
|
||||
{
|
||||
'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<ActivityListScreen> {
|
||||
}
|
||||
|
||||
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<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) {
|
||||
final imageHeight = index.isOdd ? 160.0 : 210.0;
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@ -9,26 +9,41 @@ class FriendsScreen extends StatefulWidget {
|
||||
|
||||
class _FriendsScreenState extends State<FriendsScreen> {
|
||||
final List<Map<String, dynamic>> _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<Map<String, dynamic>> _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<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(() {
|
||||
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),
|
||||
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<FriendsScreen> {
|
||||
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,
|
||||
|
||||
@ -33,6 +33,13 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
List<Activity> 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<HomeScreen> {
|
||||
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<HomeScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
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)),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@ -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'
|
||||
|
||||
BIN
releases/banxiang-v1.1.6+8.apk
Normal file
BIN
releases/banxiang-v1.1.6+8.apk
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user