Compare commits
10 Commits
c5f1a4e9ba
...
main
Author | SHA1 | Date | |
---|---|---|---|
70c26be8e3 | |||
b177b6317e | |||
3c52239efc | |||
de4f8306d7 | |||
f77cf7bd38 | |||
716d40c694 | |||
d9f8536f26 | |||
1966f72c93 | |||
e896611bd1 | |||
2d9c93fec4 |
@ -1 +1,4 @@
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
analyzer:
|
||||
errors:
|
||||
unreachable_switch_default: ignore
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -1,7 +1,11 @@
|
||||
// Flutter
|
||||
import '/models/item_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
// Local
|
||||
import '/widgets/cards.dart';
|
||||
|
||||
_launchSite(String url) async {
|
||||
try {
|
||||
if (await canLaunchUrlString(url)) {
|
||||
@ -38,23 +42,34 @@ class HelpPage extends StatelessWidget {
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
TitleCard(title: "Help"),
|
||||
Text(
|
||||
"\t\t This app is meant to be a simple budgeting tool,"
|
||||
" allowing you to view your income and expenses at a high"
|
||||
" level without micro managing specific budget items or"
|
||||
" adding receipts.",
|
||||
" adding receipts."
|
||||
"",
|
||||
),
|
||||
Text(
|
||||
"\n\t\t Tracked items can be swiped left to right for ,"
|
||||
"\n\t\t ${ItemType.expense.plural} are defined as ${ItemType.expense.description.toLowerCase()}"
|
||||
" ${ItemType.income.title} is defined as ${ItemType.income.description.toLowerCase()}"
|
||||
" ${ItemType.asset.plural} are defined as ${ItemType.asset.description.toLowerCase()}"
|
||||
"",
|
||||
),
|
||||
Text(
|
||||
"\n\t\t Tracked items can be swiped left to right for"
|
||||
" Deletion or right to left for Editing. Items are sorted"
|
||||
" from highest to lowest so that the biggest impacts are"
|
||||
" always in view.",
|
||||
" always in view."
|
||||
"",
|
||||
),
|
||||
Text(
|
||||
"\n\t\t To subscribe to app updates, install the Obtanium"
|
||||
" app, then use the URL from the Source Code button below."
|
||||
"\n\t\t To subscribe to Android updates, install Obtanium,"
|
||||
" then use the URL from the Source Code button below."
|
||||
" Otherwise the app needs installed manually by downloading"
|
||||
" APKs from the Source Code /releases/ page.",
|
||||
" APKs from the Source Code /releases/ page. Linux users"
|
||||
" currently need to install and update manually."
|
||||
"",
|
||||
),
|
||||
//Text("Another paragraph.")
|
||||
],
|
||||
|
@ -9,7 +9,6 @@ import '/pages/tracked_item.dart';
|
||||
import '/pages/report.dart';
|
||||
import '/pages/settings.dart';
|
||||
import '/pages/help.dart';
|
||||
import '/db.dart';
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
const HomePage({
|
||||
@ -56,7 +55,7 @@ class _HomePageState extends State<HomePage> {
|
||||
switch (pageSelected) {
|
||||
case 0:
|
||||
page = TrackedItemPage(
|
||||
assetsToLoad: DatabaseHelper.instance.getExpenses(),
|
||||
assetType: ItemType.expense,
|
||||
notifyParent: refresh,
|
||||
);
|
||||
dialog = TrackedItemInputDialog(
|
||||
@ -66,7 +65,7 @@ class _HomePageState extends State<HomePage> {
|
||||
break;
|
||||
case 1:
|
||||
page = TrackedItemPage(
|
||||
assetsToLoad: DatabaseHelper.instance.getIncomes(),
|
||||
assetType: ItemType.income,
|
||||
notifyParent: refresh,
|
||||
);
|
||||
dialog = TrackedItemInputDialog(
|
||||
@ -76,7 +75,7 @@ class _HomePageState extends State<HomePage> {
|
||||
break;
|
||||
case 2:
|
||||
page = TrackedItemPage(
|
||||
assetsToLoad: DatabaseHelper.instance.getAssets(),
|
||||
assetType: ItemType.asset,
|
||||
notifyParent: refresh,
|
||||
);
|
||||
dialog = TrackedItemInputDialog(
|
||||
@ -120,15 +119,15 @@ class _HomePageState extends State<HomePage> {
|
||||
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),
|
||||
|
@ -1,6 +1,5 @@
|
||||
// Flutter
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import '/models/item_type.dart';
|
||||
|
||||
@ -12,8 +11,6 @@ import '/models/tracked_item.dart';
|
||||
/// TODO:
|
||||
/// - 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 = -1,
|
||||
_expenseMonthly = -1,
|
||||
@ -68,18 +65,14 @@ class _ProjectionPageState extends State<ProjectionPage> {
|
||||
_expenseYearly < 0) {
|
||||
_showProjections = false;
|
||||
|
||||
projections = Center(
|
||||
child: SizedBox(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
|
||||
Future.delayed(Duration(seconds: 1), () {
|
||||
setState(() {
|
||||
_showProjections = true;
|
||||
});
|
||||
});
|
||||
} else {
|
||||
}
|
||||
|
||||
if (_showProjections) {
|
||||
double oneMonth = _assetTotal + _incomeMonthly - _expenseMonthly,
|
||||
threeMonths = _assetTotal + (3 * (_incomeMonthly - _expenseMonthly)),
|
||||
sixMonths = _assetTotal + (6 * (_incomeMonthly - _expenseMonthly)),
|
||||
@ -135,6 +128,12 @@ class _ProjectionPageState extends State<ProjectionPage> {
|
||||
proj6,
|
||||
],
|
||||
);
|
||||
} else {
|
||||
projections = Center(
|
||||
child: SizedBox(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Return all of the UI elements.
|
||||
|
@ -1,5 +1,9 @@
|
||||
// Flutter
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
// Local
|
||||
import '/widgets/cards.dart';
|
||||
|
||||
/// TODO:
|
||||
/// - Export DB (JSON?)
|
||||
/// - Import DB (JSON?)
|
||||
@ -14,8 +18,14 @@ class SettingsPage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Text(
|
||||
"No settings yet. :)",
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
TitleCard(title: "Settings"),
|
||||
Text(
|
||||
"No settings exist yet. :)",
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -9,14 +9,15 @@ import '/models/item_type.dart';
|
||||
import '/models/expense.dart';
|
||||
import '/models/frequency.dart';
|
||||
import '/db.dart';
|
||||
import '/widgets/cards.dart';
|
||||
|
||||
class TrackedItemPage extends StatefulWidget {
|
||||
final Future<List<TrackedItem>> assetsToLoad;
|
||||
final ItemType assetType;
|
||||
final Function() notifyParent;
|
||||
|
||||
const TrackedItemPage({
|
||||
super.key,
|
||||
required this.assetsToLoad,
|
||||
required this.assetType,
|
||||
required this.notifyParent,
|
||||
});
|
||||
|
||||
@ -25,12 +26,28 @@ class TrackedItemPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _TrackedItemPageState extends State<TrackedItemPage> {
|
||||
late Future<List<TrackedItem>> _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<List<TrackedItem>>(
|
||||
future: widget.assetsToLoad,
|
||||
future: _assetsToLoad,
|
||||
builder:
|
||||
(BuildContext context, AsyncSnapshot<List<TrackedItem>> snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
@ -46,7 +63,19 @@ class _TrackedItemPageState extends State<TrackedItemPage> {
|
||||
"Add items to get started.",
|
||||
softWrap: true,
|
||||
)
|
||||
: ListView.builder(
|
||||
: Column(
|
||||
children: [
|
||||
TitleCard(title: widget.assetType.plural),
|
||||
/*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];
|
||||
@ -63,7 +92,9 @@ class _TrackedItemPageState extends State<TrackedItemPage> {
|
||||
}
|
||||
final String itemDescription = curr.description;
|
||||
|
||||
final double itemDayAmount, itemMonthAmount, itemYearAmount;
|
||||
final double itemDayAmount,
|
||||
itemMonthAmount,
|
||||
itemYearAmount;
|
||||
final String estimateSymbolDaily,
|
||||
estimateSymbolMonthly,
|
||||
estimateSymbolYearly;
|
||||
@ -73,7 +104,9 @@ class _TrackedItemPageState extends State<TrackedItemPage> {
|
||||
estimateSymbolDaily = curr.frequency!.numDays
|
||||
.toStringAsFixed(2)
|
||||
.endsWith(".00") &&
|
||||
itemDayAmount.toStringAsFixed(3).endsWith("0")
|
||||
itemDayAmount
|
||||
.toStringAsFixed(3)
|
||||
.endsWith("0")
|
||||
? ""
|
||||
: "~";
|
||||
|
||||
@ -82,7 +115,9 @@ class _TrackedItemPageState extends State<TrackedItemPage> {
|
||||
estimateSymbolMonthly = curr.frequency!.timesPerYear
|
||||
.toStringAsFixed(2)
|
||||
.endsWith(".00") &&
|
||||
itemMonthAmount.toStringAsFixed(3).endsWith("0")
|
||||
itemMonthAmount
|
||||
.toStringAsFixed(3)
|
||||
.endsWith("0")
|
||||
? ""
|
||||
: "~";
|
||||
|
||||
@ -90,7 +125,9 @@ class _TrackedItemPageState extends State<TrackedItemPage> {
|
||||
estimateSymbolYearly = curr.frequency!.timesPerYear
|
||||
.toStringAsFixed(2)
|
||||
.endsWith(".00") &&
|
||||
itemYearAmount.toStringAsFixed(3).endsWith("0")
|
||||
itemYearAmount
|
||||
.toStringAsFixed(3)
|
||||
.endsWith("0")
|
||||
? ""
|
||||
: "~";
|
||||
} else {
|
||||
@ -102,7 +139,8 @@ class _TrackedItemPageState extends State<TrackedItemPage> {
|
||||
estimateSymbolYearly = "";
|
||||
}
|
||||
|
||||
final String monthlyTitle = curr.type == ItemType.asset
|
||||
final String monthlyTitle =
|
||||
curr.type == ItemType.asset
|
||||
? ""
|
||||
: " ${Frequency.monthly.title}";
|
||||
|
||||
@ -196,7 +234,8 @@ class _TrackedItemPageState extends State<TrackedItemPage> {
|
||||
children: [
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
itemTitle,
|
||||
@ -221,7 +260,8 @@ class _TrackedItemPageState extends State<TrackedItemPage> {
|
||||
),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
itemTopText,
|
||||
@ -244,6 +284,9 @@ class _TrackedItemPageState extends State<TrackedItemPage> {
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -352,7 +395,6 @@ class _TrackedItemInputDialogState extends State<TrackedItemInputDialog> {
|
||||
: Text("Edit ${_type!.title}"),
|
||||
),
|
||||
content: FutureBuilder<List<TrackedItem>>(
|
||||
// TODO / TBD -- This should no longer only be Expenses.
|
||||
future: items,
|
||||
builder: (BuildContext context,
|
||||
AsyncSnapshot<List<TrackedItem>> snapshot) {
|
||||
|
@ -1,3 +1,4 @@
|
||||
// Flutter
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class TitleCard extends StatelessWidget {
|
||||
@ -15,7 +16,11 @@ class TitleCard extends StatelessWidget {
|
||||
child: Center(
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(fontSize: 20),
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
Reference in New Issue
Block a user