Files
gp/lib/src/features/shell/focus_seed_home.dart
T

189 lines
6.2 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../../application/focus_seed_controller.dart';
import '../../application/focus_timer_controller.dart';
import '../../data/focus_seed_store.dart';
import '../../l10n/l10n.dart';
import '../../models/appearance_preference.dart';
import '../../models/language_preference.dart';
import '../../widgets/loading_view.dart';
import '../journey/journey_screen.dart';
import '../me/me_screen.dart';
import '../today/today_screen.dart';
class FocusSeedHome extends StatefulWidget {
const FocusSeedHome({
required this.appearance,
required this.onAppearanceChanged,
required this.language,
required this.showLanguageTools,
required this.onLanguageChanged,
this.focusSeedStore,
super.key,
});
final AppearancePreference appearance;
final ValueChanged<AppearancePreference> onAppearanceChanged;
final LanguagePreference language;
final bool showLanguageTools;
final ValueChanged<LanguagePreference> onLanguageChanged;
final FocusSeedStore? focusSeedStore;
@override
State<FocusSeedHome> createState() => _FocusSeedHomeState();
}
class _FocusSeedHomeState extends State<FocusSeedHome> {
FocusSeedController? _controller;
FocusTimerController? _timerController;
int _tabIndex = 0;
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (_controller != null) {
return;
}
_controller = FocusSeedController(
store: widget.focusSeedStore,
defaultHabitTitles: localizedStarterHabits(context.l10n),
);
_timerController = FocusTimerController(
onCompleted: (minutes) async {
final saved = await _controller!.completeFocusSession(minutes);
if (saved) {
await HapticFeedback.mediumImpact();
}
},
);
_controller!.load();
}
@override
Widget build(BuildContext context) {
final controller = _controller;
final timerController = _timerController;
if (controller == null || timerController == null) {
return const Scaffold(body: SafeArea(child: LoadingView()));
}
return AnimatedBuilder(
animation: controller,
builder: (context, _) {
controller.updateDefaultHabitTitles(
localizedStarterHabits(context.l10n),
);
final state = controller.state;
if (controller.isLoading) {
return const Scaffold(body: SafeArea(child: LoadingView()));
}
if (state == null) {
final l10n = context.l10n;
return Scaffold(
body: SafeArea(
child: Center(
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.error_outline_rounded, size: 42),
const SizedBox(height: 12),
Text(
_messageForError(l10n, controller.error) ??
l10n.couldNotOpenApp,
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
FilledButton.icon(
onPressed: controller.load,
icon: const Icon(Icons.refresh_rounded),
label: Text(l10n.tryAgain),
),
],
),
),
),
),
);
}
final theme = Theme.of(context);
final isDark = theme.brightness == Brightness.dark;
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness:
isDark ? Brightness.light : Brightness.dark,
systemNavigationBarColor:
theme.navigationBarTheme.backgroundColor ??
theme.colorScheme.surface,
systemNavigationBarIconBrightness:
isDark ? Brightness.light : Brightness.dark,
),
child: Scaffold(
body: SafeArea(
child: IndexedStack(
index: _tabIndex,
children: [
TodayScreen(
controller: controller,
timerController: timerController,
),
JourneyScreen(controller: controller),
MeScreen(
controller: controller,
appearance: widget.appearance,
onAppearanceChanged: widget.onAppearanceChanged,
language: widget.language,
showLanguageTools: widget.showLanguageTools,
onLanguageChanged: widget.onLanguageChanged,
),
],
),
),
bottomNavigationBar: NavigationBar(
selectedIndex: _tabIndex,
onDestinationSelected: (index) {
HapticFeedback.selectionClick();
setState(() => _tabIndex = index);
},
destinations: [
NavigationDestination(
icon: const Icon(Icons.eco_outlined),
selectedIcon: const Icon(Icons.eco_rounded),
label: context.l10n.navToday,
),
NavigationDestination(
icon: const Icon(Icons.landscape_outlined),
selectedIcon: const Icon(Icons.landscape_rounded),
label: context.l10n.navJourney,
),
NavigationDestination(
icon: const Icon(Icons.person_outline_rounded),
selectedIcon: const Icon(Icons.person_rounded),
label: context.l10n.navMe,
),
],
),
),
);
},
);
}
@override
void dispose() {
_timerController?.dispose();
_controller?.dispose();
super.dispose();
}
}
String? _messageForError(AppLocalizations l10n, FocusSeedError? error) {
return switch (error) {
FocusSeedError.openFailed => l10n.openFailed,
FocusSeedError.saveFailed => l10n.saveFailed,
null => null,
};
}