refactor: clean up imports and improve code readability across various files

This commit is contained in:
mso7nueh
2025-12-24 17:54:35 +03:00
parent 3d774267ca
commit e158d812e4
41 changed files with 4699 additions and 4613 deletions

View File

@@ -1,31 +1,33 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'oauth_verification_confirm_response.freezed.dart';
part 'oauth_verification_confirm_response.g.dart';
@freezed
abstract class OAuthVerificationConfirmResponse with _$OAuthVerificationConfirmResponse {
const factory OAuthVerificationConfirmResponse({
required bool success,
required String message,
required String token,
required Map<String, dynamic> user,
@JsonKey(name: 'is_new_user') required bool isNewUser,
}) = _OAuthVerificationConfirmResponse;
factory OAuthVerificationConfirmResponse.fromJson(Map<String, dynamic> json) => _$OAuthVerificationConfirmResponseFromJson(json);
}
import 'package:freezed_annotation/freezed_annotation.dart';
part 'oauth_verification_confirm_response.freezed.dart';
part 'oauth_verification_confirm_response.g.dart';
@freezed
abstract class OAuthVerificationConfirmResponse with _$OAuthVerificationConfirmResponse {
const factory OAuthVerificationConfirmResponse({
required bool success,
required String message,
required String token,
required Map<String, dynamic> user,
@JsonKey(name: 'is_new_user') required bool isNewUser,
}) = _OAuthVerificationConfirmResponse;
factory OAuthVerificationConfirmResponse.fromJson(Map<String, dynamic> json) => _$OAuthVerificationConfirmResponseFromJson(json);
}

View File

@@ -1,25 +1,27 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'oauth_verification_request.freezed.dart';
part 'oauth_verification_request.g.dart';
@freezed
abstract class OAuthVerificationRequest with _$OAuthVerificationRequest {
const factory OAuthVerificationRequest({String? email, String? phone}) = _OAuthVerificationRequest;
factory OAuthVerificationRequest.fromJson(Map<String, dynamic> json) => _$OAuthVerificationRequestFromJson(json);
}
import 'package:freezed_annotation/freezed_annotation.dart';
part 'oauth_verification_request.freezed.dart';
part 'oauth_verification_request.g.dart';
@freezed
abstract class OAuthVerificationRequest with _$OAuthVerificationRequest {
const factory OAuthVerificationRequest({String? email, String? phone}) = _OAuthVerificationRequest;
factory OAuthVerificationRequest.fromJson(Map<String, dynamic> json) => _$OAuthVerificationRequestFromJson(json);
}

View File

@@ -1,25 +1,27 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'oauth_verification_request_response.freezed.dart';
part 'oauth_verification_request_response.g.dart';
@freezed
abstract class OAuthVerificationRequestResponse with _$OAuthVerificationRequestResponse {
const factory OAuthVerificationRequestResponse({required String email, required String status}) = _OAuthVerificationRequestResponse;
factory OAuthVerificationRequestResponse.fromJson(Map<String, dynamic> json) => _$OAuthVerificationRequestResponseFromJson(json);
}
import 'package:freezed_annotation/freezed_annotation.dart';
part 'oauth_verification_request_response.freezed.dart';
part 'oauth_verification_request_response.g.dart';
@freezed
abstract class OAuthVerificationRequestResponse with _$OAuthVerificationRequestResponse {
const factory OAuthVerificationRequestResponse({required String email, required String status}) = _OAuthVerificationRequestResponse;
factory OAuthVerificationRequestResponse.fromJson(Map<String, dynamic> json) => _$OAuthVerificationRequestResponseFromJson(json);
}

View File

