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

设备横竖屏切换时如何调用setCanvasPositions函数定位容器(不重建Widget)

解决方案

要在设备横竖屏切换时调用setCanvasPositions函数计算容器位置,同时避免不必要的Widget重建,你可以通过监听窗口尺寸变化事件来实现,具体修改如下:

方法一:混入WidgetsBindingObserver监听

class _CanvasState extends State<Canvas> with WidgetsBindingObserver {
  final GlobalKey canvasKey = GlobalKey();
  Offset? canvasPosition;
  late Size _containerSize;

  @override
  void initState() {
    super.initState();
    // 注册监听
    WidgetsBinding.instance.addObserver(this);
    // 初始化时计算尺寸和位置
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _updateContainerSize();
      setCanvasPositions(_);
    });
  }

  // 更新容器尺寸
  void _updateContainerSize() {
    final mediaQuery = MediaQuery.of(context);
    _containerSize = Size(
      mediaQuery.size.width - 10,
      mediaQuery.size.height - 30,
    );
  }

  void setCanvasPositions(_) {
    RenderBox? box = canvasKey.currentContext?.findRenderObject() as RenderBox?;
    setState(() {
      canvasPosition = box?.localToGlobal(Offset.zero);
    });
  }

  // 监听窗口尺寸变化(横竖屏切换会触发)
  @override
  void didChangeMetrics() {
    super.didChangeMetrics();
    // 等待布局完成后执行,确保能获取最新的RenderBox
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _updateContainerSize();
      setCanvasPositions(_);
    });
  }

  @override
  void dispose() {
    // 移除监听,防止内存泄漏
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      key: canvasKey,
      height: _containerSize.height,
      width: _containerSize.width,
      decoration: BoxDecoration(
        color: Colors.grey,
        border: Border.all(color: Colors.blueGrey),
      ),
      child: Stack(
        children: [
          StreamElementOnCanvas(canvasPosition, context)
        ],
      ),
    );
  }
}

方法二:直接添加窗口resize监听

如果不想混入WidgetsBindingObserver,也可以直接注册窗口尺寸变化监听:

class _CanvasState extends State<Canvas> {
  final GlobalKey canvasKey = GlobalKey();
  Offset? canvasPosition;
  late Size _containerSize;
  late VoidCallback _resizeListener;

  @override
  void initState() {
    super.initState();
    // 初始化时计算尺寸和位置
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _updateContainerSize();
      setCanvasPositions(_);
    });
    // 定义resize回调
    _resizeListener = () {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        _updateContainerSize();
        setCanvasPositions(_);
      });
    };
    // 添加监听
    WidgetsBinding.instance.addWindowResizeListener(_resizeListener);
  }

  void _updateContainerSize() {
    final mediaQuery = MediaQuery.of(context);
    _containerSize = Size(
      mediaQuery.size.width - 10,
      mediaQuery.size.height - 30,
    );
  }

  void setCanvasPositions(_) {
    RenderBox? box = canvasKey.currentContext?.findRenderObject() as RenderBox?;
    setState(() {
      canvasPosition = box?.localToGlobal(Offset.zero);
    });
  }

  @override
  void dispose() {
    // 移除监听
    WidgetsBinding.instance.removeWindowResizeListener(_resizeListener);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      key: canvasKey,
      height: _containerSize.height,
      width: _containerSize.width,
      decoration: BoxDecoration(
        color: Colors.grey,
        border: Border.all(color: Colors.blueGrey),
      ),
      child: Stack(
        children: [
          StreamElementOnCanvas(canvasPosition, context)
        ],
      ),
    );
  }
}

核心说明

  • 横竖屏切换会触发窗口尺寸变化,通过监听该事件来触发setCanvasPositions调用。
  • 使用addPostFrameCallback确保在布局完成后执行位置计算,避免获取到旧的RenderBox状态。
  • 缓存容器尺寸到_containerSize,避免build方法直接依赖MediaQuery,减少不必要的重建触发(仅在更新canvasPosition时触发setState)。
  • 记得在dispose中移除监听,防止内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 03:35:23