diff --git a/lib/presentation/notification_preferences/bloc/notification_preferences_bloc.dart b/lib/presentation/notification_preferences/bloc/notification_preferences_bloc.dart index deff79c..af19983 100644 --- a/lib/presentation/notification_preferences/bloc/notification_preferences_bloc.dart +++ b/lib/presentation/notification_preferences/bloc/notification_preferences_bloc.dart @@ -1,3 +1,5 @@ +// lib/presentation/notification_preferences/bloc/notification_preferences_bloc.dart + import 'package:dio/dio.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; @@ -26,12 +28,12 @@ class NotificationPreferencesBloc LoadCategories event, Emitter emit) { final categories = [ CategoryEntity(id: "e33dd7f9-5b20-4273-8eea-59da6ca5f206", name: 'لوازم دیجیتال', icon: Assets.icons.digital), - CategoryEntity(id: "b73a868a-a2d2-4d96-8fd4-615327ed9629", name: 'کافی‌شاپ', icon: Assets.icons.coffeeshop), + CategoryEntity(id: "b73a868a-a2d2-4d96-8fd4-615327ed9629", name: 'کافی شاپ', icon: Assets.icons.coffeeshop), // Change Here CategoryEntity(id: "b5881239-bfd5-4c27-967a-187316a7e0b7", name: 'رستوران', icon: Assets.icons.resturan), - CategoryEntity(id: "6803b940-3e19-48cd-9190-28d9f25421ff", name: 'فست‌فود', icon: Assets.icons.fastfood), + CategoryEntity(id: "6803b940-3e19-48cd-9190-28d9f25421ff", name: 'فست فود', icon: Assets.icons.fastfood), // Change Here CategoryEntity(id: "71e371f8-a47a-4a58-aee6-4ed0f26bf29b", name: 'پوشاک', icon: Assets.icons.pooshak), CategoryEntity(id: "42acff41-1165-4e62-89b9-58db7329ec3a", name: 'تریا', icon: Assets.icons.teria), - CategoryEntity(id: "2f38918c-5566-4aec-a0a9-2c7c48b1e878", name: 'کیف‌وکفش', icon: Assets.icons.kafsh), + CategoryEntity(id: "2f38918c-5566-4aec-a0a9-2c7c48b1e878", name: 'کیف و کفش', icon: Assets.icons.kafsh), // Change Here CategoryEntity(id: "52c51010-3a63-4264-a350-e011c889f3dd", name: 'سینما', icon: Assets.icons.cinama), CategoryEntity(id: "34185954-f79f-4b9e-8eb2-1702679c40a0", name: 'لوازم آرایشی', icon: Assets.icons.arayesh), CategoryEntity(id: "e4517b0c-aacf-4758-94bd-85f45062980f", name: 'طلا و زیورآلات', icon: Assets.icons.tala), @@ -126,6 +128,4 @@ class NotificationPreferencesBloc ResetSubmissionStatus event, Emitter emit) { emit(state.copyWith(submissionSuccess: false)); } -} - - +} \ No newline at end of file diff --git a/lib/presentation/pages/comment_page.dart b/lib/presentation/pages/comment_page.dart index 5648030..53a0c24 100644 --- a/lib/presentation/pages/comment_page.dart +++ b/lib/presentation/pages/comment_page.dart @@ -10,6 +10,7 @@ import 'package:proxibuy/presentation/comment/bloc/comment_bloc.dart'; import 'package:proxibuy/presentation/comment/bloc/comment_event.dart'; import 'package:proxibuy/presentation/comment/bloc/comment_state.dart'; import 'package:proxibuy/presentation/pages/offers_page.dart'; +import 'package:flutter_animate/flutter_animate.dart'; class CommentPage extends StatefulWidget { final String discountId; @@ -39,7 +40,10 @@ class _CommentPageState extends State { ); return; } - final pickedFile = await _picker.pickImage(source: source, imageQuality: 80); + final pickedFile = await _picker.pickImage( + source: source, + imageQuality: 80, + ); if (pickedFile != null) { setState(() { _images.add(File(pickedFile.path)); @@ -47,6 +51,12 @@ class _CommentPageState extends State { } } + void _removeImage(int index) { + setState(() { + _images.removeAt(index); + }); + } + void _showImageSourceActionSheet() { showModalBottomSheet( context: context, @@ -58,7 +68,10 @@ class _CommentPageState extends State { child: Wrap( children: [ ListTile( - leading: const Icon(Icons.photo_library, color: AppColors.primary), + leading: const Icon( + Icons.photo_library, + color: AppColors.primary, + ), title: const Text('انتخاب از گالری'), onTap: () { Navigator.of(context).pop(); @@ -85,113 +98,126 @@ class _CommentPageState extends State { return BlocProvider( create: (context) => CommentBloc(), child: Scaffold( - body: Stack( - children: [ - Positioned.fill( - child: Image.asset(Assets.images.userinfo.path, fit: BoxFit.cover), + backgroundColor: Colors.grey[50], + appBar: _buildCustomAppBar(), + body: SingleChildScrollView( + padding: const EdgeInsets.all(24.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildHeader(), + const SizedBox(height: 24), + Center(child: _buildRatingCard()), + const SizedBox(height: 16), + _buildCommentCard(), + const SizedBox(height: 16), + _buildImagePickerSection(), + const SizedBox(height: 32), + _buildActionButtons(), + ] + .animate(interval: 100.ms) + .fadeIn(duration: 400.ms) + .slideY(begin: 0.2, curve: Curves.easeOut), + ), + ), + ), + ); + } + + PreferredSizeWidget _buildCustomAppBar() { + return PreferredSize( + preferredSize: const Size.fromHeight(70.0), + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.vertical( + bottom: Radius.circular(15), + ), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.08), + blurRadius: 10, + offset: const Offset(0, 4), ), - DraggableScrollableSheet( - initialChildSize: 0.65, - minChildSize: 0.65, - maxChildSize: 0.65, - builder: (context, scrollController) { - return Container( - decoration: const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.vertical(top: Radius.circular(32)), + ], + ), + child: SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + const Text( + 'ثبت نظر', + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.normal, + fontSize: 18, ), - child: SingleChildScrollView( - controller: scrollController, - padding: const EdgeInsets.all(24.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Center( - child: Container( - width: 50, - height: 5, - decoration: BoxDecoration( - color: Colors.grey.withOpacity(0.5), - borderRadius: BorderRadius.circular(12), - ), - ), - ), - const SizedBox(height: 24), - const Text( - 'خریدت با موفقیت انجام شد. منتظر دیدار دوباره‌ات هستیم. لطفا نظرت رو در مورد این تخفیف بهمون بگو.', - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.normal, - height: 1.5, - ), - textAlign: TextAlign.center, - ), - const SizedBox(height: 24), - Center( - child: RatingBar.builder( - initialRating: 0, - minRating: 1, - direction: Axis.horizontal, - itemCount: 5, - itemPadding: const EdgeInsets.symmetric(horizontal: 15.0), - itemBuilder: (context, _) => Icon( - Icons.star, - color: Colors.amber.shade700, - ), - onRatingUpdate: (rating) => setState(() => _rating = rating), - ), - ), - const SizedBox(height: 24), - TextField( - controller: _commentController, - maxLines: 4, - textAlign: TextAlign.right, - decoration: InputDecoration( - labelText: "گوشمون به شماست", - hintText: "نظراتت رو بگو...", - alignLabelWithHint: true, - suffixIcon: Padding( - padding: const EdgeInsets.all(12.0), - child: IconButton( - icon: SvgPicture.asset( - Assets.icons.galleryAdd.path, - color: _images.length >= 2 ? Colors.grey : AppColors.primary, - ), - onPressed: _images.length >= 2 ? null : _showImageSourceActionSheet, - ), - ), - ), - ), - const SizedBox(height: 16), - if (_images.isNotEmpty) - SizedBox( - height: 80, - child: ListView.builder( - scrollDirection: Axis.horizontal, - itemCount: _images.length, - itemBuilder: (context, index) { - return Padding( - padding: const EdgeInsets.only(left: 8.0), - child: ClipRRect( - borderRadius: BorderRadius.circular(12.0), - child: Image.file( - _images[index], - width: 80, - height: 80, - fit: BoxFit.cover, - ), - ), - ); - }, - ), - ), - const SizedBox(height: 24), - _buildActionButtons(), - ], - ), - ), - ); - }, + ), + IconButton( + icon: SvgPicture.asset(Assets.icons.arrowLeft.path), + onPressed: () => Navigator.of(context).pop(), + ), + ], + ), + ), + ), + ), + ); + } + + Widget _buildHeader() { + return Center( + child: Column( + children: [ + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: AppColors.confirm.withOpacity(0.1), + ), + child: Icon( + Icons.check_circle_outline, + color: AppColors.confirm, + size: 50, + ), + ), + const SizedBox(height: 16), + const Text( + 'خریدت با موفقیت انجام شد. منتظر دیدار دوباره‌ات هستیم. لطفا نظرت رو در مورد این تخفیف بهمون بگو.', + style: TextStyle(fontSize: 16, color: Colors.black, height: 1.5), + textAlign: TextAlign.center, + ), + ], + ), + ); + } + + Widget _buildRatingCard() { + return Card( + elevation: 1, + shadowColor: Colors.black12, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + const Text( + "امتیاز شما چقدره؟", + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 16), + RatingBar.builder( + initialRating: 0, + minRating: 1, + direction: Axis.horizontal, + itemCount: 5, + itemPadding: const EdgeInsets.symmetric(horizontal: 7.0), + itemBuilder: + (context, _) => + Icon(Icons.star, color: Colors.amber.shade700), + onRatingUpdate: (rating) => setState(() => _rating = rating), ), ], ), @@ -199,6 +225,108 @@ class _CommentPageState extends State { ); } + Widget _buildCommentCard() { + return Padding( + padding: const EdgeInsets.all(8.0), + child: TextField( + controller: _commentController, + maxLines: 4, + textAlign: TextAlign.right, + decoration: InputDecoration( + labelText: "گوشمون به شماست", + hintText: "نظراتت رو بگو...", + alignLabelWithHint: true, + suffixIcon: Padding(padding: const EdgeInsets.all(12.0)), + ), + ), + ); + } + + Widget _buildImagePickerSection() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + "افزودن عکس (اختیاری)", + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 8), + const Text( + "می‌تونی عکس تجربه خریدت رو باهامون به اشتراک بذاری!", + style: TextStyle(fontSize: 14, color: AppColors.hint), + ), + const SizedBox(height: 16), + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [ + ...List.generate(_images.length, (index) { + return Padding( + padding: const EdgeInsets.only(left: 8.0), + child: Stack( + clipBehavior: Clip.none, + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(12.0), + child: Image.file( + _images[index], + width: 80, + height: 80, + fit: BoxFit.cover, + ), + ), + Positioned( + top: -8, + right: -8, + child: GestureDetector( + onTap: () => _removeImage(index), + child: Container( + decoration: BoxDecoration( + color: Colors.red, + shape: BoxShape.circle, + border: Border.all(color: Colors.white, width: 2), + ), + child: const Icon( + Icons.close, + color: Colors.white, + size: 16, + ), + ), + ), + ), + ], + ), + ); + }), + if (_images.length < 2) + GestureDetector( + onTap: _showImageSourceActionSheet, + child: Container( + width: 80, + height: 80, + decoration: BoxDecoration( + color: Colors.grey.shade200, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: Colors.grey.shade400, + width: 1.5, + style: BorderStyle.solid, + ), + ), + child: const Icon( + Icons.add_a_photo_outlined, + color: Colors.grey, + size: 30, + ), + ), + ), + ], + ), + ), + ], + ); + } + Widget _buildActionButtons() { return BlocConsumer( listener: (context, state) { @@ -207,6 +335,7 @@ class _CommentPageState extends State { const SnackBar( content: Text('نظر شما با موفقیت ثبت شد. ممنونیم!'), backgroundColor: Colors.green, + behavior: SnackBarBehavior.floating, ), ); Navigator.of(context).pushAndRemoveUntil( @@ -215,7 +344,11 @@ class _CommentPageState extends State { ); } else if (state is CommentSubmissionFailure) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(state.error), backgroundColor: Colors.red), + SnackBar( + content: Text(state.error), + backgroundColor: Colors.red, + behavior: SnackBarBehavior.floating, + ), ); } }, @@ -226,48 +359,56 @@ class _CommentPageState extends State { SizedBox( width: double.infinity, child: ElevatedButton( - onPressed: isLoading - ? null - : () { - context.read().add( - SubmitComment( - discountId: widget.discountId, - text: _commentController.text, - score: _rating, - images: _images, - ), - ); - }, + onPressed: + isLoading + ? null + : () { + context.read().add( + SubmitComment( + discountId: widget.discountId, + text: _commentController.text, + score: _rating, + images: _images, + ), + ); + }, style: ElevatedButton.styleFrom( backgroundColor: AppColors.confirm, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 16), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(50)), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(50), + ), ), - child: isLoading - ? const SizedBox( - width: 24, - height: 24, - child: CircularProgressIndicator(color: Colors.white), - ) - : const Text('ارسال'), + child: + isLoading + ? const SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator(color: Colors.white), + ) + : const Text('ارسال'), ), ), const SizedBox(height: 12), TextButton( - onPressed: isLoading - ? null - : () { - Navigator.of(context).pushAndRemoveUntil( - MaterialPageRoute(builder: (_) => const OffersPage()), - (route) => false, - ); - }, - child: const Text('رد شدن', style: TextStyle(color: Colors.black)), + onPressed: + isLoading + ? null + : () { + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute(builder: (_) => const OffersPage()), + (route) => false, + ); + }, + child: const Text( + 'رد شدن', + style: TextStyle(color: Colors.black), + ), ), ], ); }, ); } -} \ No newline at end of file +}