From 8fb785944c4213a9003f9a25002ed00335c28855 Mon Sep 17 00:00:00 2001 From: kolo Date: Sat, 13 Jun 2026 22:43:06 +0300 Subject: [PATCH] step --- AGENTS.md | 128 ++++++++ android/app/build.gradle.kts | 5 - android/gradle.properties | 4 + android/settings.gradle.kts | 1 - lib/core/services/biometric_service.dart | 4 - lib/features/add_transaction/provider.dart | 29 +- lib/features/add_transaction/screen.dart | 10 +- .../add_transaction/widgets/account_row.dart | 2 +- .../add_transaction/widgets/type_toggle.dart | 2 +- lib/features/categories/provider.dart | 6 +- lib/features/dashboard/provider.dart | 290 +++++++++++++----- lib/features/dashboard/screen.dart | 2 +- .../account_editor_overlay.dart | 2 +- .../account_editor_overlay/color_panel.dart | 2 +- .../account_editor_overlay/delete_dialog.dart | 2 +- .../dashboard/widgets/balance_card.dart | 15 +- .../widgets/balance_card_carousel.dart | 4 +- .../dashboard/widgets/filter_chips.dart | 24 +- .../dashboard/widgets/search_bar.dart | 4 +- .../dashboard/widgets/transaction_tile.dart | 2 +- lib/features/settings/provider.dart | 128 ++++---- .../providers/amount_format_provider.dart | 10 +- linux/flutter/generated_plugin_registrant.cc | 4 - linux/flutter/generated_plugins.cmake | 2 +- macos/Flutter/GeneratedPluginRegistrant.swift | 2 - pubspec.lock | 288 +++++++++++------ pubspec.yaml | 16 +- .../flutter/generated_plugin_registrant.cc | 3 - windows/flutter/generated_plugins.cmake | 2 +- 29 files changed, 671 insertions(+), 322 deletions(-) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..c0113c8 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,128 @@ +# AGENTS.md + +## Project Overview + +Personal finance Flutter app for Android. Tracks accounts, transactions, categories, budgets with multi-currency support, biometric auth, and haptic feedback. + +## Stack + +- **Flutter** — UI framework +- **Riverpod** — state management (per-feature `provider.dart` files) +- **Drift** — local database (code generation via `.g.dart`) +- **Feature-first architecture** + +## Project Structure + +``` +lib/ +├── app/ # App root, router, theme +├── core/ # Constants, l10n, core services, utils +├── data/ # Database schema, repositories +├── features/ # Feature modules (provider + screen + widgets) +├── shared/ # Cross-feature models, providers, services, widgets +└── main.dart +``` + +### Layer Responsibilities + +**`core/`** — app-wide infrastructure. No business logic. +- `constants.dart` — global constants +- `l10n/` — localization strings and locale provider +- `services/` — biometric, haptic, card color services +- `utils/result.dart` — `Result` type for error handling + +**`data/`** — persistence only. No UI, no Riverpod providers. +- `database/` — Drift tables and generated code. Do not edit `.g.dart` files manually. +- `repositories/` — `AccountRepository`, `TransactionRepository`. All DB access goes through repositories. + +**`features//`** — self-contained feature modules. +- `provider.dart` — Riverpod providers scoped to this feature +- `screen.dart` — top-level screen widget, minimal logic +- `widgets/` — feature-specific widgets + +**`shared/`** — reusable across features. +- `models/` — `Account`, `Transaction` data classes +- `providers/` — providers used by multiple features +- `services/` — `ExchangeRateService`, `StorageService` +- `utils/` — `CurrencyUtils` +- `widgets/` — `BynSign`, `ErrorSnackbar` + +## Architecture Rules + +- Widgets never access repositories directly — always through providers +- Providers in `features//provider.dart` are local to that feature +- Providers in `shared/providers/` are app-wide +- Business logic lives in providers or repositories, not in widgets or screens +- `Result` from `core/utils/result.dart` is used for fallible operations in repositories +- Database queries are in repositories only — no raw Drift queries in providers or widgets +- After any changes to Drift tables or DAOs, run `dart run build_runner build --delete-conflicting-outputs` + +## Code Style + +**No comments anywhere in the codebase.** No `//`, no `/* */`, no `///` doc comments. Code must be self-explanatory through naming. + +- Use descriptive names — no abbreviations unless universally known (`id`, `url`, `db`) +- Prefer named constructors and named parameters +- Extract widgets aggressively — if a build method exceeds ~40 lines, split it +- Widget files: one primary widget per file, name matches filename +- Use `final` everywhere possible +- No `dynamic` types +- Avoid `late` unless genuinely necessary + +## Naming Conventions + +| Element | Convention | Example | +|---|---|---| +| Files | `snake_case.dart` | `balance_card.dart` | +| Classes | `PascalCase` | `BalanceCard` | +| Providers | `camelCase` + `Provider` suffix | `transactionListProvider` | +| Riverpod notifiers | `PascalCase` + `Notifier` suffix | `AddTransactionNotifier` | +| Private members | `_camelCase` | `_handleSubmit` | + +## Feature Structure Template + +When adding a new feature, follow this pattern: + +``` +features/ +└── / + ├── provider.dart # Riverpod providers for this feature + ├── screen.dart # Top-level screen widget + └── widgets/ # Sub-widgets (create only if needed) +``` + +## Key Patterns + +**Error handling** — use `Result`: +```dart +final result = await repository.getAccounts(); +result.when( + success: (accounts) => ..., + failure: (error) => ..., +); +``` + +**Localization** — use `AppStrings` from `core/l10n/app_strings.dart`, never hardcode strings visible to the user. + +**Currency formatting** — use `CurrencyUtils` and `amountFormatProvider`, never format amounts manually. + +**Haptics** — route all haptic feedback through `HapticService`, never call `HapticFeedback` directly. + +**Colors for accounts** — use `CardColorService`, not hardcoded colors. + +## Existing Features + +- **dashboard** — main screen with account carousel (`BalanceCard`), transaction list, search, filter chips, budget progress, account editor overlay +- **add_transaction** — form screen: amount input, type toggle (income/expense), category picker, currency picker, account selector, date/time pickers, note field +- **categories** — category list management +- **settings** — theme, language, currency, budget, amount format, card text color, haptics, currency conversions + +## What NOT to Do + +- Do not add comments +- Do not put business logic in screen or widget files +- Do not access `AppDatabase` directly from features — use repositories +- Do not create new providers in `shared/providers/` unless the provider is needed by 2+ features +- Do not use `BuildContext` across async gaps without checking `mounted` +- Do not hardcode user-facing strings — use `AppStrings` +- Do not format currency amounts manually — use `CurrencyUtils` \ No newline at end of file diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 075035a..b060afe 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -3,7 +3,6 @@ import java.io.FileInputStream plugins { id("com.android.application") - id("kotlin-android") id("dev.flutter.flutter-gradle-plugin") } @@ -23,10 +22,6 @@ android { targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = JavaVersion.VERSION_17.toString() - } - defaultConfig { applicationId = "com.kolo.casha" minSdk = flutter.minSdkVersion diff --git a/android/gradle.properties b/android/gradle.properties index fbee1d8..d5da727 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,2 +1,6 @@ org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true +# This builtInKotlin flag was added automatically by Flutter migrator +android.builtInKotlin=false +# This newDsl flag was added automatically by Flutter migrator +android.newDsl=false diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts index ca7fe06..e70423e 100644 --- a/android/settings.gradle.kts +++ b/android/settings.gradle.kts @@ -20,7 +20,6 @@ pluginManagement { plugins { id("dev.flutter.flutter-plugin-loader") version "1.0.0" id("com.android.application") version "8.11.1" apply false - id("org.jetbrains.kotlin.android") version "2.2.20" apply false } include(":app") diff --git a/lib/core/services/biometric_service.dart b/lib/core/services/biometric_service.dart index ad14bf5..0b37233 100644 --- a/lib/core/services/biometric_service.dart +++ b/lib/core/services/biometric_service.dart @@ -25,10 +25,6 @@ class BiometricService { try { return await _auth.authenticate( localizedReason: 'Confirm your identity to open Casha', - options: const AuthenticationOptions( - biometricOnly: false, - stickyAuth: true, - ), ); } catch (_) { return false; diff --git a/lib/features/add_transaction/provider.dart b/lib/features/add_transaction/provider.dart index 78cae11..c9c25a1 100644 --- a/lib/features/add_transaction/provider.dart +++ b/lib/features/add_transaction/provider.dart @@ -80,13 +80,22 @@ class AddTransactionState { bool get isEditing => editingId != null; } -class AddTransactionNotifier extends StateNotifier { - AddTransactionNotifier(Transaction? initial) - : super( - initial != null - ? AddTransactionState.fromTransaction(initial) - : AddTransactionState.empty(), - ); +final addTransactionProvider = NotifierProvider.autoDispose + .family( + (initial) => AddTransactionNotifier(initial), + ); + +class AddTransactionNotifier extends Notifier { + AddTransactionNotifier(this._initial); + + final Transaction? _initial; + + @override + AddTransactionState build() { + return _initial != null + ? AddTransactionState.fromTransaction(_initial!) + : AddTransactionState.empty(); + } void setAmount(double? v) => state = state.copyWith(amount: v); @@ -123,11 +132,6 @@ class AddTransactionNotifier extends StateNotifier { void reset() => state = AddTransactionState.empty(); } -final addTransactionProvider = StateNotifierProvider.autoDispose - .family( - (ref, initial) => AddTransactionNotifier(initial), - ); - final availableCategoriesProvider = Provider.autoDispose .family, Transaction?>((ref, initial) { final type = ref.watch( @@ -135,3 +139,4 @@ final availableCategoriesProvider = Provider.autoDispose ); return AppCategories.forType(type); }); + diff --git a/lib/features/add_transaction/screen.dart b/lib/features/add_transaction/screen.dart index 4535eee..081458c 100644 --- a/lib/features/add_transaction/screen.dart +++ b/lib/features/add_transaction/screen.dart @@ -82,7 +82,7 @@ class _AddTransactionScreenState extends ConsumerState if (widget.initial!.category == 'Transfer') { WidgetsBinding.instance.addPostFrameCallback((_) { - final allTxs = ref.read(transactionsProvider).valueOrNull ?? []; + final allTxs = ref.read(transactionsProvider).value ?? []; if (widget.initial!.type == TransactionType.expense) { final counterpart = allTxs.firstWhereOrNull( @@ -271,7 +271,7 @@ class _AddTransactionScreenState extends ConsumerState currencyCode: currencyCode, accountId: state.selectedAccountId!, ); - await ref.read(transactionsProvider.notifier).update(updatedExpense); + await ref.read(transactionsProvider.notifier).updateTransaction(updatedExpense); if (_transferIncomeRecordId != null) { final updatedIncome = Transaction( @@ -285,7 +285,7 @@ class _AddTransactionScreenState extends ConsumerState currencyCode: currencyCode, accountId: state.toAccountId!, ); - await ref.read(transactionsProvider.notifier).update(updatedIncome); + await ref.read(transactionsProvider.notifier).updateTransaction(updatedIncome); } if (mounted) context.pop(); @@ -349,7 +349,7 @@ class _AddTransactionScreenState extends ConsumerState ); if (state.isEditing) { - await ref.read(transactionsProvider.notifier).update(tx); + await ref.read(transactionsProvider.notifier).updateTransaction(tx); } else { final res = await ref.read(transactionsProvider.notifier).add(tx); @@ -486,7 +486,7 @@ class _AddTransactionScreenState extends ConsumerState .delete(counterpartId); } else { final allTxs = - ref.read(transactionsProvider).valueOrNull ?? + ref.read(transactionsProvider).value ?? []; final oppositeType = widget.initial!.type == diff --git a/lib/features/add_transaction/widgets/account_row.dart b/lib/features/add_transaction/widgets/account_row.dart index ca6cea5..3ad2579 100644 --- a/lib/features/add_transaction/widgets/account_row.dart +++ b/lib/features/add_transaction/widgets/account_row.dart @@ -41,7 +41,7 @@ class AccountRow extends ConsumerWidget { final s = ref.watch(stringsProvider); final state = ref.watch(addTransactionProvider(initial)); final accountsAsync = ref.watch(accountsProvider); - final accounts = accountsAsync.valueOrNull ?? []; + final accounts = accountsAsync.value ?? []; final isTransfer = state.type == TransactionType.transfer; if (isTransfer && accounts.length == 2 && state.selectedAccountId != null) { diff --git a/lib/features/add_transaction/widgets/type_toggle.dart b/lib/features/add_transaction/widgets/type_toggle.dart index 80bef93..95edc21 100644 --- a/lib/features/add_transaction/widgets/type_toggle.dart +++ b/lib/features/add_transaction/widgets/type_toggle.dart @@ -21,7 +21,7 @@ class TypeToggle extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final accountsAsync = ref.watch(accountsProvider); - final accounts = accountsAsync.valueOrNull ?? []; + final accounts = accountsAsync.value ?? []; final transferDisabled = accounts.length <= 1; return Container( diff --git a/lib/features/categories/provider.dart b/lib/features/categories/provider.dart index d91ef2e..7afdad4 100644 --- a/lib/features/categories/provider.dart +++ b/lib/features/categories/provider.dart @@ -4,7 +4,7 @@ import '../dashboard/provider.dart'; final categoryExpenseProvider = Provider>((ref) { final txsAsync = ref.watch(transactionsProvider); - final txs = txsAsync.valueOrNull ?? []; + final txs = txsAsync.value ?? []; final filtered = txs.where((t) => t.type == TransactionType.expense); final map = {}; @@ -16,7 +16,7 @@ final categoryExpenseProvider = Provider>((ref) { final categoryIncomeProvider = Provider>((ref) { final txsAsync = ref.watch(transactionsProvider); - final txs = txsAsync.valueOrNull ?? []; + final txs = txsAsync.value ?? []; final filtered = txs.where((t) => t.type == TransactionType.income); final map = {}; @@ -28,7 +28,7 @@ final categoryIncomeProvider = Provider>((ref) { final monthlyBreakdownProvider = Provider>((ref) { final txsAsync = ref.watch(transactionsProvider); - final txs = txsAsync.valueOrNull ?? []; + final txs = txsAsync.value ?? []; final filtered = txs.where((t) => t.type == TransactionType.expense); final now = DateTime.now(); diff --git a/lib/features/dashboard/provider.dart b/lib/features/dashboard/provider.dart index c975ed5..7164f6f 100644 --- a/lib/features/dashboard/provider.dart +++ b/lib/features/dashboard/provider.dart @@ -35,77 +35,75 @@ final storageServiceProvider = Provider((ref) { }); final transactionsProvider = - StateNotifierProvider>>(( - ref, - ) { - final repository = ref.watch(transactionRepositoryProvider); - return TransactionsNotifier(repository); - }); + AsyncNotifierProvider>( + TransactionsNotifier.new, + ); -class TransactionsNotifier - extends StateNotifier>> { - final TransactionRepository _repository; +class TransactionsNotifier extends AsyncNotifier> { + @override + Future> build() async { + final repository = ref.watch(transactionRepositoryProvider); + final result = await repository.getAll(); - TransactionsNotifier(this._repository) : super(const AsyncValue.loading()) { - _load(); - } - - Future _load() async { - state = const AsyncValue.loading(); - final result = await _repository.getAll(); - - state = result.isSuccess - ? AsyncValue.data(result.dataOrNull!) - : AsyncValue.error(result.errorOrNull!, StackTrace.current); + if (result.isSuccess) { + return result.dataOrNull!; + } else { + throw result.errorOrNull!; + } } Future> add(Transaction transaction) async { - final result = await _repository.add(transaction); + final repository = ref.read(transactionRepositoryProvider); + final result = await repository.add(transaction); if (result.isSuccess) { - await _load(); + ref.invalidateSelf(); } return result; } - Future> update(Transaction transaction) async { - final result = await _repository.update(transaction); + Future> updateTransaction(Transaction transaction) async { + final repository = ref.read(transactionRepositoryProvider); + final result = await repository.update(transaction); if (result.isSuccess) { - await _load(); + ref.invalidateSelf(); } return result; } Future> delete(String id) async { - final result = await _repository.delete(id); + final repository = ref.read(transactionRepositoryProvider); + final result = await repository.delete(id); if (result.isSuccess) { - await _load(); + ref.invalidateSelf(); } return result; } Future restore(Transaction transaction) async { - await _repository.add(transaction); - await _load(); + final repository = ref.read(transactionRepositoryProvider); + await repository.add(transaction); + ref.invalidateSelf(); } Future clearAll() async { - await _repository.deleteAll(); + final repository = ref.read(transactionRepositoryProvider); + await repository.deleteAll(); state = const AsyncValue.data([]); } Future refresh() async { - await _load(); + ref.invalidateSelf(); } } final transferPairsProvider = Provider>((ref) { - final txs = ref.watch(transactionsProvider).valueOrNull ?? []; + final txs = ref.watch(transactionsProvider).value ?? []; final transfers = txs.where((t) => t.category == 'Transfer').toList(); final Map pairs = {}; @@ -131,23 +129,46 @@ final transferPairsProvider = Provider>((ref) { return pairs; }); -final searchQueryProvider = StateProvider((ref) => ''); +final searchQueryProvider = NotifierProvider<_SearchQueryNotifier, String>( + _SearchQueryNotifier.new, +); + +class _SearchQueryNotifier extends Notifier { + @override + String build() => ''; + + void set(String v) => state = v; +} enum TransactionFilter { all, income, expense, transfer } enum TimeFilter { allTime, lastMonth } -final transactionFilterProvider = StateProvider( - (ref) => TransactionFilter.all, +final transactionFilterProvider = NotifierProvider<_TransactionFilterNotifier, TransactionFilter>( + _TransactionFilterNotifier.new, ); -final timeFilterProvider = StateProvider( - (ref) => TimeFilter.lastMonth, +class _TransactionFilterNotifier extends Notifier { + @override + TransactionFilter build() => TransactionFilter.all; + + void set(TransactionFilter v) => state = v; +} + +final timeFilterProvider = NotifierProvider<_TimeFilterNotifier, TimeFilter>( + _TimeFilterNotifier.new, ); +class _TimeFilterNotifier extends Notifier { + @override + TimeFilter build() => TimeFilter.lastMonth; + + void set(TimeFilter v) => state = v; +} + final accountFilteredTransactionsProvider = Provider>((ref) { final txsAsync = ref.watch(transactionsProvider); - final txs = txsAsync.valueOrNull ?? []; + final txs = txsAsync.value ?? []; final activeAccount = ref.watch(activeAccountProvider); if (activeAccount == null) { @@ -158,7 +179,7 @@ final accountFilteredTransactionsProvider = Provider>((ref) { }); final globalTotalBalanceProvider = Provider((ref) { - final txs = ref.watch(transactionsProvider).valueOrNull ?? []; + final txs = ref.watch(transactionsProvider).value ?? []; final exchangeService = ref.watch(exchangeRateServiceProvider); final targetCurrency = ref.watch(currencyProvider).code; @@ -181,7 +202,7 @@ final totalBalanceProvider = Provider((ref) { String targetCurrency = globalCurrency; if (index > 0) { - final accounts = accountsAsync.valueOrNull ?? []; + final accounts = accountsAsync.value ?? []; if (index <= accounts.length) { targetCurrency = accounts[index - 1].currency; } @@ -211,7 +232,7 @@ final totalIncomeProvider = Provider((ref) { String targetCurrency = globalCurrency; if (index > 0) { - final accounts = accountsAsync.valueOrNull ?? []; + final accounts = accountsAsync.value ?? []; if (index <= accounts.length) { targetCurrency = accounts[index - 1].currency; } @@ -237,7 +258,7 @@ final totalExpenseProvider = Provider((ref) { String targetCurrency = globalCurrency; if (index > 0) { - final accounts = accountsAsync.valueOrNull ?? []; + final accounts = accountsAsync.value ?? []; if (index <= accounts.length) { targetCurrency = accounts[index - 1].currency; } @@ -267,7 +288,7 @@ final currentMonthExpenseProvider = Provider((ref) { String targetCurrency = globalCurrency; if (index > 0) { - final accounts = accountsAsync.valueOrNull ?? []; + final accounts = accountsAsync.value ?? []; if (index <= accounts.length) { targetCurrency = accounts[index - 1].currency; } @@ -349,7 +370,16 @@ final accountsProvider = StreamProvider>((ref) async* { } }); -final activeAccountIndexProvider = StateProvider((ref) => 0); +final activeAccountIndexProvider = NotifierProvider<_ActiveAccountIndexNotifier, int>( + _ActiveAccountIndexNotifier.new, +); + +class _ActiveAccountIndexNotifier extends Notifier { + @override + int build() => 0; + + void set(int v) => state = v; +} final activeAccountProvider = Provider((ref) { final index = ref.watch(activeAccountIndexProvider); @@ -387,52 +417,37 @@ class CardColors { } final cardColorsProvider = - StateNotifierProvider((ref) { - final notifier = CardColorsNotifier(); - notifier.setupThemeListener(ref); - return notifier; - }); + NotifierProvider( + CardColorsNotifier.new, + ); final accountCardColorsProvider = - StateNotifierProvider.family(( - ref, - accountId, - ) { - final notifier = CardColorsNotifier(accountId: accountId); - notifier.setupThemeListener(ref); - return notifier; - }); - -class CardColorsNotifier extends StateNotifier { - final int? accountId; - - CardColorsNotifier({this.accountId}) - : super( - const CardColors( - CardColorService.defaultPrimary, - CardColorService.defaultSecondary, - CardColorService.defaultGradientLight, - CardColorService.defaultGradientDark, - ), - ) { - _load(); - } + NotifierProvider.family( + (accountId) => AccountCardColorsNotifier(accountId), + ); +class CardColorsNotifier extends Notifier { int _loadGeneration = 0; - void setupThemeListener(Ref ref) { + @override + CardColors build() { ref.listen(themeProvider, (previous, next) { if (previous != null) { _onThemeChanged(previous, next); } }); + _load(); + return const CardColors( + CardColorService.defaultPrimary, + CardColorService.defaultSecondary, + CardColorService.defaultGradientLight, + CardColorService.defaultGradientDark, + ); } Future _load() async { final currentGeneration = ++_loadGeneration; - final (c1, c2, lightG, darkG) = await CardColorService.load( - accountId: accountId, - ); + final (c1, c2, lightG, darkG) = await CardColorService.load(); if (currentGeneration != _loadGeneration) return; state = CardColors(c1, c2, lightG, darkG); } @@ -450,7 +465,6 @@ class CardColorsNotifier extends StateNotifier { secondary, lightGradient, darkGradient, - accountId: accountId, ); } @@ -473,7 +487,127 @@ class CardColorsNotifier extends StateNotifier { secondary, CardColorService.defaultGradientLight, CardColorService.defaultGradientDark, - accountId: accountId, + ); + } + + void _onThemeChanged(ThemeMode previous, ThemeMode next) { + final previousBrightness = _resolve(previous); + final nextBrightness = _resolve(next); + + if (previousBrightness == nextBrightness) return; + + final oldDefaults = _defaultsFor(previousBrightness); + final newDefaults = _defaultsFor(nextBrightness); + + final isUsingOldDefaults = + state.primary == oldDefaults.primary && + state.secondary == oldDefaults.secondary && + state.gradientTypeForBrightness(previousBrightness) == + oldDefaults.gradient; + + if (isUsingOldDefaults) { + _loadGeneration++; + state = CardColors( + newDefaults.primary, + newDefaults.secondary, + state.lightGradientType, + state.darkGradientType, + ); + } + } + + Brightness _resolve(ThemeMode mode) { + if (mode == ThemeMode.system) { + return WidgetsBinding.instance.platformDispatcher.platformBrightness; + } + return mode == ThemeMode.dark ? Brightness.dark : Brightness.light; + } + + ({Color primary, Color secondary, GradientType gradient}) _defaultsFor( + Brightness brightness, + ) { + return brightness == Brightness.dark + ? ( + primary: CardColorService.defaultPrimary, + secondary: CardColorService.defaultSecondary, + gradient: CardColorService.defaultGradientDark, + ) + : ( + primary: CardColorService.defaultPrimaryLight, + secondary: CardColorService.defaultSecondaryLight, + gradient: CardColorService.defaultGradientLight, + ); + } +} + +class AccountCardColorsNotifier extends Notifier { + AccountCardColorsNotifier(this._accountId); + + final int _accountId; + int _loadGeneration = 0; + + @override + CardColors build() { + ref.listen(themeProvider, (previous, next) { + if (previous != null) { + _onThemeChanged(previous, next); + } + }); + _load(_accountId); + return const CardColors( + CardColorService.defaultPrimary, + CardColorService.defaultSecondary, + CardColorService.defaultGradientLight, + CardColorService.defaultGradientDark, + ); + } + + Future _load(int accountId) async { + final currentGeneration = ++_loadGeneration; + final (c1, c2, lightG, darkG) = await CardColorService.load( + accountId: accountId, + ); + if (currentGeneration != _loadGeneration) return; + state = CardColors(c1, c2, lightG, darkG); + } + + Future save( + Color primary, + Color secondary, + GradientType lightGradient, + GradientType darkGradient, + ) async { + _loadGeneration++; + state = CardColors(primary, secondary, lightGradient, darkGradient); + await CardColorService.save( + primary, + secondary, + lightGradient, + darkGradient, + accountId: _accountId, + ); + } + + Future reset(bool isDark) async { + final primary = isDark + ? CardColorService.defaultPrimary + : CardColorService.defaultPrimaryLight; + final secondary = isDark + ? CardColorService.defaultSecondary + : CardColorService.defaultSecondaryLight; + _loadGeneration++; + state = CardColors( + primary, + secondary, + CardColorService.defaultGradientLight, + CardColorService.defaultGradientDark, + ); + await CardColorService.save( + primary, + secondary, + CardColorService.defaultGradientLight, + CardColorService.defaultGradientDark, + accountId: _accountId, ); } diff --git a/lib/features/dashboard/screen.dart b/lib/features/dashboard/screen.dart index d487873..264eb83 100644 --- a/lib/features/dashboard/screen.dart +++ b/lib/features/dashboard/screen.dart @@ -280,7 +280,7 @@ class _DashboardScreenState extends ConsumerState { final activeIndex = ref.watch(activeAccountIndexProvider); final accountsAsync = ref.watch(accountsProvider); - final accountCount = accountsAsync.valueOrNull?.length ?? 0; + final accountCount = accountsAsync.value?.length ?? 0; final isOnAddAccountPage = accountCount < 5 && activeIndex == accountCount + 1; diff --git a/lib/features/dashboard/widgets/account_editor_overlay/account_editor_overlay.dart b/lib/features/dashboard/widgets/account_editor_overlay/account_editor_overlay.dart index 1497823..553af00 100644 --- a/lib/features/dashboard/widgets/account_editor_overlay/account_editor_overlay.dart +++ b/lib/features/dashboard/widgets/account_editor_overlay/account_editor_overlay.dart @@ -377,7 +377,7 @@ class _AccountEditorOverlayState extends State { mainAxisSize: MainAxisSize.min, children: [ if (!dash.isAddingAccount && - (ref.watch(accountsProvider).valueOrNull?.length ?? 0) > + (ref.watch(accountsProvider).value?.length ?? 0) > 1) ...[ GestureDetector( onTap: () => setState(() => _showDeleteDialog = true), diff --git a/lib/features/dashboard/widgets/account_editor_overlay/color_panel.dart b/lib/features/dashboard/widgets/account_editor_overlay/color_panel.dart index 826a89c..f59048a 100644 --- a/lib/features/dashboard/widgets/account_editor_overlay/color_panel.dart +++ b/lib/features/dashboard/widgets/account_editor_overlay/color_panel.dart @@ -588,7 +588,7 @@ class AccountColorPanel extends StatelessWidget { final accounts = ProviderScope.containerOf( dashboardContext, - ).read(accountsProvider).valueOrNull ?? + ).read(accountsProvider).value ?? []; if (isDuplicateName( diff --git a/lib/features/dashboard/widgets/account_editor_overlay/delete_dialog.dart b/lib/features/dashboard/widgets/account_editor_overlay/delete_dialog.dart index add2984..d970db2 100644 --- a/lib/features/dashboard/widgets/account_editor_overlay/delete_dialog.dart +++ b/lib/features/dashboard/widgets/account_editor_overlay/delete_dialog.dart @@ -56,7 +56,7 @@ class AccountDeleteDialog extends ConsumerWidget { onConfirm(); - final txs = ref.read(transactionsProvider).valueOrNull ?? []; + final txs = ref.read(transactionsProvider).value ?? []; final accountTxs = txs .where((t) => t.accountId == accountId) .toList(); diff --git a/lib/features/dashboard/widgets/balance_card.dart b/lib/features/dashboard/widgets/balance_card.dart index 8280165..ea9a08f 100644 --- a/lib/features/dashboard/widgets/balance_card.dart +++ b/lib/features/dashboard/widgets/balance_card.dart @@ -145,15 +145,12 @@ class BalanceCardState extends ConsumerState .toList(); final textColorMode = ref.watch(cardTextColorProvider); - final Color onCard; - switch (textColorMode) { - case CardTextColorMode.white: - onCard = Colors.white; - case CardTextColorMode.black: - onCard = Colors.black; - case CardTextColorMode.adaptive: - onCard = primary.computeLuminance() > 0.3 ? Colors.black : Colors.white; - } + final Color onCard = switch (textColorMode) { + CardTextColorMode.white => Colors.white, + CardTextColorMode.black => Colors.black, + CardTextColorMode.adaptive => primary.computeLuminance() > 0.3 ? Colors.black : Colors.white, + _ => Colors.white, + }; return GestureDetector( onLongPress: () { diff --git a/lib/features/dashboard/widgets/balance_card_carousel.dart b/lib/features/dashboard/widgets/balance_card_carousel.dart index 1e11bdd..56c1de5 100644 --- a/lib/features/dashboard/widgets/balance_card_carousel.dart +++ b/lib/features/dashboard/widgets/balance_card_carousel.dart @@ -75,7 +75,7 @@ class _BalanceCardCarouselState extends ConsumerState { Clip.none, itemCount: totalPages, onPageChanged: (index) { - ref.read(activeAccountIndexProvider.notifier).state = index; + ref.read(activeAccountIndexProvider.notifier).set(index); if (ref.read(hapticEnabledProvider)) { HapticService.light(); } @@ -103,7 +103,7 @@ class _BalanceCardCarouselState extends ConsumerState { ); final txs = - ref.watch(transactionsProvider).valueOrNull ?? []; + ref.watch(transactionsProvider).value ?? []; final accountTxs = txs .where((t) => t.accountId == account.id) .toList(); diff --git a/lib/features/dashboard/widgets/filter_chips.dart b/lib/features/dashboard/widgets/filter_chips.dart index ade5e26..ff97e24 100644 --- a/lib/features/dashboard/widgets/filter_chips.dart +++ b/lib/features/dashboard/widgets/filter_chips.dart @@ -23,15 +23,15 @@ class FilterChips extends ConsumerWidget { _FilterChip( label: strings.filterAllTime, isSelected: timeFilter == TimeFilter.allTime, - onTap: () => ref.read(timeFilterProvider.notifier).state = - TimeFilter.allTime, + onTap: () => ref.read(timeFilterProvider.notifier).set( + TimeFilter.allTime), ), const SizedBox(width: 6), _FilterChip( label: strings.filterMonth, isSelected: timeFilter == TimeFilter.lastMonth, - onTap: () => ref.read(timeFilterProvider.notifier).state = - TimeFilter.lastMonth, + onTap: () => ref.read(timeFilterProvider.notifier).set( + TimeFilter.lastMonth), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 8), @@ -46,32 +46,32 @@ class FilterChips extends ConsumerWidget { _FilterChip( label: strings.filterAll, isSelected: typeFilter == TransactionFilter.all, - onTap: () => ref.read(transactionFilterProvider.notifier).state = - TransactionFilter.all, + onTap: () => ref.read(transactionFilterProvider.notifier).set( + TransactionFilter.all), ), const SizedBox(width: 6), _FilterChip( label: strings.filterIncome, isSelected: typeFilter == TransactionFilter.income, color: AppColors.income, - onTap: () => ref.read(transactionFilterProvider.notifier).state = - TransactionFilter.income, + onTap: () => ref.read(transactionFilterProvider.notifier).set( + TransactionFilter.income), ), const SizedBox(width: 6), _FilterChip( label: strings.filterExpense, isSelected: typeFilter == TransactionFilter.expense, color: AppColors.expense, - onTap: () => ref.read(transactionFilterProvider.notifier).state = - TransactionFilter.expense, + onTap: () => ref.read(transactionFilterProvider.notifier).set( + TransactionFilter.expense), ), const SizedBox(width: 6), _FilterChip( label: strings.filterTransfer, isSelected: typeFilter == TransactionFilter.transfer, color: Colors.blueAccent, - onTap: () => ref.read(transactionFilterProvider.notifier).state = - TransactionFilter.transfer, + onTap: () => ref.read(transactionFilterProvider.notifier).set( + TransactionFilter.transfer), ), ], ), diff --git a/lib/features/dashboard/widgets/search_bar.dart b/lib/features/dashboard/widgets/search_bar.dart index 68d7a48..e92ed8f 100644 --- a/lib/features/dashboard/widgets/search_bar.dart +++ b/lib/features/dashboard/widgets/search_bar.dart @@ -37,7 +37,7 @@ class SearchBar extends StatelessWidget { color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), onPressed: () { controller.clear(); - ref.read(searchQueryProvider.notifier).state = ''; + ref.read(searchQueryProvider.notifier).set(''); }, ) : null, @@ -63,7 +63,7 @@ class SearchBar extends StatelessWidget { vertical: 12, ), ), - onChanged: (v) => ref.read(searchQueryProvider.notifier).state = v, + onChanged: (v) => ref.read(searchQueryProvider.notifier).set(v), ); } } diff --git a/lib/features/dashboard/widgets/transaction_tile.dart b/lib/features/dashboard/widgets/transaction_tile.dart index a0b9a87..d2f77c7 100644 --- a/lib/features/dashboard/widgets/transaction_tile.dart +++ b/lib/features/dashboard/widgets/transaction_tile.dart @@ -56,7 +56,7 @@ class TransactionTile extends ConsumerWidget { : 0.0; final displaySymbol = currencyMap[displayCurrency]?.symbol ?? ''; - final accounts = ref.watch(accountsProvider).valueOrNull ?? []; + final accounts = ref.watch(accountsProvider).value ?? []; final txAccount = accounts.firstWhereOrNull( (a) => a.id == transaction.accountId, ); diff --git a/lib/features/settings/provider.dart b/lib/features/settings/provider.dart index 5c8c16e..bd2ae3a 100644 --- a/lib/features/settings/provider.dart +++ b/lib/features/settings/provider.dart @@ -11,18 +11,20 @@ import '../../shared/utils/currency_utils.dart'; import '../../shared/providers/amount_format_provider.dart'; import '../dashboard/provider.dart'; -final budgetProvider = StateNotifierProvider((ref) { - final storage = ref.watch(storageServiceProvider); - return BudgetNotifier(storage.loadBudget(), storage); -}); +final budgetProvider = NotifierProvider( + BudgetNotifier.new, +); -class BudgetNotifier extends StateNotifier { - final dynamic _storage; - - BudgetNotifier(super.initialBudget, this._storage); +class BudgetNotifier extends Notifier { + @override + double? build() { + final storage = ref.watch(storageServiceProvider); + return storage.loadBudget(); + } Future setBudget(double? budget) async { - await _storage.saveBudget(budget); + final storage = ref.read(storageServiceProvider); + await storage.saveBudget(budget); state = budget; } @@ -50,70 +52,60 @@ const Map currencyMap = { 'RUB': CurrencyInfo('₽', 'RUB'), }; -class CurrencyNotifier extends StateNotifier { - final SharedPreferences _prefs; - - CurrencyNotifier(this._prefs) : super(currencyMap['USD']!) { - _load(); - } - - void _load() { - final code = _prefs.getString('currency_code') ?? 'USD'; - state = currencyMap[code] ?? currencyMap['USD']!; +class CurrencyNotifier extends Notifier { + @override + CurrencyInfo build() { + final prefs = ref.watch(sharedPreferencesProvider); + final code = prefs.getString('currency_code') ?? 'USD'; + return currencyMap[code] ?? currencyMap['USD']!; } Future setCurrency(String code) async { + final prefs = ref.read(sharedPreferencesProvider); state = currencyMap[code] ?? currencyMap['USD']!; - await _prefs.setString('currency_code', code); + await prefs.setString('currency_code', code); } } -final currencyProvider = StateNotifierProvider(( - ref, -) { - final prefs = ref.watch(sharedPreferencesProvider); - return CurrencyNotifier(prefs); -}); +final currencyProvider = NotifierProvider( + CurrencyNotifier.new, +); -class ThemeModeNotifier extends StateNotifier { - final SharedPreferences _prefs; - - ThemeModeNotifier(this._prefs) : super(ThemeMode.system) { - _load(); - } - - void _load() { - final saved = _prefs.getString('theme_mode'); +class ThemeModeNotifier extends Notifier { + @override + ThemeMode build() { + final prefs = ref.watch(sharedPreferencesProvider); + final saved = prefs.getString('theme_mode'); if (saved == 'dark') { - state = ThemeMode.dark; + return ThemeMode.dark; } else if (saved == 'light') { - state = ThemeMode.light; + return ThemeMode.light; } else { - state = ThemeMode.system; + return ThemeMode.system; } } Future setThemeMode(ThemeMode mode) async { + final prefs = ref.read(sharedPreferencesProvider); state = mode; - await _prefs.setString('theme_mode', mode.name); + await prefs.setString('theme_mode', mode.name); } } -final themeProvider = StateNotifierProvider(( - ref, -) { - final prefs = ref.watch(sharedPreferencesProvider); - return ThemeModeNotifier(prefs); -}); +final themeProvider = NotifierProvider( + ThemeModeNotifier.new, +); enum CardTextColorMode { white, adaptive, black } -class CardTextColorNotifier extends StateNotifier { +class CardTextColorNotifier extends Notifier { static const _key = 'card_text_color'; - final SharedPreferences _prefs; - CardTextColorNotifier(this._prefs) - : super(_fromString(_prefs.getString(_key))); + @override + CardTextColorMode build() { + final prefs = ref.watch(sharedPreferencesProvider); + return _fromString(prefs.getString(_key)); + } static CardTextColorMode _fromString(String? value) { return CardTextColorMode.values.firstWhere( @@ -123,16 +115,16 @@ class CardTextColorNotifier extends StateNotifier { } void set(CardTextColorMode mode) { + final prefs = ref.read(sharedPreferencesProvider); state = mode; - _prefs.setString(_key, mode.name); + prefs.setString(_key, mode.name); } } final cardTextColorProvider = - StateNotifierProvider((ref) { - final prefs = ref.watch(sharedPreferencesProvider); - return CardTextColorNotifier(prefs); - }); + NotifierProvider( + CardTextColorNotifier.new, + ); final exchangeRateServiceProvider = Provider((ref) { final prefs = ref.watch(sharedPreferencesProvider); @@ -143,15 +135,15 @@ final ratesInitProvider = FutureProvider((ref) async { await ref.read(exchangeRateServiceProvider).fetchRates(); }); -final hapticEnabledProvider = StateNotifierProvider(( - ref, -) { - return HapticNotifier(); -}); +final hapticEnabledProvider = NotifierProvider( + HapticNotifier.new, +); -class HapticNotifier extends StateNotifier { - HapticNotifier() : super(true) { +class HapticNotifier extends Notifier { + @override + bool build() { _load(); + return true; } Future _load() async { @@ -166,13 +158,15 @@ class HapticNotifier extends StateNotifier { } final showCurrencyConversionsProvider = - StateNotifierProvider((ref) { - return ShowCurrencyConversionsNotifier(); - }); + NotifierProvider( + ShowCurrencyConversionsNotifier.new, + ); -class ShowCurrencyConversionsNotifier extends StateNotifier { - ShowCurrencyConversionsNotifier() : super(true) { +class ShowCurrencyConversionsNotifier extends Notifier { + @override + bool build() { _load(); + return true; } Future _load() async { @@ -198,7 +192,7 @@ class ExportService { Future exportToCSV() async { final transactionsAsync = _ref.read(transactionsProvider); - final transactions = transactionsAsync.valueOrNull ?? []; + final transactions = transactionsAsync.value ?? []; final fmt = _ref.read(amountFormatProvider); final buffer = StringBuffer(); diff --git a/lib/shared/providers/amount_format_provider.dart b/lib/shared/providers/amount_format_provider.dart index ea213b7..8f42761 100644 --- a/lib/shared/providers/amount_format_provider.dart +++ b/lib/shared/providers/amount_format_provider.dart @@ -2,9 +2,11 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../core/constants.dart'; -class AmountFormatNotifier extends StateNotifier { - AmountFormatNotifier() : super(AmountFormat.commasDot) { +class AmountFormatNotifier extends Notifier { + @override + AmountFormat build() { _load(); + return AmountFormat.commasDot; } void _load() async { @@ -20,6 +22,6 @@ class AmountFormatNotifier extends StateNotifier { } } -final amountFormatProvider = StateNotifierProvider( - (ref) => AmountFormatNotifier(), +final amountFormatProvider = NotifierProvider( + AmountFormatNotifier.new, ); diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 2c1ec4f..e71a16d 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -6,10 +6,6 @@ #include "generated_plugin_registrant.h" -#include void fl_register_plugins(FlPluginRegistry* registry) { - g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin"); - sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 7ea2a80..be1ee3e 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,10 +3,10 @@ # list(APPEND FLUTTER_PLUGIN_LIST - sqlite3_flutter_libs ) list(APPEND FLUTTER_FFI_PLUGIN_LIST + jni ) set(PLUGIN_BUNDLED_LIBRARIES) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 89bcd1d..80dc39e 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -7,10 +7,8 @@ import Foundation import local_auth_darwin import shared_preferences_foundation -import sqlite3_flutter_libs func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { LocalAuthPlugin.register(with: registry.registrar(forPlugin: "LocalAuthPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) - Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 08d81be..91599f4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "8d7ff3948166b8ec5da0fbb5962000926b8e02f2ed9b3e51d1738905fbd4c98d" + sha256: a49d6cf99e8d8e7a8e93668d09ced0bbdb954d0b4fccc2f5f9241c6b87fad95c url: "https://pub.dev" source: hosted - version: "93.0.0" + version: "99.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: de7148ed2fcec579b19f122c1800933dfa028f6d9fd38a152b04b1516cec120b + sha256: "663efa951fb8a45e06f491223a604c93820598f20e6a99c25617a1576065e8b7" url: "https://pub.dev" source: hosted - version: "10.0.1" + version: "12.1.0" ansicolor: dependency: transitive description: @@ -45,10 +45,10 @@ packages: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.13.1" boolean_selector: dependency: transitive description: @@ -61,10 +61,10 @@ packages: dependency: transitive description: name: build - sha256: aadd943f4f8cc946882c954c187e6115a84c98c81ad1d9c6cbf0895a8c85da9c + sha256: a156715e7cd728130c592f30552575908aae5b100005fbc1f0fb16b3c03a3d10 url: "https://pub.dev" source: hosted - version: "4.0.5" + version: "4.0.6" build_config: dependency: transitive description: @@ -85,10 +85,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "521daf8d189deb79ba474e43a696b41c49fb3987818dbacf3308f1e03673a75e" + sha256: "1523ce62448ebac2c15a8ba5fbad8acac169788658a7dd2a1c2d9c2a9318b9a6" url: "https://pub.dev" source: hosted - version: "2.13.1" + version: "2.15.0" built_collection: dependency: transitive description: @@ -101,10 +101,10 @@ packages: dependency: transitive description: name: built_value - sha256: "6ae8a6435a8c6520c7077b107e77f1fb4ba7009633259a4d49a8afd8e7efc5e9" + sha256: "34e4067d30ce212937df995f03b69992eea683539ceeac7f679a1f1eba055b56" url: "https://pub.dev" source: hosted - version: "8.12.4" + version: "8.12.6" characters: dependency: transitive description: @@ -129,6 +129,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.4" + cli_config: + dependency: transitive + description: + name: cli_config + sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec + url: "https://pub.dev" + source: hosted + version: "0.2.0" cli_util: dependency: transitive description: @@ -149,18 +157,10 @@ packages: dependency: transitive description: name: code_assets - sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687" + sha256: bf394f466ba9205f1812a0433b392d6af280f155f56651eda7c18cc32ed493b8 url: "https://pub.dev" source: hosted - version: "1.0.0" - code_builder: - dependency: transitive - description: - name: code_builder - sha256: "6a6cab2ba4680d6423f34a9b972a4c9a94ebe1b62ecec4e1a1f2cba91fd1319d" - url: "https://pub.dev" - source: hosted - version: "4.11.1" + version: "1.2.1" collection: dependency: transitive description: @@ -177,6 +177,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.2" + coverage: + dependency: transitive + description: + name: coverage + sha256: "956a3de0725ca232ad353565a8290d3357592bf4250f6f298a185e2d949c5d3d" + url: "https://pub.dev" + source: hosted + version: "1.15.1" crypto: dependency: transitive description: @@ -197,34 +205,34 @@ packages: dependency: "direct main" description: name: cupertino_icons - sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd" url: "https://pub.dev" source: hosted - version: "1.0.8" + version: "1.0.9" dart_style: dependency: transitive description: name: dart_style - sha256: "29f7ecc274a86d32920b1d9cfc7502fa87220da41ec60b55f329559d5732e2b2" + sha256: a4c1ccfee44c7e75ed80484071a5c142a385345e658fd8bd7c4b5c97e7198f98 url: "https://pub.dev" source: hosted - version: "3.1.7" + version: "3.1.8" drift: dependency: "direct main" description: name: drift - sha256: "055c249d1f91be5a47fe447f88afc24c4ca6f4cd6c5ed66767b4797d48acc2e5" + sha256: "6cc0b623c0e83f7080524d8396e9301b1d78b9c66a4fdceeb0f798211303254c" url: "https://pub.dev" source: hosted - version: "2.32.1" + version: "2.34.0" drift_dev: dependency: "direct dev" description: name: drift_dev - sha256: "88a9de3af8571518148a6d8a513b57779fd1e60a026d3ab8a481a878fba01d91" + sha256: "9cfff1576b49725da0d32c040651a41ae195e8c4af8d8da301593e41d7abc2f7" url: "https://pub.dev" source: hosted - version: "2.32.1" + version: "2.34.0" equatable: dependency: transitive description: @@ -269,10 +277,10 @@ packages: dependency: "direct main" description: name: fl_chart - sha256: "74959b99b92b9eebeed1a4049426fd67c4abc3c5a0f4d12e2877097d6a11ae08" + sha256: b938f77d042cbcd822936a7a359a7235bad8bd72070de1f827efc2cc297ac888 url: "https://pub.dev" source: hosted - version: "0.69.2" + version: "1.2.0" flutter: dependency: "direct main" description: flutter @@ -306,26 +314,26 @@ packages: dependency: "direct dev" description: name: flutter_native_splash - sha256: "4fb9f4113350d3a80841ce05ebf1976a36de622af7d19aca0ca9a9911c7ff002" + sha256: "9db4b80b044e9af17cc4b1272137fc7ace0054d879ef8210a76adc34aaf4cdff" url: "https://pub.dev" source: hosted - version: "2.4.7" + version: "2.4.8" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: ee8068e0e1cd16c4a82714119918efdeed33b3ba7772c54b5d094ab53f9b7fd1 + sha256: "3854fe5e3bff0b113c658f260b90c95dea17c92db0f2addeac2e343dd9969785" url: "https://pub.dev" source: hosted - version: "2.0.33" + version: "2.0.35" flutter_riverpod: dependency: "direct main" description: name: flutter_riverpod - sha256: "9532ee6db4a943a1ed8383072a2e3eeda041db5657cdf6d2acecf3c21ecbe7e1" + sha256: "9255e1e3ad6e38906a1b4f8287678f95f378744c5b46b1985588543f3f19046e" url: "https://pub.dev" source: hosted - version: "2.6.1" + version: "3.3.2" flutter_test: dependency: "direct dev" description: flutter @@ -336,6 +344,14 @@ packages: description: flutter source: sdk version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" glob: dependency: transitive description: @@ -348,18 +364,18 @@ packages: dependency: "direct main" description: name: go_router - sha256: f02fd7d2a4dc512fec615529824fdd217fecb3a3d3de68360293a551f21634b3 + sha256: "5922b2861e2235a3504896f0d6fa07d84141b480cf52eecd2f42cd25585a9e8a" url: "https://pub.dev" source: hosted - version: "14.8.1" + version: "17.3.0" google_fonts: dependency: "direct main" description: name: google_fonts - sha256: ba03d03bcaa2f6cb7bd920e3b5027181db75ab524f8891c8bc3aa603885b8055 + sha256: "4e9391085e524954a51e3625b7c9c7e9851dc3f376603208bb45c24b9a66255d" url: "https://pub.dev" source: hosted - version: "6.3.3" + version: "8.1.0" graphs: dependency: transitive description: @@ -372,10 +388,10 @@ packages: dependency: transitive description: name: hooks - sha256: e79ed1e8e1929bc6ecb6ec85f0cb519c887aa5b423705ded0d0f2d9226def388 + sha256: "9a62a50b50b769a737bc0a8ff381f333529df3ab746b2f6b02e83760231455ba" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "2.0.2" html: dependency: transitive description: @@ -412,18 +428,18 @@ packages: dependency: transitive description: name: image - sha256: f9881ff4998044947ec38d098bc7c8316ae1186fa786eddffdb867b9bc94dfce + sha256: "6300175e00616bbc832e2fc91bfa4d776af5402c81c7151bee6905bb08473c52" url: "https://pub.dev" source: hosted - version: "4.8.0" + version: "4.9.1" intl: dependency: "direct main" description: name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" url: "https://pub.dev" source: hosted - version: "0.19.0" + version: "0.20.2" io: dependency: transitive description: @@ -432,14 +448,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.5" + jni: + dependency: transitive + description: + name: jni + sha256: c2230682d5bc2362c1c9e8d3c7f406d9cbba23ab3f2e203a025dd47e0fb2e68f + url: "https://pub.dev" + source: hosted + version: "1.0.0" + jni_flutter: + dependency: transitive + description: + name: jni_flutter + sha256: "8b59e590786050b1cd866677dddaf76b1ade5e7bc751abe04b86e84d379d3ba6" + url: "https://pub.dev" + source: hosted + version: "1.0.1" json_annotation: dependency: transitive description: name: json_annotation - sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8 + sha256: "2a743920d81b7910627f68ee2c9ac1fc0bfee32b9fc3403587d7c6791ca12f80" url: "https://pub.dev" source: hosted - version: "4.11.0" + version: "4.12.0" leak_tracker: dependency: transitive description: @@ -476,26 +508,26 @@ packages: dependency: "direct main" description: name: local_auth - sha256: "434d854cf478f17f12ab29a76a02b3067f86a63a6d6c4eb8fbfdcfe4879c1b7b" + sha256: ae6f382f638108c6becd134318d7c3f0a93875383a54010f61d7c97ac05d5137 url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "3.0.1" local_auth_android: dependency: transitive description: name: local_auth_android - sha256: a0bdfcc0607050a26ef5b31d6b4b254581c3d3ce3c1816ab4d4f4a9173e84467 + sha256: fdb936d59ab945c7af297defd67bd1ed87b11b6db1bc16d01e94677a8f1c38ec url: "https://pub.dev" source: hosted - version: "1.0.56" + version: "2.0.9" local_auth_darwin: dependency: transitive description: name: local_auth_darwin - sha256: "699873970067a40ef2f2c09b4c72eb1cfef64224ef041b3df9fdc5c4c1f91f49" + sha256: a8c3d4e17454111f7fd31ff72a31222359f6059f7fe956c2dcfe0f88f49826d4 url: "https://pub.dev" source: hosted - version: "1.6.1" + version: "2.0.3" local_auth_platform_interface: dependency: transitive description: @@ -508,10 +540,10 @@ packages: dependency: transitive description: name: local_auth_windows - sha256: bc4e66a29b0fdf751aafbec923b5bed7ad6ed3614875d8151afe2578520b2ab5 + sha256: be12c5b8ba5e64896983123655c5f67d2484ecfcc95e367952ad6e3bff94cb16 url: "https://pub.dev" source: hosted - version: "1.0.11" + version: "2.0.1" logging: dependency: transitive description: @@ -540,10 +572,10 @@ packages: dependency: transitive description: name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.18.0" mime: dependency: transitive description: @@ -556,18 +588,26 @@ packages: dependency: transitive description: name: native_toolchain_c - sha256: "6ba77bb18063eebe9de401f5e6437e95e1438af0a87a3a39084fbd37c90df572" + sha256: f59351d28f49520cd3a74eb1f41c5f19ae15e53c65a3231d14af672e46510a96 url: "https://pub.dev" source: hosted - version: "0.17.6" + version: "0.19.1" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" objective_c: dependency: transitive description: name: objective_c - sha256: "100a1c87616ab6ed41ec263b083c0ef3261ee6cd1dc3b0f35f8ddfa4f996fe52" + sha256: "6cb691c686fa2838c6deb34980d426145c2a5d537491cb83d463c33cdbc726ed" url: "https://pub.dev" source: hosted - version: "9.3.0" + version: "9.4.1" package_config: dependency: transitive description: @@ -596,10 +636,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e + sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd" url: "https://pub.dev" source: hosted - version: "2.2.22" + version: "2.3.1" path_provider_foundation: dependency: transitive description: @@ -696,22 +736,30 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.0" + record_use: + dependency: transitive + description: + name: record_use + sha256: "2551bd8eecfe95d14ae75f6021ad0248be5c27f138c2ec12fcb52b500b3ba1ed" + url: "https://pub.dev" + source: hosted + version: "0.6.0" riverpod: dependency: transitive description: name: riverpod - sha256: "59062512288d3056b2321804332a13ffdd1bf16df70dcc8e506e411280a72959" + sha256: "17100416c51db7810c71a7bb2c34d1f881faa0074fd452afb0c4db6f8f126c76" url: "https://pub.dev" source: hosted - version: "2.6.1" + version: "3.3.2" sensors_plus: dependency: "direct main" description: name: sensors_plus - sha256: "89e2bfc3d883743539ce5774a2b93df61effde40ff958ecad78cd66b1a8b8d52" + sha256: "56e8cd4260d9ed8e00ecd8da5d9fdc8a1b2ec12345a750dfa51ff83fcf12e3fa" url: "https://pub.dev" source: hosted - version: "6.1.2" + version: "7.0.0" sensors_plus_platform_interface: dependency: transitive description: @@ -724,18 +772,18 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64" + sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf url: "https://pub.dev" source: hosted - version: "2.5.4" + version: "2.5.5" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "8374d6200ab33ac99031a852eba4c8eb2170c4bf20778b3e2c9eccb45384fb41" + sha256: "93ae5884a9df5d3bb696825bceb3a17590754548b5d740eba51500afc8d088f5" url: "https://pub.dev" source: hosted - version: "2.4.21" + version: "2.4.26" shared_preferences_foundation: dependency: transitive description: @@ -756,10 +804,10 @@ packages: dependency: transitive description: name: shared_preferences_platform_interface - sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" shared_preferences_web: dependency: transitive description: @@ -784,6 +832,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.2" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + url: "https://pub.dev" + source: hosted + version: "1.1.3" shelf_web_socket: dependency: transitive description: @@ -801,10 +865,26 @@ packages: dependency: transitive description: name: source_gen - sha256: "732792cfd197d2161a65bb029606a46e0a18ff30ef9e141a7a82172b05ea8ecd" + sha256: ec37cc0e6694374cbef59ed79685572c870a54ede6fa30a3e420feb3adffea02 url: "https://pub.dev" source: hosted - version: "4.2.2" + version: "4.2.3" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + url: "https://pub.dev" + source: hosted + version: "2.1.2" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" + url: "https://pub.dev" + source: hosted + version: "0.10.13" source_span: dependency: transitive description: @@ -817,26 +897,26 @@ packages: dependency: transitive description: name: sqlite3 - sha256: caa693ad15a587a2b4fde093b728131a1827903872171089dedb16f7665d3a91 + sha256: "37356bcb56ce0d9404d602c41e4bdb7765e7e9732a3e47adb3d98c556a6abdad" url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.3.3" sqlite3_flutter_libs: dependency: "direct main" description: name: sqlite3_flutter_libs - sha256: eeb9e3a45207649076b808f8a5a74d68770d0b7f26ccef6d5f43106eee5375ad + sha256: "3ed7553eee7bb368f8950f58ba29f634e06e813c029aff6a0d60862b96de8454" url: "https://pub.dev" source: hosted - version: "0.5.42" + version: "0.6.0+eol" sqlparser: dependency: transitive description: name: sqlparser - sha256: ab2b467425f1d4f3acfa5fd11a08226f7d6c26ff102c06be1807e1dff34e050b + sha256: "40bdddb306a727be9ce510bd2d2b9a6c9db6c586d846ef7b22e3990a2b24f02d" url: "https://pub.dev" source: hosted - version: "0.44.3" + version: "0.44.5" stack_trace: dependency: transitive description: @@ -885,14 +965,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.2" + test: + dependency: transitive + description: + name: test + sha256: "8d9ceddbab833f180fbefed08afa76d7c03513dfdba87ffcec2718b02bbcbf20" + url: "https://pub.dev" + source: hosted + version: "1.31.0" test_api: dependency: transitive description: name: test_api - sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" + sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" url: "https://pub.dev" source: hosted - version: "0.7.10" + version: "0.7.11" + test_core: + dependency: transitive + description: + name: test_core + sha256: "1991d4cfe85d5043241acac92962c3977c8d2f2add1ee73130c7b286417d1d34" + url: "https://pub.dev" + source: hosted + version: "0.6.17" typed_data: dependency: transitive description: @@ -929,10 +1025,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" + sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360" url: "https://pub.dev" source: hosted - version: "15.0.2" + version: "15.2.0" watcher: dependency: transitive description: @@ -965,6 +1061,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" xdg_directories: dependency: transitive description: @@ -977,10 +1081,10 @@ packages: dependency: transitive description: name: xml - sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" + sha256: "67f0aff7be013d107995e9b75bf4e7f2c3ef2dfdb2c8e68024bba0a7fd5756a4" url: "https://pub.dev" source: hosted - version: "6.6.1" + version: "7.0.1" yaml: dependency: transitive description: @@ -990,5 +1094,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.11.1 <4.0.0" - flutter: ">=3.38.4" + dart: ">=3.12.0 <4.0.0" + flutter: ">=3.44.0" diff --git a/pubspec.yaml b/pubspec.yaml index 634329d..649938f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,20 +10,20 @@ dependencies: flutter: sdk: flutter cupertino_icons: ^1.0.8 - flutter_riverpod: ^2.6.1 - go_router: ^14.6.2 + flutter_riverpod: ^3.3.2 + go_router: ^17.3.0 shared_preferences: ^2.3.3 - fl_chart: ^0.69.0 - google_fonts: ^6.2.1 - intl: ^0.19.0 + fl_chart: ^1.2.0 + google_fonts: ^8.1.0 + intl: ^0.20.2 uuid: ^4.5.1 path_provider: ^2.1.5 http: ^1.2.0 - sensors_plus: ^6.1.0 - local_auth: ^2.3.0 + sensors_plus: ^7.0.0 + local_auth: ^3.0.1 flutter_colorpicker: ^1.1.0 drift: ^2.14.1 - sqlite3_flutter_libs: ^0.5.20 + sqlite3_flutter_libs: ^0.6.0+eol path: ^1.8.3 flutter_launcher_icons: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 5888a93..7407ddd 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,11 +7,8 @@ #include "generated_plugin_registrant.h" #include -#include void RegisterPlugins(flutter::PluginRegistry* registry) { LocalAuthPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("LocalAuthPlugin")); - Sqlite3FlutterLibsPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 8881dfe..5e44f84 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,10 +4,10 @@ list(APPEND FLUTTER_PLUGIN_LIST local_auth_windows - sqlite3_flutter_libs ) list(APPEND FLUTTER_FFI_PLUGIN_LIST + jni ) set(PLUGIN_BUNDLED_LIBRARIES)