From 43912fee04f3c2fc1305958485cd74c12f24c695 Mon Sep 17 00:00:00 2001 From: Herwig Birke Date: Mon, 19 Feb 2024 15:16:03 +0100 Subject: [PATCH] Ende Abschnitt 6 --- lib/main.dart | 89 +++++----- lib/widgets/expenses.dart | 30 +++- lib/widgets/new_expense.dart | 307 +++++++++++++++++++++++++---------- 3 files changed, 288 insertions(+), 138 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index e392620..757645b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -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(), - ), - ); + ); +// }); } diff --git a/lib/widgets/expenses.dart b/lib/widgets/expenses.dart index ce53586..75af46d 100644 --- a/lib/widgets/expenses.dart +++ b/lib/widgets/expenses.dart @@ -31,6 +31,7 @@ class _ExpensesState extends State { void _openAddExpenseOverlay() { showModalBottomSheet( + useSafeArea: true, isScrollControlled: true, context: context, builder: (ctx) => NewExpense(onAddExpense: _addExpense), @@ -67,6 +68,8 @@ class _ExpensesState extends State { @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 { ), ], ), - 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, + ), + ], + ), ); } } diff --git a/lib/widgets/new_expense.dart b/lib/widgets/new_expense.dart index b619003..b32ac21 100644 --- a/lib/widgets/new_expense.dart +++ b/lib/widgets/new_expense.dart @@ -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 { }); } - 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 { ], ), ); + } + } + + 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 { } 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'), - ), - ], - ), - ], - ), - ); + ), + ); + }); } }