diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 29940b0..68ec53f 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -156,7 +156,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a6b826d..5e31d3d 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ MyProfileScreen(), + ), + GoRoute( + path: '/feed-screen', + name: 'feedScreen', + builder: (context, state) => FeedScreen(), + ), + GoRoute( + path: '/diary-screen', + name: 'diaryScreen', + builder: (context, state) => DiaryEditScreen(), + ), + GoRoute( + path: '/chat-select-screen', + name: 'chatSelectScreen', + builder: (context, state) => ChatSelectScreen(), + ), + ], + ), + ], + ); +} diff --git a/lib/main.dart b/lib/main.dart index 3ec0c33..e3ddb1f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,12 @@ +import 'package:eatvio_app/app_router.dart'; +import 'package:eatvio_app/presentation/chat/chat_select_screen/bloc/chat_select_bloc.dart'; +import 'package:eatvio_app/presentation/chat/chat_select_screen/models/chat_select_model.dart'; +import 'package:eatvio_app/presentation/diary/diary_edit_screen/bloc/diary_edit_bloc.dart'; +import 'package:eatvio_app/presentation/diary/diary_edit_screen/models/diary_edit_model.dart'; +import 'package:eatvio_app/presentation/feed/feed_screen/bloc/feed_bloc.dart'; +import 'package:eatvio_app/presentation/feed/feed_screen/models/feed_model.dart'; +import 'package:eatvio_app/presentation/profile/my_profile_screen/bloc/my_profile_bloc.dart'; +import 'package:eatvio_app/presentation/profile/my_profile_screen/models/my_profile_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; @@ -7,6 +16,7 @@ import 'package:path_provider/path_provider.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; var globalMessengerKey = GlobalKey(); + void main() async { WidgetsFlutterBinding.ensureInitialized(); final appDocumentDir = await getApplicationDocumentsDirectory(); @@ -25,19 +35,46 @@ void main() async { class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocProvider( - create: (context) => ThemeBloc( - ThemeState( - themeType: PrefUtils().getThemeData(), + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => ThemeBloc( + ThemeState(themeType: PrefUtils().getThemeData()), + ), ), - ), + BlocProvider( + create: (context) => MyProfileBloc( + MyProfileState( + myProfileModelObj: MyProfileModel(), + ), + )..add(MyProfileInitialEvent()), + ), + BlocProvider( + create: (context) => FeedBloc(FeedState(myProfileModelObj: FeedModel())) + ..add( + FeedInitialEvent(), + ), + ), + BlocProvider( + create: (context) => DiaryEditBloc(DiaryEditState(diaryEditModelObj: DiaryEditModel())) + ..add( + DiaryEditInitialEvent(), + ), + ), + BlocProvider( + create: (context) => ChatSelectBloc(ChatSelectState(chatSelectModelObj: ChatSelectModel())) + ..add( + ChatSelectInitialEvent(), + ), + ), + ], child: BlocBuilder( builder: (context, state) { - return MaterialApp( + return MaterialApp.router( theme: theme, title: 'Eatvio', - navigatorKey: NavigatorService.navigatorKey, - debugShowCheckedModeBanner: true, + routerConfig: AppRouter.appRouter, + debugShowCheckedModeBanner: false, localizationsDelegates: [ AppLocalizationDelegate(), GlobalMaterialLocalizations.delegate, @@ -50,8 +87,6 @@ class MyApp extends StatelessWidget { '', ), ], - initialRoute: AppRoutes.initialRoute, - routes: AppRoutes.routes, ); }, ), diff --git a/lib/presentation/auth/auth_screen/auth_screen.dart b/lib/presentation/auth/auth_screen/auth_screen.dart index e934925..558d565 100644 --- a/lib/presentation/auth/auth_screen/auth_screen.dart +++ b/lib/presentation/auth/auth_screen/auth_screen.dart @@ -30,44 +30,40 @@ class AuthScreen extends StatelessWidget { return Scaffold( resizeToAvoidBottomInset: false, appBar: _buildAppBar(context), - body:GestureDetector( - onTap: () => FocusScope.of(context).unfocus(), - child: Container( - width: double.maxFinite, - padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 9.v), - child: Column(children: [ - SizedBox(height: 36.v), - Text("user_authorization".tr, - style: theme.textTheme.headlineSmall), - SizedBox(height: 60.v), - Text("email".tr, style: theme.textTheme.bodyLarge), - SizedBox(height: 3.v), - BlocSelector( - selector: (state) => state.emailController, - builder: (context, emailController) { - return CustomTextFormField( - controller: emailController, - textInputAction: TextInputAction.done); - }), - SizedBox(height: 22.v), - Text("password".tr, style: theme.textTheme.bodyLarge), - SizedBox(height: 5.v), - BlocSelector( - selector: (state) => state.passwordController, - builder: (context, passwordController) { - return CustomTextFormField( - obscureText: true, - controller: passwordController, - ); - }), - SizedBox(height: 50.v), - NextStepButton( - text: "no_account".tr.toUpperCase(), - margin: - EdgeInsets.only(left: 16.h, right: 16.h, bottom: 50.v), - buttonStyle: CustomButtonStyles.outlinePrimaryTL15, - onPressed: onTapNoAccount(context)), - /*NextStepButton( + body: GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Container( + width: double.maxFinite, + padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 9.v), + child: Column(children: [ + SizedBox(height: 36.v), + Text("user_authorization".tr, style: theme.textTheme.headlineSmall), + SizedBox(height: 60.v), + Text("email".tr, style: theme.textTheme.bodyLarge), + SizedBox(height: 3.v), + BlocSelector( + selector: (state) => state.emailController, + builder: (context, emailController) { + return CustomTextFormField(controller: emailController, textInputAction: TextInputAction.done); + }), + SizedBox(height: 22.v), + Text("password".tr, style: theme.textTheme.bodyLarge), + SizedBox(height: 5.v), + BlocSelector( + selector: (state) => state.passwordController, + builder: (context, passwordController) { + return CustomTextFormField( + obscureText: true, + controller: passwordController, + ); + }), + SizedBox(height: 50.v), + NextStepButton( + text: "no_account".tr.toUpperCase(), + margin: EdgeInsets.only(left: 16.h, right: 16.h, bottom: 50.v), + buttonStyle: CustomButtonStyles.outlinePrimaryTL15, + onPressed: onTapNoAccount(context)), + /*NextStepButton( text: "Тест дневника".toUpperCase(), margin: EdgeInsets.only(left: 16.h, right: 16.h, bottom: 50.v), @@ -79,31 +75,26 @@ class AuthScreen extends StatelessWidget { EdgeInsets.only(left: 16.h, right: 16.h, bottom: 50.v), buttonStyle: CustomButtonStyles.outlinePrimaryTL15, onPressed: onTapProfileTest(context)),*/ - BlocBuilder(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: onTapAuth(state, context)); - }), - ])))); + BlocBuilder(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: onTapAuth(state, context)); + }), + ])))); } onTapAuth(AuthState state, BuildContext context) { return () async { - List errors = validateFields( - state.emailController!.text, state.passwordController!.text); + List errors = validateFields(state.emailController!.text, state.passwordController!.text); if (errors.isEmpty) { final Map 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); + final response = await http.post(Uri.parse(dotenv.env['SERVER_ROOT']! + '/login'), + headers: {'Content-Type': 'application/json'}, body: json.encode(credentials), encoding: utf8); print(response.statusCode); if (response.statusCode == 200) { var res = await jsonDecode(response.body); @@ -179,10 +170,7 @@ class AuthScreen extends StatelessWidget { /// Section Widget _buildAppBar(BuildContext context) { - return CustomAppBar( - leadingWidth: 29.h, - centerTitle: true, - title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio)); + return CustomAppBar(leadingWidth: 29.h, centerTitle: true, title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio)); } List validateFields(String email, String password) { diff --git a/lib/presentation/chat/chat_select_screen/chat_select_screen.dart b/lib/presentation/chat/chat_select_screen/chat_select_screen.dart index dea9d3b..92bbfe8 100644 --- a/lib/presentation/chat/chat_select_screen/chat_select_screen.dart +++ b/lib/presentation/chat/chat_select_screen/chat_select_screen.dart @@ -9,82 +9,81 @@ import 'package:eatvio_app/presentation/chat/chat_select_screen/widgets/chat_ent class ChatSelectScreen extends StatelessWidget { const ChatSelectScreen({Key? key}) : super(key: key); + static Widget builder(BuildContext context) { return BlocProvider( - create: (context) => ChatSelectBloc( - ChatSelectState(chatSelectModelObj: ChatSelectModel())) - ..add(ChatSelectInitialEvent()), - child: ChatSelectScreen()); + create: (context) => ChatSelectBloc(ChatSelectState(chatSelectModelObj: ChatSelectModel()))..add(ChatSelectInitialEvent()), child: ChatSelectScreen()); } Widget build(BuildContext context) { return BlocBuilder( - builder: (context, state) { - if (state is ChatSelectInitialState) { - // Это начальное состояние, данные ещё не загружены. - return Container( - color: Colors.white, - ); - } else if (state is ChatSelectLoadedState) { - // Данные загружены, можно строить экран. - return _buildScreen(context, state); - } else if (state is ChatSelectErrorState) { - // Произошла ошибка при загрузке данных. - return Center(child: Text(state.errorMessage)); - } else { - // Другие состояния, если они есть. - return Container(); // Пустой контейнер для необработанных состояний. - } - }, + builder: (context, state) { + if (state is ChatSelectInitialState) { + // Это начальное состояние, данные ещё не загружены. + return Container( + color: Colors.white, + ); + } else if (state is ChatSelectLoadedState) { + // Данные загружены, можно строить экран. + return _buildScreen(context, state); + } else if (state is ChatSelectErrorState) { + // Произошла ошибка при загрузке данных. + return Center(child: Text(state.errorMessage)); + } else { + // Другие состояния, если они есть. + return Container(); // Пустой контейнер для необработанных состояний. + } + }, ); } _buildScreen(BuildContext context, ChatSelectLoadedState state) { mediaQueryData = MediaQuery.of(context); return SafeArea( - child: Scaffold( - resizeToAvoidBottomInset: false, - body: Container( - width: double.maxFinite, - decoration: AppDecoration.fillGray, - child: ListView( - children: [ - SizedBox(height: 30.v), - Container( - margin: EdgeInsets.only( - left: 16.h, - right: 20.h, - bottom: 10.h, + child: Scaffold( + resizeToAvoidBottomInset: false, + body: Container( + width: double.maxFinite, + decoration: AppDecoration.fillGray, + child: ListView( + children: [ + SizedBox(height: 30.v), + Container( + margin: EdgeInsets.only( + left: 16.h, + right: 20.h, + bottom: 10.h, + ), + padding: EdgeInsets.all(12.h), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.all(Radius.circular(12)), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 5, + offset: Offset(0, 3), ), - padding: EdgeInsets.all(12.h), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: const BorderRadius.all(Radius.circular(12)), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 2, - blurRadius: 5, - offset: Offset(0, 3), - ), - ], + ], + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + CustomSearchView( + controller: state.searchController, + hintText: "Поиск", ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - CustomSearchView( - controller: state.searchController, - hintText: "Поиск", - ), - SizedBox(height: 20.v), - _buildChatList(context, state), - ], - ), - ) - ], - ), - ), - bottomNavigationBar: _buildBottomBar(context))); + SizedBox(height: 20.v), + _buildChatList(context, state), + ], + ), + ) + ], + ), + ), + ), + ); } } diff --git a/lib/presentation/diary/diary_edit_screen/diary_edit_screen.dart b/lib/presentation/diary/diary_edit_screen/diary_edit_screen.dart index 1e81c62..2aa53dd 100644 --- a/lib/presentation/diary/diary_edit_screen/diary_edit_screen.dart +++ b/lib/presentation/diary/diary_edit_screen/diary_edit_screen.dart @@ -18,17 +18,11 @@ import 'package:carousel_slider/carousel_slider.dart'; // ignore: must_be_immutable class DiaryEditScreen extends StatelessWidget { - final void Function() onInitialize; - DiaryEditScreen({Key? key, required this.onInitialize}) : super(key: key); + DiaryEditScreen({Key? key}) : super(key: key); static Widget builder(BuildContext context) { return BlocProvider( - create: (context) => - DiaryEditBloc(DiaryEditState(diaryEditModelObj: DiaryEditModel())) - ..add(DiaryEditInitialEvent()), - child: DiaryEditScreen( - onInitialize: () {}, - )); + create: (context) => DiaryEditBloc(DiaryEditState(diaryEditModelObj: DiaryEditModel()))..add(DiaryEditInitialEvent()), child: DiaryEditScreen()); } Widget build(BuildContext context) { @@ -53,8 +47,7 @@ class DiaryEditScreen extends StatelessWidget { ); } - Widget _buildDiaryEditScreen( - BuildContext context, DiaryEditLoadedState state) { + Widget _buildDiaryEditScreen(BuildContext context, DiaryEditLoadedState state) { mediaQueryData = MediaQuery.of(context); return RefreshIndicator( onRefresh: refreshPage(context), @@ -94,7 +87,6 @@ class DiaryEditScreen extends StatelessWidget { ], ), ), - bottomNavigationBar: _buildBottomBar(context), ), ), ), @@ -156,8 +148,7 @@ class DiaryEditScreen extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - CustomImageView( - imagePath: ImageConstant.imgArrowLeft, onTap: onTapBack()), + CustomImageView(imagePath: ImageConstant.imgArrowLeft, onTap: onTapBack()), Padding( padding: EdgeInsets.only(top: 1.v), child: Column( @@ -260,9 +251,7 @@ class DiaryEditScreen extends StatelessWidget { borderRadius: BorderRadiusStyle.roundedBorder5, ), child: Center( - child: CircularProgressIndicator( - color: appTheme - .blueGray300), // Замените на свой прелоадер при необходимости + child: CircularProgressIndicator(color: appTheme.blueGray300), // Замените на свой прелоадер при необходимости ), ), ], @@ -286,8 +275,7 @@ class DiaryEditScreen extends StatelessWidget { _buildMeal(BuildContext context, DiaryEditLoadedState state) { final digitsOnly = FilteringTextInputFormatter.digitsOnly; - List _options = - state.autocompleteList!.map((dynamic item) => item as String).toList(); + List _options = state.autocompleteList!.map((dynamic item) => item as String).toList(); //print(_options); FocusNode focusNodeName = FocusNode(); return Container( @@ -332,9 +320,7 @@ class DiaryEditScreen extends StatelessWidget { onTap: () => _selectMealTime(context, state), child: AbsorbPointer( child: CustomTextFormField( - hintText: state.mealTimeController - ?.format(context) - .toString(), + hintText: state.mealTimeController?.format(context).toString(), autofocus: false, ), ), @@ -348,8 +334,7 @@ class DiaryEditScreen extends StatelessWidget { SizedBox(height: 20.v), Padding( padding: EdgeInsets.symmetric(horizontal: 10.h), - child: BlocSelector( + child: BlocSelector( selector: (state) => state.diaryEditModelObj, builder: (context, diaryEditModelObj) { return CustomDropDown( @@ -441,8 +426,7 @@ class DiaryEditScreen extends StatelessWidget { ), Padding( padding: EdgeInsets.only(left: 30.h), - child: BlocSelector( + child: BlocSelector( selector: (state) => state.diaryEditModelObj, builder: (context, diaryEditModelObj) { return CustomDropDown( @@ -460,9 +444,7 @@ class DiaryEditScreen extends StatelessWidget { SizedBox(height: 30.v), _buildAddProduct(context, state), SizedBox(height: 30.v), - ...state.portions! - .map((portion) => _buildProduct(context, portion)) - .toList(), + ...state.portions!.map((portion) => _buildProduct(context, portion)).toList(), SizedBox(height: 30.v), /*...state.photos != null ? state.photos!.isEmpty @@ -575,14 +557,8 @@ class DiaryEditScreen extends StatelessWidget { child: Text('Отмена'), ), TextButton( - onPressed: triggerModeration( - context, - state.productNameController!.text, - state.caloriesController!.text, - state.proteinsController!.text, - state.fatsController!.text, - state.carbsController!.text, - dialogContext), + onPressed: triggerModeration(context, state.productNameController!.text, state.caloriesController!.text, state.proteinsController!.text, + state.fatsController!.text, state.carbsController!.text, dialogContext), child: Text('Отправить'), ), ], @@ -591,30 +567,19 @@ class DiaryEditScreen extends StatelessWidget { ); } - triggerModeration( - BuildContext context, - String name, - String energyOn100gr, - String proteinsOn100gr, - String fatsOn100gr, - String carbohydratesOn100gr, + triggerModeration(BuildContext context, String name, String energyOn100gr, String proteinsOn100gr, String fatsOn100gr, String carbohydratesOn100gr, BuildContext dialogContext) { return () { // Call function to send moderation with the entered values context.read().add(SendModeration( - name: name, - energyOn100gr: energyOn100gr, - proteinsOn100gr: proteinsOn100gr, - fatsOn100gr: fatsOn100gr, - carbohydratesOn100gr: carbohydratesOn100gr)); + name: name, energyOn100gr: energyOn100gr, proteinsOn100gr: proteinsOn100gr, fatsOn100gr: fatsOn100gr, carbohydratesOn100gr: carbohydratesOn100gr)); Navigator.of(dialogContext).pop(); // Dismiss the dialog }; } popDialog(BuildContext dialogContext) { return () { - Navigator.of(dialogContext) - .pop(); // Dismiss the dialog on Cancel button press + Navigator.of(dialogContext).pop(); // Dismiss the dialog on Cancel button press }; } @@ -624,8 +589,7 @@ class DiaryEditScreen extends StatelessWidget { }; } - selectProduct(AutocompleteOnSelected onSelected, String option, - DiaryEditLoadedState state) { + selectProduct(AutocompleteOnSelected onSelected, String option, DiaryEditLoadedState state) { state.productNameController?.text = option; return () { onSelected(option); @@ -653,9 +617,7 @@ class DiaryEditScreen extends StatelessWidget { changeMeal(BuildContext context, DiaryEditLoadedState state) { return (value) { - context - .read() - .add(ChangeMeal(meals: state.meals, value: value)); + context.read().add(ChangeMeal(meals: state.meals, value: value)); }; } @@ -672,8 +634,7 @@ class DiaryEditScreen extends StatelessWidget { return CarouselSlider( options: CarouselOptions(height: 340.0), items: state.photos!.map((photo) { - return _buildPhoto( - context, photo); // Assuming photos are stored as strings + return _buildPhoto(context, photo); // Assuming photos are stored as strings }).toList(), ); } @@ -699,9 +660,7 @@ class DiaryEditScreen extends StatelessWidget { top: 5.v, ), child: Text( - portion['product_name'].length > 8 - ? portion['product_name'].substring(0, 8) - : portion['product_name'], + portion['product_name'].length > 8 ? portion['product_name'].substring(0, 8) : portion['product_name'], style: CustomTextStyles.titleMedium16, )), Padding( @@ -711,9 +670,7 @@ class DiaryEditScreen extends StatelessWidget { bottom: 3.v, ), child: Text( - portion['quantity'].toString() + - ' ' + - portion['portion'].toString().tr, + portion['quantity'].toString() + ' ' + portion['portion'].toString().tr, ), ), Spacer(), @@ -773,15 +730,12 @@ class DiaryEditScreen extends StatelessWidget { removePhoto(BuildContext context, Map photo) { return () { - context - .read() - .add(RemoveImageEvent(photo['id'].toString(), photo['path'])); + context.read().add(RemoveImageEvent(photo['id'].toString(), photo['path'])); }; } _buildNutritionChart(BuildContext context, DiaryEditLoadedState state) { - final List recommendedValues = state - .recommendedChartValues; // Рекомендуемые значения: калории, жиры, белки, углеводы + final List recommendedValues = state.recommendedChartValues; // Рекомендуемые значения: калории, жиры, белки, углеводы final List consumedValues = state.consumedChartValues; // return Container( margin: EdgeInsets.symmetric(horizontal: 16.h), @@ -843,37 +797,25 @@ class DiaryEditScreen extends StatelessWidget { BarChartGroupData(x: 0, barRods: [ BarChartRodData( toY: consumedValues[0] / recommendedValues[0] * 100, - color: checkConsumedValues( - [recommendedValues[0]], [consumedValues[1]]) - ? Colors.red - : Colors.blue, + color: checkConsumedValues([recommendedValues[0]], [consumedValues[1]]) ? Colors.red : Colors.blue, ) ]), BarChartGroupData(x: 1, barRods: [ BarChartRodData( toY: consumedValues[1] / recommendedValues[1] * 100, - color: checkConsumedValues( - [recommendedValues[1]], [consumedValues[1]]) - ? Colors.red - : Colors.blue, + color: checkConsumedValues([recommendedValues[1]], [consumedValues[1]]) ? Colors.red : Colors.blue, ) ]), BarChartGroupData(x: 2, barRods: [ BarChartRodData( toY: consumedValues[2] / recommendedValues[2] * 100, - color: checkConsumedValues( - [recommendedValues[2]], [consumedValues[2]]) - ? Colors.red - : Colors.blue, + color: checkConsumedValues([recommendedValues[2]], [consumedValues[2]]) ? Colors.red : Colors.blue, ) ]), BarChartGroupData(x: 3, barRods: [ BarChartRodData( toY: consumedValues[3] / recommendedValues[3] * 100, - color: checkConsumedValues( - [recommendedValues[3]], [consumedValues[3]]) - ? Colors.red - : Colors.blue, + color: checkConsumedValues([recommendedValues[3]], [consumedValues[3]]) ? Colors.red : Colors.blue, ) ]), ], @@ -1063,9 +1005,7 @@ class DiaryEditScreen extends StatelessWidget { r"Подъем \ отход ко сну", style: CustomTextStyles.bodyMediumBlack900, ), - Icon(state.wakeupExpanded - ? Icons.arrow_drop_up - : Icons.arrow_drop_down), + Icon(state.wakeupExpanded ? Icons.arrow_drop_up : Icons.arrow_drop_down), ], ), ), @@ -1079,8 +1019,7 @@ class DiaryEditScreen extends StatelessWidget { onTap: () => _selectWakeupTime(context, state), child: AbsorbPointer( child: CustomTextFormField( - hintText: - state.wakeupController?.format(context).toString(), + hintText: state.wakeupController?.format(context).toString(), prefix: Icon(Icons.sunny), autofocus: false, ), @@ -1091,8 +1030,7 @@ class DiaryEditScreen extends StatelessWidget { onTap: () => _selectRetireTime(context, state), child: AbsorbPointer( child: CustomTextFormField( - hintText: - state.retireController?.format(context).toString(), + hintText: state.retireController?.format(context).toString(), prefix: Icon(Icons.bed), autofocus: false, ), @@ -1139,8 +1077,7 @@ class DiaryEditScreen extends StatelessWidget { }; } - Function() sendFinishDayData( - BuildContext dialogContext, BuildContext context) { + Function() sendFinishDayData(BuildContext dialogContext, BuildContext context) { return () { closeDialog(dialogContext); context.read().add(FinishDay()); @@ -1182,9 +1119,7 @@ class DiaryEditScreen extends StatelessWidget { "Ваше настроение после приема пищи", style: CustomTextStyles.bodyMediumBlack900, ), - Icon(state.moodExpanded - ? Icons.arrow_drop_up - : Icons.arrow_drop_down), + Icon(state.moodExpanded ? Icons.arrow_drop_up : Icons.arrow_drop_down), ], ), ), @@ -1242,8 +1177,7 @@ class DiaryEditScreen extends StatelessWidget { }; } - Future _selectWakeupTime( - BuildContext context, DiaryEditLoadedState state) async { + Future _selectWakeupTime(BuildContext context, DiaryEditLoadedState state) async { final TimeOfDay? pickedTime = await showTimePicker( context: context, initialTime: state.wakeupController ?? TimeOfDay.now(), @@ -1254,8 +1188,7 @@ class DiaryEditScreen extends StatelessWidget { } } - Future _selectRetireTime( - BuildContext context, DiaryEditLoadedState state) async { + Future _selectRetireTime(BuildContext context, DiaryEditLoadedState state) async { final TimeOfDay? pickedTime = await showTimePicker( context: context, initialTime: state.retireController ?? TimeOfDay.now(), @@ -1266,8 +1199,7 @@ class DiaryEditScreen extends StatelessWidget { } } - Future _selectMealTime( - BuildContext context, DiaryEditLoadedState state) async { + Future _selectMealTime(BuildContext context, DiaryEditLoadedState state) async { final TimeOfDay? pickedTime = await showTimePicker( context: context, initialTime: state.mealTimeController ?? TimeOfDay.now(), @@ -1279,8 +1211,7 @@ class DiaryEditScreen extends StatelessWidget { } } -bool checkConsumedValues( - List recommendedValues, List consumedValues) { +bool checkConsumedValues(List recommendedValues, List consumedValues) { for (int i = 0; i < recommendedValues.length; i++) { if (consumedValues[i] > recommendedValues[i]) { // Если хотя бы одно потребленное значение превышает рекомендованное diff --git a/lib/presentation/feed/feed_screen/feed_screen.dart b/lib/presentation/feed/feed_screen/feed_screen.dart index d5d2500..12c0065 100644 --- a/lib/presentation/feed/feed_screen/feed_screen.dart +++ b/lib/presentation/feed/feed_screen/feed_screen.dart @@ -12,10 +12,7 @@ import 'package:intl/intl.dart'; class FeedScreen extends StatelessWidget { static Widget builder(BuildContext context) { - return BlocProvider( - create: (context) => FeedBloc(FeedState(myProfileModelObj: FeedModel())) - ..add(FeedInitialEvent()), - child: FeedScreen()); + return BlocProvider(create: (context) => FeedBloc(FeedState(myProfileModelObj: FeedModel()))..add(FeedInitialEvent()), child: FeedScreen()); } Widget build(BuildContext context) { @@ -64,7 +61,6 @@ class FeedScreen extends StatelessWidget { ), ), ), - bottomNavigationBar: _buildBottomBar(context), ), ); } @@ -154,8 +150,7 @@ class FeedScreen extends StatelessWidget { child: Column(children: [ ListTile( leading: CachedNetworkImage( - imageUrl: dotenv.env['STORAGE_URL']! + - (post["author"]['profile_photo_path'] ?? ''), + imageUrl: dotenv.env['STORAGE_URL']! + (post["author"]['profile_photo_path'] ?? ''), imageBuilder: (context, imageProvider) => CircleAvatar( backgroundImage: imageProvider, radius: 25, @@ -172,8 +167,7 @@ class FeedScreen extends StatelessWidget { style: CustomTextStyles.titleMediumSemiBold, ), subtitle: Text( - DateFormat('dd.MM.yyyy') - .format(DateTime.parse(post?['post']['updated_at'])), + DateFormat('dd.MM.yyyy').format(DateTime.parse(post?['post']['updated_at'])), style: theme.textTheme.bodyMedium, ), onTap: () => Navigator.pushNamed( @@ -220,13 +214,11 @@ class FeedScreen extends StatelessWidget { ), SizedBox(height: 16.0), CachedNetworkImage( - imageUrl: dotenv.env['STORAGE_URL']! + - (post?['post']['image'] ?? ''), + imageUrl: dotenv.env['STORAGE_URL']! + (post?['post']['image'] ?? ''), imageBuilder: (context, imageProvider) => Stack( children: [ ClipRRect( - borderRadius: - const BorderRadius.all(Radius.circular(8.0)), + borderRadius: const BorderRadius.all(Radius.circular(8.0)), child: Image( image: imageProvider, ), diff --git a/lib/presentation/profile/my_profile_screen/my_profile_screen.dart b/lib/presentation/profile/my_profile_screen/my_profile_screen.dart index 5d1f9bf..d6fe53b 100644 --- a/lib/presentation/profile/my_profile_screen/my_profile_screen.dart +++ b/lib/presentation/profile/my_profile_screen/my_profile_screen.dart @@ -1,3 +1,5 @@ +import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart'; + import 'bloc/my_profile_bloc.dart'; import 'models/my_profile_model.dart'; import 'package:eatvio_app/core/app_export.dart'; @@ -19,10 +21,13 @@ import 'dart:convert'; class MyProfileScreen extends StatelessWidget { static Widget builder(BuildContext context) { return BlocProvider( - create: (context) => - MyProfileBloc(MyProfileState(myProfileModelObj: MyProfileModel())) - ..add(MyProfileInitialEvent()), - child: MyProfileScreen()); + create: (context) => MyProfileBloc( + MyProfileState( + myProfileModelObj: MyProfileModel(), + ), + )..add(MyProfileInitialEvent()), + child: MyProfileScreen(), + ); } Widget build(BuildContext context) { @@ -50,103 +55,100 @@ class MyProfileScreen extends StatelessWidget { _buildMyProfileScreen(BuildContext context, MyProfileLoadedState state) { mediaQueryData = MediaQuery.of(context); - return SafeArea( - child: Scaffold( - backgroundColor: Colors.grey[200], - resizeToAvoidBottomInset: true, - body: SingleChildScrollView( - child: Container( - width: double.maxFinite, - decoration: BoxDecoration(color: Colors.grey[200]), - child: Padding( - padding: EdgeInsets.all(16), - child: Column(children: [ - SizedBox(height: 30.v), - Container( - padding: EdgeInsets.symmetric( - horizontal: 24.h, - vertical: 12.v, - ), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: - const BorderRadius.all(Radius.circular(12)), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 2, - blurRadius: 5, - offset: Offset(0, 3), - ), - ], - ), - child: _buildInfo(context, state)), - SizedBox(height: 15.v), - Container( - padding: EdgeInsets.symmetric( - horizontal: 24.h, - vertical: 12.v, - ), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: - const BorderRadius.all(Radius.circular(12)), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 2, - blurRadius: 5, - offset: Offset(0, 3), - ), - ], - ), - child: _buildCarousel(context, state)), - SizedBox(height: 15.v), - if (!state.plans!.isEmpty) - Container( - padding: EdgeInsets.symmetric( - horizontal: 24.h, - vertical: 12.v, - ), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: const BorderRadius.all( - Radius.circular(12)), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 2, - blurRadius: 5, - offset: Offset(0, 3), - ), - ], - ), - child: _buildPlans( - context, - state.plans - ?.map((plan) => plan['name']) - .toList(), - state.plans - ?.map((plan) => plan['image']) - .toList(), - state.plans - ?.map((plan) => plan['state']) - .toList(), - )), - SizedBox(height: 5.v), - ])))), - bottomNavigationBar: _buildBottomBar(context))); + return Scaffold( + appBar: CustomAppBar( + height: 0, + ), + backgroundColor: Colors.grey[200], + resizeToAvoidBottomInset: true, + body: SingleChildScrollView( + child: Container( + width: double.maxFinite, + decoration: BoxDecoration(color: Colors.grey[200]), + child: Padding( + padding: EdgeInsets.all(16), + child: Column( + children: [ + SizedBox(height: 30.v), + Container( + padding: EdgeInsets.symmetric( + horizontal: 24.h, + vertical: 12.v, + ), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.all(Radius.circular(12)), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 5, + offset: Offset(0, 3), + ), + ], + ), + child: _buildInfo(context, state)), + SizedBox(height: 15.v), + Container( + padding: EdgeInsets.symmetric( + horizontal: 24.h, + vertical: 12.v, + ), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.all(Radius.circular(12)), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 5, + offset: Offset(0, 3), + ), + ], + ), + child: _buildCarousel(context, state)), + SizedBox(height: 15.v), + if (!state.plans!.isEmpty) + Container( + padding: EdgeInsets.symmetric( + horizontal: 24.h, + vertical: 12.v, + ), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.all(Radius.circular(12)), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 5, + offset: Offset(0, 3), + ), + ], + ), + child: _buildPlans( + context, + state.plans?.map((plan) => plan['name']).toList(), + state.plans?.map((plan) => plan['image']).toList(), + state.plans?.map((plan) => plan['state']).toList(), + )), + SizedBox(height: 5.v), + ], + ), + ), + ), + ), + ); } /// Section Widget - _buildInfo(BuildContext context, MyProfileLoadedState state) { + Widget _buildInfo(BuildContext context, MyProfileLoadedState state) { return Column( children: [ GestureDetector( onTap: () => addImage(context), child: CachedNetworkImage( - imageUrl: dotenv.env['STORAGE_URL']! + - (state.user?['profile_photo_path'] ?? ''), + imageUrl: dotenv.env['STORAGE_URL']! + (state.user?['profile_photo_path'] ?? ''), imageBuilder: (context, imageProvider) => CircleAvatar( backgroundImage: imageProvider, radius: 60, @@ -208,12 +210,7 @@ class MyProfileScreen extends StatelessWidget { SizedBox(height: 50.v), _buildChart( context, - state.chartPoints?.values - .map((e) => e['weight']) - .toList() - .map((i) => i.toDouble()) - .toList() ?? - [0.0, 0.0, 0.0, 0.0], + state.chartPoints?.values.map((e) => e['weight']).toList().map((i) => i.toDouble()).toList() ?? [0.0, 0.0, 0.0, 0.0], state.chartDates, ) ])); @@ -228,13 +225,7 @@ class MyProfileScreen extends StatelessWidget { SizedBox(height: 50.v), _buildChart( context, - _bodyMassIndex( - state.chartPoints?.values - .map((e) => e['weight']) - .toList() - .map((i) => i.toDouble()) - .toList() ?? - [0.0, 0.0, 0.0, 0.0], + _bodyMassIndex(state.chartPoints?.values.map((e) => e['weight']).toList().map((i) => i.toDouble()).toList() ?? [0.0, 0.0, 0.0, 0.0], state.userDetails?['height'].toInt() ?? 160), state.chartDates), ]); @@ -249,8 +240,7 @@ class MyProfileScreen extends StatelessWidget { ), ), SizedBox(height: 30.v), - _buildCircumferenceChart( - context, state.circumferences, state.chartDates), + _buildCircumferenceChart(context, state.circumferences, state.chartDates), _buildChartLegend() ])); default: @@ -294,8 +284,7 @@ class MyProfileScreen extends StatelessWidget { ); } - _buildChart( - BuildContext context, List chartPoints, List? dates) { + _buildChart(BuildContext context, List chartPoints, List? dates) { List spots = []; for (int i = 0; i < 4; i++) { spots.add( @@ -303,8 +292,7 @@ class MyProfileScreen extends StatelessWidget { ); } - Widget bottomTitleWidgets( - double value, TitleMeta meta, double chartWidth, List? dates) { + Widget bottomTitleWidgets(double value, TitleMeta meta, double chartWidth, List? dates) { if (value % 1 != 0) { return Container(); } @@ -316,8 +304,7 @@ class MyProfileScreen extends StatelessWidget { int index = value.toInt(); if (index >= 0 && index < dates!.length) { // Проверка на допустимый индекс в массиве dates - String formattedDate = - DateFormat('dd.MM').format(DateTime.parse(dates[index])); + String formattedDate = DateFormat('dd.MM').format(DateTime.parse(dates[index])); return SideTitleWidget( axisSide: meta.axisSide, space: 16, @@ -374,18 +361,15 @@ class MyProfileScreen extends StatelessWidget { dotData: const FlDotData(show: false), ), ], - minY: chartPoints.reduce( - (value, element) => value < element ? value : element), - maxY: chartPoints.reduce( - (value, element) => value > element ? value : element), + minY: chartPoints.reduce((value, element) => value < element ? value : element), + maxY: chartPoints.reduce((value, element) => value > element ? value : element), minX: 1, maxX: 4, titlesData: FlTitlesData( leftTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, - getTitlesWidget: (value, meta) => - leftTitleWidgets(value, meta, constraints.maxWidth), + getTitlesWidget: (value, meta) => leftTitleWidgets(value, meta, constraints.maxWidth), reservedSize: 56, ), drawBelowEverything: true, @@ -396,8 +380,7 @@ class MyProfileScreen extends StatelessWidget { bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, - getTitlesWidget: (value, meta) => bottomTitleWidgets( - value, meta, constraints.maxWidth, dates), + getTitlesWidget: (value, meta) => bottomTitleWidgets(value, meta, constraints.maxWidth, dates), reservedSize: 36, interval: 1, ), @@ -457,11 +440,9 @@ class MyProfileScreen extends StatelessWidget { }; } - _buildCircumferenceChart( - BuildContext context, chartPoints, List? dates) { + _buildCircumferenceChart(BuildContext context, chartPoints, List? dates) { // Определение осей графика - final chestCircumferences = - chartPoints.values.map((e) => e['chest_circumference']).toList(); + final chestCircumferences = chartPoints.values.map((e) => e['chest_circumference']).toList(); List spotsChest = []; for (int i = 0; i < 4; i++) { chestCircumferences[i] = chestCircumferences[i] * 10 / 10; @@ -469,8 +450,7 @@ class MyProfileScreen extends StatelessWidget { FlSpot((i * 10 / 10) + 1, chestCircumferences[i] * 10 / 10), ); } - final waistCircumferences = - chartPoints.values.map((e) => e['waist_circumference']).toList(); + final waistCircumferences = chartPoints.values.map((e) => e['waist_circumference']).toList(); List spotsWaist = []; for (int i = 0; i < 4; i++) { waistCircumferences[i] = waistCircumferences[i] * 10 / 10; @@ -478,8 +458,7 @@ class MyProfileScreen extends StatelessWidget { FlSpot((i * 10 / 10) + 1, waistCircumferences[i] * 10 / 10), ); } - final hipCircumferences = - chartPoints.values.map((e) => e['hip_circumference']).toList(); + final hipCircumferences = chartPoints.values.map((e) => e['hip_circumference']).toList(); List spotsHip = []; for (int i = 0; i < 4; i++) { hipCircumferences[i] = hipCircumferences[i] * 10 / 10; @@ -488,14 +467,9 @@ class MyProfileScreen extends StatelessWidget { ); } - final allCircumferences = [ - ...chestCircumferences, - ...hipCircumferences, - ...waistCircumferences - ]; + final allCircumferences = [...chestCircumferences, ...hipCircumferences, ...waistCircumferences]; - Widget bottomTitleWidgets( - double value, TitleMeta meta, double chartWidth, List? dates) { + Widget bottomTitleWidgets(double value, TitleMeta meta, double chartWidth, List? dates) { if (value % 1 != 0) { return Container(); } @@ -507,8 +481,7 @@ class MyProfileScreen extends StatelessWidget { int index = value.toInt(); if (index >= 0 && index < dates!.length) { // Проверка на допустимый индекс в массиве dates - String formattedDate = - DateFormat('dd.MM').format(DateTime.parse(dates[index])); + String formattedDate = DateFormat('dd.MM').format(DateTime.parse(dates[index])); return SideTitleWidget( axisSide: meta.axisSide, space: 16, @@ -587,18 +560,15 @@ class MyProfileScreen extends StatelessWidget { dotData: const FlDotData(show: false), ), ], - minY: allCircumferences.reduce( - (value, element) => value < element ? value : element), - maxY: allCircumferences.reduce( - (value, element) => value > element ? value : element), + minY: allCircumferences.reduce((value, element) => value < element ? value : element), + maxY: allCircumferences.reduce((value, element) => value > element ? value : element), minX: 1, maxX: 4, titlesData: FlTitlesData( leftTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, - getTitlesWidget: (value, meta) => - leftTitleWidgets(value, meta, constraints.maxWidth), + getTitlesWidget: (value, meta) => leftTitleWidgets(value, meta, constraints.maxWidth), reservedSize: 56, ), drawBelowEverything: true, @@ -609,8 +579,7 @@ class MyProfileScreen extends StatelessWidget { bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, - getTitlesWidget: (value, meta) => bottomTitleWidgets( - value, meta, constraints.maxWidth, dates), + getTitlesWidget: (value, meta) => bottomTitleWidgets(value, meta, constraints.maxWidth, dates), reservedSize: 36, interval: 1, ), @@ -665,125 +634,80 @@ class MyProfileScreen extends StatelessWidget { } /// Section Widget - _buildPlans(BuildContext context, List? plans, - List? imagePaths, List? states) { + _buildPlans(BuildContext context, List? plans, List? imagePaths, List? states) { return Padding( padding: EdgeInsets.only(left: 7.h), child: Column(children: [ Padding( padding: EdgeInsets.only(left: 15.h), - child: Text("plans_complete".tr, - style: CustomTextStyles.titleMediumBlack - .copyWith(color: appTheme.black900))), + child: Text("plans_complete".tr, style: CustomTextStyles.titleMediumBlack.copyWith(color: appTheme.black900))), SizedBox(height: 5.v), Container( - decoration: AppDecoration.outlineBlueGray - .copyWith(borderRadius: BorderRadiusStyle.roundedBorder5), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - height: 105.v, - width: 343.h, - child: - Stack(alignment: Alignment.bottomCenter, children: [ - Align( - alignment: Alignment.topCenter, - child: ListView.builder( - itemCount: plans?.length ?? 0, - itemBuilder: (context, i) { - String state = states?[i]; - var boxColor = appTheme.greenA700; - switch (states?[i]) { - case 'active': - boxColor = appTheme.greenA700; - break; - case 'archived': // График ИМТ - boxColor = appTheme.gray400; - break; - case 'draft': - boxColor = appTheme.deepOrange900; - break; - case 'in_progress': //График обхватов - boxColor = appTheme.lightGreen300; - break; - } - return SizedBox( - height: 40.v, - width: 343.h, - child: Stack( - alignment: Alignment.bottomRight, - children: [ - Align( - alignment: - Alignment.topCenter, - child: Container( - margin: EdgeInsets.only( - bottom: 11.v), - padding: - EdgeInsets.symmetric( - horizontal: 6.h, - vertical: 4.v), - decoration: AppDecoration - .fillGray - .copyWith( - borderRadius: - BorderRadiusStyle - .roundedBorder5), - child: Row( - mainAxisAlignment: - MainAxisAlignment - .spaceBetween, - children: [ - Padding( - padding: EdgeInsets - .only( - bottom: 1 - .v), - child: Text( - plans![i], - style: CustomTextStyles - .titleSmallBluegray800Bold)), - Container( - height: 19.v, - width: 100.h, - decoration: BoxDecoration( - color: - boxColor, - borderRadius: - BorderRadius.circular( - 5.h))) - ]))), - Align( - alignment: Alignment.center, - child: Container( - margin: EdgeInsets.only( - bottom: 12.h), - child: Image.network( - dotenv.env[ - 'STORAGE_URL']! + - imagePaths?[i], - height: 25.adaptSize, - width: 25.adaptSize, - ))), - Align( - alignment: - Alignment.bottomRight, - child: Container( - width: 70.h, - margin: EdgeInsets.only( - bottom: 14.h, - right: 22.h), - child: Text("$state".tr, - maxLines: 3, - overflow: TextOverflow - .ellipsis, - style: theme.textTheme - .titleSmall))) - ])); - })), - ])) - ])) + decoration: AppDecoration.outlineBlueGray.copyWith(borderRadius: BorderRadiusStyle.roundedBorder5), + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + SizedBox( + height: 105.v, + width: 343.h, + child: Stack(alignment: Alignment.bottomCenter, children: [ + Align( + alignment: Alignment.topCenter, + child: ListView.builder( + itemCount: plans?.length ?? 0, + itemBuilder: (context, i) { + String state = states?[i]; + var boxColor = appTheme.greenA700; + switch (states?[i]) { + case 'active': + boxColor = appTheme.greenA700; + break; + case 'archived': // График ИМТ + boxColor = appTheme.gray400; + break; + case 'draft': + boxColor = appTheme.deepOrange900; + break; + case 'in_progress': //График обхватов + boxColor = appTheme.lightGreen300; + break; + } + return SizedBox( + height: 40.v, + width: 343.h, + child: Stack(alignment: Alignment.bottomRight, children: [ + Align( + alignment: Alignment.topCenter, + child: Container( + margin: EdgeInsets.only(bottom: 11.v), + padding: EdgeInsets.symmetric(horizontal: 6.h, vertical: 4.v), + decoration: AppDecoration.fillGray.copyWith(borderRadius: BorderRadiusStyle.roundedBorder5), + child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Padding( + padding: EdgeInsets.only(bottom: 1.v), + child: Text(plans![i], style: CustomTextStyles.titleSmallBluegray800Bold)), + Container( + height: 19.v, + width: 100.h, + decoration: BoxDecoration(color: boxColor, borderRadius: BorderRadius.circular(5.h))) + ]))), + Align( + alignment: Alignment.center, + child: Container( + margin: EdgeInsets.only(bottom: 12.h), + child: Image.network( + dotenv.env['STORAGE_URL']! + imagePaths?[i], + height: 25.adaptSize, + width: 25.adaptSize, + ))), + Align( + alignment: Alignment.bottomRight, + child: Container( + width: 70.h, + margin: EdgeInsets.only(bottom: 14.h, right: 22.h), + child: Text("$state".tr, maxLines: 3, overflow: TextOverflow.ellipsis, style: theme.textTheme.titleSmall))) + ])); + })), + ])) + ])) ])); } @@ -811,44 +735,38 @@ class MyProfileScreen extends StatelessWidget { children: [ Padding( padding: EdgeInsets.symmetric(vertical: 5.v), - child: Text("chest_circumference".tr, - style: theme.textTheme.bodyLarge), + child: Text("chest_circumference".tr, style: theme.textTheme.bodyLarge), ), SizedBox(height: 5.v), CustomTextFormField( controller: state.chestCircumferenceController, keyboardType: TextInputType.number, inputFormatters: [ - FilteringTextInputFormatter - .digitsOnly, // Ограничение ввода только цифрами + FilteringTextInputFormatter.digitsOnly, // Ограничение ввода только цифрами ], ), Padding( padding: EdgeInsets.symmetric(vertical: 15.v), - child: Text("waist_circumference".tr, - style: theme.textTheme.bodyLarge), + child: Text("waist_circumference".tr, style: theme.textTheme.bodyLarge), ), SizedBox(height: 5.v), CustomTextFormField( controller: state.waistCircumferenceController, keyboardType: TextInputType.number, inputFormatters: [ - FilteringTextInputFormatter - .digitsOnly, // Ограничение ввода только цифрами + FilteringTextInputFormatter.digitsOnly, // Ограничение ввода только цифрами ], ), Padding( padding: EdgeInsets.symmetric(vertical: 15.v), - child: Text("hip_circumference".tr, - style: theme.textTheme.bodyLarge), + child: Text("hip_circumference".tr, style: theme.textTheme.bodyLarge), ), SizedBox(height: 5.v), CustomTextFormField( controller: state.hipCircumferenceController, keyboardType: TextInputType.number, inputFormatters: [ - FilteringTextInputFormatter - .digitsOnly, // Ограничение ввода только цифрами + FilteringTextInputFormatter.digitsOnly, // Ограничение ввода только цифрами ], ), ], @@ -867,8 +785,7 @@ class MyProfileScreen extends StatelessWidget { ), ], backgroundColor: Colors.white, - titlePadding: EdgeInsets.all( - 20.0), // Можете настроить отступы по вашему желанию + titlePadding: EdgeInsets.all(20.0), // Можете настроить отступы по вашему желанию ); }, ); @@ -904,8 +821,7 @@ class MyProfileScreen extends StatelessWidget { ); } else { // Ошибка от сервера - print( - "Ошибка при отправке данных. Код ответа: ${response.statusCode}"); + print("Ошибка при отправке данных. Код ответа: ${response.statusCode}"); } } catch (e) { // Обработка ошибок при отправке запроса @@ -997,8 +913,7 @@ class MyProfileScreen extends StatelessWidget { ); } else { // Ошибка от сервера - print( - "Ошибка при отправке данных. Код ответа: ${response.statusCode}"); + print("Ошибка при отправке данных. Код ответа: ${response.statusCode}"); } } catch (e) { // Обработка ошибок при отправке запроса diff --git a/lib/widgets/app_bar/custom_app_bar.dart b/lib/widgets/app_bar/custom_app_bar.dart index 7ffe17b..0581170 100644 --- a/lib/widgets/app_bar/custom_app_bar.dart +++ b/lib/widgets/app_bar/custom_app_bar.dart @@ -1,5 +1,6 @@ import 'package:eatvio_app/core/app_export.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; // ignore: must_be_immutable class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { @@ -30,7 +31,10 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { @override Widget build(BuildContext context) { return AppBar( - elevation: 0, + systemOverlayStyle: SystemUiOverlayStyle( + statusBarIconBrightness: Brightness.dark, + statusBarBrightness: Brightness.light, + ), toolbarHeight: height ?? 56.v, automaticallyImplyLeading: false, backgroundColor: Colors.transparent, diff --git a/lib/widgets/custom_bottom_bar.dart b/lib/widgets/custom_bottom_bar.dart index d6e47d6..b8e5460 100644 --- a/lib/widgets/custom_bottom_bar.dart +++ b/lib/widgets/custom_bottom_bar.dart @@ -2,6 +2,106 @@ import 'package:eatvio_app/core/app_export.dart'; import 'package:flutter/material.dart'; import 'package:eatvio_app/widgets/app_bar/appbar_title_circleimage.dart'; import 'package:eatvio_app/widgets/custom_navigation_menu.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:go_router/go_router.dart'; + +int currentBottomNavBarIndex = 0; + +class BottomNavigationBarWidget extends StatefulWidget { + final Widget child; + + const BottomNavigationBarWidget({ + super.key, + required this.child, + }); + + @override + State createState() => _BottomNavigationBarWidgetState(); +} + +class _BottomNavigationBarWidgetState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: widget.child, + bottomNavigationBar: BottomNavigationBar( + type: BottomNavigationBarType.fixed, + onTap: (index) { + if (currentBottomNavBarIndex != index) { + setState(() { + currentBottomNavBarIndex = index; + }); + } + switch (currentBottomNavBarIndex) { + case 0: + GoRouter.of(context).replace('/my-profile-screen'); + break; + case 1: + GoRouter.of(context).replace('/feed-screen'); + break; + case 2: + GoRouter.of(context).replace('/diary-screen'); + break; + case 3: + GoRouter.of(context).replace('/chat-select-screen'); + break; + case 4: + break; + case 5: + final RenderBox overlay = Overlay.of(context).context.findRenderObject() as RenderBox; + final size = overlay.size; + showMenu( + context: context, + color: Colors.white, + shadowColor: Colors.white, + surfaceTintColor: Colors.white, + items: [ + PopupMenuItem( + child: CustomNavigationMenu(), + ) + ], + position: RelativeRect.fromLTRB(size.width, size.height, 0, 0), + ); + break; + } + }, + selectedFontSize: 12, + unselectedFontSize: 12, + currentIndex: currentBottomNavBarIndex, + items: [ + BottomNavigationBarItem( + label: '', + icon: SvgPicture.asset(ImageConstant.imgLogo), + ), + BottomNavigationBarItem( + label: 'Лента', + icon: SvgPicture.asset(ImageConstant.imgFeed), + ), + BottomNavigationBarItem( + label: "diary".tr, + icon: SvgPicture.asset(ImageConstant.imgDiary), + ), + BottomNavigationBarItem( + label: "chat".tr, + icon: SvgPicture.asset(ImageConstant.imgChat), + ), + BottomNavigationBarItem( + label: '', + icon: SvgPicture.asset(ImageConstant.imgBell), + ), + BottomNavigationBarItem( + label: '', + icon: Image.asset( + ImageConstant.imgEllipse1, + width: 32, + height: 32, + ), + ), + ], + ), + ); + } +} // ignore: must_be_immutable class CustomBottomBar extends StatefulWidget { @@ -18,33 +118,38 @@ class CustomBottomBarState extends State { List bottomMenuList = [ BottomMenuModel( - icon: ImageConstant.imgLogo, - activeIcon: ImageConstant.imgLogo, - type: BottomBarEnum.tf, - image: false), + icon: ImageConstant.imgLogo, + activeIcon: ImageConstant.imgLogo, + type: BottomBarEnum.tf, + image: false, + ), BottomMenuModel( - icon: ImageConstant.imgFeed, - activeIcon: ImageConstant.imgFeed, - title: "Лента", - type: BottomBarEnum.tf, - image: false), + icon: ImageConstant.imgFeed, + activeIcon: ImageConstant.imgFeed, + title: "Лента", + type: BottomBarEnum.tf, + image: false, + ), BottomMenuModel( - icon: ImageConstant.imgDiary, - activeIcon: ImageConstant.imgDiary, - title: "diary".tr, - type: BottomBarEnum.tf, - image: false), + icon: ImageConstant.imgDiary, + activeIcon: ImageConstant.imgDiary, + title: "diary".tr, + type: BottomBarEnum.tf, + image: false, + ), BottomMenuModel( - icon: ImageConstant.imgChat, - activeIcon: ImageConstant.imgChat, - title: "chat".tr, - type: BottomBarEnum.tf, - image: false), + icon: ImageConstant.imgChat, + activeIcon: ImageConstant.imgChat, + title: "chat".tr, + type: BottomBarEnum.tf, + image: false, + ), BottomMenuModel( - icon: ImageConstant.imgBell, - activeIcon: ImageConstant.imgBell, - type: BottomBarEnum.tf, - image: false), + icon: ImageConstant.imgBell, + activeIcon: ImageConstant.imgBell, + type: BottomBarEnum.tf, + image: false, + ), BottomMenuModel( icon: ImageConstant.imgEllipse1, activeIcon: ImageConstant.imgEllipse1, @@ -84,16 +189,16 @@ class CustomBottomBarState extends State { switch (index) { case 0: - Navigator.pushNamed(context, AppRoutes.myProfile); + Navigator.pushReplacementNamed(context, AppRoutes.myProfile); break; case 1: - Navigator.pushNamed(context, AppRoutes.feedScreen); + Navigator.pushReplacementNamed(context, AppRoutes.feedScreen); break; case 2: - Navigator.pushNamed(context, AppRoutes.diaryEdit); + Navigator.pushReplacementNamed(context, AppRoutes.diaryEdit); break; case 3: - Navigator.pushNamed(context, AppRoutes.chatSelectScreen); + Navigator.pushReplacementNamed(context, AppRoutes.chatSelectScreen); break; } } @@ -131,8 +236,7 @@ class CustomBottomBarState extends State { imagePath: bottomMenuList[index].icon, height: iconHeight, width: iconWidth, - color: - isSelected ? theme.colorScheme.primary : appTheme.blueGray400, + color: isSelected ? theme.colorScheme.primary : appTheme.blueGray400, ), if (bottomMenuList[index].image) GestureDetector( @@ -143,16 +247,14 @@ class CustomBottomBarState extends State { surfaceTintColor: Colors.white, position: RelativeRect.fromLTRB( // Указываем позицию справа по центру - MediaQuery.of(context).size.width - - 50, // Расположение от правого края + MediaQuery.of(context).size.width - 50, // Расположение от правого края kToolbarHeight + 340, // Расположение от верхнего края 0, // Не смещаем по горизонтали 0, // Не смещаем по вертикали ), items: [ PopupMenuItem( - child: - CustomNavigationMenu(), // Используйте ваше меню здесь + child: CustomNavigationMenu(), // Используйте ваше меню здесь ) ], ), @@ -160,16 +262,13 @@ class CustomBottomBarState extends State { imagePath: ImageConstant.imgEllipse1, ), ), - if (bottomMenuList[index].title != - null) // Проверяем, существует ли title + if (bottomMenuList[index].title != null) // Проверяем, существует ли title Padding( padding: EdgeInsets.only(top: 8.v), child: Text( bottomMenuList[index].title!, style: CustomTextStyles.bodySmallMulishBluegray80012.copyWith( - color: isSelected - ? theme.colorScheme.primary - : appTheme.blueGray800, + color: isSelected ? theme.colorScheme.primary : appTheme.blueGray800, ), ), ), diff --git a/pubspec.yaml b/pubspec.yaml index 8f1a6f4..26be607 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,8 +1,8 @@ name: eatvio_app description: A new Flutter project. version: 0.0.0+1 -environment: - sdk: '>=2.12.0 <3.0.0' +environment: + sdk: '>=3.3.4 <4.0.0' dependencies: flutter: sdk: flutter @@ -27,6 +27,8 @@ dependencies: image_picker: ^1.0.7 flutter_typeahead: ^5.2.0 bloc_test: ^9.1.6 + # navigation + go_router: ^14.1.4 dev_dependencies: dart_code_metrics: ^5.7.6 diff --git a/test/diary_test.dart b/test/diary_test.dart index 224b05a..c7570d3 100644 --- a/test/diary_test.dart +++ b/test/diary_test.dart @@ -16,10 +16,8 @@ void main() { ); });*/ - testWidgets('renders _buildDiaryEditScreen when in loaded state', - (WidgetTester tester) async { - final diaryEditBloc = - DiaryEditBloc(DiaryEditState(diaryEditModelObj: DiaryEditModel())); + testWidgets('renders _buildDiaryEditScreen when in loaded state', (WidgetTester tester) async { + final diaryEditBloc = DiaryEditBloc(DiaryEditState(diaryEditModelObj: DiaryEditModel())); final loadedState = DiaryEditLoadedState( diaryId: 1, selectedSatiety: 4, @@ -36,9 +34,7 @@ void main() { MaterialApp( home: BlocProvider.value( value: diaryEditBloc, - child: DiaryEditScreen( - onInitialize: () {}, - ), + child: DiaryEditScreen(), ), ), );