baxcloud_chat_uikit_sdk 0.1.0 copy "baxcloud_chat_uikit_sdk: ^0.1.0" to clipboard
baxcloud_chat_uikit_sdk: ^0.1.0 copied to clipboard

PlatformAndroid

BaxCloud Chat UI Kit — realtime messaging with ready-made layouts (standard, compact, live overlay) and bring-your-own design hooks.

example/lib/main.dart

import 'package:baxcloud_chat_uikit_sdk/baxcloud_chat_uikit_sdk.dart';
import 'package:baxcloud_core/baxcloud_core.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

/// Demo modes shown in the example app.
enum _DemoMode {
  chatScreen,
  liveComments,
  compactPanel,
  customByo,
}

/// Two-device test:
/// 1. Device A → user1, room `demo-chat`
/// 2. Device B → user2, same room
/// 3. Send messages on either device
void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  const projectId = String.fromEnvironment('BAXCLOUD_PROJECT_ID');
  const apiKey = String.fromEnvironment('BAXCLOUD_API_KEY');

  final prefs = await SharedPreferences.getInstance();
  final savedUserId = prefs.getString('bax_chat_user_id') ??
      const String.fromEnvironment('BAXCLOUD_USER_ID', defaultValue: 'user1');
  final savedUserName = prefs.getString('bax_chat_user_name') ??
      const String.fromEnvironment('BAXCLOUD_USER_NAME', defaultValue: 'User 1');
  final savedRoom = prefs.getString('bax_chat_room') ?? 'demo-chat';

  final config = BaxConfig(projectId: projectId, apiKey: apiKey);
  if (config.isValid) {
    await BaxChats.initialize(
      config: config,
      localUser: BaxcloudUser(userId: savedUserId, name: savedUserName),
    );
  }

  runApp(ChatExampleApp(
    config: config,
    initialUserId: savedUserId,
    initialUserName: savedUserName,
    initialRoom: savedRoom,
  ));
}

class ChatExampleApp extends StatelessWidget {
  final BaxConfig config;
  final String initialUserId;
  final String initialUserName;
  final String initialRoom;

  const ChatExampleApp({
    super.key,
    required this.config,
    required this.initialUserId,
    required this.initialUserName,
    required this.initialRoom,
  });

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'BaxCloud Chat',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF059669)),
        useMaterial3: true,
      ),
      home: BaxChatScope(
        config: config,
        child: ChatHomePage(
          config: config,
          initialUserId: initialUserId,
          initialUserName: initialUserName,
          initialRoom: initialRoom,
        ),
      ),
    );
  }
}

class ChatHomePage extends StatefulWidget {
  final BaxConfig config;
  final String initialUserId;
  final String initialUserName;
  final String initialRoom;

  const ChatHomePage({
    super.key,
    required this.config,
    required this.initialUserId,
    required this.initialUserName,
    required this.initialRoom,
  });

  @override
  State<ChatHomePage> createState() => _ChatHomePageState();
}

class _ChatHomePageState extends State<ChatHomePage> {
  late final TextEditingController _userIdController;
  late final TextEditingController _userNameController;
  late final TextEditingController _roomController;

  _DemoMode _mode = _DemoMode.chatScreen;
  bool _inChat = false;
  bool _saving = false;
  bool _joinLeaveEnabled = true;
  bool _typingEnabled = false;
  String? _lastError;

  @override
  void initState() {
    super.initState();
    _userIdController = TextEditingController(text: widget.initialUserId);
    _userNameController = TextEditingController(text: widget.initialUserName);
    _roomController = TextEditingController(text: widget.initialRoom);
  }

  @override
  void dispose() {
    _userIdController.dispose();
    _userNameController.dispose();
    _roomController.dispose();
    super.dispose();
  }

  BaxChatUiConfig get _uiConfig {
    switch (_mode) {
      case _DemoMode.chatScreen:
        return BaxChatUiConfig.standard(
          showJoinMessages: _joinLeaveEnabled,
          showLeaveMessages: _joinLeaveEnabled,
          showSystemMessages: _joinLeaveEnabled,
          enableTypingIndicators: _typingEnabled,
          joinedTextTemplate: '{name} joined the chat',
          leftTextTemplate: '{name} left the chat',
          showTimestamps: true,
          edgeFade: BaxChatEdgeFade.none,
        );
      case _DemoMode.liveComments:
        return BaxChatUiConfig.liveOverlay(
          showJoinMessages: _joinLeaveEnabled,
          showLeaveMessages: false,
          // Stage chrome provides leave — avoid a second X mid-screen.
          showLeaveButton: false,
          enableTypingIndicators: _typingEnabled,
          joinedTextTemplate: '{name} joined the stream',
          edgeFade: BaxChatEdgeFade.top,
          edgeFadeExtent: 64,
        );
      case _DemoMode.compactPanel:
        return BaxChatUiConfig.compact(
          showJoinMessages: false,
          showLeaveMessages: false,
          showLeaveButton: true,
          enableTypingIndicators: _typingEnabled,
          bubbleBorderRadius: 10,
          localBubbleColor: const Color(0xFF059669),
        );
      case _DemoMode.customByo:
        return BaxChatUiConfig.standard(
          showHeader: false,
          backgroundColor: const Color(0xFF111827),
          localBubbleColor: const Color(0xFF7C3AED),
          bubbleBorderRadius: 20,
          enableTypingIndicators: _typingEnabled,
          showJoinMessages: _joinLeaveEnabled,
          showLeaveMessages: _joinLeaveEnabled,
          joinedTextBuilder: (name) => '👋 $name is here',
          leftTextBuilder: (name) => '👋 $name left',
        );
    }
  }

