This commit is contained in:
2026-03-22 18:00:44 +03:00
parent 3e4b4db50c
commit 2fe390b068
16 changed files with 3744 additions and 74 deletions
+256
View File
@@ -0,0 +1,256 @@
import 'dart:io';
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p;
import '../../core/utils/result.dart';
import 'tables.dart';
part 'app_database.g.dart';
@DriftDatabase(tables: [Transactions, Categories, Budgets, ExchangeRates])
class AppDatabase extends _$AppDatabase {
AppDatabase() : super(_openConnection());
@override
int get schemaVersion => 1;
// ============================================================================
// TRANSACTIONS
// ============================================================================
/// Get all transactions ordered by date descending
Future<List<dynamic>> getAllTransactions() {
return (select(
transactions,
)..orderBy([(t) => OrderingTerm.desc(t.date)])).get();
}
/// Get transactions by date range
Future<List<dynamic>> getTransactionsByDateRange(
DateTime start,
DateTime end,
) {
return (select(transactions)
..where((t) => t.date.isBiggerOrEqualValue(start))
..where((t) => t.date.isSmallerOrEqualValue(end))
..orderBy([(t) => OrderingTerm.desc(t.date)]))
.get();
}
/// Get transactions by type
Future<List<dynamic>> getTransactionsByType(String type) {
return (select(transactions)
..where((t) => t.type.equals(type))
..orderBy([(t) => OrderingTerm.desc(t.date)]))
.get();
}
/// Get transactions by category
Future<List<dynamic>> getTransactionsByCategory(String category) {
return (select(transactions)
..where((t) => t.category.equals(category))
..orderBy([(t) => OrderingTerm.desc(t.date)]))
.get();
}
/// Search transactions by note or category
Future<List<dynamic>> searchTransactions(String query) {
final lowerQuery = query.toLowerCase();
return (select(transactions)
..where(
(t) =>
t.category.lower().like('%$lowerQuery%') |
t.note.lower().like('%$lowerQuery%'),
)
..orderBy([(t) => OrderingTerm.desc(t.date)]))
.get();
}
/// Get transaction by ID
Future<dynamic> getTransactionById(String id) {
return (select(
transactions,
)..where((t) => t.id.equals(id))).getSingleOrNull();
}
/// Insert transaction
Future<Result<void>> insertTransaction(
TransactionsCompanion transaction,
) async {
return asyncResultOf(() async {
await into(transactions).insert(transaction);
});
}
/// Update transaction
Future<Result<void>> updateTransaction(dynamic transaction) async {
return asyncResultOf(() async {
final companion = transaction as TransactionsCompanion;
final updated = await (update(
transactions,
)..where((t) => t.id.equals(companion.id.value))).write(companion);
if (updated == 0) {
throw Exception('Transaction not found');
}
});
}
/// Delete transaction
Future<Result<void>> deleteTransaction(String id) async {
return asyncResultOf(() async {
final deleted = await (delete(
transactions,
)..where((t) => t.id.equals(id))).go();
if (deleted == 0) {
throw Exception('Transaction not found');
}
});
}
/// Delete all transactions
Future<void> deleteAllTransactions() {
return delete(transactions).go();
}
/// Get recurring transactions that need processing
Future<List<dynamic>> getRecurringTransactions() {
return (select(
transactions,
)..where((t) => t.recurrence.equals('none').not())).get();
}
// ============================================================================
// CATEGORIES
// ============================================================================
/// Get all categories
Future<List<Category>> getAllCategories() {
return select(categories).get();
}
/// Get categories by type
Future<List<Category>> getCategoriesByType(String type) {
return (select(categories)..where((c) => c.type.equals(type))).get();
}
/// Insert category
Future<int> insertCategory(CategoriesCompanion category) {
return into(categories).insert(category);
}
/// Update category
Future<bool> updateCategory(Category category) {
return update(categories).replace(category);
}
/// Delete category
Future<int> deleteCategory(int id) {
return (delete(categories)..where((c) => c.id.equals(id))).go();
}
// ============================================================================
// BUDGETS
// ============================================================================
/// Get budget for month/year
Future<Budget?> getBudget(int month, int year) {
return (select(budgets)
..where((b) => b.month.equals(month) & b.year.equals(year)))
.getSingleOrNull();
}
/// Insert or update budget
Future<void> upsertBudget(BudgetsCompanion budget) {
return into(budgets).insertOnConflictUpdate(budget);
}
/// Delete budget
Future<int> deleteBudget(int id) {
return (delete(budgets)..where((b) => b.id.equals(id))).go();
}
// ============================================================================
// EXCHANGE RATES
// ============================================================================
/// Get exchange rate
Future<ExchangeRate?> getExchangeRate(String from, String to) {
return (select(exchangeRates)
..where((r) => r.fromCurrency.equals(from) & r.toCurrency.equals(to)))
.getSingleOrNull();
}
/// Insert or update exchange rate
Future<void> upsertExchangeRate(ExchangeRatesCompanion rate) {
return into(exchangeRates).insertOnConflictUpdate(rate);
}
/// Get all exchange rates
Future<List<ExchangeRate>> getAllExchangeRates() {
return select(exchangeRates).get();
}
/// Delete old exchange rates (older than 24 hours)
Future<int> deleteOldExchangeRates() {
final yesterday = DateTime.now().subtract(const Duration(hours: 24));
return (delete(
exchangeRates,
)..where((r) => r.updatedAt.isSmallerThanValue(yesterday))).go();
}
// ============================================================================
// STATISTICS & AGGREGATIONS
// ============================================================================
/// Get total balance
Future<double> getTotalBalance() async {
final txs = await getAllTransactions();
return txs.fold<double>(0.0, (sum, tx) {
return tx.type == 'income' ? sum + tx.amount : sum - tx.amount;
});
}
/// Get total income for date range
Future<double> getTotalIncome(DateTime start, DateTime end) async {
final txs = await getTransactionsByDateRange(start, end);
return txs
.where((tx) => tx.type == 'income')
.fold<double>(0.0, (sum, tx) => sum + tx.amount);
}
/// Get total expense for date range
Future<double> getTotalExpense(DateTime start, DateTime end) async {
final txs = await getTransactionsByDateRange(start, end);
return txs
.where((tx) => tx.type == 'expense')
.fold<double>(0.0, (sum, tx) => sum + tx.amount);
}
/// Get category totals for date range
Future<Map<String, double>> getCategoryTotals(
DateTime start,
DateTime end,
String type,
) async {
final txs = await getTransactionsByDateRange(start, end);
final filtered = txs.where((tx) => tx.type == type);
final Map<String, double> totals = {};
for (final tx in filtered) {
totals[tx.category] = (totals[tx.category] ?? 0) + tx.amount;
}
return totals;
}
}
LazyDatabase _openConnection() {
return LazyDatabase(() async {
final dbFolder = await getApplicationDocumentsDirectory();
final file = File(p.join(dbFolder.path, 'casha.db'));
return NativeDatabase(file);
});
}
File diff suppressed because it is too large Load Diff
+49
View File
@@ -0,0 +1,49 @@
import 'package:drift/drift.dart';
/// Transactions table
class Transactions extends Table {
TextColumn get id => text()();
RealColumn get amount => real()();
TextColumn get category => text()();
TextColumn get type => text()(); // 'income' or 'expense'
DateTimeColumn get date => dateTime()();
TextColumn get note => text().nullable()();
TextColumn get recurrence => text().withDefault(const Constant('none'))();
DateTimeColumn get lastOccurrence => dateTime().nullable()();
TextColumn get currency => text().withDefault(const Constant('\$'))();
TextColumn get currencyCode => text().withDefault(const Constant('USD'))();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
@override
Set<Column> get primaryKey => {id};
}
/// Categories table for custom categories
class Categories extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get name => text().withLength(min: 1, max: 50)();
TextColumn get type => text()(); // 'income' or 'expense'
TextColumn get icon => text().nullable()();
TextColumn get color => text().nullable()();
BoolColumn get isDefault => boolean().withDefault(const Constant(false))();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
}
/// Budgets table for monthly budgets
class Budgets extends Table {
IntColumn get id => integer().autoIncrement()();
RealColumn get amount => real()();
TextColumn get categoryId => text().nullable()();
IntColumn get month => integer()();
IntColumn get year => integer()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
}
/// Exchange rates cache
class ExchangeRates extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get fromCurrency => text()();
TextColumn get toCurrency => text()();
RealColumn get rate => real()();
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
}
@@ -0,0 +1,195 @@
import 'package:drift/drift.dart';
import '../../core/utils/result.dart';
import '../../shared/models/transaction.dart' as model;
import '../database/app_database.dart';
class TransactionRepository {
final AppDatabase _db;
TransactionRepository(this._db);
/// Get all transactions
Future<Result<List<model.Transaction>>> getAll() async {
return asyncResultOf(() async {
final transactions = await _db.getAllTransactions();
return transactions.map<model.Transaction>(_toModel).toList();
});
}
/// Get transactions by date range
Future<Result<List<model.Transaction>>> getByDateRange(
DateTime start,
DateTime end,
) async {
return asyncResultOf(() async {
final transactions = await _db.getTransactionsByDateRange(start, end);
return transactions.map<model.Transaction>(_toModel).toList();
});
}
/// Get transactions by type
Future<Result<List<model.Transaction>>> getByType(
model.TransactionType type,
) async {
return asyncResultOf(() async {
final transactions = await _db.getTransactionsByType(type.name);
return transactions.map<model.Transaction>(_toModel).toList();
});
}
/// Search transactions
Future<Result<List<model.Transaction>>> search(String query) async {
return asyncResultOf(() async {
final transactions = await _db.searchTransactions(query);
return transactions.map<model.Transaction>(_toModel).toList();
});
}
/// Get transaction by ID
Future<Result<model.Transaction?>> getById(String id) async {
return asyncResultOf(() async {
final transaction = await _db.getTransactionById(id);
return transaction != null ? _toModel(transaction) : null;
});
}
/// Add transaction
Future<Result<void>> add(model.Transaction transaction) async {
return asyncResultOf(() async {
final companion = _toCompanion(transaction);
final result = await _db.insertTransaction(companion);
if (result.isFailure) {
throw Exception(result.errorOrNull);
}
});
}
/// Update transaction
Future<Result<void>> update(model.Transaction transaction) async {
return asyncResultOf(() async {
final dbTransaction = _toDbModel(transaction);
final result = await _db.updateTransaction(dbTransaction);
if (result.isFailure) {
throw Exception(result.errorOrNull);
}
});
}
/// Delete transaction
Future<Result<void>> delete(String id) async {
return _db.deleteTransaction(id);
}
/// Delete all transactions
Future<Result<void>> deleteAll() async {
return asyncResultOf(() async {
await _db.deleteAllTransactions();
});
}
/// Get recurring transactions
Future<Result<List<model.Transaction>>> getRecurring() async {
return asyncResultOf(() async {
final transactions = await _db.getRecurringTransactions();
return transactions.map<model.Transaction>(_toModel).toList();
});
}
/// Get total balance
Future<Result<double>> getTotalBalance() async {
return asyncResultOf(() async {
return await _db.getTotalBalance();
});
}
/// Get total income for date range
Future<Result<double>> getTotalIncome(DateTime start, DateTime end) async {
return asyncResultOf(() async {
return await _db.getTotalIncome(start, end);
});
}
/// Get total expense for date range
Future<Result<double>> getTotalExpense(DateTime start, DateTime end) async {
return asyncResultOf(() async {
return await _db.getTotalExpense(start, end);
});
}
/// Get category totals
Future<Result<Map<String, double>>> getCategoryTotals(
DateTime start,
DateTime end,
model.TransactionType type,
) async {
return asyncResultOf(() async {
return await _db.getCategoryTotals(start, end, type.name);
});
}
// ============================================================================
// CONVERTERS
// ============================================================================
/// Convert database model to app model
model.Transaction _toModel(dynamic dbTransaction) {
return model.Transaction(
id: dbTransaction.id as String,
amount: dbTransaction.amount as double,
category: dbTransaction.category as String,
type: (dbTransaction.type as String) == 'income'
? model.TransactionType.income
: model.TransactionType.expense,
date: dbTransaction.date as DateTime,
note: dbTransaction.note as String?,
recurrence: _parseRecurrence(dbTransaction.recurrence as String),
lastOccurrence: dbTransaction.lastOccurrence as DateTime?,
currency: dbTransaction.currency as String,
currencyCode: dbTransaction.currencyCode as String,
);
}
/// Convert app model to database model
dynamic _toDbModel(model.Transaction transaction) {
// This will be replaced with proper TransactionData after code generation
return TransactionsCompanion(
id: Value(transaction.id),
amount: Value(transaction.amount),
category: Value(transaction.category),
type: Value(transaction.type.name),
date: Value(transaction.date),
note: Value(transaction.note),
recurrence: Value(transaction.recurrence.name),
lastOccurrence: Value(transaction.lastOccurrence),
currency: Value(transaction.currency),
currencyCode: Value(transaction.currencyCode),
createdAt: Value(DateTime.now()),
);
}
/// Convert app model to companion for insert
TransactionsCompanion _toCompanion(model.Transaction transaction) {
return TransactionsCompanion(
id: Value(transaction.id),
amount: Value(transaction.amount),
category: Value(transaction.category),
type: Value(transaction.type.name),
date: Value(transaction.date),
note: Value(transaction.note),
recurrence: Value(transaction.recurrence.name),
lastOccurrence: Value(transaction.lastOccurrence),
currency: Value(transaction.currency),
currencyCode: Value(transaction.currencyCode),
);
}
/// Parse recurrence type
model.RecurrenceType _parseRecurrence(String recurrence) {
return model.RecurrenceType.values.firstWhere(
(e) => e.name == recurrence,
orElse: () => model.RecurrenceType.none,
);
}
}
+12 -9
View File
@@ -3,37 +3,40 @@ import '../../shared/models/transaction.dart';
import '../dashboard/provider.dart';
final categoryExpenseProvider = Provider<Map<String, double>>((ref) {
final txs = ref.watch(transactionsProvider)
.where((t) => t.type == TransactionType.expense);
final txsAsync = ref.watch(transactionsProvider);
final txs = txsAsync.valueOrNull ?? [];
final filtered = txs.where((t) => t.type == TransactionType.expense);
final map = <String, double>{};
for (final t in txs) {
for (final t in filtered) {
map[t.category] = (map[t.category] ?? 0) + t.amount;
}
return map;
});
final categoryIncomeProvider = Provider<Map<String, double>>((ref) {
final txs = ref.watch(transactionsProvider)
.where((t) => t.type == TransactionType.income);
final txsAsync = ref.watch(transactionsProvider);
final txs = txsAsync.valueOrNull ?? [];
final filtered = txs.where((t) => t.type == TransactionType.income);
final map = <String, double>{};
for (final t in txs) {
for (final t in filtered) {
map[t.category] = (map[t.category] ?? 0) + t.amount;
}
return map;
});
final monthlyBreakdownProvider = Provider<List<MonthlyData>>((ref) {
final txs = ref.watch(transactionsProvider)
.where((t) => t.type == TransactionType.expense);
final txsAsync = ref.watch(transactionsProvider);
final txs = txsAsync.valueOrNull ?? [];
final filtered = txs.where((t) => t.type == TransactionType.expense);
final now = DateTime.now();
final months = <MonthlyData>[];
for (var i = 5; i >= 0; i--) {
final month = DateTime(now.year, now.month - i, 1);
final total = txs
final total = filtered
.where((t) => t.date.year == month.year && t.date.month == month.month)
.fold(0.0, (sum, t) => sum + t.amount);
months.add(MonthlyData(month: month, amount: total));
+83 -46
View File
@@ -3,6 +3,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../core/services/card_color_service.dart';
import '../../core/utils/result.dart';
import '../../data/database/app_database.dart' as db;
import '../../data/repositories/transaction_repository.dart';
import '../../shared/models/transaction.dart';
import '../../shared/services/storage_service.dart';
import '../settings/provider.dart';
@@ -11,53 +13,86 @@ final sharedPreferencesProvider = Provider<SharedPreferences>((ref) {
throw UnimplementedError('Override in main');
});
final appDatabaseProvider = Provider<db.AppDatabase>((ref) {
return db.AppDatabase();
});
final transactionRepositoryProvider = Provider<TransactionRepository>((ref) {
final db = ref.watch(appDatabaseProvider);
return TransactionRepository(db);
});
final storageServiceProvider = Provider<StorageService>((ref) {
return StorageService(ref.watch(sharedPreferencesProvider));
});
final transactionsProvider =
StateNotifierProvider<TransactionsNotifier, List<Transaction>>((ref) {
final storage = ref.watch(storageServiceProvider);
return TransactionsNotifier(storage);
StateNotifierProvider<TransactionsNotifier, AsyncValue<List<Transaction>>>((
ref,
) {
final repository = ref.watch(transactionRepositoryProvider);
return TransactionsNotifier(repository);
});
class TransactionsNotifier extends StateNotifier<List<Transaction>> {
final StorageService _storage;
class TransactionsNotifier
extends StateNotifier<AsyncValue<List<Transaction>>> {
final TransactionRepository _repository;
TransactionsNotifier(this._storage)
: super(_storage.loadTransactionsUnsafe());
TransactionsNotifier(this._repository) : super(const AsyncValue.loading()) {
_load();
}
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 _storage.addTransaction(transaction);
return result.onSuccess((_) {
state = _storage.loadTransactionsUnsafe();
});
final result = await _repository.add(transaction);
if (result.isSuccess) {
await _load();
}
return result;
}
Future<Result<void>> update(Transaction transaction) async {
final result = await _storage.updateTransaction(transaction);
return result.onSuccess((_) {
state = _storage.loadTransactionsUnsafe();
});
final result = await _repository.update(transaction);
if (result.isSuccess) {
await _load();
}
return result;
}
Future<Result<void>> delete(String id) async {
final result = await _storage.deleteTransaction(id);
return result.onSuccess((_) {
state = _storage.loadTransactionsUnsafe();
});
final result = await _repository.delete(id);
if (result.isSuccess) {
await _load();
}
return result;
}
void restore(Transaction transaction) {
state = [...state, transaction];
_storage.addTransaction(transaction);
Future<void> restore(Transaction transaction) async {
await _repository.add(transaction);
await _load();
}
void clearAll() {
state = [];
SharedPreferences.getInstance().then(
(prefs) => prefs.remove('transactions'),
);
Future<void> clearAll() async {
await _repository.deleteAll();
state = const AsyncValue.data([]);
}
Future<void> refresh() async {
await _load();
}
}
@@ -76,7 +111,8 @@ final timeFilterProvider = StateProvider<TimeFilter>(
);
final totalBalanceProvider = Provider<double>((ref) {
final txs = ref.watch(transactionsProvider);
final txsAsync = ref.watch(transactionsProvider);
final txs = txsAsync.valueOrNull ?? [];
final exchangeService = ref.watch(exchangeRateServiceProvider);
final targetCurrency = ref.watch(currencyProvider).code;
@@ -91,26 +127,26 @@ final totalBalanceProvider = Provider<double>((ref) {
});
final totalIncomeProvider = Provider<double>((ref) {
final txs = ref
.watch(transactionsProvider)
.where((t) => t.type == TransactionType.income);
final txsAsync = ref.watch(transactionsProvider);
final txs = txsAsync.valueOrNull ?? [];
final filtered = txs.where((t) => t.type == TransactionType.income);
final exchangeService = ref.watch(exchangeRateServiceProvider);
final targetCurrency = ref.watch(currencyProvider).code;
return txs.fold(0.0, (sum, t) {
return filtered.fold(0.0, (sum, t) {
return sum +
exchangeService.convert(t.amount, t.currencyCode, targetCurrency);
});
});
final totalExpenseProvider = Provider<double>((ref) {
final txs = ref
.watch(transactionsProvider)
.where((t) => t.type == TransactionType.expense);
final txsAsync = ref.watch(transactionsProvider);
final txs = txsAsync.valueOrNull ?? [];
final filtered = txs.where((t) => t.type == TransactionType.expense);
final exchangeService = ref.watch(exchangeRateServiceProvider);
final targetCurrency = ref.watch(currencyProvider).code;
return txs.fold(0.0, (sum, t) {
return filtered.fold(0.0, (sum, t) {
return sum +
exchangeService.convert(t.amount, t.currencyCode, targetCurrency);
});
@@ -118,25 +154,26 @@ final totalExpenseProvider = Provider<double>((ref) {
final currentMonthExpenseProvider = Provider<double>((ref) {
final now = DateTime.now();
final txs = ref
.watch(transactionsProvider)
.where(
(t) =>
t.type == TransactionType.expense &&
t.date.year == now.year &&
t.date.month == now.month,
);
final txsAsync = ref.watch(transactionsProvider);
final txs = txsAsync.valueOrNull ?? [];
final filtered = txs.where(
(t) =>
t.type == TransactionType.expense &&
t.date.year == now.year &&
t.date.month == now.month,
);
final exchangeService = ref.watch(exchangeRateServiceProvider);
final targetCurrency = ref.watch(currencyProvider).code;
return txs.fold(0.0, (sum, t) {
return filtered.fold(0.0, (sum, t) {
return sum +
exchangeService.convert(t.amount, t.currencyCode, targetCurrency);
});
});
final filteredTransactionsProvider = Provider<List<Transaction>>((ref) {
final txs = ref.watch(transactionsProvider);
final txsAsync = ref.watch(transactionsProvider);
final txs = txsAsync.valueOrNull ?? [];
final query = ref.watch(searchQueryProvider).toLowerCase();
final typeFilter = ref.watch(transactionFilterProvider);
final timeFilter = ref.watch(timeFilterProvider);
+22 -15
View File
@@ -26,7 +26,11 @@ class BudgetNotifier extends StateNotifier<double?> {
state = budget;
}
void onCurrencyChanged(String oldCode, String newCode, ExchangeRateService rates) {
void onCurrencyChanged(
String oldCode,
String newCode,
ExchangeRateService rates,
) {
if (state == null) return;
final converted = rates.convert(state!, oldCode, newCode);
setBudget(converted);
@@ -64,12 +68,12 @@ class CurrencyNotifier extends StateNotifier<CurrencyInfo> {
}
}
final currencyProvider = StateNotifierProvider<CurrencyNotifier, CurrencyInfo>(
(ref) {
final prefs = ref.watch(sharedPreferencesProvider);
return CurrencyNotifier(prefs);
},
);
final currencyProvider = StateNotifierProvider<CurrencyNotifier, CurrencyInfo>((
ref,
) {
final prefs = ref.watch(sharedPreferencesProvider);
return CurrencyNotifier(prefs);
});
class ThemeModeNotifier extends StateNotifier<ThemeMode> {
final SharedPreferences _prefs;
@@ -95,12 +99,12 @@ class ThemeModeNotifier extends StateNotifier<ThemeMode> {
}
}
final themeProvider = StateNotifierProvider<ThemeModeNotifier, ThemeMode>(
(ref) {
final prefs = ref.watch(sharedPreferencesProvider);
return ThemeModeNotifier(prefs);
},
);
final themeProvider = StateNotifierProvider<ThemeModeNotifier, ThemeMode>((
ref,
) {
final prefs = ref.watch(sharedPreferencesProvider);
return ThemeModeNotifier(prefs);
});
final exchangeRateServiceProvider = Provider<ExchangeRateService>((ref) {
final prefs = ref.watch(sharedPreferencesProvider);
@@ -111,7 +115,9 @@ final ratesInitProvider = FutureProvider<void>((ref) async {
await ref.read(exchangeRateServiceProvider).fetchRates();
});
final hapticEnabledProvider = StateNotifierProvider<HapticNotifier, bool>((ref) {
final hapticEnabledProvider = StateNotifierProvider<HapticNotifier, bool>((
ref,
) {
return HapticNotifier();
});
@@ -141,7 +147,8 @@ class ExportService {
ExportService(this._ref);
Future<String> exportToCSV() async {
final transactions = _ref.read(transactionsProvider);
final transactionsAsync = _ref.read(transactionsProvider);
final transactions = transactionsAsync.valueOrNull ?? [];
final currency = _ref.read(currencyProvider);
final fmt = _ref.read(amountFormatProvider);
+8 -3
View File
@@ -1,15 +1,15 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'app/app.dart';
import 'core/services/haptic_service.dart';
import 'data/database/app_database.dart';
import 'features/dashboard/provider.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await initializeDateFormatting('en_US', null);
await initializeDateFormatting('ru_RU', null);
await initializeDateFormatting('en', null);
@@ -18,9 +18,14 @@ void main() async {
final prefs = await SharedPreferences.getInstance();
await HapticService.init();
final database = AppDatabase();
runApp(
ProviderScope(
overrides: [sharedPreferencesProvider.overrideWithValue(prefs)],
overrides: [
sharedPreferencesProvider.overrideWithValue(prefs),
appDatabaseProvider.overrideWithValue(database),
],
child: const App(),
),
);