Compare commits
	
		
			7 Commits
		
	
	
		
			0.1.2
			...
			e896611bd1
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e896611bd1 | |||
| 2d9c93fec4 | |||
| c5f1a4e9ba | |||
| 538a298acd | |||
| cce878ccaa | |||
| 147178e4dd | |||
| c39e09b2b6 | 
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -45,7 +45,7 @@ class HelpPage extends StatelessWidget { | ||||
|                     " adding receipts.", | ||||
|                   ), | ||||
|                   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" | ||||
|                     " from highest to lowest so that the biggest impacts are" | ||||
|                     " always in view.", | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import '/models/item_type.dart'; | ||||
| import 'dart:io'; | ||||
| import 'package:package_info_plus/package_info_plus.dart'; | ||||
|  | ||||
| // Local | ||||
| import '/pages/tracked_item.dart'; | ||||
| @@ -26,14 +27,36 @@ class _HomePageState extends State<HomePage> { | ||||
|     setState(() {}); | ||||
|   } | ||||
|  | ||||
|   PackageInfo _packageInfo = PackageInfo( | ||||
|     appName: 'Unknown', | ||||
|     packageName: 'Unknown', | ||||
|     version: 'Unknown', | ||||
|     buildNumber: 'Unknown', | ||||
|   ); | ||||
|  | ||||
|   Future _initPackageInfo() async { | ||||
|     final PackageInfo info = await PackageInfo.fromPlatform(); | ||||
|     setState(() { | ||||
|       _packageInfo = info; | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     // Get package details | ||||
|     _initPackageInfo(); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     Widget page; | ||||
|     Widget? dialog; | ||||
|  | ||||
|     switch (pageSelected) { | ||||
|       case 0: | ||||
|         page = TrackedItemPage( | ||||
|           assetsToLoad: DatabaseHelper.instance.getExpenses(), | ||||
|           assetType: ItemType.expense, | ||||
|           notifyParent: refresh, | ||||
|         ); | ||||
|         dialog = TrackedItemInputDialog( | ||||
| @@ -43,7 +66,7 @@ class _HomePageState extends State<HomePage> { | ||||
|         break; | ||||
|       case 1: | ||||
|         page = TrackedItemPage( | ||||
|           assetsToLoad: DatabaseHelper.instance.getIncomes(), | ||||
|           assetType: ItemType.income, | ||||
|           notifyParent: refresh, | ||||
|         ); | ||||
|         dialog = TrackedItemInputDialog( | ||||
| @@ -53,7 +76,7 @@ class _HomePageState extends State<HomePage> { | ||||
|         break; | ||||
|       case 2: | ||||
|         page = TrackedItemPage( | ||||
|           assetsToLoad: DatabaseHelper.instance.getAssets(), | ||||
|           assetType: ItemType.asset, | ||||
|           notifyParent: refresh, | ||||
|         ); | ||||
|         dialog = TrackedItemInputDialog( | ||||
| @@ -97,15 +120,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), | ||||
| @@ -129,6 +152,8 @@ class _HomePageState extends State<HomePage> { | ||||
|           } | ||||
|         }); | ||||
|       }, | ||||
|       leading: Text("Menu"), | ||||
|       trailing: Text("v${_packageInfo.version}"), | ||||
|     ); | ||||
|  | ||||
|     Widget main = Container( | ||||
|   | ||||
| @@ -48,15 +48,15 @@ class _ProjectionPageState extends State<ProjectionPage> { | ||||
|     // Summaries for display as well as calculation of totals for projections. | ||||
|     Widget expenseSummary = SummaryCardForTotals( | ||||
|       list: DatabaseHelper.instance.getExpenses(), | ||||
|       summaryTypeLabel: ItemType.expense.title, | ||||
|       itemType: ItemType.expense, | ||||
|     ); | ||||
|     Widget incomeSummary = SummaryCardForTotals( | ||||
|       list: DatabaseHelper.instance.getIncomes(), | ||||
|       summaryTypeLabel: ItemType.income.title, | ||||
|       itemType: ItemType.income, | ||||
|     ); | ||||
|     Widget assetSummary = SummaryCardForTotals( | ||||
|       list: DatabaseHelper.instance.getAssets(), | ||||
|       summaryTypeLabel: ItemType.asset.title, | ||||
|       itemType: ItemType.asset, | ||||
|     ); | ||||
|  | ||||
|     // Calculations for the projections. | ||||
| @@ -155,14 +155,16 @@ class SummaryCardForTotals extends StatelessWidget { | ||||
|   const SummaryCardForTotals({ | ||||
|     super.key, | ||||
|     required this.list, | ||||
|     required this.summaryTypeLabel, | ||||
|     required this.itemType, | ||||
|   }); | ||||
|  | ||||
|   final Future<List<TrackedItem>> list; | ||||
|   final String summaryTypeLabel; | ||||
|   final ItemType itemType; | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     String summaryTypeLabel = itemType.title.toString(); | ||||
|  | ||||
|     return FutureBuilder<List<TrackedItem>>( | ||||
|         future: list, | ||||
|         builder: ( | ||||
| @@ -175,12 +177,9 @@ class SummaryCardForTotals extends StatelessWidget { | ||||
|  | ||||
|           // Calculate the total fields based on item type. | ||||
|           double dailyTotal = 0, monthlyTotal = 0, yearlyTotal = 0; | ||||
|           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) { | ||||
|               throw "List in SummaryCardForTotals has incorrect item types, abort!"; | ||||
|             } | ||||
|  | ||||
|             if (e.type == ItemType.asset) { | ||||
| @@ -194,9 +193,6 @@ class SummaryCardForTotals extends StatelessWidget { | ||||
|  | ||||
|           /* Load page variables based on calculated totals. */ | ||||
|           switch (itemType) { | ||||
|             case null: | ||||
|               break; | ||||
|  | ||||
|             case ItemType.asset: | ||||
|               _assetTotal = monthlyTotal; | ||||
|               break; | ||||
|   | ||||
| @@ -11,12 +11,12 @@ import '/models/frequency.dart'; | ||||
| import '/db.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 +25,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 +62,26 @@ class _TrackedItemPageState extends State<TrackedItemPage> { | ||||
|                   "Add items to get started.", | ||||
|                   softWrap: true, | ||||
|                 ) | ||||
|               : ListView.builder( | ||||
|               : 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]; | ||||
| @@ -63,7 +98,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 +110,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 +121,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 +131,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 +145,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 +240,8 @@ class _TrackedItemPageState extends State<TrackedItemPage> { | ||||
|                                     children: [ | ||||
|                                       Column( | ||||
|                                         mainAxisSize: MainAxisSize.min, | ||||
|                                   crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                                         crossAxisAlignment: | ||||
|                                             CrossAxisAlignment.start, | ||||
|                                         children: [ | ||||
|                                           Text( | ||||
|                                             itemTitle, | ||||
| @@ -221,7 +266,8 @@ class _TrackedItemPageState extends State<TrackedItemPage> { | ||||
|                                         ), | ||||
|                                       ), | ||||
|                                       Column( | ||||
|                                   crossAxisAlignment: CrossAxisAlignment.end, | ||||
|                                         crossAxisAlignment: | ||||
|                                             CrossAxisAlignment.end, | ||||
|                                         children: [ | ||||
|                                           Text( | ||||
|                                             itemTopText, | ||||
| @@ -244,6 +290,9 @@ class _TrackedItemPageState extends State<TrackedItemPage> { | ||||
|                             ), | ||||
|                           ); | ||||
|                         }, | ||||
|                       ), | ||||
|                     ), | ||||
|                   ], | ||||
|                 ); | ||||
|         }); | ||||
|   } | ||||
| @@ -297,6 +346,21 @@ class _TrackedItemInputDialogState extends State<TrackedItemInputDialog> { | ||||
|     String amountText = | ||||
|         widget.amountText != null ? widget.amountText! : TrackedItem.amountText; | ||||
|  | ||||
|     Future<List<TrackedItem>> items; | ||||
|     switch (_type) { | ||||
|       case ItemType.expense: | ||||
|         items = DatabaseHelper.instance.getExpenses(); | ||||
|         break; | ||||
|       case ItemType.income: | ||||
|         items = DatabaseHelper.instance.getIncomes(); | ||||
|         break; | ||||
|       case ItemType.asset: | ||||
|         items = DatabaseHelper.instance.getAssets(); | ||||
|         break; | ||||
|       default: | ||||
|         throw UnimplementedError("Cannot find unimplemented type."); | ||||
|     } | ||||
|  | ||||
|     return Column( | ||||
|       // prevent AlertDialog from taking full vertical height. | ||||
|       mainAxisSize: MainAxisSize.min, | ||||
| @@ -336,15 +400,15 @@ class _TrackedItemInputDialogState extends State<TrackedItemInputDialog> { | ||||
|                 ? Text("New ${_type!.title}") | ||||
|                 : Text("Edit ${_type!.title}"), | ||||
|           ), | ||||
|           content: FutureBuilder<List<Expense>>( | ||||
|           content: FutureBuilder<List<TrackedItem>>( | ||||
|               // TODO / TBD -- This should no longer only be Expenses. | ||||
|               future: DatabaseHelper.instance.getExpenses(), | ||||
|               future: items, | ||||
|               builder: (BuildContext context, | ||||
|                   AsyncSnapshot<List<Expense>> snapshot) { | ||||
|                   AsyncSnapshot<List<TrackedItem>> snapshot) { | ||||
|                 if (!snapshot.hasData) { | ||||
|                   return Center(child: Text('Loading...')); | ||||
|                 } | ||||
|                 List<Expense> expenses = snapshot.data!; | ||||
|                 List<TrackedItem> expenses = snapshot.data!; | ||||
|                 return Form( | ||||
|                   key: _formKey, | ||||
|                   child: Column( | ||||
| @@ -392,7 +456,16 @@ class _TrackedItemInputDialogState extends State<TrackedItemInputDialog> { | ||||
|                             return "$amountText must be a valid number."; | ||||
|                           } | ||||
|                           if (double.parse(value) < 0) { | ||||
|                             return "Please use the Income page rather than having negative expenses."; | ||||
|                             switch (_type) { | ||||
|                               case ItemType.expense: | ||||
|                                 return "Please use the Income page."; | ||||
|  | ||||
|                               case ItemType.income: | ||||
|                                 return "Please use the Expense page."; | ||||
|  | ||||
|                               default: | ||||
|                                 break; | ||||
|                             } | ||||
|                           } | ||||
|                           if (double.parse(value) < 0.01) { | ||||
|                             return "$amountText must be one hundreth (0.01) or higher."; | ||||
|   | ||||
| @@ -5,11 +5,13 @@ | ||||
| import FlutterMacOS | ||||
| import Foundation | ||||
|  | ||||
| import package_info_plus | ||||
| import path_provider_foundation | ||||
| import sqflite_darwin | ||||
| import url_launcher_macos | ||||
|  | ||||
| func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { | ||||
|   FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) | ||||
|   PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) | ||||
|   SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) | ||||
|   UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) | ||||
|   | ||||
							
								
								
									
										40
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										40
									
								
								pubspec.lock
									
									
									
									
									
								
							| @@ -80,6 +80,22 @@ packages: | ||||
|     description: flutter | ||||
|     source: sdk | ||||
|     version: "0.0.0" | ||||
|   http: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: http | ||||
|       sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.3.0" | ||||
|   http_parser: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: http_parser | ||||
|       sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "4.1.2" | ||||
|   leak_tracker: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -136,6 +152,22 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.16.0" | ||||
|   package_info_plus: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: package_info_plus | ||||
|       sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "8.3.0" | ||||
|   package_info_plus_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: package_info_plus_platform_interface | ||||
|       sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.2.0" | ||||
|   path: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
| @@ -421,6 +453,14 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.1.1" | ||||
|   win32: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: win32 | ||||
|       sha256: dc6ecaa00a7c708e5b4d10ee7bec8c270e9276dfcab1783f57e9962d7884305f | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "5.12.0" | ||||
|   xdg_directories: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|   | ||||
| @@ -1,7 +1,8 @@ | ||||
| name: expense_tracker | ||||
| homepage: https://git.hyperling.com/me/flutter-expense-tracker | ||||
| description: Track recurring expenses against income and liquid assets. | ||||
| publish_to: 'none' | ||||
| version: 0.1.2 | ||||
| version: 0.1.3 | ||||
|  | ||||
| environment: | ||||
|   sdk: ^3.6.1 | ||||
| @@ -9,6 +10,7 @@ environment: | ||||
| dependencies: | ||||
|   flutter: | ||||
|     sdk: flutter | ||||
|   package_info_plus: ^8.3.0 | ||||
|   path: ^1.9.0 | ||||
|   path_provider: ^2.1.5 | ||||
|   sqflite: ^2.4.1 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user