堆栈中小部件的相对位置

问题描述 投票:0回答:1

我正在开发一个 Flutter 应用程序,我想使用自定义小部件选择图像上的位置。该小部件使用带有

Stack
InteractiveViewer
GestureDetector
来处理点击,并在用户点击的位置添加一个
Positioned
小部件并返回标准化坐标。

目标

我想实现相对于提供的图像的标准化坐标,无论显示大小或图像缩放如何,并在点击发生的位置显示位置图钉。

问题

我遇到了定位问题,其中

Positioned
小部件根据 x 轴正确显示,但在 y 轴上明显向下偏移。

class _LocationPickerState extends State<LocationPicker> {
  final TransformationController _controller = TransformationController();
  MapObject object = MapObject(coordinates: const Offset(0, 0), size: 10);
  late Size size;
  late double baseIconSize;
  Offset? selectedCoordinates;
  late Offset localPosition;

  @override
  Widget build(BuildContext context) {
    baseIconSize = MediaQuery.of(context).size.width * 0.05;

    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        Expanded(
          child: LayoutBuilder(
            builder: (context, constraints) {
              size = constraints.biggest;
              return InteractiveViewer(
                transformationController: _controller,
                onInteractionEnd: (details) => setState(() {
                  object = MapObject(
                      coordinates: object.coordinates,
                      size: baseIconSize / _controller.value.getMaxScaleOnAxis());
                }),
                minScale: 1,
                maxScale: 7.5,
                child: GestureDetector(
                  onTapUp: (details) {
                    localPosition = details.localPosition;
                    selectedCoordinates = convertToNormalizedCoordinates(object.coordinates, size);
                    setState(() {
                      object = MapObject(
                          coordinates: localPosition,
                          size: baseIconSize / _controller.value.getMaxScaleOnAxis());
                    });
                  },
                  child: Center(
                    child: Stack(
                      children: <Widget>[
                        SizedBox(child: widget.mapRadar),
                        _buildMapObject(object, size),
                      ],
                    ),
                  ),
                ),
              );
            },
          ),
        ),
        Text(selectedCoordinates != null ? 'x: ${selectedCoordinates!.dx.toStringAsFixed(4)}, y: ${selectedCoordinates!.dy.toStringAsFixed(4)}': ''),
        ElevatedButton(
            onPressed: () {
              if (selectedCoordinates != null) widget.onObjectSelected(selectedCoordinates!);
              Navigator.pop(context);
            },
            child: const Text('Done'))
      ],
    );
  }

  Widget _buildMapObject(MapObject obj, Size size) {
    return Positioned(
      left: obj.coordinates.dx,
      top: obj.coordinates.dy,
      child: FractionalTranslation(
        translation: const Offset(-0.5, -1),
        child: Icon(
          Icons.location_pin,
          size: obj.size,
          color: Colors.red,
        ),
      ),
    );
  }
}

删除

Expanded
小部件会导致
LayoutBuilder
具有 无限高度,因此不会显示任何 y 坐标,并且将
InteractiveViewer
包裹在
SizedBox
中也无法解决问题。

flutter
1个回答
0
投票

我找到了一种通过按键访问图像渲染框来实现所需结果的方法。通过它,我可以在以我选择的任何方式安装图像后获得图像的实际大小,例如

Boxfit.contain

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Relative Positioning Test'),
        ),
        body: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              Expanded(
                child: PositionPicker(
                  imagePath: 'assets/image.png',
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class PositionPicker extends StatefulWidget {
  final String imagePath;

  PositionPicker({required this.imagePath});

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

class _PositionPickerState extends State<PositionPicker> {
  double relativeX = 0;
  double relativeY = 0;
  Offset localPosition = Offset(0, 0);
  double iconSize = 20;
  GlobalKey _imageKey = GlobalKey();

  void _updatePosition(TapDownDetails details) {
    final RenderBox box = _imageKey.currentContext?.findRenderObject() as RenderBox;
    localPosition = box.globalToLocal(details.globalPosition);
    relativeX = localPosition.dx / box.size.width;
    relativeY = localPosition.dy / box.size.height;
    setState(() {
      print("Relative Position: X: ${relativeX.toStringAsFixed(2)}, Y: ${relativeY.toStringAsFixed(2)}");
    });
  }

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (BuildContext context, BoxConstraints constraints) {
        return GestureDetector(
          onTapDown: (TapDownDetails details) => _updatePosition(details),
          child: Stack(
            alignment: Alignment.topLeft,
            children: [
              Image.asset(
                widget.imagePath,
                key: _imageKey,
                fit: BoxFit.contain
              ),
              Positioned(
                left: localPosition.dx - iconSize / 2,
                top: localPosition.dy - iconSize,
                child: Icon(Icons.location_pin, size: iconSize, color: Colors.red,),
              ),
            ],
          ),
        );
      },
    );
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.