如何在没有尺寸的情况下缩放图像
How to zoom an image without the size
我想缩放图像,但我不想关心图像的大小。此小部件用于包装任何小部件。我转换的小部件在我不知道的地方。这就是为什么我添加 220 以使其可见。有人可以增强我的代码以适应任何大小的小部件。
class ZoomDetailPhoto extends StatefulWidget {
final Widget child;
const ZoomDetailPhoto({Key? key, required this.child}) : super(key: key);
@override
_ZoomDetailPhotoState createState() => _ZoomDetailPhotoState();
}
class _ZoomDetailPhotoState extends State<ZoomDetailPhoto> {
late Offset offset;
@override
void initState() {
super.initState();
offset = Offset.zero;
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Listener(
onPointerHover: (onPointerHover) {
setState(() {
offset = onPointerHover.localPosition;
});
},
child: Stack(
alignment: Alignment.center,
children: [
widget.child,
Positioned(
left: offset.dx - 90,
top: offset.dy - 90,
child: Container(
decoration: BoxDecoration(
border: Border.all(
width: 9, color: theme.colorScheme.onBackground)),
child: Container(
width: 180,
height: 180,
clipBehavior: Clip.hardEdge,
decoration: const BoxDecoration(),
child: FittedBox(
fit: BoxFit.cover,
child: Transform.scale(
scale: 4,
child: Transform.translate(
offset:
Offset(-offset.dx + 220, -offset.dy + 220),
child: widget.child)))),
),
)
],
),
);
}
}
要获得 child 的大小,您可以使用 GlobalKey
并将其分配给 child(图像或任何小部件),然后在您的 Listener
(你可能可以使用 MouseRegion
而不是 Listener
,类似的目的,使用起来稍微容易一点)你可以使用 GlobalKey
.[=24 获取其 child 小部件的大小=]
但是请注意,小部件的大小只有在完成布局过程后才能确定。如果你不介意落后一帧,你可以在下一帧用 Stack
(你当前正在做的)做放大镜效果。否则,您可以考虑使用 OverlayEntry
代替放大镜,因为叠加是在普通小部件之后的单独流程中构建的。
注意前面的注释,我个人不介意在这种特殊情况下滞后一帧,因为放大镜仅在用户将鼠标悬停时才会显示,因此首次加载页面时不会跳过一帧引人注目。
编辑:
这个问题对我来说很有趣,我只是想到了另一种在同一帧中获得child大小的方法:在Stack
中使用Positioned.fill
,不需要 GlobalKey
或 Overlay
。利用这个想法,我做了一个小部件来做到这一点。
用法:
Magnifier(
magnification: 2.0,
child: Scaffold(
...
来源:
class Magnifier extends StatefulWidget {
final Widget child;
final double magnification;
const Magnifier({
Key? key,
required this.child,
this.magnification = 2.0,
}) : super(key: key);
@override
_MagnifierState createState() => _MagnifierState();
}
class _MagnifierState extends State<Magnifier> {
Offset? _offset;
@override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: [
widget.child,
Positioned.fill(
child: LayoutBuilder(
builder: (_, BoxConstraints constraints) {
final childSize = constraints.biggest;
return MouseRegion(
onHover: (event) {
setState(() => _offset = event.localPosition);
},
onExit: (_) => setState(() => _offset = null),
child: _offset != null
? _buildBox(_offset!.dx, _offset!.dy, childSize)
: null,
);
},
),
)
],
);
}
Widget _buildBox(double dx, double dy, Size childSize) {
final magnifierSize = childSize.shortestSide / 2;
return Transform.translate(
offset: Offset(dx - magnifierSize / 2, dy - magnifierSize / 2),
child: Align(
alignment: Alignment.topLeft,
child: Stack(
children: [
SizedBox(
width: magnifierSize,
height: magnifierSize,
child: ClipRect(
child: Transform.scale(
scale: widget.magnification,
child: Transform.translate(
offset: Offset(
childSize.width / 2 - dx,
childSize.height / 2 - dy,
),
child: OverflowBox(
minWidth: childSize.width,
maxWidth: childSize.width,
minHeight: childSize.height,
maxHeight: childSize.height,
child: widget.child,
),
),
),
),
),
Positioned.fill(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black, width: 2),
color: Colors.green.withOpacity(0.2),
),
),
),
],
),
),
);
}
}
我想缩放图像,但我不想关心图像的大小。此小部件用于包装任何小部件。我转换的小部件在我不知道的地方。这就是为什么我添加 220 以使其可见。有人可以增强我的代码以适应任何大小的小部件。
class ZoomDetailPhoto extends StatefulWidget {
final Widget child;
const ZoomDetailPhoto({Key? key, required this.child}) : super(key: key);
@override
_ZoomDetailPhotoState createState() => _ZoomDetailPhotoState();
}
class _ZoomDetailPhotoState extends State<ZoomDetailPhoto> {
late Offset offset;
@override
void initState() {
super.initState();
offset = Offset.zero;
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Listener(
onPointerHover: (onPointerHover) {
setState(() {
offset = onPointerHover.localPosition;
});
},
child: Stack(
alignment: Alignment.center,
children: [
widget.child,
Positioned(
left: offset.dx - 90,
top: offset.dy - 90,
child: Container(
decoration: BoxDecoration(
border: Border.all(
width: 9, color: theme.colorScheme.onBackground)),
child: Container(
width: 180,
height: 180,
clipBehavior: Clip.hardEdge,
decoration: const BoxDecoration(),
child: FittedBox(
fit: BoxFit.cover,
child: Transform.scale(
scale: 4,
child: Transform.translate(
offset:
Offset(-offset.dx + 220, -offset.dy + 220),
child: widget.child)))),
),
)
],
),
);
}
}
要获得 child 的大小,您可以使用 GlobalKey
并将其分配给 child(图像或任何小部件),然后在您的 Listener
(你可能可以使用 MouseRegion
而不是 Listener
,类似的目的,使用起来稍微容易一点)你可以使用 GlobalKey
.[=24 获取其 child 小部件的大小=]
但是请注意,小部件的大小只有在完成布局过程后才能确定。如果你不介意落后一帧,你可以在下一帧用 Stack
(你当前正在做的)做放大镜效果。否则,您可以考虑使用 OverlayEntry
代替放大镜,因为叠加是在普通小部件之后的单独流程中构建的。
注意前面的注释,我个人不介意在这种特殊情况下滞后一帧,因为放大镜仅在用户将鼠标悬停时才会显示,因此首次加载页面时不会跳过一帧引人注目。
编辑:
这个问题对我来说很有趣,我只是想到了另一种在同一帧中获得child大小的方法:在Stack
中使用Positioned.fill
,不需要 GlobalKey
或 Overlay
。利用这个想法,我做了一个小部件来做到这一点。
用法:
Magnifier(
magnification: 2.0,
child: Scaffold(
...
来源:
class Magnifier extends StatefulWidget {
final Widget child;
final double magnification;
const Magnifier({
Key? key,
required this.child,
this.magnification = 2.0,
}) : super(key: key);
@override
_MagnifierState createState() => _MagnifierState();
}
class _MagnifierState extends State<Magnifier> {
Offset? _offset;
@override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: [
widget.child,
Positioned.fill(
child: LayoutBuilder(
builder: (_, BoxConstraints constraints) {
final childSize = constraints.biggest;
return MouseRegion(
onHover: (event) {
setState(() => _offset = event.localPosition);
},
onExit: (_) => setState(() => _offset = null),
child: _offset != null
? _buildBox(_offset!.dx, _offset!.dy, childSize)
: null,
);
},
),
)
],
);
}
Widget _buildBox(double dx, double dy, Size childSize) {
final magnifierSize = childSize.shortestSide / 2;
return Transform.translate(
offset: Offset(dx - magnifierSize / 2, dy - magnifierSize / 2),
child: Align(
alignment: Alignment.topLeft,
child: Stack(
children: [
SizedBox(
width: magnifierSize,
height: magnifierSize,
child: ClipRect(
child: Transform.scale(
scale: widget.magnification,
child: Transform.translate(
offset: Offset(
childSize.width / 2 - dx,
childSize.height / 2 - dy,
),
child: OverflowBox(
minWidth: childSize.width,
maxWidth: childSize.width,
minHeight: childSize.height,
maxHeight: childSize.height,
child: widget.child,
),
),
),
),
),
Positioned.fill(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black, width: 2),
color: Colors.green.withOpacity(0.2),
),
),
),
],
),
),
);
}
}