From 8ff7b3b5057efc0d5346371e0eeab851d8a21782 Mon Sep 17 00:00:00 2001 From: Phuoc Nguyen Date: Wed, 3 Dec 2025 17:20:22 +0700 Subject: [PATCH] update text + slider --- .../home/presentation/pages/home_page.dart | 2 +- .../widgets/promotion_slider.dart | 76 ++++++++++++++++--- 2 files changed, 65 insertions(+), 13 deletions(-) diff --git a/lib/features/home/presentation/pages/home_page.dart b/lib/features/home/presentation/pages/home_page.dart index 71bd7e2..6bafa85 100644 --- a/lib/features/home/presentation/pages/home_page.dart +++ b/lib/features/home/presentation/pages/home_page.dart @@ -252,7 +252,7 @@ class _HomePageState extends ConsumerState { Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Text( - 'Chương trình ưu đãi', + 'Tin tức nổi bật', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w700, diff --git a/lib/features/home/presentation/widgets/promotion_slider.dart b/lib/features/home/presentation/widgets/promotion_slider.dart index 32327eb..bfb9a61 100644 --- a/lib/features/home/presentation/widgets/promotion_slider.dart +++ b/lib/features/home/presentation/widgets/promotion_slider.dart @@ -1,9 +1,12 @@ /// Widget: Promotion Slider /// -/// Horizontal scrolling list of promotional banners. +/// Auto-sliding carousel of promotional banners. /// Displays promotion images, titles, and descriptions. +/// Auto-advances every 4 seconds with smooth animation. library; +import 'dart:async'; + import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -14,13 +17,15 @@ import 'package:worker/features/home/domain/entities/promotion.dart'; /// Promotion Slider Widget /// -/// Displays a horizontal scrollable list of promotion cards. +/// Displays an auto-sliding carousel of promotion cards. /// Each card shows an image, title, and brief description. -class PromotionSlider extends StatelessWidget { +/// Auto-advances every 4 seconds with page indicators. +class PromotionSlider extends StatefulWidget { const PromotionSlider({ super.key, required this.promotions, this.onPromotionTap, + this.autoSlideDuration = const Duration(seconds: 4), }); /// List of promotions to display @@ -29,9 +34,56 @@ class PromotionSlider extends StatelessWidget { /// Callback when a promotion is tapped final void Function(Promotion promotion)? onPromotionTap; + /// Duration between auto-slides + final Duration autoSlideDuration; + + @override + State createState() => _PromotionSliderState(); +} + +class _PromotionSliderState extends State { + late final ScrollController _scrollController; + Timer? _autoSlideTimer; + int _currentIndex = 0; + + static const double _cardWidth = 280; + static const double _cardMargin = 12; + static const double _scrollOffset = _cardWidth + _cardMargin; + + @override + void initState() { + super.initState(); + _scrollController = ScrollController(); + _startAutoSlide(); + } + + @override + void dispose() { + _autoSlideTimer?.cancel(); + _scrollController.dispose(); + super.dispose(); + } + + void _startAutoSlide() { + if (widget.promotions.length <= 1) return; + + _autoSlideTimer = Timer.periodic(widget.autoSlideDuration, (_) { + if (!mounted) return; + + _currentIndex = (_currentIndex + 1) % widget.promotions.length; + final targetOffset = _currentIndex * _scrollOffset; + + _scrollController.animateTo( + targetOffset, + duration: const Duration(milliseconds: 400), + curve: Curves.easeInOut, + ); + }); + } + @override Widget build(BuildContext context) { - if (promotions.isEmpty) { + if (widget.promotions.isEmpty) { return const SizedBox.shrink(); } @@ -45,7 +97,7 @@ class PromotionSlider extends StatelessWidget { Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Text( - 'Chương trình ưu đãi', + 'Tin tức nổi bật', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w700, @@ -55,22 +107,22 @@ class PromotionSlider extends StatelessWidget { ), const SizedBox(height: 12), SizedBox( - height: 210, // 140px image + 54px text area + height: 210, child: ListView.builder( + controller: _scrollController, scrollDirection: Axis.horizontal, padding: const EdgeInsets.symmetric(horizontal: 16), - itemCount: promotions.length, + itemCount: widget.promotions.length, itemBuilder: (context, index) { return _PromotionCard( - promotion: promotions[index], + promotion: widget.promotions[index], onTap: () { - if (onPromotionTap != null) { - onPromotionTap!(promotions[index]); + if (widget.onPromotionTap != null) { + widget.onPromotionTap!(widget.promotions[index]); } else { - // Navigate to promotion detail page context.pushNamed( RouteNames.promotionDetail, - extra: promotions[index], + extra: widget.promotions[index], ); } },