Files
focusflow/lib/features/settings/presentation/screens/settings_screen.dart
Oracle Public Cloud User 50931d839d Initial scaffold: FocusFlow ADHD Task Manager Flutter app
BLoC/Cubit state management, ADHD-friendly theme (calming teal, no red),
GetIt DI, GoRouter navigation. Screens: task dashboard, focus mode,
task create/detail, streaks, time perception, settings, onboarding, auth.
Custom widgets: TaskCard, RewardPopup, StreakRing, GentleNudgeCard.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 15:53:58 +00:00

197 lines
7.1 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import '../../../../core/theme/app_colors.dart';
import '../../../../features/auth/presentation/bloc/auth_bloc.dart';
/// Settings screen.
///
/// - Notification preferences.
/// - Daily task load slider (1-10).
/// - Focus session length.
/// - Reward style (playful / minimal / data).
/// - Forgiveness toggle.
/// - Theme (light / dark / system).
/// - Account management.
class SettingsScreen extends StatefulWidget {
const SettingsScreen({super.key});
@override
State<SettingsScreen> createState() => _SettingsScreenState();
}
class _SettingsScreenState extends State<SettingsScreen> {
// ── Local preference state ───────────────────────────────────────
bool _notificationsEnabled = true;
double _taskLoad = 5;
double _focusMinutes = 25;
String _rewardStyle = 'playful';
bool _forgivenessEnabled = true;
ThemeMode _themeMode = ThemeMode.system;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.arrow_back_rounded),
onPressed: () => context.go('/'),
),
title: const Text('Settings'),
),
body: ListView(
padding: const EdgeInsets.symmetric(vertical: 8),
children: [
// ── Notifications ────────────────────────────────────────
_SectionTitle('Notifications'),
SwitchListTile(
title: const Text('Enable notifications'),
subtitle: const Text('Gentle reminders, never nagging'),
value: _notificationsEnabled,
onChanged: (v) => setState(() => _notificationsEnabled = v),
),
const Divider(),
// ── Daily task load ──────────────────────────────────────
_SectionTitle('Daily Task Load'),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
Expanded(
child: Slider(
value: _taskLoad,
min: 1,
max: 10,
divisions: 9,
label: _taskLoad.round().toString(),
onChanged: (v) => setState(() => _taskLoad = v),
),
),
Text(
'${_taskLoad.round()} tasks',
style: theme.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w600),
),
],
),
),
const Divider(),
// ── Focus session length ─────────────────────────────────
_SectionTitle('Focus Session Length'),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
Expanded(
child: Slider(
value: _focusMinutes,
min: 5,
max: 60,
divisions: 11,
label: '${_focusMinutes.round()} min',
onChanged: (v) => setState(() => _focusMinutes = v),
),
),
Text(
'${_focusMinutes.round()} min',
style: theme.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w600),
),
],
),
),
const Divider(),
// ── Reward style ─────────────────────────────────────────
_SectionTitle('Reward Style'),
RadioListTile<String>(
title: const Text('Playful (animations & messages)'),
value: 'playful',
groupValue: _rewardStyle,
onChanged: (v) => setState(() => _rewardStyle = v!),
),
RadioListTile<String>(
title: const Text('Minimal (subtle feedback)'),
value: 'minimal',
groupValue: _rewardStyle,
onChanged: (v) => setState(() => _rewardStyle = v!),
),
RadioListTile<String>(
title: const Text('Data-driven (stats & charts)'),
value: 'data',
groupValue: _rewardStyle,
onChanged: (v) => setState(() => _rewardStyle = v!),
),
const Divider(),
// ── Forgiveness toggle ───────────────────────────────────
_SectionTitle('Forgiveness'),
SwitchListTile(
title: const Text('Enable grace days'),
subtitle: const Text('Missed a day? Grace days protect your streaks.'),
value: _forgivenessEnabled,
onChanged: (v) => setState(() => _forgivenessEnabled = v),
),
const Divider(),
// ── Theme ────────────────────────────────────────────────
_SectionTitle('Theme'),
RadioListTile<ThemeMode>(
title: const Text('System default'),
value: ThemeMode.system,
groupValue: _themeMode,
onChanged: (v) => setState(() => _themeMode = v!),
),
RadioListTile<ThemeMode>(
title: const Text('Light'),
value: ThemeMode.light,
groupValue: _themeMode,
onChanged: (v) => setState(() => _themeMode = v!),
),
RadioListTile<ThemeMode>(
title: const Text('Dark'),
value: ThemeMode.dark,
groupValue: _themeMode,
onChanged: (v) => setState(() => _themeMode = v!),
),
const Divider(),
// ── Account ──────────────────────────────────────────────
_SectionTitle('Account'),
ListTile(
leading: const Icon(Icons.logout_rounded, color: AppColors.error),
title: const Text('Sign out'),
onTap: () {
context.read<AuthBloc>().add(const AuthLogoutRequested());
context.go('/login');
},
),
const SizedBox(height: 40),
],
),
);
}
}
class _SectionTitle extends StatelessWidget {
final String title;
const _SectionTitle(this.title);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 4),
child: Text(
title,
style: Theme.of(context).textTheme.titleSmall?.copyWith(
color: AppColors.primary,
fontWeight: FontWeight.w700,
),
),
);
}
}