import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:praresi/presentation/controllers/internet_controller.dart'; import 'package:praresi/services/bubble_services.dart'; import 'firebase_options.dart'; import 'presentation/routes/app_pages.dart'; import 'presentation/routes/app_routes.dart'; import 'package:intl/date_symbol_data_local.dart'; // ✅ Entry point overlay @pragma("vm:entry-point") void overlayMain() { runApp( const MaterialApp( debugShowCheckedModeBanner: false, home: OverlayBubble(), ), ); } class OverlayBubble extends StatefulWidget { const OverlayBubble({super.key}); @override State createState() => _OverlayBubbleState(); } class _OverlayBubbleState extends State with WidgetsBindingObserver { bool _showInput = false; final TextEditingController _controller = TextEditingController(); @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); // 👀 pantau lifecycle app } @override void dispose() { WidgetsBinding.instance.removeObserver(this); // _controller.dispose(); super.dispose(); } // 🔔 Dipanggil saat app pause/resume/close @override void didChangeAppLifecycleState(AppLifecycleState state) async { if (state == AppLifecycleState.detached || state == AppLifecycleState.paused) { // Tutup bubble saat app ditutup atau masuk background await BubbleService().hide(); } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.transparent, body: Center( child: AnimatedSwitcher( duration: const Duration(milliseconds: 300), transitionBuilder: (child, animation) => ScaleTransition( scale: animation, child: child, ), child: _showInput ? _buildInputBox(context) : Stack( alignment: Alignment.topRight, // tombol X di pojok kanan atas children: [ GestureDetector( key: const ValueKey('bubble'), onTap: _expandToInput, child: const CircleAvatar( radius: 30, backgroundColor: Colors.white, child: Icon(Icons.book_rounded, color: Colors.black, size: 32), ), ), // Tombol Close Positioned( right: -2, // sedikit keluar dari bubble top: -5, child: GestureDetector( onTap: () async { await BubbleService().hide(); // close bubble }, child: Container( decoration: BoxDecoration( color: Colors.red, shape: BoxShape.rectangle, ), child: const Icon( Icons.close, size: 18, color: Colors.white, ), ), ), ), ], ), ), ), ); } Widget _buildInputBox(BuildContext context) { return GestureDetector( behavior: HitTestBehavior.deferToChild, onTapDown: (_) => FocusScope.of(context).unfocus(), child: Container( key: const ValueKey('inputBox'), width: 340, padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(24), boxShadow: const [ BoxShadow( color: Colors.black26, blurRadius: 15, offset: Offset(0, 6), ), ], ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( "Masukkan Data Pengiriman", style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: Colors.black87, ), ), const SizedBox(height: 10), Listener( onPointerDown: (_) {}, child: TextField( controller: _controller, // <-- ini yang menghubungkan TextField dengan controller maxLines: 4, keyboardType: TextInputType.multiline, decoration: InputDecoration( hintText: "Data Pengiriman...", hintStyle: const TextStyle(color: Colors.grey), filled: true, fillColor: Colors.grey.shade100, contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12), border: OutlineInputBorder( borderRadius: BorderRadius.circular(14), borderSide: BorderSide(color: Colors.grey.shade400), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(14), borderSide: const BorderSide(color: Colors.blue, width: 1.8), ), ), ) ), const SizedBox(height: 10), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( onPressed: () async { final clipboardData = await Clipboard.getData(Clipboard.kTextPlain); if (clipboardData?.text != null && clipboardData!.text!.isNotEmpty) { setState(() { _controller.text = clipboardData.text!; _controller.selection = TextSelection.fromPosition( TextPosition(offset: _controller.text.length), ); }); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text("Clipboard kosong")), ); } }, style: TextButton.styleFrom( foregroundColor: Colors.green, textStyle: const TextStyle(fontSize: 12), ), child: const Text("Tempel"), ), // 🔘 Tombol Hapus TextButton( onPressed: () { _controller.clear(); // menghapus isi TextField }, style: TextButton.styleFrom( foregroundColor: Colors.redAccent, textStyle: const TextStyle(fontSize: 12), ), child: const Text("Hapus"), ), const SizedBox(width: 8), TextButton( onPressed: _collapseToBubble, style: TextButton.styleFrom( foregroundColor: const Color.fromARGB(255, 57, 57, 57), textStyle: const TextStyle(fontSize: 12), ), child: const Text("Batal"), ), const SizedBox(width: 8), ElevatedButton( onPressed: () { debugPrint("Input terkirim: ${_controller.text}"); _collapseToBubble(); }, style: ElevatedButton.styleFrom( backgroundColor: Colors.blueAccent, padding: const EdgeInsets.symmetric(horizontal: 22, vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14), ), ), child: const Text( "Simpan", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Colors.white, ), ), ), ], ), ], ), ), ); } // ✅ Saat bubble diklik, ubah tampilan jadi kotak input besar Future _expandToInput() async { await BubbleService().resize(360, 260, animated: true); await Future.delayed(const Duration(milliseconds: 200)); // biar sempat apply setState(() => _showInput = true); } // ✅ Saat menekan "Batal" atau "Kirim", kembalikan ke bubble kecil Future _collapseToBubble() async { setState(() => _showInput = false); await BubbleService().resize(100, 100, animated: true); } } void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); WidgetsFlutterBinding.ensureInitialized(); // 🟢 tambahkan ini // 🟢 inisialisasi locale "id_ID" untuk format tanggal Indonesia await initializeDateFormatting('id_ID', null); Get.put(InternetController()); runApp(const MyApp()); AppLifecycleListener( onDetach: () async { await BubbleService().hide(); }, ); } class MyApp extends StatefulWidget { const MyApp({super.key}); @override State createState() => _MyAppState(); } class _MyAppState extends State with WidgetsBindingObserver { @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) async { if (state == AppLifecycleState.detached) { await BubbleService().hide(); // bubble hilang hanya saat app ditutup } } @override Widget build(BuildContext context) { return GetMaterialApp( debugShowCheckedModeBanner: false, title: 'Praresi', initialRoute: AppRoutes.splash, getPages: AppPages.routes, ); } }