From ba9f4a35842a22d4a251d23c69c3cd572241a8ab Mon Sep 17 00:00:00 2001 From: misha Date: Thu, 13 Nov 2025 10:12:11 +0300 Subject: [PATCH] improvements: added loaders --- .../auth/presentation/pages/auth_screen.dart | 8 +- .../pages/blog_post_create_screen.dart | 17 ++-- .../pages/recipe_create_screen.dart | 91 ++++++++++--------- .../presentation/pages/diary_screen.dart | 36 ++++++-- .../widgets/finish_day_button_widget.dart | 13 +-- lib/main.dart | 2 +- 6 files changed, 97 insertions(+), 70 deletions(-) diff --git a/lib/features/auth/presentation/pages/auth_screen.dart b/lib/features/auth/presentation/pages/auth_screen.dart index 2a63de39..0220c9fb 100644 --- a/lib/features/auth/presentation/pages/auth_screen.dart +++ b/lib/features/auth/presentation/pages/auth_screen.dart @@ -411,7 +411,7 @@ class _AuthScreenState extends State { _isLoading = false; }); }, - label: state is AuthStateLoading + label: state is AuthStateLoading || _isLoading ? const CupertinoActivityIndicator() : Text('Войти', style: theme.textTheme.titleSmall!.copyWith(color: Colors.white)), ), @@ -426,8 +426,8 @@ class _AuthScreenState extends State { elevation: 0, focusElevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), - backgroundColor: state is AuthStateLoading ? Colors.black.withAlpha(50) : appTheme.blueA400, - onPressed: state is AuthStateLoading + backgroundColor: state is AuthStateLoading || _isLoading ? Colors.black.withAlpha(50) : appTheme.blueA400, + onPressed: state is AuthStateLoading || _isLoading ? null : () async { await showDialog( @@ -487,7 +487,7 @@ class _AuthScreenState extends State { }, ); }, - label: state is AuthStateLoading + label: state is AuthStateLoading || _isLoading ? const CupertinoActivityIndicator() : const Icon(Icons.qr_code_scanner_rounded, color: Colors.white), ), diff --git a/lib/features/blog_post_create/presentation/pages/blog_post_create_screen.dart b/lib/features/blog_post_create/presentation/pages/blog_post_create_screen.dart index 48536468..98cb0053 100644 --- a/lib/features/blog_post_create/presentation/pages/blog_post_create_screen.dart +++ b/lib/features/blog_post_create/presentation/pages/blog_post_create_screen.dart @@ -5,6 +5,7 @@ import 'package:eatvio_app/core/app_config.dart'; import 'package:eatvio_app/core/app_router.dart'; import 'package:eatvio_app/core/permission_handler/permission_handler.dart'; import 'package:eatvio_app/core/theme/app_decoration.dart'; +import 'package:eatvio_app/core/widgets/loader.dart'; import 'package:eatvio_app/core/widgets/show_error_dialog.dart'; import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit/blog_post_create_cubit.dart'; import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit/blog_post_create_state.dart'; @@ -620,16 +621,16 @@ class _BlogPostCreateScreenState extends State { } }, borderRadius: BorderRadius.circular(16), - child: Ink( - width: MediaQuery.of(context).size.width - 32, - height: 52, - decoration: BoxDecoration(borderRadius: BorderRadius.circular(16), color: appTheme.blueA400), - child: isLoading - ? const Center(child: CircularProgressIndicator()) - : const Center( + child: isLoading + ? const Center(child: Loader()) + : Ink( + width: MediaQuery.of(context).size.width - 32, + height: 52, + decoration: BoxDecoration(borderRadius: BorderRadius.circular(16), color: appTheme.blueA400), + child: const Center( child: Text('Создать', style: TextStyle(color: Colors.white)), ), - ), + ), ); } diff --git a/lib/features/create_recipe/presentation/pages/recipe_create_screen.dart b/lib/features/create_recipe/presentation/pages/recipe_create_screen.dart index c8a6d4ef..932852ac 100644 --- a/lib/features/create_recipe/presentation/pages/recipe_create_screen.dart +++ b/lib/features/create_recipe/presentation/pages/recipe_create_screen.dart @@ -33,7 +33,7 @@ class _RecipeCreateScreenState extends State { late final CreateRecipeCubit createRecipeCubit; final GlobalKey _mdKey = GlobalKey(); bool _mdToolbarVisible = false; - + bool _isRecipeBeingPublished = false; @override void initState() { super.initState(); @@ -58,9 +58,7 @@ class _RecipeCreateScreenState extends State { return Scaffold( backgroundColor: const Color(0xFFF0F2F5), floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, - floatingActionButton: _mdToolbarVisible && _mdKey.currentState != null - ? _mdKey.currentState!.buildToolbar(context) - : null, + floatingActionButton: _mdToolbarVisible && _mdKey.currentState != null ? _mdKey.currentState!.buildToolbar(context) : null, appBar: AppBar( title: widget.recipe != null ? const Text('Редактирование рецепта') : const Text('Добавление рецепта'), centerTitle: true, @@ -83,44 +81,55 @@ class _RecipeCreateScreenState extends State { builder: (context, state) { return Builder( builder: (context) { - return InkWell( - key: AppKeys.recipeCreateScreenPublishButton, - onTap: () async { - if (context.read().validateAll()) { - final isSuccess = await context.read().publishRecipe( - widget.recipe == null ? false : true, - widget.recipe?.slug, + return _isRecipeBeingPublished + ? const Center(child: Loader()) + : InkWell( + key: AppKeys.recipeCreateScreenPublishButton, + onTap: () async { + setState(() { + _isRecipeBeingPublished = true; + }); + if (context.read().validateAll()) { + final isSuccess = await context.read().publishRecipe( + widget.recipe == null ? false : true, + widget.recipe?.slug, + ); + setState(() { + _isRecipeBeingPublished = false; + }); + if (isSuccess) { + Future.delayed(const Duration(milliseconds: 500), () { + if (!context.mounted) return; + context.pop(); + showBottomSheetWithBlur( + userRootNavigator: true, + context: context, + child: SuccessPublishedRecipeBottomSheet(title: state.entity.title ?? 'Рецепт'), + ); + }); + } else { + if (!context.mounted) return; + showErrorDialog(context, title: '${s.could_not_publish_recipe} ${state.entity.title}'); + } + } + setState(() { + _isRecipeBeingPublished = false; + }); + }, + borderRadius: BorderRadius.circular(20), + child: Ink( + width: MediaQuery.of(context).size.width - 32, + decoration: BoxDecoration(color: appTheme.blueA400, borderRadius: BorderRadius.circular(20)), + child: Container( + padding: const EdgeInsets.all(16), + alignment: Alignment.center, + child: Text( + widget.recipe != null ? 'Обновить' : 'Опубликовать', + style: const TextStyle(fontSize: 16, color: Colors.white, fontWeight: FontWeight.w600), + ), + ), + ), ); - if (isSuccess) { - Future.delayed(const Duration(milliseconds: 500), () { - if (!context.mounted) return; - context.pop(); - showBottomSheetWithBlur( - userRootNavigator: true, - context: context, - child: SuccessPublishedRecipeBottomSheet(title: state.entity.title ?? 'Рецепт'), - ); - }); - } else { - if (!context.mounted) return; - showErrorDialog(context, title: '${s.could_not_publish_recipe} ${state.entity.title}'); - } - } - }, - borderRadius: BorderRadius.circular(20), - child: Ink( - width: MediaQuery.of(context).size.width - 32, - decoration: BoxDecoration(color: appTheme.blueA400, borderRadius: BorderRadius.circular(20)), - child: Container( - padding: const EdgeInsets.all(16), - alignment: Alignment.center, - child: Text( - widget.recipe != null ? 'Обновить' : 'Опубликовать', - style: const TextStyle(fontSize: 16, color: Colors.white, fontWeight: FontWeight.w600), - ), - ), - ), - ); }, ); }, diff --git a/lib/features/diary/presentation/pages/diary_screen.dart b/lib/features/diary/presentation/pages/diary_screen.dart index ae6f9e10..607e97d2 100644 --- a/lib/features/diary/presentation/pages/diary_screen.dart +++ b/lib/features/diary/presentation/pages/diary_screen.dart @@ -44,16 +44,24 @@ class UserDiaryScreen extends StatelessWidget { } } -class DiaryScreenContent extends StatelessWidget { +class DiaryScreenContent extends StatefulWidget { const DiaryScreenContent({super.key, this.diaryId, this.currentPlanDay}); final int? diaryId; final int? currentPlanDay; + + @override + State createState() => _DiaryScreenContentState(); +} + +class _DiaryScreenContentState extends State { + bool _isDiaryFinishing = false; + @override Widget build(BuildContext context) { final s = S.of(context); return MultiBlocProvider( providers: [ - BlocProvider(create: (BuildContext context) => sl()..fetchDiaryV2(needLoading: true, diaryId: diaryId)), + BlocProvider(create: (BuildContext context) => sl()..fetchDiaryV2(needLoading: true, diaryId: widget.diaryId)), BlocProvider(create: (context) => sl()), ], child: BlocBuilder( @@ -63,13 +71,13 @@ class DiaryScreenContent extends StatelessWidget { } if (state is DiaryStateError) { return Scaffold( - body: DataErrorWidget(onReload: () => context.read().fetchDiaryV2(needLoading: false, diaryId: diaryId)), + body: DataErrorWidget(onReload: () => context.read().fetchDiaryV2(needLoading: false, diaryId: widget.diaryId)), ); } return Scaffold( backgroundColor: Colors.grey.shade200, - appBar: diaryId == null + appBar: widget.diaryId == null ? AppBar(toolbarHeight: 0) : AppBar( title: Text( @@ -83,7 +91,7 @@ class DiaryScreenContent extends StatelessWidget { physics: const BouncingScrollPhysics(), child: Column( children: [ - if (diaryId == null) const CalendarWidget(), + if (widget.diaryId == null) const CalendarWidget(), if (state is DiaryStateLoading) ...[const SizedBox(height: 60), const Loader()], if (state is DiaryStateLoaded) WakeupAndRetireWidget( @@ -116,7 +124,23 @@ class DiaryScreenContent extends StatelessWidget { if (state is DiaryStateLoaded && !state.isConsultantView && !state.isDiaryFinished && !state.isDiaryCommented) Padding( padding: const EdgeInsets.only(top: 12), - child: FinishDayButtonWidget(diaryDay: state.diary), + child: _isDiaryFinishing + ? const Center(child: Loader()) + : FinishDayButtonWidget( + onPressed: () async { + setState(() { + _isDiaryFinishing = true; + }); + await context.read().finishDiaryDay( + diaryId: state.diary.id, + wakeUp: state.diary.wakeup.toString(), + retire: state.diary.retire.toString(), + ); + setState(() { + _isDiaryFinishing = false; + }); + }, + ), ), if (state is DiaryStateLoaded && state.isConsultantView && !state.isDiaryCommented) ...[ const SizedBox(height: 16), diff --git a/lib/features/diary/presentation/widgets/finish_day_button_widget.dart b/lib/features/diary/presentation/widgets/finish_day_button_widget.dart index b3f170ec..af58216a 100644 --- a/lib/features/diary/presentation/widgets/finish_day_button_widget.dart +++ b/lib/features/diary/presentation/widgets/finish_day_button_widget.dart @@ -1,13 +1,10 @@ -import 'package:eatvio_app/features/diary/entities/diary_day_entity.dart'; -import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; class FinishDayButtonWidget extends StatefulWidget { - final DiaryDayEntity diaryDay; + final Future Function() onPressed; - const FinishDayButtonWidget({super.key, required this.diaryDay}); + const FinishDayButtonWidget({super.key, required this.onPressed}); @override State createState() => _FinishDayButtonWidgetState(); @@ -17,11 +14,7 @@ class _FinishDayButtonWidgetState extends State { @override Widget build(BuildContext context) { return InkWell( - onTap: () => context.read().finishDiaryDay( - diaryId: widget.diaryDay.id, - wakeUp: widget.diaryDay.wakeup.toString(), - retire: widget.diaryDay.retire.toString(), - ), + onTap: widget.onPressed, borderRadius: BorderRadius.circular(20), child: Ink( padding: const EdgeInsets.symmetric(horizontal: 8), diff --git a/lib/main.dart b/lib/main.dart index af6db638..b6171864 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,7 +9,7 @@ import 'package:flutter_portal/flutter_portal.dart'; import 'package:keyboard_dismisser/keyboard_dismisser.dart'; Future main() async { - await AppInitializer.initialize(buildType: AppBuildType.prod); + await AppInitializer.initialize(buildType: AppBuildType.dev); await AppInitializer.runAppWithSentry(() => runApp(const MyApp())); }