forked from eatvio/eatvio_app
Merge remote-tracking branch 'upstream/main'
Fix # Please enter a commit message to explain why this merge is necessary,
This commit is contained in:
2
.env
Normal file
2
.env
Normal file
@@ -0,0 +1,2 @@
|
||||
SERVER_ROOT='https://ohngeeri3ien-dev3.eatvio.ru/api'
|
||||
STORAGE_URL='https://ohngeeri3ien-dev3.eatvio.ru/storage/'
|
||||
@@ -1,5 +1,3 @@
|
||||
//ignore: unused_import
|
||||
import 'dart:convert';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class PrefUtils {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'dart:ui' as ui;
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
// This functions are responsible to make UI responsive across all the mobile devices.
|
||||
|
||||
MediaQueryData mediaQueryData = MediaQueryData.fromWindow(ui.window);
|
||||
MediaQueryData mediaQueryData = MediaQueryData.fromView(
|
||||
WidgetsBinding.instance.platformDispatcher.views.single);
|
||||
|
||||
// These are the Viewport values of your Figma Design.
|
||||
// These are used in the code as a reference to create your UI Responsively.
|
||||
|
||||
@@ -64,94 +64,106 @@ class AuthScreen extends StatelessWidget {
|
||||
margin:
|
||||
EdgeInsets.only(left: 16.h, right: 16.h, bottom: 50.v),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(
|
||||
context, AppRoutes.languageSelectScreen);
|
||||
}),
|
||||
onPressed: onTapNoAccount(context)),
|
||||
NextStepButton(
|
||||
text: "Тест дневника".toUpperCase(),
|
||||
margin:
|
||||
EdgeInsets.only(left: 16.h, right: 16.h, bottom: 50.v),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(context, AppRoutes.diaryEdit);
|
||||
}),
|
||||
onPressed: onTapDiaryTest(context)),
|
||||
BlocBuilder<AuthBloc, AuthState>(builder: (context, state) {
|
||||
return NextStepButton(
|
||||
text: "authentificate".tr.toUpperCase(),
|
||||
margin:
|
||||
EdgeInsets.only(left: 16.h, right: 16.h, bottom: 50.v),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: () async {
|
||||
List<String> errors = validateFields(
|
||||
state.emailController!.text,
|
||||
state.passwordController!.text);
|
||||
if (errors.isEmpty) {
|
||||
final credentials = {
|
||||
'email': state.emailController!.text,
|
||||
'password': state.passwordController!.text,
|
||||
};
|
||||
final response = await http.post(
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! + '/login'),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: json.encode(credentials),
|
||||
encoding: utf8);
|
||||
if (response.statusCode == 200) {
|
||||
var res = await jsonDecode(response.body);
|
||||
final box = await Hive.openBox('token');
|
||||
final token = res['token'];
|
||||
box.put('token', token);
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.myProfile,
|
||||
);
|
||||
} else {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text('Ошибка'),
|
||||
content: Text('wrong_credentials'.tr),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text('OK'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
} 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'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
onPressed: onTapAuth(state, context));
|
||||
}),
|
||||
])));
|
||||
}
|
||||
|
||||
onTapAuth(AuthState state, BuildContext context) {
|
||||
return () async {
|
||||
List<String> errors = validateFields(
|
||||
state.emailController!.text, state.passwordController!.text);
|
||||
if (errors.isEmpty) {
|
||||
final Map<String, dynamic> credentials = {
|
||||
'email': state.emailController!.text,
|
||||
'password': state.passwordController!.text,
|
||||
};
|
||||
final response = await http.post(
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! + '/login'),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: json.encode(credentials),
|
||||
encoding: utf8);
|
||||
if (response.statusCode == 200) {
|
||||
var res = await jsonDecode(response.body);
|
||||
final box = await Hive.openBox('token');
|
||||
final token = res['token'];
|
||||
box.put('token', token);
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.myProfile,
|
||||
);
|
||||
} else {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text('Ошибка'),
|
||||
content: Text('wrong_credentials'.tr),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: onTapOk(context),
|
||||
child: Text('OK'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
} 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: onTapOk(context),
|
||||
child: Text('OK'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
onTapOk(BuildContext context) {
|
||||
return () {
|
||||
Navigator.pop(context);
|
||||
};
|
||||
}
|
||||
|
||||
onTapDiaryTest(BuildContext context) {
|
||||
return () {
|
||||
Navigator.pushNamed(context, AppRoutes.diaryEdit);
|
||||
};
|
||||
}
|
||||
|
||||
onTapNoAccount(BuildContext context) {
|
||||
return () {
|
||||
Navigator.pushNamed(context, AppRoutes.languageSelectScreen);
|
||||
};
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context) {
|
||||
_buildAppBar(BuildContext context) {
|
||||
return CustomAppBar(
|
||||
leadingWidth: 29.h,
|
||||
centerTitle: true,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import '/core/app_export.dart';
|
||||
@@ -40,10 +40,11 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
) async {
|
||||
try {
|
||||
final token =
|
||||
"laravel_sanctum_Q22zOtJhQUcJnh0kseZFblFv5XAPaKbG3gTM5hga71ecbb49";
|
||||
"laravel_sanctum_1lPFtd9tpGmHYS243C4K7YF8BrVhxxgXm8cTmjuZda1da694";
|
||||
final response = await http.get(
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! + '/diary/index'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
@@ -56,12 +57,12 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
final response = await http.get(
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! + '/diary/show/$diaryId'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
final diary = jsonDecode(response.body)['diary'];
|
||||
if (diary is Map<String, dynamic>) {
|
||||
print(diary['meals'][1].runtimeType);
|
||||
final String diaryDate =
|
||||
diary['date'] ?? DateTime.now().toString();
|
||||
// Продолжайте обработку данных дневника
|
||||
@@ -91,7 +92,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
// Если текущее состояние - начальное, создаем новое загруженное состояние
|
||||
emit(DiaryEditLoadedState(
|
||||
currentMeal: SelectionPopupModel(
|
||||
id: 0, title: "Прием пищи 1", isSelected: true),
|
||||
id: 1, title: "Прием пищи 1", isSelected: true),
|
||||
wakeupExpanded: true,
|
||||
moodExpanded: true,
|
||||
token: token,
|
||||
@@ -145,8 +146,6 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
var updatedState =
|
||||
currentState.copyWith(selectedSatiety: event.selectedElement);
|
||||
|
||||
emit(updatedState); // Передаем обновленное состояние в emit
|
||||
|
||||
// Отправляем данные по HTTP маршруту и ждем ответ
|
||||
var response = await sendSatietyData(
|
||||
currentState.token,
|
||||
@@ -155,8 +154,8 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
currentState.diaryId);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
currentState.selectedSatiety = event.selectedElement;
|
||||
emit(currentState); // Передаем обновленное состояние в emit
|
||||
updatedState.selectedSatiety = event.selectedElement;
|
||||
emit(updatedState); // Передаем обновленное состояние в emit
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,21 +166,18 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
int? mealID,
|
||||
int diaryID,
|
||||
) async {
|
||||
String url =
|
||||
dotenv.env['SERVER_ROOT']! + '$diaryID/meal/$mealID/satiety/fill';
|
||||
final credentials = {
|
||||
String url = dotenv.env['SERVER_ROOT']! +
|
||||
'/diary/$diaryID/meal/$mealID/satiety/fill';
|
||||
final Map<String, dynamic> credentials = {
|
||||
'satiety': selectedSatiety,
|
||||
'meal_id': mealID,
|
||||
'diary_id': diaryID,
|
||||
};
|
||||
|
||||
var response = await http.post(Uri.parse(url),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: json.encode(credentials),
|
||||
encoding: utf8);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -213,15 +209,14 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
int diaryID,
|
||||
) async {
|
||||
String url =
|
||||
dotenv.env['SERVER_ROOT']! + '/$diaryID/meal/$mealID/mood/fill';
|
||||
final credentials = {
|
||||
dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/meal/$mealID/mood/fill';
|
||||
final Map<String, dynamic> credentials = {
|
||||
'mood': selectedMood,
|
||||
'meal_id': mealID,
|
||||
'diary_id': diaryID,
|
||||
};
|
||||
|
||||
var response = await http.post(Uri.parse(url),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: json.encode(credentials),
|
||||
@@ -234,7 +229,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
WakeupEvent event, Emitter<DiaryEditState> emit) async {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
var updatedState =
|
||||
var updatedState = //инициализируем новое состояние
|
||||
currentState.copyWith(wakeupController: event.selectedTime);
|
||||
|
||||
// Отправляем данные по HTTP маршруту и ждем ответ
|
||||
@@ -242,6 +237,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
currentState.token, event.selectedTime, currentState.diaryId);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
//Чекаем статус ответа
|
||||
updatedState.wakeupController = event.selectedTime;
|
||||
emit(updatedState); // Передаем обновленное состояние в emit
|
||||
}
|
||||
@@ -253,19 +249,23 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
TimeOfDay wakeup,
|
||||
int diaryID,
|
||||
) async {
|
||||
String url = dotenv.env['SERVER_ROOT']! + '/diary/???';
|
||||
final credentials = {
|
||||
'wakeup': wakeup,
|
||||
'diary_id': diaryID,
|
||||
String url = dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/wakeUp/fill';
|
||||
final formatter = DateFormat('HH:mm');
|
||||
final wakeupString =
|
||||
formatter.format(DateTime(2022, 1, 1, wakeup.hour, wakeup.minute));
|
||||
final Map<String, dynamic> credentials = {
|
||||
'wakeup': wakeupString,
|
||||
};
|
||||
|
||||
var response = await http.post(Uri.parse(url),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: json.encode(credentials),
|
||||
encoding: utf8);
|
||||
|
||||
var response = await http.post(
|
||||
Uri.parse(url),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: json.encode(credentials),
|
||||
encoding: utf8,
|
||||
);
|
||||
print(response.body);
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -290,12 +290,16 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
Future<http.Response> sendRetireData(
|
||||
String? token, TimeOfDay retire, int diaryID) async {
|
||||
String url = dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/retire/fill';
|
||||
final credentials = {
|
||||
'retire': retire,
|
||||
final formatter = DateFormat('HH:mm');
|
||||
final retireString =
|
||||
formatter.format(DateTime(2022, 1, 1, retire.hour, retire.minute));
|
||||
final Map<String, dynamic> credentials = {
|
||||
'retire': retireString,
|
||||
};
|
||||
|
||||
var response = await http.post(Uri.parse(url),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: json.encode(credentials),
|
||||
@@ -310,16 +314,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
var updatedState = currentState.copyWith(mealTimeController: event.time);
|
||||
|
||||
emit(updatedState); // Передаем обновленное состояние в emit
|
||||
|
||||
// Отправляем данные по HTTP маршруту и ждем ответ
|
||||
var response = await sendMealTimeData(currentState.token, event.time,
|
||||
currentState.currentMeal?.id, currentState.diaryId);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
currentState.retireController = event.time;
|
||||
emit(currentState); // Передаем обновленное состояние в emit
|
||||
}
|
||||
emit(updatedState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,7 +325,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
int diaryID,
|
||||
) async {
|
||||
String url = dotenv.env['SERVER_ROOT']! + '/diary/???';
|
||||
final credentials = {
|
||||
final Map<String, dynamic> credentials = {
|
||||
'time': mealTime,
|
||||
'meal_id': mealID,
|
||||
'diary_id': diaryID,
|
||||
@@ -338,6 +333,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
|
||||
var response = await http.post(Uri.parse(url),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: json.encode(credentials),
|
||||
@@ -385,56 +381,51 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
AddPortionEvent event, Emitter<DiaryEditState> emit) async {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
|
||||
int? id = currentState.currentMeal?.id;
|
||||
var updatedState = currentState.copyWith(
|
||||
currentMeal: currentState.diaryEditModelObj?.mealList[id ?? 0],
|
||||
portions: currentState.meals?[id ?? 0]['portions'],
|
||||
mealTimeController:
|
||||
toTime(currentState.meals?[id ?? 0]['time'] ?? "1$id:00"),
|
||||
selectedMood: currentState.meals?[id ?? 0]['mood'],
|
||||
selectedSatiety: currentState.meals?[id ?? 0]['satiety']);
|
||||
emit(updatedState); // Передаем обновленное состояние в emit
|
||||
|
||||
// Отправляем данные по HTTP маршруту и ждем ответ
|
||||
var response = await sendPortionData(
|
||||
currentState.token,
|
||||
event.name,
|
||||
event.portion,
|
||||
event.quantity,
|
||||
currentState.productNameController?.text,
|
||||
'pcs',
|
||||
int.parse(currentState.productQuantityController!.text),
|
||||
currentState.currentMeal!.id,
|
||||
currentState.diaryId);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
emit(currentState); // Передаем обновленное состояние в emit
|
||||
var updatedState = currentState.copyWith(
|
||||
portions: currentState.meals?[id ?? 0]['portions'],
|
||||
);
|
||||
emit(updatedState); // Передаем обновленное состояние в emit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<http.Response> sendPortionData(
|
||||
String? token,
|
||||
String name,
|
||||
String? name,
|
||||
String portion,
|
||||
int quantity,
|
||||
int? quantity,
|
||||
int? mealID,
|
||||
int diaryID,
|
||||
) async {
|
||||
String url =
|
||||
dotenv.env['SERVER_ROOT']! + '/$diaryID/meal/$mealID/portions/create';
|
||||
final credentials = {
|
||||
'name': name,
|
||||
String url = dotenv.env['SERVER_ROOT']! +
|
||||
'/diary/$diaryID/meal/$mealID/portions/create';
|
||||
final Map<String, dynamic> credentials = {
|
||||
'product_name': name,
|
||||
'portion': portion,
|
||||
'quantity': quantity,
|
||||
'meal_id': mealID,
|
||||
'diary_id': diaryID,
|
||||
};
|
||||
|
||||
print(credentials);
|
||||
var response = await http.post(Uri.parse(url),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: json.encode(credentials),
|
||||
encoding: utf8);
|
||||
|
||||
print(response.body);
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -443,7 +434,7 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
String? token = currentState.token;
|
||||
final credentials = {
|
||||
final Map<String, dynamic> credentials = {
|
||||
'name': event.input,
|
||||
};
|
||||
final uri = Uri.parse(dotenv.env['SERVER_ROOT']! +
|
||||
@@ -452,18 +443,22 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
final response = await http.get(
|
||||
uri,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
final productNutritionalValues =
|
||||
jsonDecode(response.body)['productNutritionalValue'];
|
||||
if (productNutritionalValues is Map<String, dynamic>) {
|
||||
jsonDecode(response.body)['product_nutritional_value'];
|
||||
if (productNutritionalValues is List<dynamic>) {
|
||||
var updatedState = currentState.copyWith(
|
||||
autocompleteList: productNutritionalValues.values
|
||||
.map((value) => value['name'])
|
||||
productNameController: TextEditingController(text: event.input),
|
||||
autocompleteList: productNutritionalValues
|
||||
.map((product) => product['name'])
|
||||
.toList());
|
||||
|
||||
emit(updatedState);
|
||||
} else {
|
||||
print("Ошибка в декодировании ответа из API");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -477,7 +472,42 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
print(imageUrl);
|
||||
}
|
||||
|
||||
FutureOr<void> _addComment(AddComment event, Emitter<DiaryEditState> emit) {}
|
||||
FutureOr<void> _addComment(
|
||||
AddComment event, Emitter<DiaryEditState> emit) async {
|
||||
if (state is DiaryEditLoadedState) {
|
||||
var currentState = state as DiaryEditLoadedState;
|
||||
var updatedState = currentState.copyWith(
|
||||
commentController: TextEditingController(text: event.comment),
|
||||
);
|
||||
|
||||
// Отправляем данные по HTTP маршруту и ждем ответ
|
||||
var response = await sendCommentData(
|
||||
currentState.token, event.comment, currentState.diaryId);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
emit(updatedState); // Передаем обновленное состояние в emit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<http.Response> sendCommentData(
|
||||
String? token, String comment, int diaryID) async {
|
||||
String url = dotenv.env['SERVER_ROOT']! + '/diary/$diaryID/retire/fill';
|
||||
final Map<String, dynamic> credentials = {
|
||||
'comment': comment,
|
||||
};
|
||||
|
||||
var response = await http.post(Uri.parse(url),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: json.encode(credentials),
|
||||
encoding: utf8);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<String> uploadImageToServer(String imagePath) async {
|
||||
var request = http.MultipartRequest(
|
||||
'POST',
|
||||
@@ -507,12 +537,12 @@ class DiaryEditBloc extends Bloc<DiaryEditEvent, DiaryEditState> {
|
||||
|
||||
List<SelectionPopupModel> fillMealList() {
|
||||
return [
|
||||
SelectionPopupModel(id: 0, title: "Прием пищи 1", isSelected: true),
|
||||
SelectionPopupModel(id: 1, title: "Прием пищи 2"),
|
||||
SelectionPopupModel(id: 2, title: "Прием пищи 3"),
|
||||
SelectionPopupModel(id: 3, title: "Прием пищи 4"),
|
||||
SelectionPopupModel(id: 4, title: "Прием пищи 5"),
|
||||
SelectionPopupModel(id: 5, title: "Прием пищи 6"),
|
||||
SelectionPopupModel(id: 1, title: "Прием пищи 1", isSelected: true),
|
||||
SelectionPopupModel(id: 2, title: "Прием пищи 2"),
|
||||
SelectionPopupModel(id: 3, title: "Прием пищи 3"),
|
||||
SelectionPopupModel(id: 4, title: "Прием пищи 4"),
|
||||
SelectionPopupModel(id: 5, title: "Прием пищи 5"),
|
||||
SelectionPopupModel(id: 6, title: "Прием пищи 6"),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,12 +79,9 @@ class ToggleWakeupExpandedEvent extends DiaryEditEvent {
|
||||
}
|
||||
|
||||
class AddPortionEvent extends DiaryEditEvent {
|
||||
AddPortionEvent(this.name, this.quantity, this.portion);
|
||||
final String name;
|
||||
final int quantity;
|
||||
final String portion;
|
||||
AddPortionEvent();
|
||||
@override
|
||||
List<Object> get props => [name, quantity, portion];
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class RemovePortionEvent extends DiaryEditEvent {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -51,8 +51,7 @@ class MyProfileScreen extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMyProfileScreen(
|
||||
BuildContext context, MyProfileLoadedState state) {
|
||||
_buildMyProfileScreen(BuildContext context, MyProfileLoadedState state) {
|
||||
mediaQueryData = MediaQuery.of(context);
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
@@ -84,7 +83,7 @@ class MyProfileScreen extends StatelessWidget {
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context, String username) {
|
||||
_buildAppBar(BuildContext context, String username) {
|
||||
return CustomAppBar(
|
||||
height: 54.v,
|
||||
leadingWidth: 28.h,
|
||||
@@ -100,31 +99,35 @@ class MyProfileScreen extends StatelessWidget {
|
||||
ExitButton(
|
||||
text: "exit".tr,
|
||||
margin: EdgeInsets.symmetric(horizontal: 16.h, vertical: 17.v),
|
||||
onTap: () async {
|
||||
final box = await Hive.openBox('token');
|
||||
final token = box.get('token');
|
||||
final response = await http.post(
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! + '/profile/logout'),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 204) {
|
||||
print(response.statusCode);
|
||||
box.delete('token'); // Удаляем токен из Hive
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.authScreen,
|
||||
);
|
||||
}
|
||||
},
|
||||
onTap: _onTapLogout(context),
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
||||
Function _onTapLogout(BuildContext context) {
|
||||
return () async {
|
||||
final box = await Hive.openBox('token');
|
||||
final token = box.get('token');
|
||||
final response = await http.post(
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! + '/profile/logout'),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 204) {
|
||||
print(response.statusCode);
|
||||
box.delete('token'); // Удаляем токен из Hive
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.authScreen,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
Widget _buildMyBlog(BuildContext context) {
|
||||
_buildMyBlog(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 7.h),
|
||||
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
|
||||
@@ -142,7 +145,7 @@ class MyProfileScreen extends StatelessWidget {
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
Widget _buildSettings(BuildContext context) {
|
||||
_buildSettings(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 7.h),
|
||||
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
|
||||
@@ -154,19 +157,23 @@ class MyProfileScreen extends StatelessWidget {
|
||||
Expanded(
|
||||
child: NextStepButton(
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(context, AppRoutes.mySettings);
|
||||
},
|
||||
onPressed: onTapSettings(context),
|
||||
text: "my_settings".tr.toUpperCase(),
|
||||
margin: EdgeInsets.only(left: 10.h))),
|
||||
]));
|
||||
}
|
||||
|
||||
Widget _buildBottomBar(BuildContext context) {
|
||||
onTapSettings(BuildContext context) {
|
||||
return () {
|
||||
Navigator.pushNamed(context, AppRoutes.mySettings);
|
||||
};
|
||||
}
|
||||
|
||||
_buildBottomBar(BuildContext context) {
|
||||
return CustomBottomBar(onChanged: (BottomBarEnum type) {});
|
||||
}
|
||||
|
||||
Widget _buildCarousel(BuildContext contex, MyProfileLoadedState state) {
|
||||
_buildCarousel(BuildContext contex, MyProfileLoadedState state) {
|
||||
return CarouselSlider(
|
||||
options: CarouselOptions(height: 340.0),
|
||||
items: [1, 2, 3].map((chartType) {
|
||||
@@ -175,9 +182,7 @@ class MyProfileScreen extends StatelessWidget {
|
||||
switch (chartType) {
|
||||
case 1: // График веса
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
_editWeight(context, state);
|
||||
},
|
||||
onTap: _editWeight(context, state),
|
||||
child: Column(children: [
|
||||
Text(
|
||||
"weight_chart".tr,
|
||||
@@ -220,9 +225,7 @@ class MyProfileScreen extends StatelessWidget {
|
||||
]);
|
||||
case 3: //График обхватов
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
_editCircumference(context, state);
|
||||
},
|
||||
onTap: _editCircumference(context, state),
|
||||
child: Column(children: [
|
||||
Text(
|
||||
"circumferences_chart".tr,
|
||||
@@ -244,7 +247,7 @@ class MyProfileScreen extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildChartLegend() {
|
||||
_buildChartLegend() {
|
||||
final colors = [Colors.pink, Colors.green, Colors.purple];
|
||||
final titles = ["chest".tr, "waist".tr, "hips".tr];
|
||||
final style = TextStyle(
|
||||
@@ -277,7 +280,7 @@ class MyProfileScreen extends StatelessWidget {
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
Widget _buildChart(
|
||||
_buildChart(
|
||||
BuildContext context, List<double> chartPoints, List<String>? dates) {
|
||||
// Определение осей графика
|
||||
List<FlSpot> spots = [];
|
||||
@@ -340,20 +343,7 @@ class MyProfileScreen extends StatelessWidget {
|
||||
touchTooltipData: LineTouchTooltipData(
|
||||
maxContentWidth: 100,
|
||||
tooltipBgColor: Colors.black,
|
||||
getTooltipItems: (touchedSpots) {
|
||||
return touchedSpots.map((LineBarSpot touchedSpot) {
|
||||
final textStyle = TextStyle(
|
||||
color: touchedSpot.bar.gradient?.colors[0] ??
|
||||
touchedSpot.bar.color,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14,
|
||||
);
|
||||
return LineTooltipItem(
|
||||
'${touchedSpot.x}, ${touchedSpot.y.toStringAsFixed(2)}',
|
||||
textStyle,
|
||||
);
|
||||
}).toList();
|
||||
},
|
||||
getTooltipItems: mapSpots(),
|
||||
),
|
||||
handleBuiltInTouches: true,
|
||||
getTouchLineStart: (data, index) => 0,
|
||||
@@ -410,9 +400,7 @@ class MyProfileScreen extends StatelessWidget {
|
||||
drawVerticalLine: true,
|
||||
horizontalInterval: 1.5,
|
||||
verticalInterval: 5,
|
||||
checkToShowHorizontalLine: (value) {
|
||||
return value.toInt() == 0;
|
||||
},
|
||||
checkToShowHorizontalLine: valuteToInt(),
|
||||
getDrawingHorizontalLine: (_) => FlLine(
|
||||
color: Colors.blue.withOpacity(1),
|
||||
dashArray: [8, 2],
|
||||
@@ -423,9 +411,7 @@ class MyProfileScreen extends StatelessWidget {
|
||||
dashArray: [8, 2],
|
||||
strokeWidth: 0.8,
|
||||
),
|
||||
checkToShowVerticalLine: (value) {
|
||||
return value.toInt() == 0;
|
||||
},
|
||||
checkToShowVerticalLine: valuteToInt(),
|
||||
),
|
||||
borderData: FlBorderData(show: false),
|
||||
),
|
||||
@@ -436,7 +422,29 @@ class MyProfileScreen extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCircumferenceChart(
|
||||
valuteToInt() {
|
||||
return (value) {
|
||||
return value.toInt() == 0;
|
||||
};
|
||||
}
|
||||
|
||||
mapSpots() {
|
||||
return (touchedSpots) {
|
||||
return touchedSpots.map((LineBarSpot touchedSpot) {
|
||||
final textStyle = TextStyle(
|
||||
color: touchedSpot.bar.gradient?.colors[0] ?? touchedSpot.bar.color,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14,
|
||||
);
|
||||
return LineTooltipItem(
|
||||
'${touchedSpot.x}, ${touchedSpot.y.toStringAsFixed(2)}',
|
||||
textStyle,
|
||||
);
|
||||
}).toList();
|
||||
};
|
||||
}
|
||||
|
||||
_buildCircumferenceChart(
|
||||
BuildContext context, chartPoints, List<String>? dates) {
|
||||
// Определение осей графика
|
||||
final chestCircumferences =
|
||||
@@ -526,20 +534,7 @@ class MyProfileScreen extends StatelessWidget {
|
||||
touchTooltipData: LineTouchTooltipData(
|
||||
maxContentWidth: 100,
|
||||
tooltipBgColor: Colors.black,
|
||||
getTooltipItems: (touchedSpots) {
|
||||
return touchedSpots.map((LineBarSpot touchedSpot) {
|
||||
final textStyle = TextStyle(
|
||||
color: touchedSpot.bar.gradient?.colors[0] ??
|
||||
touchedSpot.bar.color,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14,
|
||||
);
|
||||
return LineTooltipItem(
|
||||
'${touchedSpot.x}, ${touchedSpot.y.toStringAsFixed(2)}',
|
||||
textStyle,
|
||||
);
|
||||
}).toList();
|
||||
},
|
||||
getTooltipItems: mapTouchedSpots(),
|
||||
),
|
||||
handleBuiltInTouches: true,
|
||||
getTouchLineStart: (data, index) => 0,
|
||||
@@ -618,9 +613,7 @@ class MyProfileScreen extends StatelessWidget {
|
||||
drawVerticalLine: true,
|
||||
horizontalInterval: 1.5,
|
||||
verticalInterval: 5,
|
||||
checkToShowHorizontalLine: (value) {
|
||||
return value.toInt() == 0;
|
||||
},
|
||||
checkToShowHorizontalLine: valuteToInt(),
|
||||
getDrawingHorizontalLine: (_) => FlLine(
|
||||
color: Colors.blue.withOpacity(1),
|
||||
dashArray: [8, 2],
|
||||
@@ -631,9 +624,7 @@ class MyProfileScreen extends StatelessWidget {
|
||||
dashArray: [8, 2],
|
||||
strokeWidth: 0.8,
|
||||
),
|
||||
checkToShowVerticalLine: (value) {
|
||||
return value.toInt() == 0;
|
||||
},
|
||||
checkToShowVerticalLine: valuteToInt(),
|
||||
),
|
||||
borderData: FlBorderData(show: false),
|
||||
),
|
||||
@@ -644,8 +635,24 @@ class MyProfileScreen extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
mapTouchedSpots() {
|
||||
return (touchedSpots) {
|
||||
return touchedSpots.map((LineBarSpot touchedSpot) {
|
||||
final textStyle = TextStyle(
|
||||
color: touchedSpot.bar.gradient?.colors[0] ?? touchedSpot.bar.color,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14,
|
||||
);
|
||||
return LineTooltipItem(
|
||||
'${touchedSpot.x}, ${touchedSpot.y.toStringAsFixed(2)}',
|
||||
textStyle,
|
||||
);
|
||||
}).toList();
|
||||
};
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
Widget _buildPlans(BuildContext context, List<dynamic>? plans,
|
||||
_buildPlans(BuildContext context, List<dynamic>? plans,
|
||||
List<dynamic>? imagePaths, List<dynamic>? states) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(left: 7.h),
|
||||
@@ -670,7 +677,6 @@ class MyProfileScreen extends StatelessWidget {
|
||||
Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: plans?.length ?? 0,
|
||||
itemBuilder: (context, i) {
|
||||
String state = states?[i];
|
||||
@@ -781,94 +787,62 @@ class MyProfileScreen extends StatelessWidget {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
final digitsOnly = FilteringTextInputFormatter.digitsOnly;
|
||||
//final digitsOnly = FilteringTextInputFormatter.digitsOnly;
|
||||
return AlertDialog(
|
||||
title: Center(child: Text("Добавить замер")),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
Text("chest_circumference".tr,
|
||||
content: ListView(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 5.v),
|
||||
child: Text("chest_circumference".tr,
|
||||
style: theme.textTheme.bodyLarge),
|
||||
SizedBox(height: 5.v),
|
||||
CustomTextFormField(
|
||||
controller: state.chestCircumferenceController,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
digitsOnly // Ограничение ввода только цифрами
|
||||
],
|
||||
),
|
||||
SizedBox(height: 15.v),
|
||||
Text("waist_circumference".tr,
|
||||
),
|
||||
SizedBox(height: 5.v),
|
||||
CustomTextFormField(
|
||||
controller: state.chestCircumferenceController,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
FilteringTextInputFormatter
|
||||
.digitsOnly, // Ограничение ввода только цифрами
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 15.v),
|
||||
child: Text("waist_circumference".tr,
|
||||
style: theme.textTheme.bodyLarge),
|
||||
SizedBox(height: 5.v),
|
||||
CustomTextFormField(
|
||||
controller: state.waistCircumferenceController,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
digitsOnly // Ограничение ввода только цифрами
|
||||
],
|
||||
),
|
||||
SizedBox(height: 15.v),
|
||||
Text("hip_circumference".tr, style: theme.textTheme.bodyLarge),
|
||||
SizedBox(height: 5.v),
|
||||
CustomTextFormField(
|
||||
controller: state.hipCircumferenceController,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
digitsOnly // Ограничение ввода только цифрами
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5.v),
|
||||
CustomTextFormField(
|
||||
controller: state.waistCircumferenceController,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
FilteringTextInputFormatter
|
||||
.digitsOnly, // Ограничение ввода только цифрами
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 15.v),
|
||||
child: Text("hip_circumference".tr,
|
||||
style: theme.textTheme.bodyLarge),
|
||||
),
|
||||
SizedBox(height: 5.v),
|
||||
CustomTextFormField(
|
||||
controller: state.hipCircumferenceController,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
FilteringTextInputFormatter
|
||||
.digitsOnly, // Ограничение ввода только цифрами
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
onPressed: onTapOk(context),
|
||||
child: Text("Отмена", style: theme.textTheme.bodyLarge),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Map<String, dynamic> data = {
|
||||
"chest_circumference":
|
||||
state.chestCircumferenceController?.text,
|
||||
"waist_circumference":
|
||||
state.waistCircumferenceController?.text,
|
||||
"hip_circumference": state.hipCircumferenceController?.text,
|
||||
};
|
||||
|
||||
// Преобразование Map в JSON
|
||||
String jsonData = json.encode(data);
|
||||
|
||||
// Отправка HTTP-запроса
|
||||
try {
|
||||
final token = state.token;
|
||||
final response = await http.post(
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! +
|
||||
'/profile/addCircumference'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: jsonData,
|
||||
);
|
||||
|
||||
if (response.statusCode == 201) {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.myProfile,
|
||||
);
|
||||
} else {
|
||||
// Ошибка от сервера
|
||||
print(
|
||||
"Ошибка при отправке данных. Код ответа: ${response.statusCode}");
|
||||
}
|
||||
} catch (e) {
|
||||
// Обработка ошибок при отправке запроса
|
||||
print("Ошибка при отправке запроса: $e");
|
||||
}
|
||||
},
|
||||
onPressed: sendEditData(state, context),
|
||||
child: Text("Изменить",
|
||||
style: CustomTextStyles.titleMediumBlack.copyWith(
|
||||
color: Colors.blue,
|
||||
@@ -883,71 +857,76 @@ class MyProfileScreen extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
sendEditData(MyProfileLoadedState state, BuildContext context) {
|
||||
return () async {
|
||||
Map<String, dynamic> data = {
|
||||
"chest_circumference": state.chestCircumferenceController?.text,
|
||||
"waist_circumference": state.waistCircumferenceController?.text,
|
||||
"hip_circumference": state.hipCircumferenceController?.text,
|
||||
};
|
||||
|
||||
// Преобразование Map в JSON
|
||||
String jsonData = json.encode(data);
|
||||
|
||||
// Отправка HTTP-запроса
|
||||
try {
|
||||
final token = state.token;
|
||||
final response = await http.post(
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! + '/profile/addCircumference'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: jsonData,
|
||||
);
|
||||
|
||||
if (response.statusCode == 201) {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.myProfile,
|
||||
);
|
||||
} else {
|
||||
// Ошибка от сервера
|
||||
print(
|
||||
"Ошибка при отправке данных. Код ответа: ${response.statusCode}");
|
||||
}
|
||||
} catch (e) {
|
||||
// Обработка ошибок при отправке запроса
|
||||
print("Ошибка при отправке запроса: $e");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
_editWeight(BuildContext context, MyProfileLoadedState state) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
final digitsOnly = FilteringTextInputFormatter.digitsOnly;
|
||||
return AlertDialog(
|
||||
title: Center(child: Text("add_measure".tr)),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
Text("weight".tr, style: theme.textTheme.bodyLarge),
|
||||
SizedBox(height: 5.v),
|
||||
CustomTextFormField(
|
||||
controller: state.weightController,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
digitsOnly // Ограничение ввода только цифрами
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
content: ListView(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 5.v),
|
||||
child: Text("weight".tr, style: theme.textTheme.bodyLarge),
|
||||
),
|
||||
SizedBox(height: 5.v),
|
||||
CustomTextFormField(
|
||||
controller: state.weightController,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
FilteringTextInputFormatter
|
||||
.digitsOnly, // Ограничение ввода только цифрами
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
onPressed: onTapOk(context),
|
||||
child: Text("Отмена", style: theme.textTheme.bodyLarge),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Map<String, dynamic> data = {
|
||||
"weight": state.weightController!.text,
|
||||
};
|
||||
// Преобразование Map в JSON
|
||||
String jsonData = json.encode(data);
|
||||
|
||||
// Отправка HTTP-запроса
|
||||
try {
|
||||
final token = state.token;
|
||||
final response = await http.post(
|
||||
Uri.parse(
|
||||
dotenv.env['SERVER_ROOT']! + '/profile/addWeight'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: jsonData,
|
||||
);
|
||||
|
||||
if (response.statusCode == 201) {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.myProfile,
|
||||
);
|
||||
} else {
|
||||
// Ошибка от сервера
|
||||
print(
|
||||
"Ошибка при отправке данных. Код ответа: ${response.statusCode}");
|
||||
}
|
||||
} catch (e) {
|
||||
// Обработка ошибок при отправке запроса
|
||||
print("Ошибка при отправке запроса: $e");
|
||||
}
|
||||
},
|
||||
onPressed: addWeight(state, context),
|
||||
child: Text("Изменить",
|
||||
style: CustomTextStyles.titleMediumBlack.copyWith(
|
||||
color: Colors.blue,
|
||||
@@ -961,4 +940,47 @@ class MyProfileScreen extends StatelessWidget {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
onTapOk(BuildContext context) {
|
||||
return () {
|
||||
Navigator.pop(context);
|
||||
};
|
||||
}
|
||||
|
||||
addWeight(MyProfileLoadedState state, BuildContext context) {
|
||||
return () async {
|
||||
Map<String, dynamic> data = {
|
||||
"weight": state.weightController!.text,
|
||||
};
|
||||
// Преобразование Map в JSON
|
||||
String jsonData = json.encode(data);
|
||||
|
||||
// Отправка HTTP-запроса
|
||||
try {
|
||||
final token = state.token;
|
||||
final response = await http.post(
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! + '/profile/addWeight'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
body: jsonData,
|
||||
);
|
||||
|
||||
if (response.statusCode == 201) {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.myProfile,
|
||||
);
|
||||
} else {
|
||||
// Ошибка от сервера
|
||||
print(
|
||||
"Ошибка при отправке данных. Код ответа: ${response.statusCode}");
|
||||
}
|
||||
} catch (e) {
|
||||
// Обработка ошибок при отправке запроса
|
||||
print("Ошибка при отправке запроса: $e");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,14 +5,12 @@ import 'package:eatvio_app/widgets/app_bar/appbar_subtitle.dart';
|
||||
import 'package:eatvio_app/widgets/app_bar/appbar_leading_image.dart';
|
||||
import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart';
|
||||
import 'package:eatvio_app/widgets/custom_bottom_bar.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:eatvio_app/widgets/next_step_button.dart';
|
||||
import 'package:eatvio_app/widgets/custom_about_list.dart';
|
||||
import 'package:eatvio_app/widgets/custom_expandable_list.dart';
|
||||
import 'package:eatvio_app/widgets/custom_notification_screen.dart';
|
||||
import 'package:eatvio_app/widgets/custom_section_title.dart';
|
||||
import 'package:eatvio_app/widgets/custom_language_drop_down.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
class MySettingsScreen extends StatelessWidget {
|
||||
static Widget builder(BuildContext context) {
|
||||
@@ -43,43 +41,39 @@ class MySettingsScreen extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSetingsScreen(BuildContext context, MyProfileLoadedState state) {
|
||||
_buildSetingsScreen(BuildContext context, MyProfileLoadedState state) {
|
||||
mediaQueryData = MediaQuery.of(context);
|
||||
|
||||
return SafeArea(
|
||||
SafeArea(
|
||||
child: Scaffold(
|
||||
appBar: _buildAppBar(context, "my_settings".tr),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
SectionTitle('Язык'),
|
||||
LanguageDropdown(),
|
||||
SectionTitle('Уведомления'),
|
||||
NotificationSwitch('Уведомление об обновлениях'),
|
||||
NotificationSwitch('Напоминания о заполнении'),
|
||||
SectionTitle('Дополнительная информация'),
|
||||
ExpandableList('Персональные данные'),
|
||||
AboutList('О нас', ['ООО Итвио', '2023-н.в.']),
|
||||
],
|
||||
),
|
||||
child: ListView(
|
||||
children: [
|
||||
SectionTitle('Язык'),
|
||||
LanguageDropdown(),
|
||||
SectionTitle('Уведомления'),
|
||||
NotificationSwitch('Уведомление об обновлениях'),
|
||||
NotificationSwitch('Напоминания о заполнении'),
|
||||
SectionTitle('Дополнительная информация'),
|
||||
ExpandableList('Персональные данные'),
|
||||
AboutList('О нас', ['ООО Итвио', '2023-н.в.']),
|
||||
],
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: _buildBottomBar(context)));
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context, String username) {
|
||||
_buildAppBar(BuildContext context, String username) {
|
||||
return CustomAppBar(
|
||||
height: 54.v,
|
||||
leadingWidth: 28.h,
|
||||
leading: AppbarLeadingImage(
|
||||
imagePath: ImageConstant.imgArrowLeft,
|
||||
margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v),
|
||||
onTap: () {
|
||||
onTapArrowLeft(context);
|
||||
}),
|
||||
onTap: onTapBack(context)),
|
||||
title: Padding(
|
||||
padding: EdgeInsets.only(left: 20.h),
|
||||
child: Row(children: [
|
||||
@@ -90,7 +84,13 @@ class MySettingsScreen extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBottomBar(BuildContext context) {
|
||||
Function onTapBack(BuildContext context) {
|
||||
return () {
|
||||
onTapArrowLeft(context);
|
||||
};
|
||||
}
|
||||
|
||||
_buildBottomBar(BuildContext context) {
|
||||
return CustomBottomBar(onChanged: (BottomBarEnum type) {});
|
||||
}
|
||||
|
||||
@@ -98,570 +98,3 @@ class MySettingsScreen extends StatelessWidget {
|
||||
NavigatorService.goBack();
|
||||
}
|
||||
}
|
||||
|
||||
class SectionTitle extends StatelessWidget {
|
||||
final String title;
|
||||
|
||||
SectionTitle(this.title);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 18),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LanguageDropdown extends StatefulWidget {
|
||||
@override
|
||||
_LanguageDropdownState createState() => _LanguageDropdownState();
|
||||
}
|
||||
|
||||
class _LanguageDropdownState extends State<LanguageDropdown> {
|
||||
String selectedLanguage = 'Русский';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DropdownButton<String>(
|
||||
value: selectedLanguage,
|
||||
onChanged: (newValue) {
|
||||
setState(() {
|
||||
selectedLanguage = newValue!;
|
||||
});
|
||||
},
|
||||
isDense: false, // Установите isDense в false
|
||||
selectedItemBuilder: (BuildContext context) {
|
||||
return ['Русский'].map<Widget>((String language) {
|
||||
return Align(
|
||||
alignment: Alignment.center, // Выравнивание текста по центру
|
||||
child: Text(
|
||||
language,
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
},
|
||||
items: ['Русский'].map((String language) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: language,
|
||||
child: Text(language),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NotificationSwitch extends StatefulWidget {
|
||||
final String title;
|
||||
|
||||
NotificationSwitch(this.title);
|
||||
|
||||
@override
|
||||
_NotificationSwitchState createState() => _NotificationSwitchState();
|
||||
}
|
||||
|
||||
class _NotificationSwitchState extends State<NotificationSwitch> {
|
||||
bool switchValue = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
widget.title,
|
||||
style: TextStyle(
|
||||
color: Colors.black, fontSize: 14, fontWeight: FontWeight.bold),
|
||||
),
|
||||
Switch(
|
||||
value: switchValue,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
switchValue = value;
|
||||
});
|
||||
},
|
||||
inactiveThumbColor: appTheme.blueA400,
|
||||
inactiveTrackColor: Colors.white,
|
||||
activeTrackColor: appTheme.blueA400,
|
||||
activeColor: Colors.white,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HyperSwitch extends StatefulWidget {
|
||||
final String title;
|
||||
final MyProfileLoadedState? state;
|
||||
HyperSwitch(this.title, this.state);
|
||||
|
||||
@override
|
||||
_HyperState createState() => _HyperState();
|
||||
}
|
||||
|
||||
class _HyperState extends State<HyperSwitch> {
|
||||
bool switchValue = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.state?.hyperFormController == '1') {
|
||||
switchValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
widget.title,
|
||||
style: TextStyle(
|
||||
color: Colors.black, fontSize: 14, fontWeight: FontWeight.bold),
|
||||
),
|
||||
Switch(
|
||||
value: switchValue,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
switchValue = value;
|
||||
widget.state!.hyperFormController = value ? "1" : "0";
|
||||
});
|
||||
},
|
||||
inactiveThumbColor: appTheme.blueA400,
|
||||
inactiveTrackColor: Colors.white,
|
||||
activeTrackColor: appTheme.blueA400,
|
||||
activeColor: Colors.white,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HypoSwitch extends StatefulWidget {
|
||||
final String title;
|
||||
final MyProfileLoadedState? state;
|
||||
HypoSwitch(this.title, this.state);
|
||||
|
||||
@override
|
||||
_HypoState createState() => _HypoState();
|
||||
}
|
||||
|
||||
class _HypoState extends State<HypoSwitch> {
|
||||
bool switchValue = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.state?.hypoFormController == '1') {
|
||||
switchValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
widget.title,
|
||||
style: TextStyle(
|
||||
color: Colors.black, fontSize: 14, fontWeight: FontWeight.bold),
|
||||
),
|
||||
Switch(
|
||||
value: switchValue,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
switchValue = value;
|
||||
widget.state!.hypoFormController = value ? "1" : "0";
|
||||
});
|
||||
},
|
||||
inactiveThumbColor: appTheme.blueA400,
|
||||
inactiveTrackColor: Colors.white,
|
||||
activeTrackColor: appTheme.blueA400,
|
||||
activeColor: Colors.white,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ExpandableList extends StatefulWidget {
|
||||
final String title;
|
||||
|
||||
ExpandableList(this.title);
|
||||
|
||||
@override
|
||||
_ExpandableListState createState() => _ExpandableListState();
|
||||
}
|
||||
|
||||
class _ExpandableListState extends State<ExpandableList> {
|
||||
bool isExpanded = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
isExpanded = !isExpanded;
|
||||
});
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
widget.title,
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
Icon(isExpanded ? Icons.arrow_drop_up : Icons.arrow_drop_down),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (isExpanded)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0),
|
||||
child: BlocBuilder<MyProfileBloc, MyProfileState>(
|
||||
builder: (context, state) {
|
||||
if (state is MyProfileInitialState) {
|
||||
// Это начальное состояние, данные ещё не загружены.
|
||||
return CircularProgressIndicator(
|
||||
color: Colors.white,
|
||||
backgroundColor: Colors.white,
|
||||
);
|
||||
} else if (state is MyProfileLoadedState) {
|
||||
// Данные загружены, можно строить форму.
|
||||
return _buildForm(context, state);
|
||||
} else if (state is MyProfileErrorState) {
|
||||
// Произошла ошибка при загрузке данных.
|
||||
return Center(child: Text(state.errorMessage));
|
||||
} else {
|
||||
// Другие состояния, если они есть.
|
||||
return Container(); // Пустой контейнер для необработанных состояний.
|
||||
}
|
||||
}),
|
||||
),
|
||||
Divider(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildForm(BuildContext context, MyProfileLoadedState state) {
|
||||
List<String> cities = [
|
||||
'Москва',
|
||||
'Санкт-Петербург',
|
||||
'Новосибирск',
|
||||
'Екатеринбург',
|
||||
'Нижний Новгород',
|
||||
'Казань',
|
||||
'Челябинск',
|
||||
'Омск',
|
||||
'Самара',
|
||||
'Ростов-на-Дону',
|
||||
'Уфа',
|
||||
'Красноярск',
|
||||
'Воронеж',
|
||||
'Пермь',
|
||||
'Волгоград',
|
||||
'Краснодар',
|
||||
'Саратов',
|
||||
'Тюмень',
|
||||
'Тольятти',
|
||||
'Ижевск',
|
||||
'Барнаул',
|
||||
'Ульяновск',
|
||||
'Иркутск',
|
||||
'Хабаровск',
|
||||
'Ярославль',
|
||||
'Владивосток',
|
||||
'Махачкала',
|
||||
'Томск',
|
||||
'Оренбург',
|
||||
'Кемерово',
|
||||
];
|
||||
List<String> countries = [
|
||||
'Россия',
|
||||
];
|
||||
final digitsOnly = FilteringTextInputFormatter.digitsOnly;
|
||||
return Column(children: [
|
||||
TextField(
|
||||
controller: state.firstNameFormController,
|
||||
decoration: InputDecoration(labelText: 'first_name'.tr),
|
||||
),
|
||||
TextField(
|
||||
controller: state.lastNameFormController,
|
||||
decoration: InputDecoration(labelText: 'last_name'.tr),
|
||||
),
|
||||
TextField(
|
||||
decoration: InputDecoration(labelText: 'date_of_birth'.tr),
|
||||
controller: state.dateFormController,
|
||||
onTap: () {
|
||||
showDatePicker(
|
||||
context: context,
|
||||
initialDate: DateTime.now(),
|
||||
firstDate: DateTime(1900),
|
||||
lastDate: DateTime.now(),
|
||||
).then((selectedDate) {
|
||||
if (selectedDate != null) {
|
||||
setState(() {
|
||||
state.dateFormController!.text =
|
||||
DateFormat('dd-MM-yyyy').format(selectedDate);
|
||||
});
|
||||
}
|
||||
});
|
||||
}),
|
||||
DropdownButtonFormField<String>(
|
||||
value: state.selectedCountry,
|
||||
hint: Text(state.selectedCountry ?? "Не выбрано"),
|
||||
onChanged: (newValue) {
|
||||
// Обработчик изменения выбранной страны
|
||||
setSelectedCountry(state, newValue);
|
||||
},
|
||||
items: countries.map<DropdownMenuItem<String>>((String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value),
|
||||
);
|
||||
}).toList(),
|
||||
decoration: InputDecoration(labelText: 'country'.tr),
|
||||
),
|
||||
DropdownButtonFormField<String>(
|
||||
hint: Text(state.selectedCity ?? "Не выбрано"),
|
||||
value: state.selectedCity,
|
||||
onChanged: (newValue) {
|
||||
// Обработчик изменения выбранного города
|
||||
setSelectedCity(state, newValue);
|
||||
},
|
||||
items: cities.map<DropdownMenuItem<String>>((String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value),
|
||||
);
|
||||
}).toList(),
|
||||
decoration: InputDecoration(labelText: 'city_of_living'.tr),
|
||||
),
|
||||
TextField(
|
||||
controller: state.weightFormController,
|
||||
decoration: InputDecoration(labelText: 'weight_measurements'.tr),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[digitsOnly]),
|
||||
TextField(
|
||||
controller: state.heightFormController,
|
||||
decoration: InputDecoration(labelText: 'height_measurements'.tr),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[digitsOnly]),
|
||||
TextField(
|
||||
controller: state.chestCircumferenceFormController,
|
||||
decoration: InputDecoration(labelText: 'chest_circumference'.tr),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[digitsOnly]),
|
||||
TextField(
|
||||
controller: state.waistCircumferenceFormController,
|
||||
decoration: InputDecoration(labelText: 'waist_circumference'.tr),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[digitsOnly]),
|
||||
TextField(
|
||||
controller: state.hipCircumferenceFormController,
|
||||
decoration: InputDecoration(labelText: 'hip_circumference'.tr),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[digitsOnly]),
|
||||
TextField(
|
||||
controller: state.upPressureFormController,
|
||||
decoration: InputDecoration(labelText: 'up_pressure'.tr),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[digitsOnly]),
|
||||
TextField(
|
||||
controller: state.downPressureFormController,
|
||||
decoration: InputDecoration(labelText: 'down_pressure'.tr),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[digitsOnly]),
|
||||
TextField(
|
||||
controller: state.allergiesFormController,
|
||||
decoration: InputDecoration(labelText: 'allergies_presence'.tr),
|
||||
maxLines: null,
|
||||
),
|
||||
TextField(
|
||||
controller: state.pillsFormController,
|
||||
decoration: InputDecoration(labelText: 'do_you_take_pills'.tr),
|
||||
maxLines: null,
|
||||
),
|
||||
TextField(
|
||||
controller: state.sportsFormController,
|
||||
decoration: InputDecoration(labelText: 'your_favorite_sports'.tr),
|
||||
maxLines: null,
|
||||
),
|
||||
TextField(
|
||||
controller: state.favoriteFormController,
|
||||
decoration: InputDecoration(labelText: 'favorite_foods'.tr),
|
||||
maxLines: null,
|
||||
),
|
||||
TextField(
|
||||
controller: state.unfavoriteFormController,
|
||||
decoration: InputDecoration(labelText: 'unfavorite_foods'.tr),
|
||||
maxLines: null,
|
||||
),
|
||||
TextField(
|
||||
controller: state.additionalFormController,
|
||||
decoration: InputDecoration(labelText: 'additional_information'.tr),
|
||||
maxLines: null,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
HypoSwitch('do_you_have_hypotension_tendency'.tr, state),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
HyperSwitch('do_you_have_hypertension_tendency'.tr, state),
|
||||
],
|
||||
),
|
||||
NextStepButton(
|
||||
onPressed: () {
|
||||
sendProfileData(state);
|
||||
},
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
text: "save".tr.toUpperCase(),
|
||||
margin: EdgeInsets.only(top: 20.h, bottom: 20.h)),
|
||||
]);
|
||||
}
|
||||
|
||||
void setSelectedCity(MyProfileLoadedState? state, String? newValue) {
|
||||
setState(() {
|
||||
state!.selectedCity = newValue;
|
||||
});
|
||||
}
|
||||
|
||||
void setSelectedCountry(MyProfileLoadedState? state, String? newValue) {
|
||||
setState(() {
|
||||
state!.selectedCountry = newValue;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> sendProfileData(MyProfileLoadedState? state) async {
|
||||
try {
|
||||
// Создаем JSON объект с данными профиля
|
||||
Map<String, dynamic> profileData = {
|
||||
'first_name': state?.firstNameFormController?.text,
|
||||
'last_name': state?.lastNameFormController?.text,
|
||||
'chest_circumference': state?.chestCircumferenceFormController?.text,
|
||||
'waist_circumference': state?.waistCircumferenceFormController?.text,
|
||||
'hip_circumference': state?.hipCircumferenceFormController?.text,
|
||||
'birthdate': state?.dateFormController?.text,
|
||||
'height': state?.heightFormController?.text,
|
||||
'weight': state?.weightFormController?.text,
|
||||
'blood_pressure_up': state?.upPressureFormController?.text,
|
||||
'blood_pressure_down': state?.downPressureFormController?.text,
|
||||
'about_alergy': state?.allergiesFormController?.text,
|
||||
'about_taking_pils': state?.pillsFormController?.text,
|
||||
'about_sport_hobbies': state?.sportsFormController?.text,
|
||||
'favorite_foods': state?.favoriteFormController?.text,
|
||||
'unfavorite_foods': state?.unfavoriteFormController?.text,
|
||||
'additional_info': state?.additionalFormController?.text,
|
||||
'country': state?.selectedCountry ?? 'Россия',
|
||||
'city': state?.selectedCity ?? 'Москва',
|
||||
'has_hypo': state?.hypoFormController,
|
||||
'has_hyper': state?.hyperFormController,
|
||||
};
|
||||
final box = await Hive.openBox('token');
|
||||
final token = box.get('token');
|
||||
String apiUrl = dotenv.env['SERVER_ROOT']! + '/profile/update';
|
||||
http.Response response = await http.post(
|
||||
Uri.parse(apiUrl),
|
||||
body: json.encode(profileData),
|
||||
encoding: utf8,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
|
||||
print(response.body);
|
||||
if (response.statusCode == 200) {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.myProfile,
|
||||
);
|
||||
} else {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text('Ошибка'),
|
||||
content: Text('Ошибка при отправке данных профиля'.tr),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text('OK'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
throw Exception('Ошибка при отправке данных профиля');
|
||||
}
|
||||
} catch (error) {
|
||||
throw Exception('Ошибка при отправке данных профиля: $error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AboutList extends StatefulWidget {
|
||||
final String title;
|
||||
final List<String> items;
|
||||
|
||||
AboutList(this.title, this.items);
|
||||
|
||||
@override
|
||||
_AboutListState createState() => _AboutListState();
|
||||
}
|
||||
|
||||
class _AboutListState extends State<AboutList> {
|
||||
bool isExpanded = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
isExpanded = !isExpanded;
|
||||
});
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
widget.title,
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
Icon(isExpanded ? Icons.arrow_drop_up : Icons.arrow_drop_down),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (isExpanded)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0),
|
||||
child: Column(
|
||||
//children: widget.items.map((item) => Text(item)).toList(),
|
||||
children: [Text(widget.items.join('\n'))]),
|
||||
),
|
||||
Divider(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,45 +66,52 @@ class AdditionalInformationScreen extends StatelessWidget {
|
||||
margin: EdgeInsets.only(
|
||||
left: 16.h, right: 16.h, bottom: 50.v),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: () async {
|
||||
UserRegistrationSheet sheet = state
|
||||
.additionalInformationModelObj!
|
||||
.userRegistrationSheet;
|
||||
sheet.additionalInfo = state.editTextController!.text;
|
||||
final response = await http.post(
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! + '/register'),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: json.encode(sheet),
|
||||
);
|
||||
var res = await jsonDecode(response.body);
|
||||
final box = await Hive.openBox('token');
|
||||
final token = res['token'];
|
||||
box.put('token', token);
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.myProfile,
|
||||
);
|
||||
});
|
||||
onPressed: onTapFinish(state, context));
|
||||
}),
|
||||
]))));
|
||||
}
|
||||
|
||||
onTapFinish(AdditionalInformationState state, BuildContext context) {
|
||||
return () async {
|
||||
UserRegistrationSheet sheet =
|
||||
state.additionalInformationModelObj!.userRegistrationSheet;
|
||||
sheet.additionalInfo = state.editTextController!.text;
|
||||
final response = await http.post(
|
||||
Uri.parse(dotenv.env['SERVER_ROOT']! + '/register'),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: json.encode(sheet),
|
||||
);
|
||||
var res = await jsonDecode(response.body);
|
||||
final box = await Hive.openBox('token');
|
||||
final token = res['token'];
|
||||
box.put('token', token);
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.myProfile,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context) {
|
||||
_buildAppBar(BuildContext context) {
|
||||
return CustomAppBar(
|
||||
leadingWidth: 31.h,
|
||||
leading: AppbarLeadingImage(
|
||||
imagePath: ImageConstant.imgArrowLeft,
|
||||
margin: EdgeInsets.only(left: 18.h, top: 15.v, bottom: 15.v),
|
||||
onTap: () {
|
||||
onTapArrowLeft(context);
|
||||
}),
|
||||
onTap: onTapBack(context)),
|
||||
centerTitle: true,
|
||||
title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio));
|
||||
}
|
||||
|
||||
onTapBack(BuildContext context) {
|
||||
return () {
|
||||
onTapArrowLeft(context);
|
||||
};
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
Widget _buildTextFormField(BuildContext context) {
|
||||
_buildTextFormField(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 257.v,
|
||||
width: 343.h,
|
||||
@@ -147,12 +154,14 @@ class AdditionalInformationScreen extends StatelessWidget {
|
||||
title: const Text('Регистрация завершена!'),
|
||||
content: const Text('1'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('Ok'))
|
||||
TextButton(onPressed: onTapOk(context), child: const Text('Ok'))
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
onTapOk(BuildContext context) {
|
||||
return () {
|
||||
Navigator.pop(context);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,52 +67,63 @@ class _AllergiesScreenState extends State<AllergiesScreen> {
|
||||
margin: EdgeInsets.only(
|
||||
left: 16.h, right: 16.h, bottom: 50.v),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: () {
|
||||
UserRegistrationSheet sheet =
|
||||
state.allergiesModelObj!.userRegistrationSheet;
|
||||
sheet.aboutAllergy =
|
||||
state.enterInformationController!.text;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.medicineIntakeScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
});
|
||||
onPressed: onTapContinue(state, context));
|
||||
}),
|
||||
BlocBuilder<AllergiesBloc, AllergiesState>(
|
||||
builder: (context, state) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
UserRegistrationSheet sheet =
|
||||
state.allergiesModelObj!.userRegistrationSheet;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.medicineIntakeScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
},
|
||||
onTap: onTapSkip(state, context),
|
||||
child: Text("skip".tr, style: theme.textTheme.bodyLarge),
|
||||
);
|
||||
}),
|
||||
]))));
|
||||
}
|
||||
|
||||
onTapSkip(AllergiesState state, BuildContext context) {
|
||||
return () {
|
||||
UserRegistrationSheet sheet =
|
||||
state.allergiesModelObj!.userRegistrationSheet;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.medicineIntakeScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
onTapContinue(AllergiesState state, BuildContext context) {
|
||||
return () {
|
||||
UserRegistrationSheet sheet =
|
||||
state.allergiesModelObj!.userRegistrationSheet;
|
||||
sheet.aboutAllergy = state.enterInformationController!.text;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.medicineIntakeScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context) {
|
||||
_buildAppBar(BuildContext context) {
|
||||
return CustomAppBar(
|
||||
leadingWidth: 31.h,
|
||||
leading: AppbarLeadingImage(
|
||||
imagePath: ImageConstant.imgArrowLeft,
|
||||
margin: EdgeInsets.only(left: 18.h, top: 15.v, bottom: 15.v),
|
||||
onTap: () {
|
||||
onTapArrowLeft(context);
|
||||
}),
|
||||
onTap: onTapBack(context)),
|
||||
centerTitle: true,
|
||||
title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio));
|
||||
}
|
||||
|
||||
onTapBack(BuildContext context) {
|
||||
return () {
|
||||
onTapArrowLeft(context);
|
||||
};
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
Widget _buildTextFormField(BuildContext context) {
|
||||
_buildTextFormField(BuildContext context) {
|
||||
return Column(children: [
|
||||
Text("fill_in_information".tr,
|
||||
style: CustomTextStyles.titleMediumSegoeUI),
|
||||
|
||||
@@ -151,7 +151,7 @@ class _AppNavigationScreenState extends State<AppNavigationScreen> {
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
Widget _buildAppNavigation(BuildContext context) {
|
||||
_buildAppNavigation(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0XFFFFFFFF),
|
||||
@@ -205,15 +205,13 @@ class _AppNavigationScreenState extends State<AppNavigationScreen> {
|
||||
}
|
||||
|
||||
/// Common widget
|
||||
Widget _buildScreenTitle(
|
||||
_buildScreenTitle(
|
||||
BuildContext context, {
|
||||
required String screenTitle,
|
||||
Function? onTapScreenTitle,
|
||||
}) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
onTapScreenTitle!.call();
|
||||
},
|
||||
onTap: onTapScreenTitle!.call(),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0XFFFFFFFF),
|
||||
|
||||
@@ -43,24 +43,30 @@ class _BloodPressureScreenState extends State<BloodPressureScreen> {
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.h, vertical: 9.v),
|
||||
child: Column(children: [
|
||||
SizedBox(height: 7.v),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 4.h),
|
||||
child: Container(
|
||||
height: 16.v,
|
||||
width: 343.h,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.onPrimary,
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
border: Border.all(
|
||||
color: theme.colorScheme.primary,
|
||||
width: 1.h)),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
child: LinearProgressIndicator(
|
||||
value: 0.62,
|
||||
backgroundColor: theme.colorScheme.onPrimary,
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
appTheme.blueA400))))),
|
||||
Container(
|
||||
height: 16.v,
|
||||
width: 343.h,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal:
|
||||
4.h), // Apply padding directly to the Container
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.onPrimary,
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
border: Border.all(
|
||||
color: theme.colorScheme.primary,
|
||||
width: 1.h,
|
||||
),
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
child: LinearProgressIndicator(
|
||||
value: 0.62,
|
||||
backgroundColor: theme.colorScheme.onPrimary,
|
||||
valueColor:
|
||||
AlwaysStoppedAnimation<Color>(appTheme.blueA400),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 21.v),
|
||||
SizedBox(
|
||||
width: 224.h,
|
||||
@@ -130,11 +136,7 @@ class _BloodPressureScreenState extends State<BloodPressureScreen> {
|
||||
items:
|
||||
bloodPressureModelObj?.dropdownItemList ??
|
||||
[],
|
||||
onChanged: (value) {
|
||||
context
|
||||
.read<BloodPressureBloc>()
|
||||
.add(ChangeHypoDownEvent(value: value));
|
||||
});
|
||||
onChanged: onChangeHyper(context));
|
||||
})),
|
||||
SizedBox(height: 22.v),
|
||||
Text("do_you_have_hypertension_tendency".tr,
|
||||
@@ -161,10 +163,7 @@ class _BloodPressureScreenState extends State<BloodPressureScreen> {
|
||||
items:
|
||||
bloodPressureModelObj?.dropdownItemList ??
|
||||
[],
|
||||
onChanged: (value) {
|
||||
context.read<BloodPressureBloc>().add(
|
||||
ChangeHyperDropDownEvent(value: value));
|
||||
});
|
||||
onChanged: onChangeHypo(context));
|
||||
})),
|
||||
Spacer(),
|
||||
BlocBuilder<BloodPressureBloc, BloodPressureState>(
|
||||
@@ -174,79 +173,106 @@ class _BloodPressureScreenState extends State<BloodPressureScreen> {
|
||||
margin: EdgeInsets.only(
|
||||
left: 16.h, right: 16.h, bottom: 50.v),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: () {
|
||||
List<String> errors = validateFields(
|
||||
state.upPressureController!.text,
|
||||
state.downPressureController!.text);
|
||||
if (errors.isEmpty) {
|
||||
UserRegistrationSheet sheet = state
|
||||
.bloodPressureModelObj!.userRegistrationSheet;
|
||||
sheet.bloodPressureUp =
|
||||
state.upPressureController!.text;
|
||||
sheet.bloodPressureDown =
|
||||
state.downPressureController!.text;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.favoriteSportsScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
} 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'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
onPressed: onTapContinue(state, context));
|
||||
}),
|
||||
BlocBuilder<BloodPressureBloc, BloodPressureState>(
|
||||
builder: (context, state) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
UserRegistrationSheet sheet =
|
||||
state.bloodPressureModelObj!.userRegistrationSheet;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.allergiesScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
},
|
||||
onTap: onTapSkip(state, context),
|
||||
child: Text("skip".tr, style: theme.textTheme.bodyLarge),
|
||||
);
|
||||
}),
|
||||
]))));
|
||||
}
|
||||
|
||||
onTapSkip(BloodPressureState state, BuildContext context) {
|
||||
return () {
|
||||
UserRegistrationSheet sheet =
|
||||
state.bloodPressureModelObj!.userRegistrationSheet;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.allergiesScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
onTapContinue(BloodPressureState state, BuildContext context) {
|
||||
return () {
|
||||
List<String> errors = validateFields(
|
||||
state.upPressureController!.text, state.downPressureController!.text);
|
||||
if (errors.isEmpty) {
|
||||
UserRegistrationSheet sheet =
|
||||
state.bloodPressureModelObj!.userRegistrationSheet;
|
||||
sheet.bloodPressureUp = state.upPressureController!.text;
|
||||
sheet.bloodPressureDown = state.downPressureController!.text;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.favoriteSportsScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
} 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: onTapOK(context),
|
||||
child: Text('OK'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
onTapOK(BuildContext context) {
|
||||
return () {
|
||||
Navigator.pop(context);
|
||||
};
|
||||
}
|
||||
|
||||
onChangeHypo(BuildContext context) {
|
||||
return (value) {
|
||||
context
|
||||
.read<BloodPressureBloc>()
|
||||
.add(ChangeHyperDropDownEvent(value: value));
|
||||
};
|
||||
}
|
||||
|
||||
onChangeHyper(BuildContext context) {
|
||||
return (value) {
|
||||
context.read<BloodPressureBloc>().add(ChangeHypoDownEvent(value: value));
|
||||
};
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context) {
|
||||
_buildAppBar(BuildContext context) {
|
||||
return CustomAppBar(
|
||||
leadingWidth: 29.h,
|
||||
leading: AppbarLeadingImage(
|
||||
imagePath: ImageConstant.imgArrowLeft,
|
||||
margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v),
|
||||
onTap: () {
|
||||
onTapArrowLeft(context);
|
||||
}),
|
||||
onTap: onTapBack(context)),
|
||||
centerTitle: true,
|
||||
title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio));
|
||||
}
|
||||
|
||||
Function onTapBack(BuildContext context) {
|
||||
return () {
|
||||
onTapArrowLeft(context);
|
||||
};
|
||||
}
|
||||
|
||||
/// Navigates to the previous screen.
|
||||
onTapArrowLeft(BuildContext context) {
|
||||
NavigatorService.goBack();
|
||||
|
||||
@@ -105,68 +105,76 @@ class BodyCircumferencesScreen extends StatelessWidget {
|
||||
margin: EdgeInsets.only(
|
||||
left: 16.h, right: 16.h, bottom: 50.v),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: () {
|
||||
List<String> errors = validateFields(
|
||||
state.chestCircumferenceController!.text,
|
||||
state.waistCircumferenceController!.text,
|
||||
state.hipCircumferenceController!.text);
|
||||
if (errors.isEmpty) {
|
||||
UserRegistrationSheet sheet = state
|
||||
.bodyCircumferencesModelObj!
|
||||
.userRegistrationSheet;
|
||||
sheet.chestCircumference =
|
||||
state.chestCircumferenceController!.text;
|
||||
sheet.waistCircumference =
|
||||
state.waistCircumferenceController!.text;
|
||||
sheet.hipCircumference =
|
||||
state.hipCircumferenceController!.text;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.weightScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
} 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'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
onPressed: onTapContinue(state, context));
|
||||
}),
|
||||
]))));
|
||||
}
|
||||
|
||||
onTapContinue(BodyCircumferencesState state, BuildContext context) {
|
||||
return () {
|
||||
List<String> errors = validateFields(
|
||||
state.chestCircumferenceController!.text,
|
||||
state.waistCircumferenceController!.text,
|
||||
state.hipCircumferenceController!.text);
|
||||
if (errors.isEmpty) {
|
||||
UserRegistrationSheet sheet =
|
||||
state.bodyCircumferencesModelObj!.userRegistrationSheet;
|
||||
sheet.chestCircumference = state.chestCircumferenceController!.text;
|
||||
sheet.waistCircumference = state.waistCircumferenceController!.text;
|
||||
sheet.hipCircumference = state.hipCircumferenceController!.text;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.weightScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
} 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: onTapOk(context),
|
||||
child: Text('OK'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
onTapOk(BuildContext context) {
|
||||
return () {
|
||||
Navigator.pop(context);
|
||||
};
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context) {
|
||||
_buildAppBar(BuildContext context) {
|
||||
return CustomAppBar(
|
||||
leadingWidth: 29.h,
|
||||
leading: AppbarLeadingImage(
|
||||
imagePath: ImageConstant.imgArrowLeft,
|
||||
margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v),
|
||||
onTap: () {
|
||||
onTapArrowLeft(context);
|
||||
}),
|
||||
onTap: onTapBack(context)),
|
||||
centerTitle: true,
|
||||
title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio));
|
||||
}
|
||||
|
||||
Function onTapBack(BuildContext context) {
|
||||
return () {
|
||||
onTapArrowLeft(context);
|
||||
};
|
||||
}
|
||||
|
||||
/// Navigates to the previous screen.
|
||||
onTapArrowLeft(BuildContext context) {
|
||||
NavigatorService.goBack();
|
||||
|
||||
@@ -71,11 +71,7 @@ class CityOfLivingScreen extends StatelessWidget {
|
||||
width: 12.adaptSize)),
|
||||
hintText: "Не выбрано".tr,
|
||||
items: cityOfLivingModelObj?.dropdownItemList ?? [],
|
||||
onChanged: (value) {
|
||||
context
|
||||
.read<CityOfLivingBloc>()
|
||||
.add(ChangeDropDownEvent(value: value));
|
||||
});
|
||||
onChanged: onDropDownChange(context));
|
||||
}),
|
||||
SizedBox(height: 22.v),
|
||||
Text("city".tr, style: theme.textTheme.bodyLarge),
|
||||
@@ -97,11 +93,7 @@ class CityOfLivingScreen extends StatelessWidget {
|
||||
hintText: "Не выбрано".tr,
|
||||
items:
|
||||
cityOfLivingModelObj?.dropdownItemList1 ?? [],
|
||||
onChanged: (value) {
|
||||
context
|
||||
.read<CityOfLivingBloc>()
|
||||
.add(ChangeDropDown1Event(value: value));
|
||||
});
|
||||
onChanged: onDropDownChangeCity(context));
|
||||
}),
|
||||
Spacer(flex: 65),
|
||||
BlocBuilder<CityOfLivingBloc, CityOfLivingState>(
|
||||
@@ -111,33 +103,53 @@ class CityOfLivingScreen extends StatelessWidget {
|
||||
margin: EdgeInsets.only(
|
||||
left: 16.h, right: 16.h, bottom: 50.v),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: () {
|
||||
UserRegistrationSheet sheet =
|
||||
state.cityOfLivingModelObj!.userRegistrationSheet;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.bodyCircumferencesScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
});
|
||||
onPressed: onTapContinue(state, context));
|
||||
}),
|
||||
]))));
|
||||
}
|
||||
|
||||
onTapContinue(CityOfLivingState state, BuildContext context) {
|
||||
return () {
|
||||
UserRegistrationSheet sheet =
|
||||
state.cityOfLivingModelObj!.userRegistrationSheet;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.bodyCircumferencesScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
onDropDownChangeCity(BuildContext context) {
|
||||
return (value) {
|
||||
context.read<CityOfLivingBloc>().add(ChangeDropDown1Event(value: value));
|
||||
};
|
||||
}
|
||||
|
||||
onDropDownChange(BuildContext context) {
|
||||
return (value) {
|
||||
context.read<CityOfLivingBloc>().add(ChangeDropDownEvent(value: value));
|
||||
};
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context) {
|
||||
_buildAppBar(BuildContext context) {
|
||||
return CustomAppBar(
|
||||
leadingWidth: 29.h,
|
||||
leading: AppbarLeadingImage(
|
||||
imagePath: ImageConstant.imgArrowLeft,
|
||||
margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v),
|
||||
onTap: () {
|
||||
onTapArrowLeft(context);
|
||||
}),
|
||||
onTap: onTapBack(context)),
|
||||
centerTitle: true,
|
||||
title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio));
|
||||
}
|
||||
|
||||
Function onTapBack(BuildContext context) {
|
||||
return () {
|
||||
onTapArrowLeft(context);
|
||||
};
|
||||
}
|
||||
|
||||
/// Navigates to the previous screen.
|
||||
onTapArrowLeft(BuildContext context) {
|
||||
NavigatorService.goBack();
|
||||
|
||||
@@ -107,55 +107,62 @@ class CredentialsScreen extends StatelessWidget {
|
||||
margin:
|
||||
EdgeInsets.only(left: 16.h, right: 16.h, bottom: 50.v),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: () {
|
||||
List<String> errors = validateFields(
|
||||
state.emailController!.text,
|
||||
state.nameController!.text,
|
||||
state.passwordController!.text,
|
||||
state.passwordConfirmationController!.text);
|
||||
if (errors.isEmpty) {
|
||||
UserRegistrationSheet sheet =
|
||||
state.credentialsModelObj!.userRegistrationSheet;
|
||||
sheet.email = state.emailController!.text;
|
||||
sheet.name = state.nameController!.text;
|
||||
sheet.password = state.passwordController!.text;
|
||||
sheet.passwordConfirmation =
|
||||
state.passwordConfirmationController!.text;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.nameScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
} 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'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
onPressed: onTapContinue(state, context));
|
||||
}),
|
||||
])));
|
||||
}
|
||||
|
||||
onTapContinue(CredentialsState state, BuildContext context) {
|
||||
return () {
|
||||
List<String> errors = validateFields(
|
||||
state.emailController!.text,
|
||||
state.nameController!.text,
|
||||
state.passwordController!.text,
|
||||
state.passwordConfirmationController!.text);
|
||||
if (errors.isEmpty) {
|
||||
UserRegistrationSheet sheet =
|
||||
state.credentialsModelObj!.userRegistrationSheet;
|
||||
sheet.email = state.emailController!.text;
|
||||
sheet.name = state.nameController!.text;
|
||||
sheet.password = state.passwordController!.text;
|
||||
sheet.passwordConfirmation = state.passwordConfirmationController!.text;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.nameScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
} 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: onTapOk(context),
|
||||
child: Text('OK'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
onTapOk(BuildContext context) {
|
||||
return () {
|
||||
Navigator.pop(context);
|
||||
};
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context) {
|
||||
_buildAppBar(BuildContext context) {
|
||||
return CustomAppBar(
|
||||
leadingWidth: 29.h,
|
||||
centerTitle: true,
|
||||
|
||||
@@ -69,37 +69,45 @@ class _DateOfBirthScreenState extends State<DateOfBirthScreen> {
|
||||
margin: EdgeInsets.only(
|
||||
left: 16.h, right: 16.h, bottom: 50.v),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: () {
|
||||
UserRegistrationSheet sheet = state
|
||||
.dateOfBirthModelObj!.userRegistrationSheet;
|
||||
sheet.birthdate = selectedDate.toString();
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.cityOfLivingScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
});
|
||||
onPressed: onTapContinue(state, context));
|
||||
}),
|
||||
]))));
|
||||
});
|
||||
}
|
||||
|
||||
onTapContinue(DateOfBirthState state, BuildContext context) {
|
||||
return () {
|
||||
UserRegistrationSheet sheet =
|
||||
state.dateOfBirthModelObj!.userRegistrationSheet;
|
||||
sheet.birthdate = selectedDate.toString();
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.cityOfLivingScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context) {
|
||||
_buildAppBar(BuildContext context) {
|
||||
return CustomAppBar(
|
||||
leadingWidth: 29.h,
|
||||
leading: AppbarLeadingImage(
|
||||
imagePath: ImageConstant.imgArrowLeft,
|
||||
margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v),
|
||||
onTap: () {
|
||||
onTapArrowLeft(context);
|
||||
}),
|
||||
onTap: onTapBack(context)),
|
||||
centerTitle: true,
|
||||
title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio));
|
||||
}
|
||||
|
||||
Function onTapBack(BuildContext context) {
|
||||
return () {
|
||||
onTapArrowLeft(context);
|
||||
};
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
Widget _buildDateInput(BuildContext context) {
|
||||
_buildDateInput(BuildContext context) {
|
||||
return Container(
|
||||
width: 343.h,
|
||||
padding: EdgeInsets.symmetric(horizontal: 4.h, vertical: 1.v),
|
||||
@@ -119,9 +127,7 @@ class _DateOfBirthScreenState extends State<DateOfBirthScreen> {
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
openDatePickerDialog(context);
|
||||
},
|
||||
onTap: onTapCalendar(context),
|
||||
child: Text(
|
||||
'${selectedDate.day}.${selectedDate.month}.${selectedDate.year}',
|
||||
style: theme.textTheme.titleMedium))),
|
||||
@@ -129,6 +135,12 @@ class _DateOfBirthScreenState extends State<DateOfBirthScreen> {
|
||||
]));
|
||||
}
|
||||
|
||||
onTapCalendar(BuildContext context) {
|
||||
return () {
|
||||
openDatePickerDialog(context);
|
||||
};
|
||||
}
|
||||
|
||||
/// Navigates to the previous screen.
|
||||
onTapArrowLeft(BuildContext context) {
|
||||
NavigatorService.goBack();
|
||||
@@ -139,15 +151,21 @@ class _DateOfBirthScreenState extends State<DateOfBirthScreen> {
|
||||
/// This function returns a `Future` that completes with `void`.
|
||||
Future<void> openDatePickerDialog(BuildContext context) async {
|
||||
DateTime? dateTime = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: DateTime.now(),
|
||||
firstDate: DateTime(1924),
|
||||
lastDate: DateTime(
|
||||
DateTime.now().year, DateTime.now().month, DateTime.now().day));
|
||||
context: context,
|
||||
initialDate: DateTime.now(),
|
||||
firstDate: DateTime(1924),
|
||||
lastDate: DateTime(
|
||||
DateTime.now().year,
|
||||
DateTime.now().month,
|
||||
DateTime.now().day,
|
||||
),
|
||||
);
|
||||
if (dateTime != null) {
|
||||
setState(() {
|
||||
selectedDate = dateTime;
|
||||
});
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
selectedDate = dateTime;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,54 +76,64 @@ class _FavoriteFoodsScreenState extends State<FavoriteFoodsScreen> {
|
||||
margin: EdgeInsets.only(
|
||||
left: 16.h, right: 16.h, bottom: 40.v),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: () {
|
||||
UserRegistrationSheet sheet = state
|
||||
.favoriteFoodsModelObj!.userRegistrationSheet;
|
||||
sheet.favoriteFoods =
|
||||
state.favoriteFoodsController!.text;
|
||||
sheet.unfavoriteFoods =
|
||||
state.unfavoriteFoodsController!.text;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.additionalInformationScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
});
|
||||
onPressed: onTapContinue(state, context));
|
||||
}),
|
||||
BlocBuilder<FavoriteFoodsBloc, FavoriteFoodsState>(
|
||||
builder: (context, state) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
UserRegistrationSheet sheet =
|
||||
state.favoriteFoodsModelObj!.userRegistrationSheet;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.additionalInformationScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
},
|
||||
onTap: onTapSkip(state, context),
|
||||
child: Text("skip".tr, style: theme.textTheme.bodyLarge),
|
||||
);
|
||||
}),
|
||||
]))));
|
||||
}
|
||||
|
||||
onTapSkip(FavoriteFoodsState state, BuildContext context) {
|
||||
return () {
|
||||
UserRegistrationSheet sheet =
|
||||
state.favoriteFoodsModelObj!.userRegistrationSheet;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.additionalInformationScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
onTapContinue(FavoriteFoodsState state, BuildContext context) {
|
||||
return () {
|
||||
UserRegistrationSheet sheet =
|
||||
state.favoriteFoodsModelObj!.userRegistrationSheet;
|
||||
sheet.favoriteFoods = state.favoriteFoodsController!.text;
|
||||
sheet.unfavoriteFoods = state.unfavoriteFoodsController!.text;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.additionalInformationScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context) {
|
||||
_buildAppBar(BuildContext context) {
|
||||
return CustomAppBar(
|
||||
leadingWidth: 31.h,
|
||||
leading: AppbarLeadingImage(
|
||||
imagePath: ImageConstant.imgArrowLeft,
|
||||
margin: EdgeInsets.only(left: 18.h, top: 15.v, bottom: 15.v),
|
||||
onTap: () {
|
||||
onTapArrowLeft(context);
|
||||
}),
|
||||
onTap: onTapBack(context)),
|
||||
centerTitle: true,
|
||||
title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio));
|
||||
}
|
||||
|
||||
Function onTapBack(BuildContext context) {
|
||||
return () {
|
||||
onTapArrowLeft(context);
|
||||
};
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
Widget _buildTextFormFieldFav(BuildContext context) {
|
||||
_buildTextFormFieldFav(BuildContext context) {
|
||||
return Column(children: [
|
||||
Text("favorite_foods".tr, style: CustomTextStyles.titleMediumSegoeUI),
|
||||
SizedBox(height: 2.v),
|
||||
@@ -140,7 +150,7 @@ class _FavoriteFoodsScreenState extends State<FavoriteFoodsScreen> {
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
Widget _buildTextFormFieldUnfav(BuildContext context) {
|
||||
_buildTextFormFieldUnfav(BuildContext context) {
|
||||
return Column(children: [
|
||||
Text("unfavorite_foods".tr, style: CustomTextStyles.titleMediumSegoeUI),
|
||||
SizedBox(height: 2.v),
|
||||
|
||||
@@ -71,31 +71,12 @@ class _FavoriteSportsScreenState extends State<FavoriteSportsScreen> {
|
||||
margin: EdgeInsets.only(
|
||||
left: 16.h, right: 16.h, bottom: 50.v),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: () {
|
||||
UserRegistrationSheet sheet = state
|
||||
.favoriteSportsModelObj!
|
||||
.userRegistrationSheet;
|
||||
sheet.aboutSportHobbies =
|
||||
state.enterinformationController!.text;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.favoriteFoodsScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
});
|
||||
onPressed: onTapContinue(state, context));
|
||||
}),
|
||||
BlocBuilder<FavoriteSportsBloc, FavoriteSportsState>(
|
||||
builder: (context, state) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
UserRegistrationSheet sheet = state
|
||||
.favoriteSportsModelObj!.userRegistrationSheet;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.favoriteFoodsScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
},
|
||||
onTap: onTapSkip(state, context),
|
||||
child:
|
||||
Text("skip".tr, style: theme.textTheme.bodyLarge),
|
||||
);
|
||||
@@ -103,21 +84,50 @@ class _FavoriteSportsScreenState extends State<FavoriteSportsScreen> {
|
||||
]))));
|
||||
}
|
||||
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context) {
|
||||
onTapSkip(FavoriteSportsState state, BuildContext context) {
|
||||
return () {
|
||||
UserRegistrationSheet sheet =
|
||||
state.favoriteSportsModelObj!.userRegistrationSheet;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.favoriteFoodsScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
onTapContinue(FavoriteSportsState state, BuildContext context) {
|
||||
return () {
|
||||
UserRegistrationSheet sheet =
|
||||
state.favoriteSportsModelObj!.userRegistrationSheet;
|
||||
sheet.aboutSportHobbies = state.enterinformationController!.text;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.favoriteFoodsScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
_buildAppBar(BuildContext context) {
|
||||
return CustomAppBar(
|
||||
leadingWidth: 29.h,
|
||||
leading: AppbarLeadingImage(
|
||||
imagePath: ImageConstant.imgArrowLeft,
|
||||
margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v),
|
||||
onTap: () {
|
||||
onTapArrowLeft(context);
|
||||
}),
|
||||
onTap: onTapBack(context)),
|
||||
centerTitle: true,
|
||||
title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio));
|
||||
}
|
||||
|
||||
Function onTapBack(BuildContext context) {
|
||||
return () {
|
||||
onTapArrowLeft(context);
|
||||
};
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
Widget _buildTextFormFieldSection(BuildContext context) {
|
||||
_buildTextFormFieldSection(BuildContext context) {
|
||||
return Column(children: [
|
||||
Text("fill_in_information".tr,
|
||||
style: CustomTextStyles.titleMediumSegoeUI),
|
||||
|
||||
@@ -63,10 +63,7 @@ class _GenderScreenState extends State<GenderScreen> {
|
||||
title: Text('Мужской'),
|
||||
value: 'male',
|
||||
groupValue: state.selectedRadioValue,
|
||||
onChanged: (value) {
|
||||
BlocProvider.of<GenderBloc>(context)
|
||||
.add(GenderSelectEvent(value: 'male'));
|
||||
},
|
||||
onChanged: onMale(context),
|
||||
secondary: Image.asset(
|
||||
'assets/images/img_male.png',
|
||||
width: 40,
|
||||
@@ -77,10 +74,7 @@ class _GenderScreenState extends State<GenderScreen> {
|
||||
title: Text('Женский'),
|
||||
value: 'female',
|
||||
groupValue: state.selectedRadioValue,
|
||||
onChanged: (value) {
|
||||
BlocProvider.of<GenderBloc>(context)
|
||||
.add(GenderSelectEvent(value: 'female'));
|
||||
},
|
||||
onChanged: onFemale(context),
|
||||
secondary: Image.asset(
|
||||
'assets/images/img_female.png',
|
||||
width: 40,
|
||||
@@ -95,37 +89,58 @@ class _GenderScreenState extends State<GenderScreen> {
|
||||
margin: EdgeInsets.only(
|
||||
left: 16.h, right: 16.h, bottom: 50.v),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: () {
|
||||
UserRegistrationSheet sheet =
|
||||
state.genderModelObj!.userRegistrationSheet;
|
||||
sheet.gender = state.selectedRadioValue!;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.dateOfBirthScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
});
|
||||
onPressed: onTapNext(state, context));
|
||||
}),
|
||||
]))));
|
||||
});
|
||||
}
|
||||
|
||||
onTapNext(GenderState state, BuildContext context) {
|
||||
return () {
|
||||
UserRegistrationSheet sheet = state.genderModelObj!.userRegistrationSheet;
|
||||
sheet.gender = state.selectedRadioValue!;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.dateOfBirthScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
onMale(BuildContext context) {
|
||||
return (value) {
|
||||
BlocProvider.of<GenderBloc>(context)
|
||||
.add(GenderSelectEvent(value: 'male'));
|
||||
};
|
||||
}
|
||||
|
||||
onFemale(BuildContext context) {
|
||||
return (value) {
|
||||
BlocProvider.of<GenderBloc>(context)
|
||||
.add(GenderSelectEvent(value: 'female'));
|
||||
};
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context) {
|
||||
_buildAppBar(BuildContext context) {
|
||||
return CustomAppBar(
|
||||
leadingWidth: 29.h,
|
||||
leading: AppbarLeadingImage(
|
||||
imagePath: ImageConstant.imgArrowLeft,
|
||||
margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v),
|
||||
onTap: () {
|
||||
NavigatorService.pushNamed(
|
||||
AppRoutes.dateOfBirthScreen,
|
||||
);
|
||||
}),
|
||||
onTap: onTapBack()),
|
||||
centerTitle: true,
|
||||
title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio));
|
||||
}
|
||||
|
||||
Function onTapBack() {
|
||||
return () {
|
||||
NavigatorService.pushNamed(
|
||||
AppRoutes.dateOfBirthScreen,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/// Navigates to the previous screen.
|
||||
onTapArrowLeft(BuildContext context) {
|
||||
NavigatorService.goBack();
|
||||
|
||||
@@ -77,11 +77,7 @@ class _HeightScreenState extends State<HeightScreen> {
|
||||
width: 12.adaptSize)),
|
||||
hintText: "170",
|
||||
items: heightModelObj?.dropdownItemList ?? [],
|
||||
onChanged: (value) {
|
||||
context
|
||||
.read<HeightBloc>()
|
||||
.add(ChangeDropDownEvent(value: value));
|
||||
});
|
||||
onChanged: newMethod(context));
|
||||
})
|
||||
])
|
||||
]),
|
||||
@@ -93,33 +89,46 @@ class _HeightScreenState extends State<HeightScreen> {
|
||||
margin: EdgeInsets.only(
|
||||
left: 16.h, right: 16.h, bottom: 50.v),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: () {
|
||||
UserRegistrationSheet sheet =
|
||||
state.heightModelObj!.userRegistrationSheet;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.bloodPressureScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
});
|
||||
onPressed: onTapNext(state, context));
|
||||
}),
|
||||
]))));
|
||||
}
|
||||
|
||||
newMethod(BuildContext context) {
|
||||
return (value) {
|
||||
context.read<HeightBloc>().add(ChangeDropDownEvent(value: value));
|
||||
};
|
||||
}
|
||||
|
||||
onTapNext(HeightState state, BuildContext context) {
|
||||
return () {
|
||||
UserRegistrationSheet sheet = state.heightModelObj!.userRegistrationSheet;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.bloodPressureScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context) {
|
||||
_buildAppBar(BuildContext context) {
|
||||
return CustomAppBar(
|
||||
leadingWidth: 29.h,
|
||||
leading: AppbarLeadingImage(
|
||||
imagePath: ImageConstant.imgArrowLeft,
|
||||
margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v),
|
||||
onTap: () {
|
||||
onTapArrowLeft(context);
|
||||
}),
|
||||
onTap: onTapBack(context)),
|
||||
centerTitle: true,
|
||||
title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio));
|
||||
}
|
||||
|
||||
Function onTapBack(BuildContext context) {
|
||||
return () {
|
||||
onTapArrowLeft(context);
|
||||
};
|
||||
}
|
||||
|
||||
/// Navigates to the previous screen.
|
||||
onTapArrowLeft(BuildContext context) {
|
||||
NavigatorService.goBack();
|
||||
|
||||
@@ -64,23 +64,22 @@ class LanguageSelectScreen extends StatelessWidget {
|
||||
languageSelectModelObj,
|
||||
) {
|
||||
return CustomDropDown(
|
||||
icon: Container(
|
||||
margin:
|
||||
EdgeInsets.fromLTRB(30.h, 11.v, 17.h, 11.v),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(1.h)),
|
||||
child: CustomImageView(
|
||||
imagePath: ImageConstant.imgFavorite,
|
||||
height: 12.adaptSize,
|
||||
width: 12.adaptSize)),
|
||||
hintText: "Не выбрано".tr,
|
||||
items:
|
||||
languageSelectModelObj?.dropdownItemList ?? [],
|
||||
onChanged: (value) {
|
||||
context
|
||||
.read<LanguageSelectBloc>()
|
||||
.add(ChangeDropDownEvent(value: value));
|
||||
});
|
||||
icon: Container(
|
||||
margin: EdgeInsets.fromLTRB(30.h, 11.v, 17.h, 11.v),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(1.h),
|
||||
),
|
||||
child: CustomImageView(
|
||||
imagePath: ImageConstant.imgFavorite,
|
||||
height: 12.adaptSize,
|
||||
width: 12.adaptSize,
|
||||
),
|
||||
),
|
||||
hintText: "Не выбрано".tr,
|
||||
items: languageSelectModelObj?.dropdownItemList ?? [],
|
||||
onChanged: (value) =>
|
||||
_onDropDownChanged(context, value),
|
||||
);
|
||||
}),
|
||||
Spacer(flex: 63),
|
||||
])),
|
||||
@@ -92,15 +91,24 @@ class LanguageSelectScreen extends StatelessWidget {
|
||||
margin:
|
||||
EdgeInsets.only(left: 16.h, right: 16.h, bottom: 50.v),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: () {
|
||||
UserRegistrationSheet sheet =
|
||||
state.languageSelectModelObj!.userRegistrationSheet;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.credentialsScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
});
|
||||
onPressed: _saveAndPush(state, context));
|
||||
})));
|
||||
}
|
||||
|
||||
void Function()? _saveAndPush(
|
||||
LanguageSelectState state, BuildContext context) {
|
||||
return () {
|
||||
UserRegistrationSheet sheet =
|
||||
state.languageSelectModelObj!.userRegistrationSheet;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.credentialsScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
void _onDropDownChanged(BuildContext context, value) {
|
||||
context.read<LanguageSelectBloc>().add(ChangeDropDownEvent(value: value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,52 +74,63 @@ class _MedicineIntakeScreenState extends State<MedicineIntakeScreen> {
|
||||
margin: EdgeInsets.only(
|
||||
left: 16.h, right: 16.h, bottom: 50.v),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: () {
|
||||
UserRegistrationSheet sheet = state
|
||||
.medicineIntakeModelObj!.userRegistrationSheet;
|
||||
sheet.aboutTakingPils =
|
||||
state.enterInformationController!.text;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.bloodPressureScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
});
|
||||
onPressed: onTapNext(state, context));
|
||||
}),
|
||||
BlocBuilder<MedicineIntakeBloc, MedicineIntakeState>(
|
||||
builder: (context, state) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
UserRegistrationSheet sheet =
|
||||
state.medicineIntakeModelObj!.userRegistrationSheet;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.bloodPressureScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
},
|
||||
onTap: onTapSkip(state, context),
|
||||
child: Text("skip".tr, style: theme.textTheme.bodyLarge),
|
||||
);
|
||||
}),
|
||||
]))));
|
||||
}
|
||||
|
||||
onTapSkip(MedicineIntakeState state, BuildContext context) {
|
||||
return () {
|
||||
UserRegistrationSheet sheet =
|
||||
state.medicineIntakeModelObj!.userRegistrationSheet;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.bloodPressureScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
onTapNext(MedicineIntakeState state, BuildContext context) {
|
||||
return () {
|
||||
UserRegistrationSheet sheet =
|
||||
state.medicineIntakeModelObj!.userRegistrationSheet;
|
||||
sheet.aboutTakingPils = state.enterInformationController!.text;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.bloodPressureScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context) {
|
||||
_buildAppBar(BuildContext context) {
|
||||
return CustomAppBar(
|
||||
leadingWidth: 31.h,
|
||||
leading: AppbarLeadingImage(
|
||||
imagePath: ImageConstant.imgArrowLeft,
|
||||
margin: EdgeInsets.only(left: 18.h, top: 15.v, bottom: 15.v),
|
||||
onTap: () {
|
||||
onTapArrowLeft(context);
|
||||
}),
|
||||
onTap: onTapBack(context)),
|
||||
centerTitle: true,
|
||||
title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio));
|
||||
}
|
||||
|
||||
Function onTapBack(BuildContext context) {
|
||||
return () {
|
||||
onTapArrowLeft(context);
|
||||
};
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
Widget _buildTextFormField(BuildContext context) {
|
||||
_buildTextFormField(BuildContext context) {
|
||||
return Column(children: [
|
||||
Text("fill_in_information".tr,
|
||||
style: CustomTextStyles.titleMediumSegoeUI),
|
||||
|
||||
@@ -82,58 +82,19 @@ class NameScreen extends StatelessWidget {
|
||||
margin: EdgeInsets.only(
|
||||
left: 16.h, right: 16.h, bottom: 50.v),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: () {
|
||||
List<String> errors = validateFields(
|
||||
state.firstNameController!.text,
|
||||
state.lastNameController!.text);
|
||||
if (errors.isEmpty) {
|
||||
UserRegistrationSheet sheet =
|
||||
state.nameModelObj!.userRegistrationSheet;
|
||||
sheet.firstName = state.firstNameController!.text;
|
||||
sheet.lastName = state.lastNameController!.text;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.genderScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
} 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'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
onPressed: _onTapNext(context, state));
|
||||
}),
|
||||
]))));
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context) {
|
||||
_buildAppBar(BuildContext context) {
|
||||
return CustomAppBar(
|
||||
leadingWidth: 29.h,
|
||||
leading: AppbarLeadingImage(
|
||||
imagePath: ImageConstant.imgArrowLeft,
|
||||
margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v),
|
||||
onTap: () {
|
||||
onTapArrowLeft(context);
|
||||
}),
|
||||
onTap: onTapArrowLeft(context)),
|
||||
centerTitle: true,
|
||||
title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio));
|
||||
}
|
||||
@@ -143,6 +104,45 @@ class NameScreen extends StatelessWidget {
|
||||
NavigatorService.goBack();
|
||||
}
|
||||
|
||||
_onTapNext(BuildContext context, state) {
|
||||
List<String> errors = validateFields(
|
||||
state.firstNameController!.text, state.lastNameController!.text);
|
||||
if (errors.isEmpty) {
|
||||
UserRegistrationSheet sheet = state.nameModelObj!.userRegistrationSheet;
|
||||
sheet.firstName = state.firstNameController!.text;
|
||||
sheet.lastName = state.lastNameController!.text;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.genderScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
} 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: onTapOk(context),
|
||||
child: Text('OK'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
onTapOk(BuildContext context) {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
List<String> validateFields(
|
||||
String firstName,
|
||||
String lastName,
|
||||
|
||||
@@ -77,11 +77,7 @@ class _WeightScreenState extends State<WeightScreen> {
|
||||
width: 12.adaptSize)),
|
||||
hintText: "Не выбрано",
|
||||
items: weightModelObj?.dropdownItemList ?? [],
|
||||
onChanged: (value) {
|
||||
context
|
||||
.read<WeightBloc>()
|
||||
.add(ChangeDropDownEvent(value: value));
|
||||
});
|
||||
onChanged: _onDropDownChanged);
|
||||
})
|
||||
])
|
||||
]),
|
||||
@@ -93,29 +89,19 @@ class _WeightScreenState extends State<WeightScreen> {
|
||||
margin: EdgeInsets.only(
|
||||
left: 16.h, right: 16.h, bottom: 50.v),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
onPressed: () {
|
||||
UserRegistrationSheet sheet =
|
||||
state.weightModelObj!.userRegistrationSheet;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.heightScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
});
|
||||
onPressed: onTapNext(context, state));
|
||||
}),
|
||||
]))));
|
||||
}
|
||||
|
||||
/// Section Widget
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context) {
|
||||
_buildAppBar(BuildContext context) {
|
||||
return CustomAppBar(
|
||||
leadingWidth: 29.h,
|
||||
leading: AppbarLeadingImage(
|
||||
imagePath: ImageConstant.imgArrowLeft,
|
||||
margin: EdgeInsets.only(left: 16.h, top: 15.v, bottom: 15.v),
|
||||
onTap: () {
|
||||
onTapArrowLeft(context);
|
||||
}),
|
||||
onTap: onTapArrowLeft(context)),
|
||||
centerTitle: true,
|
||||
title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio));
|
||||
}
|
||||
@@ -125,10 +111,23 @@ class _WeightScreenState extends State<WeightScreen> {
|
||||
NavigatorService.goBack();
|
||||
}
|
||||
|
||||
onTapNext(BuildContext context, state) {
|
||||
UserRegistrationSheet sheet = state.weightModelObj!.userRegistrationSheet;
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.heightScreen,
|
||||
arguments: sheet,
|
||||
);
|
||||
}
|
||||
|
||||
/// Navigates to the allergiesScreen when the action is triggered.
|
||||
navigateToHeight(BuildContext context) {
|
||||
NavigatorService.pushNamed(
|
||||
AppRoutes.heightScreen,
|
||||
);
|
||||
}
|
||||
|
||||
void _onDropDownChanged(value) {
|
||||
context.read<WeightBloc>().add(ChangeDropDownEvent(value: value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,7 @@ class AppbarLeadingImage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
onTap!.call();
|
||||
},
|
||||
onTap: onTapCall,
|
||||
child: Padding(
|
||||
padding: margin ?? EdgeInsets.zero,
|
||||
child: CustomImageView(
|
||||
@@ -35,4 +33,8 @@ class AppbarLeadingImage extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void onTapCall() {
|
||||
onTap!.call();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,7 @@ class AppbarSubtitle extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
onTap!.call();
|
||||
},
|
||||
onTap: onTapCall,
|
||||
child: Padding(
|
||||
padding: margin ?? EdgeInsets.zero,
|
||||
child: Text(
|
||||
@@ -35,4 +33,8 @@ class AppbarSubtitle extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void onTapCall() {
|
||||
onTap!.call();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,9 +22,7 @@ class AppbarTitleCircleimage extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
borderRadius: BorderRadiusStyle.circleBorder8,
|
||||
onTap: () {
|
||||
onTap!.call();
|
||||
},
|
||||
onTap: onTapCall,
|
||||
child: Padding(
|
||||
padding: margin ?? EdgeInsets.zero,
|
||||
child: CustomImageView(
|
||||
@@ -39,4 +37,8 @@ class AppbarTitleCircleimage extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void onTapCall() {
|
||||
onTap!.call();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,7 @@ class AppbarTitleImage extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
onTap!.call();
|
||||
},
|
||||
onTap: onTapCall,
|
||||
child: Padding(
|
||||
padding: margin ?? EdgeInsets.zero,
|
||||
child: CustomImageView(
|
||||
@@ -35,4 +33,8 @@ class AppbarTitleImage extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void onTapCall() {
|
||||
onTap!.call();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,7 @@ class ExitButton extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
onTap!.call();
|
||||
},
|
||||
onTap: onTapCall,
|
||||
child: Padding(
|
||||
padding: margin ?? EdgeInsets.zero,
|
||||
child: Text(
|
||||
@@ -35,4 +33,8 @@ class ExitButton extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void onTapCall() {
|
||||
onTap!.call();
|
||||
}
|
||||
}
|
||||
|
||||
56
lib/widgets/custom_about_list.dart
Normal file
56
lib/widgets/custom_about_list.dart
Normal file
@@ -0,0 +1,56 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AboutList extends StatefulWidget {
|
||||
final String title;
|
||||
final List<String> items;
|
||||
|
||||
AboutList(this.title, this.items);
|
||||
|
||||
@override
|
||||
_AboutListState createState() => _AboutListState();
|
||||
}
|
||||
|
||||
class _AboutListState extends State<AboutList> {
|
||||
bool isExpanded = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: toggleList(),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
widget.title,
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
Icon(isExpanded ? Icons.arrow_drop_up : Icons.arrow_drop_down),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (isExpanded)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0),
|
||||
child: Column(
|
||||
//children: widget.items.map((item) => Text(item)).toList(),
|
||||
children: [Text(widget.items.join('\n'))]),
|
||||
),
|
||||
Divider(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
toggleList() {
|
||||
return () {
|
||||
setState(() {
|
||||
isExpanded = !isExpanded;
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
80
lib/widgets/custom_autocomplete.dart
Normal file
80
lib/widgets/custom_autocomplete.dart
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
class CustomTextFormField extends StatelessWidget {
|
||||
// ...
|
||||
|
||||
final AutocompleteOptions<String> autocompleteOptions;
|
||||
|
||||
const CustomTextFormField({
|
||||
// ... (остальные параметры)
|
||||
required this.autocompleteOptions,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return alignment != null
|
||||
? Align(
|
||||
alignment: alignment ?? Alignment.center,
|
||||
child: autocompleteFieldWidget,
|
||||
)
|
||||
: autocompleteFieldWidget;
|
||||
}
|
||||
|
||||
Widget get autocompleteFieldWidget => SizedBox(
|
||||
width: width ?? double.maxFinite,
|
||||
child: Autocomplete<String>(
|
||||
optionsBuilder: (TextEditingValue textEditingValue) {
|
||||
if (textEditingValue.text == '') {
|
||||
return const Iterable<String>.empty();
|
||||
}
|
||||
return autocompleteOptions.options.where((String option) {
|
||||
return option
|
||||
.toLowerCase()
|
||||
.contains(textEditingValue.text.toLowerCase());
|
||||
});
|
||||
},
|
||||
onSelected: (String value) {
|
||||
controller?.text = value;
|
||||
onChanged?.call(value);
|
||||
},
|
||||
fieldViewBuilder: (BuildContext context,
|
||||
TextEditingController textEditingController,
|
||||
FocusNode focusNode,
|
||||
VoidCallback onFieldSubmitted) {
|
||||
return TextFormField(
|
||||
controller: textEditingController,
|
||||
focusNode: focusNode,
|
||||
autofocus: autofocus!,
|
||||
style: textStyle ?? theme.textTheme.titleMedium,
|
||||
obscureText: obscureText!,
|
||||
textInputAction: textInputAction,
|
||||
maxLines: maxLines ?? 1,
|
||||
decoration: decoration,
|
||||
validator: validator,
|
||||
keyboardType: keyboardType,
|
||||
onSaved: onSaved,
|
||||
onChanged: onChanged,
|
||||
inputFormatters: inputFormatters,
|
||||
initialValue: initialValue,
|
||||
onFieldSubmitted: (_) => onFieldSubmitted(),
|
||||
);
|
||||
},
|
||||
optionsViewBuilder: (BuildContext context,
|
||||
AutocompleteOnSelected<String> onSelected,
|
||||
Iterable<String> options) {
|
||||
return Material(
|
||||
elevation: 4.0,
|
||||
child: ListView(
|
||||
children: options.map((String option) {
|
||||
return ListTile(
|
||||
title: Text(option),
|
||||
onTap: () {
|
||||
onSelected(option);
|
||||
},
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}*/
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:eatvio_app/core/app_export.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class CustomBottomBar extends StatefulWidget {
|
||||
CustomBottomBar({this.onChanged});
|
||||
|
||||
@@ -64,60 +65,63 @@ class CustomBottomBarState extends State<CustomBottomBar> {
|
||||
elevation: 0,
|
||||
currentIndex: selectedIndex,
|
||||
type: BottomNavigationBarType.fixed,
|
||||
items: List.generate(bottomMenuList.length, (index) {
|
||||
return BottomNavigationBarItem(
|
||||
icon: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
CustomImageView(
|
||||
imagePath: bottomMenuList[index].icon,
|
||||
height: 24.v,
|
||||
width: 19.h,
|
||||
color: appTheme.blueGray400,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 8.v),
|
||||
child: Text(
|
||||
bottomMenuList[index].title ?? "",
|
||||
style:
|
||||
CustomTextStyles.bodySmallMulishBluegray80012.copyWith(
|
||||
color: appTheme.blueGray800,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
activeIcon: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
CustomImageView(
|
||||
imagePath: bottomMenuList[index].activeIcon,
|
||||
height: 24.adaptSize,
|
||||
width: 24.adaptSize,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 9.v),
|
||||
child: Text(
|
||||
bottomMenuList[index].title ?? "",
|
||||
style: CustomTextStyles.bodySmallMulishPrimary.copyWith(
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
label: '',
|
||||
);
|
||||
}),
|
||||
onTap: (index) {
|
||||
selectedIndex = index;
|
||||
widget.onChanged?.call(bottomMenuList[index].type);
|
||||
setState(() {});
|
||||
},
|
||||
items: List.generate(bottomMenuList.length, bottomMenu),
|
||||
onTap: changeMeny,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void changeMeny(index) {
|
||||
selectedIndex = index;
|
||||
widget.onChanged?.call(bottomMenuList[index].type);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
BottomNavigationBarItem bottomMenu(index) {
|
||||
return BottomNavigationBarItem(
|
||||
icon: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
CustomImageView(
|
||||
imagePath: bottomMenuList[index].icon,
|
||||
height: 24.v,
|
||||
width: 19.h,
|
||||
color: appTheme.blueGray400,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 8.v),
|
||||
child: Text(
|
||||
bottomMenuList[index].title ?? "",
|
||||
style: CustomTextStyles.bodySmallMulishBluegray80012.copyWith(
|
||||
color: appTheme.blueGray800,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
activeIcon: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
CustomImageView(
|
||||
imagePath: bottomMenuList[index].activeIcon,
|
||||
height: 24.adaptSize,
|
||||
width: 24.adaptSize,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 9.v),
|
||||
child: Text(
|
||||
bottomMenuList[index].title ?? "",
|
||||
style: CustomTextStyles.bodySmallMulishPrimary.copyWith(
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
label: '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum BottomBarEnum {
|
||||
@@ -140,27 +144,3 @@ class BottomMenuModel {
|
||||
|
||||
BottomBarEnum type;
|
||||
}
|
||||
|
||||
class DefaultWidget extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
color: Colors.white,
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Center(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Please replace the respective Widget here',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,74 +70,72 @@ class CustomDropDown extends StatelessWidget {
|
||||
return alignment != null
|
||||
? Align(
|
||||
alignment: alignment ?? Alignment.center,
|
||||
child: dropDownWidget,
|
||||
child: _buildDropDownWidget(context),
|
||||
)
|
||||
: dropDownWidget;
|
||||
: _buildDropDownWidget(context);
|
||||
}
|
||||
|
||||
Widget get dropDownWidget => SizedBox(
|
||||
width: width ?? double.maxFinite,
|
||||
child: DropdownButtonFormField<SelectionPopupModel>(
|
||||
focusNode: focusNode ?? FocusNode(),
|
||||
icon: icon,
|
||||
autofocus: autofocus!,
|
||||
style: textStyle ?? theme.textTheme.titleMedium,
|
||||
items: items?.map((SelectionPopupModel item) {
|
||||
return DropdownMenuItem<SelectionPopupModel>(
|
||||
value: item,
|
||||
child: Text(
|
||||
item.title,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: hintStyle ?? theme.textTheme.titleMedium,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
decoration: decoration,
|
||||
validator: validator,
|
||||
onChanged: (value) {
|
||||
onChanged!(value!);
|
||||
},
|
||||
),
|
||||
);
|
||||
InputDecoration get decoration => InputDecoration(
|
||||
hintText: hintText ?? "",
|
||||
hintStyle: hintStyle ?? theme.textTheme.titleMedium,
|
||||
prefixIcon: prefix,
|
||||
prefixIconConstraints: prefixConstraints,
|
||||
suffixIcon: suffix,
|
||||
suffixIconConstraints: suffixConstraints,
|
||||
isDense: true,
|
||||
contentPadding: contentPadding ??
|
||||
EdgeInsets.only(
|
||||
left: 6.h,
|
||||
top: 6.v,
|
||||
bottom: 6.v,
|
||||
_buildDropDownWidget(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: width ?? double.maxFinite,
|
||||
child: DropdownButtonFormField<SelectionPopupModel>(
|
||||
focusNode: focusNode ?? FocusNode(),
|
||||
icon: icon,
|
||||
autofocus: autofocus!,
|
||||
style: textStyle ?? Theme.of(context).textTheme.titleMedium,
|
||||
items: items?.map((SelectionPopupModel item) {
|
||||
return DropdownMenuItem<SelectionPopupModel>(
|
||||
value: item,
|
||||
child: Text(
|
||||
item.title,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: hintStyle ?? Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
fillColor: fillColor ?? theme.colorScheme.onPrimary,
|
||||
filled: filled,
|
||||
border: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: appTheme.blueA400,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
enabledBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: appTheme.blueA400,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
focusedBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: appTheme.blueA400,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
);
|
||||
);
|
||||
}).toList(),
|
||||
decoration: _buildInputDecoration(context),
|
||||
validator: validator,
|
||||
onChanged: onDropDownChanged,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void onDropDownChanged(value) {
|
||||
onChanged!(value!);
|
||||
}
|
||||
|
||||
InputDecoration _buildInputDecoration(BuildContext context) {
|
||||
return InputDecoration(
|
||||
hintText: hintText ?? "",
|
||||
hintStyle: hintStyle ?? Theme.of(context).textTheme.titleMedium,
|
||||
prefixIcon: prefix,
|
||||
prefixIconConstraints: prefixConstraints,
|
||||
suffixIcon: suffix,
|
||||
suffixIconConstraints: suffixConstraints,
|
||||
isDense: true,
|
||||
contentPadding: contentPadding ??
|
||||
EdgeInsets.only(
|
||||
left: 6,
|
||||
top: 6,
|
||||
bottom: 6,
|
||||
),
|
||||
fillColor: fillColor ?? Theme.of(context).colorScheme.onPrimary,
|
||||
filled: filled,
|
||||
border: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
borderSide: BorderSide(color: appTheme.blueA400, width: 1),
|
||||
),
|
||||
enabledBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
borderSide: BorderSide(color: appTheme.blueA400, width: 1),
|
||||
),
|
||||
focusedBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
borderSide: BorderSide(color: appTheme.blueA400, width: 1),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,75 +73,73 @@ class CustomDropDown extends StatelessWidget {
|
||||
return alignment != null
|
||||
? Align(
|
||||
alignment: alignment ?? Alignment.center,
|
||||
child: dropDownWidget,
|
||||
child: _buildDropDownWidget(context),
|
||||
)
|
||||
: dropDownWidget;
|
||||
: _buildDropDownWidget(context);
|
||||
}
|
||||
|
||||
Widget get dropDownWidget => SizedBox(
|
||||
width: width ?? double.maxFinite,
|
||||
child: DropdownButtonFormField<SelectionPopupModel>(
|
||||
focusNode: focusNode ?? FocusNode(),
|
||||
icon: icon,
|
||||
autofocus: autofocus!,
|
||||
style: textStyle ?? theme.textTheme.titleMedium,
|
||||
items: items?.map((SelectionPopupModel item) {
|
||||
return DropdownMenuItem<SelectionPopupModel>(
|
||||
value: item,
|
||||
child: Text(
|
||||
item.title,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: hintStyle ?? theme.textTheme.titleMedium,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
decoration: decoration,
|
||||
validator: validator,
|
||||
value: defaultValue,
|
||||
onChanged: (value) {
|
||||
onChanged!(value!);
|
||||
},
|
||||
),
|
||||
);
|
||||
InputDecoration get decoration => InputDecoration(
|
||||
hintText: hintText ?? "",
|
||||
hintStyle: hintStyle ?? theme.textTheme.titleMedium,
|
||||
prefixIcon: prefix,
|
||||
prefixIconConstraints: prefixConstraints,
|
||||
suffixIcon: suffix,
|
||||
suffixIconConstraints: suffixConstraints,
|
||||
isDense: true,
|
||||
contentPadding: contentPadding ??
|
||||
EdgeInsets.only(
|
||||
left: 6.h,
|
||||
top: 6.v,
|
||||
bottom: 6.v,
|
||||
_buildDropDownWidget(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: width ?? double.maxFinite,
|
||||
child: DropdownButtonFormField<SelectionPopupModel>(
|
||||
focusNode: focusNode ?? FocusNode(),
|
||||
icon: icon,
|
||||
autofocus: autofocus!,
|
||||
style: textStyle ?? Theme.of(context).textTheme.titleMedium,
|
||||
items: items?.map((SelectionPopupModel item) {
|
||||
return DropdownMenuItem<SelectionPopupModel>(
|
||||
value: item,
|
||||
child: Text(
|
||||
item.title,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: hintStyle ?? Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
fillColor: fillColor ?? theme.colorScheme.onPrimary,
|
||||
filled: filled,
|
||||
border: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: Colors.grey,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
enabledBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: Colors.grey,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
focusedBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: Colors.grey,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
);
|
||||
);
|
||||
}).toList(),
|
||||
decoration: _buildInputDecoration(context),
|
||||
validator: validator,
|
||||
value: defaultValue,
|
||||
onChanged: onDropDownChanged,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void onDropDownChanged(value) {
|
||||
onChanged!(value!);
|
||||
}
|
||||
|
||||
InputDecoration _buildInputDecoration(BuildContext context) {
|
||||
return InputDecoration(
|
||||
hintText: hintText ?? "",
|
||||
hintStyle: hintStyle ?? Theme.of(context).textTheme.titleMedium,
|
||||
prefixIcon: prefix,
|
||||
prefixIconConstraints: prefixConstraints,
|
||||
suffixIcon: suffix,
|
||||
suffixIconConstraints: suffixConstraints,
|
||||
isDense: true,
|
||||
contentPadding: contentPadding ??
|
||||
EdgeInsets.only(
|
||||
left: 6,
|
||||
top: 6,
|
||||
bottom: 6,
|
||||
),
|
||||
fillColor: fillColor ?? Theme.of(context).colorScheme.onPrimary,
|
||||
filled: filled,
|
||||
border: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
borderSide: BorderSide(color: Colors.grey, width: 1),
|
||||
),
|
||||
enabledBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
borderSide: BorderSide(color: Colors.grey, width: 1),
|
||||
),
|
||||
focusedBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
borderSide: BorderSide(color: Colors.grey, width: 1),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:eatvio_app/core/app_export.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
@@ -92,61 +91,60 @@ class CustomTextFormField extends StatelessWidget {
|
||||
return alignment != null
|
||||
? Align(
|
||||
alignment: alignment ?? Alignment.center,
|
||||
child: textFormFieldWidget,
|
||||
child: _buildTextFormFieldWidget(context),
|
||||
)
|
||||
: textFormFieldWidget;
|
||||
: _buildTextFormFieldWidget(context);
|
||||
}
|
||||
|
||||
Widget get textFormFieldWidget => SizedBox(
|
||||
width: width ?? double.maxFinite,
|
||||
child: TextFormField(
|
||||
controller: controller,
|
||||
focusNode: focusNode ?? FocusNode(),
|
||||
autofocus: autofocus!,
|
||||
style: textStyle ?? theme.textTheme.titleMedium,
|
||||
obscureText: obscureText!,
|
||||
textInputAction: textInputAction,
|
||||
maxLines: maxLines ?? 1,
|
||||
decoration: decoration,
|
||||
validator: validator,
|
||||
keyboardType: keyboardType,
|
||||
onSaved: onSaved,
|
||||
onChanged: onChanged,
|
||||
inputFormatters: inputFormatters,
|
||||
initialValue: initialValue,
|
||||
),
|
||||
);
|
||||
InputDecoration get decoration => InputDecoration(
|
||||
hintText: hintText ?? "",
|
||||
hintStyle: hintStyle ?? theme.textTheme.bodyLarge,
|
||||
prefixIcon: prefix,
|
||||
prefixIconConstraints: prefixConstraints,
|
||||
suffixIcon: suffix,
|
||||
suffixIconConstraints: suffixConstraints,
|
||||
isDense: true,
|
||||
contentPadding: contentPadding ?? EdgeInsets.all(6.h),
|
||||
fillColor: fillColor ?? theme.colorScheme.onPrimary,
|
||||
filled: filled,
|
||||
border: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(color: Colors.grey, width: 1),
|
||||
),
|
||||
enabledBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: Colors.grey,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
focusedBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: Colors.grey,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
);
|
||||
_buildTextFormFieldWidget(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: width ?? double.maxFinite,
|
||||
child: TextFormField(
|
||||
controller: controller,
|
||||
focusNode: focusNode ?? FocusNode(),
|
||||
autofocus: autofocus!,
|
||||
style: textStyle ?? Theme.of(context).textTheme.titleMedium,
|
||||
obscureText: obscureText!,
|
||||
textInputAction: textInputAction,
|
||||
maxLines: maxLines ?? 1,
|
||||
decoration: _buildInputDecoration(context),
|
||||
validator: validator,
|
||||
keyboardType: keyboardType,
|
||||
onSaved: onSaved,
|
||||
onChanged: onChanged,
|
||||
inputFormatters: inputFormatters,
|
||||
initialValue: initialValue,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
InputDecoration _buildInputDecoration(BuildContext context) {
|
||||
return InputDecoration(
|
||||
hintText: hintText ?? "",
|
||||
hintStyle: hintStyle ?? Theme.of(context).textTheme.bodyLarge,
|
||||
prefixIcon: prefix,
|
||||
prefixIconConstraints: prefixConstraints,
|
||||
suffixIcon: suffix,
|
||||
suffixIconConstraints: suffixConstraints,
|
||||
isDense: true,
|
||||
contentPadding: contentPadding ?? EdgeInsets.all(6),
|
||||
fillColor: fillColor ?? Theme.of(context).colorScheme.onPrimary,
|
||||
filled: filled,
|
||||
border: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
borderSide: BorderSide(color: Colors.grey, width: 1),
|
||||
),
|
||||
enabledBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
borderSide: BorderSide(color: Colors.grey, width: 1),
|
||||
),
|
||||
focusedBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
borderSide: BorderSide(color: Colors.grey, width: 1),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
360
lib/widgets/custom_expandable_list.dart
Normal file
360
lib/widgets/custom_expandable_list.dart
Normal file
@@ -0,0 +1,360 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:eatvio_app/core/app_export.dart';
|
||||
import 'package:eatvio_app/presentation/profile/my_profile_screen/bloc/my_profile_bloc.dart';
|
||||
import 'package:eatvio_app/widgets/custom_hyper_switch.dart';
|
||||
import 'package:eatvio_app/widgets/custom_hypo_switch.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:eatvio_app/widgets/next_step_button.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
class ExpandableList extends StatefulWidget {
|
||||
final String title;
|
||||
|
||||
ExpandableList(this.title);
|
||||
|
||||
@override
|
||||
_ExpandableListState createState() => _ExpandableListState();
|
||||
}
|
||||
|
||||
class _ExpandableListState extends State<ExpandableList> {
|
||||
bool isExpanded = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: toggleExpand(),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
widget.title,
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
Icon(isExpanded ? Icons.arrow_drop_up : Icons.arrow_drop_down),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (isExpanded)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0),
|
||||
child: BlocBuilder<MyProfileBloc, MyProfileState>(
|
||||
builder: (context, state) {
|
||||
if (state is MyProfileInitialState) {
|
||||
// Это начальное состояние, данные ещё не загружены.
|
||||
return CircularProgressIndicator(
|
||||
color: Colors.white,
|
||||
backgroundColor: Colors.white,
|
||||
);
|
||||
} else if (state is MyProfileLoadedState) {
|
||||
// Данные загружены, можно строить форму.
|
||||
return _buildForm(context, state);
|
||||
} else if (state is MyProfileErrorState) {
|
||||
// Произошла ошибка при загрузке данных.
|
||||
return Center(child: Text(state.errorMessage));
|
||||
} else {
|
||||
// Другие состояния, если они есть.
|
||||
return Container(); // Пустой контейнер для необработанных состояний.
|
||||
}
|
||||
}),
|
||||
),
|
||||
Divider(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
toggleExpand() {
|
||||
return () {
|
||||
setState(() {
|
||||
isExpanded = !isExpanded;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
_buildForm(BuildContext context, MyProfileLoadedState state) {
|
||||
List<String> cities = [
|
||||
'Москва',
|
||||
'Санкт-Петербург',
|
||||
'Новосибирск',
|
||||
'Екатеринбург',
|
||||
'Нижний Новгород',
|
||||
'Казань',
|
||||
'Челябинск',
|
||||
'Омск',
|
||||
'Самара',
|
||||
'Ростов-на-Дону',
|
||||
'Уфа',
|
||||
'Красноярск',
|
||||
'Воронеж',
|
||||
'Пермь',
|
||||
'Волгоград',
|
||||
'Краснодар',
|
||||
'Саратов',
|
||||
'Тюмень',
|
||||
'Тольятти',
|
||||
'Ижевск',
|
||||
'Барнаул',
|
||||
'Ульяновск',
|
||||
'Иркутск',
|
||||
'Хабаровск',
|
||||
'Ярославль',
|
||||
'Владивосток',
|
||||
'Махачкала',
|
||||
'Томск',
|
||||
'Оренбург',
|
||||
'Кемерово',
|
||||
];
|
||||
List<String> countries = [
|
||||
'Россия',
|
||||
];
|
||||
final digitsOnly = FilteringTextInputFormatter.digitsOnly;
|
||||
return Column(children: [
|
||||
TextField(
|
||||
controller: state.firstNameFormController,
|
||||
decoration: InputDecoration(labelText: 'first_name'.tr),
|
||||
),
|
||||
TextField(
|
||||
controller: state.lastNameFormController,
|
||||
decoration: InputDecoration(labelText: 'last_name'.tr),
|
||||
),
|
||||
TextField(
|
||||
decoration: InputDecoration(labelText: 'date_of_birth'.tr),
|
||||
controller: state.dateFormController,
|
||||
onTap: inputTime(context, state)),
|
||||
DropdownButtonFormField<String>(
|
||||
value: state.selectedCountry,
|
||||
hint: Text(state.selectedCountry ?? "Не выбрано"),
|
||||
onChanged: setCountry(state),
|
||||
items: countries.map<DropdownMenuItem<String>>((String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value),
|
||||
);
|
||||
}).toList(),
|
||||
decoration: InputDecoration(labelText: 'country'.tr),
|
||||
),
|
||||
DropdownButtonFormField<String>(
|
||||
hint: Text(state.selectedCity ?? "Не выбрано"),
|
||||
value: state.selectedCity,
|
||||
onChanged: setCity(state),
|
||||
items: cities.map<DropdownMenuItem<String>>((String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value),
|
||||
);
|
||||
}).toList(),
|
||||
decoration: InputDecoration(labelText: 'city_of_living'.tr),
|
||||
),
|
||||
TextField(
|
||||
controller: state.weightFormController,
|
||||
decoration: InputDecoration(labelText: 'weight_measurements'.tr),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[digitsOnly]),
|
||||
TextField(
|
||||
controller: state.heightFormController,
|
||||
decoration: InputDecoration(labelText: 'height_measurements'.tr),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[digitsOnly]),
|
||||
TextField(
|
||||
controller: state.chestCircumferenceFormController,
|
||||
decoration: InputDecoration(labelText: 'chest_circumference'.tr),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[digitsOnly]),
|
||||
TextField(
|
||||
controller: state.waistCircumferenceFormController,
|
||||
decoration: InputDecoration(labelText: 'waist_circumference'.tr),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[digitsOnly]),
|
||||
TextField(
|
||||
controller: state.hipCircumferenceFormController,
|
||||
decoration: InputDecoration(labelText: 'hip_circumference'.tr),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[digitsOnly]),
|
||||
TextField(
|
||||
controller: state.upPressureFormController,
|
||||
decoration: InputDecoration(labelText: 'up_pressure'.tr),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[digitsOnly]),
|
||||
TextField(
|
||||
controller: state.downPressureFormController,
|
||||
decoration: InputDecoration(labelText: 'down_pressure'.tr),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[digitsOnly]),
|
||||
TextField(
|
||||
controller: state.allergiesFormController,
|
||||
decoration: InputDecoration(labelText: 'allergies_presence'.tr),
|
||||
maxLines: null,
|
||||
),
|
||||
TextField(
|
||||
controller: state.pillsFormController,
|
||||
decoration: InputDecoration(labelText: 'do_you_take_pills'.tr),
|
||||
maxLines: null,
|
||||
),
|
||||
TextField(
|
||||
controller: state.sportsFormController,
|
||||
decoration: InputDecoration(labelText: 'your_favorite_sports'.tr),
|
||||
maxLines: null,
|
||||
),
|
||||
TextField(
|
||||
controller: state.favoriteFormController,
|
||||
decoration: InputDecoration(labelText: 'favorite_foods'.tr),
|
||||
maxLines: null,
|
||||
),
|
||||
TextField(
|
||||
controller: state.unfavoriteFormController,
|
||||
decoration: InputDecoration(labelText: 'unfavorite_foods'.tr),
|
||||
maxLines: null,
|
||||
),
|
||||
TextField(
|
||||
controller: state.additionalFormController,
|
||||
decoration: InputDecoration(labelText: 'additional_information'.tr),
|
||||
maxLines: null,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
HypoSwitch('do_you_have_hypotension_tendency'.tr, state),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
HyperSwitch('do_you_have_hypertension_tendency'.tr, state),
|
||||
],
|
||||
),
|
||||
NextStepButton(
|
||||
onPressed: sendData(state),
|
||||
buttonStyle: CustomButtonStyles.outlinePrimaryTL15,
|
||||
text: "save".tr.toUpperCase(),
|
||||
margin: EdgeInsets.only(top: 20.h, bottom: 20.h)),
|
||||
]);
|
||||
}
|
||||
|
||||
sendData(MyProfileLoadedState state) {
|
||||
return () {
|
||||
sendProfileData(state);
|
||||
};
|
||||
}
|
||||
|
||||
setCity(MyProfileLoadedState state) {
|
||||
return (newValue) {
|
||||
// Обработчик изменения выбранного города
|
||||
setSelectedCity(state, newValue);
|
||||
};
|
||||
}
|
||||
|
||||
setCountry(MyProfileLoadedState state) {
|
||||
return (newValue) {
|
||||
// Обработчик изменения выбранной страны
|
||||
setSelectedCountry(state, newValue);
|
||||
};
|
||||
}
|
||||
|
||||
inputTime(BuildContext context, MyProfileLoadedState state) {
|
||||
return () {
|
||||
showDatePicker(
|
||||
context: context,
|
||||
initialDate: DateTime.now(),
|
||||
firstDate: DateTime(1900),
|
||||
lastDate: DateTime.now(),
|
||||
).then((selectedDate) {
|
||||
if (selectedDate != null) {
|
||||
setState(() {
|
||||
state.dateFormController!.text =
|
||||
DateFormat('dd-MM-yyyy').format(selectedDate);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
void setSelectedCity(MyProfileLoadedState? state, String? newValue) {
|
||||
setState(() {
|
||||
state!.selectedCity = newValue;
|
||||
});
|
||||
}
|
||||
|
||||
void setSelectedCountry(MyProfileLoadedState? state, String? newValue) {
|
||||
setState(() {
|
||||
state!.selectedCountry = newValue;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> sendProfileData(MyProfileLoadedState? state) async {
|
||||
try {
|
||||
// Создаем JSON объект с данными профиля
|
||||
Map<String, dynamic> profileData = {
|
||||
'first_name': state?.firstNameFormController?.text,
|
||||
'last_name': state?.lastNameFormController?.text,
|
||||
'chest_circumference': state?.chestCircumferenceFormController?.text,
|
||||
'waist_circumference': state?.waistCircumferenceFormController?.text,
|
||||
'hip_circumference': state?.hipCircumferenceFormController?.text,
|
||||
'birthdate': state?.dateFormController?.text,
|
||||
'height': state?.heightFormController?.text,
|
||||
'weight': state?.weightFormController?.text,
|
||||
'blood_pressure_up': state?.upPressureFormController?.text,
|
||||
'blood_pressure_down': state?.downPressureFormController?.text,
|
||||
'about_alergy': state?.allergiesFormController?.text,
|
||||
'about_taking_pils': state?.pillsFormController?.text,
|
||||
'about_sport_hobbies': state?.sportsFormController?.text,
|
||||
'favorite_foods': state?.favoriteFormController?.text,
|
||||
'unfavorite_foods': state?.unfavoriteFormController?.text,
|
||||
'additional_info': state?.additionalFormController?.text,
|
||||
'country': state?.selectedCountry ?? 'Россия',
|
||||
'city': state?.selectedCity ?? 'Москва',
|
||||
'has_hypo': state?.hypoFormController,
|
||||
'has_hyper': state?.hyperFormController,
|
||||
};
|
||||
final box = await Hive.openBox('token');
|
||||
final token = box.get('token');
|
||||
String apiUrl = dotenv.env['SERVER_ROOT']! + '/profile/update';
|
||||
http.Response response = await http.post(
|
||||
Uri.parse(apiUrl),
|
||||
body: json.encode(profileData),
|
||||
encoding: utf8,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
},
|
||||
);
|
||||
|
||||
print(response.body);
|
||||
if (response.statusCode == 200) {
|
||||
Navigator.pushNamed(
|
||||
context,
|
||||
AppRoutes.myProfile,
|
||||
);
|
||||
} else {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text('Ошибка'),
|
||||
content: Text('Ошибка при отправке данных профиля'.tr),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: onTapOk(context),
|
||||
child: Text('OK'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
throw Exception('Ошибка при отправке данных профиля');
|
||||
}
|
||||
} catch (error) {
|
||||
throw Exception('Ошибка при отправке данных профиля: $error');
|
||||
}
|
||||
}
|
||||
|
||||
onTapOk(BuildContext context) {
|
||||
return () {
|
||||
Navigator.pop(context);
|
||||
};
|
||||
}
|
||||
}
|
||||
55
lib/widgets/custom_hyper_switch.dart
Normal file
55
lib/widgets/custom_hyper_switch.dart
Normal file
@@ -0,0 +1,55 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:eatvio_app/core/app_export.dart';
|
||||
import 'package:eatvio_app/presentation/profile/my_profile_screen/bloc/my_profile_bloc.dart';
|
||||
|
||||
class HyperSwitch extends StatefulWidget {
|
||||
final String title;
|
||||
final MyProfileLoadedState? state;
|
||||
HyperSwitch(this.title, this.state);
|
||||
|
||||
@override
|
||||
_HyperState createState() => _HyperState();
|
||||
}
|
||||
|
||||
class _HyperState extends State<HyperSwitch> {
|
||||
bool switchValue = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.state?.hyperFormController == '1') {
|
||||
switchValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
widget.title,
|
||||
style: TextStyle(
|
||||
color: Colors.black, fontSize: 14, fontWeight: FontWeight.bold),
|
||||
),
|
||||
Switch(
|
||||
value: switchValue,
|
||||
onChanged: toggleSwitch(),
|
||||
inactiveThumbColor: appTheme.blueA400,
|
||||
inactiveTrackColor: Colors.white,
|
||||
activeTrackColor: appTheme.blueA400,
|
||||
activeColor: Colors.white,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
toggleSwitch() {
|
||||
return (value) {
|
||||
setState(() {
|
||||
switchValue = value;
|
||||
widget.state!.hyperFormController = value ? "1" : "0";
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
55
lib/widgets/custom_hypo_switch.dart
Normal file
55
lib/widgets/custom_hypo_switch.dart
Normal file
@@ -0,0 +1,55 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:eatvio_app/core/app_export.dart';
|
||||
import 'package:eatvio_app/presentation/profile/my_profile_screen/bloc/my_profile_bloc.dart';
|
||||
|
||||
class HypoSwitch extends StatefulWidget {
|
||||
final String title;
|
||||
final MyProfileLoadedState? state;
|
||||
HypoSwitch(this.title, this.state);
|
||||
|
||||
@override
|
||||
_HypoState createState() => _HypoState();
|
||||
}
|
||||
|
||||
class _HypoState extends State<HypoSwitch> {
|
||||
bool switchValue = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.state?.hypoFormController == '1') {
|
||||
switchValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
widget.title,
|
||||
style: TextStyle(
|
||||
color: Colors.black, fontSize: 14, fontWeight: FontWeight.bold),
|
||||
),
|
||||
Switch(
|
||||
value: switchValue,
|
||||
onChanged: toggleSwitch(),
|
||||
inactiveThumbColor: appTheme.blueA400,
|
||||
inactiveTrackColor: Colors.white,
|
||||
activeTrackColor: appTheme.blueA400,
|
||||
activeColor: Colors.white,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
toggleSwitch() {
|
||||
return (value) {
|
||||
setState(() {
|
||||
switchValue = value;
|
||||
widget.state!.hypoFormController = value ? "1" : "0";
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -10,22 +10,14 @@ class CustomIconButton extends StatelessWidget {
|
||||
this.decoration,
|
||||
this.child,
|
||||
this.onTap,
|
||||
}) : super(
|
||||
key: key,
|
||||
);
|
||||
}) : super(key: key);
|
||||
|
||||
final Alignment? alignment;
|
||||
|
||||
final double? height;
|
||||
|
||||
final double? width;
|
||||
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
final BoxDecoration? decoration;
|
||||
|
||||
final Widget? child;
|
||||
|
||||
final VoidCallback? onTap;
|
||||
|
||||
@override
|
||||
@@ -33,40 +25,40 @@ class CustomIconButton extends StatelessWidget {
|
||||
return alignment != null
|
||||
? Align(
|
||||
alignment: alignment ?? Alignment.center,
|
||||
child: iconButtonWidget,
|
||||
child: _buildIconButtonWidget(),
|
||||
)
|
||||
: iconButtonWidget;
|
||||
: _buildIconButtonWidget();
|
||||
}
|
||||
|
||||
Widget get iconButtonWidget => SizedBox(
|
||||
height: height ?? 0,
|
||||
width: width ?? 0,
|
||||
child: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
icon: Container(
|
||||
height: height ?? 0,
|
||||
width: width ?? 0,
|
||||
padding: padding ?? EdgeInsets.zero,
|
||||
decoration: decoration ??
|
||||
BoxDecoration(
|
||||
color: Colors.black,
|
||||
borderRadius: BorderRadius.circular(9),
|
||||
border: Border.all(
|
||||
color: Colors.blueGrey,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
onPressed: onTap,
|
||||
_buildIconButtonWidget() {
|
||||
return SizedBox(
|
||||
height: height ?? 0,
|
||||
width: width ?? 0,
|
||||
child: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
icon: Container(
|
||||
height: height ?? 0,
|
||||
width: width ?? 0,
|
||||
padding: padding ?? EdgeInsets.zero,
|
||||
decoration: decoration ??
|
||||
BoxDecoration(
|
||||
color: Colors.black,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(9)),
|
||||
border: const Border.fromBorderSide(
|
||||
BorderSide(color: Colors.blueGrey, width: 1)),
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
onPressed: onTap,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension on [CustomIconButton] to facilitate inclusion of all types of border style etc
|
||||
extension IconButtonStyleHelper on CustomIconButton {
|
||||
static BoxDecoration get fillBlueA => BoxDecoration(
|
||||
color: Colors.blue,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(14)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -42,19 +42,21 @@ class CustomImageView extends StatelessWidget {
|
||||
return alignment != null
|
||||
? Align(
|
||||
alignment: alignment!,
|
||||
child: _buildWidget(),
|
||||
child: Padding(
|
||||
padding: margin ?? EdgeInsets.zero,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
child: _buildCircleImage(),
|
||||
),
|
||||
),
|
||||
)
|
||||
: _buildWidget();
|
||||
}
|
||||
|
||||
Widget _buildWidget() {
|
||||
return Padding(
|
||||
padding: margin ?? EdgeInsets.zero,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
child: _buildCircleImage(),
|
||||
),
|
||||
);
|
||||
: Padding(
|
||||
padding: margin ?? EdgeInsets.zero,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
child: _buildCircleImage(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
///build the image with border radius
|
||||
@@ -84,7 +86,7 @@ class CustomImageView extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildImageView() {
|
||||
_buildImageView() {
|
||||
if (imagePath != null) {
|
||||
switch (imagePath!.imageType) {
|
||||
case ImageType.svg:
|
||||
@@ -96,7 +98,6 @@ class CustomImageView extends StatelessWidget {
|
||||
height: height,
|
||||
width: width,
|
||||
fit: fit ?? BoxFit.contain,
|
||||
color: color,
|
||||
),
|
||||
);
|
||||
case ImageType.file:
|
||||
|
||||
48
lib/widgets/custom_language_drop_down.dart
Normal file
48
lib/widgets/custom_language_drop_down.dart
Normal file
@@ -0,0 +1,48 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LanguageDropdown extends StatefulWidget {
|
||||
@override
|
||||
_LanguageDropdownState createState() => _LanguageDropdownState();
|
||||
}
|
||||
|
||||
class _LanguageDropdownState extends State<LanguageDropdown> {
|
||||
String selectedLanguage = 'Русский';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DropdownButton<String>(
|
||||
value: selectedLanguage,
|
||||
onChanged: selectLanguage(),
|
||||
isDense: false, // Установите isDense в false
|
||||
selectedItemBuilder: getList(),
|
||||
items: ['Русский'].map((String language) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: language,
|
||||
child: Text(language),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
getList() {
|
||||
return (BuildContext context) {
|
||||
return ['Русский'].map<Widget>((String language) {
|
||||
return Align(
|
||||
alignment: Alignment.center, // Выравнивание текста по центру
|
||||
child: Text(
|
||||
language,
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
};
|
||||
}
|
||||
|
||||
selectLanguage() {
|
||||
return (newValue) {
|
||||
setState(() {
|
||||
selectedLanguage = newValue!;
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
45
lib/widgets/custom_notification_screen.dart
Normal file
45
lib/widgets/custom_notification_screen.dart
Normal file
@@ -0,0 +1,45 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:eatvio_app/core/app_export.dart';
|
||||
|
||||
class NotificationSwitch extends StatefulWidget {
|
||||
final String title;
|
||||
|
||||
NotificationSwitch(this.title);
|
||||
|
||||
@override
|
||||
_NotificationSwitchState createState() => _NotificationSwitchState();
|
||||
}
|
||||
|
||||
class _NotificationSwitchState extends State<NotificationSwitch> {
|
||||
bool switchValue = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
widget.title,
|
||||
style: TextStyle(
|
||||
color: Colors.black, fontSize: 14, fontWeight: FontWeight.bold),
|
||||
),
|
||||
Switch(
|
||||
value: switchValue,
|
||||
onChanged: toggleSwitch(),
|
||||
inactiveThumbColor: appTheme.blueA400,
|
||||
inactiveTrackColor: Colors.white,
|
||||
activeTrackColor: appTheme.blueA400,
|
||||
activeColor: Colors.white,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
toggleSwitch() {
|
||||
return (value) {
|
||||
setState(() {
|
||||
switchValue = value;
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -31,43 +31,86 @@ class CustomOutlinedButton extends BaseButton {
|
||||
);
|
||||
|
||||
final BoxDecoration? decoration;
|
||||
|
||||
final Widget? leftIcon;
|
||||
|
||||
final Widget? rightIcon;
|
||||
|
||||
final Widget? label;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return alignment != null
|
||||
? Align(
|
||||
alignment: alignment ?? Alignment.center,
|
||||
child: buildOutlinedButtonWidget,
|
||||
)
|
||||
: buildOutlinedButtonWidget;
|
||||
}
|
||||
|
||||
Widget get buildOutlinedButtonWidget => Container(
|
||||
if (alignment != null) {
|
||||
return Align(
|
||||
alignment: alignment ?? Alignment.center,
|
||||
child: Container(
|
||||
height: this.height ?? 30.v,
|
||||
width: this.width ?? double.maxFinite,
|
||||
margin: margin,
|
||||
decoration: decoration,
|
||||
child: OutlinedButton(
|
||||
style: buttonStyle!,
|
||||
onPressed: isDisabled ?? false ? null : onPressed ?? () {},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
leftIcon ?? SizedBox.shrink(),
|
||||
Text(
|
||||
text,
|
||||
style: buttonTextStyle ?? CustomTextStyles.titleSmallBold,
|
||||
),
|
||||
rightIcon ?? SizedBox.shrink(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Container(
|
||||
height: this.height ?? 30.v,
|
||||
width: this.width ?? double.maxFinite,
|
||||
margin: margin,
|
||||
decoration: decoration,
|
||||
child: OutlinedButton(
|
||||
style: buttonStyle,
|
||||
style: buttonStyle!,
|
||||
onPressed: isDisabled ?? false ? null : onPressed ?? () {},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
leftIcon ?? const SizedBox.shrink(),
|
||||
leftIcon ?? SizedBox.shrink(),
|
||||
Text(
|
||||
text,
|
||||
style: buttonTextStyle ?? CustomTextStyles.titleSmallBold,
|
||||
),
|
||||
rightIcon ?? const SizedBox.shrink(),
|
||||
rightIcon ?? SizedBox.shrink(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/*_buildAppBarbuildOutlinedButtonWidget() {
|
||||
return Container(
|
||||
height: this.height ?? 30.v,
|
||||
width: this.width ?? double.maxFinite,
|
||||
margin: margin,
|
||||
decoration: decoration,
|
||||
child: OutlinedButton(
|
||||
style: buttonStyle!,
|
||||
onPressed: isDisabled ?? false ? null : onPressed ?? () {},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
leftIcon ?? SizedBox.shrink(),
|
||||
Text(
|
||||
text,
|
||||
style: buttonTextStyle ?? CustomTextStyles.titleSmallBold,
|
||||
),
|
||||
rightIcon ?? SizedBox.shrink(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}*/
|
||||
}
|
||||
|
||||
19
lib/widgets/custom_section_title.dart
Normal file
19
lib/widgets/custom_section_title.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SectionTitle extends StatelessWidget {
|
||||
final String title;
|
||||
|
||||
SectionTitle(this.title);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 18),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -34,51 +34,28 @@ class CustomTextFormField extends StatelessWidget {
|
||||
);
|
||||
|
||||
final Alignment? alignment;
|
||||
|
||||
final double? width;
|
||||
|
||||
final TextEditingController? controller;
|
||||
|
||||
final FocusNode? focusNode;
|
||||
|
||||
final bool? autofocus;
|
||||
|
||||
final TextStyle? textStyle;
|
||||
|
||||
final bool? obscureText;
|
||||
|
||||
final List<TextInputFormatter>? inputFormatters;
|
||||
|
||||
final TextInputAction? textInputAction;
|
||||
|
||||
final TextInputType? textInputType;
|
||||
|
||||
final int? maxLines;
|
||||
|
||||
final String? hintText;
|
||||
|
||||
final TextStyle? hintStyle;
|
||||
|
||||
final Widget? prefix;
|
||||
|
||||
final BoxConstraints? prefixConstraints;
|
||||
|
||||
final Widget? suffix;
|
||||
|
||||
final BoxConstraints? suffixConstraints;
|
||||
|
||||
final EdgeInsets? contentPadding;
|
||||
|
||||
final InputBorder? borderDecoration;
|
||||
|
||||
final Color? fillColor;
|
||||
|
||||
final bool? filled;
|
||||
|
||||
final TextInputType? keyboardType;
|
||||
|
||||
final FormFieldValidator<String>? validator;
|
||||
|
||||
final void Function(String?)? onSaved;
|
||||
|
||||
@override
|
||||
@@ -86,62 +63,149 @@ class CustomTextFormField extends StatelessWidget {
|
||||
return alignment != null
|
||||
? Align(
|
||||
alignment: alignment ?? Alignment.center,
|
||||
child: textFormFieldWidget,
|
||||
child: SizedBox(
|
||||
width: width ?? double.maxFinite,
|
||||
child: TextFormField(
|
||||
controller: controller,
|
||||
focusNode: focusNode ?? FocusNode(),
|
||||
autofocus: autofocus!,
|
||||
style: textStyle ?? theme.textTheme.titleMedium,
|
||||
obscureText: obscureText!,
|
||||
textInputAction: textInputAction,
|
||||
maxLines: maxLines ?? 1,
|
||||
decoration: InputDecoration(
|
||||
hintText: hintText ?? "",
|
||||
hintStyle: hintStyle ?? theme.textTheme.bodyLarge,
|
||||
prefixIcon: prefix,
|
||||
prefixIconConstraints: prefixConstraints,
|
||||
suffixIcon: suffix,
|
||||
suffixIconConstraints: suffixConstraints,
|
||||
isDense: true,
|
||||
contentPadding: contentPadding ?? EdgeInsets.all(6.h),
|
||||
fillColor: fillColor ?? theme.colorScheme.onPrimary,
|
||||
filled: filled,
|
||||
border: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: appTheme.blueA400,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
enabledBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: appTheme.blueA400,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
focusedBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: appTheme.blueA400,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
keyboardType: keyboardType,
|
||||
validator: validator,
|
||||
onSaved: onSaved,
|
||||
inputFormatters: inputFormatters,
|
||||
),
|
||||
),
|
||||
)
|
||||
: textFormFieldWidget;
|
||||
: SizedBox(
|
||||
width: width ?? double.maxFinite,
|
||||
child: TextFormField(
|
||||
controller: controller,
|
||||
focusNode: focusNode ?? FocusNode(),
|
||||
autofocus: autofocus!,
|
||||
style: textStyle ?? theme.textTheme.titleMedium,
|
||||
obscureText: obscureText!,
|
||||
textInputAction: textInputAction,
|
||||
maxLines: maxLines ?? 1,
|
||||
decoration: InputDecoration(
|
||||
hintText: hintText ?? "",
|
||||
hintStyle: hintStyle ?? theme.textTheme.bodyLarge,
|
||||
prefixIcon: prefix,
|
||||
prefixIconConstraints: prefixConstraints,
|
||||
suffixIcon: suffix,
|
||||
suffixIconConstraints: suffixConstraints,
|
||||
isDense: true,
|
||||
contentPadding: contentPadding ?? EdgeInsets.all(6.h),
|
||||
fillColor: fillColor ?? theme.colorScheme.onPrimary,
|
||||
filled: filled,
|
||||
border: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: appTheme.blueA400,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
enabledBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: appTheme.blueA400,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
focusedBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: appTheme.blueA400,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
keyboardType: keyboardType,
|
||||
validator: validator,
|
||||
onSaved: onSaved,
|
||||
inputFormatters: inputFormatters,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget get textFormFieldWidget => SizedBox(
|
||||
width: width ?? double.maxFinite,
|
||||
child: TextFormField(
|
||||
controller: controller,
|
||||
focusNode: focusNode ?? FocusNode(),
|
||||
autofocus: autofocus!,
|
||||
style: textStyle ?? theme.textTheme.titleMedium,
|
||||
obscureText: obscureText!,
|
||||
textInputAction: textInputAction,
|
||||
maxLines: maxLines ?? 1,
|
||||
decoration: decoration,
|
||||
validator: validator,
|
||||
keyboardType: keyboardType,
|
||||
onSaved: onSaved,
|
||||
inputFormatters: inputFormatters,
|
||||
),
|
||||
);
|
||||
InputDecoration get decoration => InputDecoration(
|
||||
hintText: hintText ?? "",
|
||||
hintStyle: hintStyle ?? theme.textTheme.bodyLarge,
|
||||
prefixIcon: prefix,
|
||||
prefixIconConstraints: prefixConstraints,
|
||||
suffixIcon: suffix,
|
||||
suffixIconConstraints: suffixConstraints,
|
||||
isDense: true,
|
||||
contentPadding: contentPadding ?? EdgeInsets.all(6.h),
|
||||
fillColor: fillColor ?? theme.colorScheme.onPrimary,
|
||||
filled: filled,
|
||||
border: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: appTheme.blueA400,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
enabledBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: appTheme.blueA400,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
focusedBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: appTheme.blueA400,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
/* InputDecoration _buildInputDecoration() {
|
||||
return InputDecoration(
|
||||
hintText: hintText ?? "",
|
||||
hintStyle: hintStyle ?? theme.textTheme.bodyLarge,
|
||||
prefixIcon: prefix,
|
||||
prefixIconConstraints: prefixConstraints,
|
||||
suffixIcon: suffix,
|
||||
suffixIconConstraints: suffixConstraints,
|
||||
isDense: true,
|
||||
contentPadding: contentPadding ?? EdgeInsets.all(6.h),
|
||||
fillColor: fillColor ?? theme.colorScheme.onPrimary,
|
||||
filled: filled,
|
||||
border: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: appTheme.blueA400,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
enabledBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: appTheme.blueA400,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
focusedBorder: borderDecoration ??
|
||||
OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.h),
|
||||
borderSide: BorderSide(
|
||||
color: appTheme.blueA400,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}*/
|
||||
@@ -31,43 +31,59 @@ class NextStepButton extends BaseButton {
|
||||
);
|
||||
|
||||
final BoxDecoration? decoration;
|
||||
|
||||
final Widget? leftIcon;
|
||||
|
||||
final Widget? rightIcon;
|
||||
|
||||
final Widget? label;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return alignment != null
|
||||
return (alignment != null)
|
||||
? Align(
|
||||
alignment: alignment ?? Alignment.center,
|
||||
child: buildOutlinedButtonWidget,
|
||||
)
|
||||
: buildOutlinedButtonWidget;
|
||||
}
|
||||
|
||||
Widget get buildOutlinedButtonWidget => Container(
|
||||
height: this.height ?? 30.v,
|
||||
width: this.width ?? double.maxFinite,
|
||||
margin: margin,
|
||||
decoration: decoration,
|
||||
child: OutlinedButton(
|
||||
style: buttonStyle,
|
||||
onPressed: isDisabled ?? false ? null : onPressed ?? () {},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
leftIcon ?? const SizedBox.shrink(),
|
||||
Text(
|
||||
text,
|
||||
style: buttonTextStyle ?? theme.textTheme.titleSmall,
|
||||
alignment: alignment!,
|
||||
child: Container(
|
||||
height: this.height ?? 30.v,
|
||||
width: this.width ?? double.maxFinite,
|
||||
margin: margin,
|
||||
decoration: decoration,
|
||||
child: OutlinedButton(
|
||||
style: buttonStyle,
|
||||
onPressed: isDisabled ?? false ? null : onPressed ?? () {},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
leftIcon ?? const SizedBox.shrink(),
|
||||
Text(
|
||||
text,
|
||||
style: buttonTextStyle ?? theme.textTheme.titleSmall,
|
||||
),
|
||||
rightIcon ?? const SizedBox.shrink(),
|
||||
],
|
||||
),
|
||||
),
|
||||
rightIcon ?? const SizedBox.shrink(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
height: this.height ?? 30.v,
|
||||
width: this.width ?? double.maxFinite,
|
||||
margin: margin,
|
||||
decoration: decoration,
|
||||
child: OutlinedButton(
|
||||
style: buttonStyle,
|
||||
onPressed: isDisabled ?? false ? null : onPressed ?? () {},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
leftIcon ?? const SizedBox.shrink(),
|
||||
Text(
|
||||
text,
|
||||
style: buttonTextStyle ?? theme.textTheme.titleSmall,
|
||||
),
|
||||
rightIcon ?? const SizedBox.shrink(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
15
test/launch_test.dart
Normal file
15
test/launch_test.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:eatvio_app/main.dart' as app;
|
||||
|
||||
void main() {
|
||||
testWidgets('Smoke test', (WidgetTester tester) async {
|
||||
// Запустить приложение
|
||||
app.main();
|
||||
|
||||
// Подождать, пока приложение загрузится
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Проверить, что нет ошибок
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
// This is a basic Flutter widget test.
|
||||
//
|
||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||
// utility that Flutter provides. For example, you can send tap and scroll
|
||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||
// tree, read text, and verify that the values of widget properties are correct.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:eatvio_app/main.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||
// Build our app and trigger a frame.
|
||||
await tester.pumpWidget(MyApp());
|
||||
|
||||
// Verify that our counter starts at 0.
|
||||
expect(find.text('0'), findsOneWidget);
|
||||
expect(find.text('1'), findsNothing);
|
||||
|
||||
// Tap the '+' icon and trigger a frame.
|
||||
await tester.tap(find.byIcon(Icons.add));
|
||||
await tester.pump();
|
||||
|
||||
// Verify that our counter has incremented.
|
||||
expect(find.text('0'), findsNothing);
|
||||
expect(find.text('1'), findsOneWidget);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user