feat: Refactor code structure and optimize performance across multiple modules

This commit is contained in:
Achintha Isuru
2025-11-17 23:29:28 -05:00
parent 831570f2e0
commit a64cbd9edf
1508 changed files with 105319 additions and 0 deletions

View File

@@ -0,0 +1,118 @@
import 'dart:developer';
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow/core/application/routing/routes.gr.dart';
import 'package:krow/core/data/enums/state_status.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_app_bar.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_button.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_loading_overlay.dart';
import 'package:krow/features/profile/personal_info/domain/bloc/personal_info_bloc.dart';
import 'package:krow/features/profile/personal_info/presentation/widgets/personal_info_form.dart';
@RoutePage()
class PersonalInfoScreen extends StatelessWidget implements AutoRouteWrapper {
const PersonalInfoScreen({
super.key,
this.isInEditMode = false,
});
final bool isInEditMode;
@override
Widget wrappedRoute(BuildContext context) {
return BlocProvider(
create: (context) =>
PersonalInfoBloc()..add(InitializeProfileInfoEvent(isInEditMode)),
child: this,
);
}
bool _handleBuildWhen(
PersonalInfoState previous,
PersonalInfoState current,
) {
return previous.status != current.status;
}
bool _handleListenWhen(
PersonalInfoState previous,
PersonalInfoState current,
) {
return previous.status != current.status ||
previous.shouldRouteToVerification != current.shouldRouteToVerification;
}
Future<void> _handleListen(
BuildContext context,
PersonalInfoState state,
) async {
if (state.shouldRouteToVerification) {
final isEmailVerified = await context.pushRoute<bool>(
EmailVerificationRoute(
verifiedEmail: state.email,
userPhone: state.phoneNumber,
),
);
if (!context.mounted) return;
context.read<PersonalInfoBloc>().add(
UpdateEmailVerificationStatus(
isEmailVerified: isEmailVerified ?? false,
),
);
return;
}
if (state.status == StateStatus.success && context.mounted) {
if (isInEditMode) {
context.maybePop();
} else {
context.pushRoute(
EmergencyContactsRoute(isInEditMode: false),
);
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: KwAppBar(
titleText: 'Personal Information'.tr(),
showNotification: isInEditMode,
),
body: SingleChildScrollView(
primary: false,
padding: const EdgeInsets.all(16),
child: PersonalInfoForm(isInEditMode: isInEditMode),
),
bottomNavigationBar: SafeArea(
top: false,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: BlocConsumer<PersonalInfoBloc, PersonalInfoState>(
buildWhen: _handleBuildWhen,
listenWhen: _handleListenWhen,
listener: _handleListen,
builder: (context, state) {
return KwLoadingOverlay(
shouldShowLoading: state.status == StateStatus.loading,
child: KwButton.primary(
label: isInEditMode ? 'save_changes'.tr() : 'save_and_continue'.tr(),
onPressed: () {
context
.read<PersonalInfoBloc>()
.add(const SaveProfileChanges());
},
),
);
},
),
),
),
);
}
}

View File

