From 4e023f50cb522bca320562dbb11595351c533b90 Mon Sep 17 00:00:00 2001 From: gioisco Date: Thu, 15 Aug 2024 00:13:32 +0200 Subject: [PATCH] Refactor AddAccount layout for better usability - Replaced `Stack` with `Column` to streamline vertical arrangement. - Wrapped the `SingleChildScrollView` component with `Expanded`. - Ensured the confirmation button remains visible and does not overlap with scrollable content. - Enhanced user experience by making sure the input fields and options are fully accessible without obstruction. --- lib/pages/accounts/add_account.dart | 562 ++++++++++++++-------------- 1 file changed, 282 insertions(+), 280 deletions(-) diff --git a/lib/pages/accounts/add_account.dart b/lib/pages/accounts/add_account.dart index 6c5248e3..25fe235b 100644 --- a/lib/pages/accounts/add_account.dart +++ b/lib/pages/accounts/add_account.dart @@ -48,313 +48,315 @@ class _AddAccountState extends ConsumerState with Functions { return Scaffold( backgroundColor: Theme.of(context).colorScheme.background, appBar: AppBar(title: Text("${selectedAccount == null ? "New" : "Edit"} account")), - body: Stack( + body: Column( children: [ - SingleChildScrollView( - child: Column( - children: [ - Container( - width: double.infinity, - margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 24), - padding: const EdgeInsets.fromLTRB(16, 12, 16, 0), - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface, - borderRadius: BorderRadius.circular(4), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "NAME", - style: Theme.of(context) - .textTheme - .labelLarge! - .copyWith(color: Theme.of(context).colorScheme.primary), - ), - TextField( - controller: nameController, - decoration: InputDecoration( - hintText: "Account name", - hintStyle: Theme.of(context).textTheme.titleLarge!.copyWith(color: grey2), - border: InputBorder.none, - contentPadding: const EdgeInsets.all(0), - ), - style: Theme.of(context).textTheme.titleLarge!.copyWith(color: grey1), - ) - ], - ), - ), - Container( - width: double.infinity, - margin: const EdgeInsets.symmetric(horizontal: 16), - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface, - borderRadius: BorderRadius.circular(4), - ), - child: Column( - children: [ - Align( - alignment: Alignment.centerLeft, - child: Text( - "ICON AND COLOR", - style: Theme.of(context) - .textTheme - .labelLarge! - .copyWith(color: Theme.of(context).colorScheme.primary), - ), + Expanded( + child: SingleChildScrollView( + child: Column( + children: [ + Container( + width: double.infinity, + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 24), + padding: const EdgeInsets.fromLTRB(16, 12, 16, 0), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: BorderRadius.circular(4), ), - const SizedBox(height: 20), - Material( - color: Colors.transparent, - child: InkWell( - borderRadius: const BorderRadius.all(Radius.circular(90)), - onTap: () => ref.read(showAccountIconsProvider.notifier).state = true, - child: Ink( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: accountColorListTheme[accountColor], - ), - padding: const EdgeInsets.all(16), - child: Icon( - accountIconList[accountIcon], - size: 48, - color: Theme.of(context).colorScheme.background, - ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "NAME", + style: Theme.of(context) + .textTheme + .labelLarge! + .copyWith(color: Theme.of(context).colorScheme.primary), ), - ), + TextField( + controller: nameController, + decoration: InputDecoration( + hintText: "Account name", + hintStyle: Theme.of(context).textTheme.titleLarge!.copyWith(color: grey2), + border: InputBorder.none, + contentPadding: const EdgeInsets.all(0), + ), + style: Theme.of(context).textTheme.titleLarge!.copyWith(color: grey1), + ) + ], ), - const SizedBox(height: 6), - Text( - "CHOOSE ICON", - style: Theme.of(context) - .textTheme - .labelMedium! - .copyWith(color: Theme.of(context).colorScheme.primary), + ), + Container( + width: double.infinity, + margin: const EdgeInsets.symmetric(horizontal: 16), + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: BorderRadius.circular(4), ), - const SizedBox(height: 12), - if (showAccountIcons) const Divider(height: 1, color: grey2), - if (showAccountIcons) - Container( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - color: Theme.of(context).colorScheme.surface, - child: Column( - children: [ - Align( - alignment: Alignment.topRight, - child: TextButton( - onPressed: () => - ref.read(showAccountIconsProvider.notifier).state = false, - child: Text( - "Done", - style: Theme.of(context) - .textTheme - .bodyLarge! - .copyWith(color: Theme.of(context).colorScheme.secondary), - ), + child: Column( + children: [ + Align( + alignment: Alignment.centerLeft, + child: Text( + "ICON AND COLOR", + style: Theme.of(context) + .textTheme + .labelLarge! + .copyWith(color: Theme.of(context).colorScheme.primary), + ), + ), + const SizedBox(height: 20), + Material( + color: Colors.transparent, + child: InkWell( + borderRadius: const BorderRadius.all(Radius.circular(90)), + onTap: () => ref.read(showAccountIconsProvider.notifier).state = true, + child: Ink( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: accountColorListTheme[accountColor], + ), + padding: const EdgeInsets.all(16), + child: Icon( + accountIconList[accountIcon], + size: 48, + color: Theme.of(context).colorScheme.background, ), ), - GridView.builder( - itemCount: accountIconList.length, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 6), - itemBuilder: (context, index) { - IconData accountIconData = - accountIconList.values.elementAt(index); - String accountIconName = accountIconList.keys.elementAt(index); - return GestureDetector( - onTap: () => ref.read(accountIconProvider.notifier).state = - accountIconName, - child: Container( - margin: const EdgeInsets.all(4), - decoration: BoxDecoration( - color: accountIconList[accountIcon] == accountIconData - ? Theme.of(context).colorScheme.secondary - : Theme.of(context).colorScheme.surface, - // borderRadius: const BorderRadius.all(Radius.circular(4)), - shape: BoxShape.circle), - child: Icon( - accountIconData, - color: accountIconList[accountIcon] == accountIconData - ? Colors.white - : Theme.of(context).colorScheme.primary, - size: 24, + ), + ), + const SizedBox(height: 6), + Text( + "CHOOSE ICON", + style: Theme.of(context) + .textTheme + .labelMedium! + .copyWith(color: Theme.of(context).colorScheme.primary), + ), + const SizedBox(height: 12), + if (showAccountIcons) const Divider(height: 1, color: grey2), + if (showAccountIcons) + Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + color: Theme.of(context).colorScheme.surface, + child: Column( + children: [ + Align( + alignment: Alignment.topRight, + child: TextButton( + onPressed: () => + ref.read(showAccountIconsProvider.notifier).state = false, + child: Text( + "Done", + style: Theme.of(context) + .textTheme + .bodyLarge! + .copyWith(color: Theme.of(context).colorScheme.secondary), ), ), - ); - }, + ), + GridView.builder( + itemCount: accountIconList.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 6), + itemBuilder: (context, index) { + IconData accountIconData = + accountIconList.values.elementAt(index); + String accountIconName = accountIconList.keys.elementAt(index); + return GestureDetector( + onTap: () => ref.read(accountIconProvider.notifier).state = + accountIconName, + child: Container( + margin: const EdgeInsets.all(4), + decoration: BoxDecoration( + color: accountIconList[accountIcon] == accountIconData + ? Theme.of(context).colorScheme.secondary + : Theme.of(context).colorScheme.surface, + // borderRadius: const BorderRadius.all(Radius.circular(4)), + shape: BoxShape.circle), + child: Icon( + accountIconData, + color: accountIconList[accountIcon] == accountIconData + ? Colors.white + : Theme.of(context).colorScheme.primary, + size: 24, + ), + ), + ); + }, + ), + ], ), - ], + ), + const Divider(height: 1, color: grey2), + const SizedBox(height: 12), + SizedBox( + height: 38, + child: ListView.separated( + scrollDirection: Axis.horizontal, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.symmetric(horizontal: 16), + separatorBuilder: (context, index) => const SizedBox(width: 16), + itemBuilder: (context, index) { + Color color = accountColorListTheme[index]; + return GestureDetector( + onTap: () => ref.read(accountColorProvider.notifier).state = index, + child: Container( + height: accountColorListTheme[accountColor] == color ? 38 : 32, + width: accountColorListTheme[accountColor] == color ? 38 : 32, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: color, + border: accountColorListTheme[accountColor] == color + ? Border.all( + color: Theme.of(context).colorScheme.primary, + width: 3, + ) + : null, + ), + ), + ); + }, + itemCount: accountColorListTheme.length, + ), ), - ), - const Divider(height: 1, color: grey2), - const SizedBox(height: 12), - SizedBox( - height: 38, - child: ListView.separated( - scrollDirection: Axis.horizontal, - physics: const BouncingScrollPhysics(), - padding: const EdgeInsets.symmetric(horizontal: 16), - separatorBuilder: (context, index) => const SizedBox(width: 16), - itemBuilder: (context, index) { - Color color = accountColorListTheme[index]; - return GestureDetector( - onTap: () => ref.read(accountColorProvider.notifier).state = index, - child: Container( - height: accountColorListTheme[accountColor] == color ? 38 : 32, - width: accountColorListTheme[accountColor] == color ? 38 : 32, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: color, - border: accountColorListTheme[accountColor] == color - ? Border.all( - color: Theme.of(context).colorScheme.primary, - width: 3, - ) - : null, - ), - ), - ); - }, - itemCount: accountColorListTheme.length, - ), - ), - const SizedBox(height: 6), - Text( - "CHOOSE COLOR", - style: Theme.of(context) - .textTheme - .labelMedium! - .copyWith(color: Theme.of(context).colorScheme.primary), - ), - const SizedBox(height: 12), - ], - ), - ), - if (selectedAccount == null) - Container( - width: double.infinity, - margin: const EdgeInsets.fromLTRB(16, 24, 16, 0), - padding: const EdgeInsets.fromLTRB(16, 12, 16, 0), - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface, - borderRadius: BorderRadius.circular(4), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "STARTING VALUE", - style: Theme.of(context) - .textTheme - .labelLarge! - .copyWith( - color: Theme.of(context).colorScheme.primary), - ), - TextField( - controller: startingValueController, - decoration: InputDecoration( - hintText: "Initial balance", - suffixText: currencyState.selectedCurrency.symbol, - hintStyle: Theme.of(context) + const SizedBox(height: 6), + Text( + "CHOOSE COLOR", + style: Theme.of(context) .textTheme - .titleLarge! - .copyWith(color: grey2), - border: InputBorder.none, - contentPadding: const EdgeInsets.all(0), + .labelMedium! + .copyWith(color: Theme.of(context).colorScheme.primary), ), - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.allow( - RegExp(r'[0-9,]')), - ], - style: Theme.of(context) - .textTheme - .titleLarge! - .copyWith(color: grey1), - ) - ], + const SizedBox(height: 12), + ], + ), ), - ), - Container( - width: double.infinity, - margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 24), - padding: const EdgeInsets.symmetric(horizontal: 16), - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface, - borderRadius: BorderRadius.circular(4), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 12.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + if (selectedAccount == null) + Container( + width: double.infinity, + margin: const EdgeInsets.fromLTRB(16, 24, 16, 0), + padding: const EdgeInsets.fromLTRB(16, 12, 16, 0), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: BorderRadius.circular(4), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Set as main account", + "STARTING VALUE", style: Theme.of(context) .textTheme - .bodyLarge! - .copyWith(color: Theme.of(context).colorScheme.primary), - ), - CupertinoSwitch( - value: accountMainSwitch, - onChanged: (value) => - ref.read(accountMainSwitchProvider.notifier).state = value, + .labelLarge! + .copyWith( + color: Theme.of(context).colorScheme.primary), ), - ], - ), - ), - const Divider(height: 1, color: grey2), - Padding( - padding: const EdgeInsets.symmetric(vertical: 12.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Counts for the net worth", + TextField( + controller: startingValueController, + decoration: InputDecoration( + hintText: "Initial balance", + suffixText: currencyState.selectedCurrency.symbol, + hintStyle: Theme.of(context) + .textTheme + .titleLarge! + .copyWith(color: grey2), + border: InputBorder.none, + contentPadding: const EdgeInsets.all(0), + ), + keyboardType: TextInputType.number, + inputFormatters: [ + FilteringTextInputFormatter.allow( + RegExp(r'[0-9,]')), + ], style: Theme.of(context) .textTheme - .bodyLarge! - .copyWith(color: Theme.of(context).colorScheme.primary), - ), - CupertinoSwitch( - value: countNetWorth, - onChanged: (value) => - ref.read(countNetWorthSwitchProvider.notifier).state = value, - ), + .titleLarge! + .copyWith(color: grey1), + ) ], ), ), - ], - ), - ), - if (selectedAccount != null) - Container( - width: double.infinity, - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), - child: TextButton.icon( - onPressed: () => ref - .read(accountsProvider.notifier) - .removeAccount(selectedAccount.id!) - .whenComplete(() => Navigator.of(context).pop()), - style: TextButton.styleFrom( - padding: const EdgeInsets.symmetric(vertical: 16), - side: const BorderSide(color: red, width: 1), + Container( + width: double.infinity, + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), + padding: const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: BorderRadius.circular(4), ), - icon: const Icon(Icons.delete_outlined, color: red), - label: Text( - "Delete account", - style: Theme.of(context).textTheme.bodyLarge!.copyWith(color: red), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Set as main account", + style: Theme.of(context) + .textTheme + .bodyLarge! + .copyWith(color: Theme.of(context).colorScheme.primary), + ), + CupertinoSwitch( + value: accountMainSwitch, + onChanged: (value) => + ref.read(accountMainSwitchProvider.notifier).state = value, + ), + ], + ), + ), + const Divider(height: 1, color: grey2), + Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Counts for the net worth", + style: Theme.of(context) + .textTheme + .bodyLarge! + .copyWith(color: Theme.of(context).colorScheme.primary), + ), + CupertinoSwitch( + value: countNetWorth, + onChanged: (value) => + ref.read(countNetWorthSwitchProvider.notifier).state = value, + ), + ], + ), + ), + ], ), ), - ), - ], - ), + if (selectedAccount != null) + Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), + child: TextButton.icon( + onPressed: () => ref + .read(accountsProvider.notifier) + .removeAccount(selectedAccount.id!) + .whenComplete(() => Navigator.of(context).pop()), + style: TextButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 16), + side: const BorderSide(color: red, width: 1), + ), + icon: const Icon(Icons.delete_outlined, color: red), + label: Text( + "Delete account", + style: Theme.of(context).textTheme.bodyLarge!.copyWith(color: red), + ), + ), + ), + ], + ), + ) ), Container( alignment: Alignment.bottomCenter,