commit
79cf29a0d3
27 changed files with 1297 additions and 710 deletions
2
.flutter
2
.flutter
|
@ -1 +1 @@
|
||||||
Subproject commit fed06b31d938f7620ea7417295b8d8d19cf7cf1d
|
Subproject commit c1df7d07ac60336309bae9dd2d48e7cb8844ec98
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -42,3 +42,4 @@ app.*.map.json
|
||||||
/android/app/debug
|
/android/app/debug
|
||||||
/android/app/profile
|
/android/app/profile
|
||||||
/android/app/release
|
/android/app/release
|
||||||
|
reports
|
|
@ -1,3 +1,9 @@
|
||||||
|
|
||||||
|
# 1.0.0-alpha+5
|
||||||
|
- Add tests
|
||||||
|
- Add searching through entries to homepage
|
||||||
|
- Replace `PlatformDialog` with `AlertDialog.adaptive`
|
||||||
|
- Add import/export of wallets
|
||||||
# 1.0.0-alpha+4
|
# 1.0.0-alpha+4
|
||||||
- Fix OCR downloads
|
- Fix OCR downloads
|
||||||
# 1.0.0-alpha+3
|
# 1.0.0-alpha+3
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# prasule
|
# prasule
|
||||||
[![Codemagic build status](https://api.codemagic.io/apps/64faee78aae8c48abc70dbc6/64faee78aae8c48abc70dbc5/status_badge.svg)](https://codemagic.io/apps/64faee78aae8c48abc70dbc6/64faee78aae8c48abc70dbc5/latest_build) [![Bug issue count](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fgit.mnau.xyz%2Fapi%2Fv1%2Frepos%2Fhernik%2Fprasule%2Fissues%3Flabels%3DKind%2FBug&query=%24.length&logo=forgejo&label=bug%20issues&color=red)](https://git.mnau.xyz/hernik/prasule/issues?q=&type=all&sort=&state=open&labels=144&milestone=0&project=0&assignee=0&poster=0) [![wakatime](https://wakatime.com/badge/user/17178fab-a33c-430f-a764-7b3f26c7b966/project/bf1f40b0-c8c0-4f72-8ad6-c861ecdcc90c.svg)](https://wakatime.com/badge/user/17178fab-a33c-430f-a764-7b3f26c7b966/project/bf1f40b0-c8c0-4f72-8ad6-c861ecdcc90c) [![Translation status](https://hosted.weblate.org/widget/prasule/svg-badge.svg)](https://hosted.weblate.org/engage/prasule/)
|
[![Codemagic build status](https://api.codemagic.io/apps/64faee78aae8c48abc70dbc6/64faee78aae8c48abc70dbc5/status_badge.svg)](https://codemagic.io/apps/64faee78aae8c48abc70dbc6/64faee78aae8c48abc70dbc5/latest_build) [![Bug issue count](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fgit.mnau.xyz%2Fapi%2Fv1%2Frepos%2Fhernik%2Fprasule%2Fissues%3Flabels%3DKind%2FBug&query=%24.length&logo=forgejo&label=bug%20issues&color=red)](https://git.mnau.xyz/hernik/prasule/issues?q=&type=all&sort=&state=open&labels=144&milestone=0&project=0&assignee=0&poster=0) [![wakatime](https://wakatime.com/badge/user/17178fab-a33c-430f-a764-7b3f26c7b966/project/bf1f40b0-c8c0-4f72-8ad6-c861ecdcc90c.svg)](https://wakatime.com/badge/user/17178fab-a33c-430f-a764-7b3f26c7b966/project/bf1f40b0-c8c0-4f72-8ad6-c861ecdcc90c) [![Translation status](https://hosted.weblate.org/widget/prasule/svg-badge.svg)](https://hosted.weblate.org/engage/prasule/) [![Please don't upload to GitHub](https://nogithub.codeberg.page/badge.svg)](https://nogithub.codeberg.page)
|
||||||
|
|
||||||
Expense manager
|
Expense manager
|
||||||
|
|
||||||
|
|
|
@ -76,4 +76,6 @@ flutter {
|
||||||
source '../..'
|
source '../..'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {}
|
dependencies {
|
||||||
|
implementation 'com.android.support:multidex:1.0.3'
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
<application
|
<application
|
||||||
android:label="Prašule"
|
android:label="Prašule"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:enableOnBackInvokedCallback="true">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
|
|
@ -1,16 +1,3 @@
|
||||||
buildscript {
|
|
||||||
ext.kotlin_version = '1.7.10'
|
|
||||||
repositories {
|
|
||||||
google()
|
|
||||||
mavenCentral()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
classpath 'com.android.tools.build:gradle:7.3.0'
|
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
|
|
|
@ -10,11 +10,17 @@ pluginManagement {
|
||||||
|
|
||||||
includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
|
includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
|
||||||
|
|
||||||
plugins {
|
repositories {
|
||||||
id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||||
|
id "com.android.application" version "7.3.0" apply false
|
||||||
|
id "org.jetbrains.kotlin.android" version "1.7.10" apply false
|
||||||
|
}
|
||||||
|
|
||||||
include ":app"
|
include ":app"
|
||||||
|
|
||||||
apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle"
|
|
||||||
|
|
140
integration_test/app_test.dart
Normal file
140
integration_test/app_test.dart
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:integration_test/integration_test.dart';
|
||||||
|
import 'package:logger/logger.dart';
|
||||||
|
import 'package:prasule/api/category.dart';
|
||||||
|
import 'package:prasule/api/entry_data.dart';
|
||||||
|
import 'package:prasule/api/wallet.dart';
|
||||||
|
import 'package:prasule/api/wallet_entry.dart';
|
||||||
|
import 'package:prasule/api/wallet_manager.dart';
|
||||||
|
|
||||||
|
import 'package:prasule/main.dart';
|
||||||
|
import 'package:prasule/pw/platformfield.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
final logger = Logger();
|
||||||
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
group("Test classes and API", () {
|
||||||
|
test("Test wallet operations", () async {
|
||||||
|
expect(
|
||||||
|
(await WalletManager.listWallets()).length,
|
||||||
|
equals(0),
|
||||||
|
); // check that there are no other wallets
|
||||||
|
await WalletManager.saveWallet(Wallet.empty);
|
||||||
|
var w = (await WalletManager.listWallets()).firstOrNull;
|
||||||
|
expect(w, isNotNull); // check that the wallet was successfully saved
|
||||||
|
expect(w!.categories.length, equals(1));
|
||||||
|
w.categories.add(
|
||||||
|
WalletCategory(
|
||||||
|
name: "Testing",
|
||||||
|
id: w.nextCategoryId,
|
||||||
|
icon: Icons.abc,
|
||||||
|
color: Colors.orange,
|
||||||
|
),
|
||||||
|
); // create test category
|
||||||
|
final testId = w.nextId;
|
||||||
|
w.entries.add(
|
||||||
|
WalletSingleEntry(
|
||||||
|
data: EntryData(amount: 200, name: "Automated"),
|
||||||
|
type: EntryType.expense,
|
||||||
|
date: DateTime.now(),
|
||||||
|
category: w.categories.last,
|
||||||
|
id: w.nextId,
|
||||||
|
),
|
||||||
|
); // create test entry
|
||||||
|
await WalletManager.saveWallet(w); // save again
|
||||||
|
w = await WalletManager.loadWallet(w.name); // try loading manually
|
||||||
|
final e = w.entries.where((element) => element.id == testId).firstOrNull;
|
||||||
|
expect(
|
||||||
|
e,
|
||||||
|
isNotNull,
|
||||||
|
); // check that the entry was successfully created
|
||||||
|
expect(
|
||||||
|
w.categories.where((element) => element.id == e!.category.id).length,
|
||||||
|
equals(1),
|
||||||
|
); // check that the category exists too
|
||||||
|
|
||||||
|
await WalletManager.deleteWallet(w);
|
||||||
|
expect(
|
||||||
|
(await WalletManager.listWallets()).length,
|
||||||
|
equals(0),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group("Test app functionality:", () {
|
||||||
|
testWidgets('First-time setup', (WidgetTester tester) async {
|
||||||
|
// Delete all data
|
||||||
|
await WalletManager.deleteAllData();
|
||||||
|
// Build our app and trigger a frame.
|
||||||
|
await tester.pumpWidget(
|
||||||
|
const MyApp(
|
||||||
|
locale: Locale('en', 'US'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
logger.i("Looking for welcome header");
|
||||||
|
expect(find.text('Welcome!'), findsOneWidget);
|
||||||
|
|
||||||
|
// Tap "Next" button
|
||||||
|
await tester.tap(find.text("Next"));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
logger.i("Next view, looking for name+balance fields");
|
||||||
|
|
||||||
|
final firstFields = find.byType(PlatformField);
|
||||||
|
|
||||||
|
expect(firstFields, findsExactly(2));
|
||||||
|
|
||||||
|
logger.i("Entering text");
|
||||||
|
await tester.enterText(find.byType(PlatformField).first, "Debugging");
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.enterText(find.byType(PlatformField).last, "100");
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
// Tap "Next" button
|
||||||
|
await tester.tap(find.text("Next"));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
// Tap "Finish" button
|
||||||
|
await tester.tap(find.text("Finish"));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
find.byWidgetPredicate(
|
||||||
|
(widget) =>
|
||||||
|
widget is DropdownButton &&
|
||||||
|
((widget as DropdownButton<int>).value ?? -1) == 0,
|
||||||
|
),
|
||||||
|
findsOne,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Test rendering of entries', (WidgetTester tester) async {
|
||||||
|
// Delete all data
|
||||||
|
await WalletManager.deleteAllData();
|
||||||
|
expect((await WalletManager.listWallets()).length, equals(0));
|
||||||
|
|
||||||
|
// Create test data
|
||||||
|
final w = Wallet.empty;
|
||||||
|
await w.createTestEntries();
|
||||||
|
expect((await WalletManager.listWallets()).length, equals(1));
|
||||||
|
|
||||||
|
// Build our app and trigger a frame.
|
||||||
|
await tester.pumpWidget(
|
||||||
|
const MyApp(
|
||||||
|
locale: Locale('en', 'US'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
// TODO: better test
|
||||||
|
|
||||||
|
expect(
|
||||||
|
find.byType(ListTile, skipOffstage: false),
|
||||||
|
findsAtLeast(10),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,24 +0,0 @@
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
import 'package:integration_test/integration_test.dart';
|
|
||||||
|
|
||||||
import 'package:prasule/main.dart';
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
|
||||||
group("Test Setup screen:", () {
|
|
||||||
testWidgets('First-time setup', (WidgetTester tester) async {
|
|
||||||
// Build our app and trigger a frame.
|
|
||||||
await tester.pumpWidget(const MyApp());
|
|
||||||
|
|
||||||
expect(find.text('Welcome!'), findsOneWidget);
|
|
||||||
|
|
||||||
// // Tap the '+' icon and trigger a frame.
|
|
||||||
// await tester.tap(find.byIcon(Icons.add));
|
|
||||||
// await tester.pump();
|
|
||||||
|
|
||||||
// // Verify that our counter has incremented.
|
|
||||||
// expect(find.text('0'), findsNothing);
|
|
||||||
// expect(find.text('1'), findsOneWidget);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,7 +1,11 @@
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:currency_picker/currency_picker.dart';
|
import 'package:currency_picker/currency_picker.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:prasule/api/category.dart';
|
import 'package:prasule/api/category.dart';
|
||||||
|
import 'package:prasule/api/entry_data.dart';
|
||||||
import 'package:prasule/api/recurring_entry.dart';
|
import 'package:prasule/api/recurring_entry.dart';
|
||||||
import 'package:prasule/api/wallet_entry.dart';
|
import 'package:prasule/api/wallet_entry.dart';
|
||||||
import 'package:prasule/api/wallet_manager.dart';
|
import 'package:prasule/api/wallet_manager.dart';
|
||||||
|
@ -156,9 +160,44 @@ class Wallet {
|
||||||
await WalletManager.saveWallet(this);
|
await WalletManager.saveWallet(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the current balance
|
||||||
|
///
|
||||||
|
/// Basically just takes *starterBalance* and adds all the entries to it
|
||||||
|
double calculateCurrentBalance() {
|
||||||
|
var toAdd = 0.0;
|
||||||
|
for (final e in entries) {
|
||||||
|
toAdd += (e.type == EntryType.income) ? e.data.amount : -e.data.amount;
|
||||||
|
}
|
||||||
|
return starterBalance + toAdd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the amount that was made/lost during a month
|
||||||
|
double calculateMonthStatus(int month, int year) {
|
||||||
|
var f = 0.0;
|
||||||
|
for (final e in entries.where(
|
||||||
|
(element) => element.date.year == year && element.date.month == month,
|
||||||
|
)) {
|
||||||
|
f += (e.type == EntryType.income) ? e.data.amount : -e.data.amount;
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
/// Empty wallet used for placeholders
|
/// Empty wallet used for placeholders
|
||||||
static final Wallet empty = Wallet(
|
static final Wallet empty = Wallet(
|
||||||
name: "Empty",
|
name: "Empty",
|
||||||
|
entries: [],
|
||||||
|
recurringEntries: [],
|
||||||
|
categories: [
|
||||||
|
WalletCategory(
|
||||||
|
name: "Default",
|
||||||
|
id: 0,
|
||||||
|
icon: IconData(
|
||||||
|
Icons.payments.codePoint,
|
||||||
|
fontFamily: 'MaterialIcons',
|
||||||
|
),
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
],
|
||||||
currency: Currency.from(
|
currency: Currency.from(
|
||||||
json: {
|
json: {
|
||||||
"code": "USD",
|
"code": "USD",
|
||||||
|
@ -175,4 +214,66 @@ class Wallet {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Creates test data used for debugging purposes
|
||||||
|
Future<void> createTestEntries() async {
|
||||||
|
entries.clear();
|
||||||
|
recurringEntries.clear();
|
||||||
|
final random = Random();
|
||||||
|
for (var i = 0; i < 30; i++) {
|
||||||
|
entries.add(
|
||||||
|
WalletSingleEntry(
|
||||||
|
data: EntryData(
|
||||||
|
name: "Test Entry #${i + 1}",
|
||||||
|
amount: random.nextInt(20000).toDouble(),
|
||||||
|
),
|
||||||
|
type: (random.nextInt(3) > 0) ? EntryType.expense : EntryType.income,
|
||||||
|
date: DateTime(
|
||||||
|
2023,
|
||||||
|
random.nextInt(12) + 1,
|
||||||
|
random.nextInt(28) + 1,
|
||||||
|
),
|
||||||
|
category: categories[random.nextInt(categories.length)],
|
||||||
|
id: nextId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.d(
|
||||||
|
"Created ${entries.length} regular entries",
|
||||||
|
);
|
||||||
|
|
||||||
|
for (var i = 0; i < 3; i++) {
|
||||||
|
final type = random.nextInt(3);
|
||||||
|
recurringEntries.add(
|
||||||
|
RecurringWalletEntry(
|
||||||
|
data: EntryData(
|
||||||
|
name: "Recurring Entry #${i + 1}",
|
||||||
|
amount: random.nextInt(20000).toDouble(),
|
||||||
|
),
|
||||||
|
type: (random.nextInt(3) > 0) ? EntryType.expense : EntryType.income,
|
||||||
|
date: DateTime(
|
||||||
|
2023,
|
||||||
|
random.nextInt(12) + 1,
|
||||||
|
random.nextInt(28) + 1,
|
||||||
|
),
|
||||||
|
category: categories[random.nextInt(categories.length)],
|
||||||
|
id: nextId,
|
||||||
|
lastRunDate: DateTime.now().subtract(
|
||||||
|
Duration(
|
||||||
|
days: (type > 0) ? 3 : 3 * 31,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
recurType: (type > 0) ? RecurType.day : RecurType.month,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.d(
|
||||||
|
"Created ${recurringEntries.length} recurring entries",
|
||||||
|
);
|
||||||
|
|
||||||
|
// save and reload
|
||||||
|
await WalletManager.saveWallet(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:archive/archive_io.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter_file_dialog/flutter_file_dialog.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:prasule/api/wallet.dart';
|
import 'package:prasule/api/wallet.dart';
|
||||||
import 'package:prasule/main.dart';
|
import 'package:prasule/main.dart';
|
||||||
|
@ -24,10 +28,143 @@ class WalletManager {
|
||||||
// TODO: do something with unreadable wallets
|
// TODO: do something with unreadable wallets
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.i(wallets.length);
|
|
||||||
return wallets;
|
return wallets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Deletes all [Wallet]s
|
||||||
|
static Future<void> deleteAllData() async {
|
||||||
|
final path =
|
||||||
|
Directory("${(await getApplicationDocumentsDirectory()).path}/wallets");
|
||||||
|
if (!path.existsSync()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final entry in path.listSync()) {
|
||||||
|
logger.d("Deleting ${entry.path}");
|
||||||
|
entry.deleteSync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a ZIP archive from all wallets
|
||||||
|
static Future<void> exportAllWallets() async {
|
||||||
|
if (kIsWeb) {
|
||||||
|
// TODO
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final archive = Archive();
|
||||||
|
for (final w in Directory(
|
||||||
|
"${(await getApplicationDocumentsDirectory()).path}/wallets",
|
||||||
|
).listSync()) {
|
||||||
|
if (w is! File) continue;
|
||||||
|
logger.i("Zipping ${w.path.split("/").last}");
|
||||||
|
final wf = w;
|
||||||
|
archive.addFile(
|
||||||
|
ArchiveFile.stream(
|
||||||
|
wf.path.split("/").last,
|
||||||
|
wf.lengthSync(),
|
||||||
|
InputFileStream(wf.path),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!await FlutterFileDialog.isPickDirectorySupported()) {
|
||||||
|
File(
|
||||||
|
"${(await getApplicationDocumentsDirectory()).path}/export_${DateFormat("dd_MM_yyyy").format(DateTime.now())}.zip",
|
||||||
|
).writeAsBytesSync(ZipEncoder().encode(archive) ?? []);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final dir = await FlutterFileDialog.pickDirectory();
|
||||||
|
if (dir == null) return;
|
||||||
|
await FlutterFileDialog.saveFileToDirectory(
|
||||||
|
directory: dir,
|
||||||
|
data: Uint8List.fromList(ZipEncoder().encode(archive) ?? []),
|
||||||
|
fileName: "export_${DateFormat("dd_MM_yyyy").format(DateTime.now())}.zip",
|
||||||
|
mimeType: "application/zip",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Exports a single [Wallet]
|
||||||
|
static Future<void> exportWallet({Wallet? wallet, String? name}) async {
|
||||||
|
if (wallet == null && name == null) {
|
||||||
|
throw Exception("You need to specify either a wallet or a name");
|
||||||
|
}
|
||||||
|
final n = name ?? wallet!.name;
|
||||||
|
|
||||||
|
if (!await FlutterFileDialog.isPickDirectorySupported()) {
|
||||||
|
File("${(await getApplicationDocumentsDirectory()).path}/wallets/$n")
|
||||||
|
.copySync(
|
||||||
|
"${await getApplicationDocumentsDirectory()}/export_${n.replaceAll(RegExp('[|\\?*<":>+\[\]/\' ]+'), '_')}_${DateFormat("dd_MM_yyyy").format(DateTime.now())}.json",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final dir = await FlutterFileDialog.pickDirectory();
|
||||||
|
if (dir == null) return;
|
||||||
|
await FlutterFileDialog.saveFileToDirectory(
|
||||||
|
directory: dir,
|
||||||
|
data:
|
||||||
|
File("${(await getApplicationDocumentsDirectory()).path}/wallets/$n")
|
||||||
|
.readAsBytesSync(),
|
||||||
|
fileName:
|
||||||
|
"export_${n.replaceAll(RegExp('[|\\?*<":>+\[\]/\' ]+'), '_')}_${DateFormat("dd_MM_yyyy").format(DateTime.now())}.json",
|
||||||
|
mimeType: "application/json",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Import a single wallet
|
||||||
|
static Future<void> importWallet({String? data}) async {
|
||||||
|
var d = data ?? "";
|
||||||
|
if (data == null) {
|
||||||
|
final filePath = await FlutterFileDialog.pickFile(
|
||||||
|
params: const OpenFileDialogParams(
|
||||||
|
mimeTypesFilter: ["application/json"],
|
||||||
|
fileExtensionsFilter: ["json"],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (filePath == null) return;
|
||||||
|
d = File(filePath).readAsStringSync();
|
||||||
|
}
|
||||||
|
final w = Wallet.fromJson(jsonDecode(d) as Map<String, dynamic>);
|
||||||
|
if (await WalletManager.exists(w.name)) {
|
||||||
|
throw Exception("Wallet already exists!");
|
||||||
|
}
|
||||||
|
await WalletManager.saveWallet(
|
||||||
|
w,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Imports wallets from a ZIP archive
|
||||||
|
static Future<void> importArchive() async {
|
||||||
|
final filePath = await FlutterFileDialog.pickFile(
|
||||||
|
params: const OpenFileDialogParams(
|
||||||
|
mimeTypesFilter: ["application/zip"],
|
||||||
|
fileExtensionsFilter: ["zip"],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (filePath == null) return;
|
||||||
|
if (kIsWeb) {
|
||||||
|
// TODO
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final temp = Directory("${(await getTemporaryDirectory()).path}/data");
|
||||||
|
if (temp.existsSync()) {
|
||||||
|
temp.deleteSync();
|
||||||
|
}
|
||||||
|
temp.createSync(recursive: true);
|
||||||
|
final archive = ZipDecoder().decodeBuffer(InputFileStream(filePath));
|
||||||
|
for (final file in archive.files) {
|
||||||
|
if (!file.isFile) {
|
||||||
|
logger.d(file.name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
file.writeContent(OutputFileStream("${temp.path}/${file.name}"));
|
||||||
|
}
|
||||||
|
for (final e in temp.listSync()) {
|
||||||
|
logger.d(e.path);
|
||||||
|
if (e is! File) continue;
|
||||||
|
await importWallet(data: e.readAsStringSync());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Loads and returns a single [Wallet] by name
|
/// Loads and returns a single [Wallet] by name
|
||||||
static Future<Wallet> loadWallet(String name) async {
|
static Future<Wallet> loadWallet(String name) async {
|
||||||
final path =
|
final path =
|
||||||
|
@ -63,4 +200,11 @@ class WalletManager {
|
||||||
Directory("${(await getApplicationDocumentsDirectory()).path}/wallets");
|
Directory("${(await getApplicationDocumentsDirectory()).path}/wallets");
|
||||||
File("${path.path}/${w.name}").deleteSync();
|
File("${path.path}/${w.name}").deleteSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if the wallet exists
|
||||||
|
static Future<bool> exists(String name) async {
|
||||||
|
return File(
|
||||||
|
"${(await getApplicationDocumentsDirectory()).path}/wallets/$name",
|
||||||
|
).existsSync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,5 +87,22 @@
|
||||||
"dayCounter":"{count, plural, =1{den} few{dny} many{dnů} other{dnů} }",
|
"dayCounter":"{count, plural, =1{den} few{dny} many{dnů} other{dnů} }",
|
||||||
"yearCounter":"{count, plural, =1{rok} few{rok} many{let} other{let} }",
|
"yearCounter":"{count, plural, =1{rok} few{rok} many{let} other{let} }",
|
||||||
"recurEvery":"{count, plural, =1{Opakovat každý} few{Opakovat každé} many{Opakovat každých} other{Opakovat každých}}",
|
"recurEvery":"{count, plural, =1{Opakovat každý} few{Opakovat každé} many{Opakovat každých} other{Opakovat každých}}",
|
||||||
"startingWithDate": "počínaje datem"
|
"startingWithDate": "počínaje datem",
|
||||||
|
"evenMoney":"Váš stav je stejný jako minulý měsíc.",
|
||||||
|
"balanceStatusA": "Váš stav je ",
|
||||||
|
"balanceStatusB": " oproti minulému měsíci.",
|
||||||
|
"searchLabel":"Prohledat záznamy...",
|
||||||
|
"exportSingle":"Exportovat peněženku",
|
||||||
|
"exportSingleDesc":"Uloží vybranou peněženku do složky Stažené",
|
||||||
|
"exportArchive":"Exportovat archiv",
|
||||||
|
"exportArchiveDesc":"Exportuje všechny peněženky do archivu do složky Stažené",
|
||||||
|
"importSingle":"Importovat peněženku",
|
||||||
|
"importSingleDesc":"Importuje jednu peněženku ze souboru",
|
||||||
|
"importArchive":"Importovat archiv",
|
||||||
|
"importArchiveDesc":"Importuje všechny nepoškozené peněženky z archivu",
|
||||||
|
"settingsData":"Data",
|
||||||
|
"selectExportWallet":"Zvolte peněženku k exportování",
|
||||||
|
"exportError":"Při exportování peněženky nastala chyba",
|
||||||
|
"exportCompleted":"Export dokončen",
|
||||||
|
"importCompleted":"Import dokončen"
|
||||||
}
|
}
|
|
@ -203,5 +203,22 @@
|
||||||
"startingWithDate": "starting",
|
"startingWithDate": "starting",
|
||||||
"@startingWithDate":{
|
"@startingWithDate":{
|
||||||
"description": "Shown after 'Recur every X Y', e.g. 'Recur every 2 month starting 20th June 2023'"
|
"description": "Shown after 'Recur every X Y', e.g. 'Recur every 2 month starting 20th June 2023'"
|
||||||
}
|
},
|
||||||
|
"evenMoney":"You're on the same balance as last month.",
|
||||||
|
"balanceStatusA": "Your balance is ",
|
||||||
|
"balanceStatusB": " compared to last month.",
|
||||||
|
"searchLabel":"Search entries...",
|
||||||
|
"exportSingle":"Export a wallet",
|
||||||
|
"exportSingleDesc":"Saves a single wallet into your downloads directory",
|
||||||
|
"exportArchive":"Export archive",
|
||||||
|
"exportArchiveDesc":"Exports all wallets into an archive to your downloads directory",
|
||||||
|
"importSingle":"Import a wallet",
|
||||||
|
"importSingleDesc":"Imports a single wallet from file",
|
||||||
|
"importArchive":"Import archive",
|
||||||
|
"importArchiveDesc":"Imports all valid wallets inside an archive",
|
||||||
|
"settingsData":"Data",
|
||||||
|
"selectExportWallet":"Select a wallet to export",
|
||||||
|
"exportError":"An error occured trying to export wallet",
|
||||||
|
"exportCompleted":"Export completed",
|
||||||
|
"importCompleted":"Import completed"
|
||||||
}
|
}
|
|
@ -29,12 +29,15 @@ final logger = Logger();
|
||||||
/// The application itself
|
/// The application itself
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
/// The application itself
|
/// The application itself
|
||||||
const MyApp({super.key});
|
const MyApp({super.key, this.locale});
|
||||||
|
|
||||||
/// If Material You was applied
|
/// If Material You was applied
|
||||||
///
|
///
|
||||||
/// Used to check if it is supported
|
/// Used to check if it is supported
|
||||||
static bool appliedYou = false;
|
static bool appliedYou = false;
|
||||||
|
|
||||||
|
/// Override locale, used for testing
|
||||||
|
final Locale? locale;
|
||||||
// This widget is the root of your application.
|
// This widget is the root of your application.
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -50,6 +53,7 @@ class MyApp extends StatelessWidget {
|
||||||
...GlobalCupertinoLocalizations.delegates,
|
...GlobalCupertinoLocalizations.delegates,
|
||||||
],
|
],
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
|
locale: locale,
|
||||||
title: 'Prašule',
|
title: 'Prašule',
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
colorScheme: _materialYou
|
colorScheme: _materialYou
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
// ignore_for_file: public_member_api_docs
|
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:prasule/pw/platformwidget.dart';
|
|
||||||
|
|
||||||
/// A [PlatformWidget] implementation of a dialog
|
|
||||||
class PlatformDialog extends PlatformWidget<AlertDialog, CupertinoAlertDialog> {
|
|
||||||
const PlatformDialog(
|
|
||||||
{required this.title, super.key, this.content, this.actions = const [],});
|
|
||||||
final String title;
|
|
||||||
final Widget? content;
|
|
||||||
final List<Widget> actions;
|
|
||||||
|
|
||||||
@override
|
|
||||||
AlertDialog createAndroidWidget(BuildContext context) => AlertDialog(
|
|
||||||
title: Text(title),
|
|
||||||
content:
|
|
||||||
(content != null) ? SingleChildScrollView(child: content) : null,
|
|
||||||
actions: actions,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
CupertinoAlertDialog createIosWidget(BuildContext context) =>
|
|
||||||
CupertinoAlertDialog(
|
|
||||||
title: Text(title),
|
|
||||||
content:
|
|
||||||
(content != null) ? SingleChildScrollView(child: content) : null,
|
|
||||||
actions: actions,
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -21,6 +21,8 @@ class PlatformField extends PlatformWidget<TextField, CupertinoTextField> {
|
||||||
this.textStyle,
|
this.textStyle,
|
||||||
this.textAlign = TextAlign.start,
|
this.textAlign = TextAlign.start,
|
||||||
this.maxLines = 1,
|
this.maxLines = 1,
|
||||||
|
this.focusNode,
|
||||||
|
this.inputBorder = const OutlineInputBorder(),
|
||||||
});
|
});
|
||||||
final TextEditingController? controller;
|
final TextEditingController? controller;
|
||||||
final bool? enabled;
|
final bool? enabled;
|
||||||
|
@ -34,6 +36,8 @@ class PlatformField extends PlatformWidget<TextField, CupertinoTextField> {
|
||||||
final TextStyle? textStyle;
|
final TextStyle? textStyle;
|
||||||
final TextAlign textAlign;
|
final TextAlign textAlign;
|
||||||
final int? maxLines;
|
final int? maxLines;
|
||||||
|
final InputBorder inputBorder;
|
||||||
|
final FocusNode? focusNode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
TextField createAndroidWidget(BuildContext context) => TextField(
|
TextField createAndroidWidget(BuildContext context) => TextField(
|
||||||
|
@ -43,10 +47,11 @@ class PlatformField extends PlatformWidget<TextField, CupertinoTextField> {
|
||||||
obscureText: obscureText,
|
obscureText: obscureText,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: labelText,
|
labelText: labelText,
|
||||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(4)),
|
border: inputBorder,
|
||||||
),
|
),
|
||||||
autocorrect: autocorrect,
|
autocorrect: autocorrect,
|
||||||
keyboardType: keyboardType,
|
keyboardType: keyboardType,
|
||||||
|
focusNode: focusNode,
|
||||||
style: textStyle,
|
style: textStyle,
|
||||||
inputFormatters: inputFormatters,
|
inputFormatters: inputFormatters,
|
||||||
onChanged: onChanged,
|
onChanged: onChanged,
|
||||||
|
@ -66,6 +71,7 @@ class PlatformField extends PlatformWidget<TextField, CupertinoTextField> {
|
||||||
keyboardType: keyboardType,
|
keyboardType: keyboardType,
|
||||||
inputFormatters: inputFormatters,
|
inputFormatters: inputFormatters,
|
||||||
onChanged: onChanged,
|
onChanged: onChanged,
|
||||||
|
focusNode: focusNode,
|
||||||
maxLines: maxLines,
|
maxLines: maxLines,
|
||||||
style: textStyle,
|
style: textStyle,
|
||||||
);
|
);
|
||||||
|
|
|
@ -150,10 +150,10 @@ class ExpensesLineChart extends StatelessWidget {
|
||||||
isStrokeCapRound: true,
|
isStrokeCapRound: true,
|
||||||
dotData: const FlDotData(show: false),
|
dotData: const FlDotData(show: false),
|
||||||
belowBarData: BarAreaData(),
|
belowBarData: BarAreaData(),
|
||||||
color:
|
color: ((MediaQuery.of(context).platformBrightness ==
|
||||||
(MediaQuery.of(context).platformBrightness == Brightness.dark)
|
Brightness.dark)
|
||||||
? Colors.green.shade300
|
? Colors.green.shade300
|
||||||
: Colors.green
|
: Colors.green)
|
||||||
.harmonizeWith(Theme.of(context).colorScheme.primary),
|
.harmonizeWith(Theme.of(context).colorScheme.primary),
|
||||||
spots: List.generate(
|
spots: List.generate(
|
||||||
yearly ? 12 : date.lastDay,
|
yearly ? 12 : date.lastDay,
|
||||||
|
@ -167,10 +167,10 @@ class ExpensesLineChart extends StatelessWidget {
|
||||||
isStrokeCapRound: true,
|
isStrokeCapRound: true,
|
||||||
dotData: const FlDotData(show: false),
|
dotData: const FlDotData(show: false),
|
||||||
belowBarData: BarAreaData(),
|
belowBarData: BarAreaData(),
|
||||||
color:
|
color: ((MediaQuery.of(context).platformBrightness ==
|
||||||
(MediaQuery.of(context).platformBrightness == Brightness.dark)
|
Brightness.dark)
|
||||||
? Colors.red.shade300
|
? Colors.red.shade300
|
||||||
: Colors.red
|
: Colors.red)
|
||||||
.harmonizeWith(Theme.of(context).colorScheme.primary),
|
.harmonizeWith(Theme.of(context).colorScheme.primary),
|
||||||
spots: List.generate(
|
spots: List.generate(
|
||||||
yearly
|
yearly
|
||||||
|
@ -522,7 +522,7 @@ class Indicator extends StatelessWidget {
|
||||||
/// Text shown next to the indicator circle
|
/// Text shown next to the indicator circle
|
||||||
final String text;
|
final String text;
|
||||||
|
|
||||||
/// Style for the text
|
/// Text style of the indicator
|
||||||
final TextStyle textStyle;
|
final TextStyle textStyle;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -1,29 +1,27 @@
|
||||||
// ignore_for_file: inference_failure_on_function_invocation
|
// ignore_for_file: inference_failure_on_function_invocation
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:dynamic_color/dynamic_color.dart';
|
import 'package:dynamic_color/dynamic_color.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_file_dialog/flutter_file_dialog.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||||
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
|
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
|
||||||
import 'package:flutter_tesseract_ocr/flutter_tesseract_ocr.dart';
|
import 'package:flutter_tesseract_ocr/flutter_tesseract_ocr.dart';
|
||||||
import 'package:grouped_list/grouped_list.dart';
|
import 'package:grouped_list/grouped_list.dart';
|
||||||
import 'package:image_picker/image_picker.dart';
|
|
||||||
import 'package:intl/date_symbol_data_local.dart';
|
import 'package:intl/date_symbol_data_local.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:prasule/api/category.dart';
|
import 'package:prasule/api/category.dart';
|
||||||
import 'package:prasule/api/entry_data.dart';
|
import 'package:prasule/api/entry_data.dart';
|
||||||
import 'package:prasule/api/recurring_entry.dart';
|
|
||||||
import 'package:prasule/api/wallet.dart';
|
import 'package:prasule/api/wallet.dart';
|
||||||
import 'package:prasule/api/wallet_entry.dart';
|
import 'package:prasule/api/wallet_entry.dart';
|
||||||
import 'package:prasule/api/wallet_manager.dart';
|
import 'package:prasule/api/wallet_manager.dart';
|
||||||
import 'package:prasule/main.dart';
|
import 'package:prasule/main.dart';
|
||||||
import 'package:prasule/network/tessdata.dart';
|
import 'package:prasule/network/tessdata.dart';
|
||||||
import 'package:prasule/pw/platformbutton.dart';
|
import 'package:prasule/pw/platformbutton.dart';
|
||||||
import 'package:prasule/pw/platformdialog.dart';
|
import 'package:prasule/pw/platformfield.dart';
|
||||||
import 'package:prasule/pw/platformroute.dart';
|
import 'package:prasule/pw/platformroute.dart';
|
||||||
import 'package:prasule/util/drawer.dart';
|
import 'package:prasule/util/drawer.dart';
|
||||||
import 'package:prasule/util/text_color.dart';
|
import 'package:prasule/util/text_color.dart';
|
||||||
|
@ -46,6 +44,9 @@ class _HomeViewState extends State<HomeView> {
|
||||||
List<Wallet> wallets = [];
|
List<Wallet> wallets = [];
|
||||||
DateTime? prevDate;
|
DateTime? prevDate;
|
||||||
late String locale;
|
late String locale;
|
||||||
|
var _searchActive = false;
|
||||||
|
var _filter = "";
|
||||||
|
final searchFocus = FocusNode();
|
||||||
@override
|
@override
|
||||||
void didChangeDependencies() {
|
void didChangeDependencies() {
|
||||||
super.didChangeDependencies();
|
super.didChangeDependencies();
|
||||||
|
@ -75,7 +76,16 @@ class _HomeViewState extends State<HomeView> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return PopScope(
|
||||||
|
canPop: !_searchActive, // don't pop when we just want
|
||||||
|
// to deactivate searchfield
|
||||||
|
onPopInvoked: (b) {
|
||||||
|
if (b) return;
|
||||||
|
_searchActive = false;
|
||||||
|
_filter = "";
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
child: Scaffold(
|
||||||
drawer: makeDrawer(context, 1),
|
drawer: makeDrawer(context, 1),
|
||||||
floatingActionButton: SpeedDial(
|
floatingActionButton: SpeedDial(
|
||||||
icon: Icons.add,
|
icon: Icons.add,
|
||||||
|
@ -88,70 +98,7 @@ class _HomeViewState extends State<HomeView> {
|
||||||
onTap: () {
|
onTap: () {
|
||||||
// debug option to quickly fill a wallet with data
|
// debug option to quickly fill a wallet with data
|
||||||
if (selectedWallet == null) return;
|
if (selectedWallet == null) return;
|
||||||
selectedWallet!.entries.clear();
|
selectedWallet!.createTestEntries().then((_) {
|
||||||
selectedWallet!.recurringEntries.clear();
|
|
||||||
final random = Random();
|
|
||||||
for (var i = 0; i < 30; i++) {
|
|
||||||
selectedWallet!.entries.add(
|
|
||||||
WalletSingleEntry(
|
|
||||||
data: EntryData(
|
|
||||||
name: "Test Entry #${i + 1}",
|
|
||||||
amount: random.nextInt(20000).toDouble(),
|
|
||||||
),
|
|
||||||
type: (random.nextInt(3) > 0)
|
|
||||||
? EntryType.expense
|
|
||||||
: EntryType.income,
|
|
||||||
date: DateTime(
|
|
||||||
2023,
|
|
||||||
random.nextInt(12) + 1,
|
|
||||||
random.nextInt(28) + 1,
|
|
||||||
),
|
|
||||||
category: selectedWallet!.categories[
|
|
||||||
random.nextInt(selectedWallet!.categories.length)],
|
|
||||||
id: selectedWallet!.nextId,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.d(
|
|
||||||
"Created ${selectedWallet!.entries.length} regular entries",
|
|
||||||
);
|
|
||||||
|
|
||||||
for (var i = 0; i < 3; i++) {
|
|
||||||
final type = random.nextInt(3);
|
|
||||||
selectedWallet!.recurringEntries.add(
|
|
||||||
RecurringWalletEntry(
|
|
||||||
data: EntryData(
|
|
||||||
name: "Recurring Entry #${i + 1}",
|
|
||||||
amount: random.nextInt(20000).toDouble(),
|
|
||||||
),
|
|
||||||
type: (random.nextInt(3) > 0)
|
|
||||||
? EntryType.expense
|
|
||||||
: EntryType.income,
|
|
||||||
date: DateTime(
|
|
||||||
2023,
|
|
||||||
random.nextInt(12) + 1,
|
|
||||||
random.nextInt(28) + 1,
|
|
||||||
),
|
|
||||||
category: selectedWallet!.categories[
|
|
||||||
random.nextInt(selectedWallet!.categories.length)],
|
|
||||||
id: selectedWallet!.nextId,
|
|
||||||
lastRunDate: DateTime.now().subtract(
|
|
||||||
Duration(
|
|
||||||
days: (type > 0) ? 3 : 3 * 31,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
recurType: (type > 0) ? RecurType.day : RecurType.month,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.d(
|
|
||||||
"Created ${selectedWallet!.recurringEntries.length} recurring entries",
|
|
||||||
);
|
|
||||||
|
|
||||||
// save and reload
|
|
||||||
WalletManager.saveWallet(selectedWallet!).then((value) {
|
|
||||||
Navigator.of(context).pushReplacement(
|
Navigator.of(context).pushReplacement(
|
||||||
platformRoute(
|
platformRoute(
|
||||||
(p0) => const HomeView(),
|
(p0) => const HomeView(),
|
||||||
|
@ -178,25 +125,38 @@ class _HomeViewState extends State<HomeView> {
|
||||||
SpeedDialChild(
|
SpeedDialChild(
|
||||||
child: const Icon(Icons.camera_alt),
|
child: const Icon(Icons.camera_alt),
|
||||||
label: AppLocalizations.of(context).addCamera,
|
label: AppLocalizations.of(context).addCamera,
|
||||||
onTap: () async {
|
onTap: () {
|
||||||
final picker = ImagePicker();
|
startOcr(SourceType.camera);
|
||||||
final media = await picker.pickImage(source: ImageSource.camera);
|
|
||||||
logger.i(media?.name);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SpeedDialChild(
|
SpeedDialChild(
|
||||||
child: const Icon(Icons.image),
|
child: const Icon(Icons.image),
|
||||||
label: AppLocalizations.of(context).addGallery,
|
label: AppLocalizations.of(context).addGallery,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
startOcr(ImageSource.gallery);
|
startOcr(SourceType.photoLibrary);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: DropdownButton<int>(
|
title: AnimatedCrossFade(
|
||||||
value:
|
duration: const Duration(milliseconds: 500),
|
||||||
(selectedWallet == null) ? -1 : wallets.indexOf(selectedWallet!),
|
crossFadeState: _searchActive
|
||||||
|
? CrossFadeState.showFirst
|
||||||
|
: CrossFadeState.showSecond,
|
||||||
|
firstChild: PlatformField(
|
||||||
|
inputBorder: InputBorder.none,
|
||||||
|
focusNode: searchFocus,
|
||||||
|
onChanged: (e) {
|
||||||
|
_filter = e;
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
labelText: AppLocalizations.of(context).searchLabel,
|
||||||
|
),
|
||||||
|
secondChild: DropdownButton<int>(
|
||||||
|
value: (selectedWallet == null)
|
||||||
|
? -1
|
||||||
|
: wallets.indexOf(selectedWallet!),
|
||||||
items: [
|
items: [
|
||||||
...wallets.map(
|
...wallets.map(
|
||||||
(e) => DropdownMenuItem(
|
(e) => DropdownMenuItem(
|
||||||
|
@ -229,7 +189,17 @@ class _HomeViewState extends State<HomeView> {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
actions: [
|
actions: [
|
||||||
|
if (!_searchActive)
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
_searchActive = true;
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.search),
|
||||||
|
),
|
||||||
|
if (!_searchActive)
|
||||||
PopupMenuButton(
|
PopupMenuButton(
|
||||||
itemBuilder: (context) => [
|
itemBuilder: (context) => [
|
||||||
AppLocalizations.of(context).settings,
|
AppLocalizations.of(context).settings,
|
||||||
|
@ -244,6 +214,7 @@ class _HomeViewState extends State<HomeView> {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.then((value) async {
|
.then((value) async {
|
||||||
|
wallets = await WalletManager.listWallets();
|
||||||
selectedWallet =
|
selectedWallet =
|
||||||
await WalletManager.loadWallet(selectedWallet!.name);
|
await WalletManager.loadWallet(selectedWallet!.name);
|
||||||
});
|
});
|
||||||
|
@ -287,36 +258,141 @@ class _HomeViewState extends State<HomeView> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
: GroupedListView(
|
: Overlay(
|
||||||
groupHeaderBuilder: (element) => Text(
|
initialEntries: [
|
||||||
DateFormat.yMMMM(locale).format(element.date),
|
OverlayEntry(
|
||||||
|
builder: (context) => Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
NumberFormat.compactCurrency(
|
||||||
|
locale: locale,
|
||||||
|
symbol: selectedWallet!.currency.symbol,
|
||||||
|
).format(
|
||||||
|
selectedWallet!.calculateCurrentBalance(),
|
||||||
|
),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 22,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
if (selectedWallet!.calculateMonthStatus(
|
||||||
|
DateTime.now().month,
|
||||||
|
DateTime.now().year,
|
||||||
|
) ==
|
||||||
|
0)
|
||||||
|
Text(AppLocalizations.of(context).evenMoney)
|
||||||
|
else
|
||||||
|
RichText(
|
||||||
|
text: TextSpan(
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: AppLocalizations.of(context)
|
||||||
|
.balanceStatusA,
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: (selectedWallet!
|
||||||
|
.calculateMonthStatus(
|
||||||
|
DateTime.now().month,
|
||||||
|
DateTime.now().year,
|
||||||
|
) >
|
||||||
|
0
|
||||||
|
? ((MediaQuery.of(context)
|
||||||
|
.platformBrightness ==
|
||||||
|
Brightness.dark)
|
||||||
|
? Colors.green.shade300
|
||||||
|
: Colors.green)
|
||||||
|
.harmonizeWith(
|
||||||
|
Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primary,
|
||||||
|
)
|
||||||
|
: ((MediaQuery.of(context)
|
||||||
|
.platformBrightness ==
|
||||||
|
Brightness.dark)
|
||||||
|
? Colors.red.shade300
|
||||||
|
: Colors.red)
|
||||||
|
.harmonizeWith(
|
||||||
|
Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primary,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
text: NumberFormat.compactCurrency(
|
||||||
|
locale: locale,
|
||||||
|
symbol:
|
||||||
|
selectedWallet!.currency.symbol,
|
||||||
|
).format(
|
||||||
|
selectedWallet!
|
||||||
|
.calculateMonthStatus(
|
||||||
|
DateTime.now().month,
|
||||||
|
DateTime.now().year,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
elements: selectedWallet!.entries,
|
),
|
||||||
itemComparator: (a, b) => b.date.compareTo(a.date),
|
TextSpan(
|
||||||
groupBy: (e) => DateFormat.yMMMM(locale).format(e.date),
|
text: AppLocalizations.of(context)
|
||||||
|
.balanceStatusB,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: GroupedListView(
|
||||||
|
groupHeaderBuilder: (element) => Text(
|
||||||
|
DateFormat.yMMMM(locale)
|
||||||
|
.format(element.date),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
elements: selectedWallet!.entries
|
||||||
|
.where((element) =>
|
||||||
|
element.data.name.contains(_filter))
|
||||||
|
.toList(),
|
||||||
|
itemComparator: (a, b) =>
|
||||||
|
b.date.compareTo(a.date),
|
||||||
|
groupBy: (e) =>
|
||||||
|
DateFormat.yMMMM(locale).format(e.date),
|
||||||
groupComparator: (a, b) {
|
groupComparator: (a, b) {
|
||||||
// TODO: better sorting algorithm lol
|
// TODO: better sorting algorithm lol
|
||||||
final yearA = RegExp(r'\d+').firstMatch(a);
|
final yearA =
|
||||||
|
RegExp(r'\d+').firstMatch(a);
|
||||||
if (yearA == null) return 0;
|
if (yearA == null) return 0;
|
||||||
final yearB = RegExp(r'\d+').firstMatch(b);
|
final yearB =
|
||||||
|
RegExp(r'\d+').firstMatch(b);
|
||||||
if (yearB == null) return 0;
|
if (yearB == null) return 0;
|
||||||
final compareYears = int.parse(yearB.group(0)!)
|
final compareYears =
|
||||||
.compareTo(int.parse(yearA.group(0)!));
|
int.parse(yearB.group(0)!).compareTo(
|
||||||
if (compareYears != 0) return compareYears;
|
int.parse(yearA.group(0)!),
|
||||||
|
);
|
||||||
|
if (compareYears != 0) {
|
||||||
|
return compareYears;
|
||||||
|
}
|
||||||
final months = List<String>.generate(
|
final months = List<String>.generate(
|
||||||
12,
|
12,
|
||||||
(index) => DateFormat.MMMM(locale).format(
|
(index) =>
|
||||||
|
DateFormat.MMMM(locale).format(
|
||||||
DateTime(2023, index + 1),
|
DateTime(2023, index + 1),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final monthA = RegExp('[^0-9 ]+').firstMatch(a);
|
final monthA =
|
||||||
|
RegExp('[^0-9 ]+').firstMatch(a);
|
||||||
if (monthA == null) return 0;
|
if (monthA == null) return 0;
|
||||||
final monthB = RegExp('[^0-9 ]+').firstMatch(b);
|
final monthB =
|
||||||
|
RegExp('[^0-9 ]+').firstMatch(b);
|
||||||
if (monthB == null) return 0;
|
if (monthB == null) return 0;
|
||||||
return months.indexOf(monthB.group(0)!).compareTo(
|
return months
|
||||||
|
.indexOf(monthB.group(0)!)
|
||||||
|
.compareTo(
|
||||||
months.indexOf(monthA.group(0)!),
|
months.indexOf(monthA.group(0)!),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -329,7 +405,8 @@ class _HomeViewState extends State<HomeView> {
|
||||||
Navigator.of(context)
|
Navigator.of(context)
|
||||||
.push<WalletSingleEntry>(
|
.push<WalletSingleEntry>(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (c) => CreateSingleEntryView(
|
builder: (c) =>
|
||||||
|
CreateSingleEntryView(
|
||||||
w: selectedWallet!,
|
w: selectedWallet!,
|
||||||
editEntry: element,
|
editEntry: element,
|
||||||
),
|
),
|
||||||
|
@ -337,44 +414,65 @@ class _HomeViewState extends State<HomeView> {
|
||||||
)
|
)
|
||||||
.then(
|
.then(
|
||||||
(editedEntry) {
|
(editedEntry) {
|
||||||
if (editedEntry == null) return;
|
if (editedEntry == null) {
|
||||||
selectedWallet!.entries.remove(element);
|
return;
|
||||||
selectedWallet!.entries.add(editedEntry);
|
}
|
||||||
WalletManager.saveWallet(selectedWallet!);
|
selectedWallet!.entries
|
||||||
|
.remove(element);
|
||||||
|
selectedWallet!.entries
|
||||||
|
.add(editedEntry);
|
||||||
|
WalletManager.saveWallet(
|
||||||
|
selectedWallet!,
|
||||||
|
);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
backgroundColor:
|
backgroundColor: Theme.of(context)
|
||||||
Theme.of(context).colorScheme.secondary,
|
.colorScheme
|
||||||
foregroundColor:
|
.secondary,
|
||||||
Theme.of(context).colorScheme.onSecondary,
|
foregroundColor: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onSecondary,
|
||||||
icon: Icons.edit,
|
icon: Icons.edit,
|
||||||
),
|
),
|
||||||
SlidableAction(
|
SlidableAction(
|
||||||
backgroundColor:
|
backgroundColor: Theme.of(context)
|
||||||
Theme.of(context).colorScheme.error,
|
.colorScheme
|
||||||
foregroundColor:
|
.error,
|
||||||
Theme.of(context).colorScheme.onError,
|
foregroundColor: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.onError,
|
||||||
icon: Icons.delete,
|
icon: Icons.delete,
|
||||||
onPressed: (c) {
|
onPressed: (c) {
|
||||||
showDialog(
|
showAdaptiveDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (cx) => PlatformDialog(
|
builder: (cx) =>
|
||||||
title:
|
AlertDialog.adaptive(
|
||||||
AppLocalizations.of(context).sureDialog,
|
title: Text(
|
||||||
|
AppLocalizations.of(
|
||||||
|
context,
|
||||||
|
).sureDialog,
|
||||||
|
),
|
||||||
content: Text(
|
content: Text(
|
||||||
AppLocalizations.of(context).deleteSure,
|
AppLocalizations.of(context)
|
||||||
|
.deleteSure,
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
PlatformButton(
|
PlatformButton(
|
||||||
text: AppLocalizations.of(context).yes,
|
text: AppLocalizations.of(
|
||||||
|
context,
|
||||||
|
).yes,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
selectedWallet?.entries.removeWhere(
|
selectedWallet?.entries
|
||||||
(e) => e.id == element.id,
|
.removeWhere(
|
||||||
|
(e) =>
|
||||||
|
e.id ==
|
||||||
|
element.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
WalletManager.saveWallet(
|
WalletManager
|
||||||
|
.saveWallet(
|
||||||
selectedWallet!,
|
selectedWallet!,
|
||||||
);
|
);
|
||||||
Navigator.of(cx).pop();
|
Navigator.of(cx).pop();
|
||||||
|
@ -382,7 +480,9 @@ class _HomeViewState extends State<HomeView> {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
PlatformButton(
|
PlatformButton(
|
||||||
text: AppLocalizations.of(context).no,
|
text: AppLocalizations.of(
|
||||||
|
context,
|
||||||
|
).no,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(cx).pop();
|
Navigator.of(cx).pop();
|
||||||
},
|
},
|
||||||
|
@ -397,15 +497,16 @@ class _HomeViewState extends State<HomeView> {
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: Container(
|
leading: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius:
|
||||||
|
BorderRadius.circular(16),
|
||||||
color: element.category.color,
|
color: element.category.color,
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
element.category.icon,
|
element.category.icon,
|
||||||
color:
|
color: element.category.color
|
||||||
element.category.color.calculateTextColor(),
|
.calculateTextColor(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -415,15 +516,19 @@ class _HomeViewState extends State<HomeView> {
|
||||||
children: [
|
children: [
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: NumberFormat.currency(
|
text: NumberFormat.currency(
|
||||||
symbol: selectedWallet!.currency.symbol,
|
symbol: selectedWallet!
|
||||||
|
.currency.symbol,
|
||||||
).format(element.data.amount),
|
).format(element.data.amount),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: (element.type == EntryType.income)
|
color: (element.type ==
|
||||||
|
EntryType.income)
|
||||||
? (MediaQuery.of(context)
|
? (MediaQuery.of(context)
|
||||||
.platformBrightness ==
|
.platformBrightness ==
|
||||||
Brightness.dark)
|
Brightness.dark)
|
||||||
? Colors.green.shade300
|
? Colors
|
||||||
: Colors.green.harmonizeWith(
|
.green.shade300
|
||||||
|
: Colors.green
|
||||||
|
.harmonizeWith(
|
||||||
Theme.of(context)
|
Theme.of(context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.primary,
|
.primary,
|
||||||
|
@ -432,7 +537,8 @@ class _HomeViewState extends State<HomeView> {
|
||||||
.platformBrightness ==
|
.platformBrightness ==
|
||||||
Brightness.dark)
|
Brightness.dark)
|
||||||
? Colors.red.shade300
|
? Colors.red.shade300
|
||||||
: Colors.red.harmonizeWith(
|
: Colors.red
|
||||||
|
.harmonizeWith(
|
||||||
Theme.of(context)
|
Theme.of(context)
|
||||||
.colorScheme
|
.colorScheme
|
||||||
.primary,
|
.primary,
|
||||||
|
@ -456,18 +562,43 @@ class _HomeViewState extends State<HomeView> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
OverlayEntry(
|
||||||
|
builder: (context) => SizedBox(
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
height: MediaQuery.of(context).size.height,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
if (!_searchActive) return;
|
||||||
|
if (!searchFocus.hasFocus) {
|
||||||
|
_searchActive = false;
|
||||||
|
_filter = "";
|
||||||
|
setState(() {});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
searchFocus.unfocus();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> startOcr(ImageSource imgSrc) async {
|
Future<void> startOcr(SourceType sourceType) async {
|
||||||
final availableLanguages = await TessdataApi.getDownloadedData();
|
final availableLanguages = await TessdataApi.getDownloadedData();
|
||||||
if (availableLanguages.isEmpty) {
|
if (availableLanguages.isEmpty) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
await showDialog(
|
await showAdaptiveDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (c) => PlatformDialog(
|
builder: (c) => AlertDialog.adaptive(
|
||||||
title: AppLocalizations.of(context).missingOcr,
|
title: Text(AppLocalizations.of(context).missingOcr),
|
||||||
actions: [
|
actions: [
|
||||||
PlatformButton(
|
PlatformButton(
|
||||||
text: AppLocalizations.of(context).download,
|
text: AppLocalizations.of(context).download,
|
||||||
|
@ -497,16 +628,18 @@ class _HomeViewState extends State<HomeView> {
|
||||||
List<bool>.filled(availableLanguages.length, false);
|
List<bool>.filled(availableLanguages.length, false);
|
||||||
selectedLanguages[0] = true;
|
selectedLanguages[0] = true;
|
||||||
|
|
||||||
await showDialog(
|
await showAdaptiveDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (c) => StatefulBuilder(
|
builder: (c) => StatefulBuilder(
|
||||||
builder: (ctx, setState) => PlatformDialog(
|
builder: (ctx, setState) => AlertDialog.adaptive(
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final picker = ImagePicker();
|
final filePath = await FlutterFileDialog.pickFile(
|
||||||
final media = await picker.pickImage(source: imgSrc);
|
params: OpenFileDialogParams(
|
||||||
if (media == null) {
|
dialogType: OpenFileDialogType.image,
|
||||||
|
sourceType: sourceType));
|
||||||
|
if (filePath == null) {
|
||||||
if (mounted) Navigator.of(context).pop();
|
if (mounted) Navigator.of(context).pop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -521,16 +654,16 @@ class _HomeViewState extends State<HomeView> {
|
||||||
logger.i(selected);
|
logger.i(selected);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
unawaited(
|
unawaited(
|
||||||
showDialog(
|
showAdaptiveDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (c) => PlatformDialog(
|
builder: (c) => AlertDialog.adaptive(
|
||||||
title: AppLocalizations.of(context).ocrLoading,
|
title: Text(AppLocalizations.of(context).ocrLoading),
|
||||||
),
|
),
|
||||||
barrierDismissible: false,
|
barrierDismissible: false,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final string = await FlutterTesseractOcr.extractText(
|
final string = await FlutterTesseractOcr.extractText(
|
||||||
media.path,
|
filePath,
|
||||||
language: selected,
|
language: selected,
|
||||||
args: {
|
args: {
|
||||||
"psm": "4",
|
"psm": "4",
|
||||||
|
@ -556,6 +689,7 @@ class _HomeViewState extends State<HomeView> {
|
||||||
}
|
}
|
||||||
description.write("${line.replaceAll(regex, "")}\n");
|
description.write("${line.replaceAll(regex, "")}\n");
|
||||||
}
|
}
|
||||||
|
if (!ctx.mounted) return;
|
||||||
Navigator.of(ctx).pop();
|
Navigator.of(ctx).pop();
|
||||||
// show edit
|
// show edit
|
||||||
final newEntry =
|
final newEntry =
|
||||||
|
@ -591,7 +725,7 @@ class _HomeViewState extends State<HomeView> {
|
||||||
child: const Text("Cancel"),
|
child: const Text("Cancel"),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
title: AppLocalizations.of(context).ocrSelect,
|
title: Text(AppLocalizations.of(context).ocrSelect),
|
||||||
content: Column(
|
content: Column(
|
||||||
children: [
|
children: [
|
||||||
...List.generate(
|
...List.generate(
|
||||||
|
@ -624,23 +758,4 @@ class _HomeViewState extends State<HomeView> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> getLostData() async {
|
|
||||||
final picker = ImagePicker();
|
|
||||||
final response = await picker.retrieveLostData();
|
|
||||||
if (response.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final files = response.files;
|
|
||||||
if (files != null) {
|
|
||||||
logger.i("Found lost files");
|
|
||||||
_handleLostFiles(files);
|
|
||||||
} else {
|
|
||||||
logger.e(response.exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleLostFiles(List<XFile> files) {
|
|
||||||
// TODO: implement
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ import 'package:prasule/api/recurring_entry.dart';
|
||||||
import 'package:prasule/api/wallet.dart';
|
import 'package:prasule/api/wallet.dart';
|
||||||
import 'package:prasule/api/wallet_manager.dart';
|
import 'package:prasule/api/wallet_manager.dart';
|
||||||
import 'package:prasule/pw/platformbutton.dart';
|
import 'package:prasule/pw/platformbutton.dart';
|
||||||
import 'package:prasule/pw/platformdialog.dart';
|
|
||||||
import 'package:prasule/pw/platformroute.dart';
|
import 'package:prasule/pw/platformroute.dart';
|
||||||
import 'package:prasule/util/drawer.dart';
|
import 'package:prasule/util/drawer.dart';
|
||||||
import 'package:prasule/util/text_color.dart';
|
import 'package:prasule/util/text_color.dart';
|
||||||
|
@ -215,9 +214,10 @@ class _RecurringEntriesViewState extends State<RecurringEntriesView> {
|
||||||
onPressed: (c) {
|
onPressed: (c) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (cx) => PlatformDialog(
|
builder: (cx) => AlertDialog.adaptive(
|
||||||
title:
|
title: Text(
|
||||||
AppLocalizations.of(context).sureDialog,
|
AppLocalizations.of(context).sureDialog,
|
||||||
|
),
|
||||||
content: Text(
|
content: Text(
|
||||||
AppLocalizations.of(context).deleteSure,
|
AppLocalizations.of(context).deleteSure,
|
||||||
),
|
),
|
||||||
|
|
|
@ -12,7 +12,6 @@ import 'package:prasule/api/wallet.dart';
|
||||||
import 'package:prasule/api/wallet_manager.dart';
|
import 'package:prasule/api/wallet_manager.dart';
|
||||||
import 'package:prasule/main.dart';
|
import 'package:prasule/main.dart';
|
||||||
import 'package:prasule/pw/platformbutton.dart';
|
import 'package:prasule/pw/platformbutton.dart';
|
||||||
import 'package:prasule/pw/platformdialog.dart';
|
|
||||||
import 'package:prasule/pw/platformfield.dart';
|
import 'package:prasule/pw/platformfield.dart';
|
||||||
import 'package:prasule/pw/platformroute.dart';
|
import 'package:prasule/pw/platformroute.dart';
|
||||||
import 'package:prasule/util/text_color.dart';
|
import 'package:prasule/util/text_color.dart';
|
||||||
|
@ -146,10 +145,10 @@ class _EditCategoriesViewState extends State<EditCategoriesView> {
|
||||||
(await SharedPreferences.getInstance())
|
(await SharedPreferences.getInstance())
|
||||||
.getBool("useMaterialYou") ??
|
.getBool("useMaterialYou") ??
|
||||||
false;
|
false;
|
||||||
if (!mounted) return;
|
if (!context.mounted) return;
|
||||||
await showDialog(
|
await showAdaptiveDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (c) => PlatformDialog(
|
builder: (c) => AlertDialog.adaptive(
|
||||||
actions: [
|
actions: [
|
||||||
PlatformButton(
|
PlatformButton(
|
||||||
text: AppLocalizations.of(context).done,
|
text: AppLocalizations.of(context).done,
|
||||||
|
@ -158,8 +157,8 @@ class _EditCategoriesViewState extends State<EditCategoriesView> {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
title:
|
title: Text(
|
||||||
AppLocalizations.of(context).pickColor,
|
AppLocalizations.of(context).pickColor),
|
||||||
content: Column(
|
content: Column(
|
||||||
children: [
|
children: [
|
||||||
ColorPicker(
|
ColorPicker(
|
||||||
|
@ -215,9 +214,9 @@ class _EditCategoriesViewState extends State<EditCategoriesView> {
|
||||||
final controller = TextEditingController(
|
final controller = TextEditingController(
|
||||||
text: selectedWallet!.categories[i].name,
|
text: selectedWallet!.categories[i].name,
|
||||||
);
|
);
|
||||||
showDialog(
|
showAdaptiveDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (c) => PlatformDialog(
|
builder: (c) => AlertDialog.adaptive(
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
@ -227,7 +226,7 @@ class _EditCategoriesViewState extends State<EditCategoriesView> {
|
||||||
await WalletManager.saveWallet(
|
await WalletManager.saveWallet(
|
||||||
selectedWallet!,
|
selectedWallet!,
|
||||||
);
|
);
|
||||||
if (!mounted) return;
|
if (!context.mounted) return;
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
|
@ -243,8 +242,8 @@ class _EditCategoriesViewState extends State<EditCategoriesView> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
title: AppLocalizations.of(context)
|
title: Text(AppLocalizations.of(context)
|
||||||
.setupCategoriesEditingName,
|
.setupCategoriesEditingName),
|
||||||
content: SizedBox(
|
content: SizedBox(
|
||||||
width: 400,
|
width: 400,
|
||||||
child:
|
child:
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:prasule/pw/platformdialog.dart';
|
|
||||||
import 'package:settings_ui/settings_ui.dart';
|
import 'package:settings_ui/settings_ui.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
@ -50,10 +49,10 @@ class _GraphTypeSettingsViewState extends State<GraphTypeSettingsView> {
|
||||||
? AppLocalizations.of(context).barChart
|
? AppLocalizations.of(context).barChart
|
||||||
: AppLocalizations.of(context).lineChart,
|
: AppLocalizations.of(context).lineChart,
|
||||||
),
|
),
|
||||||
onPressed: (c) => showDialog(
|
onPressed: (c) => showAdaptiveDialog(
|
||||||
context: c,
|
context: c,
|
||||||
builder: (ctx) => PlatformDialog(
|
builder: (ctx) => AlertDialog.adaptive(
|
||||||
title: AppLocalizations.of(context).selectType,
|
title: Text(AppLocalizations.of(context).selectType),
|
||||||
content: Column(
|
content: Column(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
|
@ -70,7 +69,7 @@ class _GraphTypeSettingsViewState extends State<GraphTypeSettingsView> {
|
||||||
final s = await SharedPreferences.getInstance();
|
final s = await SharedPreferences.getInstance();
|
||||||
await s.setInt("yearlygraph", 1);
|
await s.setInt("yearlygraph", 1);
|
||||||
_yearly = 1;
|
_yearly = 1;
|
||||||
if (!mounted) return;
|
if (!ctx.mounted) return;
|
||||||
Navigator.of(ctx).pop();
|
Navigator.of(ctx).pop();
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
|
@ -90,7 +89,7 @@ class _GraphTypeSettingsViewState extends State<GraphTypeSettingsView> {
|
||||||
final s = await SharedPreferences.getInstance();
|
final s = await SharedPreferences.getInstance();
|
||||||
await s.setInt("yearlygraph", 2);
|
await s.setInt("yearlygraph", 2);
|
||||||
_yearly = 2;
|
_yearly = 2;
|
||||||
if (!mounted) return;
|
if (!ctx.mounted) return;
|
||||||
Navigator.of(ctx).pop();
|
Navigator.of(ctx).pop();
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
|
@ -110,8 +109,8 @@ class _GraphTypeSettingsViewState extends State<GraphTypeSettingsView> {
|
||||||
),
|
),
|
||||||
onPressed: (c) => showDialog(
|
onPressed: (c) => showDialog(
|
||||||
context: c,
|
context: c,
|
||||||
builder: (ctx) => PlatformDialog(
|
builder: (ctx) => AlertDialog.adaptive(
|
||||||
title: AppLocalizations.of(context).selectType,
|
title: Text(AppLocalizations.of(context).selectType),
|
||||||
content: Column(
|
content: Column(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
|
@ -128,7 +127,7 @@ class _GraphTypeSettingsViewState extends State<GraphTypeSettingsView> {
|
||||||
final s = await SharedPreferences.getInstance();
|
final s = await SharedPreferences.getInstance();
|
||||||
await s.setInt("monthlygraph", 1);
|
await s.setInt("monthlygraph", 1);
|
||||||
_monthly = 1;
|
_monthly = 1;
|
||||||
if (!mounted) return;
|
if (!ctx.mounted) return;
|
||||||
Navigator.of(ctx).pop();
|
Navigator.of(ctx).pop();
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
|
@ -148,7 +147,7 @@ class _GraphTypeSettingsViewState extends State<GraphTypeSettingsView> {
|
||||||
final s = await SharedPreferences.getInstance();
|
final s = await SharedPreferences.getInstance();
|
||||||
await s.setInt("monthlygraph", 2);
|
await s.setInt("monthlygraph", 2);
|
||||||
_monthly = 2;
|
_monthly = 2;
|
||||||
if (!mounted) return;
|
if (!ctx.mounted) return;
|
||||||
Navigator.of(ctx).pop();
|
Navigator.of(ctx).pop();
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
import 'package:prasule/api/wallet_manager.dart';
|
||||||
import 'package:prasule/main.dart';
|
import 'package:prasule/main.dart';
|
||||||
|
import 'package:prasule/pw/platformbutton.dart';
|
||||||
import 'package:prasule/pw/platformroute.dart';
|
import 'package:prasule/pw/platformroute.dart';
|
||||||
|
import 'package:prasule/util/show_message.dart';
|
||||||
import 'package:prasule/views/settings/edit_categories.dart';
|
import 'package:prasule/views/settings/edit_categories.dart';
|
||||||
import 'package:prasule/views/settings/graph_type.dart';
|
import 'package:prasule/views/settings/graph_type.dart';
|
||||||
import 'package:prasule/views/settings/tessdata_list.dart';
|
import 'package:prasule/views/settings/tessdata_list.dart';
|
||||||
|
@ -103,6 +107,190 @@ class _SettingsViewState extends State<SettingsView> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
SettingsSection(
|
||||||
|
title: Text(AppLocalizations.of(context).settingsData),
|
||||||
|
tiles: [
|
||||||
|
SettingsTile.navigation(
|
||||||
|
title: Text(AppLocalizations.of(context).exportSingle),
|
||||||
|
description:
|
||||||
|
Text(AppLocalizations.of(context).exportSingleDesc),
|
||||||
|
onPressed: (ctx) async {
|
||||||
|
final all = await WalletManager.listWallets();
|
||||||
|
if (!ctx.mounted) return;
|
||||||
|
final w = await showAdaptiveDialog<String>(
|
||||||
|
context: ctx,
|
||||||
|
builder: (ctx) => AlertDialog.adaptive(
|
||||||
|
title: Text(
|
||||||
|
AppLocalizations.of(context).selectExportWallet,
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
PlatformButton(
|
||||||
|
text: AppLocalizations.of(context).cancel,
|
||||||
|
onPressed: () => Navigator.of(ctx).pop(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
content: SizedBox(
|
||||||
|
width: MediaQuery.of(context).size.width * 0.7,
|
||||||
|
height: MediaQuery.of(context).size.height * 0.3,
|
||||||
|
child: ListView.builder(
|
||||||
|
itemBuilder: (con, i) => InkWell(
|
||||||
|
onTap: () => Navigator.of(ctx).pop(all[i].name),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: Text(
|
||||||
|
all[i].name,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: all.length,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (w == null) return;
|
||||||
|
try {
|
||||||
|
await WalletManager.exportWallet(name: w);
|
||||||
|
} catch (e) {
|
||||||
|
if (!context.mounted) return;
|
||||||
|
unawaited(
|
||||||
|
showAdaptiveDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (ctx) => AlertDialog.adaptive(
|
||||||
|
title: Text(
|
||||||
|
AppLocalizations.of(context).exportError,
|
||||||
|
),
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: Flexible(
|
||||||
|
child: Text(e.toString()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
logger.e(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!ctx.mounted) return;
|
||||||
|
unawaited(
|
||||||
|
showMessage(
|
||||||
|
AppLocalizations.of(ctx).exportCompleted,
|
||||||
|
ctx,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SettingsTile.navigation(
|
||||||
|
title: Text(AppLocalizations.of(context).exportArchive),
|
||||||
|
description:
|
||||||
|
Text(AppLocalizations.of(context).exportArchiveDesc),
|
||||||
|
onPressed: (ctx) async {
|
||||||
|
try {
|
||||||
|
await WalletManager.exportAllWallets();
|
||||||
|
} catch (e) {
|
||||||
|
if (!ctx.mounted) return;
|
||||||
|
unawaited(
|
||||||
|
showAdaptiveDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (ctx) => AlertDialog.adaptive(
|
||||||
|
title: Text(
|
||||||
|
AppLocalizations.of(context).exportError,
|
||||||
|
),
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: Flexible(
|
||||||
|
child: Text(e.toString()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
logger.e(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!ctx.mounted) return;
|
||||||
|
unawaited(
|
||||||
|
showMessage(
|
||||||
|
AppLocalizations.of(ctx).exportCompleted,
|
||||||
|
context,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SettingsTile.navigation(
|
||||||
|
title: Text(AppLocalizations.of(context).importSingle),
|
||||||
|
description:
|
||||||
|
Text(AppLocalizations.of(context).importSingleDesc),
|
||||||
|
onPressed: (ctx) async {
|
||||||
|
try {
|
||||||
|
await WalletManager.importWallet();
|
||||||
|
} catch (e) {
|
||||||
|
if (!ctx.mounted) return;
|
||||||
|
unawaited(
|
||||||
|
showAdaptiveDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (ctx) => AlertDialog.adaptive(
|
||||||
|
title: Text(
|
||||||
|
AppLocalizations.of(context).exportError,
|
||||||
|
),
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: Flexible(
|
||||||
|
child: Text(e.toString()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
logger.e(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!ctx.mounted) return;
|
||||||
|
unawaited(
|
||||||
|
showMessage(
|
||||||
|
AppLocalizations.of(ctx).importCompleted,
|
||||||
|
context,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SettingsTile.navigation(
|
||||||
|
title: Text(AppLocalizations.of(context).importArchive),
|
||||||
|
description:
|
||||||
|
Text(AppLocalizations.of(context).importArchiveDesc),
|
||||||
|
onPressed: (ctx) async {
|
||||||
|
try {
|
||||||
|
await WalletManager.importArchive();
|
||||||
|
} catch (e) {
|
||||||
|
if (!ctx.mounted) return;
|
||||||
|
unawaited(
|
||||||
|
showAdaptiveDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (ctx) => AlertDialog.adaptive(
|
||||||
|
title: Text(
|
||||||
|
AppLocalizations.of(context).exportError,
|
||||||
|
),
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: Flexible(
|
||||||
|
child: Text(e.toString()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
logger.e(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!ctx.mounted) return;
|
||||||
|
unawaited(
|
||||||
|
showMessage(
|
||||||
|
AppLocalizations.of(ctx).importCompleted,
|
||||||
|
context,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -9,7 +9,6 @@ import 'package:flutter_tesseract_ocr/flutter_tesseract_ocr.dart';
|
||||||
import 'package:prasule/main.dart';
|
import 'package:prasule/main.dart';
|
||||||
import 'package:prasule/network/tessdata.dart';
|
import 'package:prasule/network/tessdata.dart';
|
||||||
import 'package:prasule/pw/platformbutton.dart';
|
import 'package:prasule/pw/platformbutton.dart';
|
||||||
import 'package:prasule/pw/platformdialog.dart';
|
|
||||||
|
|
||||||
/// Used to manage downloaded Tessdata for OCR
|
/// Used to manage downloaded Tessdata for OCR
|
||||||
class TessdataListView extends StatefulWidget {
|
class TessdataListView extends StatefulWidget {
|
||||||
|
@ -62,10 +61,11 @@ class _TessdataListViewState extends State<TessdataListView> {
|
||||||
final lang = _tessdata[i].keys.first;
|
final lang = _tessdata[i].keys.first;
|
||||||
if (_tessdata[i][lang]!) {
|
if (_tessdata[i][lang]!) {
|
||||||
// deleting data
|
// deleting data
|
||||||
await showDialog(
|
await showAdaptiveDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => PlatformDialog(
|
builder: (context) => AlertDialog.adaptive(
|
||||||
title: AppLocalizations.of(context).sureDialog,
|
title:
|
||||||
|
Text(AppLocalizations.of(context).sureDialog),
|
||||||
content: Text(
|
content: Text(
|
||||||
AppLocalizations.of(context).deleteOcr(lang),
|
AppLocalizations.of(context).deleteOcr(lang),
|
||||||
),
|
),
|
||||||
|
@ -75,7 +75,9 @@ class _TessdataListViewState extends State<TessdataListView> {
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await TessdataApi.deleteData(lang);
|
await TessdataApi.deleteData(lang);
|
||||||
_tessdata[i][lang] = true;
|
_tessdata[i][lang] = true;
|
||||||
if (mounted) Navigator.of(context).pop();
|
if (context.mounted) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
PlatformButton(
|
PlatformButton(
|
||||||
|
@ -96,11 +98,11 @@ class _TessdataListViewState extends State<TessdataListView> {
|
||||||
final progressStream = StreamController<double>();
|
final progressStream = StreamController<double>();
|
||||||
|
|
||||||
unawaited(
|
unawaited(
|
||||||
showDialog(
|
showAdaptiveDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (c) => PlatformDialog(
|
builder: (c) => AlertDialog.adaptive(
|
||||||
title: AppLocalizations.of(context)
|
title: Text(AppLocalizations.of(context)
|
||||||
.langDownloadDialog(lang),
|
.langDownloadDialog(lang)),
|
||||||
content: StreamBuilder(
|
content: StreamBuilder(
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.connectionState ==
|
if (snapshot.connectionState ==
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
// ignore_for_file: inference_failure_on_function_invocation
|
// ignore_for_file: inference_failure_on_function_invocation
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:currency_picker/currency_picker.dart';
|
import 'package:currency_picker/currency_picker.dart';
|
||||||
import 'package:dynamic_color/dynamic_color.dart';
|
import 'package:dynamic_color/dynamic_color.dart';
|
||||||
import 'package:flex_color_picker/flex_color_picker.dart';
|
import 'package:flex_color_picker/flex_color_picker.dart';
|
||||||
|
@ -12,7 +14,6 @@ import 'package:prasule/api/category.dart';
|
||||||
import 'package:prasule/api/wallet.dart';
|
import 'package:prasule/api/wallet.dart';
|
||||||
import 'package:prasule/api/wallet_manager.dart';
|
import 'package:prasule/api/wallet_manager.dart';
|
||||||
import 'package:prasule/pw/platformbutton.dart';
|
import 'package:prasule/pw/platformbutton.dart';
|
||||||
import 'package:prasule/pw/platformdialog.dart';
|
|
||||||
import 'package:prasule/pw/platformfield.dart';
|
import 'package:prasule/pw/platformfield.dart';
|
||||||
import 'package:prasule/pw/platformroute.dart';
|
import 'package:prasule/pw/platformroute.dart';
|
||||||
import 'package:prasule/util/show_message.dart';
|
import 'package:prasule/util/show_message.dart';
|
||||||
|
@ -118,11 +119,22 @@ class _SetupViewState extends State<SetupView> {
|
||||||
next: Text(AppLocalizations.of(context).next),
|
next: Text(AppLocalizations.of(context).next),
|
||||||
back: Text(AppLocalizations.of(context).back),
|
back: Text(AppLocalizations.of(context).back),
|
||||||
done: Text(AppLocalizations.of(context).finish),
|
done: Text(AppLocalizations.of(context).finish),
|
||||||
onDone: () {
|
onDone: () async {
|
||||||
if (name.isEmpty) {
|
if (name.isEmpty) {
|
||||||
|
unawaited(
|
||||||
showMessage(
|
showMessage(
|
||||||
AppLocalizations.of(context).errorEmptyName,
|
AppLocalizations.of(context).errorEmptyName,
|
||||||
context,
|
context,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (await WalletManager.exists(name) && context.mounted) {
|
||||||
|
unawaited(
|
||||||
|
showMessage(
|
||||||
|
AppLocalizations.of(context).walletExists,
|
||||||
|
context,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -131,25 +143,19 @@ class _SetupViewState extends State<SetupView> {
|
||||||
currency: _selectedCurrency,
|
currency: _selectedCurrency,
|
||||||
categories: categories,
|
categories: categories,
|
||||||
);
|
);
|
||||||
WalletManager.saveWallet(wallet).then(
|
await WalletManager.saveWallet(wallet);
|
||||||
(value) {
|
|
||||||
if (!value) {
|
if (widget.newWallet && context.mounted) {
|
||||||
showMessage(
|
|
||||||
AppLocalizations.of(context).walletExists,
|
|
||||||
context,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (widget.newWallet) {
|
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!context.mounted) return;
|
||||||
|
unawaited(
|
||||||
Navigator.of(context).pushReplacement(
|
Navigator.of(context).pushReplacement(
|
||||||
platformRoute(
|
platformRoute(
|
||||||
(c) => const HomeView(),
|
(c) => const HomeView(),
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
pages: [
|
pages: [
|
||||||
|
@ -297,10 +303,10 @@ class _SetupViewState extends State<SetupView> {
|
||||||
(await SharedPreferences.getInstance())
|
(await SharedPreferences.getInstance())
|
||||||
.getBool("useMaterialYou") ??
|
.getBool("useMaterialYou") ??
|
||||||
false;
|
false;
|
||||||
if (!mounted) return;
|
if (!context.mounted) return;
|
||||||
await showDialog(
|
await showAdaptiveDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (c) => PlatformDialog(
|
builder: (c) => AlertDialog.adaptive(
|
||||||
actions: [
|
actions: [
|
||||||
PlatformButton(
|
PlatformButton(
|
||||||
text: AppLocalizations.of(context)
|
text: AppLocalizations.of(context)
|
||||||
|
@ -310,8 +316,10 @@ class _SetupViewState extends State<SetupView> {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
title: AppLocalizations.of(context)
|
title: Text(
|
||||||
|
AppLocalizations.of(context)
|
||||||
.pickColor,
|
.pickColor,
|
||||||
|
),
|
||||||
content: Column(
|
content: Column(
|
||||||
children: [
|
children: [
|
||||||
ColorPicker(
|
ColorPicker(
|
||||||
|
@ -363,9 +371,9 @@ class _SetupViewState extends State<SetupView> {
|
||||||
final controller = TextEditingController(
|
final controller = TextEditingController(
|
||||||
text: categories[i].name,
|
text: categories[i].name,
|
||||||
);
|
);
|
||||||
showDialog(
|
showAdaptiveDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (c) => PlatformDialog(
|
builder: (c) => AlertDialog.adaptive(
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
@ -390,8 +398,10 @@ class _SetupViewState extends State<SetupView> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
title: AppLocalizations.of(context)
|
title: Text(
|
||||||
|
AppLocalizations.of(context)
|
||||||
.setupCategoriesEditingName,
|
.setupCategoriesEditingName,
|
||||||
|
),
|
||||||
content: SizedBox(
|
content: SizedBox(
|
||||||
width: 400,
|
width: 400,
|
||||||
child: PlatformField(
|
child: PlatformField(
|
||||||
|
|
210
pubspec.lock
210
pubspec.lock
|
@ -18,7 +18,7 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.3.0"
|
version: "6.3.0"
|
||||||
archive:
|
archive:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: archive
|
name: archive
|
||||||
sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
|
sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
|
||||||
|
@ -85,10 +85,10 @@ packages:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_runner
|
name: build_runner
|
||||||
sha256: "67d591d602906ef9201caf93452495ad1812bea2074f04e25dbd7c133785821b"
|
sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.7"
|
version: "2.4.8"
|
||||||
build_runner_core:
|
build_runner_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -149,10 +149,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: code_builder
|
name: code_builder
|
||||||
sha256: feee43a5c05e7b3199bb375a86430b8ada1b04104f2923d0e03cc01ca87b6d84
|
sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.9.0"
|
version: "4.10.0"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -177,14 +177,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.7.2"
|
version: "1.7.2"
|
||||||
cross_file:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: cross_file
|
|
||||||
sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.3.3+8"
|
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -273,38 +265,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.0"
|
version: "7.0.0"
|
||||||
file_selector_linux:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: file_selector_linux
|
|
||||||
sha256: "045d372bf19b02aeb69cacf8b4009555fb5f6f0b7ad8016e5f46dd1387ddd492"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.9.2+1"
|
|
||||||
file_selector_macos:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: file_selector_macos
|
|
||||||
sha256: b15c3da8bd4908b9918111fa486903f5808e388b8d1c559949f584725a6594d6
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.9.3+3"
|
|
||||||
file_selector_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: file_selector_platform_interface
|
|
||||||
sha256: "0aa47a725c346825a2bd396343ce63ac00bda6eff2fbc43eabe99737dede8262"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.6.1"
|
|
||||||
file_selector_windows:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: file_selector_windows
|
|
||||||
sha256: d3547240c20cabf205c7c7f01a50ecdbc413755814d6677f3cb366f04abcead0
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.9.3+1"
|
|
||||||
fixnum:
|
fixnum:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -325,10 +285,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flex_color_picker
|
name: flex_color_picker
|
||||||
sha256: f37476ab3e80dcaca94e428e159944d465dd16312fda9ff41e07e86f04bfa51c
|
sha256: "0871edc170153cfc3de316d30625f40a85daecfa76ce541641f3cc0ec7757cbf"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.0"
|
version: "3.3.1"
|
||||||
flex_seed_scheme:
|
flex_seed_scheme:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -347,6 +307,14 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_file_dialog:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_file_dialog
|
||||||
|
sha256: "9344b8f07be6a1b6f9854b723fb0cf84a8094ba94761af1d213589d3cb087488"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.2"
|
||||||
flutter_iconpicker:
|
flutter_iconpicker:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -424,14 +392,6 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
flutter_plugin_android_lifecycle:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: flutter_plugin_android_lifecycle
|
|
||||||
sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.17"
|
|
||||||
flutter_slidable:
|
flutter_slidable:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -519,14 +479,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.1.2"
|
version: "5.1.2"
|
||||||
http:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: http
|
|
||||||
sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.2"
|
|
||||||
http_multi_server:
|
http_multi_server:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -547,74 +499,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image
|
name: image
|
||||||
sha256: "028f61960d56f26414eb616b48b04eb37d700cbe477b7fb09bf1d7ce57fd9271"
|
sha256: "004a2e90ce080f8627b5a04aecb4cdfac87d2c3f3b520aa291260be5a32c033d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.3"
|
version: "4.1.4"
|
||||||
image_picker:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: image_picker
|
|
||||||
sha256: "340efe08645537d6b088a30620ee5752298b1630f23a829181172610b868262b"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.6"
|
|
||||||
image_picker_android:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: image_picker_android
|
|
||||||
sha256: "1a27bf4cc0330389cebe465bab08fe6dec97e44015b4899637344bb7297759ec"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.8.9+2"
|
|
||||||
image_picker_for_web:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: image_picker_for_web
|
|
||||||
sha256: e2423c53a68b579a7c37a1eda967b8ae536c3d98518e5db95ca1fe5719a730a3
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.0.2"
|
|
||||||
image_picker_ios:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: image_picker_ios
|
|
||||||
sha256: eac0a62104fa12feed213596df0321f57ce5a572562f72a68c4ff81e9e4caacf
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.8.9"
|
|
||||||
image_picker_linux:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: image_picker_linux
|
|
||||||
sha256: "4ed1d9bb36f7cd60aa6e6cd479779cc56a4cb4e4de8f49d487b1aaad831300fa"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.1+1"
|
|
||||||
image_picker_macos:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: image_picker_macos
|
|
||||||
sha256: "3f5ad1e8112a9a6111c46d0b57a7be2286a9a07fc6e1976fdf5be2bd31d4ff62"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.1+1"
|
|
||||||
image_picker_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: image_picker_platform_interface
|
|
||||||
sha256: "0e827c156e3a90edd3bbe7f6de048b39247b16e58173b08a835b7eb00aba239e"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.9.2"
|
|
||||||
image_picker_windows:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: image_picker_windows
|
|
||||||
sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.2.1+1"
|
|
||||||
integration_test:
|
integration_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -672,18 +560,26 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker
|
name: leak_tracker
|
||||||
sha256: "04be76c4a4bb50f14904e64749237e541e7c7bcf7ec0b196907322ab5d2fc739"
|
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "9.0.16"
|
version: "10.0.0"
|
||||||
|
leak_tracker_flutter_testing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: leak_tracker_flutter_testing
|
||||||
|
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.1"
|
||||||
leak_tracker_testing:
|
leak_tracker_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker_testing
|
name: leak_tracker_testing
|
||||||
sha256: b06739349ec2477e943055aea30172c5c7000225f79dad4702e2ec0eda79a6ff
|
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "2.0.1"
|
||||||
lints:
|
lints:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -712,10 +608,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: matcher
|
name: matcher
|
||||||
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
|
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.16"
|
version: "0.12.16+1"
|
||||||
material_color_utilities:
|
material_color_utilities:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -768,18 +664,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path
|
name: path
|
||||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.3"
|
version: "1.9.0"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
|
sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
path_provider_android:
|
path_provider_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -792,10 +688,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_foundation
|
name: path_provider_foundation
|
||||||
sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
|
sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.1"
|
version: "2.3.2"
|
||||||
path_provider_linux:
|
path_provider_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -808,10 +704,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_platform_interface
|
name: path_provider_platform_interface
|
||||||
sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
|
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
path_provider_windows:
|
path_provider_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -832,10 +728,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: platform
|
name: platform
|
||||||
sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59"
|
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
version: "3.1.4"
|
||||||
plugin_platform_interface:
|
plugin_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -848,10 +744,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: pointycastle
|
name: pointycastle
|
||||||
sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
|
sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.7.3"
|
version: "3.7.4"
|
||||||
pool:
|
pool:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -864,10 +760,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: process
|
name: process
|
||||||
sha256: "266ca5be5820feefc777793d0a583acfc8c40834893c87c00c6c09e2cf58ea42"
|
sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.1"
|
version: "5.0.2"
|
||||||
provider:
|
provider:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -920,10 +816,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_foundation
|
name: shared_preferences_foundation
|
||||||
sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7"
|
sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.4"
|
version: "2.3.5"
|
||||||
shared_preferences_linux:
|
shared_preferences_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -936,10 +832,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_platform_interface
|
name: shared_preferences_platform_interface
|
||||||
sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a
|
sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.1"
|
version: "2.3.2"
|
||||||
shared_preferences_web:
|
shared_preferences_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1157,18 +1053,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web
|
name: web
|
||||||
sha256: edc8a9573dd8c5a83a183dae1af2b6fd4131377404706ca4e5420474784906fa
|
sha256: "4188706108906f002b3a293509234588823c8c979dc83304e229ff400c996b05"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.0"
|
version: "0.4.2"
|
||||||
web_socket_channel:
|
web_socket_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web_socket_channel
|
name: web_socket_channel
|
||||||
sha256: "045ec2137c27bf1a32e6ffa0e734d532a6677bf9016a0d1a406c54e499ff945b"
|
sha256: "939ab60734a4f8fa95feacb55804fa278de28bdeef38e616dc08e44a84adea23"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.1"
|
version: "2.4.3"
|
||||||
webdriver:
|
webdriver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
name: prasule
|
name: prasule
|
||||||
description: Open-source private expense tracker
|
description: Open-source private expense tracker
|
||||||
|
|
||||||
version: 1.0.0-alpha+4
|
version: 1.0.0-alpha+5
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.1.0-262.2.beta <4.0.0'
|
sdk: '>=3.1.0-262.2.beta <4.0.0'
|
||||||
|
@ -13,6 +13,7 @@ environment:
|
||||||
# the latest version available on pub.dev. To see which dependencies have newer
|
# the latest version available on pub.dev. To see which dependencies have newer
|
||||||
# versions available, run `flutter pub outdated`.
|
# versions available, run `flutter pub outdated`.
|
||||||
dependencies:
|
dependencies:
|
||||||
|
archive: ^3.4.10
|
||||||
cupertino_icons: ^1.0.2
|
cupertino_icons: ^1.0.2
|
||||||
currency_picker: ^2.0.16
|
currency_picker: ^2.0.16
|
||||||
dio: ^5.3.0
|
dio: ^5.3.0
|
||||||
|
@ -21,6 +22,7 @@ dependencies:
|
||||||
flex_color_picker: ^3.3.0
|
flex_color_picker: ^3.3.0
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
flutter_file_dialog: ^3.0.2
|
||||||
flutter_iconpicker: ^3.2.4
|
flutter_iconpicker: ^3.2.4
|
||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
@ -29,7 +31,6 @@ dependencies:
|
||||||
flutter_tesseract_ocr: ^0.4.23
|
flutter_tesseract_ocr: ^0.4.23
|
||||||
fluttertoast: ^8.2.4
|
fluttertoast: ^8.2.4
|
||||||
grouped_list: ^5.1.2
|
grouped_list: ^5.1.2
|
||||||
image_picker: ^1.0.1
|
|
||||||
intl: any
|
intl: any
|
||||||
introduction_screen: ^3.1.11
|
introduction_screen: ^3.1.11
|
||||||
json_annotation: ^4.8.1
|
json_annotation: ^4.8.1
|
||||||
|
|
Loading…
Reference in a new issue