Prechádzať zdrojové kódy

空null问题解决

ValiZhang 11 mesiacov pred
rodič
commit
5e8b078191

+ 8 - 183
lib/features/challenge/views/challenge_list_screen.dart

@@ -1,9 +1,9 @@
 // challenge_list_screen.dart
 import 'package:flutter/material.dart';
 import 'package:flutter_riverpod/flutter_riverpod.dart';
-import 'package:intl/intl.dart';
 import 'package:japp_flutter/core/models/challenge_model.dart';
 import 'package:japp_flutter/features/challenge/view_models/challenge_list_vm.dart';
+import 'package:japp_flutter/features/challenge/views/widgets/challenge_card.dart';
 
 class ChallengeListScreen extends ConsumerWidget {
   const ChallengeListScreen({super.key});
@@ -57,191 +57,16 @@ class ChallengeListScreen extends ConsumerWidget {
               child: ListView.builder(
                 padding: const EdgeInsets.all(16),
                 itemCount: challenges.length,
-                // itemBuilder: (_, index) => ChallengeCard(challenge: challenges[index]),
-                itemBuilder: (context, index) => ListTile(
-                  title: Text(challenges[index].title),
-                  // 其他列表项内容
-                ),
-              ),
-            ),
-    );
-  }
-}
-
-class ChallengeCard extends StatelessWidget {
-  final ChallengeModel challenge;
-
-  const ChallengeCard({super.key, required this.challenge});
-
-  @override
-  Widget build(BuildContext context) {
-    final theme = Theme.of(context);
-
-    return Card(
-      elevation: 2,
-      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
-      margin: const EdgeInsets.only(bottom: 16),
-      child: Padding(
-        padding: const EdgeInsets.all(16),
-        child: Column(
-          crossAxisAlignment: CrossAxisAlignment.start,
-          children: [
-            Row(
-              children: [
-                Expanded(
-                  child: Text(
-                    challenge.title,
-                    style: const TextStyle(
-                      fontSize: 18,
-                      fontWeight: FontWeight.bold,
-                    ),
-                  ),
-                ),
-                _DifficultyChip(difficulty: challenge.difficulty),
-              ],
-            ),
-            const SizedBox(height: 8),
-            Text(
-              challenge.description,
-              style: TextStyle(color: Colors.grey[700], fontSize: 14),
-            ),
-            const SizedBox(height: 16),
-            _ProgressBar(
-              startDate: challenge.startDate,
-              endDate: challenge.endDate,
-              remainingDays: challenge.remainingDays!,
-            ),
-            const SizedBox(height: 12),
-            _CardFooter(
-              participants: challenge.participants,
-              remainingDays: challenge.remainingDays!,
-            ),
-          ],
-        ),
-      ),
-    );
-  }
-}
+                itemBuilder: (_, index) => ChallengeCard(challenge: challenges[index]),
+                
+                // itemBuilder: (context, index) => ListTile(
+                //   title: Text(challenges[index].title),
+                //   // 其他列表项内容
+                // ),
 
-// 将复杂子组件拆分为独立组件
-class _DifficultyChip extends StatelessWidget {
-  final String difficulty;
 
-  const _DifficultyChip({required this.difficulty});
-
-  Color _getColor() {
-    switch (difficulty) {
-      case '简单':
-        return Colors.green;
-      case '中等':
-        return Colors.orange;
-      case '困难':
-        return Colors.red;
-      default:
-        return Colors.grey;
-    }
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    return Chip(
-      label: Text(difficulty, style: const TextStyle(color: Colors.white)),
-      backgroundColor: _getColor(),
-    );
-  }
-}
-
-class _ProgressBar extends StatelessWidget {
-  final DateTime startDate;
-  final DateTime endDate;
-  final int remainingDays;
-
-  const _ProgressBar({
-    required this.startDate,
-    required this.endDate,
-    required this.remainingDays,
-  });
-
-  @override
-  Widget build(BuildContext context) {
-    final theme = Theme.of(context);
-    final totalDays = endDate.difference(startDate).inDays;
-    final passedDays = totalDays - remainingDays;
-    final progress = passedDays / totalDays;
-
-    return Column(
-      crossAxisAlignment: CrossAxisAlignment.start,
-      children: [
-        Text(
-          '${(progress * 100).toStringAsFixed(0)}%',
-          style: const TextStyle(fontSize: 12),
-        ),
-        const SizedBox(height: 4),
-        LinearProgressIndicator(
-          value: progress,
-          backgroundColor: Colors.grey[200],
-          borderRadius: BorderRadius.circular(10),
-          minHeight: 8,
-          color: theme.primaryColor,
-        ),
-        const SizedBox(height: 4),
-        Row(
-          mainAxisAlignment: MainAxisAlignment.spaceBetween,
-          children: [
-            Text(
-              DateFormat('MM/dd').format(startDate),
-              style: const TextStyle(fontSize: 12),
-            ),
-            Text(
-              DateFormat('MM/dd').format(endDate),
-              style: const TextStyle(fontSize: 12),
+              ),
             ),
-          ],
-        ),
-      ],
-    );
-  }
-}
-
-class _CardFooter extends StatelessWidget {
-  final int participants;
-  final int remainingDays;
-
-  const _CardFooter({required this.participants, required this.remainingDays});
-
-  @override
-  Widget build(BuildContext context) {
-    return Row(
-      mainAxisAlignment: MainAxisAlignment.spaceBetween,
-      children: [
-        _InfoChip(icon: Icons.people, text: '$participants人参加'),
-        _InfoChip(icon: Icons.calendar_today, text: '剩余${remainingDays}天'),
-      ],
-    );
-  }
-}
-
-class _InfoChip extends StatelessWidget {
-  final IconData icon;
-  final String text;
-
-  const _InfoChip({required this.icon, required this.text});
-
-  @override
-  Widget build(BuildContext context) {
-    return Container(
-      padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
-      decoration: BoxDecoration(
-        color: Colors.grey[100],
-        borderRadius: BorderRadius.circular(20),
-      ),
-      child: Row(
-        children: [
-          Icon(icon, size: 16, color: Colors.grey[600]),
-          const SizedBox(width: 4),
-          Text(text, style: TextStyle(color: Colors.grey[700])),
-        ],
-      ),
     );
   }
 }

