feat: creating debt scenarios
also changed way of loading selected wallet
This commit is contained in:
parent
300f359070
commit
ef52caa836
20 changed files with 946 additions and 478 deletions
|
@ -1,6 +1,8 @@
|
||||||
# newVersion
|
# 2.0.0
|
||||||
- Upgrade dependencies
|
- Upgrade dependencies
|
||||||
- Use less `await`s in WalletManager class
|
- Use less `await`s in WalletManager class
|
||||||
|
- Added debt management
|
||||||
|
- Changed android app ID
|
||||||
|
|
||||||
# 1.1.1
|
# 1.1.1
|
||||||
- Removed deprecated code
|
- Removed deprecated code
|
||||||
|
|
|
@ -37,6 +37,8 @@ class DebtEntry {
|
||||||
String name;
|
String name;
|
||||||
|
|
||||||
/// List of people who payed
|
/// List of people who payed
|
||||||
@JsonKey(defaultValue: DebtPerson.unknownPerson)
|
@JsonKey(defaultValue: _defaultDebtPayers)
|
||||||
List<DebtPerson> whoPayed;
|
final List<DebtPerson> whoPayed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<DebtPerson> _defaultDebtPayers() => [DebtPerson.unknownPerson()];
|
||||||
|
|
|
@ -19,7 +19,7 @@ DebtEntry _$DebtEntryFromJson(Map<String, dynamic> json) {
|
||||||
whoPayed: (json['whoPayed'] as List<dynamic>?)
|
whoPayed: (json['whoPayed'] as List<dynamic>?)
|
||||||
?.map((e) => DebtPerson.fromJson(e as Map<String, dynamic>))
|
?.map((e) => DebtPerson.fromJson(e as Map<String, dynamic>))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
DebtPerson.unknownPerson(),
|
_defaultDebtPayers(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ part 'debt_person.g.dart';
|
||||||
/// Represents a single person in a debt scenario
|
/// Represents a single person in a debt scenario
|
||||||
class DebtPerson {
|
class DebtPerson {
|
||||||
/// Represents a single person in a debt scenario
|
/// Represents a single person in a debt scenario
|
||||||
DebtPerson({required this.id, required this.name});
|
DebtPerson({required this.id, required this.name, this.bankAccount});
|
||||||
|
|
||||||
/// Default [DebtPerson] instance for json_serializable
|
/// Default [DebtPerson] instance for json_serializable
|
||||||
factory DebtPerson.unknownPerson() => DebtPerson(id: -1, name: "Unknown");
|
factory DebtPerson.unknownPerson() => DebtPerson(id: -1, name: "Unknown");
|
||||||
|
@ -25,4 +25,9 @@ class DebtPerson {
|
||||||
/// Identifier that the user will see
|
/// Identifier that the user will see
|
||||||
@JsonKey(defaultValue: "Unknown")
|
@JsonKey(defaultValue: "Unknown")
|
||||||
String name;
|
String name;
|
||||||
|
|
||||||
|
/// Person's bank account
|
||||||
|
///
|
||||||
|
/// Used to generate a QR code payment
|
||||||
|
String? bankAccount;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ DebtPerson _$DebtPersonFromJson(Map<String, dynamic> json) {
|
||||||
return DebtPerson(
|
return DebtPerson(
|
||||||
id: (json['id'] as num).toInt(),
|
id: (json['id'] as num).toInt(),
|
||||||
name: json['name'] as String? ?? 'Unknown',
|
name: json['name'] as String? ?? 'Unknown',
|
||||||
|
bankAccount: json['bankAccount'] as String?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,4 +23,5 @@ Map<String, dynamic> _$DebtPersonToJson(DebtPerson instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'id': instance.id,
|
'id': instance.id,
|
||||||
'name': instance.name,
|
'name': instance.name,
|
||||||
|
'bankAccount': instance.bankAccount,
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,8 +11,8 @@ class DebtScenario {
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.isArchived,
|
required this.isArchived,
|
||||||
|
required this.people,
|
||||||
this.entries = const [],
|
this.entries = const [],
|
||||||
this.people = const [],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Generates a class instance from a Map
|
/// Generates a class instance from a Map
|
||||||
|
@ -22,7 +22,7 @@ class DebtScenario {
|
||||||
/// Converts the data in this instance into a Map
|
/// Converts the data in this instance into a Map
|
||||||
Map<String, dynamic> toJson() => _$DebtScenarioToJson(this);
|
Map<String, dynamic> toJson() => _$DebtScenarioToJson(this);
|
||||||
|
|
||||||
/// Unique identified
|
/// Unique identifier
|
||||||
@JsonKey(disallowNullValue: true)
|
@JsonKey(disallowNullValue: true)
|
||||||
final int id;
|
final int id;
|
||||||
|
|
||||||
|
@ -36,11 +36,29 @@ class DebtScenario {
|
||||||
|
|
||||||
/// All entries
|
/// All entries
|
||||||
@JsonKey(defaultValue: [])
|
@JsonKey(defaultValue: [])
|
||||||
List<DebtEntry> entries;
|
final List<DebtEntry> entries;
|
||||||
|
|
||||||
/// All people
|
/// All people
|
||||||
@JsonKey(defaultValue: _defaultPeopleList)
|
@JsonKey(defaultValue: _defaultPeopleList)
|
||||||
List<DebtPerson> people;
|
final List<DebtPerson> people;
|
||||||
|
|
||||||
|
/// Getter for the next unused unique number ID for a [DebtPerson]
|
||||||
|
int get nextPersonId {
|
||||||
|
var id = 1;
|
||||||
|
while (people.where((element) => element.id == id).isNotEmpty) {
|
||||||
|
id++; // create unique ID
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Getter for the next unused unique number ID for a [DebtEntry]
|
||||||
|
int get nextEntryId {
|
||||||
|
var id = 1;
|
||||||
|
while (entries.where((element) => element.id == id).isNotEmpty) {
|
||||||
|
id++; // create unique ID
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<DebtPerson> _defaultPeopleList() => [DebtPerson.unknownPerson()];
|
List<DebtPerson> _defaultPeopleList() => [DebtPerson.unknownPerson()];
|
||||||
|
|
|
@ -9,6 +9,7 @@ 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/debt_scenario.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/recurring_entry.dart';
|
||||||
import 'package:prasule/api/wallet_entry.dart';
|
import 'package:prasule/api/wallet_entry.dart';
|
||||||
|
@ -31,6 +32,7 @@ class Wallet {
|
||||||
this.categories = const [],
|
this.categories = const [],
|
||||||
this.entries = const [],
|
this.entries = const [],
|
||||||
this.recurringEntries = const [],
|
this.recurringEntries = const [],
|
||||||
|
this.debts = const [],
|
||||||
this.starterBalance = 0,
|
this.starterBalance = 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -53,6 +55,10 @@ class Wallet {
|
||||||
@JsonKey(defaultValue: [])
|
@JsonKey(defaultValue: [])
|
||||||
final List<WalletSingleEntry> entries;
|
final List<WalletSingleEntry> entries;
|
||||||
|
|
||||||
|
/// List of user's [DebtScenario]s
|
||||||
|
@JsonKey(defaultValue: [])
|
||||||
|
final List<DebtScenario> debts;
|
||||||
|
|
||||||
/// The starting balance of the wallet
|
/// The starting balance of the wallet
|
||||||
///
|
///
|
||||||
/// Used to calculate current balance
|
/// Used to calculate current balance
|
||||||
|
@ -88,6 +94,16 @@ class Wallet {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Getter for the next unused unique number ID in the wallet's **debts**
|
||||||
|
/// list
|
||||||
|
int get nextDebtId {
|
||||||
|
var id = 0;
|
||||||
|
while (debts.where((element) => element.id == id).isNotEmpty) {
|
||||||
|
id++; // create unique ID
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
/// Handles adding recurring entries to the entry list
|
/// Handles adding recurring entries to the entry list
|
||||||
void recurEntries() {
|
void recurEntries() {
|
||||||
final n = DateTime.now();
|
final n = DateTime.now();
|
||||||
|
|
|
@ -25,6 +25,10 @@ Wallet _$WalletFromJson(Map<String, dynamic> json) => Wallet(
|
||||||
RecurringWalletEntry.fromJson(e as Map<String, dynamic>))
|
RecurringWalletEntry.fromJson(e as Map<String, dynamic>))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
[],
|
[],
|
||||||
|
debts: (json['debts'] as List<dynamic>?)
|
||||||
|
?.map((e) => DebtScenario.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList() ??
|
||||||
|
[],
|
||||||
starterBalance: (json['starterBalance'] as num?)?.toDouble() ?? 0,
|
starterBalance: (json['starterBalance'] as num?)?.toDouble() ?? 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -33,6 +37,7 @@ Map<String, dynamic> _$WalletToJson(Wallet instance) => <String, dynamic>{
|
||||||
'name': instance.name,
|
'name': instance.name,
|
||||||
'categories': instance.categories,
|
'categories': instance.categories,
|
||||||
'entries': instance.entries,
|
'entries': instance.entries,
|
||||||
|
'debts': instance.debts,
|
||||||
'starterBalance': instance.starterBalance,
|
'starterBalance': instance.starterBalance,
|
||||||
'currency': instance.currency,
|
'currency': instance.currency,
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,7 +16,7 @@ import 'package:prasule/main.dart';
|
||||||
/// Used for [Wallet]-managing operations
|
/// Used for [Wallet]-managing operations
|
||||||
class WalletManager {
|
class WalletManager {
|
||||||
/// Currently selected wallet
|
/// Currently selected wallet
|
||||||
static Wallet? selectedWallet;
|
static late Wallet selectedWallet;
|
||||||
|
|
||||||
/// Path to the directory with wallet files
|
/// Path to the directory with wallet files
|
||||||
///
|
///
|
||||||
|
|
|
@ -122,5 +122,14 @@
|
||||||
"incomePerYearCategory":"Příjmy podle kategorie za rok {year}",
|
"incomePerYearCategory":"Příjmy podle kategorie za rok {year}",
|
||||||
"incomePerMonthCategory":"Příjmy podle kategorie za měsíc {monthYear}",
|
"incomePerMonthCategory":"Příjmy podle kategorie za měsíc {monthYear}",
|
||||||
"selectYear":"Zvolte rok",
|
"selectYear":"Zvolte rok",
|
||||||
"selectMonth":"Zvolte měsíc a rok"
|
"selectMonth":"Zvolte měsíc a rok",
|
||||||
|
"debts":"Dlužníček",
|
||||||
|
"debtNamePlaceholder":"Dluhy přátel",
|
||||||
|
"people":"Lidé",
|
||||||
|
"addSomePeople":"Přidej lidi pomocí tlačítka níže",
|
||||||
|
"bankAccount":"Číslo bankovního účtu",
|
||||||
|
"noDebtScenarios":"Žádné seznamy dlužníků :(",
|
||||||
|
"noDebtScenariosSub":"Nový můžete vytvořit pomocí plovoucího tlačítka.",
|
||||||
|
"noPersonError":"Musíte vložit alespoň jednoho člověka!",
|
||||||
|
"unnamed":"Bez jména"
|
||||||
}
|
}
|
|
@ -313,5 +313,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"selectYear":"Select a year",
|
"selectYear":"Select a year",
|
||||||
"selectMonth":"Select a month and year"
|
"selectMonth":"Select a month and year",
|
||||||
|
"debts":"Debts",
|
||||||
|
"debtNamePlaceholder":"Friends' debts",
|
||||||
|
"people":"People",
|
||||||
|
"addSomePeople":"Add people using the button below",
|
||||||
|
"bankAccount":"Bank account number",
|
||||||
|
"noDebtScenarios":"No debt scenarios :(",
|
||||||
|
"noDebtScenariosSub":"Create one using the floating action button.",
|
||||||
|
"noPersonError":"You need to add at least one person!",
|
||||||
|
"unnamed":"Unnamed"
|
||||||
}
|
}
|
|
@ -10,9 +10,9 @@ import 'package:flutter/services.dart';
|
||||||
import 'package:prasule/pw/platformwidget.dart';
|
import 'package:prasule/pw/platformwidget.dart';
|
||||||
|
|
||||||
/// A [PlatformWidget] implementation of a text field
|
/// A [PlatformWidget] implementation of a text field
|
||||||
class PlatformField extends PlatformWidget<TextField, CupertinoTextField> {
|
class PlatformField extends PlatformWidget<TextFormField, CupertinoTextField> {
|
||||||
const PlatformField({
|
const PlatformField(
|
||||||
super.key,
|
{super.key,
|
||||||
this.controller,
|
this.controller,
|
||||||
this.enabled,
|
this.enabled,
|
||||||
this.labelText,
|
this.labelText,
|
||||||
|
@ -29,7 +29,7 @@ class PlatformField extends PlatformWidget<TextField, CupertinoTextField> {
|
||||||
this.inputBorder = const OutlineInputBorder(),
|
this.inputBorder = const OutlineInputBorder(),
|
||||||
this.suffix,
|
this.suffix,
|
||||||
this.prefix,
|
this.prefix,
|
||||||
});
|
this.validator});
|
||||||
final TextEditingController? controller;
|
final TextEditingController? controller;
|
||||||
final bool? enabled;
|
final bool? enabled;
|
||||||
final bool obscureText;
|
final bool obscureText;
|
||||||
|
@ -46,9 +46,10 @@ class PlatformField extends PlatformWidget<TextField, CupertinoTextField> {
|
||||||
final FocusNode? focusNode;
|
final FocusNode? focusNode;
|
||||||
final Widget? suffix;
|
final Widget? suffix;
|
||||||
final Widget? prefix;
|
final Widget? prefix;
|
||||||
|
final String? Function(String?)? validator;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
TextField createAndroidWidget(BuildContext context) => TextField(
|
TextFormField createAndroidWidget(BuildContext context) => TextFormField(
|
||||||
textAlign: textAlign,
|
textAlign: textAlign,
|
||||||
controller: controller,
|
controller: controller,
|
||||||
enabled: enabled,
|
enabled: enabled,
|
||||||
|
@ -67,6 +68,7 @@ class PlatformField extends PlatformWidget<TextField, CupertinoTextField> {
|
||||||
onChanged: onChanged,
|
onChanged: onChanged,
|
||||||
autofillHints: autofillHints,
|
autofillHints: autofillHints,
|
||||||
maxLines: maxLines,
|
maxLines: maxLines,
|
||||||
|
validator: validator,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -5,12 +5,13 @@
|
||||||
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/platformroute.dart';
|
import 'package:prasule/pw/platformroute.dart';
|
||||||
|
import 'package:prasule/views/debts/debt_view.dart';
|
||||||
import 'package:prasule/views/graphs/graph_view.dart';
|
import 'package:prasule/views/graphs/graph_view.dart';
|
||||||
import 'package:prasule/views/home.dart';
|
import 'package:prasule/views/home.dart';
|
||||||
import 'package:prasule/views/recurring/recurring_view.dart';
|
import 'package:prasule/views/recurring/recurring_view.dart';
|
||||||
|
|
||||||
/// Makes the drawer because I won't enter the same code in every view
|
/// Makes the drawer because I won't enter the same code in every view
|
||||||
Drawer makeDrawer(BuildContext context, int page) => Drawer(
|
Drawer makeDrawer(BuildContext context, Pages page) => Drawer(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: [
|
children: [
|
||||||
const DrawerHeader(child: Text("Prašule")),
|
const DrawerHeader(child: Text("Prašule")),
|
||||||
|
@ -19,9 +20,9 @@ Drawer makeDrawer(BuildContext context, int page) => Drawer(
|
||||||
title: Text(
|
title: Text(
|
||||||
AppLocalizations.of(context).home,
|
AppLocalizations.of(context).home,
|
||||||
),
|
),
|
||||||
selected: page == 1,
|
selected: page == Pages.home,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (page == 1) {
|
if (page == Pages.home) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -34,9 +35,9 @@ Drawer makeDrawer(BuildContext context, int page) => Drawer(
|
||||||
title: Text(
|
title: Text(
|
||||||
AppLocalizations.of(context).graphs,
|
AppLocalizations.of(context).graphs,
|
||||||
),
|
),
|
||||||
selected: page == 2,
|
selected: page == Pages.graphs,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (page == 2) {
|
if (page == Pages.graphs) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -49,9 +50,9 @@ Drawer makeDrawer(BuildContext context, int page) => Drawer(
|
||||||
title: Text(
|
title: Text(
|
||||||
AppLocalizations.of(context).recurringPayments,
|
AppLocalizations.of(context).recurringPayments,
|
||||||
),
|
),
|
||||||
selected: page == 3,
|
selected: page == Pages.recurringEntries,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (page == 3) {
|
if (page == Pages.recurringEntries) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -60,6 +61,37 @@ Drawer makeDrawer(BuildContext context, int page) => Drawer(
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.people),
|
||||||
|
title: Text(
|
||||||
|
AppLocalizations.of(context).debts,
|
||||||
|
),
|
||||||
|
selected: page == Pages.debts,
|
||||||
|
onTap: () {
|
||||||
|
if (page == Pages.debts) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Navigator.of(context).pushReplacement(
|
||||||
|
platformRoute((p0) => const DebtView()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// All the pages that drawer can navigate to
|
||||||
|
enum Pages {
|
||||||
|
/// [HomeView]
|
||||||
|
home,
|
||||||
|
|
||||||
|
/// [GraphView]
|
||||||
|
graphs,
|
||||||
|
|
||||||
|
/// [RecurringEntriesView]
|
||||||
|
recurringEntries,
|
||||||
|
|
||||||
|
/// [DebtView]
|
||||||
|
debts
|
||||||
|
}
|
||||||
|
|
141
lib/views/debts/debt_view.dart
Normal file
141
lib/views/debts/debt_view.dart
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
import 'package:prasule/api/debt_scenario.dart';
|
||||||
|
import 'package:prasule/api/wallet.dart';
|
||||||
|
import 'package:prasule/api/wallet_manager.dart';
|
||||||
|
import 'package:prasule/main.dart';
|
||||||
|
import 'package:prasule/pw/platformroute.dart';
|
||||||
|
import 'package:prasule/util/drawer.dart';
|
||||||
|
import 'package:prasule/views/debts/setup_debt_scenario.dart';
|
||||||
|
import 'package:prasule/views/setup.dart';
|
||||||
|
|
||||||
|
/// Shows the selected [DebtScenario]
|
||||||
|
class DebtView extends StatefulWidget {
|
||||||
|
/// Shows the selected [DebtScenario]
|
||||||
|
const DebtView({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<DebtView> createState() => _DebtViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DebtViewState extends State<DebtView> {
|
||||||
|
List<Wallet> wallets = [];
|
||||||
|
void loadWallet() {
|
||||||
|
wallets = WalletManager.listWallets();
|
||||||
|
if (wallets.isEmpty && mounted) {
|
||||||
|
unawaited(
|
||||||
|
Navigator.of(context)
|
||||||
|
.pushReplacement(platformRoute((c) => const SetupView())),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
loadWallet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return DefaultTabController(
|
||||||
|
length: WalletManager.selectedWallet.debts.isEmpty
|
||||||
|
? 1
|
||||||
|
: WalletManager.selectedWallet.debts.length,
|
||||||
|
child: Scaffold(
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||||
|
foregroundColor: Theme.of(context).colorScheme.onPrimary,
|
||||||
|
child: const Icon(Icons.add),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context)
|
||||||
|
.push(platformRoute((c) => const SetupDebtScenario()))
|
||||||
|
.then((v) {
|
||||||
|
setState(() {});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
appBar: AppBar(
|
||||||
|
title: DropdownButton<int>(
|
||||||
|
value: wallets.indexOf(
|
||||||
|
wallets
|
||||||
|
.where((w) => w.name == WalletManager.selectedWallet.name)
|
||||||
|
.first,
|
||||||
|
),
|
||||||
|
items: [
|
||||||
|
...wallets.map(
|
||||||
|
(e) => DropdownMenuItem(
|
||||||
|
value: wallets.indexOf(
|
||||||
|
e,
|
||||||
|
),
|
||||||
|
child: Text(e.name),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: -1,
|
||||||
|
child: Text(AppLocalizations.of(context).newWallet),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
onChanged: (v) async {
|
||||||
|
if (v == null || v == -1) {
|
||||||
|
await Navigator.of(context).push(
|
||||||
|
platformRoute(
|
||||||
|
(c) => const SetupView(
|
||||||
|
newWallet: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
wallets = WalletManager.listWallets();
|
||||||
|
logger.i(wallets.length);
|
||||||
|
WalletManager.selectedWallet = wallets.last;
|
||||||
|
setState(() {});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
WalletManager.selectedWallet = wallets[v];
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
bottom: TabBar(
|
||||||
|
tabs: (WalletManager.selectedWallet.debts.isEmpty)
|
||||||
|
? [Text(AppLocalizations.of(context).welcome)]
|
||||||
|
: List.generate(
|
||||||
|
WalletManager.selectedWallet.debts.length,
|
||||||
|
(index) => Tab(
|
||||||
|
text: WalletManager.selectedWallet.debts[index].name,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
drawer: makeDrawer(context, Pages.debts),
|
||||||
|
body: TabBarView(
|
||||||
|
children: (WalletManager.selectedWallet.debts.isEmpty)
|
||||||
|
? [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
AppLocalizations.of(context).noDebtScenarios,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 20,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(AppLocalizations.of(context).noDebtScenariosSub),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
: List.generate(
|
||||||
|
WalletManager.selectedWallet.debts.length,
|
||||||
|
(c) => const Placeholder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
254
lib/views/debts/setup_debt_scenario.dart
Normal file
254
lib/views/debts/setup_debt_scenario.dart
Normal file
|
@ -0,0 +1,254 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
import 'package:prasule/api/debt_person.dart';
|
||||||
|
import 'package:prasule/api/debt_scenario.dart';
|
||||||
|
import 'package:prasule/api/wallet_manager.dart';
|
||||||
|
import 'package:prasule/pw/platformfield.dart';
|
||||||
|
import 'package:prasule/util/show_message.dart';
|
||||||
|
|
||||||
|
/// Used to create and/or edit [DebtScenario]s
|
||||||
|
class SetupDebtScenario extends StatefulWidget {
|
||||||
|
/// Used to create and/or edit [DebtScenario]s
|
||||||
|
const SetupDebtScenario({super.key, this.toEdit});
|
||||||
|
|
||||||
|
/// If not null, loads this [DebtScenario]'s data for editing
|
||||||
|
final DebtScenario? toEdit;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SetupDebtScenario> createState() => _SetupDebtScenarioState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SetupDebtScenarioState extends State<SetupDebtScenario> {
|
||||||
|
late DebtScenario _scenario;
|
||||||
|
final _nameController = TextEditingController();
|
||||||
|
final GlobalKey<FormState> _formKey = GlobalKey();
|
||||||
|
|
||||||
|
/// Stores data for each [ExpansionPanel]
|
||||||
|
final List<bool> _isOpen = [];
|
||||||
|
final List<TextEditingController> _panelControllers = [];
|
||||||
|
bool _isLoading = true;
|
||||||
|
@override
|
||||||
|
void didChangeDependencies() {
|
||||||
|
super.didChangeDependencies();
|
||||||
|
if (!_isLoading) return;
|
||||||
|
_scenario = widget.toEdit ??
|
||||||
|
DebtScenario(
|
||||||
|
id: WalletManager.selectedWallet.nextDebtId,
|
||||||
|
name: AppLocalizations.of(context).debtNamePlaceholder,
|
||||||
|
isArchived: false,
|
||||||
|
people: [],
|
||||||
|
entries: [],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Load stuff from the scenario
|
||||||
|
_nameController.text = _scenario.name;
|
||||||
|
_isOpen.addAll(List.filled(_scenario.people.length, false));
|
||||||
|
_panelControllers.addAll(
|
||||||
|
List.filled(_scenario.people.length * 2, TextEditingController()),
|
||||||
|
);
|
||||||
|
|
||||||
|
_isLoading = false;
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
child: Center(
|
||||||
|
child: SizedBox(
|
||||||
|
width: MediaQuery.of(context).size.width * 0.95,
|
||||||
|
child: Form(
|
||||||
|
key: _formKey,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: SizedBox(
|
||||||
|
width: MediaQuery.of(context).size.width * 0.8,
|
||||||
|
child: PlatformField(
|
||||||
|
labelText: AppLocalizations.of(context).name,
|
||||||
|
controller: _nameController,
|
||||||
|
validator: (input) {
|
||||||
|
if (input == null || input.isEmpty) {
|
||||||
|
return AppLocalizations.of(context).errorEmptyName;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 15,
|
||||||
|
),
|
||||||
|
Text(AppLocalizations.of(context).people),
|
||||||
|
const SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
LimitedBox(
|
||||||
|
maxHeight: MediaQuery.of(context).size.height * 0.6,
|
||||||
|
child: Container(
|
||||||
|
width: MediaQuery.of(context).size.width * 0.8,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: (_scenario.people.isEmpty)
|
||||||
|
? Text(AppLocalizations.of(context).addSomePeople)
|
||||||
|
: ExpansionPanelList(
|
||||||
|
expansionCallback: (panelIndex, isExpanded) {
|
||||||
|
_isOpen[panelIndex] = isExpanded;
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
children: List<ExpansionPanel>.generate(
|
||||||
|
_scenario.people.length,
|
||||||
|
(index) => ExpansionPanel(
|
||||||
|
isExpanded: _isOpen[index],
|
||||||
|
headerBuilder: (context, isOpen) => Row(
|
||||||
|
mainAxisAlignment:
|
||||||
|
MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
child: SizedBox(
|
||||||
|
width: MediaQuery.of(context)
|
||||||
|
.size
|
||||||
|
.width *
|
||||||
|
0.45,
|
||||||
|
child: PlatformField(
|
||||||
|
labelText:
|
||||||
|
AppLocalizations.of(context)
|
||||||
|
.name,
|
||||||
|
controller:
|
||||||
|
_panelControllers[index],
|
||||||
|
onChanged: (input) {
|
||||||
|
_scenario.people[index].name =
|
||||||
|
input;
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
validator: (input) {
|
||||||
|
if (input == null ||
|
||||||
|
input.isEmpty) {
|
||||||
|
return AppLocalizations.of(
|
||||||
|
context,
|
||||||
|
).errorEmptyName;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
_scenario.people.removeAt(index);
|
||||||
|
_panelControllers.removeRange(
|
||||||
|
index,
|
||||||
|
index + 2,
|
||||||
|
);
|
||||||
|
_isOpen.removeAt(index);
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.delete_forever,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: SizedBox(
|
||||||
|
width: MediaQuery.of(context)
|
||||||
|
.size
|
||||||
|
.width *
|
||||||
|
0.8,
|
||||||
|
child: PlatformField(
|
||||||
|
labelText:
|
||||||
|
AppLocalizations.of(context)
|
||||||
|
.bankAccount,
|
||||||
|
controller: _panelControllers[
|
||||||
|
index + 1],
|
||||||
|
onChanged: (input) {
|
||||||
|
_scenario.people[index]
|
||||||
|
.bankAccount =
|
||||||
|
(input.isEmpty)
|
||||||
|
? null
|
||||||
|
: input;
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
_scenario.people.add(
|
||||||
|
DebtPerson(id: _scenario.nextPersonId, name: ""),
|
||||||
|
);
|
||||||
|
_isOpen.add(false);
|
||||||
|
_panelControllers.addAll(
|
||||||
|
[TextEditingController(), TextEditingController()],
|
||||||
|
);
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.add),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 15,
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (!_formKey.currentState!.validate()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_scenario.people.isEmpty) {
|
||||||
|
showMessage(
|
||||||
|
AppLocalizations.of(context).noPersonError,
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_scenario.name = _nameController.text;
|
||||||
|
if (widget.toEdit != null) {
|
||||||
|
// If editing, only replace
|
||||||
|
WalletManager.selectedWallet.debts[WalletManager
|
||||||
|
.selectedWallet.debts
|
||||||
|
.indexWhere((d) => d.id == widget.toEdit!.id)] =
|
||||||
|
_scenario;
|
||||||
|
} else {
|
||||||
|
// else add new
|
||||||
|
WalletManager.selectedWallet.debts.add(_scenario);
|
||||||
|
}
|
||||||
|
WalletManager.saveWallet(WalletManager.selectedWallet);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: Text(AppLocalizations.of(context).save),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,7 +31,6 @@ class GraphView extends StatefulWidget {
|
||||||
|
|
||||||
class _GraphViewState extends State<GraphView> {
|
class _GraphViewState extends State<GraphView> {
|
||||||
var _selectedDate = DateTime.now();
|
var _selectedDate = DateTime.now();
|
||||||
Wallet? selectedWallet;
|
|
||||||
List<Wallet> wallets = [];
|
List<Wallet> wallets = [];
|
||||||
String? locale;
|
String? locale;
|
||||||
bool yearly = true;
|
bool yearly = true;
|
||||||
|
@ -48,9 +47,8 @@ class _GraphViewState extends State<GraphView> {
|
||||||
yearly ? 12 : DateTime(d.year, d.month, 0).day,
|
yearly ? 12 : DateTime(d.year, d.month, 0).day,
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
if (selectedWallet == null) return [];
|
|
||||||
for (var i = 0; i < data.length; i++) {
|
for (var i = 0; i < data.length; i++) {
|
||||||
final entriesForRange = selectedWallet!.entries.where(
|
final entriesForRange = WalletManager.selectedWallet.entries.where(
|
||||||
(element) =>
|
(element) =>
|
||||||
((!yearly)
|
((!yearly)
|
||||||
? element.date.month == _selectedDate.month &&
|
? element.date.month == _selectedDate.month &&
|
||||||
|
@ -80,9 +78,9 @@ class _GraphViewState extends State<GraphView> {
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
selectedWallet = wallets.first;
|
WalletManager.selectedWallet = wallets.first;
|
||||||
availableYears.clear();
|
availableYears.clear();
|
||||||
for (final entry in selectedWallet!.entries) {
|
for (final entry in WalletManager.selectedWallet.entries) {
|
||||||
if (!availableYears.any((element) => element.value == entry.date.year)) {
|
if (!availableYears.any((element) => element.value == entry.date.year)) {
|
||||||
availableYears.add(
|
availableYears.add(
|
||||||
WheelChoice<int>(
|
WheelChoice<int>(
|
||||||
|
@ -200,10 +198,10 @@ class _GraphViewState extends State<GraphView> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
title: DropdownButton<int>(
|
title: DropdownButton<int>(
|
||||||
value: (selectedWallet == null)
|
value: wallets.indexOf(
|
||||||
? -1
|
wallets
|
||||||
: wallets.indexOf(
|
.where((w) => w.name == WalletManager.selectedWallet.name)
|
||||||
wallets.where((w) => w.name == selectedWallet!.name).first,
|
.first,
|
||||||
),
|
),
|
||||||
items: [
|
items: [
|
||||||
...wallets.map(
|
...wallets.map(
|
||||||
|
@ -230,11 +228,11 @@ class _GraphViewState extends State<GraphView> {
|
||||||
);
|
);
|
||||||
wallets = WalletManager.listWallets();
|
wallets = WalletManager.listWallets();
|
||||||
logger.i(wallets.length);
|
logger.i(wallets.length);
|
||||||
selectedWallet = wallets.last;
|
WalletManager.selectedWallet = wallets.last;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
selectedWallet = wallets[v];
|
WalletManager.selectedWallet = wallets[v];
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -253,8 +251,8 @@ class _GraphViewState extends State<GraphView> {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.then((value) async {
|
.then((value) async {
|
||||||
selectedWallet =
|
WalletManager.selectedWallet = WalletManager.loadWallet(
|
||||||
WalletManager.loadWallet(selectedWallet!.name);
|
WalletManager.selectedWallet.name);
|
||||||
final s = await SharedPreferences.getInstance();
|
final s = await SharedPreferences.getInstance();
|
||||||
chartType = s.getInt("monthlygraph") ?? 2;
|
chartType = s.getInt("monthlygraph") ?? 2;
|
||||||
setState(() {});
|
setState(() {});
|
||||||
|
@ -266,17 +264,13 @@ class _GraphViewState extends State<GraphView> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
drawer: makeDrawer(context, 2),
|
drawer: makeDrawer(context, Pages.graphs),
|
||||||
body: TabBarView(
|
body: TabBarView(
|
||||||
children: [
|
children: [
|
||||||
// EXPENSE TAB
|
// EXPENSE TAB
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: (selectedWallet == null)
|
child: SizedBox(
|
||||||
? const CircularProgressIndicator(
|
|
||||||
strokeWidth: 5,
|
|
||||||
)
|
|
||||||
: SizedBox(
|
|
||||||
width: MediaQuery.of(context).size.width,
|
width: MediaQuery.of(context).size.width,
|
||||||
height: MediaQuery.of(context).size.height,
|
height: MediaQuery.of(context).size.height,
|
||||||
child: Column(
|
child: Column(
|
||||||
|
@ -285,8 +279,7 @@ class _GraphViewState extends State<GraphView> {
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 200,
|
width: 200,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment:
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
AppLocalizations.of(context).monthly,
|
AppLocalizations.of(context).monthly,
|
||||||
|
@ -298,8 +291,7 @@ class _GraphViewState extends State<GraphView> {
|
||||||
value: yearly,
|
value: yearly,
|
||||||
onChanged: (v) async {
|
onChanged: (v) async {
|
||||||
yearly = v;
|
yearly = v;
|
||||||
final s =
|
final s = await SharedPreferences.getInstance();
|
||||||
await SharedPreferences.getInstance();
|
|
||||||
chartType = yearly
|
chartType = yearly
|
||||||
? (s.getInt("yearlygraph") ?? 1)
|
? (s.getInt("yearlygraph") ?? 1)
|
||||||
: (s.getInt("monthlygraph") ?? 2);
|
: (s.getInt("monthlygraph") ?? 2);
|
||||||
|
@ -319,8 +311,8 @@ class _GraphViewState extends State<GraphView> {
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
boxShadow: (MediaQuery.of(context)
|
boxShadow:
|
||||||
.platformBrightness ==
|
(MediaQuery.of(context).platformBrightness ==
|
||||||
Brightness.light)
|
Brightness.light)
|
||||||
? [
|
? [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
|
@ -334,12 +326,9 @@ class _GraphViewState extends State<GraphView> {
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
: null,
|
: null,
|
||||||
color: (MediaQuery.of(context)
|
color: (MediaQuery.of(context).platformBrightness ==
|
||||||
.platformBrightness ==
|
|
||||||
Brightness.dark)
|
Brightness.dark)
|
||||||
? Theme.of(context)
|
? Theme.of(context).colorScheme.secondaryContainer
|
||||||
.colorScheme
|
|
||||||
.secondaryContainer
|
|
||||||
: Theme.of(context).colorScheme.surface,
|
: Theme.of(context).colorScheme.surface,
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
@ -366,37 +355,32 @@ class _GraphViewState extends State<GraphView> {
|
||||||
height: 15,
|
height: 15,
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: MediaQuery.of(context).size.width *
|
width: MediaQuery.of(context).size.width * 0.9,
|
||||||
0.9,
|
|
||||||
height:
|
height:
|
||||||
MediaQuery.of(context).size.height *
|
MediaQuery.of(context).size.height * 0.35,
|
||||||
0.35,
|
|
||||||
child: (chartType == null)
|
child: (chartType == null)
|
||||||
? const CircularProgressIndicator()
|
? const CircularProgressIndicator()
|
||||||
: (chartType == 1)
|
: (chartType == 1)
|
||||||
? ExpensesBarChart(
|
? ExpensesBarChart(
|
||||||
currency:
|
currency: WalletManager
|
||||||
selectedWallet!.currency,
|
.selectedWallet.currency,
|
||||||
date: _selectedDate,
|
date: _selectedDate,
|
||||||
locale: locale ?? "en",
|
locale: locale ?? "en",
|
||||||
yearly: yearly,
|
yearly: yearly,
|
||||||
expenseData:
|
expenseData: generateChartData(
|
||||||
generateChartData(
|
|
||||||
EntryType.expense,
|
EntryType.expense,
|
||||||
),
|
),
|
||||||
incomeData: const [],
|
incomeData: const [],
|
||||||
)
|
)
|
||||||
: Padding(
|
: Padding(
|
||||||
padding:
|
padding: const EdgeInsets.all(8),
|
||||||
const EdgeInsets.all(8),
|
|
||||||
child: ExpensesLineChart(
|
child: ExpensesLineChart(
|
||||||
currency: selectedWallet!
|
currency: WalletManager
|
||||||
.currency,
|
.selectedWallet.currency,
|
||||||
date: _selectedDate,
|
date: _selectedDate,
|
||||||
locale: locale ?? "en",
|
locale: locale ?? "en",
|
||||||
yearly: yearly,
|
yearly: yearly,
|
||||||
expenseData:
|
expenseData: generateChartData(
|
||||||
generateChartData(
|
|
||||||
EntryType.expense,
|
EntryType.expense,
|
||||||
),
|
),
|
||||||
incomeData: const [],
|
incomeData: const [],
|
||||||
|
@ -413,8 +397,8 @@ class _GraphViewState extends State<GraphView> {
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
boxShadow: (MediaQuery.of(context)
|
boxShadow:
|
||||||
.platformBrightness ==
|
(MediaQuery.of(context).platformBrightness ==
|
||||||
Brightness.light)
|
Brightness.light)
|
||||||
? [
|
? [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
|
@ -428,12 +412,9 @@ class _GraphViewState extends State<GraphView> {
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
: null,
|
: null,
|
||||||
color: (MediaQuery.of(context)
|
color: (MediaQuery.of(context).platformBrightness ==
|
||||||
.platformBrightness ==
|
|
||||||
Brightness.dark)
|
Brightness.dark)
|
||||||
? Theme.of(context)
|
? Theme.of(context).colorScheme.secondaryContainer
|
||||||
.colorScheme
|
|
||||||
.secondaryContainer
|
|
||||||
: Theme.of(context).colorScheme.surface,
|
: Theme.of(context).colorScheme.surface,
|
||||||
),
|
),
|
||||||
width: MediaQuery.of(context).size.width * 0.95,
|
width: MediaQuery.of(context).size.width * 0.95,
|
||||||
|
@ -467,23 +448,23 @@ class _GraphViewState extends State<GraphView> {
|
||||||
child: CategoriesPieChart(
|
child: CategoriesPieChart(
|
||||||
// TODO: better size adaptivity without overflow
|
// TODO: better size adaptivity without overflow
|
||||||
locale: locale ?? "en",
|
locale: locale ?? "en",
|
||||||
symbol: selectedWallet!.currency.symbol,
|
symbol: WalletManager
|
||||||
entries: selectedWallet!.entries
|
.selectedWallet.currency.symbol,
|
||||||
|
entries: WalletManager.selectedWallet.entries
|
||||||
.where(
|
.where(
|
||||||
(element) =>
|
(element) =>
|
||||||
((!yearly)
|
((!yearly)
|
||||||
? element.date.month ==
|
? element.date.month ==
|
||||||
_selectedDate
|
_selectedDate.month &&
|
||||||
.month &&
|
|
||||||
element.date.year ==
|
element.date.year ==
|
||||||
_selectedDate.year
|
_selectedDate.year
|
||||||
: element.date.year ==
|
: element.date.year ==
|
||||||
_selectedDate.year) &&
|
_selectedDate.year) &&
|
||||||
element.type ==
|
element.type == EntryType.expense,
|
||||||
EntryType.expense,
|
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
categories: selectedWallet!.categories,
|
categories:
|
||||||
|
WalletManager.selectedWallet.categories,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -496,11 +477,7 @@ class _GraphViewState extends State<GraphView> {
|
||||||
), // Expense Tab END
|
), // Expense Tab END
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: (selectedWallet == null)
|
child: SizedBox(
|
||||||
? const CircularProgressIndicator(
|
|
||||||
strokeWidth: 5,
|
|
||||||
)
|
|
||||||
: SizedBox(
|
|
||||||
width: MediaQuery.of(context).size.width,
|
width: MediaQuery.of(context).size.width,
|
||||||
height: MediaQuery.of(context).size.height,
|
height: MediaQuery.of(context).size.height,
|
||||||
child: Column(
|
child: Column(
|
||||||
|
@ -509,8 +486,7 @@ class _GraphViewState extends State<GraphView> {
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 200,
|
width: 200,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment:
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
AppLocalizations.of(context).monthly,
|
AppLocalizations.of(context).monthly,
|
||||||
|
@ -522,8 +498,7 @@ class _GraphViewState extends State<GraphView> {
|
||||||
value: yearly,
|
value: yearly,
|
||||||
onChanged: (v) async {
|
onChanged: (v) async {
|
||||||
yearly = v;
|
yearly = v;
|
||||||
final s =
|
final s = await SharedPreferences.getInstance();
|
||||||
await SharedPreferences.getInstance();
|
|
||||||
chartType = yearly
|
chartType = yearly
|
||||||
? (s.getInt("yearlygraph") ?? 1)
|
? (s.getInt("yearlygraph") ?? 1)
|
||||||
: (s.getInt("monthlygraph") ?? 2);
|
: (s.getInt("monthlygraph") ?? 2);
|
||||||
|
@ -543,8 +518,8 @@ class _GraphViewState extends State<GraphView> {
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
boxShadow: (MediaQuery.of(context)
|
boxShadow:
|
||||||
.platformBrightness ==
|
(MediaQuery.of(context).platformBrightness ==
|
||||||
Brightness.light)
|
Brightness.light)
|
||||||
? [
|
? [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
|
@ -558,12 +533,9 @@ class _GraphViewState extends State<GraphView> {
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
: null,
|
: null,
|
||||||
color: (MediaQuery.of(context)
|
color: (MediaQuery.of(context).platformBrightness ==
|
||||||
.platformBrightness ==
|
|
||||||
Brightness.dark)
|
Brightness.dark)
|
||||||
? Theme.of(context)
|
? Theme.of(context).colorScheme.secondaryContainer
|
||||||
.colorScheme
|
|
||||||
.secondaryContainer
|
|
||||||
: Theme.of(context).colorScheme.surface,
|
: Theme.of(context).colorScheme.surface,
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
@ -590,17 +562,15 @@ class _GraphViewState extends State<GraphView> {
|
||||||
height: 15,
|
height: 15,
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: MediaQuery.of(context).size.width *
|
width: MediaQuery.of(context).size.width * 0.9,
|
||||||
0.9,
|
|
||||||
height:
|
height:
|
||||||
MediaQuery.of(context).size.height *
|
MediaQuery.of(context).size.height * 0.35,
|
||||||
0.35,
|
|
||||||
child: (chartType == null)
|
child: (chartType == null)
|
||||||
? const CircularProgressIndicator()
|
? const CircularProgressIndicator()
|
||||||
: (chartType == 1)
|
: (chartType == 1)
|
||||||
? ExpensesBarChart(
|
? ExpensesBarChart(
|
||||||
currency:
|
currency: WalletManager
|
||||||
selectedWallet!.currency,
|
.selectedWallet.currency,
|
||||||
date: _selectedDate,
|
date: _selectedDate,
|
||||||
locale: locale ?? "en",
|
locale: locale ?? "en",
|
||||||
yearly: yearly,
|
yearly: yearly,
|
||||||
|
@ -610,17 +580,15 @@ class _GraphViewState extends State<GraphView> {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Padding(
|
: Padding(
|
||||||
padding:
|
padding: const EdgeInsets.all(8),
|
||||||
const EdgeInsets.all(8),
|
|
||||||
child: ExpensesLineChart(
|
child: ExpensesLineChart(
|
||||||
currency: selectedWallet!
|
currency: WalletManager
|
||||||
.currency,
|
.selectedWallet.currency,
|
||||||
date: _selectedDate,
|
date: _selectedDate,
|
||||||
locale: locale ?? "en",
|
locale: locale ?? "en",
|
||||||
yearly: yearly,
|
yearly: yearly,
|
||||||
expenseData: const [],
|
expenseData: const [],
|
||||||
incomeData:
|
incomeData: generateChartData(
|
||||||
generateChartData(
|
|
||||||
EntryType.income,
|
EntryType.income,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -636,8 +604,8 @@ class _GraphViewState extends State<GraphView> {
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
boxShadow: (MediaQuery.of(context)
|
boxShadow:
|
||||||
.platformBrightness ==
|
(MediaQuery.of(context).platformBrightness ==
|
||||||
Brightness.light)
|
Brightness.light)
|
||||||
? [
|
? [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
|
@ -651,12 +619,9 @@ class _GraphViewState extends State<GraphView> {
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
: null,
|
: null,
|
||||||
color: (MediaQuery.of(context)
|
color: (MediaQuery.of(context).platformBrightness ==
|
||||||
.platformBrightness ==
|
|
||||||
Brightness.dark)
|
Brightness.dark)
|
||||||
? Theme.of(context)
|
? Theme.of(context).colorScheme.secondaryContainer
|
||||||
.colorScheme
|
|
||||||
.secondaryContainer
|
|
||||||
: Theme.of(context).colorScheme.surface,
|
: Theme.of(context).colorScheme.surface,
|
||||||
),
|
),
|
||||||
width: MediaQuery.of(context).size.width * 0.95,
|
width: MediaQuery.of(context).size.width * 0.95,
|
||||||
|
@ -688,23 +653,23 @@ class _GraphViewState extends State<GraphView> {
|
||||||
padding: const EdgeInsets.all(6),
|
padding: const EdgeInsets.all(6),
|
||||||
child: CategoriesPieChart(
|
child: CategoriesPieChart(
|
||||||
locale: locale ?? "en",
|
locale: locale ?? "en",
|
||||||
symbol: selectedWallet!.currency.symbol,
|
symbol: WalletManager
|
||||||
entries: selectedWallet!.entries
|
.selectedWallet.currency.symbol,
|
||||||
|
entries: WalletManager.selectedWallet.entries
|
||||||
.where(
|
.where(
|
||||||
(element) =>
|
(element) =>
|
||||||
((!yearly)
|
((!yearly)
|
||||||
? element.date.month ==
|
? element.date.month ==
|
||||||
_selectedDate
|
_selectedDate.month &&
|
||||||
.month &&
|
|
||||||
element.date.year ==
|
element.date.year ==
|
||||||
_selectedDate.year
|
_selectedDate.year
|
||||||
: element.date.year ==
|
: element.date.year ==
|
||||||
_selectedDate.year) &&
|
_selectedDate.year) &&
|
||||||
element.type ==
|
element.type == EntryType.income,
|
||||||
EntryType.income,
|
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
categories: selectedWallet!.categories,
|
categories:
|
||||||
|
WalletManager.selectedWallet.categories,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -94,8 +94,10 @@ class _HomeViewState extends State<HomeView> {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
drawer: makeDrawer(context, 1),
|
drawer: makeDrawer(context, Pages.home),
|
||||||
floatingActionButton: SpeedDial(
|
floatingActionButton: SpeedDial(
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||||
|
foregroundColor: Theme.of(context).colorScheme.onPrimary,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||||
),
|
),
|
||||||
|
|
|
@ -31,6 +31,7 @@ class _InitializationScreenState extends State<InitializationScreen> {
|
||||||
.pushReplacement(platformRoute((c) => const SetupView()));
|
.pushReplacement(platformRoute((c) => const SetupView()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
WalletManager.selectedWallet = wallets.first;
|
||||||
Navigator.of(context)
|
Navigator.of(context)
|
||||||
.pushReplacement(platformRoute((c) => const HomeView()));
|
.pushReplacement(platformRoute((c) => const HomeView()));
|
||||||
});
|
});
|
||||||
|
|
|
@ -64,7 +64,7 @@ class _RecurringEntriesViewState extends State<RecurringEntriesView> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
drawer: makeDrawer(context, 3),
|
drawer: makeDrawer(context, Pages.recurringEntries),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: DropdownButton<int>(
|
title: DropdownButton<int>(
|
||||||
value: (selectedWallet == null)
|
value: (selectedWallet == null)
|
||||||
|
|
|
@ -167,6 +167,9 @@ class _SetupViewState extends State<SetupView> {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WalletManager.selectedWallet = wallet;
|
||||||
|
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
unawaited(
|
unawaited(
|
||||||
Navigator.of(context).pushReplacement(
|
Navigator.of(context).pushReplacement(
|
||||||
|
|
Loading…
Reference in a new issue