148 lines
4.5 KiB
Dart
148 lines
4.5 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import '../../app/app_theme.dart';
|
|
import '../../core/date_keys.dart';
|
|
import '../../models/app_state.dart';
|
|
import '../../models/habit.dart';
|
|
import '../../widgets/app_scaffold.dart';
|
|
import '../../widgets/dialogs.dart';
|
|
import '../../widgets/empty_state.dart';
|
|
import '../../widgets/habit_row.dart';
|
|
import '../../widgets/inline_editable_text.dart';
|
|
import '../../widgets/metric_tile.dart';
|
|
import '../../widgets/section_label.dart';
|
|
import '../../widgets/seed_card.dart';
|
|
import '../../widgets/weekly_progress.dart';
|
|
|
|
class HomeScreen extends StatelessWidget {
|
|
const HomeScreen({
|
|
required this.state,
|
|
required this.onAnchorChanged,
|
|
required this.onOpenTimer,
|
|
required this.onToggleHabit,
|
|
required this.onAddHabit,
|
|
super.key,
|
|
});
|
|
|
|
final AppState state;
|
|
final ValueChanged<String> onAnchorChanged;
|
|
final VoidCallback onOpenTimer;
|
|
final ValueChanged<Habit> onToggleHabit;
|
|
final ValueChanged<String> onAddHabit;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final today = todayKey();
|
|
final completed =
|
|
state.habits
|
|
.where((habit) => habit.completedDates.contains(today))
|
|
.length;
|
|
final focusMinutes = minutesForDate(state.sessions, today);
|
|
final streak = currentStreak(
|
|
state.habits,
|
|
state.sessions,
|
|
state.reflections,
|
|
);
|
|
|
|
return AppScaffold(
|
|
title: 'Focus Seed',
|
|
subtitle: readableToday(),
|
|
child: ListView(
|
|
padding: const EdgeInsets.fromLTRB(20, 8, 20, 24),
|
|
children: [
|
|
SeedCard(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
const SectionLabel(
|
|
icon: Icons.flag_outlined,
|
|
text: 'Today anchor',
|
|
),
|
|
const SizedBox(height: 12),
|
|
InlineEditableText(
|
|
value: state.anchor,
|
|
emptyText: 'Choose one important task',
|
|
onSubmitted: onAnchorChanged,
|
|
),
|
|
const SizedBox(height: 16),
|
|
FilledButton.icon(
|
|
onPressed: onOpenTimer,
|
|
icon: const Icon(Icons.play_arrow_rounded),
|
|
label: const Text('Start focus'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(height: 14),
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: MetricTile(
|
|
label: 'Focus',
|
|
value: '$focusMinutes',
|
|
suffix: 'min',
|
|
icon: Icons.timer_outlined,
|
|
),
|
|
),
|
|
const SizedBox(width: 10),
|
|
Expanded(
|
|
child: MetricTile(
|
|
label: 'Habits',
|
|
value: '$completed/${state.habits.length}',
|
|
suffix: '',
|
|
icon: Icons.check_circle_outline,
|
|
),
|
|
),
|
|
const SizedBox(width: 10),
|
|
Expanded(
|
|
child: MetricTile(
|
|
label: 'Streak',
|
|
value: '$streak',
|
|
suffix: 'days',
|
|
icon: Icons.local_fire_department_outlined,
|
|
accent: AppTheme.accent,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 18),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
'Today habits',
|
|
style: Theme.of(context).textTheme.titleMedium,
|
|
),
|
|
IconButton.filledTonal(
|
|
tooltip: 'Add habit',
|
|
onPressed: () => showHabitDialog(context, onAddHabit),
|
|
icon: const Icon(Icons.add),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 8),
|
|
if (state.habits.isEmpty)
|
|
EmptyState(
|
|
icon: Icons.checklist_rtl,
|
|
title: 'Add one tiny habit',
|
|
action: 'Add habit',
|
|
onAction: () => showHabitDialog(context, onAddHabit),
|
|
)
|
|
else
|
|
...state.habits
|
|
.take(5)
|
|
.map(
|
|
(habit) => HabitRow(
|
|
habit: habit,
|
|
dateKey: today,
|
|
onToggle: () => onToggleHabit(habit),
|
|
),
|
|
),
|
|
const SizedBox(height: 18),
|
|
WeeklyProgress(state: state),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|