+ 182 - 0
lib/features/challenge/views/widgets/challenge_card.dart

@@ -0,0 +1,182 @@
+import 'package:flutter/material.dart';
+import 'package:intl/intl.dart';
+import 'package:japp_flutter/core/models/challenge_model.dart';
+
+
+class ChallengeCard extends StatelessWidget {
+  final ChallengeModel challenge;
+
+  const ChallengeCard({super.key, required this.challenge});
+
+  @override
+  Widget build(BuildContext context) {
+    final theme = Theme.of(context);
+
+    return Card(
+      elevation: 2,
+      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
+      margin: const EdgeInsets.only(bottom: 16),
+      child: Padding(
+        padding: const EdgeInsets.all(16),
+        child: Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            Row(
+              children: [
+                Expanded(
+                  child: Text(
+                    challenge.title,
+                    style: const TextStyle(
+                      fontSize: 18,
+                      fontWeight: FontWeight.bold,
+                    ),
+                  ),
+                ),
+                _DifficultyChip(difficulty: challenge.difficulty),
+              ],
+            ),
+            const SizedBox(height: 8),
+            Text(
+              challenge.description,
+              style: TextStyle(color: Colors.grey[700], fontSize: 14),
+            ),
+            const SizedBox(height: 16),
+            _ProgressBar(
+              startDate: challenge.startDate,
+              endDate: challenge.endDate,
+              remainingDays: challenge.remainingDays?? 0,
+            ),
+            const SizedBox(height: 12),
+            _CardFooter(
+              participants: challenge.participants,
+              remainingDays: challenge.remainingDays ?? 0,
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+}
+
+// 将复杂子组件拆分为独立组件
+class _DifficultyChip extends StatelessWidget {
+  final String difficulty;
+
+  const _DifficultyChip({required this.difficulty});
+
+  Color _getColor() {
+    switch (difficulty) {
+      case '简单':
+        return Colors.green;
+      case '中等':
+        return Colors.orange;
+      case '困难':
+        return Colors.red;
+      default:
+        return Colors.grey;
+    }
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Chip(
+      label: Text(difficulty, style: const TextStyle(color: Colors.white)),
+      backgroundColor: _getColor(),
+    );
+  }
+}
+
+class _ProgressBar extends StatelessWidget {
+  final DateTime startDate;
+  final DateTime endDate;
+  final int remainingDays;
+
+  const _ProgressBar({
+    required this.startDate,
+    required this.endDate,
+    required this.remainingDays,
+  });
+
+  @override
+  Widget build(BuildContext context) {
+    final theme = Theme.of(context);
+    final totalDays = endDate.difference(startDate).inDays;
+    final passedDays = totalDays - remainingDays;
+    final progress = passedDays / totalDays;
+
+    return Column(
+      crossAxisAlignment: CrossAxisAlignment.start,
+      children: [
+        Text(
+          '${(progress * 100).toStringAsFixed(0)}%',
+          style: const TextStyle(fontSize: 12),
+        ),
+        const SizedBox(height: 4),
+        LinearProgressIndicator(
+          value: progress,
+          backgroundColor: Colors.grey[200],
+          borderRadius: BorderRadius.circular(10),
+          minHeight: 8,
+          color: theme.primaryColor,
+        ),
+        const SizedBox(height: 4),
+        Row(
+          mainAxisAlignment: MainAxisAlignment.spaceBetween,
+          children: [
+            Text(
+              DateFormat('MM/dd').format(startDate),
+              style: const TextStyle(fontSize: 12),
+            ),
+            Text(
+              DateFormat('MM/dd').format(endDate),
+              style: const TextStyle(fontSize: 12),
+            ),
+          ],
+        ),
+      ],
+    );
+  }
+}
+
+class _CardFooter extends StatelessWidget {
+  final int participants;
+  final int remainingDays;
+
+  const _CardFooter({required this.participants, required this.remainingDays});
+
+  @override
+  Widget build(BuildContext context) {
+    return Row(
+      mainAxisAlignment: MainAxisAlignment.spaceBetween,
+      children: [
+        _InfoChip(icon: Icons.people, text: '$participants人参加'),
+        _InfoChip(icon: Icons.calendar_today, text: '剩余${remainingDays}天'),
+      ],
+    );
+  }
+}
+
+class _InfoChip extends StatelessWidget {
+  final IconData icon;
+  final String text;
+
+  const _InfoChip({required this.icon, required this.text});
+
+  @override
+  Widget build(BuildContext context) {
+    return Container(
+      padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
+      decoration: BoxDecoration(
+        color: Colors.grey[100],
+        borderRadius: BorderRadius.circular(20),
+      ),
+      child: Row(
+        children: [
+          Icon(icon, size: 16, color: Colors.grey[600]),
+          const SizedBox(width: 4),
+          Text(text, style: TextStyle(color: Colors.grey[700])),
+        ],
+      ),
+    );
+  }
+}