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

Flutter中如何在两个相邻Widget间渲染可拖动的中间Widget?

解决方案

一、正确获取Widget构建后尺寸的规范方法

你之前用WidgetsBinding.instance.addPostFrameCallback()获取高度为0,大概率是因为RenderBox还未完成布局绑定。以下两种方案更符合Flutter设计理念,无需依赖延迟:

1. GlobalKey + addPostFrameCallback 精准获取真实尺寸

确保将GlobalKey绑定到目标Widget,在当前帧布局完成后读取尺寸:

final GlobalKey _upperBoxKey = GlobalKey();
double _upperBoxHeight = 0;

@override
void initState() {
  super.initState();
  WidgetsBinding.instance.addPostFrameCallback((_) {
    _getUpperBoxSize();
  });
}

void _getUpperBoxSize() {
  final RenderBox renderBox = _upperBoxKey.currentContext!.findRenderObject() as RenderBox;
  setState(() {
    _upperBoxHeight = renderBox.size.height;
  });
}

// 线缆盒上部Widget绑定key
Widget _buildUpperBox() {
  return Container(
    key: _upperBoxKey,
    color: Colors.grey[300],
    child: Text("线缆盒上部"), // 自适应高度场景也能正确获取
  );
}

2. LayoutBuilder 动态读取父容器约束

如果线缆盒上部的高度由父容器约束决定,可直接通过LayoutBuilder拿到实时约束:

Widget _buildUpperBox() {
  return LayoutBuilder(
    builder: (context, constraints) {
      setState(() {
        _upperBoxHeight = constraints.maxHeight;
      });
      return Container(
        height: constraints.maxHeight,
        color: Colors.grey[300],
        child: Text("线缆盒上部"),
      );
    },
  );
}

二、结合Stack+Positioned的布局实现方案

核心思路是通过Stack控制层级,让音频插孔穿插在上下线缆盒之间,同时用Positioned将下部精准定位到上部底部:

class AudioJackDemo extends StatefulWidget {
  @override
  _AudioJackDemoState createState() => _AudioJackDemoState();
}

class _AudioJackDemoState extends State<AudioJackDemo> {
  final GlobalKey _upperBoxKey = GlobalKey();
  double _upperBoxHeight = 0;
  Offset _jackOffset = Offset(0, 0); // 音频插孔位置

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _initJackPosition();
    });
  }

  void _initJackPosition() {
    final RenderBox renderBox = _upperBoxKey.currentContext!.findRenderObject() as RenderBox;
    setState(() {
      _upperBoxHeight = renderBox.size.height;
      // 初始位置让插孔对齐上部底部
      _jackOffset = Offset(
        MediaQuery.of(context).size.width / 2 - 20,
        _upperBoxHeight - 20,
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          // 1. 底层:线缆盒上部
          Container(
            key: _upperBoxKey,
            height: 150, // 自适应场景可移除,由内部内容决定高度
            color: Colors.grey[300],
            alignment: Alignment.center,
            child: Text("线缆盒上部"),
          ),
          // 2. 中层:可拖动音频插孔(穿插在上下盒之间)
          Positioned(
            left: _jackOffset.dx,
            top: _jackOffset.dy,
            child: GestureDetector(
              onPanUpdate: (details) {
                setState(() {
                  _jackOffset += details.delta;
                  // 限制拖动范围,模拟插入逻辑
                  _jackOffset = Offset(
                    _jackOffset.dx.clamp(20, MediaQuery.of(context).size.width - 40),
                    _jackOffset.dy.clamp(_upperBoxHeight - 40, _upperBoxHeight + 80),
                  );
                });
              },
              child: Container(
                width: 40,
                height: 80,
                color: Colors.black,
                child: Align(
                  alignment: Alignment.bottomCenter,
                  child: Container(width: 20, height: 60, color: Colors.grey[700]),
                ),
              ),
            ),
          ),
          // 3. 顶层:线缆盒下部(紧贴上部)
          if (_upperBoxHeight > 0)
            Positioned(
              top: _upperBoxHeight,
              left: 0,
              right: 0,
              height: 150,
              child: Container(
                color: Colors.grey[400],
                alignment: Alignment.center,
                child: Text("线缆盒下部"),
              ),
            ),
        ],
      ),
    );
  }
}

关键说明

  • 层级控制:Stack子Widget的顺序即层级顺序,上部在底层、插孔在中间、下部在顶层,拖动时插孔会被下部遮挡,完美模拟插入效果。
  • 拖动逻辑:用GestureDetector.onPanUpdate实现自定义拖动,比Draggable更灵活,完全可控位置边界。
  • 尺寸稳定性:通过GlobalKey或LayoutBuilder获取的尺寸是布局完成后的真实值,避免了Future.delayed的不可靠性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 23:23:09