You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React组件卸载时如何销毁内嵌的Flutter Web应用?

解决React组件卸载时Flutter Web应用未销毁的问题

你的核心问题是仅移除DOM元素和清空变量引用,没有调用Flutter Web引擎的销毁API,导致Flutter的隔离环境、事件监听和后台逻辑仍在运行。以下是具体修复方案:

关键问题分析

当前清理逻辑只做了:

  • 移除DOM元素
  • 清空flutterState.current、engineInitializer、appRunner变量
    但Flutter Web引擎实例仍在浏览器后台运行,包括:
  • Dart虚拟机的隔离环境未终止
  • Flutter与React的通信监听未移除
  • 引擎内部的定时器、状态更新逻辑仍在执行

修复步骤

1. 拆分引用存储

将flutterState拆分为两个ref,分别存储Flutter引擎实例和通信状态对象,避免混淆:

const flutterEngineRef = useRef<any>(null); // 存appRunner
const flutterCommStateRef = useRef<any>(null); // 存CustomEvent传递的state

2. 在清理函数中调用引擎销毁API

Flutter Web的appRunner和engineInitializer提供了dispose()方法,用于销毁引擎实例:

// 清理函数中添加
if (flutterEngineRef.current) {
  // 销毁Flutter应用实例
  await flutterEngineRef.current.dispose();
  flutterEngineRef.current = null;
}

if (engineInitializer) {
  // 销毁引擎初始化器
  engineInitializer.dispose();
  engineInitializer = null;
}

3. 移除Flutter到React的状态监听

你的代码中调用了state.listenToFlutterState,需要在清理时移除该监听(假设Flutter端提供了移除方法,比如removeStateListener):

if (flutterCommStateRef.current) {
  flutterCommStateRef.current.removeStateListener();
  flutterCommStateRef.current = null;
}

如果Flutter端没提供移除方法,需要在Dart代码中实现:

// Flutter端示例:添加移除监听的方法
void removeStateListener() {
  _stateStreamController.close();
}

4. 彻底清理DOM和关联资源

除了移除宿主元素,还要清理Flutter注入的全局资源:

// 移除Flutter生成的DOM节点
if (target) {
  target.innerHTML = '';
  if (target.parentNode) {
    target.parentNode.removeChild(target);
  }
}

// 清理全局_flutter相关的临时资源(可选,视情况而定)
if (window._flutter?.loader) {
  // 若有未完成的加载任务,取消它
  window._flutter.loader.cancelLoading?.();
}

修改后的完整React组件清理逻辑

export const FlutterEmbedComponent: React.FC<> = memo(({
  assetBase = '',
  src = 'main.dart.js',
}) => {
  const flutterEngineRef = useRef<any>(null);
  const flutterCommStateRef = useRef<any>(null);
  const ref = useRef<HTMLDivElement>(null);

  const onFlutterAppLoaded = (state: any) => {
    flutterCommStateRef.current = state;

    // Update Flutter state once when loaded
    state.stateone(stateone);
    state.statetwo(statetwo);
    
    state.updateEnv(ENV);
    state.listenToFlutterState(state => {
      console.log(">>> state from flutter: ", state);
    });
  };

  useEffect(() => {
    const target = ref.current;
    let engineInitializer;
  
    const initFlutterApp = async () => {
      engineInitializer = await new Promise<any>((resolve) => {
        _flutter.loader.loadEntrypoint({
          entrypointUrl: src,
          onEntrypointLoaded: resolve,
        });
      });
  
      console.log('initialize Flutter engine...');
      const appRunner = await engineInitializer?.initializeEngine({
        hostElement: target,
        assetBase: assetBase,
      });
  
      console.log('run Flutter engine...');
      await appRunner?.runApp();
      flutterEngineRef.current = appRunner;
    };
  
    initFlutterApp();
  
    const eventListener = (event: Event) => {
      let state = (event as CustomEvent).detail;
      console.log('Flutter app initialized:', state);
      onFlutterAppLoaded(state);
    };
  
    target?.addEventListener('flutter-initialized', eventListener, { once: true });
  
    return async () => {
      // 移除事件监听
      target?.removeEventListener('flutter-initialized', eventListener);

      // 移除Flutter到React的状态监听
      if (flutterCommStateRef.current) {
        flutterCommStateRef.current.removeStateListener?.();
        flutterCommStateRef.current = null;
      }

      // 销毁Flutter引擎实例
      if (flutterEngineRef.current) {
        try {
          await flutterEngineRef.current.dispose();
        } catch (e) {
          console.error('Failed to dispose Flutter engine:', e);
        }
        flutterEngineRef.current = null;
      }

      // 销毁引擎初始化器
      if (engineInitializer) {
        try {
          engineInitializer.dispose();
        } catch (e) {
          console.error('Failed to dispose engine initializer:', e);
        }
        engineInitializer = null;
      }

      // 清理DOM
      if (target) {
        target.innerHTML = '';
        if (target.parentNode) {
          target.parentNode.removeChild(target);
        }
      }
    };
  }, [assetBase, src]);

  // ... 其他useEffect和渲染逻辑不变

  return (
    <div
      ref={ref}
      style={{
        height: '100%',
        width: '100%',
        display: showFlutterApp ? 'flex' : 'none',
        zIndex: 99,
        position: activePage === 2 ? 'fixed' : 'static',
        top: activePage === 2 ? 50 : 'auto',
        right: activePage === 2 ? 0 : 'auto',
        flex: 1
      }}
    ></div>
  );
});

额外注意事项

  • Flutter端配合:确保Dart代码中所有后台任务(定时器、流、WebSocket等)在引擎销毁时能自动终止,或者提供手动关闭的方法。
  • Service Worker清理:如果你的Flutter应用注册了Service Worker,若不需要全局保留,可以在清理时注销:
    if ('serviceWorker' in navigator) {
      const registrations = await navigator.serviceWorker.getRegistrations();
      for (let reg of registrations) {
        if (reg.scope.includes('/flutter/')) { // 匹配你的Flutter资源路径
          await reg.unregister();
        }
      }
    }
    
  • 内存泄漏检测:可以用Chrome DevTools的Memory面板,对比组件卸载前后的内存占用,确认是否还有残留的Flutter相关对象。

内容的提问来源于stack exchange,提问作者user321553125

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 00:35:56