TKK_E32230859/lib/app/modules/home/views/home_view.dart

308 lines
8.6 KiB
Dart

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<HomeController> {
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<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 1200),
)..repeat(reverse: true);
_animation = Tween<double>(
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);
}
}