Compare commits

..

No commits in common. "e896611bd1b20c72de4a44235d28bfd64c6e8a6c" and "c5f1a4e9ba18388bffd855b64c1f7e3d700c20e2" have entirely different histories.

4 changed files with 191 additions and 245 deletions

View File

@ -2,26 +2,21 @@ enum ItemType {
expense( expense(
title: "Expense", title: "Expense",
plural: "Expenses", plural: "Expenses",
description: "Items which cost revenue, or decrease asset value.",
), ),
income( income(
title: "Income", title: "Income",
plural: "Income", plural: "Incomes",
description: "Items which bring in revenue, or increase asset value.",
), ),
asset( asset(
title: "Asset", title: "Asset",
plural: "Assets", plural: "Assets",
description: "Value which has been earned and can be spent.",
); );
const ItemType({ const ItemType({
required this.title, required this.title,
required this.plural, required this.plural,
required this.description,
}); });
final String title; final String title;
final String plural; final String plural;
final String description;
} }

View File

@ -45,7 +45,7 @@ class HelpPage extends StatelessWidget {
" adding receipts.", " adding receipts.",
), ),
Text( Text(
"\n\t\t Tracked items can be swiped left to right for" "\n\t\t Tracked items can be swiped left to right for ,"
" Deletion or right to left for Editing. Items are sorted" " Deletion or right to left for Editing. Items are sorted"
" from highest to lowest so that the biggest impacts are" " from highest to lowest so that the biggest impacts are"
" always in view.", " always in view.",

View File

@ -56,7 +56,7 @@ class _HomePageState extends State<HomePage> {
switch (pageSelected) { switch (pageSelected) {
case 0: case 0:
page = TrackedItemPage( page = TrackedItemPage(
assetType: ItemType.expense, assetsToLoad: DatabaseHelper.instance.getExpenses(),
notifyParent: refresh, notifyParent: refresh,
); );
dialog = TrackedItemInputDialog( dialog = TrackedItemInputDialog(
@ -66,7 +66,7 @@ class _HomePageState extends State<HomePage> {
break; break;
case 1: case 1:
page = TrackedItemPage( page = TrackedItemPage(
assetType: ItemType.income, assetsToLoad: DatabaseHelper.instance.getIncomes(),
notifyParent: refresh, notifyParent: refresh,
); );
dialog = TrackedItemInputDialog( dialog = TrackedItemInputDialog(
@ -76,7 +76,7 @@ class _HomePageState extends State<HomePage> {
break; break;
case 2: case 2:
page = TrackedItemPage( page = TrackedItemPage(
assetType: ItemType.asset, assetsToLoad: DatabaseHelper.instance.getAssets(),
notifyParent: refresh, notifyParent: refresh,
); );
dialog = TrackedItemInputDialog( dialog = TrackedItemInputDialog(
@ -120,15 +120,15 @@ class _HomePageState extends State<HomePage> {
destinations: [ destinations: [
NavigationRailDestination( NavigationRailDestination(
icon: Icon(Icons.payment), icon: Icon(Icons.payment),
label: Text(ItemType.expense.plural), label: Text('Expenses'),
), ),
NavigationRailDestination( NavigationRailDestination(
icon: Icon(Icons.account_balance), icon: Icon(Icons.account_balance),
label: Text(ItemType.income.plural), label: Text('Income'),
), ),
NavigationRailDestination( NavigationRailDestination(
icon: Icon(Icons.attach_money), icon: Icon(Icons.attach_money),
label: Text(ItemType.asset.plural), label: Text('Liquid Assets'),
), ),
NavigationRailDestination( NavigationRailDestination(
icon: Icon(Icons.bar_chart), icon: Icon(Icons.bar_chart),

View File

@ -11,12 +11,12 @@ import '/models/frequency.dart';
import '/db.dart'; import '/db.dart';
class TrackedItemPage extends StatefulWidget { class TrackedItemPage extends StatefulWidget {
final ItemType assetType; final Future<List<TrackedItem>> assetsToLoad;
final Function() notifyParent; final Function() notifyParent;
const TrackedItemPage({ const TrackedItemPage({
super.key, super.key,
required this.assetType, required this.assetsToLoad,
required this.notifyParent, required this.notifyParent,
}); });
@ -25,28 +25,12 @@ class TrackedItemPage extends StatefulWidget {
} }
class _TrackedItemPageState extends State<TrackedItemPage> { class _TrackedItemPageState extends State<TrackedItemPage> {
late Future<List<TrackedItem>> _assetsToLoad;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
switch (widget.assetType) {
case ItemType.expense:
_assetsToLoad = DatabaseHelper.instance.getExpenses();
break;
case ItemType.income:
_assetsToLoad = DatabaseHelper.instance.getIncomes();
break;
case ItemType.asset:
_assetsToLoad = DatabaseHelper.instance.getAssets();
break;
default:
throw UnimplementedError("Unsure whch asset group to load.");
}
return FutureBuilder<List<TrackedItem>>( return FutureBuilder<List<TrackedItem>>(
future: _assetsToLoad, future: widget.assetsToLoad,
builder: builder:
(BuildContext context, AsyncSnapshot<List<TrackedItem>> snapshot) { (BuildContext context, AsyncSnapshot<List<TrackedItem>> snapshot) {
if (!snapshot.hasData) { if (!snapshot.hasData) {
@ -62,237 +46,204 @@ class _TrackedItemPageState extends State<TrackedItemPage> {
"Add items to get started.", "Add items to get started.",
softWrap: true, softWrap: true,
) )
: Column( : ListView.builder(
children: [ itemCount: snapshot.data!.length,
Text( itemBuilder: (_, index) {
"${widget.assetType.plural}", final TrackedItem curr = snapshot.data![index];
style: TextStyle(
fontSize: 24.0,
decoration: TextDecoration.underline,
fontWeight: FontWeight.bold,
),
),
/*Text(
"${widget.assetType.description}",
style: TextStyle(
fontSize: 16.0,
decoration: TextDecoration.none,
fontWeight: FontWeight.bold,
),
),*/
Expanded(
child: ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (_, index) {
final TrackedItem curr = snapshot.data![index];
final itemKey = Key(curr.id!.toString()); final itemKey = Key(curr.id!.toString());
final String itemTitle = curr.name; final String itemTitle = curr.name;
final String itemAmount; final String itemAmount;
if (curr.frequency != null) { if (curr.frequency != null) {
itemAmount = itemAmount =
"${curr.amount.toStringAsFixed(2)} ${curr.frequency!.title}"; "${curr.amount.toStringAsFixed(2)} ${curr.frequency!.title}";
} else { } else {
itemAmount = curr.amount.toStringAsFixed(2); itemAmount = curr.amount.toStringAsFixed(2);
} }
final String itemDescription = curr.description; final String itemDescription = curr.description;
final double itemDayAmount, final double itemDayAmount, itemMonthAmount, itemYearAmount;
itemMonthAmount, final String estimateSymbolDaily,
itemYearAmount; estimateSymbolMonthly,
final String estimateSymbolDaily, estimateSymbolYearly;
estimateSymbolMonthly,
estimateSymbolYearly;
if (curr.frequency != null) { if (curr.frequency != null) {
itemDayAmount = curr.calcComparableAmountDaily(); itemDayAmount = curr.calcComparableAmountDaily();
estimateSymbolDaily = curr.frequency!.numDays estimateSymbolDaily = curr.frequency!.numDays
.toStringAsFixed(2) .toStringAsFixed(2)
.endsWith(".00") && .endsWith(".00") &&
itemDayAmount itemDayAmount.toStringAsFixed(3).endsWith("0")
.toStringAsFixed(3) ? ""
.endsWith("0") : "~";
? ""
: "~";
itemMonthAmount = itemMonthAmount =
(curr.calcComparableAmountYearly() / 12); (curr.calcComparableAmountYearly() / 12);
estimateSymbolMonthly = curr.frequency!.timesPerYear estimateSymbolMonthly = curr.frequency!.timesPerYear
.toStringAsFixed(2) .toStringAsFixed(2)
.endsWith(".00") && .endsWith(".00") &&
itemMonthAmount itemMonthAmount.toStringAsFixed(3).endsWith("0")
.toStringAsFixed(3) ? ""
.endsWith("0") : "~";
? ""
: "~";
itemYearAmount = curr.calcComparableAmountYearly(); itemYearAmount = curr.calcComparableAmountYearly();
estimateSymbolYearly = curr.frequency!.timesPerYear estimateSymbolYearly = curr.frequency!.timesPerYear
.toStringAsFixed(2) .toStringAsFixed(2)
.endsWith(".00") && .endsWith(".00") &&
itemYearAmount itemYearAmount.toStringAsFixed(3).endsWith("0")
.toStringAsFixed(3) ? ""
.endsWith("0") : "~";
? "" } else {
: "~"; itemDayAmount = -1;
} else { estimateSymbolDaily = "";
itemDayAmount = -1; itemMonthAmount = curr.amount;
estimateSymbolDaily = ""; estimateSymbolMonthly = "";
itemMonthAmount = curr.amount; itemYearAmount = -1;
estimateSymbolMonthly = ""; estimateSymbolYearly = "";
itemYearAmount = -1; }
estimateSymbolYearly = "";
}
final String monthlyTitle = final String monthlyTitle = curr.type == ItemType.asset
curr.type == ItemType.asset ? ""
? "" : " ${Frequency.monthly.title}";
: " ${Frequency.monthly.title}";
final String itemTopText = itemDayAmount < 0 final String itemTopText = itemDayAmount < 0
? "" ? ""
: "$estimateSymbolDaily${itemDayAmount.toStringAsFixed(2)} ${Frequency.daily.title}"; : "$estimateSymbolDaily${itemDayAmount.toStringAsFixed(2)} ${Frequency.daily.title}";
final String itemMiddleText = itemMonthAmount < 0 final String itemMiddleText = itemMonthAmount < 0
? "" ? ""
: "$estimateSymbolMonthly${itemMonthAmount.toStringAsFixed(2)}$monthlyTitle"; : "$estimateSymbolMonthly${itemMonthAmount.toStringAsFixed(2)}$monthlyTitle";
final String itemBottomText = itemYearAmount < 0 final String itemBottomText = itemYearAmount < 0
? "" ? ""
: "$estimateSymbolYearly${itemYearAmount.toStringAsFixed(2)} ${Frequency.yearly.title}"; : "$estimateSymbolYearly${itemYearAmount.toStringAsFixed(2)} ${Frequency.yearly.title}";
return Padding( return Padding(
padding: const EdgeInsets.all(4.0),
child: Dismissible(
key: itemKey,
background: Container(
color: Colors.red,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Icon(Icons.delete),
Text("Delete"),
],
),
),
secondaryBackground: Container(
color: Colors.orange,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text("Edit"),
Icon(Icons.edit),
],
),
),
onDismissed: (direction) {
setState(() {
snapshot.data!.remove(curr);
switch (direction) {
case DismissDirection.startToEnd:
// Remove the item from the database.
if (curr is Expense) {
DatabaseHelper.instance.removeExpense(
curr.id!,
);
} else if (curr is Income) {
DatabaseHelper.instance.removeIncome(
curr.id!,
);
} else if (curr is Asset) {
DatabaseHelper.instance.removeAsset(
curr.id!,
);
} else {
throw UnimplementedError(
"Cannot remove unimplemented item type.");
}
break;
case DismissDirection.endToStart:
// Open an edit dialog, then remove the item from the list.
showDialog(
context: context,
builder: (_) => AlertDialog(
content: TrackedItemInputDialog(
notifyParent: widget.notifyParent,
entry: curr,
amountText: curr.getAmountText(),
type: curr.type!,
),
),
);
break;
default:
UnimplementedError(
"Direction ${direction.toString()} not recognized.",
);
}
});
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: theme.colorScheme.onPrimary,
),
child: Padding(
padding: const EdgeInsets.all(4.0), padding: const EdgeInsets.all(4.0),
child: Dismissible( child: Row(
key: itemKey, mainAxisSize: MainAxisSize.max,
background: Container( children: [
color: Colors.red, Column(
child: Row( mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Icon(Icons.delete), Text(
Text("Delete"), itemTitle,
style: TextStyle(fontSize: 20.0),
),
Text(
itemAmount,
style: TextStyle(fontSize: 12.0),
),
], ],
), ),
), Expanded(
secondaryBackground: Container( child: Center(
color: Colors.orange, child: Text(
child: Row( itemDescription,
mainAxisAlignment: MainAxisAlignment.end, style: TextStyle(
children: [ fontSize: 12.0,
Text("Edit"),
Icon(Icons.edit),
],
),
),
onDismissed: (direction) {
setState(() {
snapshot.data!.remove(curr);
switch (direction) {
case DismissDirection.startToEnd:
// Remove the item from the database.
if (curr is Expense) {
DatabaseHelper.instance.removeExpense(
curr.id!,
);
} else if (curr is Income) {
DatabaseHelper.instance.removeIncome(
curr.id!,
);
} else if (curr is Asset) {
DatabaseHelper.instance.removeAsset(
curr.id!,
);
} else {
throw UnimplementedError(
"Cannot remove unimplemented item type.");
}
break;
case DismissDirection.endToStart:
// Open an edit dialog, then remove the item from the list.
showDialog(
context: context,
builder: (_) => AlertDialog(
content: TrackedItemInputDialog(
notifyParent: widget.notifyParent,
entry: curr,
amountText: curr.getAmountText(),
type: curr.type!,
),
),
);
break;
default:
UnimplementedError(
"Direction ${direction.toString()} not recognized.",
);
}
});
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: theme.colorScheme.onPrimary,
),
child: Padding(
padding: const EdgeInsets.all(4.0),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
itemTitle,
style: TextStyle(fontSize: 20.0),
),
Text(
itemAmount,
style: TextStyle(fontSize: 12.0),
),
],
), ),
Expanded( softWrap: true,
child: Center( textAlign: TextAlign.center,
child: Text( ),
itemDescription,
style: TextStyle(
fontSize: 12.0,
),
softWrap: true,
textAlign: TextAlign.center,
),
),
),
Column(
crossAxisAlignment:
CrossAxisAlignment.end,
children: [
Text(
itemTopText,
style: TextStyle(fontSize: 12.0),
),
Text(
itemMiddleText,
style: TextStyle(fontSize: 12.0),
),
Text(
itemBottomText,
style: TextStyle(fontSize: 12.0),
),
],
),
],
), ),
), ),
), Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
itemTopText,
style: TextStyle(fontSize: 12.0),
),
Text(
itemMiddleText,
style: TextStyle(fontSize: 12.0),
),
Text(
itemBottomText,
style: TextStyle(fontSize: 12.0),
),
],
),
],
), ),
); ),
}, ),
), ),
), );
], },
); );
}); });
} }