part of 'home_page.dart'; typedef OnLikeCallback = void Function(String title, bool isLiked)?; class CardData { final String text; final String description; final IconData icon; final double score; final String imageUrl; const CardData( this.text, { required this.description, this.icon = Icons.add_call, this.score = 0, this.imageUrl = "https://i.pinimg.com/736x/5f/14/b3/5f14b3f14fcd157bc4dffa39085396cc.jpg", }); } class _Card extends StatefulWidget { final String text; final String description; final String? imageUrl; final double score; final OnLikeCallback onLike; final VoidCallback? onTap; const _Card( this.text, { required this.description, required this.imageUrl, required this.score, this.onLike, this.onTap, }); factory _Card.fromData( CardData data, { OnLikeCallback onLike, VoidCallback? onTap, }) => _Card( data.text, description: data.description, imageUrl: data.imageUrl, score: data.score, onLike: onLike, onTap: onTap, ); @override State<_Card> createState() => _CardState(); } class _CardState extends State<_Card> { bool isLiked = false; @override Widget build(BuildContext context) { List desc = widget.description.split("\n"); return GestureDetector( onTap: widget.onTap, child: Container( margin: const EdgeInsets.all(10), constraints: const BoxConstraints(minHeight: 140), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20), border: Border.all(color: Colors.green, width: 2), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(.4), offset: const Offset(0, 5), blurRadius: 6, spreadRadius: 3), ]), child: IntrinsicHeight( child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ Stack(children: [ ClipRRect( borderRadius: const BorderRadius.only( bottomLeft: Radius.circular(18), topLeft: Radius.circular(18), ), child: SizedBox( height: 300, width: 120, child: Image.network( widget.imageUrl ?? '', fit: BoxFit.cover, errorBuilder: (_, __, ___) => const Placeholder(), ), ), ), Align( alignment: Alignment.bottomLeft, child: Container( decoration: const BoxDecoration( color: Colors.purple, borderRadius: BorderRadius.only( topRight: Radius.circular(20), bottomLeft: Radius.circular(18))), padding: const EdgeInsets.fromLTRB(8, 2, 8, 2), child: Text(generateStars(widget.score), style: Theme.of(context) .textTheme .bodyMedium ?.copyWith(color: Colors.white)), ), ), ]), Expanded( child: Padding( padding: const EdgeInsets.all(10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ Text( widget.text, style: Theme.of(context).textTheme.headlineMedium, ), Text( desc[0], style: Theme.of(context).textTheme.bodySmall, ) ], ), ), ), Align( alignment: Alignment.center, child: Padding( padding: const EdgeInsets.only( left: 8, right: 8, bottom: 8, ), child: GestureDetector( onTap: () { setState(() => isLiked = !isLiked); widget.onLike?.call(widget.text, isLiked); }, child: AnimatedSwitcher( duration: const Duration(milliseconds: 300), child: isLiked ? const Icon( Icons.favorite, color: Colors.red, key: ValueKey(0), ) : const Icon( Icons.favorite_border, key: ValueKey(1), ), ), )), ) ], ), ), ), ); } } String generateStars(double rating) { rating = rating / 2; int fullStars = rating.floor(); bool hasHalfStar = (rating - fullStars) >= 0.5; String stars = '★' * fullStars; if (hasHalfStar) { stars += '✪'; } int emptyStars = 5 - stars.length; stars += '☆' * emptyStars; return stars; }