Files
rely/lib/features/dashboard/dashboard_view.dart
T
2026-02-15 15:12:23 +01:00

231 lines
7.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:relationship_saver/core/config/app_theme.dart';
import 'package:relationship_saver/features/local/local_models.dart';
import 'package:relationship_saver/features/local/local_repository.dart';
import 'package:relationship_saver/features/shared/frosted_card.dart';
class DashboardView extends ConsumerWidget {
const DashboardView({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final AsyncValue<LocalDataState> localData = ref.watch(
localRepositoryProvider,
);
return localData.when(
data: (LocalDataState data) {
final DashboardSummary summary = data.summary();
final List<DashboardTask> tasks = data.tasks;
return SingleChildScrollView(
padding: const EdgeInsets.fromLTRB(28, 24, 28, 36),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Today', style: Theme.of(context).textTheme.headlineMedium),
const SizedBox(height: 8),
Text(
'Focus on consistency over intensity. Small moments compound.',
style: Theme.of(
context,
).textTheme.bodyLarge?.copyWith(color: AppTheme.textSecondary),
),
const SizedBox(height: 22),
Wrap(
spacing: 16,
runSpacing: 16,
children: <Widget>[
_StatCard(
label: 'Active People',
value: '${summary.activePeople}',
accent: const Color(0xFF16B8CA),
),
_StatCard(
label: 'Upcoming Plans',
value: '${summary.upcomingPlans}',
accent: const Color(0xFF2B7FFF),
),
_StatCard(
label: 'Pending Ideas',
value: '${summary.pendingIdeas}',
accent: const Color(0xFFFFA447),
),
_StatCard(
label: 'Weekly Rhythm',
value: '${summary.weeklyConsistency}%',
accent: const Color(0xFF30B66A),
),
],
),
const SizedBox(height: 24),
FrostedCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Next Moves',
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 14),
...tasks.map(
(DashboardTask task) => _TaskRow(
task: task,
onToggleDone: () {
ref
.read(localRepositoryProvider.notifier)
.toggleTaskDone(task.id);
},
),
),
],
),
),
],
),
);
},
loading: () => const Center(child: CircularProgressIndicator()),
error: (Object error, StackTrace stackTrace) {
return Center(
child: Text(
'Could not load local data.',
style: Theme.of(context).textTheme.bodyLarge,
),
);
},
);
}
}
class _StatCard extends StatelessWidget {
const _StatCard({
required this.label,
required this.value,
required this.accent,
});
final String label;
final String value;
final Color accent;
@override
Widget build(BuildContext context) {
return FrostedCard(
padding: const EdgeInsets.all(16),
child: SizedBox(
width: 188,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
height: 8,
width: 40,
decoration: BoxDecoration(
color: accent,
borderRadius: BorderRadius.circular(99),
),
),
const SizedBox(height: 16),
Text(
value,
style: Theme.of(
context,
).textTheme.headlineMedium?.copyWith(fontWeight: FontWeight.w700),
),
const SizedBox(height: 4),
Text(
label,
style: Theme.of(
context,
).textTheme.bodyMedium?.copyWith(color: AppTheme.textSecondary),
),
],
),
),
);
}
}
class _TaskRow extends StatelessWidget {
const _TaskRow({required this.task, required this.onToggleDone});
final DashboardTask task;
final VoidCallback onToggleDone;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: const Color(0xFFF5FAFB),
borderRadius: BorderRadius.circular(16),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
InkWell(
onTap: onToggleDone,
borderRadius: BorderRadius.circular(20),
child: Icon(
task.done
? Icons.check_circle_rounded
: Icons.radio_button_unchecked_rounded,
color: task.done
? const Color(0xFF1D9C66)
: const Color(0xFF1AB6C8),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
task.title,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
decoration: task.done ? TextDecoration.lineThrough : null,
),
),
const SizedBox(height: 4),
Text(
task.description,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: AppTheme.textSecondary,
),
),
],
),
),
const SizedBox(width: 12),
Text(
_shortDate(task.when),
style: Theme.of(
context,
).textTheme.labelLarge?.copyWith(color: AppTheme.textSecondary),
),
],
),
),
);
}
}
String _shortDate(DateTime value) {
const List<String> weekdays = <String>[
'Mon',
'Tue',
'Wed',
'Thu',
'Fri',
'Sat',
'Sun',
];
final String weekday = weekdays[value.weekday - 1];
final String day = value.day.toString().padLeft(2, '0');
return '$weekday $day';
}