import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:lba/gen/assets.gen.dart'; class Bestnearby extends StatefulWidget { const Bestnearby({ super.key, }); @override State createState() => _BestnearbyState(); } class _BestnearbyState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; late List> _staggeredAnimations; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 1500), ); // --- انیمیشن‌های مرحله‌ای برای هر بخش --- _staggeredAnimations = List.generate(3, (index) { return Tween(begin: 0.0, end: 1.0).animate( CurvedAnimation( parent: _controller, curve: Interval( (0.2 * index), (0.6 + 0.2 * index).clamp(0.0, 1.0), curve: Curves.easeOutCubic, ), ), ); }); _controller.forward(); } @override void dispose() { _controller.dispose(); super.dispose(); } // --- ویجت انیمیشنی برای هر بخش --- Widget _buildAnimatedSection(Widget child, int index) { return FadeTransition( opacity: _staggeredAnimations[index], child: SlideTransition( position: Tween( begin: const Offset(0.0, 0.5), end: Offset.zero, ).animate(_staggeredAnimations[index]), child: child, ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Colors.white, elevation: 2, shadowColor: Colors.grey, automaticallyImplyLeading: false, leading: InkWell( onTap: () => Navigator.pop(context), child: SvgPicture.asset( Assets.icons.back.path, fit: BoxFit.scaleDown, color: Colors.black, ), ), title: const Text("Top Deals & Stores", style: TextStyle(fontSize: 18, color: Colors.black)), ), body: SingleChildScrollView( child: Column( children: [ _buildAnimatedSection(NearbyItems(detailsType: 0), 0), _buildAnimatedSection(NearbyItems(detailsType: 1), 1), _buildAnimatedSection(NearbyItems(detailsType: 2), 2), ], ), ), ); } } class NearbyItems extends StatefulWidget { const NearbyItems({super.key, required this.detailsType}); final int detailsType; @override State createState() => _NearbyItemsState(); } class _NearbyItemsState extends State with SingleTickerProviderStateMixin { late AnimationController _listController; late List> _itemAnimations; @override void initState() { super.initState(); _listController = AnimationController( vsync: this, duration: const Duration(milliseconds: 1000), ); _itemAnimations = List.generate(5, (index) { return Tween(begin: 0.0, end: 1.0).animate( CurvedAnimation( parent: _listController, curve: Interval( (0.15 * index), (0.5 + 0.15 * index).clamp(0.0, 1.0), curve: Curves.easeInOut, ), ), ); }); _listController.forward(); } @override void dispose() { _listController.dispose(); super.dispose(); } Widget _buildAnimatedItem(Widget child, int index) { return FadeTransition( opacity: _itemAnimations[index], child: SlideTransition( position: Tween( begin: const Offset(0.3, 0.0), end: Offset.zero, ).animate(_itemAnimations[index]), child: child, ), ); } @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(8.0), child: Column( children: [ DividerTitle(detailsType: widget.detailsType), SizedBox( height: widget.detailsType == 0 ? 180 : widget.detailsType == 1 ? 240 : widget.detailsType == 2 ? 240 : 200, child: ListView.builder( itemCount: 5, scrollDirection: Axis.horizontal, itemBuilder: (context, index) { return _buildAnimatedItem( Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 7), Container( width: 120, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), color: Colors.grey[200], ), child: Image.asset( Assets.images.topDealsAndStores.path, ), ), SizedBox(height: 5), Text( "Dubai Outlet Mall", style: TextStyle(fontWeight: FontWeight.bold), ), SizedBox(height: 5), if (widget.detailsType == 0) Row( children: [ SvgPicture.asset(Assets.icons.routing.path), SizedBox(width: 4), Text( "1.2 km away", style: TextStyle(fontSize: 12), ), ], ) else if (widget.detailsType == 1) Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 2), Row( children: [ SvgPicture.asset( Assets.icons.category2.path), SizedBox(width: 4), Text("Stationery store"), ], ), SizedBox(height: 5), Row( children: [ SvgPicture.asset( Assets.icons.icRoundLocalOffer.path, ), SizedBox(width: 4), Text( "22 - 35% off", style: TextStyle(color: Colors.red), ), ], ), SizedBox(height: 5), Row( children: [ SvgPicture.asset(Assets.icons.routing.path), SizedBox(width: 4), Text("3.6 km away"), ], ), ], ) else if (widget.detailsType == 2) Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 2), Row( children: [ SvgPicture.asset(Assets.icons.shop2.path), SizedBox(width: 4), Text("Jumbo Electronics "), ], ), SizedBox(height: 5), Row( children: [ SvgPicture.asset( Assets.icons.icRoundLocalOffer.path, ), SizedBox(width: 4), Text( "22 - 35% off", style: TextStyle(color: Colors.red), ), ], ), SizedBox(height: 5), Row( children: [ SvgPicture.asset(Assets.icons.routing.path), SizedBox(width: 4), Text("3.6 km away"), ], ), ], ) ], ), ), index, ); }, ), ), ], ), ); } } class DividerTitle extends StatelessWidget { const DividerTitle({ super.key, required this.detailsType, }); final int detailsType; @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(8.0), child: Row( children: [ Text( detailsType == 0 ? "Highest Discount Areas" : detailsType == 1 ? "Highest Discount Areas" : "Best Deal, Don't Miss", style: TextStyle(fontSize: 15), ), const SizedBox(width: 8), const Expanded( child: Divider(color: Colors.grey, thickness: 2), ), ], ), ); } }