Flutter健身APP创建锻炼集合后数据重复展示异常求助
问题:锻炼集合数据重复显示,新分类无法正确展示
我用Flutter开发了一款健身APP,现在遇到一个问题——创建的每个锻炼集合都会显示相同的数据。在个人资料页面创建新的锻炼分类后,页面没有展示对应的新数据,而是依然显示重复内容。
以下是锻炼日程页面的核心代码:
class WorkoutContentAdded extends StatefulWidget { final String programId; const WorkoutContentAdded({Key? key, required this.programId}) : super(key: key); @override _WorkoutContentAddedState createState() => _WorkoutContentAddedState(); } class _WorkoutContentAddedState extends State<WorkoutContentAdded> { late Stream<QuerySnapshot> _workoutScheduleStream; @override void initState() { super.initState(); _initializeWorkoutScheduleStream(); } @override void didUpdateWidget(WorkoutContentAdded oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.programId != widget.programId) { _initializeWorkoutScheduleStream(); } } void _initializeWorkoutScheduleStream() { print('Initializing workout schedule stream for program: ${widget.programId}'); _workoutScheduleStream = FirebaseFirestore.instance .collection('programs') .doc(widget.programId) .collection('workout_schedule') .orderBy('order') .snapshots(); } @override Widget build(BuildContext context) { print('Building WorkoutContentAdded for program: ${widget.programId}'); return Scaffold( // ... other scaffold properties body: CustomScrollView( slivers: [ // ... other slivers SliverToBoxAdapter( child: Padding( padding: EdgeInsets.fromLTRB(16, 16, 16, 0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Workout Schedule', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, ), ), StreamBuilder<QuerySnapshot>( stream: _workoutScheduleStream, builder: (context, scheduleSnapshot) { print('Workout schedule stream update for program: ${widget.programId}'); print('Connection state: ${scheduleSnapshot.connectionState}'); if (scheduleSnapshot.hasError) { print('Error in schedule stream: ${scheduleSnapshot.error}'); } if (scheduleSnapshot.hasData) { print('Number of workout days: ${scheduleSnapshot.data!.docs.length}'); } if (scheduleSnapshot.connectionState == ConnectionState.waiting) { return CircularProgressIndicator(); } if (scheduleSnapshot.hasError) { return Text('Error: ${scheduleSnapshot.error}'); } if (!scheduleSnapshot.hasData || scheduleSnapshot.data!.docs.isEmpty) { return Padding( padding: EdgeInsets.only(top: 16), child: Text('No workout schedule added for this program.'), ); } List<DocumentSnapshot> sortedDocs = scheduleSnapshot.data!.docs; return ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), itemCount: sortedDocs.length, padding: EdgeInsets.only(top: 10), itemBuilder: (context, index) { Map<String, dynamic> data = sortedDocs[index].data() as Map<String, dynamic>; String day = data['day'] ?? 'Unknown Day'; return GestureDetector( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => VideoContent( day: day, allowEditing: true, programId: widget.programId, ), ), ); }, child: Card( // Card content ), ); }, ); }, ), ], ), ), ), ], ), floatingActionButton: FloatingActionButton( onPressed: _showAddCategoryBottomSheet, backgroundColor: Colors.black, child: Icon(Icons.add, color: Colors.white), ), floatingActionButtonLocation: FloatingActionButtonLocation.startFloat, ); } void _showAddCategoryBottomSheet() { String selectedOrdinal = '1st'; String selectedDay = 'Monday'; String estimatedKcal = ''; showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), builder: (BuildContext context) { return StatefulBuilder( builder: (BuildContext context, StateSetter setState) { return Padding( padding: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom, left: 24, right: 24, top: 24, ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Choose the day of the week', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), SizedBox(height: 20), Row( children: [ Expanded( child: DropdownButtonFormField<String>( value: selectedOrdinal, decoration: InputDecoration( border: OutlineInputBorder(), contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), ), items: ['1st', '2nd', '3rd', '4th'] .map((String value) { return DropdownMenuItem<String>( value: value, child: Text(value), ); }).toList(), onChanged: (String? newValue) { if (newValue != null) { setState(() { selectedOrdinal = newValue; }); } }, ), ), SizedBox(width: 10), Expanded( child: DropdownButtonFormField<String>( value: selectedDay, decoration: InputDecoration( border: OutlineInputBorder(), contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), ), items: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'] .map((String value) { return DropdownMenuItem<String>( value: value, child: Text(value), ); }).toList(), onChanged: (String? newValue) { if (newValue != null) { setState(() { selectedDay = newValue; }); } }, ), ), ], ), SizedBox(height: 20), TextField( decoration: InputDecoration( hintText: 'Estimative kcal burnt', border: OutlineInputBorder(), ), keyboardType: TextInputType.number, onChanged: (value) { estimatedKcal = value; }, ), SizedBox(height: 20), Center( child: ElevatedButton( onPressed: () async { await FirebaseFirestore.instance.collection('workout_schedule').add({ 'ordinal': selectedOrdinal, 'day': selectedDay, 'estimatedKcal': estimatedKcal, }); Navigator.pop(context); }, child: Text('Save'), style: ElevatedButton.styleFrom( backgroundColor: Colors.black, foregroundColor: Colors.white, padding: EdgeInsets.symmetric(horizontal: 32, vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(30), ), ), ), ), SizedBox(height: 20), ], ), ); } ); }, ); } }
解决方案
1. 修复数据存储路径错误
代码中存在核心问题:读取和保存数据的路径不匹配。
- 读取数据时,从
programs/{programId}/workout_schedule子集合获取; - 保存数据时,直接存入根集合
workout_schedule,导致所有program读取的都是同一批数据,因此显示重复。
修改_showAddCategoryBottomSheet中的保存逻辑,将数据存入对应program的子集合:
// 替换原有的保存代码 await FirebaseFirestore.instance .collection('programs') .doc(widget.programId) // 关联当前program的ID .collection('workout_schedule') .add({ 'ordinal': selectedOrdinal, 'day': selectedDay, 'estimatedKcal': estimatedKcal, 'order': _getOrderFromOrdinal(selectedOrdinal), // 补充排序用的order字段 });
同时添加辅助函数,将ordinal转换为数值类型的order(读取时用了orderBy('order'),原代码缺少该字段会导致排序异常):
int _getOrderFromOrdinal(String ordinal) { switch(ordinal) { case '1st': return 1; case '2nd': return 2; case '3rd': return 3; case '4th': return 4; default: return 0; } }
2. 验证Stream更新逻辑
当前代码的didUpdateWidget已经处理了programId变化时重新初始化Stream的逻辑,这部分是正确的。可以通过控制台打印的日志,确认programId是否正确切换,确保Stream指向对应program的子集合。
内容的提问来源于stack exchange,提问作者sanda17
相关产品推荐
相关产品推荐