@@ -1,19 +1,21 @@
import 'package:dio/dio.dart';
import 'package:eatvio_app/features/barcode/models/barcode_product_model.dart';
import 'package:retrofit/retrofit.dart';
part 'barcode_service.g.dart';
@RestApi()
abstract class BarcodeService {
factory BarcodeService(Dio dio, {String baseUrl}) = _BarcodeService;
@GET('/api/gsone/barcode/{barcode}')
Future<BarcodeProductModel> getProductByBarcode({@Path('barcode') required String barcode});
@GET('/api/gsone/barcode/status/{task_id}')
Future<BarcodeTaskStatusModel> getTaskStatus({@Path('task_id') required String taskId});
}
import 'package:dio/dio.dart';
import 'package:eatvio_app/features/barcode/models/barcode_product_model.dart';
import 'package:retrofit/retrofit.dart';
part 'barcode_service.g.dart';
@RestApi()
abstract class BarcodeService {
factory BarcodeService(Dio dio, {String baseUrl}) = _BarcodeService;
@GET('/api/gsone/barcode/{barcode}')
Future<BarcodeProductModel> getProductByBarcode({@Path('barcode') required String barcode});
@GET('/api/gsone/barcode/status/{task_id}')
Future<BarcodeTaskStatusModel> getTaskStatus({@Path('task_id') required String taskId});
}

View File

