This commit is contained in:
2024-02-08 17:49:45 +01:00
parent 3cb4f01a3b
commit 232f9450b9
9 changed files with 323 additions and 7 deletions
View File
+8 -2
View File
@@ -1,5 +1,11 @@
import 'package:flutter/material.dart';
import 'package:expense_tracker/widgets/expenses.dart';
void main() {
runApp(MaterialApp(home: // Todo,),);
}
runApp(
MaterialApp(
theme: ThemeData(useMaterial3: true),
home: const Expenses(),
),
);
}
+35
View File
@@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
import 'package:uuid/uuid.dart';
import 'package:intl/intl.dart';
final formatter = DateFormat('dd.MM.yyyy');
const uuid = Uuid();
enum Category { foot, travel, leisure, work }
const categoryIcons = {
Category.foot: Icons.lunch_dining,
Category.travel: Icons.flight_takeoff,
Category.leisure: Icons.movie,
Category.work: Icons.work,
};
class Expense {
Expense({
required this.title,
required this.amount,
required this.date,
required this.category,
}) : id = uuid.v4();
final String id;
final String title;
final double amount;
final DateTime date;
final Category category;
String get formattedDate {
return formatter.format(date);
}
}
+58
View File
@@ -0,0 +1,58 @@
import 'package:expense_tracker/widgets/expenses_list/expenses_list.dart';
import 'package:expense_tracker/widgets/new_expense.dart';
import 'package:flutter/material.dart';
import 'package:expense_tracker/models/expense.dart';
class Expenses extends StatefulWidget {
const Expenses({super.key});
@override
State<Expenses> createState() {
return _ExpensesState();
}
}
class _ExpensesState extends State<Expenses> {
final List<Expense> _registeredExpenses = [
Expense(
title: 'Flutter Course',
amount: 19.99,
date: DateTime.now(),
category: Category.work,
),
Expense(
title: 'Cinema',
amount: 15.69,
date: DateTime.now(),
category: Category.leisure,
),
];
void _openAddExpenseOverlay() {
showModalBottomSheet(
context: context, builder: (ctx) => const NewExpense());
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter ExpenseTracker'),
actions: [
IconButton(
onPressed: _openAddExpenseOverlay,
icon: const Icon(Icons.add),
),
],
),
body: Column(
children: [
const Text('The chart'),
Expanded(
child: ExpensesList(expenses: _registeredExpenses),
),
],
),
);
}
}
@@ -0,0 +1,39 @@
import 'package:expense_tracker/models/expense.dart';
import 'package:flutter/material.dart';
class ExpenseItem extends StatelessWidget {
const ExpenseItem(this.expense, {super.key});
final Expense expense;
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 16,
),
child: Column(
children: [
Text(expense.title),
const SizedBox(height: 4),
Row(
children: [
Text('\$${expense.amount.toStringAsFixed(2)}'),
const Spacer(),
Row(
children: [
Icon(categoryIcons[expense.category]),
const SizedBox(width: 8),
Text(expense.formattedDate),
],
),
],
),
],
),
),
);
}
}
@@ -0,0 +1,20 @@
import 'package:expense_tracker/widgets/expenses_list/expense_item.dart';
import 'package:flutter/material.dart';
import 'package:expense_tracker/models/expense.dart';
class ExpensesList extends StatelessWidget {
const ExpensesList({
super.key,
required this.expenses,
});
final List<Expense> expenses;
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: expenses.length,
itemBuilder: (ctx, index) => ExpenseItem(expenses[index]),
);
}
}
+108
View File
@@ -0,0 +1,108 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
final formatter = DateFormat('dd.MM.yyyy');
class NewExpense extends StatefulWidget {
const NewExpense({super.key});
@override
State<StatefulWidget> createState() {
return _NewExpenseState();
}
}
class _NewExpenseState extends State<NewExpense> {
final _titleController = TextEditingController();
final _amountController = TextEditingController();
DateTime? _selectedDate;
void _presentDatePicker() async {
final now = DateTime.now();
final firstDate = DateTime(now.year - 1, now.month, now.day);
final pickedDate = await showDatePicker(
context: context,
initialDate: now,
firstDate: firstDate,
lastDate: now,
);
setState(() {
_selectedDate = pickedDate;
});
}
@override
void dispose() {
_titleController.dispose();
_amountController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
TextField(
controller: _titleController,
maxLength: 50,
decoration: const InputDecoration(
label: Text('Title'),
),
),
Row(
children: [
Expanded(
child: TextField(
controller: _amountController,
keyboardType: const TextInputType.numberWithOptions(
decimal: true,
),
decoration: const InputDecoration(
prefix: Text('\$ '),
label: Text('Amount'),
),
),
),
const SizedBox(width: 16),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
_selectedDate == null
? 'No date selected'
: formatter.format(_selectedDate!),
),
IconButton(
onPressed: _presentDatePicker,
icon: Icon(Icons.calendar_month),
),
],
),
)
],
),
Row(
children: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Cancel'),
),
ElevatedButton(
onPressed: () {
print(_titleController.text);
print(_amountController.text);
},
child: const Text('Save Expense'),
),
],
),
],
),
);
}
}
+52 -4
View File
@@ -41,6 +41,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.18.0"
crypto:
dependency: transitive
description:
name: crypto
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
url: "https://pub.dev"
source: hosted
version: "3.0.3"
cupertino_icons:
dependency: "direct main"
description:
@@ -57,6 +65,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.1"
fixnum:
dependency: transitive
description:
name: fixnum
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
flutter:
dependency: "direct main"
description: flutter
@@ -66,23 +82,31 @@ packages:
dependency: "direct dev"
description:
name: flutter_lints
sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04
sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7
url: "https://pub.dev"
source: hosted
version: "2.0.3"
version: "3.0.1"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
intl:
dependency: "direct main"
description:
name: intl
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
url: "https://pub.dev"
source: hosted
version: "0.19.0"
lints:
dependency: transitive
description:
name: lints
sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "3.0.0"
matcher:
dependency: transitive
description:
@@ -128,6 +152,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.10.0"
sprintf:
dependency: transitive
description:
name: sprintf
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
stack_trace:
dependency: transitive
description:
@@ -168,6 +200,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.6.1"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
url: "https://pub.dev"
source: hosted
version: "1.3.2"
uuid:
dependency: "direct main"
description:
name: uuid
sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8
url: "https://pub.dev"
source: hosted
version: "4.3.3"
vector_math:
dependency: transitive
description:
+3 -1
View File
@@ -35,6 +35,8 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
uuid: ^4.3.3
intl: ^0.19.0
dev_dependencies:
flutter_test:
@@ -45,7 +47,7 @@ dev_dependencies:
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^2.0.0
flutter_lints: ^3.0.1
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec