Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
4fb0b88
feat 1.0.0: Implement core financial tracking features for accounts, …
HellBus1 Feb 20, 2026
5c9284b
feat account-management-1.0.0: Implement account management features …
HellBus1 Feb 20, 2026
c71907a
feat account-management-1.0.0: conditionally display FloatingActionBu…
HellBus1 Feb 20, 2026
cb51a6e
feat: Add CI workflow and refactor for lint compliance
HellBus1 Feb 21, 2026
aee031c
Merge pull request #1 from HellBus1/account-management-1.0.0
HellBus1 Feb 21, 2026
f1ab5e9
feat account-management-1.0.0: Implement comprehensive transaction an…
HellBus1 Feb 21, 2026
abcac25
Merge pull request #2 from HellBus1/transaction-logging-1.0.0
HellBus1 Feb 21, 2026
536ea33
feat smart-settlement-1.0.0: Add credit account bill payment function…
HellBus1 Feb 21, 2026
e63fe2e
Merge pull request #3 from HellBus1/smart-settlement-1.0.0
HellBus1 Feb 21, 2026
461dc0b
feat google-drive-sync-1.0.0: Implement Google Drive backup and resto…
HellBus1 Feb 21, 2026
059e770
Merge pull request #4 from HellBus1/google-drive-sync-1.0.0
HellBus1 Feb 21, 2026
0f595de
feat bank-ready-reports-1.0.0: enable PDF and Excel report generation…
HellBus1 Feb 21, 2026
64def36
Merge pull request #5 from HellBus1/bank-ready-reports-1.0.0
HellBus1 Feb 21, 2026
ac46ad4
feat dashboard-1.0.0: Implement quick spending stats strip on the led…
HellBus1 Feb 21, 2026
4ae2a49
Merge pull request #6 from HellBus1/dashboard-1.0.0
HellBus1 Feb 21, 2026
fd32417
refactor code-lint-enhancement-1.0.0: Add `context.mounted` checks fo…
HellBus1 Feb 21, 2026
286a025
Merge pull request #7 from HellBus1/code-lint-enhancement-1.0.0
HellBus1 Feb 21, 2026
4fb4e14
feat: Lower minimum Dart SDK version requirement and add analysis out…
HellBus1 Feb 21, 2026
4ea978e
chore 1.0.0: Upgrade Dart SDK to 3.11.0, remove explicit Flutter vers…
HellBus1 Feb 21, 2026
234547a
refactor 1.0.0: Update github workflows
HellBus1 Feb 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: VentExpensePro CI

on:
pull_request:
branches:
- master
- staging

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
channel: 'stable'

- name: Install dependencies
run: flutter pub get

- name: Analyze project source
run: flutter analyze

- name: Run tests
run: flutter test
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,6 @@ app.*.map.json
/android/app/release

*.env
*.freezed.dart
*.freezed.dart

google-services.json
44 changes: 21 additions & 23 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.

# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml

linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
prefer_single_quotes: true
always_declare_return_types: true
annotate_overrides: true
avoid_empty_else: true
avoid_print: true
avoid_relative_lib_imports: true
avoid_unnecessary_containers: true
prefer_const_constructors: true
prefer_const_declarations: true
prefer_final_fields: true
prefer_final_locals: true
sort_child_properties_last: true
unnecessary_brace_in_string_interps: true
use_key_in_widget_constructors: true

# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
analyzer:
errors:
missing_return: error
dead_code: warning
exclude:
- "**/*.g.dart"
- "**/*.freezed.dart"
Binary file added assets/fonts/JetBrainsMono-Bold.ttf
Binary file not shown.
Binary file added assets/fonts/JetBrainsMono-Regular.ttf
Binary file not shown.
Binary file added assets/fonts/Lora-Bold.ttf
Binary file not shown.
Binary file added assets/fonts/Lora-Italic.ttf
Binary file not shown.
Binary file added assets/fonts/Lora-Regular.ttf
Binary file not shown.
78 changes: 78 additions & 0 deletions lib/core/di/service_locator.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import 'package:get_it/get_it.dart';

import '../../data/datasources/google_drive_service.dart';
import '../../data/repositories/account_repository_impl.dart';
import '../../data/repositories/category_repository_impl.dart';
import '../../data/repositories/sync_repository_impl.dart';
import '../../data/repositories/transaction_repository_impl.dart';
import '../../domain/repositories/account_repository.dart';
import '../../domain/repositories/category_repository.dart';
import '../../domain/repositories/sync_repository.dart';
import '../../domain/repositories/transaction_repository.dart';
import '../../domain/usecases/calculate_net_position.dart';
import '../../domain/usecases/log_transaction.dart';
import '../../domain/usecases/manage_account.dart';
import '../../domain/usecases/manage_transaction.dart';
import '../../domain/usecases/settle_credit_bill.dart';
import '../../domain/usecases/sync_data.dart';
import '../../domain/usecases/generate_report.dart';
import '../../domain/repositories/report_repository.dart';
import '../../data/repositories/report_repository_impl.dart';
import '../../data/datasources/pdf_report_service.dart';
import '../../data/datasources/excel_report_service.dart';

/// Global service locator instance.
final sl = GetIt.instance;

