feat: Refactor onboarding experience and personal info pages

- Updated ExperiencePage to include subtitles in ExperienceSectionTitle.
- Modified ExperienceSectionTitle widget to accept an optional subtitle parameter.
- Refactored PersonalInfoPage to improve imports and structure.
- Removed unused PersonalInfoContent and PersonalInfoForm widgets.
- Introduced new widgets: EditableField, FieldLabel, ReadOnlyField, TappableRow, and LanguageSelector for better modularity.
- Added AccountCard and SecurityNotice widgets for bank account section.
- Enhanced SaveButton to utilize UiButton for consistency.
This commit is contained in:
Achintha Isuru
2026-03-01 03:06:28 -05:00
parent ea6b3fcc76
commit 015f1fbc1b
18 changed files with 562 additions and 530 deletions

View File

@@ -1,14 +1,16 @@
import 'package:core_localization/core_localization.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_modular/flutter_modular.dart';
import 'package:design_system/design_system.dart';
import 'package:core_localization/core_localization.dart';
import 'package:krow_domain/krow_domain.dart';
import 'package:krow_core/core.dart';
import 'package:krow_domain/krow_domain.dart';
import '../blocs/bank_account_cubit.dart';
import '../blocs/bank_account_state.dart';
import '../widgets/account_card.dart';
import '../widgets/add_account_form.dart';
import '../widgets/security_notice.dart';
class BankAccountPage extends StatelessWidget {
const BankAccountPage({super.key});
@@ -26,19 +28,9 @@ class BankAccountPage extends StatelessWidget {
final dynamic strings = t.staff.profile.bank_account_page;
return Scaffold(
backgroundColor: UiColors.background,
appBar: AppBar(
backgroundColor: UiColors.background, // Was surface
elevation: 0,
leading: IconButton(
icon: const Icon(UiIcons.arrowLeft, color: UiColors.textSecondary),
onPressed: () => Modular.to.popSafe(),
),
title: Text(strings.title, style: UiTypography.headline3m.textPrimary),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(1.0),
child: Container(color: UiColors.border, height: 1.0),
),
appBar: UiAppBar(
title: strings.title,
showBackButton: true,
),
body: BlocConsumer<BankAccountCubit, BankAccountState>(
bloc: cubit,
@@ -88,18 +80,51 @@ class BankAccountPage extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
_buildSecurityNotice(strings),
SecurityNotice(strings: strings),
const SizedBox(height: UiConstants.space6),
Text(
strings.linked_accounts,
style: UiTypography.headline4m.copyWith(
color: UiColors.textPrimary,
if (state.accounts.isEmpty)
Center(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: UiConstants.space10,
),
child: Column(
children: <Widget>[
const Icon(
UiIcons.building,
size: 48,
color: UiColors.iconSecondary,
),
const SizedBox(height: UiConstants.space4),
Text(
'No accounts yet',
style: UiTypography.headline4m,
textAlign: TextAlign.center,
),
Text(
'Add your first bank account to get started',
style: UiTypography.body2m.textSecondary,
textAlign: TextAlign.center,
),
],
),
),
)
else ...<Widget>[
Text(
strings.linked_accounts,
style: UiTypography.headline4m.copyWith(
color: UiColors.textPrimary,
),
),
),
const SizedBox(height: UiConstants.space3),
...state.accounts.map(
(StaffBankAccount a) => _buildAccountCard(a, strings),
), // Added type
const SizedBox(height: UiConstants.space3),
...state.accounts.map<Widget>(
(StaffBankAccount account) => AccountCard(
account: account,
strings: strings,
),
),
],
// Add extra padding at bottom
const SizedBox(height: UiConstants.space20),
],
@@ -157,119 +182,4 @@ class BankAccountPage extends StatelessWidget {
),
);
}
Widget _buildSecurityNotice(dynamic strings) {
return Container(
padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration(
color: UiColors.primary.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Icon(UiIcons.shield, color: UiColors.primary, size: 20),
const SizedBox(width: UiConstants.space3),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
strings.secure_title,
style: UiTypography.body2m.textPrimary,
),
const SizedBox(height: UiConstants.space1 - 2), // 2px
Text(
strings.secure_subtitle,
style: UiTypography.body3r.textSecondary,
),
],
),
),
],
),
);
}
Widget _buildAccountCard(StaffBankAccount account, dynamic strings) {
final bool isPrimary = account.isPrimary;
const Color primaryColor = UiColors.primary;
return Container(
margin: const EdgeInsets.only(bottom: UiConstants.space3),
padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration(
color: UiColors.bgPopup, // Was surface, using bgPopup (white) for card
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
border: Border.all(
color: isPrimary ? primaryColor : UiColors.border,
width: isPrimary ? 2 : 1,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
children: <Widget>[
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: primaryColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
),
child: const Center(
child: Icon(
UiIcons.building,
color: primaryColor,
size: UiConstants.iconLg,
),
),
),
const SizedBox(width: UiConstants.space3),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
account.bankName,
style: UiTypography.body2m.textPrimary,
),
Text(
strings.account_ending(
last4: account.last4?.isNotEmpty == true
? account.last4!
: '----',
),
style: UiTypography.body2r.textSecondary,
),
],
),
],
),
if (isPrimary)
Container(
padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space2,
vertical: UiConstants.space1,
),
decoration: BoxDecoration(
color: primaryColor.withValues(alpha: 0.15),
borderRadius: UiConstants.radiusFull,
),
child: Row(
children: <Widget>[
const Icon(
UiIcons.check,
size: UiConstants.iconXs,
color: primaryColor,
),
const SizedBox(width: UiConstants.space1),
Text(strings.primary, style: UiTypography.body3m.primary),
],
),
),
],
),
);
}
}

