This commit is contained in:
2026-03-20 01:08:46 +03:00
commit 3dcbb6164e
142 changed files with 6599 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
import 'package:flutter/material.dart';
import 'router.dart';
import 'theme.dart';
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'Finance Tracker',
debugShowCheckedModeBanner: false,
theme: buildAppTheme(),
routerConfig: appRouter,
);
}
}
+79
View File
@@ -0,0 +1,79 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import '../features/dashboard/screen.dart';
import '../features/add_transaction/screen.dart';
import '../features/categories/screen.dart';
final _shellKey = GlobalKey<NavigatorState>();
final appRouter = GoRouter(
initialLocation: '/dashboard',
routes: [
ShellRoute(
navigatorKey: _shellKey,
builder: (context, state, child) => AppShell(child: child),
routes: [
GoRoute(
path: '/dashboard',
pageBuilder: (context, state) => const NoTransitionPage(
child: DashboardScreen(),
),
),
GoRoute(
path: '/categories',
pageBuilder: (context, state) => const NoTransitionPage(
child: CategoriesScreen(),
),
),
],
),
GoRoute(
path: '/add',
builder: (context, state) => const AddTransactionScreen(),
),
],
);
class AppShell extends StatelessWidget {
final Widget child;
const AppShell({super.key, required this.child});
int _locationToIndex(BuildContext context) {
final location = GoRouterState.of(context).uri.toString();
if (location.startsWith('/categories')) return 1;
return 0;
}
@override
Widget build(BuildContext context) {
final idx = _locationToIndex(context);
return Scaffold(
body: child,
floatingActionButton: FloatingActionButton(
onPressed: () => context.push('/add'),
backgroundColor: const Color(0xFF7C6DED),
child: const Icon(Icons.add, color: Colors.white),
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: NavigationBar(
selectedIndex: idx,
onDestinationSelected: (i) {
if (i == 0) context.go('/dashboard');
if (i == 1) context.go('/categories');
},
destinations: const [
NavigationDestination(
icon: Icon(Icons.dashboard_outlined),
selectedIcon: Icon(Icons.dashboard_rounded),
label: 'Dashboard',
),
NavigationDestination(
icon: Icon(Icons.pie_chart_outline_rounded),
selectedIcon: Icon(Icons.pie_chart_rounded),
label: 'Categories',
),
],
),
);
}
}
+101
View File
@@ -0,0 +1,101 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import '../core/constants.dart';
ThemeData buildAppTheme() {
final base = ThemeData.dark(useMaterial3: true);
final textTheme = GoogleFonts.poppinsTextTheme(base.textTheme).apply(
bodyColor: AppColors.textPrimary,
displayColor: AppColors.textPrimary,
);
return base.copyWith(
textTheme: textTheme,
scaffoldBackgroundColor: AppColors.background,
colorScheme: const ColorScheme.dark(
surface: AppColors.surface,
primary: AppColors.accent,
secondary: AppColors.accent,
onPrimary: Colors.white,
onSurface: AppColors.textPrimary,
),
cardTheme: CardThemeData(
color: AppColors.surface,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
margin: EdgeInsets.zero,
),
appBarTheme: AppBarTheme(
backgroundColor: AppColors.background,
elevation: 0,
centerTitle: false,
titleTextStyle: GoogleFonts.poppins(
color: AppColors.textPrimary,
fontSize: 20,
fontWeight: FontWeight.w600,
),
iconTheme: const IconThemeData(color: AppColors.textPrimary),
),
navigationBarTheme: NavigationBarThemeData(
backgroundColor: AppColors.surface,
indicatorColor: AppColors.accent.withOpacity(0.2),
labelTextStyle: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return GoogleFonts.poppins(
color: AppColors.accent,
fontSize: 12,
fontWeight: FontWeight.w600,
);
}
return GoogleFonts.poppins(
color: AppColors.textSecondary,
fontSize: 12,
);
}),
iconTheme: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return const IconThemeData(color: AppColors.accent);
}
return const IconThemeData(color: AppColors.textSecondary);
}),
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: AppColors.surface,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: AppColors.divider),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: AppColors.divider),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: AppColors.accent, width: 1.5),
),
labelStyle: const TextStyle(color: AppColors.textSecondary),
hintStyle: const TextStyle(color: AppColors.textSecondary),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.accent,
foregroundColor: Colors.white,
minimumSize: const Size(double.infinity, 52),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
),
textStyle: GoogleFonts.poppins(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
),
dividerTheme: const DividerThemeData(
color: AppColors.divider,
thickness: 1,
),
);
}