| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- // 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';
- class ChallengeListScreen extends ConsumerWidget {
- const ChallengeListScreen({super.key});
- @override
- Widget build(BuildContext context, WidgetRef ref) {
- final challengesState = ref.watch(challengeListProvider);
- return Scaffold(
- appBar: AppBar(
- title: const Text('挑战列表'),
- actions: [
- IconButton(
- icon: const Icon(Icons.refresh),
- onPressed: () => ref.read(challengeListProvider.notifier).refresh(),
- ),
- ],
- ),
- body: _buildBody(challengesState, ref),
- floatingActionButton: FloatingActionButton(
- onPressed: () => Navigator.pushNamed(context, '/challenge/add'),
- child: const Icon(Icons.add),
- ),
- );
- }
- Widget _buildBody(AsyncValue<List<ChallengeModel>> state, WidgetRef ref) {
- return state.when(
- loading: () => const Center(child: CircularProgressIndicator()),
- error: (error, stack) => Center(
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- const Icon(Icons.error, color: Colors.red, size: 48),
- const SizedBox(height: 16),
- Text('加载失败: $error', style: const TextStyle(color: Colors.red)),
- const SizedBox(height: 16),
- ElevatedButton(
- onPressed: () =>
- ref.read(challengeListProvider.notifier).refresh(),
- child: const Text('重试'),
- ),
- ],
- ),
- ),
- data: (challenges) => challenges.isEmpty
- ? const Center(child: Text('暂无挑战', style: TextStyle(fontSize: 18)))
- : RefreshIndicator(
- onRefresh: () =>
- ref.read(challengeListProvider.notifier).refresh(),
- 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!,
- ),
- ],
- ),
- ),
- );
- }
- }
- // 将复杂子组件拆分为独立组件
- 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])),
- ],
- ),
- );
- }
- }
|