From 60bfd9481f10467d35fe3ea4932f5215194d79ae Mon Sep 17 00:00:00 2001 From: "Alexander \"PapaTutuWawa" Date: Fri, 9 May 2025 20:23:45 +0200 Subject: [PATCH] UI fixes --- lib/ui/pages/template_list.dart | 4 +- lib/ui/pages/transaction_details.dart | 1 + lib/ui/utils.dart | 52 ++++++- lib/ui/widgets/add_recurring_transaction.dart | 83 +++++----- lib/ui/widgets/add_template.dart | 138 +++++++++-------- lib/ui/widgets/add_transaction.dart | 146 ++++++++---------- 6 files changed, 234 insertions(+), 190 deletions(-) diff --git a/lib/ui/pages/template_list.dart b/lib/ui/pages/template_list.dart index 332fb50..72948fc 100644 --- a/lib/ui/pages/template_list.dart +++ b/lib/ui/pages/template_list.dart @@ -28,8 +28,7 @@ class TemplateListState extends State { itemCount: state.recurringTransactions.length, shrinkWrap: true, itemBuilder: - (ctx, idx) => Card( - child: ListTile( + (ctx, idx) => ListTile( title: Text( state .recurringTransactions[idx] @@ -38,7 +37,6 @@ class TemplateListState extends State { .name, ), ), - ), ), ), ], diff --git a/lib/ui/pages/transaction_details.dart b/lib/ui/pages/transaction_details.dart index fa0ce5d..63b0eed 100644 --- a/lib/ui/pages/transaction_details.dart +++ b/lib/ui/pages/transaction_details.dart @@ -77,6 +77,7 @@ class TransactionDetailsPage extends StatelessWidget { child: ListView( children: [ Row( + crossAxisAlignment: CrossAxisAlignment.start, children: [ StreamBuilder( stream: watchBeneficiaryObject( diff --git a/lib/ui/utils.dart b/lib/ui/utils.dart index 59c36a0..927de3a 100644 --- a/lib/ui/utils.dart +++ b/lib/ui/utils.dart @@ -1,10 +1,14 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:okane/database/collections/template.dart'; import 'package:okane/screen.dart'; +import 'package:okane/ui/state/core.dart'; Future showDialogOrModal({ required BuildContext context, required WidgetBuilder builder, bool showDragHandle = true, + bool horizontalPaddingOnMobile = true, }) { final screenSize = getScreenSize(context); final width = MediaQuery.sizeOf(context).shortestSide; @@ -18,6 +22,8 @@ Future showDialogOrModal({ (context) => Padding( padding: EdgeInsets.only( bottom: 32 + MediaQuery.of(context).viewInsets.bottom, + left: horizontalPaddingOnMobile ? 16 : 0, + right: horizontalPaddingOnMobile ? 16 : 0, ), child: builder(context), ), @@ -26,11 +32,14 @@ Future showDialogOrModal({ context: context, builder: (context) => Dialog( - child: Container( - constraints: BoxConstraints(maxWidth: width * 0.7), - child: Padding( - padding: EdgeInsets.only(bottom: 32), - child: builder(context), + child: Padding( + padding: EdgeInsets.only(top: 16), + child: Container( + constraints: BoxConstraints(maxWidth: width * 0.7), + child: Padding( + padding: EdgeInsets.only(bottom: 32), + child: builder(context), + ), ), ), ), @@ -38,6 +47,39 @@ Future showDialogOrModal({ }; } +Future selectTransactionTemplate(BuildContext context) { + return showDialogOrModal( + context: context, + builder: (context) { + return BlocBuilder( + builder: (context, state) { + if (state.transactionTemplates.isEmpty) { + return Padding( + padding: EdgeInsets.symmetric(horizontal: 16), + child: Text("No templates defined"), + ); + } + + return ListView.builder( + itemCount: state.transactionTemplates.length, + itemBuilder: + (context, index) => ListTile( + title: Text( + state.transactionTemplates[index].name, + ), + onTap: () { + Navigator.of( + context, + ).pop(state.transactionTemplates[index]); + }, + ), + ); + }, + ); + }, + ); +} + DateTime toMidnight(DateTime t) { return DateTime(t.year, t.month, t.day); } diff --git a/lib/ui/widgets/add_recurring_transaction.dart b/lib/ui/widgets/add_recurring_transaction.dart index 7f3089f..7bd0a6d 100644 --- a/lib/ui/widgets/add_recurring_transaction.dart +++ b/lib/ui/widgets/add_recurring_transaction.dart @@ -151,44 +151,6 @@ class _AddRecurringTransactionTemplateWidgetState decoration: InputDecoration(label: Text("Template name")), ), ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: SearchField( - suggestions: - beneficiaries - .where((el) { - final bloc = GetIt.I.get(); - if (el.type == BeneficiaryType.account) { - return el.account.value?.id != bloc.activeAccount?.id; - } - return true; - }) - .map((el) { - return SearchFieldListItem( - getBeneficiaryName(el), - item: el, - ); - }) - .toList(), - hint: "Beneficiary", - controller: _beneficiaryTextController, - selectedValue: _selectedBeneficiary, - onSuggestionTap: (beneficiary) { - setState(() => _selectedBeneficiary = beneficiary); - }, - ), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: TextField( - controller: _amountTextController, - keyboardType: TextInputType.numberWithOptions( - signed: false, - decimal: false, - ), - decoration: InputDecoration(hintText: "Amount"), - ), - ), Padding( padding: const EdgeInsets.symmetric(vertical: 8), @@ -213,6 +175,51 @@ class _AddRecurringTransactionTemplateWidgetState ), ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: SearchField( + suggestions: + beneficiaries + .where((el) { + final bloc = GetIt.I.get(); + if (el.type == BeneficiaryType.account) { + return el.account.value?.id != bloc.activeAccount?.id; + } + return true; + }) + .map((el) { + return SearchFieldListItem( + getBeneficiaryName(el), + item: el, + ); + }) + .toList(), + hint: switch (_selectedDirection) { + TransactionDirection.send => "Payee", + TransactionDirection.receive => "Payer", + }, + controller: _beneficiaryTextController, + selectedValue: _selectedBeneficiary, + onSuggestionTap: (beneficiary) { + setState(() => _selectedBeneficiary = beneficiary); + }, + ), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: TextField( + controller: _amountTextController, + keyboardType: TextInputType.numberWithOptions( + signed: false, + decimal: false, + ), + decoration: InputDecoration( + hintText: "Amount", + icon: Icon(Icons.euro), + ), + ), + ), + Padding( padding: EdgeInsets.only(left: 16, right: 16, top: 16), child: SegmentedButton( diff --git a/lib/ui/widgets/add_template.dart b/lib/ui/widgets/add_template.dart index 5cda454..5a50634 100644 --- a/lib/ui/widgets/add_template.dart +++ b/lib/ui/widgets/add_template.dart @@ -39,7 +39,7 @@ class _AddTransactionTemplateWidgetState TransactionDirection _selectedDirection = TransactionDirection.send; - ExpenseCategory? _expenseCategory = null; + ExpenseCategory? _expenseCategory; String getBeneficiaryName(Beneficiary item) { return switch (item.type) { @@ -129,68 +129,6 @@ class _AddTransactionTemplateWidgetState decoration: InputDecoration(label: Text("Template name")), ), ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: BlocBuilder( - builder: - (context, state) => SearchField( - suggestions: - state.beneficiaries - .where((el) { - final bloc = GetIt.I.get(); - if (el.type == BeneficiaryType.account) { - return el.account.value?.id != - bloc.activeAccount?.id; - } - return true; - }) - .map((el) { - return SearchFieldListItem( - getBeneficiaryName(el), - item: el, - ); - }) - .toList(), - hint: "Beneficiary", - controller: _beneficiaryTextController, - selectedValue: _selectedBeneficiary, - onSuggestionTap: (beneficiary) { - setState(() => _selectedBeneficiary = beneficiary); - }, - ), - ), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: TextField( - controller: _amountTextController, - keyboardType: TextInputType.numberWithOptions( - signed: false, - decimal: false, - ), - decoration: InputDecoration(hintText: "Amount"), - ), - ), - - Row( - children: [ - Text("Expense category"), - OutlinedButton( - onPressed: () async { - final category = await showDialogOrModal( - context: context, - builder: (_) => AddExpenseCategory(), - ); - if (category == null) { - return; - } - - setState(() => _expenseCategory = category); - }, - child: Text(_expenseCategory?.name ?? "None"), - ), - ], - ), Padding( padding: const EdgeInsets.symmetric(vertical: 8), @@ -215,6 +153,80 @@ class _AddTransactionTemplateWidgetState ), ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: BlocBuilder( + builder: + (context, state) => SearchField( + suggestions: + state.beneficiaries + .where((el) { + final bloc = GetIt.I.get(); + if (el.type == BeneficiaryType.account) { + return el.account.value?.id != + bloc.activeAccount?.id; + } + return true; + }) + .map((el) { + return SearchFieldListItem( + getBeneficiaryName(el), + item: el, + ); + }) + .toList(), + hint: switch (_selectedDirection) { + TransactionDirection.send => "Payee", + TransactionDirection.receive => "Payer", + }, + controller: _beneficiaryTextController, + selectedValue: _selectedBeneficiary, + onSuggestionTap: (beneficiary) { + setState(() => _selectedBeneficiary = beneficiary); + }, + ), + ), + ), + + Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: TextField( + controller: _amountTextController, + keyboardType: TextInputType.numberWithOptions( + signed: false, + decimal: false, + ), + decoration: InputDecoration( + hintText: "Amount", + icon: Icon(Icons.euro), + ), + ), + ), + + Row( + children: [ + Text("Expense category"), + + Padding( + padding: EdgeInsets.only(left: 16), + child: OutlinedButton( + onPressed: () async { + final category = await showDialogOrModal( + context: context, + builder: (_) => AddExpenseCategory(), + ); + if (category == null) { + return; + } + + setState(() => _expenseCategory = category); + }, + child: Text(_expenseCategory?.name ?? "None"), + ), + ), + ], + ), + Align( alignment: Alignment.centerRight, child: OutlinedButton( diff --git a/lib/ui/widgets/add_transaction.dart b/lib/ui/widgets/add_transaction.dart index 42632c9..e087a46 100644 --- a/lib/ui/widgets/add_transaction.dart +++ b/lib/ui/widgets/add_transaction.dart @@ -134,33 +134,7 @@ class _AddTransactionWidgetState extends State { children: [ OutlinedButton( onPressed: () async { - final template = await showDialogOrModal( - context: context, - builder: (context) { - return BlocBuilder( - builder: (context, state) { - if (state.transactionTemplates.isEmpty) { - return Text("No templates defined"); - } - - return ListView.builder( - itemCount: state.transactionTemplates.length, - itemBuilder: - (context, index) => ListTile( - title: Text( - state.transactionTemplates[index].name, - ), - onTap: () { - Navigator.of( - context, - ).pop(state.transactionTemplates[index]); - }, - ), - ); - }, - ); - }, - ); + final template = await selectTransactionTemplate(context); if (template == null) { return; } @@ -180,6 +154,30 @@ class _AddTransactionWidgetState extends State { }, child: Text("Use template"), ), + + Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: SegmentedButton( + segments: [ + ButtonSegment( + value: TransactionDirection.send, + label: Text("Send"), + icon: Icon(Icons.remove), + ), + ButtonSegment( + value: TransactionDirection.receive, + label: Text("Receive"), + icon: Icon(Icons.add), + ), + ], + selected: {_selectedDirection}, + multiSelectionEnabled: false, + onSelectionChanged: (selection) { + setState(() => _selectedDirection = selection.first); + }, + ), + ), + Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: BlocBuilder( @@ -215,29 +213,6 @@ class _AddTransactionWidgetState extends State { ), ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: SegmentedButton( - segments: [ - ButtonSegment( - value: TransactionDirection.send, - label: Text("Send"), - icon: Icon(Icons.remove), - ), - ButtonSegment( - value: TransactionDirection.receive, - label: Text("Receive"), - icon: Icon(Icons.add), - ), - ], - selected: {_selectedDirection}, - multiSelectionEnabled: false, - onSelectionChanged: (selection) { - setState(() => _selectedDirection = selection.first); - }, - ), - ), - Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: TextField( @@ -246,7 +221,10 @@ class _AddTransactionWidgetState extends State { signed: false, decimal: false, ), - decoration: InputDecoration(hintText: "Amount"), + decoration: InputDecoration( + hintText: "Amount", + icon: Icon(Icons.euro), + ), ), ), @@ -256,25 +234,28 @@ class _AddTransactionWidgetState extends State { mainAxisAlignment: MainAxisAlignment.start, children: [ Text("Date"), - OutlinedButton( - onPressed: () async { - final dt = await showDatePicker( - context: context, - initialDate: _selectedDate, - firstDate: DateTime(1), - lastDate: DateTime(9999), - ); - if (dt == null) return; + Padding( + padding: EdgeInsets.only(left: 16), + child: OutlinedButton( + onPressed: () async { + final dt = await showDatePicker( + context: context, + initialDate: _selectedDate, + firstDate: DateTime(1), + lastDate: DateTime(9999), + ); + if (dt == null) return; - setState(() => _selectedDate = dt); - }, - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Icon(Icons.date_range), - Text(formatDateTime(_selectedDate)), - ], + setState(() => _selectedDate = dt); + }, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Icon(Icons.date_range), + Text(formatDateTime(_selectedDate)), + ], + ), ), ), ], @@ -284,19 +265,22 @@ class _AddTransactionWidgetState extends State { Row( children: [ Text("Expense category"), - OutlinedButton( - onPressed: () async { - final category = await showDialogOrModal( - context: context, - builder: (_) => AddExpenseCategory(), - ); - if (category == null) { - return; - } + Padding( + padding: EdgeInsets.only(left: 16), + child: OutlinedButton( + onPressed: () async { + final category = await showDialogOrModal( + context: context, + builder: (_) => AddExpenseCategory(), + ); + if (category == null) { + return; + } - setState(() => _expenseCategory = category); - }, - child: Text(_expenseCategory?.name ?? "None"), + setState(() => _expenseCategory = category); + }, + child: Text(_expenseCategory?.name ?? "None"), + ), ), ], ),