From ed850911ebd3a0a55d65a337fedf37938acfb3a0 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 28 Nov 2024 17:26:35 +0300 Subject: [PATCH] feat(registration): hide/show password --- .../diary/presentation/bloc/diary_cubit.dart | 6 +- .../presentation/pages/diary_screen.dart | 1 + .../presentation/widgets/meal_widget.dart | 2 + .../widgets/photo_gallery_widget.dart | 359 ++++++++++-------- .../pages/credentials_screen.dart | 28 +- 5 files changed, 223 insertions(+), 173 deletions(-) diff --git a/lib/features/diary/presentation/bloc/diary_cubit.dart b/lib/features/diary/presentation/bloc/diary_cubit.dart index 386755ad..1aa7c0da 100644 --- a/lib/features/diary/presentation/bloc/diary_cubit.dart +++ b/lib/features/diary/presentation/bloc/diary_cubit.dart @@ -128,12 +128,11 @@ class DiaryCubit extends Cubit { uploadPhotos({ required List files, required int diaryId, - required int mealId, }) async { await diaryRepository.uploadPhotos( files: await ImageCompressUtility.compressFilesList(files), diaryId: diaryId, - mealId: mealId, + mealId: selectedMeal, ); await fetchDiary(); } @@ -141,12 +140,11 @@ class DiaryCubit extends Cubit { deletePhoto({ required DiaryPhotoModel photo, required int diaryId, - required int mealId, }) async { await diaryRepository.deletePhoto( photo: photo, diaryId: diaryId, - mealId: mealId, + mealId: selectedMeal, ); await fetchDiary(); } diff --git a/lib/features/diary/presentation/pages/diary_screen.dart b/lib/features/diary/presentation/pages/diary_screen.dart index 7d0d09fa..b5e65795 100644 --- a/lib/features/diary/presentation/pages/diary_screen.dart +++ b/lib/features/diary/presentation/pages/diary_screen.dart @@ -28,6 +28,7 @@ class _DiaryScreenState extends State { toolbarHeight: 0, ), body: SingleChildScrollView( + padding: const EdgeInsets.only(bottom: 16), physics: const BouncingScrollPhysics(), child: Column( children: [ diff --git a/lib/features/diary/presentation/widgets/meal_widget.dart b/lib/features/diary/presentation/widgets/meal_widget.dart index fb981147..3b2acdc4 100644 --- a/lib/features/diary/presentation/widgets/meal_widget.dart +++ b/lib/features/diary/presentation/widgets/meal_widget.dart @@ -1,5 +1,6 @@ import 'package:eatvio_app/features/diary/presentation/widgets/add_product_button_widget.dart'; import 'package:eatvio_app/features/diary/presentation/widgets/meal_button_widget.dart'; +import 'package:eatvio_app/features/diary/presentation/widgets/photo_gallery_widget.dart'; import 'package:eatvio_app/features/diary/presentation/widgets/products_list_widget.dart'; import 'package:flutter/material.dart'; @@ -45,6 +46,7 @@ class _MealWidgetState extends State { ProductsListWidget(), const SizedBox(height: 12), AddProductButtonWidget(), + PhotoGalleryWidget(), ], ), ), diff --git a/lib/features/diary/presentation/widgets/photo_gallery_widget.dart b/lib/features/diary/presentation/widgets/photo_gallery_widget.dart index 6388136b..a56b6695 100644 --- a/lib/features/diary/presentation/widgets/photo_gallery_widget.dart +++ b/lib/features/diary/presentation/widgets/photo_gallery_widget.dart @@ -1,167 +1,192 @@ -// import 'package:flutter/material.dart'; -// -// class PhotoGalleryWidget extends StatefulWidget { -// const PhotoGalleryWidget({super.key}); -// -// @override -// State createState() => _PhotoGalleryWidgetState(); -// } -// -// class _PhotoGalleryWidgetState extends State { -// showImagePicker(DiaryDayModel diaryDay) { -// showCupertinoModalPopup( -// context: context, -// builder: (context) => CupertinoActionSheet( -// actions: [ -// CupertinoActionSheetAction( -// onPressed: () async { -// Navigator.of(context).pop(); -// final picker = ImagePicker(); -// XFile? image = await picker.pickImage(source: ImageSource.camera); -// if (image != null) -// diaryCubit.uploadPhotos( -// files: [File(image.path)], -// diaryId: diaryDay.id, -// mealId: diaryDay.meals[currentSelectedMealNumber - 1].id, -// date: _selectedDay.toString(), -// ); -// }, -// child: const Row( -// children: const [ -// Icon(Icons.photo_camera), -// SizedBox(width: 8), -// Text('Фото'), -// ], -// ), -// ), -// CupertinoActionSheetAction( -// onPressed: () async { -// Navigator.of(context).pop(); -// final picker = ImagePicker(); -// List? images = await picker.pickMultiImage(); -// if (images.isNotEmpty) -// diaryCubit.uploadPhotos( -// files: images.map((f) => File(f.path)).toList(), -// diaryId: diaryDay.id, -// mealId: diaryDay.meals[currentSelectedMealNumber - 1].id, -// date: _selectedDay.toString(), -// ); -// }, -// child: const Row( -// children: const [ -// Icon(Icons.photo), -// SizedBox(width: 8), -// Text('Галерея'), -// ], -// ), -// ), -// ], -// cancelButton: CupertinoActionSheetAction( -// onPressed: () => Navigator.of(context).pop(), -// isDestructiveAction: true, -// child: const Text('Отмена'), -// ), -// ), -// ); -// } -// -// @override -// Widget build(BuildContext context) { -// return Column( -// mainAxisSize: MainAxisSize.min, -// crossAxisAlignment: CrossAxisAlignment.start, -// children: [ -// Stack( -// alignment: Alignment.topRight, -// children: [ -// ClipRRect( -// borderRadius: BorderRadius.circular(12), -// child: CachedNetworkImage( -// imageUrl: baseUrl + '/storage/' + photos[currentSelectedPhotoIndex].path, -// ), -// ), -// Padding( -// padding: const EdgeInsets.all(12), -// child: IconButton.filled( -// style: IconButton.styleFrom( -// backgroundColor: Colors.white, -// elevation: 0, -// ), -// onPressed: () async { -// await diaryCubit.deletePhoto( -// photo: photos[currentSelectedPhotoIndex], -// diaryId: diaryDay.id, -// mealId: diaryDay.meals[currentSelectedMealNumber - 1].id, -// date: _selectedDay.toString(), -// ); -// setState(() { -// if (currentSelectedPhotoIndex == photos.length - 1 && currentSelectedPhotoIndex != 0) { -// currentSelectedPhotoIndex = photos.length - 2; -// } -// }); -// }, -// icon: Icon( -// Icons.close_rounded, -// color: Colors.grey, -// ), -// ), -// ), -// ], -// ), -// const SizedBox(height: 8), -// SingleChildScrollView( -// physics: const BouncingScrollPhysics(), -// scrollDirection: Axis.horizontal, -// child: Row( -// mainAxisSize: MainAxisSize.min, -// mainAxisAlignment: MainAxisAlignment.start, -// children: [ -// for (int i = 0; i < photos.length + 1; i++) ...[ -// if (i == photos.length) -// Padding( -// padding: const EdgeInsets.only(left: 8), -// child: InkWell( -// onTap: () => _showImagePicker(diaryDay), -// borderRadius: BorderRadius.circular(8), -// child: Ink( -// height: 52, -// width: 52, -// decoration: BoxDecoration( -// borderRadius: BorderRadius.circular(8), -// color: Color(0xFFF1F2F6), -// ), -// child: Icon( -// Icons.add_rounded, -// color: appTheme.blueA400, -// ), -// ), -// ), -// ), -// if (i < photos.length) -// GestureDetector( -// onTap: () { -// setState(() { -// currentSelectedPhotoIndex = i; -// }); -// }, -// child: ClipRRect( -// borderRadius: BorderRadius.circular(8), -// child: SizedBox( -// height: 52, -// width: 52, -// child: CachedNetworkImage( -// imageUrl: baseUrl + '/storage/' + photos[i].path, -// fit: BoxFit.fitWidth, -// ), -// ), -// ), -// ), -// if (i < photos.length - 1) const SizedBox(width: 8), -// ], -// ], -// ), -// ), -// ], -// ); -// } -// } +import 'dart:io'; + +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:eatvio_app/core/const.dart'; +import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:eatvio_app/features/diary/models/diary_day_model.dart'; +import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart'; +import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:image_picker/image_picker.dart'; + +class PhotoGalleryWidget extends StatefulWidget { + const PhotoGalleryWidget({super.key}); + + @override + State createState() => _PhotoGalleryWidgetState(); +} + +class _PhotoGalleryWidgetState extends State { + int currentSelectedPhotoIndex = 0; + + showImagePicker(DiaryDayModel diaryDay) { + showCupertinoModalPopup( + context: context, + builder: (context) => CupertinoActionSheet( + actions: [ + CupertinoActionSheetAction( + onPressed: () async { + Navigator.of(context).pop(); + final picker = ImagePicker(); + XFile? image = await picker.pickImage(source: ImageSource.camera); + if (image != null) + context.read().uploadPhotos( + files: [File(image.path)], + diaryId: diaryDay.id, + ); + }, + child: const Row( + children: const [ + Icon(Icons.photo_camera), + SizedBox(width: 8), + Text('Фото'), + ], + ), + ), + CupertinoActionSheetAction( + onPressed: () async { + Navigator.of(context).pop(); + final picker = ImagePicker(); + List? images = await picker.pickMultiImage(); + if (images.isNotEmpty) + context.read().uploadPhotos( + files: images.map((f) => File(f.path)).toList(), + diaryId: diaryDay.id, + ); + }, + child: const Row( + children: const [ + Icon(Icons.photo), + SizedBox(width: 8), + Text('Галерея'), + ], + ), + ), + ], + cancelButton: CupertinoActionSheetAction( + onPressed: () => Navigator.of(context).pop(), + isDestructiveAction: true, + child: const Text('Отмена'), + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is DiaryStateLoaded) { + if (state.diary.meals[state.selectedMeal].photos.isEmpty) { + return GestureDetector( + onTap: () => showImagePicker(state.diary), + child: SvgPicture.asset( + width: MediaQuery.of(context).size.width - 32, + 'assets/no_one_image_was_added.svg', + ), + ); + } else { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + alignment: Alignment.topRight, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(12), + child: CachedNetworkImage( + imageUrl: baseUrl + '/storage/' + state.diary.meals[state.selectedMeal].photos[currentSelectedPhotoIndex].path, + ), + ), + Padding( + padding: const EdgeInsets.all(12), + child: IconButton.filled( + style: IconButton.styleFrom( + backgroundColor: Colors.white, + elevation: 0, + ), + onPressed: () async { + await context.read().deletePhoto( + photo: state.diary.meals[state.selectedMeal].photos[currentSelectedPhotoIndex], + diaryId: state.diary.id, + ); + setState(() { + if (currentSelectedPhotoIndex == state.diary.meals[state.selectedMeal].photos.length - 1 && currentSelectedPhotoIndex != 0) { + currentSelectedPhotoIndex = state.diary.meals[state.selectedMeal].photos.length - 2; + } + }); + }, + icon: Icon( + Icons.close_rounded, + color: Colors.grey, + ), + ), + ), + ], + ), + const SizedBox(height: 8), + SingleChildScrollView( + physics: const BouncingScrollPhysics(), + scrollDirection: Axis.horizontal, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + for (int i = 0; i < state.diary.meals[state.selectedMeal].photos.length + 1; i++) ...[ + if (i == state.diary.meals[state.selectedMeal].photos.length) + Padding( + padding: const EdgeInsets.only(left: 8), + child: InkWell( + onTap: () => showImagePicker(state.diary), + borderRadius: BorderRadius.circular(8), + child: Ink( + height: 52, + width: 52, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + color: Color(0xFFF1F2F6), + ), + child: Icon( + Icons.add_rounded, + color: appTheme.blueA400, + ), + ), + ), + ), + if (i < state.diary.meals[state.selectedMeal].photos.length) + GestureDetector( + onTap: () { + setState(() { + currentSelectedPhotoIndex = i; + }); + }, + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: SizedBox( + height: 52, + width: 52, + child: CachedNetworkImage( + imageUrl: baseUrl + '/storage/' + state.diary.meals[state.selectedMeal].photos[i].path, + fit: BoxFit.fitWidth, + ), + ), + ), + ), + if (i < state.diary.meals[state.selectedMeal].photos.length - 1) const SizedBox(width: 8), + ], + ], + ), + ), + ], + ); + } + } + return const SizedBox.shrink(); + }, + ); + } +} diff --git a/lib/features/registration/presentation/pages/credentials_screen.dart b/lib/features/registration/presentation/pages/credentials_screen.dart index b5aa404a..ee9ea21d 100644 --- a/lib/features/registration/presentation/pages/credentials_screen.dart +++ b/lib/features/registration/presentation/pages/credentials_screen.dart @@ -21,6 +21,8 @@ class _CredentialsScreenState extends State { final passwordConfirmationController = TextEditingController(); + bool hidePassword = true; + @override void initState() { super.initState(); @@ -55,10 +57,21 @@ class _CredentialsScreenState extends State { Text(s.password), SizedBox(height: 4), CustomTextFormField( - obscureText: true, + obscureText: hidePassword, controller: passwordController, hintText: 'Введите пароль', textInputAction: TextInputAction.next, + suffix: IconButton( + onPressed: () { + setState(() { + hidePassword = !hidePassword; + }); + }, + icon: Icon( + hidePassword ? Icons.visibility_outlined : Icons.visibility_off_outlined, + color: Colors.grey, + ), + ), onChanged: (newValue) { final registrationCubit = context.read(); registrationCubit.updateSheet(registrationCubit.state.sheet.copyWith(password: newValue)); @@ -68,10 +81,21 @@ class _CredentialsScreenState extends State { Text(s.password_confirmation), SizedBox(height: 4), CustomTextFormField( - obscureText: true, + obscureText: hidePassword, controller: passwordConfirmationController, textInputAction: TextInputAction.done, hintText: 'Подтвердите пароль', + suffix: IconButton( + onPressed: () { + setState(() { + hidePassword = !hidePassword; + }); + }, + icon: Icon( + hidePassword ? Icons.visibility_outlined : Icons.visibility_off_outlined, + color: Colors.grey, + ), + ), onChanged: (newValue) { final registrationCubit = context.read(); registrationCubit.updateSheet(registrationCubit.state.sheet.copyWith(passwordConfirmation: newValue));