Add ItemType for determining which DB methods should be called based on the screen data. Begin implementing it.
This commit is contained in:
parent
be66f52cbf
commit
42548d437c
@ -1,4 +1,6 @@
|
||||
// Local
|
||||
import 'package:flutter_expense_tracker/models/item_type.dart';
|
||||
|
||||
import '/models/tracked_item.dart';
|
||||
import '/models/frequency.dart';
|
||||
|
||||
@ -7,6 +9,7 @@ class Expense extends TrackedItem {
|
||||
|
||||
Expense({
|
||||
super.id,
|
||||
super.type = ItemType.expense,
|
||||
required super.name,
|
||||
required super.amount,
|
||||
required super.frequency,
|
||||
|
22
lib/models/item_type.dart
Normal file
22
lib/models/item_type.dart
Normal file
@ -0,0 +1,22 @@
|
||||
enum ItemType {
|
||||
expense(
|
||||
title: "Expense",
|
||||
plural: "Expenses",
|
||||
),
|
||||
income(
|
||||
title: "Income",
|
||||
plural: "Incomes",
|
||||
),
|
||||
asset(
|
||||
title: "Asset",
|
||||
plural: "Assets",
|
||||
);
|
||||
|
||||
const ItemType({
|
||||
required this.title,
|
||||
required this.plural,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final String plural;
|
||||
}
|
@ -1,8 +1,11 @@
|
||||
// Local
|
||||
import 'package:flutter_expense_tracker/models/item_type.dart';
|
||||
|
||||
import '/models/frequency.dart';
|
||||
|
||||
abstract class TrackedItem {
|
||||
int? id;
|
||||
ItemType? type;
|
||||
String name;
|
||||
double amount;
|
||||
Frequency? frequency;
|
||||
@ -10,6 +13,7 @@ abstract class TrackedItem {
|
||||
|
||||
TrackedItem({
|
||||
this.id,
|
||||
this.type,
|
||||
required this.name,
|
||||
required this.amount,
|
||||
this.frequency,
|
||||
|
@ -1,12 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AssetPage extends StatelessWidget {
|
||||
const AssetPage({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Placeholder();
|
||||
}
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
// Flutter
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_expense_tracker/models/item_type.dart';
|
||||
import 'dart:io';
|
||||
|
||||
// Local
|
||||
import '/pages/expense.dart';
|
||||
import '/pages/income.dart';
|
||||
import '/pages/asset.dart';
|
||||
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({
|
||||
@ -32,14 +32,15 @@ class _HomePageState extends State<HomePage> {
|
||||
Widget? dialog;
|
||||
switch (pageSelected) {
|
||||
case 0:
|
||||
page = ExpensePage();
|
||||
page = TrackedItemPage(assetsToLoad: DatabaseHelper.instance.getExpenses());
|
||||
dialog = TrackedItemInputDialog(
|
||||
notifyParent: refresh,
|
||||
type: ItemType.expense,
|
||||
);
|
||||
case 1:
|
||||
page = IncomePage();
|
||||
page = Placeholder();
|
||||
case 2:
|
||||
page = AssetPage();
|
||||
page = Placeholder();
|
||||
case 3:
|
||||
page = ProjectionPage();
|
||||
case 4:
|
||||
|
@ -1,12 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class IncomePage extends StatelessWidget {
|
||||
const IncomePage({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Placeholder();
|
||||
}
|
||||
}
|
@ -1,24 +1,30 @@
|
||||
// Flutter
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_expense_tracker/models/asset.dart';
|
||||
import 'package:flutter_expense_tracker/models/income.dart';
|
||||
|
||||
// Local
|
||||
import '/models/tracked_item.dart';
|
||||
import '/models/item_type.dart';
|
||||
import '/models/expense.dart';
|
||||
import '/models/frequency.dart';
|
||||
import '/db.dart';
|
||||
|
||||
// TODO: Make this a generic UI based on a superclass of Expense, Income, and Assets.
|
||||
|
||||
class ExpensePage extends StatefulWidget {
|
||||
const ExpensePage({
|
||||
class TrackedItemPage extends StatefulWidget {
|
||||
final Future<List<TrackedItem>> assetsToLoad;
|
||||
|
||||
const TrackedItemPage({
|
||||
super.key,
|
||||
required this.assetsToLoad,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ExpensePage> createState() => _ExpensePageState();
|
||||
State<TrackedItemPage> createState() => _TrackedItemPageState();
|
||||
}
|
||||
|
||||
class _ExpensePageState extends State<ExpensePage> {
|
||||
class _TrackedItemPageState extends State<TrackedItemPage> {
|
||||
refresh() {
|
||||
setState(() {});
|
||||
}
|
||||
@ -28,9 +34,9 @@ class _ExpensePageState extends State<ExpensePage> {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return FutureBuilder<List<TrackedItem>>(
|
||||
future: DatabaseHelper.instance.getExpenses(),
|
||||
builder: (BuildContext context,
|
||||
AsyncSnapshot<List<TrackedItem>> snapshot) {
|
||||
future: widget.assetsToLoad,
|
||||
builder:
|
||||
(BuildContext context, AsyncSnapshot<List<TrackedItem>> snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return Text('Loading...');
|
||||
}
|
||||
@ -41,13 +47,12 @@ class _ExpensePageState extends State<ExpensePage> {
|
||||
);
|
||||
return snapshot.data!.isEmpty
|
||||
? Text(
|
||||
"Add expenses to get started.",
|
||||
"Add items to get started.",
|
||||
softWrap: true,
|
||||
)
|
||||
: ListView.builder(
|
||||
itemCount: snapshot.data!.length,
|
||||
itemBuilder: (_, index) {
|
||||
//List<Expense> expenses = snapshot.data!;
|
||||
final TrackedItem curr = snapshot.data![index];
|
||||
|
||||
final itemKey = Key(curr.id!.toString());
|
||||
@ -139,7 +144,17 @@ class _ExpensePageState extends State<ExpensePage> {
|
||||
snapshot.data!.remove(curr);
|
||||
switch (direction) {
|
||||
case DismissDirection.startToEnd:
|
||||
DatabaseHelper.instance.removeExpense(curr.id!);
|
||||
if (curr is Expense) {
|
||||
DatabaseHelper.instance
|
||||
.removeExpense(curr.id!);
|
||||
} else if (curr is Income) {
|
||||
// TODO
|
||||
} else if (curr is Asset) {
|
||||
// TODO
|
||||
} else {
|
||||
throw UnimplementedError(
|
||||
"Cannot remove unimplemented item type.");
|
||||
}
|
||||
break;
|
||||
case DismissDirection.endToStart:
|
||||
// Open an edit dialog, then remove the item from the list.
|
||||
@ -150,6 +165,7 @@ class _ExpensePageState extends State<ExpensePage> {
|
||||
notifyParent: refresh,
|
||||
entry: curr,
|
||||
amountText: curr.getAmountText(),
|
||||
type: curr.type!,
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -230,22 +246,22 @@ class TrackedItemInputDialog extends StatefulWidget {
|
||||
final Function() notifyParent;
|
||||
final TrackedItem? entry;
|
||||
final String? amountText;
|
||||
final ItemType? type;
|
||||
|
||||
const TrackedItemInputDialog({
|
||||
super.key,
|
||||
required this.notifyParent,
|
||||
this.entry,
|
||||
this.amountText,
|
||||
this.type,
|
||||
});
|
||||
|
||||
@override
|
||||
State<TrackedItemInputDialog> createState() =>
|
||||
_TrackedItemInputDialogState();
|
||||
State<TrackedItemInputDialog> createState() => _TrackedItemInputDialogState();
|
||||
}
|
||||
|
||||
class _TrackedItemInputDialogState
|
||||
extends State<TrackedItemInputDialog> {
|
||||
final _expenseFormKey = GlobalKey<FormState>();
|
||||
class _TrackedItemInputDialogState extends State<TrackedItemInputDialog> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
int? _id;
|
||||
String _name = "";
|
||||
@ -255,12 +271,19 @@ class _TrackedItemInputDialogState
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (widget.type == null &&
|
||||
(widget.entry != null && widget.entry!.type == null)) {
|
||||
throw FlutterError("No ItemType provided for TrackedItemInputDialog.");
|
||||
}
|
||||
ItemType? _type = widget.type;
|
||||
|
||||
if (widget.entry != null) {
|
||||
_id = widget.entry!.id;
|
||||
_name = widget.entry!.name;
|
||||
_amount = widget.entry!.amount;
|
||||
widget.entry!.frequency == null ? null : _freq = widget.entry!.frequency!;
|
||||
_desc = widget.entry!.description;
|
||||
_type = widget.entry!.type!;
|
||||
}
|
||||
|
||||
String amountText =
|
||||
@ -276,7 +299,20 @@ class _TrackedItemInputDialogState
|
||||
onPressed: () {
|
||||
if (widget.entry != null) {
|
||||
setState(() {
|
||||
DatabaseHelper.instance.addExpense(widget.entry!);
|
||||
switch (_type) {
|
||||
case ItemType.expense:
|
||||
DatabaseHelper.instance.addExpense(widget.entry!);
|
||||
break;
|
||||
case ItemType.income:
|
||||
// TODO
|
||||
break;
|
||||
case ItemType.asset:
|
||||
// TODO
|
||||
break;
|
||||
default:
|
||||
throw UnimplementedError(
|
||||
"Cannot add unimplemented type.");
|
||||
}
|
||||
widget.notifyParent();
|
||||
});
|
||||
}
|
||||
@ -289,7 +325,7 @@ class _TrackedItemInputDialogState
|
||||
insetPadding: EdgeInsets.all(0),
|
||||
title: Center(
|
||||
child: widget.entry == null
|
||||
? Text("New Expense")
|
||||
? Text("New ${_type!.title}")
|
||||
: Text("Edit Expense"),
|
||||
),
|
||||
content: FutureBuilder<List<Expense>>(
|
||||
@ -301,7 +337,7 @@ class _TrackedItemInputDialogState
|
||||
}
|
||||
List<Expense> expenses = snapshot.data!;
|
||||
return Form(
|
||||
key: _expenseFormKey,
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
@ -424,8 +460,8 @@ class _TrackedItemInputDialogState
|
||||
Center(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
if (_expenseFormKey.currentState!.validate()) {
|
||||
_expenseFormKey.currentState!.save();
|
||||
if (_formKey.currentState!.validate()) {
|
||||
_formKey.currentState!.save();
|
||||
setState(() {
|
||||
Expense expense = Expense(
|
||||
id: _id,
|
Loading…
x
Reference in New Issue
Block a user