im_plugin_tencent_web 0.0.6 copy "im_plugin_tencent_web: ^0.0.6" to clipboard
im_plugin_tencent_web: ^0.0.6 copied to clipboard

Platformweb

A Flutter plugin integrating Tencent IM for web, supporting chat and audio/video calls. Uses UI-free access method, providing ready-to-use APIs. Web-only integration.

example/lib/main.dart

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import 'package:im_plugin_tencent_web/im/init.dart';
import 'package:im_plugin_tencent_web/im_plugin_tencent_web.dart';
import 'package:im_plugin_tencent_web/state/im_state.dart';
import 'package:im_plugin_tencent_web/widget/conversation_list.dart';
import 'package:im_plugin_tencent_web/widget/chat.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();

  final TextEditingController imSdkAppIdController = TextEditingController();
  final TextEditingController imLoginUserIdController = TextEditingController();
  final TextEditingController imLoginUserSigController = TextEditingController();

  /// 拨打音视频通话的对方用户id,多个用 , 分割
  final TextEditingController imCallUserIdController = TextEditingController();

  final ValueNotifier<String> _uiTypeNotifier = ValueNotifier('default');

  /// 当前登录 im 用户的 userId 复制是否成功,显示一下复制成功的对勾后,会立刻置为 false
  final ValueNotifier<bool> _userIdCopyNotifier = ValueNotifier(false);

  late final VoidCallback _imSdkAppIDListener;
  late final VoidCallback _imChatLoginListener;

  @override
  void initState() {
    super.initState();

    _imSdkAppIDListener = () {
      imSdkAppIdController.text = ImState.imSdkAppID.value?.toString().trim() ?? '';
    };
    ImState.imSdkAppID.addListener(_imSdkAppIDListener);

    _imChatLoginListener = () {
      imLoginUserIdController.text = ImState.chatLoginInfo.value?.userId ?? '';
      imLoginUserSigController.text = ImState.chatLoginInfo.value?.userSig ?? '';
    };
    ImState.chatLoginInfo.addListener(_imChatLoginListener);

    WidgetsBinding.instance.addPostFrameCallback((_) async {
      try {
        await doImTencentWebPluginInit();
      } catch (_) {
        Future.delayed(Duration(milliseconds: 400), () {
          _scaffoldKey.currentState?.openEndDrawer();
        });
      }
    });
  }

  @override
  void dispose() {
    ImState.imSdkAppID.removeListener(_imSdkAppIDListener);
    ImState.chatLoginInfo.removeListener(_imChatLoginListener);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        key: _scaffoldKey,
        appBar: AppBar(
          title: const Text('腾讯 IM WEB 插件', style: TextStyle(fontSize: 16, color: Colors.white,),),
          backgroundColor: Colors.transparent,
          actions: [
            Builder(
              builder: (context) => Padding(
                padding: EdgeInsets.only(right: 10),
                child: IconButton(
                  icon: Icon(Icons.settings),
                  onPressed: () {
                    Scaffold.of(context).openEndDrawer();
                  },
                ),
              ),
            ),
          ],
        ),
        endDrawer: Builder(builder: (ctx) {
          return Container(
            color: Colors.grey.shade900,
            width: 400,
            height: MediaQuery.of(context).size.height,
            child: SingleChildScrollView(
              padding: EdgeInsets.only(left: 20, right: 20, top: 20, bottom: 20,),
              child: AnimatedBuilder(
                animation: Listenable.merge([
                  ImState.chatInstance,
                  ImState.chatLoginInfo,
                ]),
                builder: (ctx, _) {
                  return Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text('IM 初始化', style: TextStyle(fontSize: 14, color: Colors.grey.withAlpha(160)),),
                      const SizedBox(height: 5,),
                      Row(
                        children: [
                          Expanded(
                            child: IgnorePointer(
                              ignoring: ImState.chatInstance.value != null,
                              child: Opacity(
                                opacity: ImState.chatInstance.value != null ? 0.2 : 1,
                                child: SizedBox(
                                  height: 40,
                                  child: TextField(
                                    controller: imSdkAppIdController,
                                    style: TextStyle(fontSize: 14, color: Colors.white,),
                                    decoration: InputDecoration(
                                      hintText: '在这里输入 sdkAppID',
                                      hintStyle: TextStyle(fontSize: 14, color: Colors.white.withAlpha(30),),
                                      filled: true,
                                      fillColor: Colors.black87.withAlpha(100),
                                      border: OutlineInputBorder(
                                        borderRadius: BorderRadius.circular(8).copyWith(topRight: Radius.zero, bottomRight: Radius.zero,),
                                        borderSide: BorderSide.none,
                                      ),
                                      contentPadding: const EdgeInsets.symmetric(vertical: 13, horizontal: 12),
                                    ),
                                    keyboardType: TextInputType.number,
                                    autofocus: true,
                                  ),
                                ),
                              ),
                            ),
                          ),
                          IgnorePointer(
                            ignoring: ImState.chatInstance.value != null,
                            child: Opacity(
                              opacity: ImState.chatInstance.value != null ? 0.2 : 1,
                              child: SizedBox(
                                height: 40,
                                child: ElevatedButton(
                                  style: ButtonStyle(
                                    elevation: WidgetStateProperty.all(0),
                                    shadowColor: WidgetStateProperty.all(Colors.transparent),
                                    backgroundColor: WidgetStateProperty.all(Colors.black),
                                    shape: WidgetStateProperty.all(
                                      RoundedRectangleBorder(
                                        borderRadius: BorderRadius.circular(8).copyWith(topLeft: Radius.zero, bottomLeft: Radius.zero,),
                                      ),
                                    ),
                                  ),
                                  onPressed: () {
                                    int? sdkAppId = int.tryParse(imSdkAppIdController.text);
                                    if (sdkAppId == null) {
                                      imSdkAppIdController.text = '';
                                      ScaffoldMessenger.of(ctx).showSnackBar(SnackBar(
                                        content: const Text('请输入有效的 sdkAppId'),
                                        duration: Duration(milliseconds: 800),
                                      ));
                                      return;
                                    }
                                    doImTencentWebPluginInit(sdkAppId: sdkAppId);
                                  },
                                  child: Text('初始化腾讯 IM'),
                                ),
                              ),
                            ),
                          ),
                        ],
                      ),
                      const SizedBox(height: 15,),
                      Text('IM 销毁', style: TextStyle(fontSize: 14, color: Colors.grey.withAlpha(160)),),
                      const SizedBox(height: 5,),
                      IgnorePointer(
                        ignoring: ImState.chatInstance.value == null,
                        child: Opacity(
                          opacity: ImState.chatInstance.value == null ? 0.2 : 1,
                          child: SizedBox(
                            width: MediaQuery.of(context).size.width,
                            height: 40,
                            child: ElevatedButton(
                              style: ButtonStyle(
                                elevation: WidgetStateProperty.all(0),
                                shadowColor: WidgetStateProperty.all(Colors.transparent),
                                backgroundColor: WidgetStateProperty.all(Colors.redAccent),
                                foregroundColor: WidgetStateProperty.all(Colors.white),
                                shape: WidgetStateProperty.all(
                                  RoundedRectangleBorder(
                                    borderRadius: BorderRadius.circular(8),
                                  ),
                                ),
                              ),
                              onPressed: () async {
                                try {
                                  await ImPluginTencentWeb.destroy();
                                  if (ctx.mounted) {
                                    ScaffoldMessenger.of(ctx).showSnackBar(SnackBar(
                                      content: Text('销毁成功'),
                                      duration: Duration(milliseconds: 800),
                                    ));
                                  }
                                } catch(err) {
                                  if (kDebugMode) {
                                    print('销毁出错:$err');
                                  }
                                  if (ctx.mounted) {
                                    ScaffoldMessenger.of(ctx).showSnackBar(SnackBar(
                                      content: Text('销毁出错:$err'),
                                      duration: Duration(milliseconds: 800),
                                    ));
                                  }
                                }
                              },
                              child: Text('销毁腾讯 IM'),
                            ),
                          ),
                        ),
                      ),
                      const SizedBox(height: 15,),
                      Text('登录 / 登出 IM 账号', style: TextStyle(fontSize: 14, color: Colors.grey.withAlpha(160)),),
                      const SizedBox(height: 5,),
                      Container(
                        width: MediaQuery.of(context).size.width,
                        decoration: BoxDecoration(
                          color: Colors.black87.withAlpha(40),
                          borderRadius: BorderRadius.circular(8),
                        ),
                        padding: EdgeInsets.all(10),
                        child: Column(
                          children: [
                            Row(
                              children: [
                                Expanded(child: IgnorePointer(
                                  ignoring: ImState.chatInstance.value == null || ImState.chatLoginInfo.value != null,
                                  child: Opacity(
                                    opacity: ImState.chatInstance.value == null || ImState.chatLoginInfo.value != null ? 0.2 : 1,
                                    child: Row(
                                      children: [
                                        Container(
                                          width: 50,
                                          height: 40,
                                          decoration: BoxDecoration(
                                            color: Colors.black87.withAlpha(80),
                                            borderRadius: BorderRadius.circular(8).copyWith(topRight: Radius.zero, bottomRight: Radius.zero,),
                                          ),
                                          padding: EdgeInsets.only(left: 6),
                                          child: Center(
                                            child: Icon(Icons.account_circle_sharp, color: Colors.grey.withAlpha(60),),
                                          ),
                                        ),
                                        Expanded(child: SizedBox(
                                          height: 40,
                                          child: TextField(
                                            controller: imLoginUserIdController,
                                            style: TextStyle(fontSize: 14, color: Colors.white,),
                                            decoration: InputDecoration(
                                              hintText: '输入 userID',
                                              hintStyle: TextStyle(fontSize: 14, color: Colors.white.withAlpha(30),),
                                              filled: true,
                                              fillColor: Colors.black87.withAlpha(100),
                                              border: OutlineInputBorder(
                                                borderRadius: BorderRadius.circular(8).copyWith(topLeft: Radius.zero, bottomLeft: Radius.zero,),
                                                borderSide: BorderSide.none,
                                              ),
                                              contentPadding: const EdgeInsets.symmetric(vertical: 13, horizontal: 10),
                                            ),
                                            keyboardType: TextInputType.number,
                                            autofocus: true,
                                          ),
                                        ),),
                                      ],
                                    ),
                                  ),
                                ),),
                                if (ImState.chatInstance.value != null && ImState.chatLoginInfo.value != null) GestureDetector(
                                  onTap: () async {
                                    if (_userIdCopyNotifier.value) {
                                      return;
                                    }
                                    await Clipboard.setData(ClipboardData(text: imLoginUserIdController.text.trim()));
                                    _userIdCopyNotifier.value = true;
                                    Future.delayed(Duration(milliseconds: 800), () {
                                      _userIdCopyNotifier.value = false;
                                    });
                                  },
                                  behavior: HitTestBehavior.opaque,
                                  child: Container(
                                    width: 50,
                                    height: 40,
                                    padding: EdgeInsets.only(left: 6),
                                    child: Center(
                                      child: ValueListenableBuilder(
                                        valueListenable: _userIdCopyNotifier,
                                        builder: (c, userIdCopyResult, _) {
                                          return userIdCopyResult ? Icon(Icons.done, color: Colors.green, size: 16,) : Icon(Icons.copy_all_sharp, color: Colors.white, size: 16,);
                                        },
                                      ),
                                    ),
                                  ),
                                ),
                              ],
                            ),
                            const SizedBox(height: 5,),
                            IgnorePointer(
                              ignoring: ImState.chatInstance.value == null || ImState.chatLoginInfo.value != null,
                              child: Opacity(
                                opacity: ImState.chatInstance.value == null || ImState.chatLoginInfo.value != null ? 0.2 : 1,
                                child: Row(
                                  children: [
                                    Container(
                                      width: 50,
                                      height: 40,
                                      decoration: BoxDecoration(
                                        color: Colors.black87.withAlpha(80),
                                        borderRadius: BorderRadius.circular(8).copyWith(topRight: Radius.zero, bottomRight: Radius.zero,),
                                      ),
                                      padding: EdgeInsets.only(left: 6),
                                      child: Center(
                                        child: Icon(Icons.signpost_sharp, color: Colors.grey.withAlpha(60),),
                                      ),
                                    ),
                                    Expanded(child: SizedBox(
                                      height: 40,
                                      child: TextField(
                                        controller: imLoginUserSigController,
                                        style: TextStyle(fontSize: 14, color: Colors.white,),
                                        decoration: InputDecoration(
                                          hintText: '输入 userSig',
                                          hintStyle: TextStyle(fontSize: 14, color: Colors.white.withAlpha(30),),
                                          filled: true,
                                          fillColor: Colors.black87.withAlpha(100),
                                          border: OutlineInputBorder(
                                            borderRadius: BorderRadius.circular(8).copyWith(topLeft: Radius.zero, bottomLeft: Radius.zero,),
                                            borderSide: BorderSide.none,
                                          ),
                                          contentPadding: const EdgeInsets.symmetric(vertical: 13, horizontal: 10),
                                        ),
                                        keyboardType: TextInputType.number,
                                        autofocus: true,
                                      ),
                                    ),),
                                  ],
                                ),
                              ),
                            ),
                            const SizedBox(height: 20,),
                            IgnorePointer(
                              ignoring: ImState.chatInstance.value == null,
                              child: Opacity(
                                opacity: ImState.chatInstance.value == null ? 0.2 : 1,
                                child: Builder(builder: (ctx) {
                                  if (ImState.chatLoginInfo.value == null) {
                                    return SizedBox(
                                      width: MediaQuery.of(context).size.width,
                                      height: 40,
                                      child: ElevatedButton(
                                        style: ButtonStyle(
                                          elevation: WidgetStateProperty.all(0),
                                          shadowColor: WidgetStateProperty.all(Colors.transparent),
                                          backgroundColor: WidgetStateProperty.all(Colors.black),
                                          shape: WidgetStateProperty.all(
                                            RoundedRectangleBorder(
                                              borderRadius: BorderRadius.circular(8),
                                            ),
                                          ),
                                        ),
                                        onPressed: () async {
                                          if (imLoginUserIdController.text.trim() == '') {
                                            ScaffoldMessenger.of(ctx).showSnackBar(SnackBar(
                                              content: const Text('请输入 userID'),
                                              duration: Duration(milliseconds: 800),
                                            ));
                                            return;
                                          }
                                          if (imLoginUserSigController.text.trim() == '') {
                                            ScaffoldMessenger.of(ctx).showSnackBar(SnackBar(
                                              content: const Text('请输入 userSig'),
                                              duration: Duration(milliseconds: 800),
                                            ));
                                            return;
                                          }
                                          try {
                                            doImTencentWebPluginInit(userId: imLoginUserIdController.text.trim(), userSig: imLoginUserSigController.text.trim());
                                          } catch(err) {
                                            if (kDebugMode) {
                                              print('登录出错:$err');
                                            }
                                            if (ctx.mounted) {
                                              ScaffoldMessenger.of(ctx).showSnackBar(SnackBar(
                                                content: Text('登录出错:$err'),
                                                duration: Duration(milliseconds: 800),
                                              ));
                                            }
                                          }
                                        },
                                        child: Text('登录腾讯 IM 账号'),
                                      ),
                                    );
                                  }

                                  return SizedBox(
                                    width: MediaQuery.of(context).size.width,
                                    height: 40,
                                    child: ElevatedButton(
                                      style: ButtonStyle(
                                        elevation: WidgetStateProperty.all(0),
                                        shadowColor: WidgetStateProperty.all(Colors.transparent),
                                        backgroundColor: WidgetStateProperty.all(Colors.redAccent),
                                        foregroundColor: WidgetStateProperty.all(Colors.white),
                                        shape: WidgetStateProperty.all(
                                          RoundedRectangleBorder(
                                            borderRadius: BorderRadius.circular(8),
                                          ),
                                        ),
                                      ),
                                      onPressed: () async {
                                        try {
                                          await ImPluginTencentWeb.logout();
                                        } catch(err) {
                                          if (kDebugMode) {
                                            print('登出出错:$err');
                                          }
                                          if (ctx.mounted) {
                                            ScaffoldMessenger.of(ctx).showSnackBar(SnackBar(
                                              content: Text('登出出错:$err'),
                                              duration: Duration(milliseconds: 800),
                                            ));
                                          }
                                        }
                                      },
                                      child: Text('登出腾讯 IM 账号'),
                                    ),
                                  );
                                }),
                              ),
                            ),
                          ],
                        ),
                      ),
                      const SizedBox(height: 25,),
                      Text('UI 使用方式', style: TextStyle(fontSize: 14, color: Colors.grey.withAlpha(160)),),
                      const SizedBox(height: 8,),
                      Opacity(
                        opacity: ImState.chatInstance.value == null || ImState.chatLoginInfo.value == null ? 0.1 : 1,
                        child: IgnorePointer(
                          ignoring: ImState.chatInstance.value == null || ImState.chatLoginInfo.value == null,
                          child: ValueListenableBuilder(
                            valueListenable: _uiTypeNotifier,
                            builder: (ctx, uiType, _) {
                              return Row(
                                children: [
                                  Expanded(
                                    child: GestureDetector(
                                      behavior: HitTestBehavior.opaque,
                                      onTap: () {
                                        _uiTypeNotifier.value = 'default';
                                      },
                                      child: Column(
                                        children: [
                                          Container(
                                            padding: EdgeInsets.all(4),
                                            width: double.infinity,
                                            decoration: BoxDecoration(
                                              color: uiType == 'default' ? Colors.blue : Colors.grey.shade400,
                                              borderRadius: BorderRadius.circular(2),
                                            ),
                                            child: Row(
                                              children: [
                                                Container(
                                                  width: 30,
                                                  height: 60,
                                                  decoration: BoxDecoration(
                                                    color: Colors.white,
                                                    borderRadius: BorderRadius.circular(2),
                                                  ),
                                                ),
                                                const SizedBox(width: 2,),
                                                Expanded(child: Container(
                                                  height: 60,
                                                  decoration: BoxDecoration(
                                                    color: Colors.white,
                                                    borderRadius: BorderRadius.circular(2),
                                                  ),
                                                  child: Stack(
                                                    children: [
                                                      Positioned(
                                                        top: 2,
                                                        left: 2,
                                                        right: 2,
                                                        child: Container(
                                                          height: 6,
                                                          color: Colors.grey.shade400,
                                                        ),
                                                      ),
                                                      Positioned(
                                                        bottom: 2,
                                                        left: 2,
                                                        right: 2,
                                                        child: Container(
                                                          height: 12,
                                                          decoration: BoxDecoration(
                                                            border: Border.all(width: 1.4, color: Colors.grey.shade300),
                                                            borderRadius: BorderRadius.circular(2),
                                                          ),
                                                        ),
                                                      ),
                                                    ],
                                                  ),
                                                ),),
                                              ],
                                            ),
                                          ),
                                          const SizedBox(height: 6,),
                                          Text('经典组合UI', style: TextStyle(fontSize: 12, color: Colors.white,),),
                                        ],
                                      ),
                                    ),
                                  ),
                                  const SizedBox(width: 10,),
                                  Expanded(
                                    child: GestureDetector(
                                      behavior: HitTestBehavior.opaque,
                                      onTap: () {
                                        _uiTypeNotifier.value = 'conversation-list';
                                      },
                                      child: Column(
                                        children: [
                                          Container(
                                            padding: EdgeInsets.all(4),
                                            width: double.infinity,
                                            decoration: BoxDecoration(
                                              color: uiType == 'conversation-list' ? Colors.blue : Colors.grey.shade400,
                                              borderRadius: BorderRadius.circular(2),
                                            ),
                                            child: Row(
                                              mainAxisAlignment: MainAxisAlignment.center,
                                              children: [
                                                Container(
                                                  width: 30,
                                                  height: 60,
                                                  decoration: BoxDecoration(
                                                    color: Colors.white,
                                                    borderRadius: BorderRadius.circular(2),
                                                  ),
                                                ),
                                              ],
                                            ),
                                          ),
                                          const SizedBox(height: 6,),
                                          Text('会话列表', style: TextStyle(fontSize: 12, color: Colors.white,),),
                                        ],
                                      ),
                                    ),
                                  ),
                                  const SizedBox(width: 10,),
                                  Expanded(
                                    child: GestureDetector(
                                      behavior: HitTestBehavior.opaque,
                                      onTap: () {
                                        _uiTypeNotifier.value = 'chat';
                                      },
                                      child: Column(
                                        children: [
                                          Container(
                                            padding: EdgeInsets.all(4),
                                            width: double.infinity,
                                            decoration: BoxDecoration(
                                              color: uiType == 'chat' ? Colors.blue : Colors.grey.shade400,
                                              borderRadius: BorderRadius.circular(2),
                                            ),
                                            child: Row(
                                              mainAxisAlignment: MainAxisAlignment.center,
                                              children: [
                                                Container(
                                                  width: 70,
                                                  height: 60,
                                                  decoration: BoxDecoration(
                                                    color: Colors.white,
                                                    borderRadius: BorderRadius.circular(2),
                                                  ),
                                                  child: Stack(
                                                    children: [
                                                      Positioned(
                                                        top: 2,
                                                        left: 2,
                                                        right: 2,
                                                        child: Container(
                                                          height: 6,
                                                          color: Colors.grey.shade400,
                                                        ),
                                                      ),
                                                      Positioned(
                                                        bottom: 2,
                                                        left: 2,
                                                        right: 2,
                                                        child: Container(
                                                          height: 12,
                                                          decoration: BoxDecoration(
                                                            border: Border.all(width: 1.4, color: Colors.grey.shade300),
                                                            borderRadius: BorderRadius.circular(2),
                                                          ),
                                                        ),
                                                      ),
                                                    ],
                                                  ),
                                                ),
                                              ],
                                            ),
                                          ),
                                          const SizedBox(height: 6,),
                                          Text('聊天窗口', style: TextStyle(fontSize: 12, color: Colors.white,),),
                                        ],
                                      ),
                                    ),
                                  ),
                                ],
                              );
                            },
                          ),
                        ),
                      ),

                      if (ImState.callInstance.value != null) const SizedBox(height: 25,),
                      if (ImState.callInstance.value != null) Text('拨打音视频通话', style: TextStyle(fontSize: 14, color: Colors.grey.withAlpha(160)),),
                      if (ImState.callInstance.value != null) const SizedBox(height: 8,),
                      if (ImState.callInstance.value != null) Opacity(
                        opacity: ImState.chatInstance.value == null || ImState.chatLoginInfo.value == null || ImState.callInstance.value == null ? 0.1 : 1,
                        child: IgnorePointer(
                          ignoring: ImState.chatInstance.value == null || ImState.chatLoginInfo.value == null || ImState.callInstance.value == null,
                          child: AnimatedBuilder(
                            animation: Listenable.merge([ImState.callTypeNotifier, ImState.callStateNotifier]),
                            builder: (c, _) {
                              return Container(
                                decoration: BoxDecoration(
                                  color: Colors.black87.withAlpha(80),
                                  borderRadius: BorderRadius.circular(8),
                                ),
                                padding: EdgeInsets.only(left: 8, right: 8, top: 6, bottom: 6,),
                                child: Column(
                                  children: [
                                    IntrinsicHeight(
                                      child: Row(
                                        crossAxisAlignment: CrossAxisAlignment.stretch,
                                        children: [
                                          Container(
                                            width: 50,
                                            decoration: BoxDecoration(
                                              color: Colors.black87.withAlpha(80),
                                              borderRadius: BorderRadius.circular(8).copyWith(topRight: Radius.zero, bottomRight: Radius.zero,),
                                            ),
                                            padding: EdgeInsets.only(left: 6),
                                            child: Align(
                                              alignment: Alignment.topCenter,
                                              child: Padding(
                                                padding: const EdgeInsets.only(top: 10),
                                                child: Icon(Icons.supervised_user_circle_sharp, color: Colors.grey.withAlpha(60),),
                                              ),
                                            ),
                                          ),
                                          Expanded(child: ConstrainedBox(
                                            constraints: const BoxConstraints(
                                              minHeight: 40,
                                              maxHeight: 120,
                                            ),
                                            child: IgnorePointer(
                                              ignoring: ImState.callStateNotifier.value == CallState.inviting,
                                              child: TextField(
                                                controller: imCallUserIdController,
                                                style: TextStyle(fontSize: 14, color: Colors.white,),
                                                decoration: InputDecoration(
                                                  hintText: '对方用户的 userId(多个用 , 分割)',
                                                  hintStyle: TextStyle(fontSize: 14, color: Colors.white.withAlpha(30),),
                                                  filled: true,
                                                  fillColor: Colors.black87.withAlpha(100),
                                                  border: OutlineInputBorder(
                                                    borderRadius: BorderRadius.circular(8).copyWith(topLeft: Radius.zero, bottomLeft: Radius.zero,),
                                                    borderSide: BorderSide.none,
                                                  ),
                                                  contentPadding: const EdgeInsets.symmetric(vertical: 13, horizontal: 10),
                                                ),
                                                keyboardType: TextInputType.number,
                                                autofocus: true,
                                                maxLines: 5,
                                                minLines: 2,
                                              ),
                                            ),
                                          ),),
                                        ],
                                      ),
                                    ),
                                    const SizedBox(height: 14,),
                                    Opacity(
                                      opacity: ImState.callTypeNotifier.value == CallType.video && ImState.callStateNotifier.value == CallState.inviting ? 0.2 : 1,
                                      child: IgnorePointer(
                                        ignoring: ImState.callTypeNotifier.value == CallType.video && ImState.callStateNotifier.value == CallState.inviting,
                                        child: SizedBox(
                                          width: MediaQuery.of(context).size.width,
                                          height: 40,
                                          child: ElevatedButton(
                                            style: ButtonStyle(
                                              elevation: WidgetStateProperty.all(0),
                                              shadowColor: WidgetStateProperty.all(Colors.transparent),
                                              backgroundColor: WidgetStateProperty.all(Colors.lightBlue.shade700),
                                              foregroundColor: WidgetStateProperty.all(Colors.white),
                                              shape: WidgetStateProperty.all(
                                                RoundedRectangleBorder(
                                                  borderRadius: BorderRadius.circular(8),
                                                ),
                                              ),
                                            ),
                                            onPressed: () async {
                                              if (imCallUserIdController.text.trim() == '') {
                                                ScaffoldMessenger.of(ctx).showSnackBar(SnackBar(
                                                  content: const Text('请输入对方用户的 userId'),
                                                  duration: Duration(milliseconds: 800),
                                                ));
                                                return;
                                              }
                                              if (imCallUserIdController.text.trim().contains(',')) {
                                                ScaffoldMessenger.of(ctx).showSnackBar(SnackBar(
                                                  content: const Text('多个拨打对象直接请用英文逗号,不要使用中文逗号'),
                                                  duration: Duration(milliseconds: 800),
                                                ));
                                                return;
                                              }
                                              if (imCallUserIdController.text.trim().contains(imLoginUserIdController.text.trim())) {
                                                ScaffoldMessenger.of(ctx).showSnackBar(SnackBar(
                                                  content: const Text('音频拨打对象中不能包含自身用户的 userId'),
                                                  duration: Duration(milliseconds: 800),
                                                ));
                                                return;
                                              }

                                              try {
                                                await ImPluginTencentWeb.call(ctx, callType: CallType.audio, userIdList: imCallUserIdController.text.trim().split(','));
                                              } catch(err) {
                                                if (kDebugMode) {
                                                  print('拨打语音通话错误:$err');
                                                }
                                                if (ctx.mounted) {
                                                  ScaffoldMessenger.of(ctx).showSnackBar(SnackBar(
                                                    content: Text('拨打语音通话出错:$err'),
                                                    duration: Duration(milliseconds: 800),
                                                  ));
                                                }
                                              }
                                            },
                                            child: Row(
                                              mainAxisSize: MainAxisSize.min,
                                              children: [
                                                Icon(Icons.call, size: 16, color: Colors.white,),
                                                const SizedBox(width: 8,),
                                                Text(ImState.callTypeNotifier.value == CallType.audio && ImState.callStateNotifier.value == CallState.inviting ? '等待接听' : '发起语音通话'),
                                              ],
                                            ),
                                          ),
                                        ),
                                      ),
                                    ),
                                    const SizedBox(height: 4,),
                                    Opacity(
                                      opacity: ImState.callTypeNotifier.value == CallType.audio && ImState.callStateNotifier.value == CallState.inviting ? 0.2 : 1,
                                      child: IgnorePointer(
                                        ignoring: ImState.callTypeNotifier.value == CallType.audio && ImState.callStateNotifier.value == CallState.inviting,
                                        child: SizedBox(
                                          width: MediaQuery.of(context).size.width,
                                          height: 40,
                                          child: ElevatedButton(
                                            style: ButtonStyle(
                                              elevation: WidgetStateProperty.all(0),
                                              shadowColor: WidgetStateProperty.all(Colors.transparent),
                                              backgroundColor: WidgetStateProperty.all(Colors.indigoAccent),
                                              foregroundColor: WidgetStateProperty.all(Colors.white),
                                              shape: WidgetStateProperty.all(
                                                RoundedRectangleBorder(
                                                  borderRadius: BorderRadius.circular(8),
                                                ),
                                              ),
                                            ),
                                            onPressed: () async {
                                              if (imCallUserIdController.text.trim() == '') {
                                                ScaffoldMessenger.of(ctx).showSnackBar(SnackBar(
                                                  content: const Text('请输入对方用户的 userId'),
                                                  duration: Duration(milliseconds: 800),
                                                ));
                                                return;
                                              }
                                              if (imCallUserIdController.text.trim().contains(',')) {
                                                ScaffoldMessenger.of(ctx).showSnackBar(SnackBar(
                                                  content: const Text('多个拨打对象直接请用英文逗号,不要使用中文逗号'),
                                                  duration: Duration(milliseconds: 800),
                                                ));
                                                return;
                                              }
                                              if (imCallUserIdController.text.trim().contains(imLoginUserIdController.text.trim())) {
                                                ScaffoldMessenger.of(ctx).showSnackBar(SnackBar(
                                                  content: const Text('视频拨打对象中不能包含自身用户的 userId'),
                                                  duration: Duration(milliseconds: 800),
                                                ));
                                                return;
                                              }

                                              try {
                                                await ImPluginTencentWeb.call(ctx, callType: CallType.video, userIdList: imCallUserIdController.text.trim().split(','));
                                              } catch(err) {
                                                if (kDebugMode) {
                                                  print('拨打视频通话错误:$err');
                                                }
                                                if (ctx.mounted) {
                                                  ScaffoldMessenger.of(ctx).showSnackBar(SnackBar(
                                                    content: Text('拨打视频通话出错:$err'),
                                                    duration: Duration(milliseconds: 800),
                                                  ));
                                                }
                                              }
                                            },
                                            child: Row(
                                              mainAxisSize: MainAxisSize.min,
                                              children: [
                                                Icon(Icons.video_camera_front_sharp, size: 16, color: Colors.white,),
                                                const SizedBox(width: 8,),
                                                Text(ImState.callTypeNotifier.value == CallType.video && ImState.callStateNotifier.value == CallState.inviting ? '等待接听' : '发起视频通话'),
                                              ],
                                            ),
                                          ),
                                        ),
                                      ),
                                    ),
                                  ],
                                ),
                              );
                            },
                          ),
                        ),
                      ),
                    ],
                  );
                },
              ),
            ),
          );
        }),
        backgroundColor: Colors.black87,
        body: Builder(builder: (ctx) {
          return ValueListenableBuilder(
            valueListenable: _uiTypeNotifier,
            builder: (ctx, uiType, _) {
              double minWidth = 1000;
              double clientWidth = MediaQuery.of(context).size.width * 0.9;
              double conversationListWidth = 280;
              if (uiType == 'default') {
                minWidth = 1000;
                clientWidth = MediaQuery.of(context).size.width * 0.9;
                conversationListWidth = 280;
              }
              if (uiType == 'conversation-list') {
                minWidth = 340;
                clientWidth = 340;
                conversationListWidth = 340;
              }
              if (uiType == 'chat') {
                minWidth = 1000;
                clientWidth = 1000;
              }
              return Center(
                child: OverflowBox(
                  minWidth: minWidth,
                  maxWidth: double.infinity,
                  minHeight: 600,
                  maxHeight: double.infinity,
                  child: ValueListenableBuilder(
                    valueListenable: ImState.chatLoginInfo,
                    builder: (ctx, chatLoginInfo, _) {
                      if (chatLoginInfo == null) {
                        return Container(
                          width: MediaQuery.of(context).size.width * 0.9,
                          height: MediaQuery.of(context).size.height * 0.88,
                          alignment: Alignment(0, -0.26),
                          child: Column(
                            mainAxisSize: MainAxisSize.min,
                            children: [
                              Icon(Icons.message, size: 60, color: Colors.grey.shade600,),
                              const SizedBox(height: 10,),
                              Text('等待 IM 登录', style: TextStyle(fontSize: 13, color: Colors.grey.shade600,),),
                              Text('点击右上角的 ⚙ 进行登录', style: TextStyle(fontSize: 12, color: Colors.grey.shade600,),),
                            ],
                          ),
                        );
                      }
                      return Container(
                        width: clientWidth,
                        height: MediaQuery.of(context).size.height * 0.88,
                        clipBehavior: Clip.antiAlias,
                        decoration: BoxDecoration(
                          color: Colors.white,
                          borderRadius: BorderRadius.circular(10),
                          boxShadow: [
                            BoxShadow(
                              color: Colors.black,
                              blurRadius: 10,
                            ),
                          ],
                        ),
                        child: Row(
                          children: [
                            if (uiType != 'chat') SizedBox(
                              width: conversationListWidth,
                              child: ConversationListWidget(),
                            ),
                            if (uiType == 'default') VerticalDivider(
                              width: 0.1,
                              color: Colors.black87.withAlpha(20),
                            ),
                            if (uiType == 'default') Expanded(
                              child: ChatWidget(),
                            ),
                            if (uiType == 'chat') SizedBox(
                              width: clientWidth,
                              child: ChatWidget(),
                            ),
                          ],
                        ),
                      );
                    },
                  ),
                ),
              );
            },
          );
        }),
      ),
    );
  }
}
1
likes
150
points
401
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A Flutter plugin integrating Tencent IM for web, supporting chat and audio/video calls. Uses UI-free access method, providing ready-to-use APIs. Web-only integration.

Repository

License

MIT (license)

Dependencies

flutter, flutter_web_plugins, meta, plugin_platform_interface, scrollview_observer, web

More

Packages that depend on im_plugin_tencent_web

Packages that implement im_plugin_tencent_web