Ende Abschnitt 6
This commit is contained in:
+48
-41
@@ -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(),
|
||||
),
|
||||
);
|
||||
);
|
||||
// });
|
||||
}
|
||||
|
||||
@@ -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
@@ -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'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user