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

Flutter中ConnectManagerContent组件为何被重复实例化?

解决Flutter中ConnectManagerContent组件重复实例化的问题

问题分析

从打印日志可以看到,ConnectManagerContent的initState(对应打印3、4)被执行了两次,导致定时器重复创建,网络恢复时触发两次API调用。核心原因有两点:

  1. Flutter框架在SnackBar的动画渲染过程中,可能会重新构建content组件;未指定唯一Key时,Flutter无法复用原有组件实例,会创建新的StatefulWidget实例。
  2. 未在组件销毁时取消定时器,旧实例的定时器仍在运行,叠加新实例的定时器导致重复回调。

解决方案

1. 给ConnectManagerContent添加唯一Key

在创建ConnectManagerContent时指定唯一Key,让Flutter识别这是同一个组件,避免重复实例化。

修改ConnectManager中showConnectManager方法的SnackBar内容部分:

content: ConnectManagerContent(
  key: ValueKey(id), // 添加唯一Key
  id: id,
  message: dioInternetConnectionRequired,
  onConnect: onConnect,
),

同时修改ConnectManagerContent的构造函数,移除currentContext参数(改用组件自身context):

const ConnectManagerContent({
  super.key,
  required this.message,
  required this.onConnect,
  this.id,
});

2. 在组件销毁时取消定时器

在_ConnectManagerContentState中重写dispose方法,确保定时器被及时取消,避免内存泄漏和重复回调:

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

3. 优化定时器逻辑与context使用

将checkInternetConnection的回调中使用组件自身的context,替换原来的外部传递的currentContext:

if (value) {
  timer.cancel();
  widget.onConnect();
  ScaffoldMessenger.of(context).hideCurrentSnackBar(); // 使用自身context
}

4. 确保弹窗只被调用一次

在调用方的Widget中添加状态标记,避免因Bloc状态重复触发导致多次调用showConnectManager:

// 在调用方的State类中添加变量
bool _isConnectSnackBarVisible = false;

// 触发逻辑中添加判断
if (isCennectionError(state.error) && !_isConnectSnackBarVisible) {
  _isConnectSnackBarVisible = true;
  print("1 SETUP ========");

  ConnectManager(
    id: "1234",
    context: context,
    connectBloc: connectionBloc,
    onVisible: handleErrorVisible,
    onConnect: () {
      context.read<MediasBloc>().add(
            UpdateMedia(
              id: widget.media.id!,
              className: className,
              data: {
                "title": mediaTitleFieldController.text,
                "description": mediaDescFieldController.text
              },
            ),
          );
      handleErrorDismiss();
      _isConnectSnackBarVisible = false; // 重置标记
    },
  ).showConnectManager();

  return;
}

修改后的完整ConnectManagerContent代码

class ConnectManagerContent extends StatefulWidget {
  final String message;
  final Function onConnect;
  final String? id;

  const ConnectManagerContent({
    super.key,
    required this.message,
    required this.onConnect,
    this.id,
  });

  @override
  State<ConnectManagerContent> createState() => _ConnectManagerContentState();
}

class _ConnectManagerContentState extends State<ConnectManagerContent> {
  final Debouncer debouncer = Debouncer();
  bool connected = false;
  late final Timer timer;

  @override
  void initState() {
    super.initState();
    print("3 SETUP ========");
    print(widget.id);
    print("3 SETUP ========");
    init();
  }

  Future checkInternetConnection() async {
    try {
      final result = await InternetAddress.lookup("google.com");
      if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
        return true;
      }
    } on SocketException catch (_) {
      return false;
    }
    return false;
  }

  void init() {
    print("4 SETUP ========");
    timer = Timer.periodic(
      const Duration(seconds: 3),
      (timer) {
        checkInternetConnection().then(
          (value) {
            if (value) {
              timer.cancel();
              widget.onConnect();
              ScaffoldMessenger.of(context).hideCurrentSnackBar();
            }
          },
        );
      },
    );
  }

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

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Expanded(
              child: Text(
                widget.message,
                textAlign: TextAlign.center,
                style: const TextStyle(
                  height: 1.3,
                ),
              ),
            ),
          ],
        ),
        const SizedBox(height: 20),
        const Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [Text("Checking...")],
        ),
      ],
    );
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 02:10:42