cb_prestige_qr/lib/features/home/presentation/views/home_view.dart
2026-04-27 15:38:26 +06:30

210 lines
7.4 KiB
Dart

import 'package:carousel_slider/carousel_slider.dart';
import 'package:cb_prestige_qr/core/presentation/widgets/global_loading_overlay.dart';
import 'package:cb_prestige_qr/features/history/presentation/widgets/history_item_tile.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../viewmodels/home_ui_state.dart';
import '../viewmodels/home_view_model.dart';
import '../widgets/home_today_summary_section.dart';
class HomeView extends ConsumerWidget {
const HomeView({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = Theme.of(context);
final viewModel = ref.read(homeViewModelProvider.notifier);
final stateAsync = ref.watch(homeViewModelProvider);
return Scaffold(
backgroundColor: theme.scaffoldBackgroundColor,
appBar: AppBar(
elevation: 0,
scrolledUnderElevation: 0,
titleSpacing: 16,
title: Row(
children: [
Image.asset(
'assets/images/logo_white.png',
height: 60,
fit: BoxFit.contain,
),
const Spacer(),
Image.asset(
'assets/images/cb_logo_white.png',
height: 50,
fit: BoxFit.contain,
),
],
),
),
body: stateAsync.when(
data: (state) => _HomeBody(
state: state,
onSeeAll: viewModel.onSeeAllTapped,
onRecentScanTap: viewModel.onRecentScanTapped,
),
loading: () => const GlobalLoadingOverlay(isLoading: true),
error: (error, stackTrace) => Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text('Failed to load Home'),
const SizedBox(height: 10),
TextButton(
onPressed: () => ref.invalidate(homeViewModelProvider),
child: const Text('Retry'),
),
],
),
),
),
),
);
}
}
class _HomeBody extends StatelessWidget {
const _HomeBody({
required this.state,
required this.onSeeAll,
required this.onRecentScanTap,
});
final HomeUiState state;
final VoidCallback onSeeAll;
final void Function(int index) onRecentScanTap;
@override
Widget build(BuildContext context) {
final shellBottomInset = 70 + MediaQuery.of(context).padding.bottom;
final contentBottomPadding = shellBottomInset + 8;
return CustomScrollView(
slivers: [
SliverPadding(
padding: const EdgeInsets.fromLTRB(16, 10, 16, 0),
sliver: SliverToBoxAdapter(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(16),
child: CarouselSlider(
options: CarouselOptions(
height: MediaQuery.of(context).size.height * 0.3,
viewportFraction: 1.0,
autoPlay: true,
autoPlayInterval: const Duration(seconds: 3),
autoPlayAnimationDuration: const Duration(
milliseconds: 800,
),
autoPlayCurve: Curves.fastOutSlowIn,
),
items: state.carouselAssets.map((assetPath) {
return Builder(
builder: (BuildContext context) {
return SizedBox(
width: double.infinity,
child: Image.asset(assetPath, fit: BoxFit.cover),
);
},
);
}).toList(),
),
),
const SizedBox(height: 20),
HomeTodaySummarySection(
customersLabel: '18',
earnedCoinsLabel: '+3,240',
redeemedCoinsLabel: '-1,120',
onViewDetailsTap: onSeeAll,
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'Recent History',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
TextButton(
onPressed: onSeeAll,
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
minimumSize: Size.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
foregroundColor: Colors.blue,
),
child: const Text('See All'),
),
],
),
const SizedBox(height: 10),
],
),
),
),
SliverPadding(
padding: EdgeInsets.fromLTRB(16, 0, 16, contentBottomPadding),
sliver: state.recentScans.isEmpty
? const SliverToBoxAdapter(
child: Padding(
padding: EdgeInsets.only(top: 40),
child: Center(child: Text('No recent scans')),
),
)
: SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index.isOdd) return const SizedBox(height: 10);
final itemIndex = index ~/ 2;
final item = state.recentScans[itemIndex];
final colorScheme = Theme.of(context).colorScheme;
return Container(
decoration: BoxDecoration(
color: colorScheme.surface,
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: colorScheme.outlineVariant.withOpacity(0.5),
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.06),
blurRadius: 14,
offset: const Offset(0, 6),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: HistoryItemTile(
title: item.title,
subtitle: item.subtitle,
timeLabel: 'Just now',
amountLabel: null,
isRedeem: null,
onTap: () => onRecentScanTap(itemIndex),
),
),
);
},
childCount: state.recentScans.isEmpty
? 0
: state.recentScans.length * 2 - 1,
),
),
),
],
);
}
}