Ende Abschnitt 6

This commit is contained in:
2024-02-19 15:16:03 +01:00
parent 8aa2252d90
commit 43912fee04
3 changed files with 288 additions and 138 deletions
+48 -41
View File
@@ -1,4 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:expense_tracker/widgets/expenses.dart';
var kColorScheme = ColorScheme.fromSeed(
@@ -11,48 +13,53 @@ var kDarkColorScheme = ColorScheme.fromSeed(
);
void main() {
runApp(
MaterialApp(
darkTheme: ThemeData.dark().copyWith(
useMaterial3: true,
colorScheme: kDarkColorScheme,
cardTheme: const CardTheme().copyWith(
color: kDarkColorScheme.secondaryContainer,
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: kDarkColorScheme.primaryContainer,
foregroundColor: kDarkColorScheme.onPrimaryContainer,
// WidgetsFlutterBinding.ensureInitialized();
// SystemChrome.setPreferredOrientations([
// DeviceOrientation.portraitUp,
// ]).then((fn) {
runApp(
MaterialApp(
darkTheme: ThemeData.dark().copyWith(
useMaterial3: true,
colorScheme: kDarkColorScheme,
cardTheme: const CardTheme().copyWith(
color: kDarkColorScheme.secondaryContainer,
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: kDarkColorScheme.primaryContainer,
foregroundColor: kDarkColorScheme.onPrimaryContainer,
),
),
),
theme: ThemeData().copyWith(
useMaterial3: true,
colorScheme: kColorScheme,
appBarTheme: const AppBarTheme().copyWith(
backgroundColor: kColorScheme.onPrimaryContainer,
foregroundColor: kColorScheme.primaryContainer,
),
cardTheme: const CardTheme().copyWith(
color: kColorScheme.secondaryContainer,
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: kColorScheme.primaryContainer,
),
),
textTheme: ThemeData().textTheme.copyWith(
titleLarge: TextStyle(
fontWeight: FontWeight.bold,
color: kColorScheme.onSecondaryContainer,
fontSize: 16,
),
),
),
themeMode: ThemeMode.light,
home: const Expenses(),
),
theme: ThemeData().copyWith(
useMaterial3: true,
colorScheme: kColorScheme,
appBarTheme: const AppBarTheme().copyWith(
backgroundColor: kColorScheme.onPrimaryContainer,
foregroundColor: kColorScheme.primaryContainer,
),
cardTheme: const CardTheme().copyWith(
color: kColorScheme.secondaryContainer,
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: kColorScheme.primaryContainer,
),
),
textTheme: ThemeData().textTheme.copyWith(
titleLarge: TextStyle(
fontWeight: FontWeight.bold,
color: kColorScheme.onSecondaryContainer,
fontSize: 16,
),
),
),
themeMode: ThemeMode.light,
home: const Expenses(),
),
);
);
// });
}
+22 -8
View File
@@ -31,6 +31,7 @@ class _ExpensesState extends State<Expenses> {
void _openAddExpenseOverlay() {
showModalBottomSheet(
useSafeArea: true,
isScrollControlled: true,
context: context,
builder: (ctx) => NewExpense(onAddExpense: _addExpense),
@@ -67,6 +68,8 @@ class _ExpensesState extends State<Expenses> {
@override
Widget build(BuildContext context) {
var width = MediaQuery.of(context).size.width;
Widget mainContent = const Center(
child: Text('No expenses found. Start adding some!'),
);
@@ -85,14 +88,25 @@ class _ExpensesState extends State<Expenses> {
),
],
),
body: Column(
children: [
Chart(expenses: _registeredExpenses),
Expanded(
child: mainContent,
),
],
),
body: width < 600
? Column(
children: [
Chart(expenses: _registeredExpenses),
Expanded(
child: mainContent,
),
],
)
: Row(
children: [
Expanded(
child: Chart(expenses: _registeredExpenses),
),
Expanded(
child: mainContent,
),
],
),
);
}
}
+218 -89
View File
@@ -1,9 +1,12 @@
import 'package:expense_tracker/models/expense.dart';
import 'package:expense_tracker/widgets/expenses.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'dart:io';
final formatter = DateFormat('dd.MM.yyyy');
class NewExpense extends StatefulWidget {
@@ -37,13 +40,25 @@ class _NewExpenseState extends State<NewExpense> {
});
}
void _submitExpenseData() {
final enteredAmount = double.tryParse(_amountController.text);
final amountIsInvalid = enteredAmount == null || enteredAmount <= 0;
if (_titleController.text.trim().isEmpty ||
amountIsInvalid ||
_selectedCategory == null) {
void _showDialog() {
if (Platform.isIOS) {
showCupertinoDialog(
context: context,
builder: (ctx) => CupertinoAlertDialog(
title: const Text('Invalid input'),
content: const Text(
'Please make sure a valid title, amount date and category was entered.'),
actions: [
TextButton(
onPressed: () {
Navigator.pop(ctx);
},
child: const Text('Okay'),
),
],
),
);
} else {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
@@ -60,6 +75,17 @@ class _NewExpenseState extends State<NewExpense> {
],
),
);
}
}
void _submitExpenseData() {
final enteredAmount = double.tryParse(_amountController.text);
final amountIsInvalid = enteredAmount == null || enteredAmount <= 0;
if (_titleController.text.trim().isEmpty ||
amountIsInvalid ||
_selectedDate == null) {
_showDialog();
return;
}
@@ -80,89 +106,192 @@ class _NewExpenseState extends State<NewExpense> {
}
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(16, 48, 16, 16),
child: Column(
children: [
TextField(
controller: _titleController,
maxLength: 50,
decoration: const InputDecoration(
label: Text('Title'),
final keyboardSpace = MediaQuery.of(context).viewInsets.bottom;
return LayoutBuilder(builder: (ctx, constraints) {
final width = constraints.maxWidth;
return SizedBox(
height: double.infinity,
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.fromLTRB(16, 48, 16, keyboardSpace + 16),
child: Column(
children: [
if (width >= 600)
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: TextField(
controller: _titleController,
maxLength: 50,
decoration: const InputDecoration(
label: Text('Title'),
),
),
),
const SizedBox(
width: 24,
),
Expanded(
child: TextField(
controller: _amountController,
keyboardType: const TextInputType.numberWithOptions(
decimal: true,
),
decoration: const InputDecoration(
prefix: Text('\$ '),
label: Text('Amount'),
),
),
),
],
)
else
TextField(
controller: _titleController,
maxLength: 50,
decoration: const InputDecoration(
label: Text('Title'),
),
),
if (width >= 600)
Row(
children: [
DropdownButton(
value: _selectedCategory,
items: Category.values
.map(
(category) => DropdownMenuItem(
value: category,
child: Text(
category.name.toUpperCase(),
),
),
)
.toList(),
onChanged: (value) {
if (value == null) {
return;
}
setState(() {
_selectedCategory = value;
});
}),
const SizedBox(width: 24),
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),
),
],
),
),
],
)
else
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),
),
],
),
),
],
),
const SizedBox(height: 16),
if (width >= 600)
Row(
children: [
const Spacer(),
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Cancel'),
),
ElevatedButton(
onPressed: _submitExpenseData,
child: const Text('Save Expense'),
),
],
)
else
Row(
children: [
DropdownButton(
value: _selectedCategory,
items: Category.values
.map(
(category) => DropdownMenuItem(
value: category,
child: Text(
category.name.toUpperCase(),
),
),
)
.toList(),
onChanged: (value) {
if (value == null) {
return;
}
setState(() {
_selectedCategory = value;
});
}),
const Spacer(),
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Cancel'),
),
ElevatedButton(
onPressed: _submitExpenseData,
child: const Text('Save Expense'),
),
],
),
],
),
),
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),
),
],
),
)
],
),
const SizedBox(height: 16),
Row(
children: [
DropdownButton(
value: _selectedCategory,
items: Category.values
.map(
(category) => DropdownMenuItem(
value: category,
child: Text(
category.name.toUpperCase(),
),
),
)
.toList(),
onChanged: (value) {
if (value == null) {
return;
}
setState(() {
_selectedCategory = value;
});
}),
const Spacer(),
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Cancel'),
),
ElevatedButton(
onPressed: _submitExpenseData,
child: const Text('Save Expense'),
),
],
),
],
),
);
),
);
});
}
}