Compare commits

...

2 Commits

Author SHA1 Message Date
kolo 127a917eac step 2026-06-13 23:24:00 +03:00
kolo 8fb785944c step 2026-06-13 22:43:06 +03:00
32 changed files with 902 additions and 358 deletions
+128
View File
@@ -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<T>` 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/<name>/`** — 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/<name>/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<T>` 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/
└── <feature_name>/
├── 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<T>`:
```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`
-5
View File
@@ -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
+4
View File
@@ -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
-1
View File
@@ -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")
+1
View File
@@ -167,6 +167,7 @@ class AppStrings {
String get colorPrimary => _ru ? 'Основной' : 'Primary';
String get colorSecondary => _ru ? 'Второй' : 'Secondary';
String get colorSecond => _ru ? 'Второй' : 'Second';
String get colorSolid => _ru ? 'Однотон' : 'Solid';
String get gradientLinear => _ru ? 'Линейный' : 'Linear';
String get gradientReverse => _ru ? 'Обратный' : 'Reverse';
-4
View File
@@ -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;
+16 -11
View File
@@ -80,14 +80,23 @@ class AddTransactionState {
bool get isEditing => editingId != null;
}
class AddTransactionNotifier extends StateNotifier<AddTransactionState> {
AddTransactionNotifier(Transaction? initial)
: super(
initial != null
? AddTransactionState.fromTransaction(initial)
: AddTransactionState.empty(),
final addTransactionProvider = NotifierProvider.autoDispose
.family<AddTransactionNotifier, AddTransactionState, Transaction?>(
(initial) => AddTransactionNotifier(initial),
);
class AddTransactionNotifier extends Notifier<AddTransactionState> {
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);
void setCategory(String v) => state = state.copyWith(category: v);
@@ -123,11 +132,6 @@ class AddTransactionNotifier extends StateNotifier<AddTransactionState> {
void reset() => state = AddTransactionState.empty();
}
final addTransactionProvider = StateNotifierProvider.autoDispose
.family<AddTransactionNotifier, AddTransactionState, Transaction?>(
(ref, initial) => AddTransactionNotifier(initial),
);
final availableCategoriesProvider = Provider.autoDispose
.family<List<String>, Transaction?>((ref, initial) {
final type = ref.watch(
@@ -135,3 +139,4 @@ final availableCategoriesProvider = Provider.autoDispose
);
return AppCategories.forType(type);
});
+5 -5
View File
@@ -82,7 +82,7 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen>
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<AddTransactionScreen>
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<AddTransactionScreen>
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<AddTransactionScreen>
);
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<AddTransactionScreen>
.delete(counterpartId);
} else {
final allTxs =
ref.read(transactionsProvider).valueOrNull ??
ref.read(transactionsProvider).value ??
[];
final oppositeType =
widget.initial!.type ==
@@ -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) {
@@ -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(
+3 -3
View File
@@ -4,7 +4,7 @@ import '../dashboard/provider.dart';
final categoryExpenseProvider = Provider<Map<String, double>>((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 = <String, double>{};
@@ -16,7 +16,7 @@ final categoryExpenseProvider = Provider<Map<String, double>>((ref) {
final categoryIncomeProvider = Provider<Map<String, double>>((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 = <String, double>{};
@@ -28,7 +28,7 @@ final categoryIncomeProvider = Provider<Map<String, double>>((ref) {
final monthlyBreakdownProvider = Provider<List<MonthlyData>>((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();
+211 -77
View File
@@ -35,77 +35,75 @@ final storageServiceProvider = Provider<StorageService>((ref) {
});
final transactionsProvider =
StateNotifierProvider<TransactionsNotifier, AsyncValue<List<Transaction>>>((
ref,
) {
AsyncNotifierProvider<TransactionsNotifier, List<Transaction>>(
TransactionsNotifier.new,
);
class TransactionsNotifier extends AsyncNotifier<List<Transaction>> {
@override
Future<List<Transaction>> build() async {
final repository = ref.watch(transactionRepositoryProvider);
return TransactionsNotifier(repository);
});
final result = await repository.getAll();
class TransactionsNotifier
extends StateNotifier<AsyncValue<List<Transaction>>> {
final TransactionRepository _repository;
TransactionsNotifier(this._repository) : super(const AsyncValue.loading()) {
_load();
if (result.isSuccess) {
return result.dataOrNull!;
} else {
throw result.errorOrNull!;
}
Future<void> _load() async {
state = const AsyncValue.loading();
final result = await _repository.getAll();
state = result.isSuccess
? AsyncValue.data(result.dataOrNull!)
: AsyncValue.error(result.errorOrNull!, StackTrace.current);
}
Future<Result<void>> 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<Result<void>> update(Transaction transaction) async {
final result = await _repository.update(transaction);
Future<Result<void>> 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<Result<void>> 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<void> restore(Transaction transaction) async {
await _repository.add(transaction);
await _load();
final repository = ref.read(transactionRepositoryProvider);
await repository.add(transaction);
ref.invalidateSelf();
}
Future<void> clearAll() async {
await _repository.deleteAll();
final repository = ref.read(transactionRepositoryProvider);
await repository.deleteAll();
state = const AsyncValue.data([]);
}
Future<void> refresh() async {
await _load();
ref.invalidateSelf();
}
}
final transferPairsProvider = Provider<Map<String, Transaction>>((ref) {
final txs = ref.watch(transactionsProvider).valueOrNull ?? [];
final txs = ref.watch(transactionsProvider).value ?? [];
final transfers = txs.where((t) => t.category == 'Transfer').toList();
final Map<String, Transaction> pairs = {};
@@ -131,23 +129,46 @@ final transferPairsProvider = Provider<Map<String, Transaction>>((ref) {
return pairs;
});
final searchQueryProvider = StateProvider<String>((ref) => '');
final searchQueryProvider = NotifierProvider<_SearchQueryNotifier, String>(
_SearchQueryNotifier.new,
);
class _SearchQueryNotifier extends Notifier<String> {
@override
String build() => '';
void set(String v) => state = v;
}
enum TransactionFilter { all, income, expense, transfer }
enum TimeFilter { allTime, lastMonth }
final transactionFilterProvider = StateProvider<TransactionFilter>(
(ref) => TransactionFilter.all,
final transactionFilterProvider = NotifierProvider<_TransactionFilterNotifier, TransactionFilter>(
_TransactionFilterNotifier.new,
);
final timeFilterProvider = StateProvider<TimeFilter>(
(ref) => TimeFilter.lastMonth,
class _TransactionFilterNotifier extends Notifier<TransactionFilter> {
@override
TransactionFilter build() => TransactionFilter.all;
void set(TransactionFilter v) => state = v;
}
final timeFilterProvider = NotifierProvider<_TimeFilterNotifier, TimeFilter>(
_TimeFilterNotifier.new,
);
class _TimeFilterNotifier extends Notifier<TimeFilter> {
@override
TimeFilter build() => TimeFilter.lastMonth;
void set(TimeFilter v) => state = v;
}
final accountFilteredTransactionsProvider = Provider<List<Transaction>>((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<List<Transaction>>((ref) {
});
final globalTotalBalanceProvider = Provider<double>((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<double>((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<double>((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<double>((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<double>((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<List<Account>>((ref) async* {
}
});
final activeAccountIndexProvider = StateProvider<int>((ref) => 0);
final activeAccountIndexProvider = NotifierProvider<_ActiveAccountIndexNotifier, int>(
_ActiveAccountIndexNotifier.new,
);
class _ActiveAccountIndexNotifier extends Notifier<int> {
@override
int build() => 0;
void set(int v) => state = v;
}
final activeAccountProvider = Provider<Account?>((ref) {
final index = ref.watch(activeAccountIndexProvider);
@@ -387,52 +417,37 @@ class CardColors {
}
final cardColorsProvider =
StateNotifierProvider<CardColorsNotifier, CardColors>((ref) {
final notifier = CardColorsNotifier();
notifier.setupThemeListener(ref);
return notifier;
});
NotifierProvider<CardColorsNotifier, CardColors>(
CardColorsNotifier.new,
);
final accountCardColorsProvider =
StateNotifierProvider.family<CardColorsNotifier, CardColors, int>((
ref,
accountId,
) {
final notifier = CardColorsNotifier(accountId: accountId);
notifier.setupThemeListener(ref);
return notifier;
});
class CardColorsNotifier extends StateNotifier<CardColors> {
final int? accountId;
CardColorsNotifier({this.accountId})
: super(
const CardColors(
CardColorService.defaultPrimary,
CardColorService.defaultSecondary,
CardColorService.defaultGradientLight,
CardColorService.defaultGradientDark,
),
) {
_load();
}
NotifierProvider.family<AccountCardColorsNotifier, CardColors, int>(
(accountId) => AccountCardColorsNotifier(accountId),
);
class CardColorsNotifier extends Notifier<CardColors> {
int _loadGeneration = 0;
void setupThemeListener(Ref ref) {
@override
CardColors build() {
ref.listen<ThemeMode>(themeProvider, (previous, next) {
if (previous != null) {
_onThemeChanged(previous, next);
}
});
_load();
return const CardColors(
CardColorService.defaultPrimary,
CardColorService.defaultSecondary,
CardColorService.defaultGradientLight,
CardColorService.defaultGradientDark,
);
}
Future<void> _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<CardColors> {
secondary,
lightGradient,
darkGradient,
accountId: accountId,
);
}
@@ -473,7 +487,127 @@ class CardColorsNotifier extends StateNotifier<CardColors> {
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<CardColors> {
AccountCardColorsNotifier(this._accountId);
final int _accountId;
int _loadGeneration = 0;
@override
CardColors build() {
ref.listen<ThemeMode>(themeProvider, (previous, next) {
if (previous != null) {
_onThemeChanged(previous, next);
}
});
_load(_accountId);
return const CardColors(
CardColorService.defaultPrimary,
CardColorService.defaultSecondary,
CardColorService.defaultGradientLight,
CardColorService.defaultGradientDark,
);
}
Future<void> _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<void> 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<void> 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,
);
}
+1 -1
View File
@@ -280,7 +280,7 @@ class _DashboardScreenState extends ConsumerState<DashboardScreen> {
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;
@@ -85,7 +85,7 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
Widget build(BuildContext context) {
final mq = MediaQuery.of(widget.context);
final cardTop = mq.padding.top + kToolbarHeight + 16;
const cardHeight = 230.0;
const cardHeight = 190.0;
const editorPanelHeight = 102.0;
final editorPanelTop = cardTop + cardHeight + 20;
final colorPanelTop = editorPanelTop + editorPanelHeight + 12;
@@ -377,7 +377,7 @@ class _AccountEditorOverlayState extends State<AccountEditorOverlay> {
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),
@@ -588,7 +588,7 @@ class AccountColorPanel extends StatelessWidget {
final accounts =
ProviderScope.containerOf(
dashboardContext,
).read(accountsProvider).valueOrNull ??
).read(accountsProvider).value ??
[];
if (isDuplicateName(
@@ -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();
@@ -3,7 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../../core/constants.dart';
import '../../../../shared/widgets/byn_sign.dart';
class AccountEditorPanel extends ConsumerWidget {
class AccountEditorPanel extends ConsumerStatefulWidget {
final TextEditingController nameController;
final String selectedCurrency;
final bool showCurrencyDropdown;
@@ -26,15 +26,29 @@ class AccountEditorPanel extends ConsumerWidget {
});
@override
Widget build(BuildContext context, WidgetRef ref) {
ConsumerState<AccountEditorPanel> createState() => _AccountEditorPanelState();
}
class _AccountEditorPanelState extends ConsumerState<AccountEditorPanel> {
bool _showNameError = false;
void _triggerNameError() {
setState(() => _showNameError = true);
Future.delayed(const Duration(seconds: 2), () {
if (mounted) setState(() => _showNameError = false);
});
}
@override
Widget build(BuildContext context) {
return Container(
height: panelHeight,
height: widget.panelHeight,
decoration: BoxDecoration(
color: Theme.of(dashboardContext).colorScheme.surface,
color: Theme.of(widget.dashboardContext).colorScheme.surface,
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: Theme.of(
dashboardContext,
widget.dashboardContext,
).colorScheme.onSurface.withOpacity(0.1),
width: 1.5,
),
@@ -58,7 +72,7 @@ class AccountEditorPanel extends ConsumerWidget {
fontSize: 11,
fontWeight: FontWeight.w600,
color: Theme.of(
dashboardContext,
widget.dashboardContext,
).colorScheme.onSurface.withOpacity(0.6),
),
),
@@ -70,7 +84,7 @@ class AccountEditorPanel extends ConsumerWidget {
Expanded(
flex: 3,
child: TextField(
controller: nameController,
controller: widget.nameController,
buildCounter:
(
context, {
@@ -84,23 +98,23 @@ class AccountEditorPanel extends ConsumerWidget {
hintStyle: TextStyle(
fontSize: 13,
color: Theme.of(
dashboardContext,
widget.dashboardContext,
).colorScheme.onSurface.withOpacity(0.4),
),
filled: true,
fillColor: Theme.of(
dashboardContext,
widget.dashboardContext,
).colorScheme.onSurface.withOpacity(0.05),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color:
(showLimitError ||
showDuplicateError ||
nameController.text.trim().isEmpty)
(widget.showLimitError ||
widget.showDuplicateError ||
_showNameError)
? Colors.red
: Theme.of(
dashboardContext,
widget.dashboardContext,
).colorScheme.onSurface.withOpacity(0.15),
width: 1.5,
),
@@ -109,12 +123,12 @@ class AccountEditorPanel extends ConsumerWidget {
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color:
(showLimitError ||
showDuplicateError ||
nameController.text.trim().isEmpty)
(widget.showLimitError ||
widget.showDuplicateError ||
_showNameError)
? Colors.red
: Theme.of(
dashboardContext,
widget.dashboardContext,
).colorScheme.onSurface.withOpacity(0.15),
width: 1.5,
),
@@ -123,9 +137,9 @@ class AccountEditorPanel extends ConsumerWidget {
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(
color:
(showLimitError ||
showDuplicateError ||
nameController.text.trim().isEmpty)
(widget.showLimitError ||
widget.showDuplicateError ||
_showNameError)
? Colors.red
: const Color(0xFF7C6DED),
width: 1.5,
@@ -141,19 +155,19 @@ class AccountEditorPanel extends ConsumerWidget {
const SizedBox(width: 8),
Expanded(
child: GestureDetector(
onTap: onCurrencyDropdownToggle,
onTap: widget.onCurrencyDropdownToggle,
child: Container(
height: double.infinity,
decoration: BoxDecoration(
color: Theme.of(
dashboardContext,
widget.dashboardContext,
).colorScheme.onSurface.withOpacity(0.05),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: showCurrencyDropdown
color: widget.showCurrencyDropdown
? const Color(0xFF7C6DED)
: Theme.of(
dashboardContext,
widget.dashboardContext,
).colorScheme.onSurface.withOpacity(0.15),
width: 1.5,
),
@@ -162,17 +176,17 @@ class AccountEditorPanel extends ConsumerWidget {
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
selectedCurrency == 'BYN'
widget.selectedCurrency == 'BYN'
? BynSign(
fontSize: 15,
color: Theme.of(
dashboardContext,
widget.dashboardContext,
).colorScheme.onSurface,
)
: Text(
kDisplayCurrencies
.firstWhere(
(c) => c.$1 == selectedCurrency,
(c) => c.$1 == widget.selectedCurrency,
)
.$2,
style: const TextStyle(
@@ -182,12 +196,12 @@ class AccountEditorPanel extends ConsumerWidget {
),
const SizedBox(width: 4),
Icon(
showCurrencyDropdown
widget.showCurrencyDropdown
? Icons.arrow_drop_up
: Icons.arrow_drop_down,
size: 20,
color: Theme.of(
dashboardContext,
widget.dashboardContext,
).colorScheme.onSurface.withOpacity(0.6),
),
],
@@ -145,15 +145,12 @@ class BalanceCardState extends ConsumerState<BalanceCard>
.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: () {
@@ -174,7 +171,7 @@ class BalanceCardState extends ConsumerState<BalanceCard>
..rotateY(_tiltY * 0.42),
child: Container(
width: double.infinity,
height: 220,
height: 180,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
gradient: _buildGradient(primary, secondary, gradientType),
@@ -66,7 +66,7 @@ class _BalanceCardCarouselState extends ConsumerState<BalanceCardCarousel> {
return Column(
children: [
SizedBox(
height: 230,
height: 190,
child: OverflowBox(
maxWidth: MediaQuery.of(context).size.width,
child: PageView.builder(
@@ -75,7 +75,7 @@ class _BalanceCardCarouselState extends ConsumerState<BalanceCardCarousel> {
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<BalanceCardCarousel> {
);
final txs =
ref.watch(transactionsProvider).valueOrNull ?? [];
ref.watch(transactionsProvider).value ?? [];
final accountTxs = txs
.where((t) => t.accountId == account.id)
.toList();
@@ -154,14 +154,14 @@ class _BalanceCardCarouselState extends ConsumerState<BalanceCardCarousel> {
);
},
loading: () => const SizedBox(
height: 220,
height: 180,
child: Center(child: CircularProgressIndicator()),
),
error: (error, stack) {
return Column(
children: [
SizedBox(
height: 220,
height: 180,
child: BalanceCard(
balance: widget.balance,
currencyInfo: widget.currencyInfo,
@@ -198,7 +198,7 @@ class AddAccountCard extends StatelessWidget {
painter: _DashedBorderPainter(),
child: Container(
width: double.infinity,
height: 205,
height: 165,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface.withOpacity(0.4),
borderRadius: BorderRadius.circular(20),
@@ -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),
),
],
),
@@ -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),
);
}
}
@@ -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,
);
+61 -67
View File
@@ -11,18 +11,20 @@ import '../../shared/utils/currency_utils.dart';
import '../../shared/providers/amount_format_provider.dart';
import '../dashboard/provider.dart';
final budgetProvider = StateNotifierProvider<BudgetNotifier, double?>((ref) {
final budgetProvider = NotifierProvider<BudgetNotifier, double?>(
BudgetNotifier.new,
);
class BudgetNotifier extends Notifier<double?> {
@override
double? build() {
final storage = ref.watch(storageServiceProvider);
return BudgetNotifier(storage.loadBudget(), storage);
});
class BudgetNotifier extends StateNotifier<double?> {
final dynamic _storage;
BudgetNotifier(super.initialBudget, this._storage);
return storage.loadBudget();
}
Future<void> 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<String, CurrencyInfo> currencyMap = {
'RUB': CurrencyInfo('', 'RUB'),
};
class CurrencyNotifier extends StateNotifier<CurrencyInfo> {
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<CurrencyInfo> {
@override
CurrencyInfo build() {
final prefs = ref.watch(sharedPreferencesProvider);
final code = prefs.getString('currency_code') ?? 'USD';
return currencyMap[code] ?? currencyMap['USD']!;
}
Future<void> 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<CurrencyNotifier, CurrencyInfo>((
ref,
) {
final currencyProvider = NotifierProvider<CurrencyNotifier, CurrencyInfo>(
CurrencyNotifier.new,
);
class ThemeModeNotifier extends Notifier<ThemeMode> {
@override
ThemeMode build() {
final prefs = ref.watch(sharedPreferencesProvider);
return CurrencyNotifier(prefs);
});
class ThemeModeNotifier extends StateNotifier<ThemeMode> {
final SharedPreferences _prefs;
ThemeModeNotifier(this._prefs) : super(ThemeMode.system) {
_load();
}
void _load() {
final saved = _prefs.getString('theme_mode');
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<void> 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<ThemeModeNotifier, ThemeMode>((
ref,
) {
final prefs = ref.watch(sharedPreferencesProvider);
return ThemeModeNotifier(prefs);
});
final themeProvider = NotifierProvider<ThemeModeNotifier, ThemeMode>(
ThemeModeNotifier.new,
);
enum CardTextColorMode { white, adaptive, black }
class CardTextColorNotifier extends StateNotifier<CardTextColorMode> {
class CardTextColorNotifier extends Notifier<CardTextColorMode> {
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<CardTextColorMode> {
}
void set(CardTextColorMode mode) {
final prefs = ref.read(sharedPreferencesProvider);
state = mode;
_prefs.setString(_key, mode.name);
prefs.setString(_key, mode.name);
}
}
final cardTextColorProvider =
StateNotifierProvider<CardTextColorNotifier, CardTextColorMode>((ref) {
final prefs = ref.watch(sharedPreferencesProvider);
return CardTextColorNotifier(prefs);
});
NotifierProvider<CardTextColorNotifier, CardTextColorMode>(
CardTextColorNotifier.new,
);
final exchangeRateServiceProvider = Provider<ExchangeRateService>((ref) {
final prefs = ref.watch(sharedPreferencesProvider);
@@ -143,15 +135,15 @@ final ratesInitProvider = FutureProvider<void>((ref) async {
await ref.read(exchangeRateServiceProvider).fetchRates();
});
final hapticEnabledProvider = StateNotifierProvider<HapticNotifier, bool>((
ref,
) {
return HapticNotifier();
});
final hapticEnabledProvider = NotifierProvider<HapticNotifier, bool>(
HapticNotifier.new,
);
class HapticNotifier extends StateNotifier<bool> {
HapticNotifier() : super(true) {
class HapticNotifier extends Notifier<bool> {
@override
bool build() {
_load();
return true;
}
Future<void> _load() async {
@@ -166,13 +158,15 @@ class HapticNotifier extends StateNotifier<bool> {
}
final showCurrencyConversionsProvider =
StateNotifierProvider<ShowCurrencyConversionsNotifier, bool>((ref) {
return ShowCurrencyConversionsNotifier();
});
NotifierProvider<ShowCurrencyConversionsNotifier, bool>(
ShowCurrencyConversionsNotifier.new,
);
class ShowCurrencyConversionsNotifier extends StateNotifier<bool> {
ShowCurrencyConversionsNotifier() : super(true) {
class ShowCurrencyConversionsNotifier extends Notifier<bool> {
@override
bool build() {
_load();
return true;
}
Future<void> _load() async {
@@ -198,7 +192,7 @@ class ExportService {
Future<String> exportToCSV() async {
final transactionsAsync = _ref.read(transactionsProvider);
final transactions = transactionsAsync.valueOrNull ?? [];
final transactions = transactionsAsync.value ?? [];
final fmt = _ref.read(amountFormatProvider);
final buffer = StringBuffer();
+181 -1
View File
@@ -2,10 +2,188 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uuid/uuid.dart';
import 'app/app.dart';
import 'core/services/haptic_service.dart';
import 'data/database/app_database.dart';
import 'data/database/app_database.dart' hide Account, Transaction;
import 'data/repositories/account_repository.dart';
import 'data/repositories/transaction_repository.dart';
import 'features/dashboard/provider.dart';
import 'shared/models/account.dart';
import 'shared/models/transaction.dart';
Future<void> seedTestData(AppDatabase database) async {
final accountRepo = AccountRepository(database);
final transactionRepo = TransactionRepository(database);
final existingAccounts = await accountRepo.getAll();
if (existingAccounts.length > 1) {
return;
}
final now = DateTime.now();
final uuid = const Uuid();
final cashId = await accountRepo.add(
Account(
id: 0,
name: 'Cash',
isMain: false,
sortOrder: 1,
currency: 'USD',
createdAt: now,
),
);
final cardId = await accountRepo.add(
Account(
id: 0,
name: 'Card',
isMain: false,
sortOrder: 2,
currency: 'USD',
createdAt: now,
),
);
final savingsId = await accountRepo.add(
Account(
id: 0,
name: 'Savings',
isMain: false,
sortOrder: 3,
currency: 'USD',
createdAt: now,
),
);
final transactions = [
Transaction(
id: uuid.v4(),
amount: 3500.0,
category: 'Salary',
type: TransactionType.income,
date: now.subtract(const Duration(days: 28)),
note: 'Monthly salary',
accountId: cardId,
),
Transaction(
id: uuid.v4(),
amount: 85.50,
category: 'Groceries',
type: TransactionType.expense,
date: now.subtract(const Duration(days: 27)),
note: 'Weekly shopping',
accountId: cardId,
),
Transaction(
id: uuid.v4(),
amount: 45.00,
category: 'Transportation',
type: TransactionType.expense,
date: now.subtract(const Duration(days: 25)),
accountId: cashId,
),
Transaction(
id: uuid.v4(),
amount: 120.00,
category: 'Utilities',
type: TransactionType.expense,
date: now.subtract(const Duration(days: 22)),
note: 'Electricity bill',
accountId: cardId,
),
Transaction(
id: uuid.v4(),
amount: 200.0,
category: 'Freelance',
type: TransactionType.income,
date: now.subtract(const Duration(days: 20)),
note: 'Side project',
accountId: cashId,
),
Transaction(
id: uuid.v4(),
amount: 500.0,
category: 'Savings',
type: TransactionType.expense,
date: now.subtract(const Duration(days: 18)),
note: 'Monthly savings',
accountId: savingsId,
),
Transaction(
id: uuid.v4(),
amount: 65.25,
category: 'Dining',
type: TransactionType.expense,
date: now.subtract(const Duration(days: 15)),
accountId: cardId,
),
Transaction(
id: uuid.v4(),
amount: 30.00,
category: 'Entertainment',
type: TransactionType.expense,
date: now.subtract(const Duration(days: 12)),
note: 'Movie tickets',
accountId: cashId,
),
Transaction(
id: uuid.v4(),
amount: 95.00,
category: 'Groceries',
type: TransactionType.expense,
date: now.subtract(const Duration(days: 10)),
accountId: cardId,
),
Transaction(
id: uuid.v4(),
amount: 150.0,
category: 'Bonus',
type: TransactionType.income,
date: now.subtract(const Duration(days: 8)),
note: 'Performance bonus',
accountId: cardId,
),
Transaction(
id: uuid.v4(),
amount: 75.00,
category: 'Shopping',
type: TransactionType.expense,
date: now.subtract(const Duration(days: 6)),
accountId: cardId,
),
Transaction(
id: uuid.v4(),
amount: 40.00,
category: 'Transportation',
type: TransactionType.expense,
date: now.subtract(const Duration(days: 4)),
accountId: cashId,
),
Transaction(
id: uuid.v4(),
amount: 55.80,
category: 'Dining',
type: TransactionType.expense,
date: now.subtract(const Duration(days: 2)),
note: 'Dinner with friends',
accountId: cardId,
),
Transaction(
id: uuid.v4(),
amount: 100.0,
category: 'Gift',
type: TransactionType.income,
date: now.subtract(const Duration(days: 1)),
accountId: cashId,
),
];
for (final transaction in transactions) {
await transactionRepo.add(transaction);
}
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
@@ -20,6 +198,8 @@ void main() async {
final database = AppDatabase();
await seedTestData(database);
runApp(
ProviderScope(
overrides: [
@@ -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<AmountFormat> {
AmountFormatNotifier() : super(AmountFormat.commasDot) {
class AmountFormatNotifier extends Notifier<AmountFormat> {
@override
AmountFormat build() {
_load();
return AmountFormat.commasDot;
}
void _load() async {
@@ -20,6 +22,6 @@ class AmountFormatNotifier extends StateNotifier<AmountFormat> {
}
}
final amountFormatProvider = StateNotifierProvider<AmountFormatNotifier, AmountFormat>(
(ref) => AmountFormatNotifier(),
final amountFormatProvider = NotifierProvider<AmountFormatNotifier, AmountFormat>(
AmountFormatNotifier.new,
);
@@ -6,10 +6,6 @@
#include "generated_plugin_registrant.h"
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
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);
}
+1 -1
View File
@@ -3,10 +3,10 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
sqlite3_flutter_libs
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST
jni
)
set(PLUGIN_BUNDLED_LIBRARIES)
@@ -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"))
}
+196 -92
View File
@@ -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"
+8 -8
View File
@@ -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:
@@ -7,11 +7,8 @@
#include "generated_plugin_registrant.h"
#include <local_auth_windows/local_auth_plugin.h>
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
LocalAuthPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("LocalAuthPlugin"));
Sqlite3FlutterLibsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin"));
}
+1 -1
View File
@@ -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)