diff --git a/.flutter-plugins-dependencies b/.flutter-plugins-dependencies index d741512..3a100df 100644 --- a/.flutter-plugins-dependencies +++ b/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"image_picker_ios","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_ios-0.8.13\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.4.2\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_foundation-2.5.4\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"flutter_plugin_android_lifecycle","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_plugin_android_lifecycle-2.0.31\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"image_picker_android","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_android-0.8.13+1\\\\","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"],"dev_dependency":false},{"name":"path_provider_android","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_android-2.2.19\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_android","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_android-2.4.13\\\\","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"file_selector_macos","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_selector_macos-0.9.4+4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"image_picker_macos","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_macos-0.2.2\\\\","native_build":false,"dependencies":["file_selector_macos"],"dev_dependency":false},{"name":"path_provider_foundation","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.4.2\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_foundation-2.5.4\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"file_selector_linux","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_selector_linux-0.9.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"image_picker_linux","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_linux-0.2.2\\\\","native_build":false,"dependencies":["file_selector_linux"],"dev_dependency":false},{"name":"path_provider_linux","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_linux-2.2.1\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","native_build":false,"dependencies":["url_launcher_linux"],"dev_dependency":false},{"name":"shared_preferences_linux","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_linux-2.4.1\\\\","native_build":false,"dependencies":["path_provider_linux"],"dev_dependency":false},{"name":"url_launcher_linux","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_linux-3.2.2\\\\","native_build":true,"dependencies":[],"dev_dependency":false}],"windows":[{"name":"file_selector_windows","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_selector_windows-0.9.3+5\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"image_picker_windows","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_windows-0.2.2\\\\","native_build":false,"dependencies":["file_selector_windows"],"dev_dependency":false},{"name":"path_provider_windows","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_windows-2.3.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","native_build":true,"dependencies":["url_launcher_windows"],"dev_dependency":false},{"name":"shared_preferences_windows","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_windows-2.4.1\\\\","native_build":false,"dependencies":["path_provider_windows"],"dev_dependency":false},{"name":"url_launcher_windows","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_windows-3.1.5\\\\","native_build":true,"dependencies":[],"dev_dependency":false}],"web":[{"name":"image_picker_for_web","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_for_web-3.1.1\\\\","dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","dependencies":["url_launcher_web"],"dev_dependency":false},{"name":"shared_preferences_web","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_web-2.4.3\\\\","dependencies":[],"dev_dependency":false},{"name":"url_launcher_web","path":"C:\\\\Users\\\\Divya\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_web-2.4.1\\\\","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"file_selector_linux","dependencies":[]},{"name":"file_selector_macos","dependencies":[]},{"name":"file_selector_windows","dependencies":[]},{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"image_picker","dependencies":["image_picker_android","image_picker_for_web","image_picker_ios","image_picker_linux","image_picker_macos","image_picker_windows"]},{"name":"image_picker_android","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"image_picker_for_web","dependencies":[]},{"name":"image_picker_ios","dependencies":[]},{"name":"image_picker_linux","dependencies":["file_selector_linux"]},{"name":"image_picker_macos","dependencies":["file_selector_macos"]},{"name":"image_picker_windows","dependencies":["file_selector_windows"]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"share_plus","dependencies":["url_launcher_web","url_launcher_windows","url_launcher_linux"]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_web","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2025-12-11 16:10:50.183176","version":"3.38.3","swift_package_manager_enabled":{"ios":false,"macos":false}} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"image_picker_ios","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_ios-0.8.13\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.4.2\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"pusher_channels_flutter","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\pusher_channels_flutter-2.5.0\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_foundation-2.5.4\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"flutter_plugin_android_lifecycle","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\flutter_plugin_android_lifecycle-2.0.31\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"image_picker_android","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_android-0.8.13+1\\\\","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"],"dev_dependency":false},{"name":"path_provider_android","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_android-2.2.19\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"pusher_channels_flutter","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\pusher_channels_flutter-2.5.0\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_android","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_android-2.4.13\\\\","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"file_selector_macos","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_selector_macos-0.9.4+4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"image_picker_macos","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_macos-0.2.2\\\\","native_build":false,"dependencies":["file_selector_macos"],"dev_dependency":false},{"name":"path_provider_foundation","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_foundation-2.4.2\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"shared_preferences_foundation","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_foundation-2.5.4\\\\","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"file_selector_linux","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_selector_linux-0.9.4\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"image_picker_linux","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_linux-0.2.2\\\\","native_build":false,"dependencies":["file_selector_linux"],"dev_dependency":false},{"name":"path_provider_linux","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_linux-2.2.1\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","native_build":false,"dependencies":["url_launcher_linux"],"dev_dependency":false},{"name":"shared_preferences_linux","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_linux-2.4.1\\\\","native_build":false,"dependencies":["path_provider_linux"],"dev_dependency":false},{"name":"url_launcher_linux","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_linux-3.2.2\\\\","native_build":true,"dependencies":[],"dev_dependency":false}],"windows":[{"name":"file_selector_windows","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\file_selector_windows-0.9.3+5\\\\","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"image_picker_windows","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_windows-0.2.2\\\\","native_build":false,"dependencies":["file_selector_windows"],"dev_dependency":false},{"name":"path_provider_windows","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\path_provider_windows-2.3.0\\\\","native_build":false,"dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","native_build":true,"dependencies":["url_launcher_windows"],"dev_dependency":false},{"name":"shared_preferences_windows","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_windows-2.4.1\\\\","native_build":false,"dependencies":["path_provider_windows"],"dev_dependency":false},{"name":"url_launcher_windows","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_windows-3.1.5\\\\","native_build":true,"dependencies":[],"dev_dependency":false}],"web":[{"name":"image_picker_for_web","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\image_picker_for_web-3.1.1\\\\","dependencies":[],"dev_dependency":false},{"name":"pusher_channels_flutter","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\pusher_channels_flutter-2.5.0\\\\","dependencies":[],"dev_dependency":false},{"name":"share_plus","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\share_plus-10.1.4\\\\","dependencies":["url_launcher_web"],"dev_dependency":false},{"name":"shared_preferences_web","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\shared_preferences_web-2.4.3\\\\","dependencies":[],"dev_dependency":false},{"name":"url_launcher_web","path":"C:\\\\Users\\\\malia\\\\AppData\\\\Local\\\\Pub\\\\Cache\\\\hosted\\\\pub.dev\\\\url_launcher_web-2.4.1\\\\","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"file_selector_linux","dependencies":[]},{"name":"file_selector_macos","dependencies":[]},{"name":"file_selector_windows","dependencies":[]},{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"image_picker","dependencies":["image_picker_android","image_picker_for_web","image_picker_ios","image_picker_linux","image_picker_macos","image_picker_windows"]},{"name":"image_picker_android","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"image_picker_for_web","dependencies":[]},{"name":"image_picker_ios","dependencies":[]},{"name":"image_picker_linux","dependencies":["file_selector_linux"]},{"name":"image_picker_macos","dependencies":["file_selector_macos"]},{"name":"image_picker_windows","dependencies":["file_selector_windows"]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"pusher_channels_flutter","dependencies":[]},{"name":"share_plus","dependencies":["url_launcher_web","url_launcher_windows","url_launcher_linux"]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_web","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2025-12-15 09:40:43.969151","version":"3.32.2","swift_package_manager_enabled":{"ios":false,"macos":false}} \ No newline at end of file diff --git a/lib/config/api_config.dart b/lib/config/api_config.dart index 079e0aa..b132eff 100644 --- a/lib/config/api_config.dart +++ b/lib/config/api_config.dart @@ -1,3 +1,3 @@ class ApiConfig { - static const String baseUrl = "http://192.168.0.100:8000/api"; + static const String baseUrl = "http://10.11.236.74:8000/api"; } diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index 5c09b72..c0b252c 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -6,7 +6,7 @@ class AppConfig { static const String logoUrlEmulator = "http://10.0.2.2:8000/images/kent_logo2.png"; // For Physical Device (Replace with your actual PC local IP) - static const String logoUrlDevice = "http://10.207.50.74:8000/images/kent_logo2.png"; + static const String logoUrlDevice = "http://10.11.236.74:8000/images/kent_logo2.png"; // Which one to use? static const String logoUrl = logoUrlDevice; // CHANGE THIS WHEN TESTING ON REAL DEVICE diff --git a/lib/main.dart b/lib/main.dart index 3eb265b..a7a671c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:kent_logistics_app/providers/chat_unread_provider.dart'; import 'package:kent_logistics_app/providers/dashboard_provider.dart'; import 'package:kent_logistics_app/providers/invoice_provider.dart'; import 'package:kent_logistics_app/providers/mark_list_provider.dart'; @@ -30,6 +31,7 @@ void main() async { ), ChangeNotifierProvider(create: (_) => InvoiceProvider()), + ChangeNotifierProvider(create: (_) => ChatUnreadProvider()), ], child: const KentApp(), )); @@ -54,7 +56,7 @@ class KentApp extends StatelessWidget { ), ), ChangeNotifierProvider(create: (_) => InvoiceProvider()), - + ChangeNotifierProvider(create: (_) => ChatUnreadProvider()), ], child: MaterialApp( diff --git a/lib/providers/chat_unread_provider.dart b/lib/providers/chat_unread_provider.dart new file mode 100644 index 0000000..56627ae --- /dev/null +++ b/lib/providers/chat_unread_provider.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +class ChatUnreadProvider extends ChangeNotifier { + int _unreadCount = 0; + bool _chatOpen = false; + + int get unreadCount => _unreadCount; + bool get isChatOpen => _chatOpen; + + /// πŸ“© Called when ADMIN sends message + void increment() { + if (!_chatOpen) { + _unreadCount++; + notifyListeners(); + } + } + + /// πŸ‘ Called when chat screen is opened + void setChatOpen(bool open) { + _chatOpen = open; + + if (open) { + _unreadCount = 0; // reset badge when user opens chat + } + + notifyListeners(); + } + + /// πŸ” Manual reset (optional) + void reset() { + _unreadCount = 0; + notifyListeners(); + } +} diff --git a/lib/screens/chat_screen.dart b/lib/screens/chat_screen.dart index 35bc017..ccc6895 100644 --- a/lib/screens/chat_screen.dart +++ b/lib/screens/chat_screen.dart @@ -1,12 +1,188 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; -class ChatScreen extends StatelessWidget { +import '../services/chat_service.dart'; +import '../services/reverb_socket_service.dart'; +import '../services/dio_client.dart'; +import '../providers/chat_unread_provider.dart'; + +class ChatScreen extends StatefulWidget { const ChatScreen({super.key}); + @override + State createState() => _ChatScreenState(); +} + +class _ChatScreenState extends State { + final TextEditingController _messageCtrl = TextEditingController(); + final ScrollController _scrollCtrl = ScrollController(); + + late ChatService _chatService; + final ReverbSocketService _socket = ReverbSocketService(); + + int? ticketId; + List> messages = []; + bool isLoading = true; + + // ============================ + // INIT STATE + // ============================ + @override + void initState() { + super.initState(); + + _chatService = ChatService(DioClient.getInstance(context)); + + // πŸ”” Mark chat as OPEN (important) + WidgetsBinding.instance.addPostFrameCallback((_) { + context.read().setChatOpen(true); + }); + + _initChat(); + } + + // ============================ + // INIT CHAT + // ============================ + Future _initChat() async { + // 1️⃣ Start chat + final ticketRes = await _chatService.startChat(); + ticketId = ticketRes['ticket']['id']; + + // 2️⃣ Load messages + final msgs = await _chatService.getMessages(ticketId!); + messages = List>.from(msgs); + + // 3️⃣ Realtime socket + await _socket.connect( + context: context, + ticketId: ticketId!, + onMessage: (msg) { + if (!mounted) return; + setState(() => messages.add(msg)); + _scrollToBottom(); + }, + onAdminMessage: () { + if (!mounted) { + context.read().increment(); + } + }, + ); + + + + + if (!mounted) return; + setState(() => isLoading = false); + _scrollToBottom(); + } + + // ============================ + // SCROLL + // ============================ + void _scrollToBottom() { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (_scrollCtrl.hasClients) { + _scrollCtrl.jumpTo(_scrollCtrl.position.maxScrollExtent); + } + }); + } + + // ============================ + // SEND MESSAGE + // ============================ + Future _sendMessage() async { + final text = _messageCtrl.text.trim(); + if (text.isEmpty) return; + + _messageCtrl.clear(); + + await _chatService.sendMessage( + ticketId!, + message: text, + ); + } + + // ============================ + // DISPOSE + // ============================ + @override + void dispose() { + // πŸ”• Mark chat CLOSED + context.read().setChatOpen(false); + + _socket.disconnect(); + _messageCtrl.dispose(); + _scrollCtrl.dispose(); + super.dispose(); + } + + // ============================ + // UI + // ============================ @override Widget build(BuildContext context) { - return const Center( - child: Text("Invoice Content He", style: TextStyle(fontSize: 18)), + return Scaffold( + appBar: AppBar(title: const Text("Support Chat")), + body: isLoading + ? const Center(child: CircularProgressIndicator()) + : Column( + children: [ + Expanded(child: _buildMessages()), + _buildInput(), + ], + ), + ); + } + + Widget _buildMessages() { + return ListView.builder( + controller: _scrollCtrl, + padding: const EdgeInsets.all(12), + itemCount: messages.length, + itemBuilder: (_, index) { + final msg = messages[index]; + final isUser = msg['sender_type'] == 'App\\Models\\User'; + + return Align( + alignment: + isUser ? Alignment.centerRight : Alignment.centerLeft, + child: Container( + margin: const EdgeInsets.symmetric(vertical: 4), + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: isUser ? Colors.blue : Colors.grey.shade300, + borderRadius: BorderRadius.circular(12), + ), + child: Text( + msg['message'] ?? '', + style: TextStyle( + color: isUser ? Colors.white : Colors.black, + ), + ), + ), + ); + }, + ); + } + + Widget _buildInput() { + return SafeArea( + child: Row( + children: [ + Expanded( + child: TextField( + controller: _messageCtrl, + decoration: + const InputDecoration(hintText: "Type message"), + ), + ), + IconButton( + icon: const Icon(Icons.send), + onPressed: _sendMessage, + ), + ], + ), ); } } diff --git a/lib/screens/main_bottom_nav.dart b/lib/screens/main_bottom_nav.dart index ccb7b9e..d7b6320 100644 --- a/lib/screens/main_bottom_nav.dart +++ b/lib/screens/main_bottom_nav.dart @@ -5,6 +5,9 @@ import 'order_screen.dart'; import 'invoice_screen.dart'; import 'chat_screen.dart'; import 'settings_screen.dart'; +import 'package:provider/provider.dart'; +import '../providers/chat_unread_provider.dart'; + class MainBottomNav extends StatefulWidget { const MainBottomNav({super.key}); @@ -20,7 +23,7 @@ class MainBottomNavState extends State { setState(() => _currentIndex = index); } - final List _screens = const [ + final List _screens = [ DashboardScreen(), OrdersScreen(), InvoiceScreen(), @@ -161,11 +164,54 @@ class MainBottomNavState extends State { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon( + index == 3 + ? Consumer( + builder: (_, chat, __) { + return Stack( + clipBehavior: Clip.none, + children: [ + Icon( + _icons[index], + size: 22 * scale, + color: Colors.black87, + ), + + if (chat.unreadCount > 0) + Positioned( + right: -6, + top: -6, + child: Container( + padding: const EdgeInsets.all(4), + decoration: const BoxDecoration( + color: Colors.red, + shape: BoxShape.circle, + ), + constraints: const BoxConstraints( + minWidth: 20, + minHeight: 20, + ), + child: Center( + child: Text( + chat.unreadCount.toString(), + style: const TextStyle( + color: Colors.white, + fontSize: 10, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ], + ); + }, + ) + : Icon( _icons[index], size: 22 * scale, color: Colors.black87, ), + SizedBox(height: 4 * scale), Text( _labels[index], diff --git a/lib/services/chat_realtime_service.dart b/lib/services/chat_realtime_service.dart new file mode 100644 index 0000000..73fd704 --- /dev/null +++ b/lib/services/chat_realtime_service.dart @@ -0,0 +1,62 @@ +import 'dart:convert'; +import 'package:pusher_channels_flutter/pusher_channels_flutter.dart'; +import '../services/dio_client.dart'; + +class ChatRealtimeService { + static final PusherChannelsFlutter _pusher = + PusherChannelsFlutter.getInstance(); + + static Future connect({ + required int ticketId, + required Function(Map) onMessage, + }) async { + + print(" πŸ§ͺπŸ§ͺπŸ§ͺπŸ§ͺπŸ§ͺπŸ§ͺπŸ§ͺπŸ§ͺπŸ§ͺSUBSCRIBING TO: private-ticket.$ticketId"); + + await _pusher.init( + apiKey: "q5fkk5rvcnatvbgadwvl", + cluster: "mt1", + + onAuthorizer: (channelName, socketId, options) async { + print("πŸ§ͺπŸ§ͺπŸ§ͺπŸ§ͺπŸ§ͺπŸ§ͺπŸ§ͺπŸ§ͺπŸ§ͺπŸ§ͺAUTHORIZING CHANNEL: $channelName"); + + final dio = DioClient.getInstance(options); + + final res = await dio.post( + '/broadcasting/auth', + data: { + 'socket_id': socketId, + 'channel_name': channelName, + }, + ); + + return res.data; + }, + + onEvent: (event) { + if (event.eventName == "NewChatMessage") { + final data = jsonDecode(event.data); + onMessage(Map.from(data)); + } + }, + + onConnectionStateChange: (current, previous) { + print("PUSHER STATE: $current"); + }, + + onError: (message, code, error) { + print("PUSHER ERROR: $message"); + }, + ); + + await _pusher.subscribe( + channelName: "private-ticket.$ticketId", + ); + + await _pusher.connect(); + } + + static Future disconnect() async { + await _pusher.disconnect(); + } +} diff --git a/lib/services/chat_service.dart b/lib/services/chat_service.dart new file mode 100644 index 0000000..f9f34c9 --- /dev/null +++ b/lib/services/chat_service.dart @@ -0,0 +1,41 @@ +import 'package:dio/dio.dart'; +import '../config/api_config.dart'; + +class ChatService { + final Dio dio; + + ChatService(this.dio); + + /// Start chat / get ticket + Future> startChat() async { + final res = await dio.get('/user/chat/start'); + return Map.from(res.data); + } + + /// Get all messages + Future> getMessages(int ticketId) async { + final res = await dio.get('/user/chat/messages/$ticketId'); + return res.data['messages']; + } + + /// Send message (text or file) + Future sendMessage( + int ticketId, { + String? message, + String? filePath, + }) async { + final form = FormData(); + + if (message != null) form.fields.add(MapEntry('message', message)); + if (filePath != null) { + form.files.add( + MapEntry( + 'file', + await MultipartFile.fromFile(filePath), + ), + ); + } + + await dio.post('/user/chat/send/$ticketId', data: form); + } +} diff --git a/lib/services/reverb_socket_service.dart b/lib/services/reverb_socket_service.dart new file mode 100644 index 0000000..188edad --- /dev/null +++ b/lib/services/reverb_socket_service.dart @@ -0,0 +1,204 @@ +import 'dart:async'; +import 'dart:convert'; +import 'package:web_socket_channel/web_socket_channel.dart'; +import 'package:flutter/material.dart'; +import '../services/dio_client.dart'; + +class ReverbSocketService { + WebSocketChannel? _channel; + Timer? _pingTimer; + bool _connected = false; + + late BuildContext _context; + late int _ticketId; + late Function(Map) _onMessage; + late VoidCallback _onBackgroundMessage; + late VoidCallback _onAdminMessage; + + + /// Prevent duplicate messages on reconnect + final Set _receivedIds = {}; + + // ============================ + // CONNECT + // ============================ + Future connect({ + required BuildContext context, + required int ticketId, + required Function(Map) onMessage, + //required VoidCallback onBackgroundMessage, + required VoidCallback onAdminMessage, // πŸ‘ˆ ADD + }) async { + _context = context; + _ticketId = ticketId; + _onMessage = onMessage; + //_onBackgroundMessage = onBackgroundMessage; + _onAdminMessage = onAdminMessage; // πŸ‘ˆ SAVE + + final uri = Uri.parse( + 'ws://10.11.236.74:8080/app/q5fkk5rvcnatvbgadwvl' + '?protocol=7&client=flutter&version=1.0', + ); + + debugPrint("πŸ”Œ CONNECTING SOCKET β†’ $uri"); + + _channel = WebSocketChannel.connect(uri); + + _channel!.stream.listen( + _handleMessage, + onDone: _handleDisconnect, + onError: (e) { + debugPrint("❌ SOCKET ERROR: $e"); + _handleDisconnect(); + }, + ); + } + + // ============================ + // HANDLE SOCKET MESSAGE + // ============================ + Future _handleMessage(dynamic raw) async { + debugPrint("πŸ“₯ RAW: $raw"); + + final payload = jsonDecode(raw); + final event = payload['event']?.toString() ?? ''; + + // -------------------------------- + // CONNECTED + // -------------------------------- + if (event == 'pusher:connection_established') { + final socketId = jsonDecode(payload['data'])['socket_id']; + debugPrint("βœ… CONNECTED | socket_id=$socketId"); + + await _subscribe(socketId); + _startHeartbeat(); + _connected = true; + return; + } + + // -------------------------------- + // HEARTBEAT + // -------------------------------- + if (event == 'pusher:pong') { + debugPrint("πŸ’“ PONG"); + return; + } + + // -------------------------------- + // CHAT MESSAGE + // -------------------------------- + if ( + event == 'NewChatMessage' || + event.endsWith('.NewChatMessage') || + event.contains('NewChatMessage')) { + dynamic data = payload['data']; + if (data is String) data = jsonDecode(data); + + final int msgId = data['id']; + final String senderType = data['sender_type'] ?? ''; + + // πŸ” Prevent duplicates + if (_receivedIds.contains(msgId)) { + debugPrint("πŸ” DUPLICATE MESSAGE IGNORED: $msgId"); + return; + } + _receivedIds.add(msgId); + + debugPrint("πŸ“© NEW MESSAGE"); + debugPrint("πŸ†” id=$msgId"); + debugPrint("πŸ‘€ sender=$senderType"); + debugPrint("πŸ’¬ text=${data['message']}"); + + // Always push message to UI + _onMessage(Map.from(data)); + + // πŸ”” Increment unread ONLY if ADMIN sent message + // πŸ”” Increment unread ONLY if ADMIN sent message + if (senderType == 'App\\Models\\Admin') { + debugPrint("πŸ”” ADMIN MESSAGE β†’ UNREAD +1"); + _onAdminMessage(); // βœ… ACTUAL INCREMENT + } + + + return; + } + } + + // ============================ + // SUBSCRIBE PRIVATE CHANNEL + // ============================ + Future _subscribe(String socketId) async { + debugPrint("πŸ“‘ SUBSCRIBING private-ticket.$_ticketId"); + + final dio = DioClient.getInstance(_context); + + final res = await dio.post( + '/broadcasting/auth', + data: { + 'socket_id': socketId, + 'channel_name': 'private-ticket.$_ticketId', + }, + ); + + _channel!.sink.add(jsonEncode({ + 'event': 'pusher:subscribe', + 'data': { + 'channel': 'private-ticket.$_ticketId', + 'auth': res.data['auth'], + }, + })); + + debugPrint("βœ… SUBSCRIBED private-ticket.$_ticketId"); + } + + // ============================ + // HEARTBEAT + // ============================ + void _startHeartbeat() { + _pingTimer?.cancel(); + + _pingTimer = Timer.periodic( + const Duration(seconds: 30), + (_) { + if (_connected) { + debugPrint("πŸ’“ SENDING PING"); + _channel?.sink.add(jsonEncode({ + 'event': 'pusher:ping', + 'data': {} + })); + } + }, + ); + } + + // ============================ + // HANDLE DISCONNECT + // ============================ + void _handleDisconnect() { + debugPrint("⚠️ SOCKET DISCONNECTED β†’ RECONNECTING"); + + _connected = false; + _pingTimer?.cancel(); + _channel?.sink.close(); + + Future.delayed(const Duration(seconds: 2), () { + connect( + context: _context, + ticketId: _ticketId, + onMessage: _onMessage, + // onBackgroundMessage: _onBackgroundMessage, + onAdminMessage: _onAdminMessage, // πŸ‘ˆ ADD + ); + }); + } + + // ============================ + // MANUAL DISCONNECT + // ============================ + void disconnect() { + debugPrint("❌ SOCKET CLOSED MANUALLY"); + _connected = false; + _pingTimer?.cancel(); + _channel?.sink.close(); + } +} diff --git a/pubspec.lock b/pubspec.lock index 69c47f0..d55cc77 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -296,30 +296,38 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.2" + js: + dependency: transitive + description: + name: js + sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" + url: "https://pub.dev" + source: hosted + version: "0.7.2" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" url: "https://pub.dev" source: hosted - version: "11.0.2" + version: "10.0.9" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 url: "https://pub.dev" source: hosted - version: "3.0.10" + version: "3.0.9" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.1" lints: dependency: transitive description: @@ -348,10 +356,10 @@ packages: dependency: transitive description: name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.16.0" mime: dependency: transitive description: @@ -480,6 +488,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.5+1" + pusher_channels_flutter: + dependency: "direct main" + description: + name: pusher_channels_flutter + sha256: "4d83b2012079c7d1a3c42cee1d37a48c974504d869b6a9c085144b2d4e35e58a" + url: "https://pub.dev" + source: hosted + version: "2.5.0" qr: dependency: transitive description: @@ -609,10 +625,10 @@ packages: dependency: transitive description: name: test_api - sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.7.7" + version: "0.7.4" typed_data: dependency: transitive description: @@ -665,10 +681,10 @@ packages: dependency: transitive description: name: vector_math - sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.1.4" vm_service: dependency: transitive description: @@ -685,6 +701,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + web_socket_channel: + dependency: "direct main" + description: + name: web_socket_channel + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + url: "https://pub.dev" + source: hosted + version: "2.4.0" win32: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 190dd26..4be21b7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -40,7 +40,8 @@ dependencies: share_plus: ^10.0.0 path_provider: ^2.1.2 pdf: ^3.11.0 - + pusher_channels_flutter: ^2.3.0 + web_socket_channel: ^2.4.0 # The following adds the Cupertino Icons font to your application.