Flutter绘图应用添加AppBar后绘制线条偏移问题求助
问题:添加AppBar后绘图位置与触摸位置偏移
我正在使用Flutter开发一款绘图应用。当Scaffold仅设置home时,可以正常绘制线条,但添加AppBar后,绘制的线条会从顶部向下偏移,偏移距离与AppBar的高度一致,实际绘制位置与屏幕触摸位置不符。尝试过调整padding、将Container放入SizedBox等方法,但问题仍未解决。
相关代码
class HiraganaWriting extends StatefulWidget { const HiraganaWriting({Key? key}) : super(key: key); @override State<HiraganaWriting> createState() => _HiraganaWritingState(); } class _HiraganaWritingState extends State<HiraganaWriting> { final GlobalKey _globalKey = GlobalKey(); List<DrawnLine> lines = <DrawnLine>[]; DrawnLine line = DrawnLine([Offset(50, 50)], Colors.black, 5.0); Color selectedColor = Colors.black; double selectedWidth = 5.0; StreamController<List<DrawnLine>> linesStreamController = StreamController<List<DrawnLine>>.broadcast(); StreamController<DrawnLine> currentLineStreamController = StreamController<DrawnLine>.broadcast(); String picName = "Hi"; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("AppBar"), ), body: Container( child: Stack( children: [ buildAllPaths(context), buildCurrentPath(context), ], ), ), bottomNavigationBar: BottomNavigationBar( items: [ BottomNavigationBarItem(icon: Icon(Icons.arrow_back), label: ""), BottomNavigationBarItem(icon: Icon(Icons.arrow_back), label: ""), BottomNavigationBarItem(icon: Icon(Icons.arrow_back), label: ""), BottomNavigationBarItem(icon: Icon(Icons.arrow_back), label: ""), ], ), ); } Widget buildAllPaths(BuildContext context) { return RepaintBoundary( key: _globalKey, child: Container( width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height, color: Colors.transparent, padding: EdgeInsets.all(4), alignment: Alignment.topLeft, child: StreamBuilder<List<DrawnLine>>( stream: linesStreamController.stream, builder: (context, snapshot) { return CustomPaint( painter: Sketcher( lines: lines, ), ); }, ), ), ); } Widget buildCurrentPath(BuildContext context) { return GestureDetector( onPanStart: onPanStart, onPanUpdate: onPanUpdate, onPanEnd: onPanEnd, child: RepaintBoundary( child: Container( width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height, padding: EdgeInsets.all(4), color: Colors.transparent, alignment: Alignment.topLeft, child: StreamBuilder<DrawnLine>( stream: currentLineStreamController.stream, builder: (context, snapshot) { return CustomPaint( painter: Sketcher( lines: [line], ), ); }, ), ), ), ); } void onPanStart(DragStartDetails details) { RenderBox? box = context.findRenderObject() as RenderBox?; Offset point = box!.globalToLocal(details.globalPosition); line = DrawnLine([point], selectedColor, selectedWidth); } void onPanUpdate(DragUpdateDetails details) { RenderBox? box = context.findRenderObject() as RenderBox?; Offset point = box!.globalToLocal(details.globalPosition); List<Offset> path = List.from(line.path)..add(point); line = DrawnLine(path, selectedColor, selectedWidth); currentLineStreamController.add(line); } void onPanEnd(DragEndDetails details) { lines = List.from(lines)..add(line); linesStreamController.add(lines); } }
解决方案
问题根源
你在onPanStart和onPanUpdate中使用的context是页面根State的context,对应的RenderBox包含了AppBar的高度空间,导致globalToLocal转换后的坐标自带AppBar的偏移量,最终绘制位置和触摸位置不符。
修复步骤
- 在State类中添加一个GlobalKey,用于绑定GestureDetector下的容器:
final GlobalKey _gestureContainerKey = GlobalKey();
- 修改
buildCurrentPath中的Container,绑定这个key:
child: RepaintBoundary( child: Container( key: _gestureContainerKey, // 新增绑定key width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height, padding: EdgeInsets.all(4), color: Colors.transparent, alignment: Alignment.topLeft, child: StreamBuilder<DrawnLine>( // 原有代码不变 ), ), ),
- 更新
onPanStart和onPanUpdate方法,使用新的key获取正确的RenderBox:
void onPanStart(DragStartDetails details) { RenderBox? box = _gestureContainerKey.currentContext?.findRenderObject() as RenderBox?; Offset point = box!.globalToLocal(details.globalPosition); line = DrawnLine([point], selectedColor, selectedWidth); } void onPanUpdate(DragUpdateDetails details) { RenderBox? box = _gestureContainerKey.currentContext?.findRenderObject() as RenderBox?; Offset point = box!.globalToLocal(details.globalPosition); List<Offset> path = List.from(line.path)..add(point); line = DrawnLine(path, selectedColor, selectedWidth); currentLineStreamController.add(line); }
额外优化
当前代码中容器高度设置为MediaQuery.of(context).size.height,会导致绘制区域超出AppBar和BottomNavigationBar的范围,可以改成适配可用空间的高度:
height: MediaQuery.of(context).size.height - AppBar().preferredSize.height - MediaQuery.of(context).padding.top - kBottomNavigationBarHeight,
这个修改需要同时应用到buildAllPaths和buildCurrentPath中的Container上,确保绘制区域完全在可视范围内。
内容的提问来源于stack exchange,提问作者MinhDat
相关产品推荐
相关产品推荐

