forked from eatvio/eatvio_app
441 lines
15 KiB
Dart
441 lines
15 KiB
Dart
import 'package:eatvio_app/core/utils/image_constant.dart';
|
|
import 'package:eatvio_app/core/widgets/loader.dart';
|
|
import 'package:eatvio_app/features/registration/presentation/bloc/registration_cubit/registration_cubit.dart';
|
|
import 'package:eatvio_app/features/registration/presentation/bloc/registration_cubit/registration_state.dart';
|
|
import 'package:eatvio_app/features/registration/presentation/pages/additional_information_screen.dart';
|
|
import 'package:eatvio_app/features/registration/presentation/pages/allergies_screen.dart';
|
|
import 'package:eatvio_app/features/registration/presentation/pages/blood_pressure_screen.dart';
|
|
import 'package:eatvio_app/features/registration/presentation/pages/body_circumferences_screen.dart';
|
|
import 'package:eatvio_app/features/registration/presentation/pages/city_of_living_screen.dart';
|
|
import 'package:eatvio_app/features/registration/presentation/pages/credentials_screen.dart';
|
|
import 'package:eatvio_app/features/registration/presentation/pages/date_of_birth_screen.dart';
|
|
import 'package:eatvio_app/features/registration/presentation/pages/favorite_foods_screen.dart';
|
|
import 'package:eatvio_app/features/registration/presentation/pages/favorite_sports_screen.dart';
|
|
import 'package:eatvio_app/features/registration/presentation/pages/gender_screen.dart';
|
|
import 'package:eatvio_app/features/registration/presentation/pages/height_screen.dart';
|
|
import 'package:eatvio_app/features/registration/presentation/pages/medicine_intake_screen.dart';
|
|
import 'package:eatvio_app/features/registration/presentation/pages/name_screen.dart';
|
|
import 'package:eatvio_app/features/registration/presentation/pages/phone_screen.dart';
|
|
import 'package:eatvio_app/features/registration/presentation/pages/weight_screen.dart';
|
|
import 'package:eatvio_app/generated/l10n.dart';
|
|
import 'package:eatvio_app/core/locator_service.dart';
|
|
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:flutter_svg/flutter_svg.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:validators/validators.dart';
|
|
|
|
class RegistrationScreen extends StatefulWidget {
|
|
const RegistrationScreen({super.key});
|
|
|
|
@override
|
|
State<RegistrationScreen> createState() => _RegistrationScreenState();
|
|
}
|
|
|
|
class _RegistrationScreenState extends State<RegistrationScreen> {
|
|
double progressValue = 1 / 15;
|
|
int step = 1;
|
|
final pageController = PageController();
|
|
final RegistrationCubit registrationCubit = sl<RegistrationCubit>();
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return BlocProvider<RegistrationCubit>(
|
|
create: (BuildContext context) => registrationCubit,
|
|
child: BlocBuilder<RegistrationCubit, RegistrationState>(
|
|
builder: (BuildContext context, RegistrationState state) {
|
|
return Scaffold(
|
|
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
|
|
floatingActionButton: Row(
|
|
children: [
|
|
Container(
|
|
height: 48,
|
|
width: MediaQuery.of(context).size.width * 0.2,
|
|
padding: EdgeInsets.only(left: 16, right: 4),
|
|
child: FloatingActionButton.extended(
|
|
heroTag: 'backButtonRegistration',
|
|
highlightElevation: 0,
|
|
focusElevation: 0,
|
|
elevation: 0,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
backgroundColor: Color(0xFFEAF2FD),
|
|
onPressed: () async {
|
|
setState(() {
|
|
pageController.previousPage(
|
|
duration: const Duration(milliseconds: 350),
|
|
curve: Curves.easeInOut,
|
|
);
|
|
});
|
|
},
|
|
label: Icon(
|
|
Icons.arrow_back_ios_new_rounded,
|
|
color: appTheme.blueA400,
|
|
),
|
|
),
|
|
),
|
|
Container(
|
|
height: 48,
|
|
width: MediaQuery.of(context).size.width * 0.8,
|
|
padding: EdgeInsets.only(left: 4, right: 16),
|
|
child: FloatingActionButton.extended(
|
|
heroTag: 'nextButtonRegistration',
|
|
highlightElevation: 0,
|
|
elevation: 0,
|
|
focusElevation: 0,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
backgroundColor: appTheme.blueA400,
|
|
onPressed: () async {
|
|
if (await canGoNext(state)) {
|
|
setState(() {
|
|
pageController.nextPage(
|
|
duration: const Duration(milliseconds: 350),
|
|
curve: Curves.easeInOut,
|
|
);
|
|
});
|
|
}
|
|
},
|
|
label: Text(
|
|
'Продолжить',
|
|
style: theme.textTheme.titleSmall!.copyWith(color: Colors.white),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
appBar: AppBar(
|
|
automaticallyImplyLeading: false,
|
|
leading: Builder(
|
|
builder: (context) => IconButton(
|
|
icon: Icon(Icons.close_rounded),
|
|
onPressed: () => GoRouter.of(context).pop(),
|
|
),
|
|
),
|
|
centerTitle: true,
|
|
title: SvgPicture.asset(
|
|
ImageConstant.imgLogoEatvio,
|
|
height: 48,
|
|
fit: BoxFit.contain,
|
|
),
|
|
actions: [
|
|
Padding(
|
|
padding: const EdgeInsets.only(right: 16),
|
|
child: Text('шаг $step из 15'),
|
|
),
|
|
],
|
|
bottom: PreferredSize(
|
|
preferredSize: Size(MediaQuery.of(context).size.width, 32),
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16),
|
|
child: TweenAnimationBuilder<double>(
|
|
duration: const Duration(milliseconds: 350),
|
|
curve: Curves.easeInOut,
|
|
tween: Tween<double>(
|
|
begin: 1 / 15,
|
|
end: progressValue,
|
|
),
|
|
builder: (context, value, _) => LinearProgressIndicator(
|
|
value: value,
|
|
borderRadius: BorderRadius.circular(16),
|
|
minHeight: 8,
|
|
backgroundColor: Colors.grey.shade100,
|
|
valueColor: AlwaysStoppedAnimation<Color>(appTheme.blueA400),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
body: PageView(
|
|
physics: NeverScrollableScrollPhysics(),
|
|
controller: pageController,
|
|
onPageChanged: (index) {
|
|
setState(() {
|
|
step = index + 1;
|
|
progressValue = step / 15;
|
|
});
|
|
},
|
|
children: [
|
|
// LanguageSelectScreen(sheet: state.sheet),
|
|
PhoneScreen(sheet: state.sheet),
|
|
CredentialsScreen(sheet: state.sheet),
|
|
NameScreen(sheet: state.sheet),
|
|
GenderScreen(sheet: state.sheet),
|
|
DateOfBirthScreen(sheet: state.sheet),
|
|
CityOfLivingScreen(sheet: state.sheet),
|
|
BodyCircumferencesScreen(sheet: state.sheet),
|
|
WeightScreen(sheet: state.sheet),
|
|
HeightScreen(sheet: state.sheet),
|
|
BloodPressureScreen(sheet: state.sheet),
|
|
AllergiesScreen(sheet: state.sheet),
|
|
MedicineIntakeScreen(sheet: state.sheet),
|
|
FavoriteSportsScreen(sheet: state.sheet),
|
|
FavoriteFoodsScreen(sheet: state.sheet),
|
|
AdditionalInformationScreen(sheet: state.sheet),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<bool> canGoNext(RegistrationState state) async {
|
|
switch (step) {
|
|
case 1:
|
|
if (state.sheet.phone.length != 18) {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: Text('Ошибка'),
|
|
content: Text('Введите корректный номер телефона'),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context),
|
|
child: Text('OK'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
case 2:
|
|
List<String> errors = validateCredentialsFields(
|
|
state.sheet.email,
|
|
state.sheet.password,
|
|
state.sheet.passwordConfirmation,
|
|
context,
|
|
);
|
|
if (errors.isEmpty) {
|
|
return true;
|
|
} else {
|
|
String errorMessage = '';
|
|
|
|
for (var error in errors) {
|
|
errorMessage += error + '\n\n';
|
|
}
|
|
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: Text('Ошибка'),
|
|
content: Text(errorMessage),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context),
|
|
child: Text('OK'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
return false;
|
|
}
|
|
case 3:
|
|
List<String> errors = validateNameFields(
|
|
state.sheet.firstName.toString(),
|
|
state.sheet.lastName.toString(),
|
|
context,
|
|
);
|
|
if (errors.isEmpty) {
|
|
return true;
|
|
} else {
|
|
String errorMessage = '';
|
|
|
|
for (var error in errors) {
|
|
errorMessage += error + '\n\n';
|
|
}
|
|
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: Text('Ошибка'),
|
|
content: Text(errorMessage),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context),
|
|
child: Text('OK'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
return false;
|
|
}
|
|
case 4:
|
|
if (state.sheet.gender.isNotEmpty)
|
|
return true;
|
|
else {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: Text('Ошибка'),
|
|
content: Text('Выберете пол'),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context),
|
|
child: Text('OK'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
return false;
|
|
}
|
|
case 6:
|
|
if (state.sheet.location.isNotEmpty)
|
|
return true;
|
|
else {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: Text('Ошибка'),
|
|
content: Text('Заполните все поля'),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context),
|
|
child: Text('OK'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
return false;
|
|
}
|
|
case 7:
|
|
List<String> errors = validateCircumferencesFields(
|
|
state.sheet.chestCircumference.toString(),
|
|
state.sheet.waistCircumference.toString(),
|
|
state.sheet.hipCircumference.toString(),
|
|
context,
|
|
);
|
|
if (errors.isEmpty) {
|
|
return true;
|
|
} else {
|
|
String errorMessage = '';
|
|
|
|
for (var error in errors) {
|
|
errorMessage += error + '\n\n';
|
|
}
|
|
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: Text('Ошибка'),
|
|
content: Text(errorMessage),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context),
|
|
child: Text('OK'),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
return false;
|
|
}
|
|
case 15:
|
|
try {
|
|
showDialog(
|
|
barrierDismissible: false,
|
|
useSafeArea: false,
|
|
context: context,
|
|
builder: (context) => PopScope(
|
|
canPop: false,
|
|
child: const Loader(),
|
|
),
|
|
);
|
|
await registrationCubit.tryRegistration();
|
|
Navigator.pop(context);
|
|
} catch (e) {
|
|
Navigator.pop(context);
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
title: Text('Ошибка при попытке регистрации'),
|
|
),
|
|
);
|
|
}
|
|
return false;
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
List<String> validateCircumferencesFields(
|
|
String chestCircumference,
|
|
String waistCircumference,
|
|
String hipCircumference,
|
|
BuildContext context,
|
|
) {
|
|
final s = S.of(context);
|
|
|
|
List<String> errors = [];
|
|
|
|
if (chestCircumference.isEmpty | waistCircumference.isEmpty | hipCircumference.isEmpty) {
|
|
errors.add(s.circumference_empty);
|
|
}
|
|
|
|
if (![chestCircumference, waistCircumference, hipCircumference].every((circumference) => int.tryParse(circumference) != null)) {
|
|
errors.add(s.circumference_is_not_a_number);
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
List<String> validateCredentialsFields(
|
|
String email,
|
|
String password,
|
|
String passwordConfirmation,
|
|
BuildContext context,
|
|
) {
|
|
final s = S.of(context);
|
|
|
|
List<String> errors = [];
|
|
|
|
if (!isEmail(email)) {
|
|
errors.add(s.email_invalid);
|
|
}
|
|
|
|
if (email.isEmpty) {
|
|
errors.add(s.email_empty);
|
|
}
|
|
|
|
if (password.length < 6) {
|
|
errors.add(s.password_short);
|
|
}
|
|
|
|
if (password != passwordConfirmation) {
|
|
errors.add(s.password_confirmation_mismatch);
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
List<String> validateNameFields(
|
|
String firstName,
|
|
String lastName,
|
|
BuildContext context,
|
|
) {
|
|
List<String> errors = [];
|
|
|
|
if (firstName.isEmpty) {
|
|
errors.add('First name cannot be empty');
|
|
}
|
|
|
|
if (firstName.length < 1) {
|
|
errors.add('First name is too short');
|
|
}
|
|
|
|
if (lastName.isEmpty) {
|
|
errors.add('Last name cannot be empty');
|
|
}
|
|
|
|
if (lastName.length < 1) {
|
|
errors.add('Last name is too short');
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
}
|