I am trying to get the positioned lines(purple one) while dragging. Please see that attachment to know more about the issue.
I am trying to get the positioned lines(purple one) while dragging. Please see that attachment to know more about the issue.
the lines are drawn with canvas.drawPoints() at the and of paint() method - you can change this to draw your dotted line
now, with solid, black lines it looks like this:
class FooResizer extends StatefulWidget {
@override
_FooResizerState createState() => _FooResizerState();
}
class _FooResizerState extends State<FooResizer> with TickerProviderStateMixin, ChangeNotifier {
Sizer currentSizer;
double angle = 0.0;
AnimationController ctrl;
final Map<String, Sizer> sizers = {
'l': Sizer('l', Alignment.centerLeft, {'t': 0.5, 'b': 0.5, 'M': 0.5}),
't': Sizer('t', Alignment.topCenter, {'l': 0.5, 'r': 0.5, 'M': 0.5}),
'r': Sizer('r', Alignment.centerRight, {'t': 0.5, 'b': 0.5, 'R': 1.0, 'M': 0.5}),
'b': Sizer('b', Alignment.bottomCenter, {'l': 0.5, 'r': 0.5, 'R': 1.0, 'M': 0.5}),
'R': Sizer('R', Alignment.bottomRight, {}),
'M': Sizer('M', Alignment.center, {}),
};
@override
void initState() {
super.initState();
ctrl = AnimationController(vsync: this, duration: Duration(milliseconds: 300), value: 1.0);
}
@override
Widget build(BuildContext context) {
return ClipRect(
child: ColoredBox(
color: Colors.black12,
// CustomMultiChildLayoutPainter:
// https://gist.github.com/pskink/0f82724b41d9ebe89604782fbf62fe03#file-multi_layout_painter-dart-L447
child: CustomMultiChildLayoutPainter(
delegate: _FooResizerDelegate(sizers, this),
children: [
LayoutId(
id: 'body',
child: AnimatedBuilder(
animation: this,
builder: (ctx, child) {
return Transform.rotate(
angle: angle,
child: child,
);
},
child: Material(color: Colors.grey[300], elevation: 4, child: FlutterLogo()),
),
),
...sizers.values.map(_sizerBuilder),
],
),
),
);
}
Widget _sizerBuilder(Sizer sizer) {
final colors = {
'M': Colors.orange,
'R': Colors.teal,
};
return LayoutId(
id: sizer.id,
child: GestureDetector(
onPanStart: (details) => _panStart(sizer),
onPanUpdate: _panUpdate,
onPanEnd: _panEnd,
child: AnimatedBuilder(
animation: ctrl,
builder: (context, child) {
final color = colors[sizer.id] ?? Colors.green;
return Opacity(
opacity: currentSizer == sizer? 1.0 : Curves.ease.transform(ctrl.value),
child: Container(
decoration: ShapeDecoration(
shape: CircleBorder(side: BorderSide(width: lerpDouble(0.0, 2.0, ctrl.value), color: Colors.black38)),
color: currentSizer == sizer? Color.lerp(Colors.deepPurple, color, ctrl.value) : color,
shadows: [BoxShadow.lerp(BoxShadow(spreadRadius: 2, blurRadius: 4, offset: Offset(2, 2)), null, ctrl.value)],
),
),
);
}
),
),
);
}
_panStart(Sizer sizer) {
currentSizer = sizer;
ctrl.reverse();
}
_panUpdate(DragUpdateDetails details) {
assert(currentSizer != null);
if (currentSizer.id == 'M') {
// move
sizers.values.forEach((sizer) => sizer.center += details.delta);
} else
if (currentSizer.id == 'R') {
// rotate
final localCenter = sizers['M'].center;
final globalCenter = (context.findRenderObject() as RenderBox).localToGlobal(localCenter);
final angle0 = (details.globalPosition - details.delta - globalCenter).direction;
final angle1 = (details.globalPosition - globalCenter).direction;
final deltaAngle = angle1 - angle0;
sizers.values
.where((sizer) => sizer.id != 'M')
.forEach((sizer) {
final vector = sizer.center - localCenter;
sizer.center = localCenter + Offset.fromDirection(vector.direction + deltaAngle, vector.distance);
});
angle += deltaAngle;
} else {
// resize
final adjustedAngle = angle + currentSizer.angleAdjustment;
final rotatedDistance = details.delta.distance * math.cos(details.delta.direction - adjustedAngle);
final vector = Offset.fromDirection(adjustedAngle, rotatedDistance);
currentSizer.center += vector;
currentSizer.dependents.forEach((id, factor) => sizers[id].center += vector * factor);
}
notifyListeners();
}
_panEnd(DragEndDetails details) {
assert(currentSizer != null);
// currentSizer = null;
ctrl.forward();
}
}
class _FooResizerDelegate extends MultiChildLayoutPainterDelegate {
static const SIZE = 48.0;
final Map<String, Sizer> sizers;
_FooResizerDelegate(this.sizers, Listenable relayout) : super(relayout: relayout);
@override
void performLayout(Size size) {
sizers['M'].center ??= init(size);
for (var sizer in sizers.values) {
layoutChild(sizer.id, BoxConstraints.tight(Size(SIZE, SIZE)));
positionChild(sizer.id, sizer.center - Offset(SIZE / 2, SIZE / 2));
}
final w = (sizers['l'].center - sizers['r'].center).distance;
final h = (sizers['t'].center - sizers['b'].center).distance;
layoutChild('body', BoxConstraints.tight(Size(w, h)));
positionChild('body', sizers['M'].center - Offset(w / 2, h / 2));
}
Offset init(Size size) {
final rect = (Offset.zero & size).deflate(24);
print('init rect: $rect');
for (var sizer in sizers.values) {
sizer
..center = sizer.alignment.withinRect(rect)
..angleAdjustment = sizer.alignment.x == 0? math.pi / 2 : 0;
}
return sizers['M'].center;
}
@override
bool shouldRelayout(covariant MultiChildLayoutDelegate oldDelegate) => true;
@override
void foregroundPaint(Canvas canvas, Size size) {
}
@override
void paint(Canvas canvas, Size size) {
final w = (sizers['r'].center - sizers['l'].center).distance;
final h = (sizers['b'].center - sizers['t'].center).distance;
final rect = Rect.fromCenter(center: sizers['M'].center, width: w, height: h);
final angle = (sizers['r'].center - sizers['l'].center).direction;
final matrix = Matrix4.identity()
..translate(rect.center.dx, rect.center.dy)
..rotateZ(angle)
..translate(-rect.center.dx, -rect.center.dy);
final transformedRect = MatrixUtils.transformRect(matrix, rect);
final points = [
Offset(transformedRect.left, 0), Offset(transformedRect.left, size.height),
Offset(0, transformedRect.top), Offset(size.width, transformedRect.top),
Offset(transformedRect.right, 0), Offset(transformedRect.right, size.height),
Offset(0, transformedRect.bottom), Offset(size.width, transformedRect.bottom),
];
canvas.drawPoints(PointMode.lines, points, Paint());
}
}
class Sizer {
final String id;
final Alignment alignment;
final Map<String, double> dependents;
Offset center;
double angleAdjustment;
Sizer(this.id, this.alignment, this.dependents);
}