Merge remote-tracking branch 'upstream/main'

Fix # Please enter a commit message to explain why this merge is necessary,
This commit is contained in:
2024-03-11 13:18:16 +03:00
49 changed files with 2859 additions and 2354 deletions

2
.env Normal file
View File

@@ -0,0 +1,2 @@
SERVER_ROOT='https://ohngeeri3ien-dev3.eatvio.ru/api'
STORAGE_URL='https://ohngeeri3ien-dev3.eatvio.ru/storage/'

View File

@@ -1,5 +1,3 @@
//ignore: unused_import
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
class PrefUtils {

View File

@@ -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.

View File

@@ -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,

View File

@@ -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"),
];
}
}

View File

@@ -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 {

View File

@@ -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");
}
};
}
}

View File

@@ -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(),
],
);
}
}

View File

@@ -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);
};
}
}

View File

@@ -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),

View File

@@ -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),

View File

@@ -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();

View File

@@ -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();

View File

@@ -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();

View File

@@ -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,

View File

@@ -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;
});
}
}
}
}

View File

@@ -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),

View File

@@ -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),

View File

@@ -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();

View File

@@ -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();

View File

@@ -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));
}
}

View File

@@ -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),

View File

@@ -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,

View File

@@ -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));
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View 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;
});
};
}
}

View 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(),
),
);
},
),
);
}*/

View File

@@ -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,
),
),
],
),
),
);
}
}

View File

@@ -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),
),
);
}
}

View File

@@ -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),
),
);
}
}

View File

@@ -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),
),
);
}
}

View 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);
};
}
}

View 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";
});
};
}
}

View 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";
});
};
}
}

View File

@@ -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)),
);
}

View File

@@ -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:

View 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!;
});
};
}
}

View 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;
});
};
}
}

View File

@@ -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(),
],
),
),
);
}*/
}

View 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),
),
);
}
}

View File

@@ -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,
),
),
);
}
}*/

View File

@@ -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
View 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);
});
}

View File

@@ -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);
});
}