我正在开发一个 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
中也无法解决问题。
我找到了一种通过按键访问图像渲染框来实现所需结果的方法。通过它,我可以在以我选择的任何方式安装图像后获得图像的实际大小,例如
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,),
),
],
),
);
},
);
}
}