Initial commit

This commit is contained in:
jahongireshonqulov
2025-10-18 09:40:06 +05:00
commit 1bf3e41abe
352 changed files with 16315 additions and 0 deletions

View File

@@ -0,0 +1,118 @@
import 'package:cargocalculaterapp/core/local_source/local_source.dart';
import 'package:cargocalculaterapp/core/usecase/usecase.dart';
import 'package:cargocalculaterapp/router/app_routes.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../constants/constants.dart';
import '../../../../injector_container.dart';
import '../../../../router/name_routes.dart';
import '../../data/models/profile_response.dart';
import '../../data/models/profile_update_request.dart';
import '../../domain/usecase/delete_profile_usecase.dart';
import '../../domain/usecase/profile_update_usecase.dart';
import '../../domain/usecase/profile_usecase.dart';
part 'profile_event.dart';
part 'profile_state.dart';
class ProfileBloc extends Bloc<ProfileEvent, ProfileState> {
ProfileBloc(
this.profileUseCase,
this.profileUpdateUseCase,
this.deleteProfileUseCase,
) : super(const ProfileState(readOnly: true, isLoading: false)) {
on<GetProfileDataEvent>(_getProfileData);
on<EditProfileEvent>(_editProfile);
on<UpdateProfileEvent>(_updateUserInfo);
on<DeleteProfileEvent>(_deleteProfile);
}
final ProfileUseCase profileUseCase;
final ProfileUpdateUseCase profileUpdateUseCase;
final DeleteProfileUseCase deleteProfileUseCase;
Future<void> _getProfileData(
GetProfileDataEvent event,
Emitter<ProfileState> emit,
) async {
emit(state.copyWith(isLoading: true));
final cacheResponse = await profileUseCase(true);
cacheResponse.fold((l) {}, (r) {
emit(state.copyWith(isLoading: false, profileData: r));
});
final response = await profileUseCase(false);
response.fold(
(l) {
emit(state.copyWith(isLoading: false));
},
(r) {
sl<LocalSource>().setUCode(r.ucode ?? "");
emit(state.copyWith(isLoading: false, profileData: r));
},
);
}
void _editProfile(EditProfileEvent event, Emitter<ProfileState> emit) {
emit(state.copyWith(readOnly: false));
}
Future<void> _updateUserInfo(
UpdateProfileEvent event,
Emitter<ProfileState> emit,
) async {
bool hasError = false;
if (event.fullName.isEmpty) {
hasError = true;
} else if ((event.phone.isNotEmpty) &&
!RegExConst.phoneRegex.hasMatch(event.phone)) {
hasError = true;
} else if ((event.email.isNotEmpty) &&
!RegExConst.emailRegex.hasMatch(event.email)) {
hasError = true;
}
if (hasError) {
emit(state.copyWith(hasError: true));
return;
} else {
emit(state.copyWith(hasError: false, isLoading: true));
}
final response = await profileUpdateUseCase(
ProfileUpdateRequest(
email: event.email,
phoneNumber: event.phone.replaceAll("+", ""),
fullname: event.fullName,
),
);
response.fold(
(l) {
emit(state.copyWith(isLoading: false));
},
(r) {
emit(state.copyWith(isLoading: false, readOnly: true));
},
);
}
Future<void> _deleteProfile(
DeleteProfileEvent event,
Emitter<ProfileState> emit,
) async {
final response = await deleteProfileUseCase(const NoParams());
await response.fold((l) {}, (r) async {
final language = sl<LocalSource>().getLocale();
await sl<LocalSource>().clear().then((value) {
sl<LocalSource>().setLocale(language);
sl<LocalSource>().setIsFirstEnter(false);
if (rootNavigatorKey.currentContext?.mounted ?? false) {
Navigator.pushNamedAndRemoveUntil(
rootNavigatorKey.currentContext!,
Routes.auth,
(route) => false,
);
}
});
});
}
}

View File

@@ -0,0 +1,41 @@
part of 'profile_bloc.dart';
sealed class ProfileEvent extends Equatable {
const ProfileEvent();
}
final class GetProfileDataEvent extends ProfileEvent {
const GetProfileDataEvent();
@override
List<Object?> get props => [];
}
final class EditProfileEvent extends ProfileEvent {
const EditProfileEvent();
@override
List<Object?> get props => [];
}
final class UpdateProfileEvent extends ProfileEvent {
const UpdateProfileEvent({
required this.email,
required this.fullName,
required this.phone,
});
final String email;
final String fullName;
final String phone;
@override
List<Object?> get props => [email, fullName, phone];
}
final class DeleteProfileEvent extends ProfileEvent {
const DeleteProfileEvent();
@override
List<Object?> get props => [];
}

View File

@@ -0,0 +1,32 @@
part of 'profile_bloc.dart';
class ProfileState extends Equatable {
const ProfileState({
required this.isLoading,
required this.readOnly,
this.profileData,
this.hasError,
});
final bool isLoading;
final bool readOnly;
final ProfileResponse? profileData;
final bool? hasError;
ProfileState copyWith({
bool? isLoading,
bool? readOnly,
ProfileResponse? profileData,
bool? hasError,
}) {
return ProfileState(
isLoading: isLoading ?? this.isLoading,
readOnly: readOnly ?? this.readOnly,
profileData: profileData ?? this.profileData,
hasError: hasError ?? this.hasError,
);
}
@override
List<Object?> get props => [isLoading, readOnly, profileData, hasError];
}

View File

@@ -0,0 +1,19 @@
import 'package:flutter/cupertino.dart';
mixin ProfileMixin {
late TextEditingController fullNameController;
late TextEditingController phoneController;
late TextEditingController emailController;
void initControllers() {
fullNameController = TextEditingController();
phoneController = TextEditingController();
emailController = TextEditingController();
}
void disposeControllers() {
fullNameController.dispose();
phoneController.dispose();
emailController.dispose();
}
}

View File

@@ -0,0 +1,87 @@
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
import 'package:cargocalculaterapp/core/theme/colors/app_colors.dart';
import 'package:cargocalculaterapp/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../../../../../core/utils/app_utils.dart';
class DeleteProfileDialog extends StatelessWidget {
const DeleteProfileDialog({super.key});
@override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: context.color.scaffoldBackgroundColor,
insetPadding: AppUtils.kPaddingAll16,
child: Padding(
padding: const EdgeInsets.only(
top: 32,
left: 16,
right: 16,
bottom: 24,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SvgPicture.asset(
"assets/svg/ic_warning.svg",
height: 48,
width: 48,
),
AppUtils.kBoxHeight16,
Text(
AppLocalization.current.delete_account_desc,
style: context.text.secondaryText14,
textAlign: TextAlign.center,
),
AppUtils.kBoxHeight16,
Row(
children: [
Expanded(
child: InkWell(
onTap: () {
Navigator.pop(context);
},
borderRadius: AppUtils.kBorderRadius24,
child: Ink(
decoration: BoxDecoration(
color: context.color.scaffoldBackgroundColor,
borderRadius: AppUtils.kBorderRadius24,
border: Border.all(color: context.color.primaryColor),
),
height: 56,
padding: AppUtils.kPaddingHor16,
child: Center(
child: Text(
AppLocalization.current.cancel,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: context.color.textColor,
),
),
),
),
),
),
AppUtils.kBoxWidth16,
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: LightThemeColors.errorColor,
),
onPressed: () {
Navigator.pop(context, true);
},
child: Text(AppLocalization.current.delete),
),
),
],
),
],
),
),
);
}
}

View File

@@ -0,0 +1,96 @@
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
import 'package:cargocalculaterapp/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../../core/app_bloc/app_bloc.dart';
class LanguageDialog extends StatelessWidget {
const LanguageDialog({super.key});
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
decoration: BoxDecoration(
color: context.color.scaffoldBackgroundColor,
borderRadius: AppUtils.kBorderRadiusTop24,
),
child: SafeArea(
minimum: AppUtils.kPaddingL16R16T16B24,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
AppLocalization.current.change_language,
style: context.text.orderTitle,
),
AppUtils.kBoxHeight32,
InkWell(
onTap: () {
context.read<AppBloc>().add(const AppChangeLocale("uz"));
Navigator.pop(context);
},
child: Ink(
child: Row(
children: [
const Image(
width: 20,
height: 20,
image: AssetImage("assets/png/ic_uz.png"),
),
AppUtils.kBoxWidth16,
Text("O'zbek", style: context.text.profileCategory),
],
),
),
),
AppUtils.kBoxHeight32,
InkWell(
onTap: () {
context.read<AppBloc>().add(const AppChangeLocale("ru"));
Navigator.pop(context);
},
child: Ink(
child: Row(
children: [
const Image(
width: 20,
height: 20,
image: AssetImage("assets/png/ic_ru.png"),
),
AppUtils.kBoxWidth16,
Text("Русский", style: context.text.profileCategory),
],
),
),
),
AppUtils.kBoxHeight32,
InkWell(
onTap: () {
context.read<AppBloc>().add(const AppChangeLocale("zh"));
Navigator.pop(context);
},
child: Ink(
child: Row(
children: [
const Image(
width: 20,
height: 20,
image: AssetImage("assets/png/ic_china.png"),
),
AppUtils.kBoxWidth16,
Text("Chinese", style: context.text.profileCategory),
],
),
),
),
AppUtils.kBoxHeight16,
],
),
),
);
}
}

View File

@@ -0,0 +1,486 @@
import 'dart:io';
import 'package:cargocalculaterapp/core/app_bloc/app_bloc.dart';
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
import 'package:cargocalculaterapp/core/theme/app_text_styles.dart';
import 'package:cargocalculaterapp/core/theme/colors/app_colors.dart';
import 'package:cargocalculaterapp/core/theme/theme_data.dart';
import 'package:cargocalculaterapp/core/utils/app_utils.dart';
import 'package:cargocalculaterapp/core/widgets/text_filds/custom_text_field_name.dart';
import 'package:cargocalculaterapp/features/profile/presentation/pages/widgets/user_info_widget.dart';
import 'package:cargocalculaterapp/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../../../../constants/constants.dart';
import '../../../../core/local_source/local_source.dart';
import '../../../../core/widgets/loading/progress_hud.dart';
import '../../../../injector_container.dart';
import '../../../../router/name_routes.dart';
import '../bloc/profile_bloc.dart';
import '../mixins/profile_mixin.dart';
import 'dialog/delete_profile_dialog.dart';
import 'dialog/language_dialog.dart';
class ProfilePage extends StatefulWidget {
const ProfilePage({super.key});
@override
State<ProfilePage> createState() => _ProfilePageState();
}
class _ProfilePageState extends State<ProfilePage> with ProfileMixin {
@override
void initState() {
initControllers();
context.read<ProfileBloc>().add(const GetProfileDataEvent());
super.initState();
}
@override
Widget build(BuildContext context) {
return BlocConsumer<ProfileBloc, ProfileState>(
listenWhen: (previous, current) =>
previous.profileData != current.profileData,
listener: (context, state) {
fullNameController.text = state.profileData?.fullname ?? "";
emailController.text = state.profileData?.email ?? "";
phoneController.text = state.profileData?.phoneNumber ?? "";
},
builder: (context, state) {
return ModalProgressHUD(
inAsyncCall: state.isLoading,
child: Scaffold(
appBar: AppBar(
elevation: 0.5,
automaticallyImplyLeading: false,
title: Text(AppLocalization.current.profile),
),
body: ListView(
padding: AppUtils.kPaddingAll16,
children: [
state.readOnly
? Container(
padding: const EdgeInsets.only(
left: 16,
right: 16,
top: 16,
),
width: double.infinity,
decoration: BoxDecoration(
borderRadius: AppUtils.kBorderRadius16,
color: context.color.statusBackground,
border: Border.all(color: context.color.lightBorder),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
AppLocalization.current.personal_info,
style: context.text.orderTitle,
),
),
if (state.readOnly)
GestureDetector(
onTap: () {
context.read<ProfileBloc>().add(
const EditProfileEvent(),
);
},
child: SvgPicture.asset(
"assets/svg/ic_edit.svg",
),
),
],
),
AppUtils.kBoxHeight16,
UserInfoWidget(
title: AppLocalization.current.full_name,
name: state.profileData?.fullname ?? "-",
),
UserInfoWidget(
title: AppLocalization.current.phone_number,
name: state.profileData?.phoneNumber ?? "-",
),
UserInfoWidget(
title: AppLocalization.current.email,
name: state.profileData?.email ?? "-",
),
],
),
)
: Container(
padding: const EdgeInsets.only(
left: 16,
right: 16,
top: 16,
),
width: double.infinity,
decoration: BoxDecoration(
borderRadius: AppUtils.kBorderRadius16,
color: context.color.statusBackground,
border: Border.all(color: context.color.lightBorder),
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
AppLocalization.current.personal_info,
style: context.text.orderTitle,
),
AppUtils.kBoxHeight16,
CustomTextFieldName(
hint: AppLocalization.current.full_name_hint,
name: AppLocalization.current.full_name,
controller: fullNameController,
inputType: TextInputType.name,
errorText:
AppLocalization.current.error_full_name,
isError:
(state.hasError ?? false) &&
(fullNameController.text.isNotEmpty),
),
AppUtils.kBoxHeight16,
CustomTextFieldName(
hint: AppLocalization.current.phone_number_text,
name: AppLocalization.current.phone_number,
controller: phoneController,
inputType: TextInputType.phone,
errorText: AppLocalization.current.error_in_phone,
isError:
(state.hasError ?? false) &&
!RegExConst.phoneRegex.hasMatch(
phoneController.text,
),
),
AppUtils.kBoxHeight16,
CustomTextFieldName(
hint: AppLocalization.current.email_address,
name: AppLocalization.current.email,
controller: emailController,
inputType: TextInputType.emailAddress,
errorText: AppLocalization.current.error_email,
isError:
(state.hasError ?? false) &&
!RegExConst.emailRegex.hasMatch(
emailController.text,
),
),
AppUtils.kBoxHeight16,
ElevatedButton(
onPressed: () {
context.read<ProfileBloc>().add(
UpdateProfileEvent(
fullName: fullNameController.text,
email: emailController.text,
phone: phoneController.text,
),
);
},
child: Text(AppLocalization.current.save),
),
AppUtils.kBoxHeight16,
],
),
),
AppUtils.kBoxHeight16,
Material(
color: Colors.transparent,
child: InkWell(
borderRadius: AppUtils.kBorderRadius16,
onTap: () {
Clipboard.setData(
ClipboardData(text: sl<LocalSource>().getUCode()),
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(AppLocalization.current.text_copied),
),
);
},
child: Ink(
padding: AppUtils.kPaddingHor16,
height: 56,
width: double.infinity,
decoration: BoxDecoration(
borderRadius: AppUtils.kBorderRadius16,
color: context.color.scaffoldBackgroundColor,
border: Border.all(color: context.color.borderColor),
),
child: Row(
children: [
Icon(
Icons.numbers_outlined,
size: 24,
color: context.color.textColor,
),
AppUtils.kBoxWidth8,
Text(
AppLocalization.current.user_id,
style: context.text.profileCategory,
),
const Spacer(),
Text(
sl<LocalSource>().getUCode(),
style: context.text.statusNumber,
),
AppUtils.kBoxWidth8,
Icon(
Icons.copy_outlined,
color: context.color.textColor,
size: 16,
),
],
),
),
),
),
AppUtils.kBoxHeight16,
Material(
color: Colors.transparent,
child: InkWell(
borderRadius: AppUtils.kBorderRadius16,
onTap: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
isDismissible: true,
builder: (context) => const LanguageDialog(),
);
},
child: Ink(
padding: AppUtils.kPaddingHor16,
height: 56,
width: double.infinity,
decoration: BoxDecoration(
borderRadius: AppUtils.kBorderRadius16,
color: context.color.scaffoldBackgroundColor,
border: Border.all(color: context.color.borderColor),
),
child: Row(
children: [
SvgPicture.asset(
"assets/svg/ic_world.svg",
width: 24,
height: 24,
colorFilter: ColorFilter.mode(
context.color.textColor,
BlendMode.srcIn,
),
),
AppUtils.kBoxWidth8,
Text(
AppLocalization.current.change_language,
style: context.text.profileCategory,
),
const Spacer(),
Text(
sl<LocalSource>().getLocale() == "uz"
? "O'zbek"
: sl<LocalSource>().getLocale() == "ru"
? "Русский"
: "Chinese",
style: context.text.statusNumber,
),
AppUtils.kBoxWidth8,
Icon(
Icons.arrow_forward_ios_rounded,
color: context.color.textColor,
size: 16,
),
],
),
),
),
),
AppUtils.kBoxHeight16,
Material(
color: Colors.transparent,
child: InkWell(
onTap: () {
if (sl<LocalSource>().getThemeMode() ==
ThemeMode.light.name) {
context.read<AppBloc>().add(
AppThemeSwitchDark(darkTheme: darkTheme),
);
} else {
context.read<AppBloc>().add(
AppThemeSwitchLight(lightTheme: lightTheme),
);
}
},
borderRadius: AppUtils.kBorderRadius16,
child: Ink(
padding: AppUtils.kPaddingHor16,
height: 56,
width: double.infinity,
decoration: BoxDecoration(
borderRadius: AppUtils.kBorderRadius16,
color: context.color.scaffoldBackgroundColor,
border: Border.all(color: context.color.borderColor),
),
child: Row(
children: [
SvgPicture.asset(
"assets/svg/ic_sun.svg",
width: 24,
height: 24,
colorFilter: ColorFilter.mode(
context.color.textColor,
BlendMode.srcIn,
),
),
AppUtils.kBoxWidth8,
Text(
AppLocalization.current.theme_mode,
style: context.text.profileCategory,
),
const Spacer(),
Text(
sl<LocalSource>().getThemeMode() ==
ThemeMode.light.name
? AppLocalization.current.light
: AppLocalization.current.dark,
style: context.text.statusNumber,
),
AppUtils.kBoxWidth8,
AnimatedContainer(
duration: const Duration(milliseconds: 200),
width: 24,
height: 16,
padding: const EdgeInsets.all(2),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
border: Border.all(color: Colors.black, width: 2),
),
alignment:
sl<LocalSource>().getThemeMode() ==
ThemeMode.light.name
? Alignment.centerRight
: Alignment.centerLeft,
child: Container(
width: 8,
height: 8,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.black,
),
),
),
],
),
),
),
),
AppUtils.kBoxHeight16,
Material(
color: Colors.transparent,
child: Padding(
padding: AppUtils.kPaddingHor24,
child: InkWell(
borderRadius: AppUtils.kBorderRadius24,
onTap: () async {
final language = sl<LocalSource>().getLocale();
await sl<LocalSource>().clear().then((value) {
sl<LocalSource>().setLocale(language);
sl<LocalSource>().setIsFirstEnter(false);
if (context.mounted) {
Navigator.pushNamedAndRemoveUntil(
context,
Routes.auth,
(route) => false,
);
}
});
},
child: Ink(
padding: AppUtils.kPaddingHor16,
height: 56,
width: double.infinity,
decoration: BoxDecoration(
borderRadius: AppUtils.kBorderRadius24,
color: context.color.scaffoldBackgroundColor,
border: Border.all(color: context.color.borderColor),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
"assets/svg/ic_log_out.svg",
width: 24,
height: 24,
colorFilter: const ColorFilter.mode(
ThemeColors.timerRed,
BlendMode.srcIn,
),
),
AppUtils.kBoxWidth8,
Text(
AppLocalization.current.logout,
style: AppTextStyles.saleRed,
),
],
),
),
),
),
),
AppUtils.kBoxHeight24,
//if (Platform.isIOS)
SizedBox(
height:
MediaQuery.of(context).size.height -
MediaQuery.of(context).padding.top -
MediaQuery.of(context).padding.bottom -
kBottomNavigationBarHeight-600
),
if (Platform.isIOS)
Center(
child: TextButton.icon(
onPressed: () {
showDialog(
context: context,
builder: (context) => const DeleteProfileDialog(),
).then((value) {
if (value is bool && context.mounted) {
context.read<ProfileBloc>().add(
const DeleteProfileEvent(),
);
}
});
},
label: Text(
AppLocalization.current.delete_account,
style: AppTextStyles.orderTitle,
),
icon: SvgPicture.asset(
"assets/svg/ic_trash.svg",
width: 18,
height: 18,
colorFilter: ColorFilter.mode(
context.color.textColor,
BlendMode.srcIn,
),
),
),
),
],
),
),
);
},
);
}
@override
void dispose() {
disposeControllers();
super.dispose();
}
}

