From 1b95feb5d4fbd150fa046ae4bd5005f94fa17a6e Mon Sep 17 00:00:00 2001 From: Hyperling Date: Wed, 5 Feb 2025 13:20:04 -0700 Subject: [PATCH] Forms is working, items show if hot reloaded, need to refactor code and get the global variable into the state of the expense page. --- lib/main.dart | 225 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 144 insertions(+), 81 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index aca717a..6dbb0cb 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,17 +8,37 @@ void main() { } // https://www.tutorialspoint.com/dart_programming/dart_programming_enumeration.htm -enum Recurrence { daily, weekly, biweekly, montly, yearly } +enum Frequency { + daily(title: "Daily"), + weekly(title: "Weekly"), + biweekly(title: "Biweekly"), + montly(title: "Monthly"), + yearly(title: "Yearly"); + + const Frequency({required this.title}); + + final String title; +} class Expense { - String name; - double cost; - Recurrence recurrence; - String description; + final String name; + final double cost; + final Frequency frequency; + final String description; - Expense(this.name, this.cost, this.recurrence, this.description); + const Expense( + {required this.name, + required this.cost, + required this.frequency, + required this.description}); + + String toString() { + return "$name, $cost, ${frequency.title}, $description"; + } } +List expenses = []; + class MainApp extends StatelessWidget { const MainApp({super.key}); @@ -85,6 +105,7 @@ class _HomePageState extends State { return LayoutBuilder(builder: (context, constraints) { return Scaffold( + // TODO: Add a drawer instead of nav rail. body: Row( children: [ SafeArea( @@ -141,6 +162,29 @@ class ExpensePage extends StatelessWidget { @override Widget build(BuildContext context) { + return ListView.builder( + itemCount: expenses.length, + itemBuilder: (_, index) { + final Expense curr = expenses[index]; + return Center( + child: Padding( + padding: const EdgeInsets.all(4.0), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + color: Colors.greenAccent, + ), + child: Column( + children: [ + Text(curr.name), + Text("${curr.cost.toString()} ${curr.frequency.title}"), + ], + ), + ), + ), + ); + }); + /* return ListView( children: [ ListTile( @@ -153,6 +197,7 @@ class ExpensePage extends StatelessWidget { ), ], ); + */ } } @@ -168,22 +213,21 @@ class ExpenseInputDialog extends StatefulWidget { class _ExpenseInputDialogState extends State { final _expenseFormKey = GlobalKey(); + String _name = ""; + double _cost = 0.0; + Frequency _freq = Frequency.montly; + String _desc = ""; + @override Widget build(BuildContext context) { const inputWidth = 300.0; const inputHeight = 50.0; - const recurrenceValues = [ - DropdownMenuItem(value: Recurrence.daily, child: Text("Daily")), - DropdownMenuItem(value: Recurrence.weekly, child: Text("Weekly")), - DropdownMenuItem(value: Recurrence.biweekly, child: Text("Biweekly")), - DropdownMenuItem(value: Recurrence.montly, child: Text("Monthly")), - DropdownMenuItem( - value: Recurrence.yearly, - child: Text( - "Yearly", - )), - ]; + List freqValues = []; + for (var freq in Frequency.values) { + freqValues.add(DropdownMenuItem(value: freq, child: Text(freq.title))); + } + ; return AlertDialog( title: Center(child: Text("Add New Expense")), @@ -192,24 +236,25 @@ class _ExpenseInputDialogState extends State { autovalidateMode: AutovalidateMode.onUserInteraction, child: Column(mainAxisSize: MainAxisSize.min, spacing: 10, children: [ SizedBox( - width: inputWidth, - height: inputHeight, - child: TextFormField( - keyboardType: TextInputType.text, - decoration: InputDecoration( - labelText: "Name", - hintText: "Example: Red Pocket Phone Bill", - ), - validator: (value) { - if (value!.isEmpty) { - return "Name must be provided."; - } - return null; - }, - onChanged: (newValue) { - // _expenseFormKey.currentState!.reset(); - }, - )), + width: inputWidth, + height: inputHeight, + child: TextFormField( + keyboardType: TextInputType.text, + decoration: InputDecoration( + labelText: "Name", + hintText: "Example: Red Pocket Phone Bill", + ), + validator: (value) { + if (value!.isEmpty) { + return "Name must be provided."; + } + return null; + }, + onSaved: (newValue) { + _name = newValue!; + }, + ), + ), SizedBox( width: inputWidth, height: inputHeight, @@ -221,13 +266,13 @@ class _ExpenseInputDialogState extends State { if (value!.isEmpty) { return "Cost must be provided."; } - if (double.tryParse(value!) == null) { + if (double.tryParse(value) == null) { return "Cost must be a valid number."; } return null; }, - onChanged: (newValue) { - //_expenseFormKey.currentState!.reset(); + onSaved: (newValue) { + _cost = double.parse(newValue!); }, ), ), @@ -235,66 +280,84 @@ class _ExpenseInputDialogState extends State { width: inputWidth, height: inputHeight, child: DropdownButtonFormField( - items: recurrenceValues, + items: freqValues, decoration: InputDecoration( labelText: "Recurrence", hintText: "Example: Monthly"), validator: (value) { if (value == null) { - return "Recurrence must be provided."; + return "Frequency must be provided."; + } + if (!Frequency.values.contains(value)) { + return "Value not valid."; } return null; }, onChanged: (newValue) { - //_expenseFormKey.currentState!.reset(); + _freq = newValue; }, ), ), SizedBox( - width: inputWidth, - height: inputHeight, - child: TextFormField( - keyboardType: TextInputType.text, - decoration: InputDecoration( - labelText: "Description", - hintText: "Example: 1GB data with unlimited talk & text."), - validator: (value) { - return null; - }, - onChanged: (newValue) { - //_expenseFormKey.currentState!.reset(); - }, - )), + width: inputWidth, + height: inputHeight, + child: TextFormField( + keyboardType: TextInputType.text, + decoration: InputDecoration( + labelText: "Description", + hintText: "Example: 1GB data with unlimited talk & text."), + validator: (value) { + return null; + }, + onSaved: (newValue) { + _desc = newValue!; + }, + ), + ), ]), ), actions: [ SizedBox( - width: inputWidth, - height: inputHeight, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - ElevatedButton.icon( - onPressed: () { - print("TODO: Clear fields!"); - Navigator.of(context).pop(); - }, - icon: Icon(Icons.cancel), - label: Text('Cancel'), - ), - ElevatedButton.icon( - onPressed: () { - print("TODO: Save expense!"); - if (_expenseFormKey.currentState!.validate()) { - _expenseFormKey.currentState!.save(); - Navigator.of(context).pop(); + width: inputWidth, + height: inputHeight, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ElevatedButton.icon( + onPressed: () { + print("TODO: Clear fields!"); + Navigator.of(context).pop(); + }, + icon: Icon(Icons.cancel), + label: Text('Cancel'), + ), + ElevatedButton.icon( + onPressed: () { + print("TODO: Save expense!"); + if (_expenseFormKey.currentState!.validate()) { + _expenseFormKey.currentState!.save(); + setState(() { + expenses.add( + Expense( + name: _name, + cost: _cost, + frequency: _freq, + description: _desc), + ); + }); + print(expenses.toString()); + for (var expense in expenses) { + print(expense.toString()); } - }, - icon: Icon(Icons.save), - label: Text('Submit'), - ), - ], - )) + Navigator.of(context).pop(); + } + }, + icon: Icon(Icons.save), + label: Text('Submit'), + ), + ], + ), + ) ], ); }