Added core data models, repositories, and utilities

This commit is contained in:
Tomas Dvorak
2026-01-03 18:37:48 +01:00
parent 1639de69d4
commit 572fbb971c
17 changed files with 1248 additions and 0 deletions
@@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
class AppScaffold extends StatelessWidget {
final String? title;
final Widget body;
final List<Widget>? actions;
final Widget? floatingActionButton;
final bool showBackButton;
final VoidCallback? onBackPressed;
final Widget? bottomNavigationBar;
const AppScaffold({
super.key,
this.title,
required this.body,
this.actions,
this.floatingActionButton,
this.showBackButton = false,
this.onBackPressed,
this.bottomNavigationBar,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: title != null
? AppBar(
title: Text(title!),
centerTitle: true,
automaticallyImplyLeading: showBackButton,
leading: showBackButton
? IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: onBackPressed ?? () => Navigator.pop(context),
)
: null,
actions: actions,
)
: null,
body: SafeArea(child: body),
floatingActionButton: floatingActionButton,
bottomNavigationBar: bottomNavigationBar,
);
}
}
@@ -0,0 +1,74 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class BottomNavScaffold extends StatefulWidget {
final Widget child;
const BottomNavScaffold({
super.key,
required this.child,
});
@override
State<BottomNavScaffold> createState() => _BottomNavScaffoldState();
}
class _BottomNavScaffoldState extends State<BottomNavScaffold> {
int _currentIndex = 0;
final List<NavigationDestination> _destinations = const [
NavigationDestination(
icon: Icon(Icons.home_outlined),
selectedIcon: Icon(Icons.home),
label: 'Home',
),
NavigationDestination(
icon: Icon(Icons.flag_outlined),
selectedIcon: Icon(Icons.flag),
label: 'Goals',
),
NavigationDestination(
icon: Icon(Icons.people_outline),
selectedIcon: Icon(Icons.people),
label: 'Social',
),
NavigationDestination(
icon: Icon(Icons.person_outline),
selectedIcon: Icon(Icons.person),
label: 'Profile',
),
];
void _onDestinationSelected(int index) {
setState(() {
_currentIndex = index;
});
switch (index) {
case 0:
context.go('/home');
break;
case 1:
context.go('/goals');
break;
case 2:
context.go('/social');
break;
case 3:
context.go('/profile');
break;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: widget.child,
bottomNavigationBar: NavigationBar(
selectedIndex: _currentIndex,
onDestinationSelected: _onDestinationSelected,
destinations: _destinations,
),
);
}
}
@@ -0,0 +1,58 @@
import 'package:flutter/material.dart';
class EmptyState extends StatelessWidget {
final IconData icon;
final String title;
final String? subtitle;
final String? actionLabel;
final VoidCallback? onAction;
const EmptyState({
super.key,
required this.icon,
required this.title,
this.subtitle,
this.actionLabel,
this.onAction,
});
@override
Widget build(BuildContext context) {
return Center(
child: Padding(
padding: const EdgeInsets.all(32.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
icon,
size: 80,
color: Theme.of(context).colorScheme.primary.withOpacity(0.5),
),
const SizedBox(height: 24),
Text(
title,
style: Theme.of(context).textTheme.headlineMedium,
textAlign: TextAlign.center,
),
if (subtitle != null) ...[
const SizedBox(height: 8),
Text(
subtitle!,
style: Theme.of(context).textTheme.bodyMedium,
textAlign: TextAlign.center,
),
],
if (actionLabel != null && onAction != null) ...[
const SizedBox(height: 24),
ElevatedButton(
onPressed: onAction,
child: Text(actionLabel!),
),
],
],
),
),
);
}
}
@@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
class LoadingIndicator extends StatelessWidget {
final String? message;
const LoadingIndicator({
super.key,
this.message,
});
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const CircularProgressIndicator(),
if (message != null) ...[
const SizedBox(height: 16),
Text(
message!,
style: Theme.of(context).textTheme.bodyMedium,
),
],
],
),
);
}
}