firebase ssh updated

This commit is contained in:
mohamadmahdi jebeli 2025-09-24 08:38:07 +03:30
parent 87ec98def4
commit 08d6e4e9e9
9 changed files with 681 additions and 46 deletions

View File

@ -9,7 +9,7 @@ plugins {
android { android {
namespace = "com.example.lba" namespace = "com.example.lba"
compileSdk = flutter.compileSdkVersion compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion ndkVersion = "27.0.12077973"
compileOptions { compileOptions {
// استفاده از جاوا ۸ برای سازگاری بهتر // استفاده از جاوا ۸ برای سازگاری بهتر

View File

@ -28,5 +28,5 @@ plugins {
// The Kotlin plugin version is also managed. // The Kotlin plugin version is also managed.
id("org.jetbrains.kotlin.android") apply false id("org.jetbrains.kotlin.android") apply false
// اضافه کردن این خط برای تعریف پلاگین گوگل سرویسز // اضافه کردن این خط برای تعریف پلاگین گوگل سرویسز
id("com.google.gms.google-services") version "4.4.1" apply false id("com.google.gms.google-services") version "4.3.15" apply false
} }

View File

@ -19,6 +19,9 @@ pluginManagement {
plugins { plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0" id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.7.0" apply false id("com.android.application") version "8.7.0" apply false
// START: FlutterFire Configuration
id("com.google.gms.google-services") version("4.3.15") apply false
// END: FlutterFire Configuration
// Updated Kotlin version to the latest stable release // Updated Kotlin version to the latest stable release
id("org.jetbrains.kotlin.android") version "2.0.0" apply false id("org.jetbrains.kotlin.android") version "2.0.0" apply false
} }

1
firebase.json Normal file
View File

@ -0,0 +1 @@
{"flutter":{"platforms":{"android":{"default":{"projectId":"lba-app-c4a7e","appId":"1:81202355575:android:6d4c7db49b6120f8239572","fileOutput":"android/app/google-services.json"}},"dart":{"lib/firebase_options.dart":{"projectId":"lba-app-c4a7e","configurations":{"android":"1:81202355575:android:6d4c7db49b6120f8239572","ios":"1:81202355575:ios:d40682afd403fc5b239572","macos":"1:81202355575:ios:d40682afd403fc5b239572","web":"1:81202355575:web:0cf25bf19cb4a8a1239572","windows":"1:81202355575:web:03aeac4a6dbda4d3239572"}}}}}}

View File

@ -0,0 +1,93 @@
// File generated by FlutterFire CLI.
// ignore_for_file: type=lint
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart'
show defaultTargetPlatform, kIsWeb, TargetPlatform;
/// Default [FirebaseOptions] for use with your Firebase apps.
///
/// Example:
/// ```dart
/// import 'firebase_options.dart';
/// // ...
/// await Firebase.initializeApp(
/// options: DefaultFirebaseOptions.currentPlatform,
/// );
/// ```
class DefaultFirebaseOptions {
static FirebaseOptions get currentPlatform {
if (kIsWeb) {
return web;
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
return android;
case TargetPlatform.iOS:
return ios;
case TargetPlatform.macOS:
return macos;
case TargetPlatform.windows:
return windows;
case TargetPlatform.linux:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for linux - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
default:
throw UnsupportedError(
'DefaultFirebaseOptions are not supported for this platform.',
);
}
}
static const FirebaseOptions web = FirebaseOptions(
apiKey: 'AIzaSyCeRwWv-Pep_624hoUVIw-SSlicDVRHGiY',
appId: '1:81202355575:web:0cf25bf19cb4a8a1239572',
messagingSenderId: '81202355575',
projectId: 'lba-app-c4a7e',
authDomain: 'lba-app-c4a7e.firebaseapp.com',
storageBucket: 'lba-app-c4a7e.firebasestorage.app',
measurementId: 'G-8D14ETTE9T',
);
static const FirebaseOptions android = FirebaseOptions(
apiKey: 'AIzaSyB22NqmwB_PpI1s37xbc2ABJ5_COEHeC8g',
appId: '1:81202355575:android:6d4c7db49b6120f8239572',
messagingSenderId: '81202355575',
projectId: 'lba-app-c4a7e',
storageBucket: 'lba-app-c4a7e.firebasestorage.app',
);
static const FirebaseOptions ios = FirebaseOptions(
apiKey: 'AIzaSyCJsr8Baaw0CbucB0zrB_kWv_7fcBNRwIk',
appId: '1:81202355575:ios:d40682afd403fc5b239572',
messagingSenderId: '81202355575',
projectId: 'lba-app-c4a7e',
storageBucket: 'lba-app-c4a7e.firebasestorage.app',
androidClientId: '81202355575-fchlrrp4fu3irskh6co1ep8i04oi3adr.apps.googleusercontent.com',
iosClientId: '81202355575-hcgc97f7glhbpa8h7iqq4d57efcsrtqo.apps.googleusercontent.com',
iosBundleId: 'com.example.lba',
);
static const FirebaseOptions macos = FirebaseOptions(
apiKey: 'AIzaSyCJsr8Baaw0CbucB0zrB_kWv_7fcBNRwIk',
appId: '1:81202355575:ios:d40682afd403fc5b239572',
messagingSenderId: '81202355575',
projectId: 'lba-app-c4a7e',
storageBucket: 'lba-app-c4a7e.firebasestorage.app',
androidClientId: '81202355575-fchlrrp4fu3irskh6co1ep8i04oi3adr.apps.googleusercontent.com',
iosClientId: '81202355575-hcgc97f7glhbpa8h7iqq4d57efcsrtqo.apps.googleusercontent.com',
iosBundleId: 'com.example.lba',
);
static const FirebaseOptions windows = FirebaseOptions(
apiKey: 'AIzaSyCeRwWv-Pep_624hoUVIw-SSlicDVRHGiY',
appId: '1:81202355575:web:03aeac4a6dbda4d3239572',
messagingSenderId: '81202355575',
projectId: 'lba-app-c4a7e',
authDomain: 'lba-app-c4a7e.firebaseapp.com',
storageBucket: 'lba-app-c4a7e.firebasestorage.app',
measurementId: 'G-2EZ542PHK0',
);
}

View File

@ -48,6 +48,7 @@ class _ItemState extends State<Item> with TickerProviderStateMixin {
"yesCount": 2, "yesCount": 2,
"noCount": 0, "noCount": 0,
"date": "Jun 09, 2025", "date": "Jun 09, 2025",
"userLiked": null,
}, },
{ {
"name": "Khalid A", "name": "Khalid A",
@ -57,6 +58,7 @@ class _ItemState extends State<Item> with TickerProviderStateMixin {
"yesCount": 5, "yesCount": 5,
"noCount": 10, "noCount": 10,
"date": "Dec 26, 2024", "date": "Dec 26, 2024",
"userLiked": null,
}, },
{ {
"name": "Khalid A", "name": "Khalid A",
@ -66,6 +68,7 @@ class _ItemState extends State<Item> with TickerProviderStateMixin {
"yesCount": 5, "yesCount": 5,
"noCount": 10, "noCount": 10,
"date": "Dec 26, 2024", "date": "Dec 26, 2024",
"userLiked": null,
}, },
{ {
"name": "Sara M", "name": "Sara M",
@ -75,6 +78,7 @@ class _ItemState extends State<Item> with TickerProviderStateMixin {
"yesCount": 2, "yesCount": 2,
"noCount": 0, "noCount": 0,
"date": "Jun 09, 2025", "date": "Jun 09, 2025",
"userLiked": null,
}, },
]; ];
@ -246,6 +250,32 @@ class _ItemState extends State<Item> with TickerProviderStateMixin {
yesCount: review['yesCount'], yesCount: review['yesCount'],
noCount: review['noCount'], noCount: review['noCount'],
date: review['date'], date: review['date'],
initialLikeState: review['userLiked'],
onLikeDislike: (isLike) {
setState(() {
if (isLike) {
if (review['userLiked'] == true) {
review['userLiked'] = null;
} else {
if (review['userLiked'] == false) {
review['noCount'] = (review['noCount'] as int) - 1;
}
review['userLiked'] = true;
review['yesCount'] = (review['yesCount'] as int) + 1;
}
} else {
if (review['userLiked'] == false) {
review['userLiked'] = null;
} else {
if (review['userLiked'] == true) {
review['yesCount'] = (review['yesCount'] as int) - 1;
}
review['userLiked'] = false;
review['noCount'] = (review['noCount'] as int) + 1;
}
}
});
},
), ),
), ),
); );
@ -279,7 +309,7 @@ class _ItemState extends State<Item> with TickerProviderStateMixin {
), ),
], ],
), ),
const SizedBox(height: 20), const SizedBox(height: 7),
const PriceReserveWidget(), const PriceReserveWidget(),
], ],
), ),