@@ -1,176 +1,178 @@
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/utils/date_time_utils.dart';
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
import 'package:eatvio_app/features/registration_v2/cubit/registration_cubit.dart';
import 'package:eatvio_app/features/registration_v2/cubit/registration_state.dart';
import 'package:eatvio_app/features/registration_v2/presentation/widgets/sex_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:mask_text_input_formatter/mask_text_input_formatter.dart';
class FirstStepContent extends StatefulWidget {
const FirstStepContent({super.key});
@override
State<FirstStepContent> createState() => _FirstStepContentState();
}
class _FirstStepContentState extends State<FirstStepContent> {
final maskFormatterForBirthday = MaskTextInputFormatter(mask: '##.##.####', filter: {"#": RegExp(r'[0-9]')}, type: MaskAutoCompletionType.lazy);
final firstNameController = TextEditingController();
final lastNameController = TextEditingController();
final emailController = TextEditingController();
final phoneController = TextEditingController();
final dateController = TextEditingController();
@override
void initState() {
super.initState();
firstNameController.text = context.read<RegistrationCubitV2>().state.firstName;
lastNameController.text = context.read<RegistrationCubitV2>().state.lastName;
emailController.text = context.read<RegistrationCubitV2>().state.email;
phoneController.text = context.read<RegistrationCubitV2>().state.phone;
dateController.text = context.read<RegistrationCubitV2>().state.birthday?.format('dd.MM.yyyy') ?? '';
}
@override
Widget build(BuildContext context) {
final s = S.of(context);
return BlocBuilder<RegistrationCubitV2, RegistrationStateV2>(
builder: (context, state) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(s.first_name, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
const SizedBox(height: 4),
CustomTextFormField(
hintText: s.your_name,
hasError: !state.isFirstNameValid,
controller: firstNameController,
onChanged: (val) {
context.read<RegistrationCubitV2>().setFirstName(val);
},
),
if (!state.isFirstNameValid) ...[const SizedBox(height: 4), ValidationErrorWidget(error: s.first_name_validation_error)],
],
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(s.last_name, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
const SizedBox(height: 4),
CustomTextFormField(
hintText: s.your_last_name,
hasError: !state.isLastNameValid,
controller: lastNameController,
onChanged: (val) {
context.read<RegistrationCubitV2>().setLastName(val);
},
),
if (!state.isLastNameValid) ...[const SizedBox(height: 4), ValidationErrorWidget(error: s.last_name_error)],
],
),
),
],
),
const SizedBox(height: 12),
Text(s.email, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
CustomTextFormField(
hintText: s.type_email,
hasError: !state.isEmailValid,
controller: emailController,
onChanged: (val) {
context.read<RegistrationCubitV2>().setEmail(val);
},
),
if (!state.isEmailValid) ...[
const SizedBox(height: 4),
ValidationErrorWidget(error: state.emailDisplayError.isEmpty ? s.email_error : state.emailDisplayError),
],
const SizedBox(height: 12),
Text(s.phone, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
Column(
children: [
CustomTextFormField(
hintText: '+7 (999) 999 99 99',
controller: phoneController,
hasError: !state.isPhoneValid,
onChanged: (val) {
context.read<RegistrationCubitV2>().setPhone(val);
},
),
if (!state.isPhoneValid) ...[
const SizedBox(height: 4),
ValidationErrorWidget(error: state.phoneDisplayError.isEmpty ? s.phone_error : state.phoneDisplayError),
],
],
),
const SizedBox(height: 12),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(s.date_of_birth, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
const SizedBox(height: 4),
CustomTextFormField(
textInputAction: TextInputAction.done,
hintText: '01.01.2000',
keyboardType: TextInputType.number,
inputFormatters: [maskFormatterForBirthday],
hasError: !state.isBirthdayValid,
controller: dateController,
onChanged: (val) {
final dateArray = val.split('.');
if (dateArray.length != 3) return;
final date = DateTime(int.parse(dateArray[2]), int.parse(dateArray[1]), int.parse(dateArray[0]));
context.read<RegistrationCubitV2>().setBirthday(date);
},
),
if (!state.isBirthdayValid) ...[
const SizedBox(height: 4),
ValidationErrorWidget(error: state.dateOfBirthDisplayError.isEmpty ? s.date_of_birth_error : state.dateOfBirthDisplayError),
],
],
),
),
const SizedBox(width: 12),
const Expanded(child: SexWidget()),
],
),
const Spacer(),
CustomOutlinedButton(
onPressed: () {
context.read<RegistrationCubitV2>().nextStep();
},
text: s.next,
height: 52,
buttonTextStyle: TextStyles.textSSemibold.copyWith(color: appTheme.whiteA700),
buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.blueA400)),
),
const SizedBox(height: 20),
],
);
},
);
}
}
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/utils/date_time_utils.dart';
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
import 'package:eatvio_app/features/registration_v2/cubit/registration_cubit.dart';
import 'package:eatvio_app/features/registration_v2/cubit/registration_state.dart';
import 'package:eatvio_app/features/registration_v2/presentation/widgets/sex_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:mask_text_input_formatter/mask_text_input_formatter.dart';
class FirstStepContent extends StatefulWidget {
const FirstStepContent({super.key});
@override
State<FirstStepContent> createState() => _FirstStepContentState();
}
class _FirstStepContentState extends State<FirstStepContent> {
final maskFormatterForBirthday = MaskTextInputFormatter(mask: '##.##.####', filter: {"#": RegExp(r'[0-9]')}, type: MaskAutoCompletionType.lazy);
final firstNameController = TextEditingController();
final lastNameController = TextEditingController();
final emailController = TextEditingController();
final phoneController = TextEditingController();
final dateController = TextEditingController();
@override
void initState() {
super.initState();
firstNameController.text = context.read<RegistrationCubitV2>().state.firstName;
lastNameController.text = context.read<RegistrationCubitV2>().state.lastName;
emailController.text = context.read<RegistrationCubitV2>().state.email;
phoneController.text = context.read<RegistrationCubitV2>().state.phone;
dateController.text = context.read<RegistrationCubitV2>().state.birthday?.format('dd.MM.yyyy') ?? '';
}
@override
Widget build(BuildContext context) {
final s = S.of(context);
return BlocBuilder<RegistrationCubitV2, RegistrationStateV2>(
builder: (context, state) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(s.first_name, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
const SizedBox(height: 4),
CustomTextFormField(
hintText: s.your_name,
hasError: !state.isFirstNameValid,
controller: firstNameController,
onChanged: (val) {
context.read<RegistrationCubitV2>().setFirstName(val);
},
),
if (!state.isFirstNameValid) ...[const SizedBox(height: 4), ValidationErrorWidget(error: s.first_name_validation_error)],
],
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(s.last_name, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
const SizedBox(height: 4),
CustomTextFormField(
hintText: s.your_last_name,
hasError: !state.isLastNameValid,
controller: lastNameController,
onChanged: (val) {
context.read<RegistrationCubitV2>().setLastName(val);
},
),
if (!state.isLastNameValid) ...[const SizedBox(height: 4), ValidationErrorWidget(error: s.last_name_error)],
],
),
),
],
),
const SizedBox(height: 12),
Text(s.email, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
CustomTextFormField(
hintText: s.type_email,
hasError: !state.isEmailValid,
keyboardType: TextInputType.emailAddress,
controller: emailController,
onChanged: (val) {
context.read<RegistrationCubitV2>().setEmail(val);
},
),
if (!state.isEmailValid) ...[
const SizedBox(height: 4),
ValidationErrorWidget(error: state.emailDisplayError.isEmpty ? s.email_error : state.emailDisplayError),
],
const SizedBox(height: 12),
Text(s.phone, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
Column(
children: [
CustomTextFormField(
hintText: '+7 (999) 999 99 99',
controller: phoneController,
hasError: !state.isPhoneValid,
keyboardType: TextInputType.phone,
onChanged: (val) {
context.read<RegistrationCubitV2>().setPhone(val);
},
),
if (!state.isPhoneValid) ...[
const SizedBox(height: 4),
ValidationErrorWidget(error: state.phoneDisplayError.isEmpty ? s.phone_error : state.phoneDisplayError),
],
],
),
const SizedBox(height: 12),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(s.date_of_birth, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
const SizedBox(height: 4),
CustomTextFormField(
textInputAction: TextInputAction.done,
hintText: '01.01.2000',
keyboardType: TextInputType.number,
inputFormatters: [maskFormatterForBirthday],
hasError: !state.isBirthdayValid,
controller: dateController,
onChanged: (val) {
final dateArray = val.split('.');
if (dateArray.length != 3) return;
final date = DateTime(int.parse(dateArray[2]), int.parse(dateArray[1]), int.parse(dateArray[0]));
context.read<RegistrationCubitV2>().setBirthday(date);
},
),
if (!state.isBirthdayValid) ...[
const SizedBox(height: 4),
ValidationErrorWidget(error: state.dateOfBirthDisplayError.isEmpty ? s.date_of_birth_error : state.dateOfBirthDisplayError),
],
],
),
),
const SizedBox(width: 12),
const Expanded(child: SexWidget()),
],
),
const Spacer(),
CustomOutlinedButton(
onPressed: () {
context.read<RegistrationCubitV2>().nextStep();
},
text: s.next,
height: 52,
buttonTextStyle: TextStyles.textSSemibold.copyWith(color: appTheme.whiteA700),
buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.blueA400)),
),
const SizedBox(height: 20),
],
);
},
);
}
}