@@ -0,0 +1,174 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:gap/gap.dart';
import 'package:krow/core/presentation/styles/kw_text_styles.dart';
import 'package:krow/core/presentation/styles/theme.dart';
import 'package:krow/core/presentation/widgets/profile_icon.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_input.dart';
import 'package:krow/features/profile/personal_info/domain/bloc/personal_info_bloc.dart';
class PersonalInfoForm extends StatefulWidget {
const PersonalInfoForm({
super.key,
required this.isInEditMode,
});
final bool isInEditMode;
@override
State<PersonalInfoForm> createState() => _PersonalInfoFormState();
}
class _PersonalInfoFormState extends State<PersonalInfoForm> {
final _firstNameController = TextEditingController();
final _lastNameController = TextEditingController();
final _middleNameController = TextEditingController();
final _emailController = TextEditingController();
late final _bloc = context.read<PersonalInfoBloc>();
void _syncControllersWithState(PersonalInfoState state) {
_firstNameController.text = state.firstName;
_lastNameController.text = state.lastName;
_middleNameController.text = state.middleName ?? '';
_emailController.text = state.email;
}
@override
void initState() {
super.initState();
_syncControllersWithState(_bloc.state);
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (!widget.isInEditMode) ...[
const Gap(4),
Text(
'lets_get_started'.tr(),
style: AppTextStyles.headingH1,
textAlign: TextAlign.start,
),
const Gap(8),
Text(
'tell_us_about_yourself'.tr(),
style: AppTextStyles.bodyMediumReg.copyWith(
color: AppColors.blackGray,
),
textAlign: TextAlign.start,
),
const Gap(24),
],
BlocBuilder<PersonalInfoBloc, PersonalInfoState>(
buildWhen: (previous, current) =>
previous.profileImageUrl != current.profileImageUrl ||
previous.validationErrors != current.validationErrors,
builder: (context, state) {
return ProfileIcon(
onChange: (imagePath) {
_bloc.add(UpdateProfileImage(imagePath));
},
imagePath: state.profileImagePath,
imageUrl: state.profileImageUrl,
showError: state.validationErrors
.containsKey(ProfileRequiredField.avatar),
);
},
),
const Gap(8),
BlocConsumer<PersonalInfoBloc, PersonalInfoState>(
buildWhen: (previous, current) {
return previous.status != current.status ||
previous.validationErrors != current.validationErrors;
},
listenWhen: (previous, current) =>
previous.isUpdateReceived != current.isUpdateReceived,
listener: (_, state) {
if (!state.isUpdateReceived) return;
_syncControllersWithState(state);
},
builder: (context, state) {
return Column(
children: [
KwTextInput(
title: 'first_name'.tr(),
hintText: 'enter_first_name'.tr(),
controller: _firstNameController,
keyboardType: TextInputType.name,
textInputAction: TextInputAction.next,
textCapitalization: TextCapitalization.sentences,
showError: state.validationErrors
.containsKey(ProfileRequiredField.firstName),
helperText:
state.validationErrors[ProfileRequiredField.firstName],
onChanged: (firstName) {
_bloc.add(UpdateFirstName(firstName));
},
),
const Gap(8),
KwTextInput(
title: 'last_name'.tr(),
hintText: 'enter_last_name'.tr(),
controller: _lastNameController,
keyboardType: TextInputType.name,
textInputAction: TextInputAction.next,
textCapitalization: TextCapitalization.sentences,
showError: state.validationErrors
.containsKey(ProfileRequiredField.lastName),
helperText:
state.validationErrors[ProfileRequiredField.lastName],
onChanged: (lastName) {
_bloc.add(UpdateLastName(lastName));
},
),
const Gap(8),
KwTextInput(
title: 'middle_name_optional'.tr(),
hintText: 'enter_middle_name'.tr(),
controller: _middleNameController,
keyboardType: TextInputType.name,
textInputAction: TextInputAction.next,
textCapitalization: TextCapitalization.sentences,
onChanged: (middleName) {
_bloc.add(UpdateMiddleName(middleName));
},
),
const Gap(8),
KwTextInput(
title: 'email'.tr(),
hintText: 'email@website.com',
controller: _emailController,
keyboardType: TextInputType.emailAddress,
showError: state.validationErrors
.containsKey(ProfileRequiredField.email),
helperText:
state.validationErrors[ProfileRequiredField.email],
textInputAction: TextInputAction.done,
onChanged: (email) {
_bloc.add(UpdateEmail(email));
},
),
// It is nor clear if we will need it in future.
// if (widget.isInEditMode) ...[
// const Gap(8),
// KwPhoneInput(
// label: 'Phone',
// onChanged: (phoneNumber) {
// _bloc.add(UpdatePhoneNumber(phoneNumber));
// },
// ),
// ]
],
);
},
),
const Gap(40),
],
);
}
}