E41220983_MuhamadSugengCahy.../praresi/lib/main.dart

337 lines
9.4 KiB
Dart

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<OverlayBubble> createState() => _OverlayBubbleState();
}
class _OverlayBubbleState extends State<OverlayBubble> 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<void> _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<void> _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<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> 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,
);
}
}