From 3f75bffd09f0ced2cf04ce3976ab83c1dc6d86a7 Mon Sep 17 00:00:00 2001 From: efrilm Date: Wed, 20 Aug 2025 13:57:00 +0700 Subject: [PATCH] feat: pull to refresh on home --- lib/presentation/pages/home/home_page.dart | 219 +++++++++--------- .../pages/home/widgets/top_product.dart | 4 + 2 files changed, 117 insertions(+), 106 deletions(-) diff --git a/lib/presentation/pages/home/home_page.dart b/lib/presentation/pages/home/home_page.dart index 78ba82e..114ccc9 100644 --- a/lib/presentation/pages/home/home_page.dart +++ b/lib/presentation/pages/home/home_page.dart @@ -67,119 +67,126 @@ class _HomePageState extends State with TickerProviderStateMixin { Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColor.background, - body: BlocBuilder( - builder: (context, state) { - return CustomScrollView( - physics: const BouncingScrollPhysics( - parent: ClampingScrollPhysics(), - ), - slivers: [ - // SliverAppBar with HomeHeader as background - SliverAppBar( - expandedHeight: 260, // Adjust based on HomeHeader height - floating: true, - pinned: true, - snap: true, - elevation: 0, - scrolledUnderElevation: 8, - backgroundColor: AppColor.primary, - surfaceTintColor: Colors.transparent, - flexibleSpace: LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - // Calculate collapse progress (0.0 = expanded, 1.0 = collapsed) - final double expandedHeight = 200; - final double collapsedHeight = - kToolbarHeight + MediaQuery.of(context).padding.top; - final double currentHeight = constraints.maxHeight; + body: RefreshIndicator( + onRefresh: () { + context.read().add(HomeEvent.fetchedDashboard()); + return Future.value(); + }, + child: BlocBuilder( + builder: (context, state) { + return CustomScrollView( + physics: const BouncingScrollPhysics( + parent: ClampingScrollPhysics(), + ), + slivers: [ + // SliverAppBar with HomeHeader as background + SliverAppBar( + expandedHeight: 260, // Adjust based on HomeHeader height + floating: true, + pinned: true, + snap: true, + elevation: 0, + scrolledUnderElevation: 8, + backgroundColor: AppColor.primary, + surfaceTintColor: Colors.transparent, + flexibleSpace: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + // Calculate collapse progress (0.0 = expanded, 1.0 = collapsed) + final double expandedHeight = 200; + final double collapsedHeight = + kToolbarHeight + MediaQuery.of(context).padding.top; + final double currentHeight = constraints.maxHeight; - double collapseProgress = - 1.0 - - ((currentHeight - collapsedHeight) / - (expandedHeight - collapsedHeight)); - collapseProgress = collapseProgress.clamp(0.0, 1.0); + double collapseProgress = + 1.0 - + ((currentHeight - collapsedHeight) / + (expandedHeight - collapsedHeight)); + collapseProgress = collapseProgress.clamp(0.0, 1.0); - return FlexibleSpaceBar( - title: Opacity( - opacity: collapseProgress, // Title muncul saat collapse - child: Row( - children: [ - Expanded( - child: Text( - AppConstant.appName, - style: AppStyle.xl.copyWith( - fontWeight: FontWeight.w700, - fontSize: 18, - letterSpacing: -0.5, - color: AppColor.white, + return FlexibleSpaceBar( + title: Opacity( + opacity: + collapseProgress, // Title muncul saat collapse + child: Row( + children: [ + Expanded( + child: Text( + AppConstant.appName, + style: AppStyle.xl.copyWith( + fontWeight: FontWeight.w700, + fontSize: 18, + letterSpacing: -0.5, + color: AppColor.white, + ), ), ), - ), - ActionIconButton( - onTap: () {}, - icon: LineIcons.bell, - ), - ], - ), - ), - titlePadding: const EdgeInsets.only( - left: 20, - right: 12, - bottom: 16, - ), - background: AnimatedBuilder( - animation: _headerAnimationController, - builder: (context, child) { - return Transform.translate( - offset: Offset( - 0, - 50 * (1 - _headerAnimationController.value), - ), - child: Opacity( - opacity: _headerAnimationController.value, - child: HomeHeader( - totalRevenue: - state.dashboard.overview.totalSales, + ActionIconButton( + onTap: () {}, + icon: LineIcons.bell, ), - ), - ); - }, - ), - ); - }, - ), - ), - - // Main Content - SliverToBoxAdapter( - child: AnimatedBuilder( - animation: _contentAnimationController, - builder: (context, child) { - return Transform.translate( - offset: Offset( - 0, - 30 * (1 - _contentAnimationController.value), - ), - child: Opacity( - opacity: _contentAnimationController.value, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - HomeFeature(), - HomeStats(overview: state.dashboard.overview), - HomeTopProduct( - products: state.dashboard.topProducts, - ), - const SpaceHeight(40), - ], + ], + ), ), - ), - ); - }, + titlePadding: const EdgeInsets.only( + left: 20, + right: 12, + bottom: 16, + ), + background: AnimatedBuilder( + animation: _headerAnimationController, + builder: (context, child) { + return Transform.translate( + offset: Offset( + 0, + 50 * (1 - _headerAnimationController.value), + ), + child: Opacity( + opacity: _headerAnimationController.value, + child: HomeHeader( + totalRevenue: + state.dashboard.overview.totalSales, + ), + ), + ); + }, + ), + ); + }, + ), ), - ), - ], - ); - }, + + // Main Content + SliverToBoxAdapter( + child: AnimatedBuilder( + animation: _contentAnimationController, + builder: (context, child) { + return Transform.translate( + offset: Offset( + 0, + 30 * (1 - _contentAnimationController.value), + ), + child: Opacity( + opacity: _contentAnimationController.value, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + HomeFeature(), + HomeStats(overview: state.dashboard.overview), + HomeTopProduct( + products: state.dashboard.topProducts, + ), + const SpaceHeight(40), + ], + ), + ), + ); + }, + ), + ), + ], + ); + }, + ), ), ); } diff --git a/lib/presentation/pages/home/widgets/top_product.dart b/lib/presentation/pages/home/widgets/top_product.dart index 3500d3c..b95b8a1 100644 --- a/lib/presentation/pages/home/widgets/top_product.dart +++ b/lib/presentation/pages/home/widgets/top_product.dart @@ -13,6 +13,10 @@ class HomeTopProduct extends StatelessWidget { @override Widget build(BuildContext context) { + if (products.isEmpty) { + return const SizedBox.shrink(); + } + return Padding( padding: const EdgeInsets.symmetric( vertical: 24,