diff --git a/lib/models/item_type.dart b/lib/models/item_type.dart index b13fafd..feca2d3 100644 --- a/lib/models/item_type.dart +++ b/lib/models/item_type.dart @@ -2,21 +2,26 @@ enum ItemType { expense( title: "Expense", plural: "Expenses", + description: "Items which cost revenue, or decrease asset value.", ), income( title: "Income", - plural: "Incomes", + plural: "Income", + description: "Items which bring in revenue, or increase asset value.", ), asset( title: "Asset", plural: "Assets", + description: "Value which has been earned and can be spent.", ); const ItemType({ required this.title, required this.plural, + required this.description, }); final String title; final String plural; + final String description; } diff --git a/lib/pages/home.dart b/lib/pages/home.dart index e105c4f..11796a8 100644 --- a/lib/pages/home.dart +++ b/lib/pages/home.dart @@ -56,7 +56,7 @@ class _HomePageState extends State { switch (pageSelected) { case 0: page = TrackedItemPage( - assetsToLoad: DatabaseHelper.instance.getExpenses(), + assetType: ItemType.expense, notifyParent: refresh, ); dialog = TrackedItemInputDialog( @@ -66,7 +66,7 @@ class _HomePageState extends State { break; case 1: page = TrackedItemPage( - assetsToLoad: DatabaseHelper.instance.getIncomes(), + assetType: ItemType.income, notifyParent: refresh, ); dialog = TrackedItemInputDialog( @@ -76,7 +76,7 @@ class _HomePageState extends State { break; case 2: page = TrackedItemPage( - assetsToLoad: DatabaseHelper.instance.getAssets(), + assetType: ItemType.asset, notifyParent: refresh, ); dialog = TrackedItemInputDialog( @@ -120,15 +120,15 @@ class _HomePageState extends State { destinations: [ NavigationRailDestination( icon: Icon(Icons.payment), - label: Text('Expenses'), + label: Text(ItemType.expense.plural), ), NavigationRailDestination( icon: Icon(Icons.account_balance), - label: Text('Income'), + label: Text(ItemType.income.plural), ), NavigationRailDestination( icon: Icon(Icons.attach_money), - label: Text('Liquid Assets'), + label: Text(ItemType.asset.plural), ), NavigationRailDestination( icon: Icon(Icons.bar_chart), diff --git a/lib/pages/tracked_item.dart b/lib/pages/tracked_item.dart index 7b22af1..ae2992d 100644 --- a/lib/pages/tracked_item.dart +++ b/lib/pages/tracked_item.dart @@ -11,12 +11,12 @@ import '/models/frequency.dart'; import '/db.dart'; class TrackedItemPage extends StatefulWidget { - final Future> assetsToLoad; + final ItemType assetType; final Function() notifyParent; const TrackedItemPage({ super.key, - required this.assetsToLoad, + required this.assetType, required this.notifyParent, }); @@ -25,12 +25,28 @@ class TrackedItemPage extends StatefulWidget { } class _TrackedItemPageState extends State { + late Future> _assetsToLoad; + @override Widget build(BuildContext 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>( - future: widget.assetsToLoad, + future: _assetsToLoad, builder: (BuildContext context, AsyncSnapshot> snapshot) { if (!snapshot.hasData) { @@ -46,204 +62,237 @@ class _TrackedItemPageState extends State { "Add items to get started.", softWrap: true, ) - : ListView.builder( - itemCount: snapshot.data!.length, - itemBuilder: (_, index) { - final TrackedItem curr = snapshot.data![index]; - - final itemKey = Key(curr.id!.toString()); - final String itemTitle = curr.name; - - final String itemAmount; - if (curr.frequency != null) { - itemAmount = - "${curr.amount.toStringAsFixed(2)} ${curr.frequency!.title}"; - } else { - itemAmount = curr.amount.toStringAsFixed(2); - } - final String itemDescription = curr.description; - - final double itemDayAmount, itemMonthAmount, itemYearAmount; - final String estimateSymbolDaily, - estimateSymbolMonthly, - estimateSymbolYearly; - - if (curr.frequency != null) { - itemDayAmount = curr.calcComparableAmountDaily(); - estimateSymbolDaily = curr.frequency!.numDays - .toStringAsFixed(2) - .endsWith(".00") && - itemDayAmount.toStringAsFixed(3).endsWith("0") - ? "" - : "~"; - - itemMonthAmount = - (curr.calcComparableAmountYearly() / 12); - estimateSymbolMonthly = curr.frequency!.timesPerYear - .toStringAsFixed(2) - .endsWith(".00") && - itemMonthAmount.toStringAsFixed(3).endsWith("0") - ? "" - : "~"; - - itemYearAmount = curr.calcComparableAmountYearly(); - estimateSymbolYearly = curr.frequency!.timesPerYear - .toStringAsFixed(2) - .endsWith(".00") && - itemYearAmount.toStringAsFixed(3).endsWith("0") - ? "" - : "~"; - } else { - itemDayAmount = -1; - estimateSymbolDaily = ""; - itemMonthAmount = curr.amount; - estimateSymbolMonthly = ""; - itemYearAmount = -1; - estimateSymbolYearly = ""; - } - - final String monthlyTitle = curr.type == ItemType.asset - ? "" - : " ${Frequency.monthly.title}"; - - final String itemTopText = itemDayAmount < 0 - ? "" - : "$estimateSymbolDaily${itemDayAmount.toStringAsFixed(2)} ${Frequency.daily.title}"; - final String itemMiddleText = itemMonthAmount < 0 - ? "" - : "$estimateSymbolMonthly${itemMonthAmount.toStringAsFixed(2)}$monthlyTitle"; - final String itemBottomText = itemYearAmount < 0 - ? "" - : "$estimateSymbolYearly${itemYearAmount.toStringAsFixed(2)} ${Frequency.yearly.title}"; - - 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), - 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( - child: 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( + children: [ + Text( + "${widget.assetType.plural}", + 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 String itemTitle = curr.name; + + final String itemAmount; + if (curr.frequency != null) { + itemAmount = + "${curr.amount.toStringAsFixed(2)} ${curr.frequency!.title}"; + } else { + itemAmount = curr.amount.toStringAsFixed(2); + } + final String itemDescription = curr.description; + + final double itemDayAmount, + itemMonthAmount, + itemYearAmount; + final String estimateSymbolDaily, + estimateSymbolMonthly, + estimateSymbolYearly; + + if (curr.frequency != null) { + itemDayAmount = curr.calcComparableAmountDaily(); + estimateSymbolDaily = curr.frequency!.numDays + .toStringAsFixed(2) + .endsWith(".00") && + itemDayAmount + .toStringAsFixed(3) + .endsWith("0") + ? "" + : "~"; + + itemMonthAmount = + (curr.calcComparableAmountYearly() / 12); + estimateSymbolMonthly = curr.frequency!.timesPerYear + .toStringAsFixed(2) + .endsWith(".00") && + itemMonthAmount + .toStringAsFixed(3) + .endsWith("0") + ? "" + : "~"; + + itemYearAmount = curr.calcComparableAmountYearly(); + estimateSymbolYearly = curr.frequency!.timesPerYear + .toStringAsFixed(2) + .endsWith(".00") && + itemYearAmount + .toStringAsFixed(3) + .endsWith("0") + ? "" + : "~"; + } else { + itemDayAmount = -1; + estimateSymbolDaily = ""; + itemMonthAmount = curr.amount; + estimateSymbolMonthly = ""; + itemYearAmount = -1; + estimateSymbolYearly = ""; + } + + final String monthlyTitle = + curr.type == ItemType.asset + ? "" + : " ${Frequency.monthly.title}"; + + final String itemTopText = itemDayAmount < 0 + ? "" + : "$estimateSymbolDaily${itemDayAmount.toStringAsFixed(2)} ${Frequency.daily.title}"; + final String itemMiddleText = itemMonthAmount < 0 + ? "" + : "$estimateSymbolMonthly${itemMonthAmount.toStringAsFixed(2)}$monthlyTitle"; + final String itemBottomText = itemYearAmount < 0 + ? "" + : "$estimateSymbolYearly${itemYearAmount.toStringAsFixed(2)} ${Frequency.yearly.title}"; + + 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), + 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( + child: 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), + ), + ], + ), + ], + ), + ), + ), + ), + ); + }, + ), + ), + ], ); }); }