update
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 57 KiB |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground>
|
||||
<inset
|
||||
android:drawable="@drawable/ic_launcher_foreground"
|
||||
android:inset="16%" />
|
||||
</foreground>
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 24 KiB |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#0F0F14</color>
|
||||
</resources>
|
||||
|
After Width: | Height: | Size: 60 KiB |
@@ -431,7 +431,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
@@ -488,7 +488,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
|
||||
@@ -1,122 +1 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-20x20@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-20x20@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-40x40@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-40x40@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-60x60@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-60x60@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-20x20@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-20x20@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-29x29@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-29x29@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-40x40@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-40x40@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-76x76@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-76x76@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "83.5x83.5",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-83.5x83.5@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "1024x1024",
|
||||
"idiom" : "ios-marketing",
|
||||
"filename" : "Icon-App-1024x1024@1x.png",
|
||||
"scale" : "1x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
{"images":[{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@3x.png","scale":"3x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"Icon-App-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"Icon-App-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 216 KiB |
|
Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 704 B After Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 586 B After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 762 B After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 20 KiB |
@@ -11,7 +11,6 @@ class App extends ConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final themeMode = ref.watch(themeProvider);
|
||||
|
||||
// Trigger exchange rate fetch on app start
|
||||
ref.watch(ratesInitProvider);
|
||||
|
||||
return MaterialApp.router(
|
||||
|
||||
@@ -211,5 +211,4 @@ class AppTheme {
|
||||
}
|
||||
}
|
||||
|
||||
// Keep for backward compatibility
|
||||
ThemeData buildAppTheme() => AppTheme.darkTheme;
|
||||
|
||||
@@ -12,7 +12,6 @@ class AppColors {
|
||||
static const divider = Color(0xFF2A2A38);
|
||||
static const warning = Color(0xFFFFB74D);
|
||||
|
||||
// Light theme colors
|
||||
static const lightBackground = Color(0xFFF5F5F7);
|
||||
static const lightSurface = Color(0xFFFFFFFF);
|
||||
static const lightTextPrimary = Color(0xFF1A1A24);
|
||||
@@ -96,7 +95,6 @@ extension AmountFormatExt on AmountFormat {
|
||||
String format(double amount) {
|
||||
switch (this) {
|
||||
case AmountFormat.commasDot:
|
||||
// groups of 3 with commas, dot decimal
|
||||
final parts = amount.toStringAsFixed(2).split('.');
|
||||
final intPart = parts[0].replaceAllMapped(
|
||||
RegExp(r'(\d)(?=(\d{3})+$)'), (m) => '${m[1]},');
|
||||
|
||||
@@ -79,7 +79,6 @@ class AddTransactionNotifier extends StateNotifier<AddTransactionState> {
|
||||
void setCategory(String v) => state = state.copyWith(category: v);
|
||||
|
||||
void setType(TransactionType v) {
|
||||
// Reset category to first item of new type
|
||||
final newCategory = AppCategories.forType(v).first;
|
||||
state = state.copyWith(type: v, category: newCategory);
|
||||
}
|
||||
@@ -102,7 +101,6 @@ final addTransactionProvider = StateNotifierProvider.autoDispose
|
||||
(ref, initial) => AddTransactionNotifier(initial),
|
||||
);
|
||||
|
||||
// Reactive categories based on selected type
|
||||
final availableCategoriesProvider =
|
||||
Provider.autoDispose.family<List<String>, Transaction?>((ref, initial) {
|
||||
final type = ref.watch(addTransactionProvider(initial).select((s) => s.type));
|
||||
|
||||
@@ -40,7 +40,6 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen> {
|
||||
_amountController.text = widget.initial!.amount.toString();
|
||||
_noteController.text = widget.initial!.note ?? '';
|
||||
} else {
|
||||
// Set default currency from global provider after first frame
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final curr = ref.read(currencyProvider);
|
||||
ref.read(addTransactionProvider(null).notifier).setCurrency(curr.symbol, curr.code);
|
||||
@@ -161,7 +160,6 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen> {
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(20),
|
||||
children: [
|
||||
// Type toggle
|
||||
_TypeToggle(
|
||||
selected: state.type,
|
||||
onChanged: (t) =>
|
||||
@@ -169,7 +167,6 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen> {
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Amount
|
||||
_SectionLabel('Amount'),
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
@@ -231,7 +228,6 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen> {
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Currency
|
||||
Text(
|
||||
'Currency',
|
||||
style: TextStyle(
|
||||
@@ -247,7 +243,6 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen> {
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Category
|
||||
_SectionLabel('Category'),
|
||||
const SizedBox(height: 8),
|
||||
_CategoryPicker(
|
||||
@@ -258,7 +253,6 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen> {
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Date
|
||||
_SectionLabel('Date'),
|
||||
const SizedBox(height: 8),
|
||||
InkWell(
|
||||
@@ -288,7 +282,6 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen> {
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Note
|
||||
_SectionLabel('Note (optional)'),
|
||||
const SizedBox(height: 8),
|
||||
TextFormField(
|
||||
|
||||
@@ -13,7 +13,6 @@ final categoryExpenseProvider = Provider<Map<String, double>>((ref) {
|
||||
return map;
|
||||
});
|
||||
|
||||
// Monthly breakdown for last 6 months
|
||||
final monthlyBreakdownProvider = Provider<List<MonthlyData>>((ref) {
|
||||
final txs = ref.watch(transactionsProvider)
|
||||
.where((t) => t.type == TransactionType.expense);
|
||||
|
||||
@@ -29,7 +29,6 @@ class _CategoriesScreenState extends ConsumerState<CategoriesScreen> {
|
||||
final total = data.values.fold(0.0, (a, b) => a + b);
|
||||
final currencyInfo = ref.watch(currencyProvider);
|
||||
|
||||
// Sort categories by amount descending
|
||||
final sortedEntries = data.entries.toList()
|
||||
..sort((a, b) => b.value.compareTo(a.value));
|
||||
|
||||
|
||||
@@ -45,12 +45,10 @@ class TransactionsNotifier extends StateNotifier<List<Transaction>> {
|
||||
|
||||
void clearAll() {
|
||||
state = [];
|
||||
// also clear from SharedPreferences:
|
||||
SharedPreferences.getInstance().then((prefs) => prefs.remove('transactions'));
|
||||
}
|
||||
}
|
||||
|
||||
// Search and filter state
|
||||
final searchQueryProvider = StateProvider<String>((ref) => '');
|
||||
|
||||
enum TransactionFilter { all, income, expense }
|
||||
@@ -58,7 +56,6 @@ enum TransactionFilter { all, income, expense }
|
||||
final transactionFilterProvider =
|
||||
StateProvider<TransactionFilter>((ref) => TransactionFilter.all);
|
||||
|
||||
// Converted balance providers (convert all transactions to selected currency)
|
||||
final totalBalanceProvider = Provider<double>((ref) {
|
||||
final txs = ref.watch(transactionsProvider);
|
||||
final exchangeService = ref.watch(exchangeRateServiceProvider);
|
||||
@@ -111,14 +108,12 @@ final filteredTransactionsProvider = Provider<List<Transaction>>((ref) {
|
||||
|
||||
var filtered = txs;
|
||||
|
||||
// Apply type filter
|
||||
if (filter == TransactionFilter.income) {
|
||||
filtered = filtered.where((t) => t.type == TransactionType.income).toList();
|
||||
} else if (filter == TransactionFilter.expense) {
|
||||
filtered = filtered.where((t) => t.type == TransactionType.expense).toList();
|
||||
}
|
||||
|
||||
// Apply search query
|
||||
if (query.isNotEmpty) {
|
||||
filtered = filtered.where((t) {
|
||||
final matchesCategory = t.category.toLowerCase().contains(query);
|
||||
@@ -127,7 +122,6 @@ final filteredTransactionsProvider = Provider<List<Transaction>>((ref) {
|
||||
}).toList();
|
||||
}
|
||||
|
||||
// Sort by date descending
|
||||
filtered.sort((a, b) => b.date.compareTo(a.date));
|
||||
return filtered;
|
||||
});
|
||||
|
||||
@@ -12,7 +12,6 @@ import '../../shared/providers/amount_format_provider.dart';
|
||||
import '../settings/provider.dart';
|
||||
import 'provider.dart';
|
||||
|
||||
// Helper for balance card only - hides .00 decimals
|
||||
String _smartBalance(double amount, AmountFormat fmt, String symbol) {
|
||||
const spaceAfter = {'Br'};
|
||||
final sep = spaceAfter.contains(symbol) ? ' ' : '';
|
||||
@@ -20,7 +19,6 @@ String _smartBalance(double amount, AmountFormat fmt, String symbol) {
|
||||
|
||||
String formatted;
|
||||
if (isWhole) {
|
||||
// format the integer, then manually remove .00
|
||||
formatted = fmt.format(amount);
|
||||
if (formatted.endsWith('.00')) {
|
||||
formatted = formatted.substring(0, formatted.length - 3);
|
||||
|
||||
@@ -32,7 +32,6 @@ class BudgetNotifier extends StateNotifier<double?> {
|
||||
}
|
||||
}
|
||||
|
||||
// Currency info: symbol and code
|
||||
class CurrencyInfo {
|
||||
final String symbol;
|
||||
final String code;
|
||||
@@ -95,7 +94,6 @@ final themeProvider = StateNotifierProvider<ThemeModeNotifier, ThemeMode>(
|
||||
(ref) => ThemeModeNotifier(),
|
||||
);
|
||||
|
||||
// Exchange rate service
|
||||
final exchangeRateServiceProvider = Provider<ExchangeRateService>((ref) {
|
||||
final prefs = ref.watch(sharedPreferencesProvider);
|
||||
return ExchangeRateService(prefs);
|
||||
@@ -119,11 +117,9 @@ class ExportService {
|
||||
final currency = _ref.read(currencyProvider);
|
||||
final fmt = _ref.read(amountFormatProvider);
|
||||
|
||||
// CSV header
|
||||
final buffer = StringBuffer();
|
||||
buffer.writeln('Date,Type,Category,Amount,Currency,Note');
|
||||
|
||||
// CSV rows
|
||||
for (final tx in transactions) {
|
||||
final date = DateFormat('yyyy-MM-dd').format(tx.date);
|
||||
final type = tx.type.name;
|
||||
@@ -133,7 +129,6 @@ class ExportService {
|
||||
buffer.writeln('$date,$type,$category,$amount,${tx.currencyCode},$note');
|
||||
}
|
||||
|
||||
// Save to Downloads
|
||||
final directory = await getApplicationDocumentsDirectory();
|
||||
final timestamp = DateFormat('yyyyMMdd_HHmmss').format(DateTime.now());
|
||||
final file = File('${directory.path}/transactions_$timestamp.csv');
|
||||
|
||||
@@ -52,7 +52,6 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
|
||||
}
|
||||
|
||||
void _confirmClearData(BuildContext context, WidgetRef ref) {
|
||||
// First confirmation
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
@@ -66,7 +65,6 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(ctx);
|
||||
// Second confirmation
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx2) => AlertDialog(
|
||||
@@ -109,7 +107,6 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
|
||||
final fmt = ref.watch(amountFormatProvider);
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
// Update currency format when it changes
|
||||
_currencyFmt = NumberFormat.currency(symbol: currencyInfo.symbol, decimalDigits: 2);
|
||||
|
||||
return Scaffold(
|
||||
@@ -139,7 +136,6 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
|
||||
padding: const EdgeInsets.fromLTRB(20, 16, 20, 20),
|
||||
children: [
|
||||
|
||||
// Theme Toggle
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
@@ -194,7 +190,6 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Budget Setting
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
@@ -310,7 +305,6 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Amount Format Selector
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
@@ -393,7 +387,6 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Currency Selector
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
@@ -486,7 +479,6 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Danger Zone
|
||||
Text(
|
||||
'Danger Zone',
|
||||
style: TextStyle(
|
||||
|
||||
@@ -40,7 +40,6 @@ class ExchangeRateService {
|
||||
|
||||
Future<void> fetchRates() async {
|
||||
try {
|
||||
// Try primary URL
|
||||
final response = await http
|
||||
.get(Uri.parse(_primaryUrl))
|
||||
.timeout(const Duration(seconds: 10));
|
||||
@@ -60,11 +59,9 @@ class ExchangeRateService {
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Primary failed, try fallback
|
||||
}
|
||||
|
||||
try {
|
||||
// Try fallback URL
|
||||
final response = await http
|
||||
.get(Uri.parse(_fallbackUrl))
|
||||
.timeout(const Duration(seconds: 10));
|
||||
@@ -84,10 +81,8 @@ class ExchangeRateService {
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Both failed, use cached or fallback
|
||||
}
|
||||
|
||||
// If both failed and no cache, use fallback
|
||||
if (_rates.isEmpty) {
|
||||
_rates = Map.from(_fallbackRates);
|
||||
}
|
||||
@@ -104,7 +99,6 @@ class ExchangeRateService {
|
||||
final fromRate = currentRates[from] ?? 1.0;
|
||||
final toRate = currentRates[to] ?? 1.0;
|
||||
|
||||
// Convert to USD first, then to target currency
|
||||
final amountInUsd = amount / fromRate;
|
||||
return amountInUsd * toRate;
|
||||
}
|
||||
|
||||
@@ -70,14 +70,13 @@ class StorageService {
|
||||
}
|
||||
|
||||
bool loadThemeMode() {
|
||||
return _prefs.getBool(_themeKey) ?? true; // default dark
|
||||
return _prefs.getBool(_themeKey) ?? true;
|
||||
}
|
||||
|
||||
Future<void> saveThemeMode(bool isDark) async {
|
||||
await _prefs.setBool(_themeKey, isDark);
|
||||
}
|
||||
|
||||
// Process recurring transactions
|
||||
Future<void> processRecurringTransactions() async {
|
||||
final transactions = loadTransactions();
|
||||
final now = DateTime.now();
|
||||
@@ -115,7 +114,6 @@ class StorageService {
|
||||
}
|
||||
|
||||
if (shouldCreate) {
|
||||
// Create new occurrence
|
||||
final newTx = Transaction(
|
||||
id: _uuid.v4(),
|
||||
amount: tx.amount,
|
||||
@@ -128,7 +126,6 @@ class StorageService {
|
||||
);
|
||||
transactions.add(newTx);
|
||||
|
||||
// Update original transaction's lastOccurrence
|
||||
final index = transactions.indexWhere((t) => t.id == tx.id);
|
||||
if (index != -1) {
|
||||
transactions[index] = tx.copyWith(lastOccurrence: today);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import '../../core/constants.dart';
|
||||
|
||||
String formatAmount(String symbol, double amount, AmountFormat fmt) {
|
||||
// Symbols that need a space after them (prefix symbols like Br, ₽ etc.)
|
||||
const spaceAfter = {'Br'};
|
||||
final formatted = fmt.format(amount);
|
||||
final sep = spaceAfter.contains(symbol) ? ' ' : '';
|
||||
|
||||
@@ -1,6 +1,22 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.9"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.7.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -25,6 +41,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: checked_yaml
|
||||
sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.4"
|
||||
cli_util:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cli_util
|
||||
sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.2"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -118,6 +150,14 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_launcher_icons:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_launcher_icons
|
||||
sha256: "10f13781741a2e3972126fae08393d3c4e01fa4cd7473326b94b72cf594195e7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.14.4"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
@@ -192,6 +232,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.2"
|
||||
image:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: image
|
||||
sha256: f9881ff4998044947ec38d098bc7c8316ae1186fa786eddffdb867b9bc94dfce
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.8.0"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -200,6 +248,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.19.0"
|
||||
json_annotation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: json_annotation
|
||||
sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.11.0"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -336,6 +392,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: petitparser
|
||||
sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.2"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -352,6 +416,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
posix:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: posix
|
||||
sha256: "185ef7606574f789b40f289c233efa52e96dead518aed988e040a10737febb07"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.5.0"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -549,6 +621,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xml
|
||||
sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.6.1"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -21,10 +21,22 @@ dependencies:
|
||||
http: ^1.2.0
|
||||
sensors_plus: ^6.1.0
|
||||
|
||||
flutter_launcher_icons:
|
||||
android: true
|
||||
ios: true
|
||||
image_path: "assets/icon/icon.png"
|
||||
min_sdk_android: 21
|
||||
web:
|
||||
generate: true
|
||||
image_path: "assets/icon/icon.png"
|
||||
adaptive_icon_background: "#0F0F14"
|
||||
adaptive_icon_foreground: "assets/icon/icon.png"
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^6.0.0
|
||||
flutter_launcher_icons: ^0.14.1
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
|
||||
|
Before Width: | Height: | Size: 917 B After Width: | Height: | Size: 829 B |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 68 KiB |
@@ -32,4 +32,4 @@
|
||||
"purpose": "maskable"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||