设备横竖屏切换时如何调用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
相关产品推荐
相关产品推荐

