Flutter可拖拽缩放容器:如何实现全点位缩放功能
实现Flutter容器全点位缩放控制
要完成8个点位的完整缩放,核心是根据每个控制点的位置,计算拖动时容器的宽高变化,同时保持预设宽高比,并且调整容器的坐标(x/y)匹配拖动轨迹。以下是补全所有点位逻辑后的完整代码:
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: Scaffold( body: SafeArea( child: ImageManager(), ), ), ); } } final ballRadius = 7.5; class ImageManager extends StatefulWidget { @override _ImageManagerState createState() => _ImageManagerState(); } class _ImageManagerState extends State<ImageManager> { double _x = 0; double _y = 0; double _height = 200; double _width = 300; double _aspectRatio = 200 / 300; @override Widget build(BuildContext context) { return Stack( children: <Widget>[ Positioned( top: _y, left: _x, child: GestureDetector( onPanUpdate: (DragUpdateDetails details) { setState(() { _x += details.delta.dx; _y += details.delta.dy; }); }, child: Image.network( "https://via.placeholder.com/300x200", width: _width, fit: BoxFit.fill, ), ), ), // 左上角 Positioned( top: _y - ballRadius, left: _x - ballRadius, child: Ball( onDragStart: () {}, onDrag: (double dx, double dy) { var newWidth = _width - dx; var newHeight = newWidth * _aspectRatio; setState(() { _y = _y + (_height - newHeight); _x = _x + dx; _width = newWidth; _height = newHeight; }); }, onDragEnd: () {}, ), ), // 上中 Positioned( top: _y - ballRadius, left: _x + _width / 2 - ballRadius, child: Ball( onDragStart: () {}, onDrag: (double dx, double dy) { var newHeight = _height - dy; var newWidth = newHeight / _aspectRatio; setState(() { _y += dy; _height = newHeight; _width = newWidth; }); }, onDragEnd: () {}, ), ), // 右上角 Positioned( top: _y - ballRadius, left: _x + _width - ballRadius, child: Ball( onDragStart: () {}, onDrag: (double dx, double dy) { var newWidth = _width + dx; var newHeight = newWidth * _aspectRatio; setState(() { _y += (_height - newHeight); _width = newWidth; _height = newHeight; }); }, onDragEnd: () {}, ), ), // 左中 Positioned( top: _y + _height / 2 - ballRadius, left: _x - ballRadius, child: Ball( onDragStart: () {}, onDrag: (double dx, double dy) { var newWidth = _width - dx; var newHeight = newWidth * _aspectRatio; setState(() { _x += dx; _width = newWidth; _height = newHeight; }); }, onDragEnd: () {}, ), ), // 右中 Positioned( top: _y + _height / 2 - ballRadius, left: _x + _width - ballRadius, child: Ball( onDragStart: () {}, onDrag: (double dx, double dy) { var newWidth = _width + dx; var newHeight = newWidth * _aspectRatio; setState(() { _width = newWidth; _height = newHeight; }); }, onDragEnd: () {}, ), ), // 左下角 Positioned( top: _y + _height - ballRadius, left: _x - ballRadius, child: Ball( onDragStart: () {}, onDrag: (double dx, double dy) { var newWidth = _width - dx; var newHeight = newWidth * _aspectRatio; setState(() { _x += dx; _width = newWidth; _height = newHeight; }); }, onDragEnd: () {}, ), ), // 下中 Positioned( top: _y + _height - ballRadius, left: _x + _width / 2 - ballRadius, child: Ball( onDragStart: () {}, onDrag: (double dx, double dy) { var newHeight = _height + dy; var newWidth = newHeight / _aspectRatio; setState(() { _height = newHeight; _width = newWidth; }); }, onDragEnd: () {}, ), ), // 右下角 Positioned( top: _y + _height - ballRadius, left: _x + _width - ballRadius, child: Ball( onDragStart: () {}, onDrag: (double dx, double dy) { var newWidth = _width + dx; var newHeight = newWidth * _aspectRatio; setState(() { _width = newWidth; _height = newHeight; }); }, onDragEnd: () {}, ), ), ], ); } } class Ball extends StatelessWidget { final Function onDragStart; final Function onDrag; final Function onDragEnd; const Ball({required this.onDragStart, required this.onDrag, required this.onDragEnd}); void _onDragStart(DragStartDetails details) { if (onDragStart != null) onDragStart(); } void _onDragUpdate(DragUpdateDetails details) { if (onDrag != null) onDrag(details.delta.dx, details.delta.dy); } void _onDragEnd(DragEndDetails details) { if (onDragEnd != null) onDragEnd(); } @override Widget build(BuildContext context) { return GestureDetector( onPanStart: _onDragStart, onPanUpdate: _onDragUpdate, onPanEnd: _onDragEnd, child: Container( height: 2 * ballRadius, width: 2 * ballRadius, decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.circular(ballRadius), border: Border.all( width: 3, color: Colors.white, ), ), ), ); } }
各点位缩放逻辑说明
- 上中:垂直拖动时,通过
dy计算新高度,再按宽高比推导新宽度,同时调整容器顶部_y坐标匹配拖动位置。 - 右上角:水平拖动改变宽度,按宽高比计算新高度,调整容器顶部
_y坐标以保持比例,右侧宽度随拖动扩展。 - 左中:水平拖动改变左侧位置,通过
dx计算新宽度,再按比例得到新高度,容器_x坐标随拖动调整。 - 右中:水平拖动直接扩展宽度,按宽高比自动调整高度,容器位置保持不变。
- 左下角:水平拖动改变左侧位置,计算新宽度后按比例得到新高度,容器
_x坐标随拖动调整。 - 下中:垂直拖动扩展高度,按宽高比推导新宽度,容器底部位置随拖动扩展。
所有点位的缩放都严格遵循预设的_aspectRatio,确保容器始终保持初始宽高比例。
内容的提问来源于stack exchange,提问作者Harsh Rajpara
相关产品推荐
相关产品推荐