View File

@@ -1,175 +1,181 @@
import 'package:eatvio_app/core/app_config.dart';
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
import 'package:eatvio_app/features/registration_v2/cubit/registration_cubit.dart';
import 'package:eatvio_app/features/registration_v2/cubit/registration_state.dart';
import 'package:eatvio_app/features/registration_v2/presentation/widgets/phisical_activity.dart';
import 'package:eatvio_app/features/registration_v2/presentation/widgets/show_successful_registration_bottom_sheet.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:url_launcher/url_launcher.dart';
class SecondStepContent extends StatefulWidget {
const SecondStepContent({super.key});
@override
State<SecondStepContent> createState() => _SecondStepContentState();
}
class _SecondStepContentState extends State<SecondStepContent> {
bool isLoading = false;
final weightController = TextEditingController();
final heightController = TextEditingController();
@override
void initState() {
super.initState();
heightController.text = context.read<RegistrationCubitV2>().state.height.toString();
weightController.text = context.read<RegistrationCubitV2>().state.weight.toString();
}
@override
Widget build(BuildContext context) {
final s = S.of(context);
return BlocBuilder<RegistrationCubitV2, RegistrationStateV2>(
builder: (context, state) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(s.last_step, style: TextStyles.textFS24FW700.copyWith(color: appTheme.black)),
const SizedBox(height: 12),
Text(s.user_data_desc, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(s.height, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
const SizedBox(height: 4),
CustomTextFormField(
hintText: s.type_height,
hasError: !state.isHeightValid,
controller: heightController,
textInputType: const TextInputType.numberWithOptions(),
onChanged: (val) {
context.read<RegistrationCubitV2>().setHeight(int.parse(val));
},
),
if (!state.isHeightValid) ValidationErrorWidget(error: s.height_error),
],
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(s.weight, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
const SizedBox(height: 4),
CustomTextFormField(
hintText: s.type_weight,
hasError: !state.isWeightValid,
controller: weightController,
textInputType: const TextInputType.numberWithOptions(),
onChanged: (val) {
context.read<RegistrationCubitV2>().setWeight(int.parse(val));
},
),
if (!state.isWeightValid) ValidationErrorWidget(error: s.weight_error),
],
),
),
],
),
const SizedBox(height: 12),
Text(s.phisical_activity, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
const PhisicalActivity(),
const SizedBox(height: 12),
const Spacer(),
Row(
children: [
InkWell(
onTap: () {
context.read<RegistrationCubitV2>().prevStep();
},
borderRadius: BorderRadius.circular(20),
child: Ink(
height: 52,
width: 52,
decoration: BoxDecoration(color: appTheme.gray50, borderRadius: BorderRadius.circular(20)),
child: Center(child: Icon(Icons.arrow_back_ios_new_rounded, color: appTheme.blueA400, size: 24)),
),
),
const SizedBox(width: 4),
isLoading
? const Expanded(child: Center(child: Loader()))
: Expanded(
child: CustomOutlinedButton(
onPressed: () async {
setState(() {
isLoading = true;
});
final cubit = context.read<RegistrationCubitV2>();
final (commonResult, hasValidationError) = await cubit.register();
setState(() {
isLoading = false;
});
if (context.mounted && !hasValidationError) {
showRegistrationBottomSheet(context, cubit, commonResult);
}
},
text: s.register,
height: 52,
buttonTextStyle: TextStyles.textSSemibold.copyWith(color: appTheme.whiteA700),
buttonStyle: ButtonStyle(
side: const WidgetStatePropertyAll(BorderSide.none),
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
),
),
),
],
),
const SizedBox(height: 12),
Center(
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
text: 'Нажимая “Зарегистрироваться”, Вы соглашаетесь \n',
style: const TextStyle(fontSize: 12, color: Colors.black),
children: [
const TextSpan(text: 'c '),
if (!hidePlans) ...[
TextSpan(
text: ' Публичной офертой',
style: TextStyle(fontSize: 12, color: appTheme.blueA400, decorationColor: appTheme.blueA400, decoration: TextDecoration.underline),
recognizer: TapGestureRecognizer()..onTap = () async => await launchUrl(Uri.parse('${sl<AppConfig>().baseUrl}/oferta_bs.pdf')),
),
const TextSpan(text: ' и '),
],
TextSpan(
text: 'Политикой конфиденциальности\nи защитой персональных данных',
style: TextStyle(fontSize: 12, color: appTheme.blueA400, decorationColor: appTheme.blueA400, decoration: TextDecoration.underline),
recognizer: TapGestureRecognizer()..onTap = () async => await launchUrl(Uri.parse('${sl<AppConfig>().baseUrl}/docs/privacy.pdf')),
),
],
),
),
),
const SizedBox(height: 30),
],
);
},
);
}
}
import 'package:eatvio_app/core/app_config.dart';
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
import 'package:eatvio_app/features/registration_v2/cubit/registration_cubit.dart';
import 'package:eatvio_app/features/registration_v2/cubit/registration_state.dart';
import 'package:eatvio_app/features/registration_v2/presentation/widgets/phisical_activity.dart';
import 'package:eatvio_app/features/registration_v2/presentation/widgets/show_successful_registration_bottom_sheet.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:url_launcher/url_launcher.dart';
class SecondStepContent extends StatefulWidget {
const SecondStepContent({super.key});
@override
State<SecondStepContent> createState() => _SecondStepContentState();
}
class _SecondStepContentState extends State<SecondStepContent> {
bool isLoading = false;
final weightController = TextEditingController();
final heightController = TextEditingController();
@override
void initState() {
super.initState();
heightController.text = context.read<RegistrationCubitV2>().state.height.toString() == '0'
? ''
: context.read<RegistrationCubitV2>().state.height.toString();
weightController.text = context.read<RegistrationCubitV2>().state.weight.toString() == '0'
? ''
: context.read<RegistrationCubitV2>().state.weight.toString();
}
@override
Widget build(BuildContext context) {
final s = S.of(context);
return BlocBuilder<RegistrationCubitV2, RegistrationStateV2>(
builder: (context, state) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(s.last_step, style: TextStyles.textFS24FW700.copyWith(color: appTheme.black)),
const SizedBox(height: 12),
Text(s.user_data_desc, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(s.height, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
const SizedBox(height: 4),
CustomTextFormField(
hintText: s.type_height,
hasError: !state.isHeightValid,
controller: heightController,
keyboardType: TextInputType.number,
textInputType: const TextInputType.numberWithOptions(),
onChanged: (val) {
context.read<RegistrationCubitV2>().setHeight(int.parse(val));
},
),
if (!state.isHeightValid) ValidationErrorWidget(error: s.height_error),
],
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(s.weight, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
const SizedBox(height: 4),
CustomTextFormField(
hintText: s.type_weight,
hasError: !state.isWeightValid,
controller: weightController,
keyboardType: TextInputType.number,
textInputType: const TextInputType.numberWithOptions(),
onChanged: (val) {
context.read<RegistrationCubitV2>().setWeight(int.parse(val));
},
),
if (!state.isWeightValid) ValidationErrorWidget(error: s.weight_error),
],
),
),
],
),
const SizedBox(height: 12),
Text(s.phisical_activity, style: TextStyles.textSMedium.copyWith(color: appTheme.black)),
const PhisicalActivity(),
const SizedBox(height: 12),
const Spacer(),
Row(
children: [
InkWell(
onTap: () {
context.read<RegistrationCubitV2>().prevStep();
},
borderRadius: BorderRadius.circular(20),
child: Ink(
height: 52,
width: 52,
decoration: BoxDecoration(color: appTheme.gray50, borderRadius: BorderRadius.circular(20)),
child: Center(child: Icon(Icons.arrow_back_ios_new_rounded, color: appTheme.blueA400, size: 24)),
),
),
const SizedBox(width: 4),
isLoading
? const Expanded(child: Center(child: Loader()))
: Expanded(
child: CustomOutlinedButton(
onPressed: () async {
setState(() {
isLoading = true;
});
final cubit = context.read<RegistrationCubitV2>();
final (commonResult, hasValidationError) = await cubit.register();
setState(() {
isLoading = false;
});
if (context.mounted && !hasValidationError) {
showRegistrationBottomSheet(context, cubit, commonResult);
}
},
text: s.register,
height: 52,
buttonTextStyle: TextStyles.textSSemibold.copyWith(color: appTheme.whiteA700),
buttonStyle: ButtonStyle(
side: const WidgetStatePropertyAll(BorderSide.none),
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
),
),
),
],
),
const SizedBox(height: 12),
Center(
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
text: 'Нажимая “Зарегистрироваться”, Вы соглашаетесь \n',
style: const TextStyle(fontSize: 12, color: Colors.black),
children: [
const TextSpan(text: 'c '),
if (!hidePlans) ...[
TextSpan(
text: ' Публичной офертой',
style: TextStyle(fontSize: 12, color: appTheme.blueA400, decorationColor: appTheme.blueA400, decoration: TextDecoration.underline),
recognizer: TapGestureRecognizer()..onTap = () async => await launchUrl(Uri.parse('${sl<AppConfig>().baseUrl}/oferta_bs.pdf')),
),
const TextSpan(text: ' и '),
],
TextSpan(
text: 'Политикой конфиденциальности\nи защитой персональных данных',
style: TextStyle(fontSize: 12, color: appTheme.blueA400, decorationColor: appTheme.blueA400, decoration: TextDecoration.underline),
recognizer: TapGestureRecognizer()..onTap = () async => await launchUrl(Uri.parse('${sl<AppConfig>().baseUrl}/docs/privacy.pdf')),
),
],
),
),
),
const SizedBox(height: 30),
],
);
},
);
}
}