import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../controllers/home_controller.dart'; import '../../../widgets/banner_slider.dart'; import '../../../widgets/monitoring_card.dart'; class HomeView extends GetView { const HomeView({super.key}); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF5F7FA), body: SafeArea( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildHeader(context), BannerSlider( bannerImages: controller.bannerImages, currentCarouselIndex: controller.currentCarouselIndex, onPageChanged: controller.updateCarouselIndex, ), const SizedBox(height: 16), _buildSectionHeader(), const SizedBox(height: 8), Expanded(child: _buildMonitoringList()), ], ), ), ); } Widget _buildHeader(BuildContext context) { return Container( color: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Selamat Datang 👋', style: TextStyle( fontSize: 13, color: Colors.grey[500], fontWeight: FontWeight.w400, ), ), const SizedBox(height: 2), const Text( 'Halo, Perawat', style: TextStyle( fontSize: 22, fontWeight: FontWeight.bold, color: Color(0xFF1A1D2E), ), ), ], ), InkWell( onTap: () => controller.showLogoutDialog(context), borderRadius: BorderRadius.circular(12), child: Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: const Color(0xFFFFF0F0), border: Border.all(color: const Color(0xFFFFCDD2)), borderRadius: BorderRadius.circular(12), ), child: const Icon( Icons.logout_rounded, color: Colors.red, size: 20, ), ), ), ], ), ); } Widget _buildSectionHeader() { return Padding( padding: const EdgeInsets.symmetric(horizontal: 20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Monitoring Infus', style: TextStyle( fontSize: 17, fontWeight: FontWeight.bold, color: Color(0xFF1A1D2E), ), ), ], ), ); } Widget _buildMonitoringList() { return Obx(() { if (controller.isLoading.value) { return _buildLoadingState(); } if (controller.monitoringList.isEmpty) { return _buildEmptyState(); } return RefreshIndicator( color: const Color(0xFF0091EA), onRefresh: () async { // Trigger refresh — stream akan otomatis update, // ini hanya untuk UX pull-to-refresh await Future.delayed(const Duration(milliseconds: 800)); }, child: ListView.builder( padding: const EdgeInsets.fromLTRB(20, 4, 20, 20), physics: const AlwaysScrollableScrollPhysics(), itemCount: controller.monitoringList.length, itemBuilder: (context, index) { final item = controller.monitoringList[index]; return Padding( padding: const EdgeInsets.only(bottom: 12), child: MonitoringCard( monitoring: item, onTap: () => Get.toNamed( '/device-detail', arguments: {'deviceId': item.deviceId, 'roomId': item.roomId}, ), ), ); }, ), ); }); } Widget _buildLoadingState() { return ListView.builder( padding: const EdgeInsets.fromLTRB(20, 4, 20, 20), itemCount: 4, itemBuilder: (context, index) { return Padding( padding: const EdgeInsets.only(bottom: 12), child: _buildSkeletonCard(), ); }, ); } Widget _buildSkeletonCard() { return Container( height: 110, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.04), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Row( children: [ _shimmerBox(width: 36, height: 36, radius: 10), const SizedBox(width: 12), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _shimmerBox(width: 140, height: 13, radius: 6), const SizedBox(height: 6), _shimmerBox(width: 90, height: 11, radius: 6), ], ), const Spacer(), _shimmerBox(width: 60, height: 26, radius: 20), ], ), const SizedBox(height: 12), Row( children: [ _shimmerBox(width: 80, height: 11, radius: 6), const SizedBox(width: 16), _shimmerBox(width: 60, height: 11, radius: 6), ], ), ], ), ), ); } Widget _shimmerBox({ required double width, required double height, double radius = 4, }) { return _ShimmerWidget( child: Container( width: width, height: height, decoration: BoxDecoration( color: Colors.grey[200], borderRadius: BorderRadius.circular(radius), ), ), ); } Widget _buildEmptyState() { return Center( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 40), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: 100, height: 100, decoration: BoxDecoration( color: const Color(0xFFE3F2FD), borderRadius: BorderRadius.circular(30), ), child: const Icon( Icons.water_drop_outlined, size: 48, color: Color(0xFF0091EA), ), ), const SizedBox(height: 20), const Text( 'Belum Ada Data', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Color(0xFF1A1D2E), ), ), const SizedBox(height: 8), Text( 'Data monitoring infus akan tampil di sini setelah perangkat terhubung.', textAlign: TextAlign.center, style: TextStyle( fontSize: 14, color: Colors.grey[500], height: 1.5, ), ), ], ), ), ); } } /// Widget shimmer animasi untuk skeleton loading class _ShimmerWidget extends StatefulWidget { final Widget child; const _ShimmerWidget({required this.child}); @override State<_ShimmerWidget> createState() => _ShimmerWidgetState(); } class _ShimmerWidgetState extends State<_ShimmerWidget> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation _animation; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 1200), )..repeat(reverse: true); _animation = Tween( begin: 0.4, end: 1.0, ).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut)); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return FadeTransition(opacity: _animation, child: widget.child); } }