Skip to content

Instantly share code, notes, and snippets.

@Ansh-Rathod
Last active June 30, 2023 10:00
Show Gist options
  • Save Ansh-Rathod/e0de56562faab54427a135fbd32e4554 to your computer and use it in GitHub Desktop.
Save Ansh-Rathod/e0de56562faab54427a135fbd32e4554 to your computer and use it in GitHub Desktop.
Resizable-widget with rotation
import 'dart:math' as math;
import 'dart:ui';
import 'package:flutter/material.dart';
const duration = Duration(milliseconds: 50);
extension OffsetExtensions on Offset {
Offset offsetDistance(Offset end) {
double dx = end.dx - this.dx;
double dy = end.dy - this.dy;
return Offset(dx, dy);
}
double distanceTo(Offset other) {
return math.sqrt(math.pow(other.dx - dx, 2) + math.pow(other.dy - dy, 2));
}
}
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: FirstExample(),
);
}
}
class FirstExample extends StatefulWidget {
const FirstExample({
Key? key,
}) : super(key: key);
@override
State<FirstExample> createState() => _FirstExampleState();
}
class _FirstExampleState extends State<FirstExample> {
List<Offset> offsets = [];
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
fit: StackFit.passthrough,
clipBehavior: Clip.none,
alignment: Alignment.center,
children: [
ResizableWidget(
offsets: offsets,
onOffsetUpdate: (off) {
setState(() {
offsets.add(off);
});
},
child: Center(
child: Stack(
alignment: Alignment.center,
children: [
Container(
width: 1,
color: Colors.black,
height: double.infinity,
),
Container(
width: double.infinity,
color: Colors.black,
height: 1,
),
],
),
),
)
],
));
}
}
class ResizableWidget extends StatefulWidget {
final Widget child;
final List<Offset> offsets;
final Function(Offset offset) onOffsetUpdate;
const ResizableWidget({
Key? key,
required this.child,
required this.offsets,
required this.onOffsetUpdate,
}) : super(key: key);
@override
State<ResizableWidget> createState() => _ResizableWidgetState();
}
class _ResizableWidgetState extends State<ResizableWidget> {
double top = 200;
double left = 200;
double rotation = 0;
double width = 200;
double height = 100;
bool isSelected = true;
GlobalKey key = GlobalKey();
GlobalKey canvasKey = GlobalKey();
@override
Widget build(BuildContext context) {
final canvasWidth = MediaQuery.sizeOf(context).width;
final canvasHeight = MediaQuery.sizeOf(context).height;
final canvasSize =
math.sqrt(canvasWidth * canvasWidth + canvasHeight * canvasHeight);
const kSideBorder = BorderSide(width: 3, color: Colors.blue);
return Positioned(
key: canvasKey,
height: canvasSize,
width: canvasSize,
child: Container(
decoration: BoxDecoration(border: Border.all(color: Colors.red)),
child: Stack(
clipBehavior: Clip.none,
alignment: Alignment.topLeft,
children: <Widget>[
Positioned(
top: top,
left: left,
child: MoveableContainer(
onDrag: (dx, dy) {
setState(() {
top = top + dy;
left = left + dx;
});
},
child: Transform.rotate(
angle: rotation,
alignment: Alignment.center,
child: Container(
decoration: BoxDecoration(
color: isSelected
? Colors.blue.withOpacity(0.05)
: Colors.transparent,
border: Border.all(
color: isSelected ? Colors.blue : Colors.transparent,
width: 1,
),
),
height: height,
width: width,
child: widget.child,
),
),
),
),
Positioned(
child: Container(
width: 3,
color: Colors.black,
height: 3,
),
),
Positioned(
top: top,
left: left,
width: width,
height: height,
child: Transform.rotate(
angle: rotation,
alignment: Alignment.center,
child: Stack(
clipBehavior: Clip.none,
children: [
Positioned(
top: 0,
left: 0,
child: DraggableHandle(
width: 10,
height: 10,
decoration: const BoxDecoration(
border: Border(
left: kSideBorder,
top: kSideBorder,
)),
onDrag: (dx, dy) {
var t = top;
var l = left;
var w = width;
var h = height;
if (dy < 0) {
t += dy;
var newHeight = height - dy;
h = newHeight > 0 ? newHeight : 0;
} else if (dy > 0) {
t += dy;
var newHeight = height - dy;
h = newHeight > 0 ? newHeight : 0;
}
if (dx < 0) {
var newWidth = width - dx;
w = newWidth > 0 ? newWidth : 0;
l += dx;
} else if (dx > 0) {
l += dx;
var newWidth = width - dx;
w = newWidth > 0 ? newWidth : 0;
}
setState(() {
top = t;
left = l;
width = w;
height = h;
});
},
),
),
// center center
// top right
Positioned(
top: 0,
right: 0,
child: DraggableHandle(
width: 10,
height: 10,
decoration: const BoxDecoration(
border: Border(
right: kSideBorder,
top: kSideBorder,
)),
onDrag: (dx, dy) {
var t = top;
var l = left;
var w = width;
var h = height;
if (dy < 0 && dx > 0) {
t += dy;
var newHeight = height - dy;
h = newHeight > 0 ? newHeight : 0;
var newWidth = width + dx;
w = newWidth > 0 ? newWidth : 0;
} else if (dy > 0 && dx < 0) {
var newHeight = height - dy;
h = newHeight > 0 ? newHeight : 0;
var newWidth = width + dx;
w = newWidth > 0 ? newWidth : 0;
t += dy;
}
setState(() {
top = t;
left = l;
width = w;
height = h;
});
},
),
),
// bottom right
Positioned(
bottom: 0,
right: 0,
child: DraggableHandle(
width: 10,
height: 10,
decoration: const BoxDecoration(
border: Border(
right: kSideBorder,
bottom: kSideBorder,
)),
onDrag: (dx, dy) {
var w = width;
var h = height;
var newHeight = height + dy;
h = newHeight > 0 ? newHeight : 0;
var newWidth = width + dx;
w = newWidth > 0 ? newWidth : 0;
setState(() {
width = w;
height = h;
});
},
),
),
// bottom left
Positioned(
bottom: 0,
left: 0,
child: DraggableHandle(
width: 10,
height: 10,
decoration: const BoxDecoration(
border: Border(
left: kSideBorder,
bottom: kSideBorder,
),
),
onDrag: (dx, dy) {
var t = top;
var l = left;
var w = width;
var h = height;
if (dy > 0 && dx < 0) {
l += dx;
var newHeight = height + dy;
h = newHeight > 0 ? newHeight : 0;
var newWidth = width - dx;
w = newWidth > 0 ? newWidth : 0;
} else if (dy < 0 && dx > 0) {
l += dx;
var newHeight = height + dy;
h = newHeight > 0 ? newHeight : 0;
var newWidth = width - dx;
w = newWidth > 0 ? newWidth : 0;
}
setState(() {
top = t;
left = l;
width = w;
height = h;
});
},
),
),
],
),
),
),
Positioned(
top: top - 31,
left: left - 31,
child: Container(
alignment: Alignment.center,
width: width + 60,
height: height + 60,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(),
MouseRegion(
cursor: SystemMouseCursors.grab,
child: GestureDetector(
onPanUpdate: (v) {
setState(() {
rotation -= v.delta.dx * 0.01;
});
},
child: AnimatedContainer(
duration: const Duration(microseconds: 50),
width: 20,
height: 20,
constraints: const BoxConstraints(
maxHeight: 20,
maxWidth: 20,
),
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
boxShadow: kElevationToShadow[1],
),
child: const Icon(
Icons.rotate_left,
size: 12,
color: Colors.black,
),
),
),
),
],
),
),
)
],
),
),
);
}
}
class DraggableHandle extends StatefulWidget {
final double width;
final double height;
final BoxDecoration decoration;
final MouseCursor? cursor;
final Function(double dx, double dy) onDrag;
const DraggableHandle({
Key? key,
required this.width,
required this.height,
required this.decoration,
this.cursor,
required this.onDrag,
}) : super(key: key);
@override
State<DraggableHandle> createState() => _DraggableHandleState();
}
class _DraggableHandleState extends State<DraggableHandle> {
_handleUpdate(DragUpdateDetails details) {
widget.onDrag(details.delta.dx, details.delta.dy);
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onPanUpdate: _handleUpdate,
child: MouseRegion(
cursor: widget.cursor ?? SystemMouseCursors.basic,
child: Container(
width: widget.width,
height: widget.height,
decoration: widget.decoration,
),
),
);
}
}
class MoveableContainer extends StatefulWidget {
final Widget child;
final Function onDrag;
const MoveableContainer({
Key? key,
required this.child,
required this.onDrag,
}) : super(key: key);
@override
State<MoveableContainer> createState() => _MoveableContainerState();
}
class _MoveableContainerState extends State<MoveableContainer> {
_handleUpdate(details) {
widget.onDrag(details.delta.dx, details.delta.dy);
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onPanUpdate: _handleUpdate,
child: Container(
child: widget.child,
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment