Initial commit
This commit is contained in:
118
lib/features/profile/presentation/bloc/profile_bloc.dart
Normal file
118
lib/features/profile/presentation/bloc/profile_bloc.dart
Normal 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,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
41
lib/features/profile/presentation/bloc/profile_event.dart
Normal file
41
lib/features/profile/presentation/bloc/profile_event.dart
Normal 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 => [];
|
||||
}
|
||||
32
lib/features/profile/presentation/bloc/profile_state.dart
Normal file
32
lib/features/profile/presentation/bloc/profile_state.dart
Normal 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];
|
||||
}
|
||||
19
lib/features/profile/presentation/mixins/profile_mixin.dart
Normal file
19
lib/features/profile/presentation/mixins/profile_mixin.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
486
lib/features/profile/presentation/pages/profile_page.dart
Normal file
486
lib/features/profile/presentation/pages/profile_page.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user