View File

@@ -0,0 +1,65 @@
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
import 'package:flutter/cupertino.dart';
class ProfileInfoFieldWidget extends StatelessWidget {
const ProfileInfoFieldWidget({
super.key,
required this.title,
this.inputType,
this.controller,
this.readOnly = false,
this.hasError,
this.errorText,
required this.isValid,
});
final String title;
final TextInputType? inputType;
final TextEditingController? controller;
final bool readOnly;
final bool? hasError;
final bool isValid;
final String? errorText;
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CupertinoTextFormFieldRow(
readOnly: readOnly,
cursorColor: context.color.accentColor,
prefix: SizedBox(
width: 90,
child: Text(
title,
style: context.text.profileCategory.copyWith(
fontWeight: FontWeight.w700,
fontSize: 16,
),
),
),
controller: controller,
style: context.text.profileCategory,
textAlign: TextAlign.left,
keyboardType: inputType,
),
if ((controller?.text.isNotEmpty ?? false) &&
(hasError ?? false) &&
(!isValid))
Padding(
padding: const EdgeInsets.only(left: 16.0, top: 0),
child: Text(
errorText ?? "",
style: const TextStyle(
color: CupertinoColors.systemRed,
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
),
],
);
}
}

View File

@@ -0,0 +1,32 @@
import 'package:cargocalculaterapp/core/extension/build_context_extension.dart';
import 'package:flutter/material.dart';
import '../../../../../core/utils/app_utils.dart';
class UserInfoWidget extends StatelessWidget {
const UserInfoWidget({super.key, required this.title, required this.name});
final String title;
final String name;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(child: Text(title, style: context.text.orderListTitle)),
AppUtils.kBoxWidth8,
Expanded(
child: Text(
name,
style: context.text.profileCategory,
textAlign: TextAlign.end,
),
),
],
),
);
}
}