tencent_cloud_whiteboard 0.0.3 copy "tencent_cloud_whiteboard: ^0.0.3" to clipboard
tencent_cloud_whiteboard: ^0.0.3 copied to clipboard

Tencent Cloud Whiteboard for flutter

Tencent Cloud Whiteboard Flutter Plugin #

腾讯云白板 Flutter 插件,提供完整的白板功能。

功能特性 #

  • ✅ 完整的白板绘制功能
  • ✅ 多种工具支持(画笔、橡皮擦、激光笔等)
  • ✅ 实时同步
  • ✅ 文件上传和管理
  • ✅ 状态保持优化(组件尺寸改变时不会重新build)
  • ✅ Resize Loading覆盖层(尺寸改变时显示loading)
  • ✅ 高性能渲染

状态保持优化 #

问题描述 #

在之前的版本中,当 TencentCloudWhiteboard 组件的尺寸发生变化时(如父容器大小改变、屏幕旋转等),整个组件会重新 build,导致:

  1. WebView 重新创建
  2. 白板状态丢失(绘制内容、工具选择等)
  3. 性能下降

解决方案 #

我们通过以下技术手段解决了这个问题:

1. AutomaticKeepAliveClientMixin

class _TencentCloudWhiteboardState extends State<TencentCloudWhiteboard> 
    with AutomaticKeepAliveClientMixin {
  
  @override
  bool get wantKeepAlive => true; // 保持组件状态
  
  @override
  Widget build(BuildContext context) {
    super.build(context); // 必须调用super.build
    // ... 构建逻辑
  }
}

2. RepaintBoundary

使用 RepaintBoundary 包装 WebView,避免不必要的重绘:

return RepaintBoundary(
  child: InAppWebView(
    // ... WebView 配置
  ),
);

3. GlobalKey

使用 GlobalKey 来保持 WebView 的引用:

final GlobalKey _webViewKey = GlobalKey();

// 在 InAppWebView 中使用
InAppWebView(
  key: _webViewKey,
  // ... 其他配置
)

4. 状态管理优化

// 添加状态保持相关变量
InAppWebViewController? _webViewController;
TencentCloudWhiteboardController? _boardController;
bool _isWebViewInitialized = false;

// 在 onLoadStop 中只初始化一次
onLoadStop: (controller, url) {
  if (!_isWebViewInitialized) {
    _boardController = TencentCloudWhiteboardController(controller);
    _isWebViewInitialized = true;
    // ... 其他初始化逻辑
  }
},

Resize Loading覆盖层 #

功能描述 #

当 WebView 容器尺寸发生变化时,会在组件上覆盖一层loading层,提供更好的用户体验:

  1. 用户知道系统正在处理尺寸变化
  2. 避免尺寸变化过程中的视觉混乱
  3. 保持WebView状态不变
  4. 提供清晰的视觉反馈

技术实现 #

1. 尺寸监听

使用 LayoutBuilder 监听容器尺寸变化:

return LayoutBuilder(
  builder: (context, constraints) {
    // 处理尺寸变化
    _handleSizeChange(Size(constraints.maxWidth, constraints.maxHeight));
    
    return Stack(
      children: [
        // WebView层
        InAppWebView(...),
        // Loading覆盖层
        if (_isResizing) LoadingOverlay(),
      ],
    );
  },
);

2. Loading状态管理

// 尺寸变化相关变量
bool _isResizing = false;
Timer? _resizeTimer;
static const Duration _resizeDelay = Duration(milliseconds: 300);

void _handleSizeChange(Size newSize) {
  // 显示resize loading
  setState(() {
    _isResizing = true;
  });
  
  // 取消之前的定时器
  _resizeTimer?.cancel();
  
  // 设置新的定时器,在resize完成后隐藏loading
  _resizeTimer = Timer(_resizeDelay, () {
    if (mounted) {
      setState(() {
        _isResizing = false;
      });
    }
  });
}

3. Loading覆盖层

// Resize Loading覆盖层
if (_isResizing)
  Positioned.fill(
    child: Container(
      color: Colors.black.withOpacity(0.3),
      child: const Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            CircularProgressIndicator(
              valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
            ),
            SizedBox(height: 16),
            Text(
              '调整尺寸中...',
              style: TextStyle(
                color: Colors.white,
                fontSize: 16,
                fontWeight: FontWeight.w500,
              ),
            ),
          ],
        ),
      ),
    ),
  ),

使用示例 #

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  TencentCloudWhiteboardController? _boardController;
  double _boardHeight = 600.0;
  double _boardWidth = 800.0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Container(
          constraints: BoxConstraints(
            maxWidth: _boardWidth,
            maxHeight: _boardHeight,
          ),
          child: TencentCloudWhiteboard(
            onBoardCreated: (controller) {
              setState(() {
                _boardController = controller;
              });
            },
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            // 改变尺寸,会显示loading覆盖层
            _boardHeight = _boardHeight == 600.0 ? 800.0 : 600.0;
            _boardWidth = _boardWidth == 800.0 ? 1200.0 : 800.0;
          });
        },
        child: Icon(Icons.aspect_ratio),
      ),
    );
  }
}

优化效果 #

经过优化后,当组件尺寸改变时:

  1. ✅ WebView 不会重新创建
  2. ✅ 白板状态完全保持
  3. ✅ 绘制内容不会丢失
  4. ✅ 工具选择状态保持
  5. ✅ 显示清晰的loading反馈
  6. ✅ 性能显著提升

安装 #

pubspec.yaml 中添加依赖:

dependencies:
  tencent_cloud_whiteboard: ^latest_version

基本使用 #

import 'package:tencent_cloud_whiteboard/tencent_cloud_whiteboard.dart';

TencentCloudWhiteboard(
  onBoardCreated: (controller) {
    // 白板创建完成后的回调
    controller.initBoard({
      "classId": 123456,
      "sdkAppId": 1400313729,
      "userId": "user123",
      "userSig": "your_user_sig",
    });
  },
  onBoardLog: (message) {
    // 处理白板日志
    print('[TencentCloudWhiteboard] $message');
  },
)

日志功能 #

onBoardLog回调 #

onBoardLog回调用于接收白板组件的内部日志信息,包括:

  • WebView生命周期事件
  • 尺寸变化信息
  • 错误信息
  • 控制台消息
  • HTTP错误
TencentCloudWhiteboard(
  onBoardLog: (message) {
    // 自定义日志处理
    print('[TencentCloudWhiteboard] $message');
    // 或者发送到日志服务
    // Logger.log(message);
  },
)

日志示例 #

[TencentCloudWhiteboard] localhostServer.port: 8081 cost: 150
[TencentCloudWhiteboard] build whiteboard start 1703123456789
[TencentCloudWhiteboard] onWebViewCreated 1703123456790
[TencentCloudWhiteboard] onLoadStart http://localhost:8081/packages/tencent_cloud_whiteboard/assets/index.html 1703123456791
[TencentCloudWhiteboard] onLoadStop 1703123456890 load time: 100
[TencentCloudWhiteboard] Initial content size set: Size(800.0, 600.0)
[TencentCloudWhiteboard] Content resize started: Size(800.0, 600.0) -> Size(1200.0, 800.0)
[TencentCloudWhiteboard] Content resize completed

工具面板 #

TencentCloudWhiteboardTools(
  whiteboardController: controller,
  toolsController: TencentCloudWhiteboardToolsController(),
)

许可证 #

MIT License