当 InteractiveViewer 放大时,可拖动反馈小部件会获得偏移

Gpa*_*ack 5 interactive draggable flutter

我正在尝试将一个小部件拖动到 InteractiveViewer 之上。
当比例为 1.0 时,代码运行良好。
但是,如果放大,当我拖动圆圈时:

  1. 反馈小部件向右下角偏移几个像素
  2. 当我放手时,圆圈向上移动

为什么会发生这种情况?

这是一个演示,说明了我正在谈论的内容:

演示动图

下面是这个应用程序的代码

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

enum _Action { scale, pan }

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

const _defaultMarkerSize = 48.0;

class _MyHomePageState extends State<MyHomePage> {
  Offset _pos = Offset.zero; // Position could go from -1 to 1 in both directions
  _Action action; // pan or pinch, useful to know if we need to scale down pin
  final _transformationController = TransformationController();

  @override
  Widget build(BuildContext context) {
    final scale = _transformationController.value.getMaxScaleOnAxis();
    final size = _defaultMarkerSize / scale;
    return Scaffold(
      appBar: AppBar(title: Text(widget.title)),
      body: InteractiveViewer(
        transformationController: _transformationController,
        maxScale: 5,
        minScale: 1,
        child: Stack(
          children: [
            Center(child: Image.asset('image/board.png')),
            DraggablePin(
              pos: _pos,
              size: size,
              onDragEnd: (details) {
                final matrix = Matrix4.inverted(_transformationController.value);
                final height = AppBar().preferredSize.height;
                final sceneY = details.offset.dy - height;
                final viewportPoint = MatrixUtils.transformPoint(
                  matrix,
                  Offset(details.offset.dx, sceneY) + Offset(_defaultMarkerSize / 2, _defaultMarkerSize / 2),
                );
                final screenSize = MediaQuery.of(context).size;
                final x = viewportPoint.dx * 2 / screenSize.width - 1;
                final y = viewportPoint.dy * 2 / screenSize.height - 1;
                setState(() {
                  _pos = Offset(x, y);
                });
              },
            ),
          ],
        ),
        onInteractionStart: (details) {
          // No need to call setState as we don't need to rebuild
          action = null;
        },
        onInteractionUpdate: (details) {
          if (action == null) {
            if (details.scale == 1)
              action = _Action.pan;
            else
              action = _Action.scale;
          }
          if (action == _Action.scale) {
            // Need to resize the pins so that they keep the same size
            setState(() {});
          }
        },
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.restore),
        onPressed: () {
          setState(() {
            _pos = Offset.zero;
          });
        },
      ),
    );
  }
}

class DraggablePin extends StatelessWidget {
  final Offset pos;
  final double size;
  final void Function(DraggableDetails) onDragEnd;
  const DraggablePin({this.pos, this.size, this.onDragEnd, Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final offset = size / 2;
    Widget pinWidget = Pin(size);

    final screenSize = MediaQuery.of(context).size;
    final height = screenSize.height - AppBar().preferredSize.height;

    pinWidget = Draggable(
      child: pinWidget,
      feedback: Pin(_defaultMarkerSize),
      childWhenDragging: Container(),
      onDragEnd: onDragEnd,
    );

    return Positioned(
      top: pos.dy * height / 2 + height / 2 - offset,
      left: pos.dx * screenSize.width / 2 + screenSize.width / 2 - offset,
      child: pinWidget,
    );
  }
}

class Pin extends StatelessWidget {
  const Pin(this.size, {Key key}) : super(key: key);
  final double size;

  @override
  Widget build(BuildContext context) {
    return Material(
      color: Colors.transparent,
      child: Center(
        child: Ink(
          decoration: ShapeDecoration(
            color: Colors.green,
            shape: const CircleBorder(),
          ),
          child: IconButton(
            constraints: BoxConstraints.tightFor(width: size, height: size),
            padding: const EdgeInsets.all(0),
            iconSize: size,
            splashRadius: size / 2,
            splashColor: Colors.white,
            icon: Container(),
            onPressed: () {},
          ),
        ),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)