  BaxcloudUser get _user => BaxcloudUser(
        userId: _userIdController.text.trim(),
        name: _userNameController.text.trim(),
      );

  Future<void> _saveProfile() async {
    setState(() {
      _saving = true;
      _lastError = null;
    });
    try {
      final prefs = await SharedPreferences.getInstance();
      await prefs.setString('bax_chat_user_id', _userIdController.text.trim());
      await prefs.setString('bax_chat_user_name', _userNameController.text.trim());
      await prefs.setString('bax_chat_room', _roomController.text.trim());
      if (widget.config.isValid) {
        await BaxChats.updateLocalUser(_user);
      }
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('Profile saved')),
        );
      }
    } catch (e) {
      setState(() => _lastError = '$e');
    } finally {
      if (mounted) setState(() => _saving = false);
    }
  }

  void _joinChat() {
    if (!widget.config.isValid) {
      setState(() => _lastError = 'Set BAXCLOUD_PROJECT_ID and BAXCLOUD_API_KEY');
      return;
    }
    final room = _roomController.text.trim();
    final userId = _userIdController.text.trim();
    if (room.isEmpty || userId.isEmpty) {
      setState(() => _lastError = 'Room and user id are required');
      return;
    }
    setState(() {
      _lastError = null;
      _inChat = true;
    });
  }

  Widget _buildChatBody() {
    final view = BaxChatView(
      roomName: _roomController.text.trim(),
      user: _user,
      isHost: true,
      uiConfig: _uiConfig,
      onLeave: () => setState(() => _inChat = false),
      inChatBuilder: _mode == _DemoMode.customByo
          ? (context, session) {
              return Column(
                children: [
                  Container(
                    width: double.infinity,
                    padding: const EdgeInsets.fromLTRB(16, 48, 16, 12),
                    color: const Color(0xFF1F2937),
                    child: Row(
                      children: [
                        const Expanded(
                          child: Text(
                            'My custom chat chrome',
                            style: TextStyle(
                              color: Colors.white,
                              fontWeight: FontWeight.w700,
                              fontSize: 18,
                            ),
                          ),
                        ),
                        TextButton(
                          onPressed: session.leave,
                          child: const Text('Leave'),
                        ),
                      ],
                    ),
                  ),
                  Expanded(child: session.chatSurface),
                ],
              );
            }
          : null,
    );

    if (_mode == _DemoMode.liveComments) {
      return Stack(
        fit: StackFit.expand,
        children: [
          // Fake live video / stage background
          const DecoratedBox(
            decoration: BoxDecoration(
              gradient: LinearGradient(
                begin: Alignment.topLeft,
                end: Alignment.bottomRight,
                colors: [
                  Color(0xFF7C2D12),
                  Color(0xFF1E1B4B),
                  Color(0xFF0F172A),
                ],
              ),
            ),
          ),
          const Align(
            alignment: Alignment(0.35, -0.35),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                Icon(Icons.live_tv, color: Colors.white70, size: 56),
                SizedBox(height: 8),
                Text(
                  'LIVE STAGE (demo)',
                  style: TextStyle(
                    color: Colors.white70,
                    fontWeight: FontWeight.w600,
                    letterSpacing: 1.2,
                  ),
                ),
              ],
            ),
          ),
          // Live comments docked to the left (typical stream chat).
          Positioned(
            left: 0,
            bottom: 0,
            width: MediaQuery.sizeOf(context).width * 0.72,
            height: MediaQuery.sizeOf(context).height * 0.48,
            child: view,
          ),
          Positioned(
            top: 0,
            right: 0,
            child: SafeArea(
              child: Padding(
                padding: const EdgeInsets.all(8),
                child: Material(
                  color: Colors.black.withValues(alpha: 0.45),
                  shape: const CircleBorder(),
                  clipBehavior: Clip.antiAlias,
                  child: IconButton(
                    tooltip: 'Leave',
                    onPressed: () => setState(() => _inChat = false),
                    icon: const Icon(Icons.close_rounded, color: Colors.white),
                  ),
                ),
              ),
            ),
          ),
        ],
      );
    }

    if (_mode == _DemoMode.compactPanel) {
      return ColoredBox(
        color: const Color(0xFF0F172A),
        child: SafeArea(
          child: Align(
            alignment: Alignment.centerRight,
            child: SizedBox(
              width: MediaQuery.sizeOf(context).width * 0.78,
              child: Material(
                elevation: 8,
                color: const Color(0xFF0B1220),
                child: view,
              ),
            ),
          ),
        ),
      );
    }

    return view;
  }

  @override
  Widget build(BuildContext context) {
    if (_inChat) {
      return Scaffold(
        body: _buildChatBody(),
      );
    }

    return Scaffold(
      appBar: AppBar(title: const Text('BaxCloud Chat Example')),
      body: ListView(
        padding: const EdgeInsets.all(20),
        children: [
          Text(
            'Demo modes',
            style: Theme.of(context).textTheme.titleLarge,
          ),
          const SizedBox(height: 8),
          Text(
            'Pick a preset, then join the same room on two devices (user1 / user2).',
            style: Theme.of(context).textTheme.bodyMedium?.copyWith(
                  color: Theme.of(context).colorScheme.onSurfaceVariant,
                ),
          ),
          const SizedBox(height: 16),
          ..._DemoMode.values.map((mode) {
            final selected = _mode == mode;
            return Card(
              margin: const EdgeInsets.only(bottom: 8),
              color: selected
                  ? Theme.of(context).colorScheme.primaryContainer
                  : null,
              child: ListTile(
                selected: selected,
                title: Text(_modeTitle(mode)),
                subtitle: Text(_modeSubtitle(mode)),
                onTap: () => setState(() => _mode = mode),
              ),
            );
          }),
          const SizedBox(height: 8),
          SwitchListTile(
            contentPadding: EdgeInsets.zero,
            title: const Text('Show join / leave messages'),
            subtitle: const Text(
              'Uses customizable templates (i18n-ready)',
            ),
            value: _joinLeaveEnabled,
            onChanged: (v) => setState(() => _joinLeaveEnabled = v),
          ),
          SwitchListTile(
            contentPadding: EdgeInsets.zero,
            title: const Text('Enable typing indicators'),
            subtitle: const Text(
              'Off by default — opt in to show “is typing…”',
            ),
            value: _typingEnabled,
            onChanged: (v) => setState(() => _typingEnabled = v),
          ),
          const SizedBox(height: 16),
          TextField(
            controller: _userIdController,
            decoration: const InputDecoration(
              labelText: 'Your user id',
              border: OutlineInputBorder(),
            ),
          ),
          const SizedBox(height: 12),
          TextField(
            controller: _userNameController,
            decoration: const InputDecoration(
              labelText: 'Display name',
              border: OutlineInputBorder(),
            ),
          ),
          const SizedBox(height: 12),
          TextField(
            controller: _roomController,
            decoration: const InputDecoration(
              labelText: 'Room name',
              border: OutlineInputBorder(),
            ),
          ),
          if (_lastError != null) ...[
            const SizedBox(height: 16),
            Text(_lastError!, style: const TextStyle(color: Colors.red)),
          ],
          const SizedBox(height: 24),
          FilledButton.icon(
            onPressed: _saving ? null : _saveProfile,
            icon: _saving
                ? const SizedBox(
                    width: 18,
                    height: 18,
                    child: CircularProgressIndicator(strokeWidth: 2),
                  )
                : const Icon(Icons.save_outlined),
            label: const Text('Save profile'),
          ),
          const SizedBox(height: 12),
          FilledButton.icon(
            onPressed: _joinChat,
            icon: const Icon(Icons.chat_bubble_outline),
            label: const Text('Open demo'),
          ),
        ],
      ),
    );
  }

  String _modeTitle(_DemoMode mode) {
    switch (mode) {
      case _DemoMode.chatScreen:
        return 'Chat screen';
      case _DemoMode.liveComments:
        return 'Live streaming comments';
      case _DemoMode.compactPanel:
        return 'Compact side panel';
      case _DemoMode.customByo:
        return 'Bring your own chrome';
    }
  }

  String _modeSubtitle(_DemoMode mode) {
    switch (mode) {
      case _DemoMode.chatScreen:
        return 'Full-screen standard chat with header + timestamps';
      case _DemoMode.liveComments:
        return 'Left-aligned overlay comments; leave via top-right X';
      case _DemoMode.compactPanel:
        return 'Dense chat docked right — leave via floating X';
      case _DemoMode.customByo:
        return 'inChatBuilder wraps kit surface with custom header';
    }
  }
}
0
likes
160
points
107
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

BaxCloud Chat UI Kit — realtime messaging with ready-made layouts (standard, compact, live overlay) and bring-your-own design hooks.

Homepage

Topics

#baxcloud #chat #messaging #realtime #flutter

License

MIT (license)

Dependencies

baxcloud_core, flutter, livekit_client, livekit_components, uuid

More

Packages that depend on baxcloud_chat_uikit_sdk