TKK_E32230211/lib/app/modules/navbar/views/navbar_view.dart

234 lines
6.9 KiB
Dart

// lib/app/modules/navbar/views/navbar_view.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import '../controllers/navbar_controller.dart';
import '../../home/views/home_view.dart';
import '../../history/views/history_view.dart';
class NavbarView extends StatelessWidget {
const NavbarView({super.key});
@override
Widget build(BuildContext context) {
final controller = Get.find<NavbarController>();
return Scaffold(
backgroundColor: Colors.white,
body: Obx(
() => IndexedStack(
index: controller.selectedIndex.value,
children: const [HomeView(), HistoryView()],
),
),
bottomNavigationBar: SafeArea(
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
height: 70,
child: Stack(
clipBehavior: Clip.none,
alignment: Alignment.center,
children: [
// Navbar bar
Container(
height: 70,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(22),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.08),
blurRadius: 16,
offset: const Offset(0, 4),
),
],
),
child: Row(
children: [
Expanded(
child: Obx(
() => _NavItem(
icon: Icons.home_rounded,
label: 'Home',
active: controller.selectedIndex.value == 0,
onTap: () {
HapticFeedback.lightImpact();
controller.changeTabIndex(0);
},
),
),
),
const SizedBox(width: 76),
Expanded(
child: Obx(
() => _NavItem(
icon: Icons.receipt_long_rounded,
label: 'Riwayat',
active: controller.selectedIndex.value == 1,
onTap: () {
HapticFeedback.lightImpact();
controller.changeTabIndex(1);
},
),
),
),
],
),
),
// Tombol tengah — posisi sedikit naik dari navbar
Positioned(
top: -18, // naik 18px dari tengah navbar, tidak terlalu tinggi
child: const _CoinSlotButton(),
),
],
),
),
),
);
}
}
class _NavItem extends StatelessWidget {
final IconData icon;
final String label;
final bool active;
final VoidCallback onTap;
const _NavItem({
required this.icon,
required this.label,
required this.active,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
behavior: HitTestBehavior.opaque,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
icon,
size: 26,
color: active ? const Color(0xFF5B9BD5) : const Color(0xFFBDCFE0),
),
const SizedBox(height: 3),
Text(
label,
style: TextStyle(
color: active ? const Color(0xFF5B9BD5) : const Color(0xFFBDCFE0),
fontSize: 10,
fontWeight: active ? FontWeight.w700 : FontWeight.w400,
),
),
],
),
);
}
}
class _CoinSlotButton extends StatefulWidget {
const _CoinSlotButton();
@override
State<_CoinSlotButton> createState() => _CoinSlotButtonState();
}
class _CoinSlotButtonState extends State<_CoinSlotButton>
with SingleTickerProviderStateMixin {
late AnimationController _pressCtrl;
late Animation<double> _scaleAnim;
final NavbarController controller = Get.find<NavbarController>();
@override
void initState() {
super.initState();
_pressCtrl = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 100),
);
_scaleAnim = Tween<double>(
begin: 1.0,
end: 0.92,
).animate(CurvedAnimation(parent: _pressCtrl, curve: Curves.easeOut));
}
@override
void dispose() {
_pressCtrl.dispose();
super.dispose();
}
void _handleTap() async {
HapticFeedback.mediumImpact();
await _pressCtrl.forward();
_pressCtrl.reverse();
controller.toggleIot();
}
@override
Widget build(BuildContext context) {
return Obx(() {
final isOn = controller.isIotActive.value;
return AnimatedBuilder(
animation: _scaleAnim,
builder: (_, __) => Transform.scale(
scale: _scaleAnim.value,
child: GestureDetector(
onTap: _handleTap,
child: Container(
width: 62,
height: 62,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: isOn ? const Color(0xFFE57373) : const Color(0xFF5B9BD5),
border: Border.all(color: Colors.white, width: 3),
boxShadow: [
BoxShadow(
color:
(isOn
? const Color(0xFFE57373)
: const Color(0xFF5B9BD5))
.withOpacity(0.35),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
transitionBuilder: (child, anim) =>
ScaleTransition(scale: anim, child: child),
child: Icon(
isOn ? Icons.move_to_inbox_rounded : Icons.inventory_2,
key: ValueKey(isOn),
color: Colors.white,
size: 26,
),
),
const SizedBox(height: 2),
Text(
isOn ? 'TUTUP' : 'BUKA',
style: const TextStyle(
color: Colors.white,
fontSize: 7,
fontWeight: FontWeight.w800,
letterSpacing: 1.0,
),
),
],
),
),
),
),
);
});
}
}