Projections are working! Just 1 issue noticed so far.
This commit is contained in:
parent
15b713215f
commit
e7dc369c4f
@ -7,9 +7,16 @@ import '/db.dart';
|
|||||||
import '/models/tracked_item.dart';
|
import '/models/tracked_item.dart';
|
||||||
|
|
||||||
/// TODO:
|
/// TODO:
|
||||||
/// - Projected Assets in:
|
/// - Projected Assets:
|
||||||
/// - 1 week, 1 month, 1 quarter, 1 year
|
/// - Allow customization?
|
||||||
/// - 1/2 year? 2 years? 5 years? 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 {
|
class ProjectionPage extends StatefulWidget {
|
||||||
const ProjectionPage({
|
const ProjectionPage({
|
||||||
@ -23,6 +30,7 @@ class ProjectionPage extends StatefulWidget {
|
|||||||
class _ProjectionPageState extends State<ProjectionPage> {
|
class _ProjectionPageState extends State<ProjectionPage> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
// Summaries for display as well as calculation of totals for projections.
|
||||||
Widget expenseSummary = SummaryCardForTotals(
|
Widget expenseSummary = SummaryCardForTotals(
|
||||||
list: DatabaseHelper.instance.getExpenses(),
|
list: DatabaseHelper.instance.getExpenses(),
|
||||||
summaryTypeLabel: ItemType.expense.title,
|
summaryTypeLabel: ItemType.expense.title,
|
||||||
@ -36,16 +44,93 @@ class _ProjectionPageState extends State<ProjectionPage> {
|
|||||||
summaryTypeLabel: ItemType.asset.title,
|
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(
|
return ListView(
|
||||||
children: [
|
children: [
|
||||||
|
TitleCard(title: "Summaries"),
|
||||||
expenseSummary,
|
expenseSummary,
|
||||||
incomeSummary,
|
incomeSummary,
|
||||||
assetSummary,
|
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 {
|
class SummaryCardForTotals extends StatelessWidget {
|
||||||
const SummaryCardForTotals({
|
const SummaryCardForTotals({
|
||||||
super.key,
|
super.key,
|
||||||
@ -70,11 +155,16 @@ class SummaryCardForTotals extends StatelessWidget {
|
|||||||
|
|
||||||
// Calculate the total fields based on item type.
|
// Calculate the total fields based on item type.
|
||||||
double dailyTotal = 0, monthlyTotal = 0, yearlyTotal = 0;
|
double dailyTotal = 0, monthlyTotal = 0, yearlyTotal = 0;
|
||||||
bool isAsset = false;
|
ItemType? itemType;
|
||||||
for (TrackedItem e in snapshot.data!) {
|
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) {
|
if (e.type == ItemType.asset) {
|
||||||
monthlyTotal += e.amount;
|
monthlyTotal += e.amount;
|
||||||
isAsset = true;
|
|
||||||
} else {
|
} else {
|
||||||
dailyTotal += e.calcComparableAmountDaily();
|
dailyTotal += e.calcComparableAmountDaily();
|
||||||
monthlyTotal += e.calcComparableAmountYearly() / 12;
|
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. */
|
/* Determine what needs displayed for the item type. */
|
||||||
// Header
|
// Header
|
||||||
String plural = snapshot.data!.length == 1 ? "" : "s";
|
String plural = snapshot.data!.length == 1 ? "" : "s";
|
||||||
String header = "$summaryTypeLabel Total";
|
String header = "$summaryTypeLabel Total";
|
||||||
header += isAsset ? "" : "s";
|
header += itemType == ItemType.asset ? "" : "s";
|
||||||
header += " (${snapshot.data!.length} Item$plural)";
|
header += " (${snapshot.data!.length} Item$plural)";
|
||||||
|
|
||||||
// Total Fields
|
// Total Fields
|
||||||
@ -97,7 +209,7 @@ class SummaryCardForTotals extends StatelessWidget {
|
|||||||
yearlyEstimate =
|
yearlyEstimate =
|
||||||
yearlyTotal.toStringAsFixed(3).endsWith("0") ? "" : "~";
|
yearlyTotal.toStringAsFixed(3).endsWith("0") ? "" : "~";
|
||||||
String leftText = "", middleText = "", rightText = "";
|
String leftText = "", middleText = "", rightText = "";
|
||||||
if (isAsset) {
|
if (itemType == ItemType.asset) {
|
||||||
middleText = "$monthlyEstimate${monthlyTotal.toStringAsFixed(2)}";
|
middleText = "$monthlyEstimate${monthlyTotal.toStringAsFixed(2)}";
|
||||||
} else {
|
} else {
|
||||||
leftText = "$dailyEstimate${dailyTotal.toStringAsFixed(2)} Daily";
|
leftText = "$dailyEstimate${dailyTotal.toStringAsFixed(2)} Daily";
|
||||||
@ -118,7 +230,7 @@ class SummaryCardForTotals extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SummaryCard extends StatelessWidget {
|
class SummaryCard extends StatefulWidget {
|
||||||
const SummaryCard({
|
const SummaryCard({
|
||||||
super.key,
|
super.key,
|
||||||
required this.name,
|
required this.name,
|
||||||
@ -132,6 +244,11 @@ class SummaryCard extends StatelessWidget {
|
|||||||
final String middleText;
|
final String middleText;
|
||||||
final String rightText;
|
final String rightText;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SummaryCard> createState() => _SummaryCardState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SummaryCardState extends State<SummaryCard> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Card(
|
return Card(
|
||||||
@ -143,7 +260,7 @@ class SummaryCard extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
name,
|
widget.name,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
decoration: TextDecoration.underline,
|
decoration: TextDecoration.underline,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
@ -155,19 +272,19 @@ class SummaryCard extends StatelessWidget {
|
|||||||
flex: 3,
|
flex: 3,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
leftText,
|
widget.leftText,
|
||||||
),
|
),
|
||||||
Spacer(
|
Spacer(
|
||||||
flex: 1,
|
flex: 1,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
middleText,
|
widget.middleText,
|
||||||
),
|
),
|
||||||
Spacer(
|
Spacer(
|
||||||
flex: 1,
|
flex: 1,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
rightText,
|
widget.rightText,
|
||||||
),
|
),
|
||||||
Spacer(
|
Spacer(
|
||||||
flex: 3,
|
flex: 3,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user