/// Registers all dependencies. Call once at app startup.
Future<void> initServiceLocator() async {
// — Repositories —
sl.registerLazySingleton<AccountRepository>(() => AccountRepositoryImpl());
sl.registerLazySingleton<TransactionRepository>(
() => TransactionRepositoryImpl(),
);
sl.registerLazySingleton<CategoryRepository>(() => CategoryRepositoryImpl());

// — Google Drive Sync —
sl.registerLazySingleton(() => GoogleDriveService());
sl.registerLazySingleton<SyncRepository>(
() => SyncRepositoryImpl(sl<GoogleDriveService>()),
);

// — Reports —
sl.registerLazySingleton(() => PdfReportService());
sl.registerLazySingleton(() => ExcelReportService());
sl.registerLazySingleton<ReportRepository>(
() => ReportRepositoryImpl(
pdfService: sl<PdfReportService>(),
excelService: sl<ExcelReportService>(),
),
);

// — Use Cases —
sl.registerFactory(() => CalculateNetPosition(sl<AccountRepository>()));
sl.registerFactory(
() => LogTransaction(sl<TransactionRepository>(), sl<AccountRepository>()),
);
sl.registerFactory(
() =>
SettleCreditBill(sl<TransactionRepository>(), sl<AccountRepository>()),
);
sl.registerFactory(() => ManageAccount(sl<AccountRepository>()));
sl.registerFactory(
() => ManageTransaction(
sl<TransactionRepository>(),
sl<AccountRepository>(),
sl<LogTransaction>(),
),
);
sl.registerFactory(() => SyncData(sl<SyncRepository>()));
sl.registerFactory(
() => GenerateReport(
reportRepository: sl<ReportRepository>(),
transactionRepository: sl<TransactionRepository>(),
accountRepository: sl<AccountRepository>(),
categoryRepository: sl<CategoryRepository>(),
),
);
}
49 changes: 49 additions & 0 deletions lib/core/theme/app_colors.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import 'package:flutter/material.dart';

/// The Stationery color palette — ink on cream paper.
class AppColors {
AppColors._();

// — Backgrounds —
/// Cream paper background.
static const Color paper = Color(0xFFFFF8F0);

/// Slightly darker cream for cards / elevated surfaces.
static const Color paperElevated = Color(0xFFF5EDE0);

/// Warm grey for subtle separators.
static const Color divider = Color(0xFFD6CFC4);

// — Ink / Text —
/// Deep ink blue — primary brand color.
static const Color inkBlue = Color(0xFF1B3A5C);

/// Dark charcoal for body text.
static const Color inkDark = Color(0xFF2C2C2C);

/// Muted grey for secondary text.
static const Color inkLight = Color(0xFF7A7570);

// — Accents —
/// Stamp red — used for debts, liabilities, expenses.
static const Color stampRed = Color(0xFFC0392B);

/// Faded stamp red for backgrounds.
static const Color stampRedLight = Color(0xFFFDECEA);

/// Green ink — used for income, assets, positive values.
static const Color inkGreen = Color(0xFF27774E);

/// Faded green for backgrounds.
static const Color inkGreenLight = Color(0xFFE8F5EE);

// — Functional —
/// Settlement / transfer accent.
static const Color transferAmber = Color(0xFFD4A017);

/// Error / invalid state.
static const Color error = Color(0xFFB71C1C);

/// Disabled / inactive elements.
static const Color disabled = Color(0xFFBDB5AA);
}
106 changes: 106 additions & 0 deletions lib/core/theme/app_theme.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import 'package:flutter/material.dart';

import 'app_colors.dart';
import 'app_typography.dart';

/// Builds the full [ThemeData] for the Stationery aesthetic.
class AppTheme {
AppTheme._();

static ThemeData get light {
return ThemeData(
useMaterial3: true,
brightness: Brightness.light,

// — Colors —
scaffoldBackgroundColor: AppColors.paper,
colorScheme: const ColorScheme.light(
primary: AppColors.inkBlue,
onPrimary: AppColors.paper,
secondary: AppColors.stampRed,
onSecondary: Colors.white,
surface: AppColors.paperElevated,
onSurface: AppColors.inkDark,
error: AppColors.error,
onError: Colors.white,
),

// — App Bar —
appBarTheme: const AppBarTheme(
backgroundColor: AppColors.paper,
foregroundColor: AppColors.inkBlue,
elevation: 0,
centerTitle: true,
titleTextStyle: AppTypography.titleLarge,
),

// — Bottom Navigation —
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
backgroundColor: AppColors.paperElevated,
selectedItemColor: AppColors.inkBlue,
unselectedItemColor: AppColors.disabled,
type: BottomNavigationBarType.fixed,
elevation: 8,
selectedLabelStyle: AppTypography.label,
unselectedLabelStyle: AppTypography.label,
),

// — Cards —
cardTheme: CardThemeData(
color: AppColors.paperElevated,
elevation: 1,
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: const BorderSide(color: AppColors.divider, width: 0.5),
),
),

// — FAB —
floatingActionButtonTheme: const FloatingActionButtonThemeData(
backgroundColor: AppColors.inkBlue,
foregroundColor: AppColors.paper,
elevation: 4,
),

// — Text —
textTheme: const TextTheme(
displayLarge: AppTypography.displayLarge,
displayMedium: AppTypography.displayMedium,
titleLarge: AppTypography.titleLarge,
titleMedium: AppTypography.titleMedium,
bodyLarge: AppTypography.bodyLarge,
bodyMedium: AppTypography.bodyMedium,
bodySmall: AppTypography.bodySmall,
labelSmall: AppTypography.label,
),

// — Divider —
dividerTheme: const DividerThemeData(
color: AppColors.divider,
thickness: 0.5,
space: 1,
),

// — Input —
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: AppColors.paper,
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 12,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(color: AppColors.divider),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(color: AppColors.inkBlue, width: 1.5),
),
labelStyle: AppTypography.bodyMedium,
hintStyle: AppTypography.bodyMedium.copyWith(color: AppColors.disabled),
),
);
}
}
Loading