import 'package:flutter/material.dart'; import 'package:nearledaily/constants/color_constants.dart'; import '../constants/font_constants.dart'; class ReusableTextWidget extends StatelessWidget { final String text; final double? fontSize; final double? textHeight; final TextOverflow? overflow; final String? fontFamily; final FontWeight? fontWeight; final FontStyle? fontStyle; final Color? color; final TextAlign? textAlign; final int? maxLines; final TextDecoration? isUnderText; final TextDecoration? textDecoration; const ReusableTextWidget({ super.key, required this.text, this.fontSize, this.textHeight, this.fontFamily, this.fontWeight, this.fontStyle, this.overflow, this.color, this.textAlign, this.maxLines, this.isUnderText, this.textDecoration, }); @override Widget build(BuildContext context) { return Text( text, softWrap: true, maxLines: maxLines, overflow: overflow ?? TextOverflow.ellipsis, textAlign: textAlign ?? TextAlign.start, style: TextStyle( fontFamily: fontFamily ?? FontConstants.fontFamily, fontWeight: fontWeight ?? FontWeight.normal, fontStyle: fontStyle ?? FontStyle.normal, fontSize: fontSize ?? 13, height: textHeight, color: color ?? Colors.grey.shade900, decoration: isUnderText, decorationColor: color, decorationStyle: TextDecorationStyle.solid, decorationThickness: 1, ), ); } } class CategoryStickyHeader extends SliverPersistentHeaderDelegate { final List categories; int currentIndex; CategoryStickyHeader({ required this.categories, this.currentIndex = 0, }); @override double get minExtent => 80; @override double get maxExtent => 80; @override Widget build( BuildContext context, double shrinkOffset, bool overlapsContent, ) { final bool isPinned = shrinkOffset > 0 || overlapsContent; return AnimatedContainer( duration: const Duration(milliseconds: 180), decoration: BoxDecoration( color: Colors.white, boxShadow: isPinned ? [ BoxShadow( color: Colors.black.withOpacity(0.12), blurRadius: 8, offset: const Offset(0, 4), ), ] : [], ), child: SizedBox( height: 80, child: StatefulBuilder( builder: (context, setHeaderState) { return ListView.builder( scrollDirection: Axis.horizontal, padding: const EdgeInsets.symmetric(horizontal: 10), itemCount: categories.length, itemBuilder: (context, index) { final item = categories[index]; final bool isActive = index == currentIndex; return _CategoryItem( item: item, isActive: isActive, onTap: () { setHeaderState(() { currentIndex = index; }); }, ); }, ); }, ), ), ); } @override bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) { return true; } } /// ------------------------------------------------------------------------- /// CATEGORY ITEM WIDGET (handles zoom + bounce cleanly) /// ------------------------------------------------------------------------- class _CategoryItem extends StatefulWidget { final Map item; final bool isActive; final VoidCallback onTap; const _CategoryItem({ required this.item, required this.isActive, required this.onTap, }); @override State<_CategoryItem> createState() => _CategoryItemState(); } class _CategoryItemState extends State<_CategoryItem> { double _scale = 1.0; Future _animateTap() async { setState(() => _scale = 0.88); await Future.delayed(const Duration(milliseconds: 90)); setState(() => _scale = 1.0); } @override Widget build(BuildContext context) { return GestureDetector( behavior: HitTestBehavior.opaque, onTapDown: (_) { setState(() => _scale = 0.88); }, onTapCancel: () { setState(() => _scale = 1.0); }, onTap: () async { await _animateTap(); widget.onTap(); }, child: AnimatedScale( scale: _scale, duration: const Duration(milliseconds: 120), curve: Curves.easeOutBack, child: Container( width: 57, margin: const EdgeInsets.only(right: 14), child: Column( mainAxisAlignment: MainAxisAlignment.end, children: [ Container( height: 57, width: 57, padding: const EdgeInsets.all(12), decoration: const BoxDecoration( shape: BoxShape.circle, ), child: Image.network( widget.item["icon"], fit: BoxFit.contain, errorBuilder: (_, __, ___) => const Icon(Icons.image, size: 24), ), ), const SizedBox(height: 0), ReusableTextWidget( text: widget.item["name"], color: Colors.black, fontSize: 11, fontWeight: FontWeight.w600, maxLines: 1, textAlign: TextAlign.center, ), const SizedBox(height: 4), Container( height: 3, width: 30, decoration: BoxDecoration( color: widget.isActive ? ColorConstants.primaryColor : Colors.transparent, borderRadius: BorderRadius.circular(2), ), ), ], ), ), ), ); } }