diff --git a/lib/pages/report.dart b/lib/pages/report.dart index f211fb8..abae849 100644 --- a/lib/pages/report.dart +++ b/lib/pages/report.dart @@ -7,9 +7,16 @@ import '/db.dart'; import '/models/tracked_item.dart'; /// TODO: -/// - Projected Assets in: -/// - 1 week, 1 month, 1 quarter, 1 year -/// - 1/2 year? 2 years? 5 years? Allow customization? +/// - Projected Assets: +/// - Allow customization? +/// - Fix bug where editing an item does not reflect immediately when returning to Reports page. +/// - Currently reflects after going back to Reports the 2nd time. + +double _assetTotal = 0, + _expenseMonthly = 0, + _expenseYearly = 0, + _incomeMonthly = 0, + _incomeYearly = 0; class ProjectionPage extends StatefulWidget { const ProjectionPage({ @@ -23,6 +30,7 @@ class ProjectionPage extends StatefulWidget { class _ProjectionPageState extends State { @override Widget build(BuildContext context) { + // Summaries for display as well as calculation of totals for projections. Widget expenseSummary = SummaryCardForTotals( list: DatabaseHelper.instance.getExpenses(), summaryTypeLabel: ItemType.expense.title, @@ -36,16 +44,93 @@ class _ProjectionPageState extends State { summaryTypeLabel: ItemType.asset.title, ); + // Calculations for the projections. + double oneMonth = _assetTotal + _incomeMonthly - _expenseMonthly, + threeMonths = _assetTotal + (3 * (_incomeMonthly - _expenseMonthly)), + sixMonths = _assetTotal + (6 * (_incomeMonthly - _expenseMonthly)), + oneYear = _assetTotal + (_incomeYearly - _expenseYearly), + twoYears = _assetTotal + (2 * (_incomeYearly - _expenseYearly)), + fiveYears = _assetTotal + (5 * (_incomeYearly - _expenseYearly)); + + // Widgets to show the projections. + Widget proj1 = SummaryCard( + name: "One month from now...", + leftText: "", + middleText: oneMonth.toStringAsFixed(2), + rightText: "", + ); + Widget proj2 = SummaryCard( + name: "Three months from now...", + leftText: "", + middleText: threeMonths.toStringAsFixed(2), + rightText: "", + ); + Widget proj3 = SummaryCard( + name: "Half a year from now...", + leftText: "", + middleText: sixMonths.toStringAsFixed(2), + rightText: "", + ); + Widget proj4 = SummaryCard( + name: "One year from now...", + leftText: "", + middleText: oneYear.toStringAsFixed(2), + rightText: "", + ); + Widget proj5 = SummaryCard( + name: "Two years from now...", + leftText: "", + middleText: twoYears.toStringAsFixed(2), + rightText: "", + ); + Widget proj6 = SummaryCard( + name: "Five years from now...", + leftText: "", + middleText: fiveYears.toStringAsFixed(2), + rightText: "", + ); + + // Return all of the UI elements. return ListView( children: [ + TitleCard(title: "Summaries"), expenseSummary, incomeSummary, assetSummary, + TitleCard(title: "Projections"), + proj1, + proj2, + proj3, + proj4, + proj5, + proj6, ], ); } } +class TitleCard extends StatelessWidget { + const TitleCard({ + super.key, + required this.title, + }); + + final String title; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: Center( + child: Text( + title, + style: TextStyle(fontSize: 20), + ), + ), + ); + } +} + class SummaryCardForTotals extends StatelessWidget { const SummaryCardForTotals({ super.key, @@ -70,11 +155,16 @@ class SummaryCardForTotals extends StatelessWidget { // Calculate the total fields based on item type. double dailyTotal = 0, monthlyTotal = 0, yearlyTotal = 0; - bool isAsset = false; + ItemType? itemType; for (TrackedItem e in snapshot.data!) { + if (itemType == null) { + itemType = e.type!; + } else if (itemType != e.type) { + throw "List in SummaryCardForTotals has multiple item types, abort!"; + } + if (e.type == ItemType.asset) { monthlyTotal += e.amount; - isAsset = true; } else { dailyTotal += e.calcComparableAmountDaily(); monthlyTotal += e.calcComparableAmountYearly() / 12; @@ -82,11 +172,33 @@ class SummaryCardForTotals extends StatelessWidget { } } + /* Load page variables based on calculated totals. */ + switch (itemType) { + case ItemType.asset: + _assetTotal = monthlyTotal; + break; + + case ItemType.expense: + _expenseMonthly = monthlyTotal; + _expenseYearly = yearlyTotal; + break; + + case ItemType.income: + _incomeMonthly = monthlyTotal; + _incomeYearly = yearlyTotal; + break; + + default: + throw UnimplementedError( + "Item type ${itemType!.title} not handled in SummaryCardForTotals!", + ); + } + /* Determine what needs displayed for the item type. */ // Header String plural = snapshot.data!.length == 1 ? "" : "s"; String header = "$summaryTypeLabel Total"; - header += isAsset ? "" : "s"; + header += itemType == ItemType.asset ? "" : "s"; header += " (${snapshot.data!.length} Item$plural)"; // Total Fields @@ -97,7 +209,7 @@ class SummaryCardForTotals extends StatelessWidget { yearlyEstimate = yearlyTotal.toStringAsFixed(3).endsWith("0") ? "" : "~"; String leftText = "", middleText = "", rightText = ""; - if (isAsset) { + if (itemType == ItemType.asset) { middleText = "$monthlyEstimate${monthlyTotal.toStringAsFixed(2)}"; } else { leftText = "$dailyEstimate${dailyTotal.toStringAsFixed(2)} Daily"; @@ -118,7 +230,7 @@ class SummaryCardForTotals extends StatelessWidget { } } -class SummaryCard extends StatelessWidget { +class SummaryCard extends StatefulWidget { const SummaryCard({ super.key, required this.name, @@ -132,6 +244,11 @@ class SummaryCard extends StatelessWidget { final String middleText; final String rightText; + @override + State createState() => _SummaryCardState(); +} + +class _SummaryCardState extends State { @override Widget build(BuildContext context) { return Card( @@ -143,7 +260,7 @@ class SummaryCard extends StatelessWidget { child: Column( children: [ Text( - name, + widget.name, style: TextStyle( decoration: TextDecoration.underline, fontSize: 16, @@ -155,19 +272,19 @@ class SummaryCard extends StatelessWidget { flex: 3, ), Text( - leftText, + widget.leftText, ), Spacer( flex: 1, ), Text( - middleText, + widget.middleText, ), Spacer( flex: 1, ), Text( - rightText, + widget.rightText, ), Spacer( flex: 3,