From 737264fa2f8f5b45f9d20a3693434fe9c13314cf Mon Sep 17 00:00:00 2001 From: Hyperling Date: Tue, 11 Feb 2025 15:16:46 -0700 Subject: [PATCH] More work to make the Expense page arbitrary for re-use. --- lib/db.dart | 5 +-- lib/models/tracked_type.dart | 3 ++ lib/pages/expense.dart | 63 +++++++++++++++++++++--------------- lib/pages/home.dart | 2 +- 4 files changed, 44 insertions(+), 29 deletions(-) diff --git a/lib/db.dart b/lib/db.dart index 89a3237..d23f4b2 100644 --- a/lib/db.dart +++ b/lib/db.dart @@ -3,6 +3,7 @@ // SQLite import 'dart:io'; import 'dart:async'; +import 'package:flutter_expense_tracker/models/recurring_tracked_type.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; import 'package:sqflite/sqflite.dart'; @@ -76,7 +77,7 @@ class DatabaseHelper { return expenseList; } - Future addExpense(Expense expense) async { + Future addExpense(RecurringTrackedType expense) async { Database db = await instance.db; return await db.insert( "expense", @@ -93,7 +94,7 @@ class DatabaseHelper { ); } - Future updateExpense(Expense expense) async { + Future updateExpense(RecurringTrackedType expense) async { Database db = await instance.db; return await db.update( "expense", diff --git a/lib/models/tracked_type.dart b/lib/models/tracked_type.dart index 03dfecd..8070a32 100644 --- a/lib/models/tracked_type.dart +++ b/lib/models/tracked_type.dart @@ -11,6 +11,9 @@ abstract class TrackedType { required this.description, }); + static String amountText = "Amount"; + String getAmountText() => amountText; + @override String toString() { return toMap().toString(); diff --git a/lib/pages/expense.dart b/lib/pages/expense.dart index 0896428..377cf13 100644 --- a/lib/pages/expense.dart +++ b/lib/pages/expense.dart @@ -1,6 +1,8 @@ // Flutter import 'package:flutter/material.dart'; import 'package:flutter_expense_tracker/db.dart'; +import 'package:flutter_expense_tracker/models/recurring_tracked_type.dart'; +import 'package:flutter_expense_tracker/models/tracked_type.dart'; // Local import '/models/expense.dart'; @@ -26,9 +28,10 @@ class _ExpensePageState extends State { Widget build(BuildContext context) { final theme = Theme.of(context); - return FutureBuilder>( + return FutureBuilder>( future: DatabaseHelper.instance.getExpenses(), - builder: (BuildContext context, AsyncSnapshot> snapshot) { + builder: (BuildContext context, + AsyncSnapshot> snapshot) { if (!snapshot.hasData) { return Text('Loading...'); } @@ -45,8 +48,8 @@ class _ExpensePageState extends State { : ListView.builder( itemCount: snapshot.data!.length, itemBuilder: (_, index) { - List expenses = snapshot.data!; - final Expense curr = expenses[index]; + //List expenses = snapshot.data!; + final RecurringTrackedType curr = snapshot.data![index]; final String estimateSymbolYearly = curr .frequency.timesPerYear .toStringAsFixed(2) @@ -92,7 +95,7 @@ class _ExpensePageState extends State { ), onDismissed: (direction) { setState(() { - expenses.remove(curr); + snapshot.data!.remove(curr); switch (direction) { case DismissDirection.startToEnd: DatabaseHelper.instance.removeExpense(curr.id!); @@ -102,9 +105,10 @@ class _ExpensePageState extends State { showDialog( context: context, builder: (_) => AlertDialog( - content: ExpenseInputDialog( + content: RecurringTrackedTypeInputDialog( notifyParent: refresh, - expense: curr, + entry: curr, + amountText: curr.getAmountText(), ), ), ); @@ -179,21 +183,25 @@ class _ExpensePageState extends State { } } -class ExpenseInputDialog extends StatefulWidget { +class RecurringTrackedTypeInputDialog extends StatefulWidget { final Function() notifyParent; - final Expense? expense; + final RecurringTrackedType? entry; + final String? amountText; - const ExpenseInputDialog({ + const RecurringTrackedTypeInputDialog({ super.key, required this.notifyParent, - this.expense, + this.entry, + this.amountText, }); @override - State createState() => _ExpenseInputDialogState(); + State createState() => + _RecurringTrackedTypeInputDialogState(); } -class _ExpenseInputDialogState extends State { +class _RecurringTrackedTypeInputDialogState + extends State { final _expenseFormKey = GlobalKey(); int? _id; @@ -204,14 +212,17 @@ class _ExpenseInputDialogState extends State { @override Widget build(BuildContext context) { - if (widget.expense != null) { - _id = widget.expense!.id; - _name = widget.expense!.name; - _amount = widget.expense!.amount; - _freq = widget.expense!.frequency; - _desc = widget.expense!.description; + if (widget.entry != null) { + _id = widget.entry!.id; + _name = widget.entry!.name; + _amount = widget.entry!.amount; + _freq = widget.entry!.frequency; + _desc = widget.entry!.description; } + String amountText = + widget.amountText != null ? widget.amountText! : TrackedType.amountText; + return Column( // prevent AlertDialog from taking full vertical height. mainAxisSize: MainAxisSize.min, @@ -220,9 +231,9 @@ class _ExpenseInputDialogState extends State { alignment: FractionalOffset.topRight, child: IconButton( onPressed: () { - if (widget.expense != null) { + if (widget.entry != null) { setState(() { - DatabaseHelper.instance.addExpense(widget.expense!); + DatabaseHelper.instance.addExpense(widget.entry!); widget.notifyParent(); }); } @@ -234,7 +245,7 @@ class _ExpenseInputDialogState extends State { AlertDialog( insetPadding: EdgeInsets.all(0), title: Center( - child: widget.expense == null + child: widget.entry == null ? Text("New Expense") : Text("Edit Expense"), ), @@ -279,7 +290,7 @@ class _ExpenseInputDialogState extends State { keyboardType: TextInputType.numberWithOptions(decimal: true), decoration: InputDecoration( - labelText: "${Expense.amountText}", + labelText: amountText, hintText: "Example: 10.00", hintStyle: TextStyle(fontSize: 10.0), errorStyle: TextStyle(fontSize: 10.0), @@ -287,16 +298,16 @@ class _ExpenseInputDialogState extends State { initialValue: _amount != 0 ? _amount.toString() : "", validator: (value) { if (value == null || value.isEmpty) { - return "${Expense.amountText} must be provided."; + return "$amountText must be provided."; } if (double.tryParse(value) == null) { - return "${Expense.amountText} must be a valid number."; + return "$amountText must be a valid number."; } if (double.parse(value) < 0) { return "Please use the Income page rather than having negative expenses."; } if (double.parse(value) < 0.01) { - return "${Expense.amountText} must be one hundreth (0.01) or higher."; + return "$amountText must be one hundreth (0.01) or higher."; } return null; }, diff --git a/lib/pages/home.dart b/lib/pages/home.dart index f9aece2..b01b538 100644 --- a/lib/pages/home.dart +++ b/lib/pages/home.dart @@ -33,7 +33,7 @@ class _HomePageState extends State { switch (pageSelected) { case 0: page = ExpensePage(); - dialog = ExpenseInputDialog( + dialog = RecurringTrackedTypeInputDialog( notifyParent: refresh, ); case 1: