background-activity #1

Open
Mr.Jebelli wants to merge 6 commits from background-activity into main
2 changed files with 285 additions and 144 deletions
Showing only changes of commit 60c91e8fbb - Show all commits

View File

@ -1,3 +1,5 @@
// lib/presentation/notification_preferences/bloc/notification_preferences_bloc.dart
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
@ -26,12 +28,12 @@ class NotificationPreferencesBloc
LoadCategories event, Emitter<NotificationPreferencesState> emit) { LoadCategories event, Emitter<NotificationPreferencesState> emit) {
final categories = [ final categories = [
CategoryEntity(id: "e33dd7f9-5b20-4273-8eea-59da6ca5f206", name: 'لوازم دیجیتال', icon: Assets.icons.digital), 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: "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: "71e371f8-a47a-4a58-aee6-4ed0f26bf29b", name: 'پوشاک', icon: Assets.icons.pooshak),
CategoryEntity(id: "42acff41-1165-4e62-89b9-58db7329ec3a", name: 'تریا', icon: Assets.icons.teria), 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: "52c51010-3a63-4264-a350-e011c889f3dd", name: 'سینما', icon: Assets.icons.cinama),
CategoryEntity(id: "34185954-f79f-4b9e-8eb2-1702679c40a0", name: 'لوازم آرایشی', icon: Assets.icons.arayesh), CategoryEntity(id: "34185954-f79f-4b9e-8eb2-1702679c40a0", name: 'لوازم آرایشی', icon: Assets.icons.arayesh),
CategoryEntity(id: "e4517b0c-aacf-4758-94bd-85f45062980f", name: 'طلا و زیورآلات', icon: Assets.icons.tala), CategoryEntity(id: "e4517b0c-aacf-4758-94bd-85f45062980f", name: 'طلا و زیورآلات', icon: Assets.icons.tala),
@ -126,6 +128,4 @@ class NotificationPreferencesBloc
ResetSubmissionStatus event, Emitter<NotificationPreferencesState> emit) { ResetSubmissionStatus event, Emitter<NotificationPreferencesState> emit) {
emit(state.copyWith(submissionSuccess: false)); emit(state.copyWith(submissionSuccess: false));
} }
} }

View File

@ -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_event.dart';
import 'package:proxibuy/presentation/comment/bloc/comment_state.dart'; import 'package:proxibuy/presentation/comment/bloc/comment_state.dart';
import 'package:proxibuy/presentation/pages/offers_page.dart'; import 'package:proxibuy/presentation/pages/offers_page.dart';
import 'package:flutter_animate/flutter_animate.dart';
class CommentPage extends StatefulWidget { class CommentPage extends StatefulWidget {
final String discountId; final String discountId;
@ -39,7 +40,10 @@ class _CommentPageState extends State<CommentPage> {
); );
return; return;
} }
final pickedFile = await _picker.pickImage(source: source, imageQuality: 80); final pickedFile = await _picker.pickImage(
source: source,
imageQuality: 80,
);
if (pickedFile != null) { if (pickedFile != null) {
setState(() { setState(() {
_images.add(File(pickedFile.path)); _images.add(File(pickedFile.path));
@ -47,6 +51,12 @@ class _CommentPageState extends State<CommentPage> {
} }
} }
void _removeImage(int index) {
setState(() {
_images.removeAt(index);
});
}
void _showImageSourceActionSheet() { void _showImageSourceActionSheet() {
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
@ -58,7 +68,10 @@ class _CommentPageState extends State<CommentPage> {
child: Wrap( child: Wrap(
children: <Widget>[ children: <Widget>[
ListTile( ListTile(
leading: const Icon(Icons.photo_library, color: AppColors.primary), leading: const Icon(
Icons.photo_library,
color: AppColors.primary,
),
title: const Text('انتخاب از گالری'), title: const Text('انتخاب از گالری'),
onTap: () { onTap: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
@ -85,113 +98,126 @@ class _CommentPageState extends State<CommentPage> {
return BlocProvider( return BlocProvider(
create: (context) => CommentBloc(), create: (context) => CommentBloc(),
child: Scaffold( child: Scaffold(
body: Stack( backgroundColor: Colors.grey[50],
children: [ appBar: _buildCustomAppBar(),
Positioned.fill( body: SingleChildScrollView(
child: Image.asset(Assets.images.userinfo.path, fit: BoxFit.cover), 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, child: SafeArea(
maxChildSize: 0.65, child: Padding(
builder: (context, scrollController) { padding: const EdgeInsets.symmetric(horizontal: 16.0),
return Container( child: Row(
decoration: const BoxDecoration( mainAxisAlignment: MainAxisAlignment.end,
color: Colors.white, children: [
borderRadius: BorderRadius.vertical(top: Radius.circular(32)), const Text(
'ثبت نظر',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.normal,
fontSize: 18,
), ),
child: SingleChildScrollView( ),
controller: scrollController, IconButton(
padding: const EdgeInsets.all(24.0), icon: SvgPicture.asset(Assets.icons.arrowLeft.path),
child: Column( onPressed: () => Navigator.of(context).pop(),
crossAxisAlignment: CrossAxisAlignment.start, ),
children: [ ],
Center( ),
child: Container( ),
width: 50, ),
height: 5, ),
decoration: BoxDecoration( );
color: Colors.grey.withOpacity(0.5), }
borderRadius: BorderRadius.circular(12),
), Widget _buildHeader() {
), return Center(
), child: Column(
const SizedBox(height: 24), children: [
const Text( Container(
'خریدت با موفقیت انجام شد. منتظر دیدار دوباره‌ات هستیم. لطفا نظرت رو در مورد این تخفیف بهمون بگو.', padding: const EdgeInsets.all(16),
style: TextStyle( decoration: BoxDecoration(
fontSize: 16, shape: BoxShape.circle,
fontWeight: FontWeight.normal, color: AppColors.confirm.withOpacity(0.1),
height: 1.5, ),
), child: Icon(
textAlign: TextAlign.center, Icons.check_circle_outline,
), color: AppColors.confirm,
const SizedBox(height: 24), size: 50,
Center( ),
child: RatingBar.builder( ),
initialRating: 0, const SizedBox(height: 16),
minRating: 1, const Text(
direction: Axis.horizontal, 'خریدت با موفقیت انجام شد. منتظر دیدار دوباره‌ات هستیم. لطفا نظرت رو در مورد این تخفیف بهمون بگو.',
itemCount: 5, style: TextStyle(fontSize: 16, color: Colors.black, height: 1.5),
itemPadding: const EdgeInsets.symmetric(horizontal: 15.0), textAlign: TextAlign.center,
itemBuilder: (context, _) => Icon( ),
Icons.star, ],
color: Colors.amber.shade700, ),
), );
onRatingUpdate: (rating) => setState(() => _rating = rating), }
),
), Widget _buildRatingCard() {
const SizedBox(height: 24), return Card(
TextField( elevation: 1,
controller: _commentController, shadowColor: Colors.black12,
maxLines: 4, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
textAlign: TextAlign.right, child: Padding(
decoration: InputDecoration( padding: const EdgeInsets.all(16.0),
labelText: "گوشمون به شماست", child: Column(
hintText: "نظراتت رو بگو...", children: [
alignLabelWithHint: true, const Text(
suffixIcon: Padding( "امتیاز شما چقدره؟",
padding: const EdgeInsets.all(12.0), style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
child: IconButton( ),
icon: SvgPicture.asset( const SizedBox(height: 16),
Assets.icons.galleryAdd.path, RatingBar.builder(
color: _images.length >= 2 ? Colors.grey : AppColors.primary, initialRating: 0,
), minRating: 1,
onPressed: _images.length >= 2 ? null : _showImageSourceActionSheet, direction: Axis.horizontal,
), itemCount: 5,
), itemPadding: const EdgeInsets.symmetric(horizontal: 7.0),
), itemBuilder:
), (context, _) =>
const SizedBox(height: 16), Icon(Icons.star, color: Colors.amber.shade700),
if (_images.isNotEmpty) onRatingUpdate: (rating) => setState(() => _rating = rating),
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(),
],
),
),
);
},
), ),
], ],
), ),
@ -199,6 +225,108 @@ class _CommentPageState extends State<CommentPage> {
); );
} }
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() { Widget _buildActionButtons() {
return BlocConsumer<CommentBloc, CommentState>( return BlocConsumer<CommentBloc, CommentState>(
listener: (context, state) { listener: (context, state) {
@ -207,6 +335,7 @@ class _CommentPageState extends State<CommentPage> {
const SnackBar( const SnackBar(
content: Text('نظر شما با موفقیت ثبت شد. ممنونیم!'), content: Text('نظر شما با موفقیت ثبت شد. ممنونیم!'),
backgroundColor: Colors.green, backgroundColor: Colors.green,
behavior: SnackBarBehavior.floating,
), ),
); );
Navigator.of(context).pushAndRemoveUntil( Navigator.of(context).pushAndRemoveUntil(
@ -215,7 +344,11 @@ class _CommentPageState extends State<CommentPage> {
); );
} else if (state is CommentSubmissionFailure) { } else if (state is CommentSubmissionFailure) {
ScaffoldMessenger.of(context).showSnackBar( 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<CommentPage> {
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,
child: ElevatedButton( child: ElevatedButton(
onPressed: isLoading onPressed:
? null isLoading
: () { ? null
context.read<CommentBloc>().add( : () {
SubmitComment( context.read<CommentBloc>().add(
discountId: widget.discountId, SubmitComment(
text: _commentController.text, discountId: widget.discountId,
score: _rating, text: _commentController.text,
images: _images, score: _rating,
), images: _images,
); ),
}, );
},
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: AppColors.confirm, backgroundColor: AppColors.confirm,
foregroundColor: Colors.white, foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16), padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(50)), shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
), ),
child: isLoading child:
? const SizedBox( isLoading
width: 24, ? const SizedBox(
height: 24, width: 24,
child: CircularProgressIndicator(color: Colors.white), height: 24,
) child: CircularProgressIndicator(color: Colors.white),
: const Text('ارسال'), )
: const Text('ارسال'),
), ),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
TextButton( TextButton(
onPressed: isLoading onPressed:
? null isLoading
: () { ? null
Navigator.of(context).pushAndRemoveUntil( : () {
MaterialPageRoute(builder: (_) => const OffersPage()), Navigator.of(context).pushAndRemoveUntil(
(route) => false, MaterialPageRoute(builder: (_) => const OffersPage()),
); (route) => false,
}, );
child: const Text('رد شدن', style: TextStyle(color: Colors.black)), },
child: const Text(
'رد شدن',
style: TextStyle(color: Colors.black),
),
), ),
], ],
); );
}, },
); );
} }
} }