View File

@ -2,4 +2,7 @@ class SharedPreferencesKey {
static const String token = 'token'; static const String token = 'token';
static const String isDarkMode = 'isDarkMode'; static const String isDarkMode = 'isDarkMode';
static const String hasManualThemeOverride = 'hasManualThemeOverride'; static const String hasManualThemeOverride = 'hasManualThemeOverride';
static const String hasSeenOnboarding = 'hasSeenOnboarding';
static const String selectedLanguage = 'selectedLanguage';
static const String selectedFlag = 'selectedFlag';
} }

View File

@ -4,6 +4,7 @@ import 'package:lba/gen/assets.gen.dart';
import 'package:lba/res/colors.dart'; import 'package:lba/res/colors.dart';
import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart'; import 'package:google_sign_in/google_sign_in.dart';
import 'package:lba/screens/auth/login_page.dart';
Future<void> showLogoutDialog(BuildContext context) async { Future<void> showLogoutDialog(BuildContext context) async {
showDialog( showDialog(
@ -121,12 +122,23 @@ class _AnimatedLogoutDialogState extends State<_AnimatedLogoutDialog>
await GoogleSignIn().signOut(); await GoogleSignIn().signOut();
await FirebaseAuth.instance.signOut(); await FirebaseAuth.instance.signOut();
print('✅ Logout successful'); print('✅ Logout successful');
if (context.mounted) {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) => const LoginPage(),
),
(Route<dynamic> route) => false,
);
}
} catch (e) { } catch (e) {
print('❌ Logout error: $e'); print('❌ Logout error: $e');
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('خطا در خروج: $e')), SnackBar(content: Text('خطا در خروج: $e')),
); );
} }
}
}, },
child: Text( child: Text(
"Log Out", "Log Out",

View File

@ -5,14 +5,17 @@ import 'package:lba/res/colors.dart';
import 'package:lba/widgets/buildWarpedInfo.dart'; import 'package:lba/widgets/buildWarpedInfo.dart';
import 'package:lba/widgets/rate.dart'; import 'package:lba/widgets/rate.dart';
class Reviews extends StatelessWidget { enum LikeState { none, liked, disliked }
class Reviews extends StatefulWidget {
final String name; final String name;
final String comment; final String comment;
final double rate; final double rate;
final int yesCount; final int yesCount;
final int noCount; final int noCount;
final String date; final String date;
final Function(bool isLike)? onLikeDislike;
final bool? initialLikeState;
const Reviews({ const Reviews({
super.key, super.key,
@ -22,8 +25,484 @@ class Reviews extends StatelessWidget {
required this.yesCount, required this.yesCount,
required this.noCount, required this.noCount,
required this.date, required this.date,
this.onLikeDislike,
this.initialLikeState,
}); });
@override
State<Reviews> createState() => _ReviewsState();
}
class _ReviewsState extends State<Reviews> with TickerProviderStateMixin {
LikeState _likeState = LikeState.none;
int _currentYesCount = 0;
int _currentNoCount = 0;
late AnimationController _likeScaleController;
late AnimationController _dislikeScaleController;
late AnimationController _particleController;
late AnimationController _pulseController;
late AnimationController _countController;
late AnimationController _likeIconController;
late AnimationController _dislikeIconController;
late Animation<double> _likeScaleAnimation;
late Animation<double> _dislikeScaleAnimation;
late Animation<double> _particleAnimation;
late Animation<double> _pulseAnimation;
late Animation<double> _countAnimation;
late Animation<Color?> _likeColorAnimation;
late Animation<Color?> _dislikeColorAnimation;
late Animation<double> _likeIconRotationAnimation;
late Animation<double> _dislikeIconRotationAnimation;
late Animation<double> _likeIconScaleAnimation;
late Animation<double> _dislikeIconScaleAnimation;
@override
void initState() {
super.initState();
_currentYesCount = widget.yesCount;
_currentNoCount = widget.noCount;
if (widget.initialLikeState == true) {
_likeState = LikeState.liked;
} else if (widget.initialLikeState == false) {
_likeState = LikeState.disliked;
}
_likeScaleController = AnimationController(
duration: const Duration(milliseconds: 600),
vsync: this,
);
_dislikeScaleController = AnimationController(
duration: const Duration(milliseconds: 600),
vsync: this,
);
_particleController = AnimationController(
duration: const Duration(milliseconds: 1200),
vsync: this,
);
_pulseController = AnimationController(
duration: const Duration(milliseconds: 800),
vsync: this,
);
_countController = AnimationController(
duration: const Duration(milliseconds: 400),
vsync: this,
);
_likeIconController = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
_dislikeIconController = AnimationController(
duration: const Duration(milliseconds: 500),
vsync: this,
);
_likeScaleAnimation = Tween<double>(begin: 1.0, end: 1.08).animate(
CurvedAnimation(parent: _likeScaleController, curve: Curves.easeOutBack),
);
_dislikeScaleAnimation = Tween<double>(begin: 1.0, end: 1.08).animate(
CurvedAnimation(
parent: _dislikeScaleController,
curve: Curves.easeOutBack,
),
);
_particleAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(parent: _particleController, curve: Curves.easeOutCubic),
);
_pulseAnimation = Tween<double>(begin: 1.0, end: 1.15).animate(
CurvedAnimation(parent: _pulseController, curve: Curves.easeInOut),
);
_countAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(parent: _countController, curve: Curves.bounceOut),
);
_likeColorAnimation = ColorTween(
begin: AppColors.hint,
end: const Color(0xFF4CAF50),
).animate(_likeScaleController);
_dislikeColorAnimation = ColorTween(
begin: AppColors.hint,
end: const Color(0xFFF44336),
).animate(_dislikeScaleController);
_likeIconRotationAnimation = Tween<double>(begin: 0.0, end: 0.1).animate(
CurvedAnimation(parent: _likeIconController, curve: Curves.elasticOut),
);
_dislikeIconRotationAnimation = Tween<double>(
begin: 0.0,
end: -0.1,
).animate(
CurvedAnimation(parent: _dislikeIconController, curve: Curves.elasticOut),
);
_likeIconScaleAnimation = Tween<double>(begin: 1.0, end: 1.3).animate(
CurvedAnimation(parent: _likeIconController, curve: Curves.elasticOut),
);
_dislikeIconScaleAnimation = Tween<double>(begin: 1.0, end: 1.3).animate(
CurvedAnimation(parent: _dislikeIconController, curve: Curves.elasticOut),
);
}
@override
void dispose() {
_likeScaleController.dispose();
_dislikeScaleController.dispose();
_particleController.dispose();
_pulseController.dispose();
_countController.dispose();
_likeIconController.dispose();
_dislikeIconController.dispose();
super.dispose();
}
void _onLikeTap() async {
if (_likeState == LikeState.liked) {
setState(() {
_likeState = LikeState.none;
_currentYesCount--;
});
_likeScaleController.reverse();
} else {
if (_likeState == LikeState.disliked) {
_currentNoCount--;
_dislikeScaleController.reverse();
}
setState(() {
_likeState = LikeState.liked;
_currentYesCount++;
});
_likeScaleController.forward();
_likeIconController.forward().then((_) => _likeIconController.reverse());
_particleController.forward().then((_) => _particleController.reset());
_pulseController.forward().then((_) => _pulseController.reverse());
_countController.forward().then((_) => _countController.reset());
}
widget.onLikeDislike?.call(true);
}
void _onDislikeTap() async {
if (_likeState == LikeState.disliked) {
setState(() {
_likeState = LikeState.none;
_currentNoCount--;
});
_dislikeScaleController.reverse();
} else {
if (_likeState == LikeState.liked) {
_currentYesCount--;
_likeScaleController.reverse();
}
setState(() {
_likeState = LikeState.disliked;
_currentNoCount++;
});
_dislikeScaleController.forward();
_dislikeIconController.forward().then(
(_) => _dislikeIconController.reverse(),
);
_particleController.forward().then((_) => _particleController.reset());
_pulseController.forward().then((_) => _pulseController.reverse());
_countController.forward().then((_) => _countController.reset());
}
widget.onLikeDislike?.call(false);
}
Widget _buildParticleEffect({required bool isLike}) {
return AnimatedBuilder(
animation: _particleAnimation,
builder: (context, child) {
return CustomPaint(
painter: ParticleEffectPainter(
progress: _particleAnimation.value,
color: isLike ? const Color(0xFF4CAF50) : const Color(0xFFF44336),
),
size: const Size(10, 10),
);
},
);
}
Widget _buildAnimatedLikeButton() {
return AnimatedBuilder(
animation: Listenable.merge([
_likeScaleController,
_pulseController,
_countController,
]),
builder: (context, child) {
final isLiked = _likeState == LikeState.liked;
return GestureDetector(
onTap: _onLikeTap,
child: Stack(
clipBehavior: Clip.none,
children: [
if (isLiked)
Positioned(
child: Transform.scale(
scale: _pulseAnimation.value,
child: Container(
width: 30,
height: 25,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: _likeColorAnimation.value?.withOpacity(0.2),
),
),
),
),
Transform.scale(
scale: _likeScaleAnimation.value,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color:
isLiked
? _likeColorAnimation.value?.withOpacity(0.1)
: null,
border:
isLiked
? Border.all(
color:
_likeColorAnimation.value ??
Colors.transparent,
width: 1.5,
)
: null,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
AnimatedBuilder(
animation: Listenable.merge([
_likeColorAnimation,
_likeIconController,
]),
builder: (context, child) {
return Transform.rotate(
angle: _likeIconRotationAnimation.value,
child: Transform.scale(
scale:
isLiked ? _likeIconScaleAnimation.value : 1.0,
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
child: SvgPicture.asset(
isLiked
? Assets.icons.like.path
: Assets.icons.like.path,
key: ValueKey(isLiked ? 'liked' : 'normal'),
color:
isLiked
? _likeColorAnimation.value
: AppColors.hint,
width: 16,
height: 16,
),
),
),
);
},
),
const SizedBox(width: 4),
AnimatedBuilder(
animation: _countAnimation,
builder: (context, child) {
return Transform.scale(
scale: 1.0 + (_countAnimation.value * 0.1),
child: AnimatedBuilder(
animation: _likeColorAnimation,
builder: (context, child) {
return Text(
"Yes ($_currentYesCount)",
style: TextStyle(
color:
isLiked
? _likeColorAnimation.value
: AppColors.hint,
fontWeight:
isLiked
? FontWeight.w600
: FontWeight.normal,
fontSize: 12 + (_countAnimation.value * 1),
),
);
},
),
);
},
),
],
),
),
),
if (isLiked)
Positioned(
top: -5,
right: -5,
child: _buildParticleEffect(isLike: true),
),
],
),
);
},
);
}
Widget _buildAnimatedDislikeButton() {
return AnimatedBuilder(
animation: Listenable.merge([
_dislikeScaleController,
_pulseController,
_countController,
]),
builder: (context, child) {
final isDisliked = _likeState == LikeState.disliked;
return GestureDetector(
onTap: _onDislikeTap,
child: Stack(
clipBehavior: Clip.none,
children: [
if (isDisliked)
Positioned(
child: Transform.scale(
scale: _pulseAnimation.value,
child: Container(
width: 30,
height: 25,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color: _dislikeColorAnimation.value?.withOpacity(0.2),
),
),
),
),
Transform.scale(
scale: _dislikeScaleAnimation.value,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
color:
isDisliked
? _dislikeColorAnimation.value?.withOpacity(0.1)
: null,
border:
isDisliked
? Border.all(
color:
_dislikeColorAnimation.value ??
Colors.transparent,
width: 1.5,
)
: null,
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
AnimatedBuilder(
animation: Listenable.merge([
_dislikeColorAnimation,
_dislikeIconController,
]),
builder: (context, child) {
return Transform.rotate(
angle: _dislikeIconRotationAnimation.value,
child: Transform.scale(
scale:
isDisliked
? _dislikeIconScaleAnimation.value
: 1.0,
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 200),
child: SvgPicture.asset(
isDisliked
? Assets.icons.dislike.path
: Assets.icons.dislike.path,
key: ValueKey(
isDisliked ? 'disliked' : 'normal',
),
color:
isDisliked
? _dislikeColorAnimation.value
: AppColors.hint,
width: 16,
height: 16,
),
),
),
);
},
),
const SizedBox(width: 4),
AnimatedBuilder(
animation: _countAnimation,
builder: (context, child) {
return Transform.scale(
scale: 1.0 + (_countAnimation.value * 0.1),
child: AnimatedBuilder(
animation: _dislikeColorAnimation,
builder: (context, child) {
return Text(
"No ($_currentNoCount)",
style: TextStyle(
color:
isDisliked
? _dislikeColorAnimation.value
: AppColors.hint,
fontWeight:
isDisliked
? FontWeight.w600
: FontWeight.normal,
fontSize: 12 + (_countAnimation.value * 1),
),
);
},
),
);
},
),
],
),
),
),
if (isDisliked)
Positioned(
top: -5,
right: -5,
child: _buildParticleEffect(isLike: false),
),
],
),
);
},
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Padding( return Padding(
@ -38,8 +517,8 @@ class Reviews extends StatelessWidget {
children: [ children: [
Row( Row(
children: [ children: [
Text(name), Text(widget.name),
SizedBox(width: 2), const SizedBox(width: 2),
Image.asset(Assets.images.usa.path), Image.asset(Assets.images.usa.path),
], ],
), ),
@ -49,54 +528,68 @@ class Reviews extends StatelessWidget {
), ),
], ],
), ),
CustomStarRating(rating: rate), CustomStarRating(rating: widget.rate),
], ],
), ),
SizedBox(height: 7), const SizedBox(height: 7),
buildWrappedInfo( buildWrappedInfo("", widget.comment),
"", const SizedBox(height: 10),
comment,
),
SizedBox(height: 10,),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Row( Row(
children: [ children: [
InkWell( _buildAnimatedLikeButton(),
child: Row( const SizedBox(width: 7),
children: [ _buildAnimatedDislikeButton(),
SvgPicture.asset(Assets.icons.like.path),
SizedBox(width: 1,),
Text("Yes (${yesCount.toString()})",style: TextStyle(
color: AppColors.hint,
),),
], ],
), ),
), Text(widget.date, style: TextStyle(color: AppColors.hint)),
SizedBox(width: 10,),
InkWell(
child: Row(
children: [
SvgPicture.asset(Assets.icons.dislike.path),
SizedBox(width: 1,),
Text("No (${noCount.toString()})",style: TextStyle(
color: AppColors.hint,
),),
], ],
), ),
), const SizedBox(height: 5),
], const Divider(thickness: 1.2, height: 2),
),
Text(date,style: TextStyle(
color: AppColors.hint
),)
],
),
SizedBox(height: 5,),
Divider(thickness: 1.2, height: 2),
], ],
), ),
); );
} }
} }
class ParticleEffectPainter extends CustomPainter {
final double progress;
final Color color;
ParticleEffectPainter({required this.progress, required this.color});
@override
void paint(Canvas canvas, Size size) {
if (progress == 0.0) return;
final paint =
Paint()
..color = color.withOpacity(1.0 - progress)
..style = PaintingStyle.fill;
final particles = [
[0.2, -0.3, 3.0],
[0.5, -0.5, 2.5],
[0.8, -0.2, 2.0],
[0.1, -0.6, 1.5],
[0.9, -0.4, 2.0],
];
for (final particle in particles) {
final x =
size.width * particle[0] + (progress * 20 * (particle[0] - 0.5));
final y = size.height * particle[1] * progress;
final particleSize = particle[2] * (1.0 - progress * 0.5);
canvas.drawCircle(Offset(x, y), particleSize, paint);
}
}
@override
bool shouldRepaint(ParticleEffectPainter oldDelegate) {
return oldDelegate.progress != progress;
}
}