114
This commit is contained in:
+8
-2
@@ -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(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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]),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user