Skip to main content

connect_x_flutter_livechat_sdk 1.2.2 copy "connect_x_flutter_livechat_sdk: ^1.2.2" to clipboard
connect_x_flutter_livechat_sdk: ^1.2.2 copied to clipboard

Live Chat SDK for Flutter

example/lib/main.dart

import 'package:connect_x_flutter_livechat_sdk/connect_x_flutter_livechat_sdk.dart';
import 'package:flutter/material.dart';

// --- Connection config (kept here so the setup screen can re-use it) ---
// Pass with: flutter run --dart-define=CX_TOKEN=<token>
const String _token = String.fromEnvironment('CX_TOKEN');
const String _orgId = "0000OrgRAndDConnectX";
const String _channelId = "9318254639";
const String _externalValue = "Chubby"; // This links the user to your CRM

// Sample customer images for testers to choose from.
// The selected URL is sent as `cx_customerImage` in initialize().
const List<String> _sampleCustomerImages = [
  'https://bucket.connect-x.tech/connectx/api/storage/files/84c5a330-43dc-4506-b88d-4f31005b8cd9',
  'https://i.pravatar.cc/300?img=12',
  'https://i.pravatar.cc/300?img=47',
];

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: SetupPage());
  }
}

/// Setup screen: tester picks one of the sample customer images, then we
/// initialize the chat connector with that image as `cx_customerImage`.
class SetupPage extends StatefulWidget {
  const SetupPage({super.key});

  @override
  State<SetupPage> createState() => _SetupPageState();
}

class _SetupPageState extends State<SetupPage> {
  int _selectedIndex = 0;
  bool _isConnecting = false;

  Future<void> _connectAndContinue() async {
    setState(() => _isConnecting = true);

    try {
      await ChatController.instance.initialize(
        token: _token,
        orgId: _orgId,
        channelId: _channelId,
        externalValue: _externalValue,
        chatInfo: {
          'cx_customerImage': _sampleCustomerImages[_selectedIndex],
        },
      );

      if (!mounted) return;
      Navigator.pushReplacement(
        context,
        MaterialPageRoute(builder: (context) => const MyHomePage()),
      );
    } catch (e) {
      if (!mounted) return;
      setState(() => _isConnecting = false);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text("Connect failed: $e")),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("เลือกรูปลูกค้าเพื่อทดสอบ")),
      body: Padding(
        padding: const EdgeInsets.all(24),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            const Text(
              "เลือกรูปภาพที่จะส่งเป็น cx_customerImage แล้วกดเชื่อมต่อ",
              style: TextStyle(fontSize: 16),
            ),
            const SizedBox(height: 24),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: List.generate(_sampleCustomerImages.length, (index) {
                final isSelected = index == _selectedIndex;
                return GestureDetector(
                  onTap: _isConnecting
                      ? null
                      : () => setState(() => _selectedIndex = index),
                  child: Container(
                    decoration: BoxDecoration(
                      shape: BoxShape.circle,
                      border: Border.all(
                        color: isSelected
                            ? Colors.blueAccent
                            : Colors.grey.shade300,
                        width: isSelected ? 4 : 2,
                      ),
                    ),
                    child: CircleAvatar(
                      radius: 44,
                      backgroundColor: Colors.grey.shade200,
                      backgroundImage:
                          NetworkImage(_sampleCustomerImages[index]),
                    ),
                  ),
                );
              }),
            ),
            const Spacer(),
            ElevatedButton(
              style: ElevatedButton.styleFrom(
                padding: const EdgeInsets.symmetric(vertical: 16),
                backgroundColor: Colors.blueAccent,
              ),
              onPressed: _isConnecting ? null : _connectAndContinue,
              child: _isConnecting
                  ? const SizedBox(
                      height: 20,
                      width: 20,
                      child: CircularProgressIndicator(
                        strokeWidth: 2,
                        color: Colors.white,
                      ),
                    )
                  : const Text(
                      "เชื่อมต่อ & เปิดแอป",
                      style: TextStyle(color: Colors.white, fontSize: 16),
                    ),
            ),
          ],
        ),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
  }

  void _openChatScreen() {
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => ChatScreen(
          onClose: () => Navigator.pop(context),
          isHideCloseButton: true,
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("User App"),
        actions: [
          ValueListenableBuilder<bool>(
            valueListenable: ChatController.instance.hasUnreadMessagesNotifier,
            builder: (context, hasUnread, child) {
              return Stack(
                children: [
                  IconButton(
                    icon: const Icon(Icons.notifications),
                    onPressed: () {
                      // Clear the red dot and open the chat screen
                      ChatController.instance.markAsRead();
                      _openChatScreen();
                    },
                  ),
                  if (hasUnread)
                    Positioned(
                      right: 12,
                      top: 12,
                      child: Container(
                        padding: const EdgeInsets.all(2),
                        decoration: BoxDecoration(
                          color: Colors.red,
                          borderRadius: BorderRadius.circular(6),
                        ),
                        constraints: const BoxConstraints(
                          minWidth: 12,
                          minHeight: 12,
                        ),
                      ),
                    ),
                ],
              );
            },
          ),
        ],
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // 2. SEND MESSAGE MANUALLY (FROM FORM)
            ElevatedButton(
              style: ElevatedButton.styleFrom(
                padding:
                    const EdgeInsets.symmetric(horizontal: 30, vertical: 15),
                backgroundColor: Colors.blueAccent,
              ),
              onPressed: () =>
                  _showManualSendModal(context), // Open the small modal
              child: const Text(
                "Quick Support Message",
                style: TextStyle(color: Colors.white, fontSize: 16),
              ),
            ),

            const SizedBox(height: 20),

            // 3. START CHAT UI
            ElevatedButton(
              onPressed: _openChatScreen,
              child: const Text("Open Chat UI"),
            ),
          ],
        ),
      ),
    );
  }
}

void _showManualSendModal(BuildContext context) {
  final TextEditingController modalController = TextEditingController();

  showDialog(
    context: context,
    builder: (context) {
      return AlertDialog(
        title: const Text("Quick Message"),
        content: TextField(
          controller: modalController,
          autofocus: true,
          decoration: const InputDecoration(
            hintText: "Enter your message here...",
            border: OutlineInputBorder(),
          ),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text("Cancel"),
          ),
          ElevatedButton(
            onPressed: () {
              final text = modalController.text.trim();
              if (text.isNotEmpty) {
                // Call your library
                ChatController.instance.sendMessage(text: text);

                Navigator.pop(context); // Close modal

                ScaffoldMessenger.of(context).showSnackBar(
                  const SnackBar(content: Text("Message sent successfully!")),
                );
              }
            },
            child: const Text("Send"),
          ),
        ],
      );
    },
  );
}