View File

@@ -0,0 +1,97 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:krow_domain/krow_domain.dart';
class AccountCard extends StatelessWidget {
final StaffBankAccount account;
final dynamic strings;
const AccountCard({
super.key,
required this.account,
required this.strings,
});
@override
Widget build(BuildContext context) {
final bool isPrimary = account.isPrimary;
const Color primaryColor = UiColors.primary;
return Container(
margin: const EdgeInsets.only(bottom: UiConstants.space3),
padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration(
color: UiColors.bgPopup,
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
border: Border.all(
color: isPrimary ? primaryColor : UiColors.border,
width: isPrimary ? 2 : 1,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
children: <Widget>[
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: primaryColor.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
),
child: const Center(
child: Icon(
UiIcons.building,
color: primaryColor,
size: UiConstants.iconLg,
),
),
),
const SizedBox(width: UiConstants.space3),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
account.bankName,
style: UiTypography.body2m.textPrimary,
),
Text(
strings.account_ending(
last4: account.last4?.isNotEmpty == true
? account.last4!
: '----',
),
style: UiTypography.body2r.textSecondary,
),
],
),
],
),
if (isPrimary)
Container(
padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space2,
vertical: UiConstants.space1,
),
decoration: BoxDecoration(
color: primaryColor.withValues(alpha: 0.15),
borderRadius: UiConstants.radiusFull,
),
child: Row(
children: <Widget>[
const Icon(
UiIcons.check,
size: UiConstants.iconXs,
color: primaryColor,
),
const SizedBox(width: UiConstants.space1),
Text(strings.primary, style: UiTypography.body3m.primary),
],
),
),
],
),
);
}
}

View File

@@ -0,0 +1,20 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
class SecurityNotice extends StatelessWidget {
final dynamic strings;
const SecurityNotice({
super.key,
required this.strings,
});
@override
Widget build(BuildContext context) {
return UiNoticeBanner(
icon: UiIcons.shield,
title: strings.secure_title,
description: strings.secure_subtitle,
);
}
}