How to drag multiple objects inside a Container - flutter

I'm trying to have multiple images inside a big container and be able to move them around and rotate them, as shown in the image below:
I've been playing around with CostumePainter and this is my result whilst following this guide and
Does anyone have an idea on how to do this with Images and multiple of them?
My code:
dynamic _balls;
double xPos = 100;
double yPos = 100;
bool isClick = false;
#override
Widget build(BuildContext context) {
_balls = _paint(xPosition: xPos, yPosition: yPos, ballRad: 20);
return Scaffold(
appBar: AppBar(
title: const Text("Drag and Drop"),
),
body: Center(
child: GestureDetector(
onHorizontalDragDown: (details) {
setState(() {
if (_balls.isBallRegion(
details.localPosition.dx, details.localPosition.dy)) {
isClick = true;
}
});
},
onHorizontalDragEnd: (details) {
setState(() {
isClick = false;
});
},
onHorizontalDragUpdate: (details) {
if (isClick) {
setState(() {
xPos = details.localPosition.dx;
yPos = details.localPosition.dy;
});
}
},
child: Container(
height: 300,
width: 300,
color: Colors.lightBlueAccent,
child: CustomPaint(
painter: _balls,
),
),
),
),
);
}

I followed this guide and it helped me, btw this is the blog.
Here is the code btw:
// #dart=2.9
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class ContainerList {
double height;
double width;
double scale;
double rotation;
double xPosition;
double yPosition;
ContainerList({
this.height,
this.rotation,
this.scale,
this.width,
this.xPosition,
this.yPosition,
});
}
class HomePage extends StatefulWidget {
const HomePage({Key key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<ContainerList> list = [];
Offset _initPos;
Offset _currentPos = Offset(0, 0);
double _currentScale;
double _currentRotation;
Size screen;
#override
void initState() {
screen = Size(400, 500);
list.add(ContainerList(
height: 200.0,
width: 200.0,
rotation: 0.0,
scale: 1.0,
xPosition: 0.1,
yPosition: 0.1,
));
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
height: 500.0,
color: Colors.blue.withOpacity(0.8),
width: double.infinity,
child: Stack(
children: list.map((value) {
return GestureDetector(
onScaleStart: (details) {
if (value == null) return;
_initPos = details.focalPoint;
_currentPos = Offset(value.xPosition, value.yPosition);
_currentScale = value.scale;
_currentRotation = value.rotation;
},
onScaleUpdate: (details) {
if (value == null) return;
final delta = details.focalPoint - _initPos;
final left = (delta.dx / screen.width) + _currentPos.dx;
final top = (delta.dy / screen.height) + _currentPos.dy;
setState(() {
value.xPosition = Offset(left, top).dx;
value.yPosition = Offset(left, top).dy;
value.rotation = details.rotation + _currentRotation;
value.scale = details.scale * _currentScale;
});
},
child: Stack(
children: [
Positioned(
left: value.xPosition * screen.width,
top: value.yPosition * screen.height,
child: Transform.scale(
scale: value.scale,
child: Transform.rotate(
angle: value.rotation,
child: Container(
height: value.height,
width: value.width,
child: FittedBox(
fit: BoxFit.fill,
child: Listener(
onPointerDown: (details) {
// if (_inAction) return;
// _inAction = true;
// _activeItem = val;
_initPos = details.position;
_currentPos =
Offset(value.xPosition, value.yPosition);
_currentScale = value.scale;
_currentRotation = value.rotation;
},
onPointerUp: (details) {
// _inAction = false;
},
child: Container(
height: value.height,
width: value.width,
color: Colors.red,
),
// child: Image.network(value.name),
),
),
),
),
),
)
],
),
);
}).toList(),
),
),
);
}
}

As a learning exercise, I created a basic flutter app that at least demonstrates dragging around a number of independent items and doing a couple of things like selecting, deleting, and re-ordering. The rotation function is a little buggy.
https://github.com/SpiRaiL/emoji_party_2

Related

i want to create some thing like this flip container flutter

it has three container when i tap on it it change to next flip I tried with flip_card package and it has only front and back but I need extra one
flip_board (https://pub.dev/packages/flip_board#middle-flip) package does exactly what you want.
Sample Code : -
FlipWidget({
flipType: FlipType.spinFlip, // Change flip effect
itemStream: _stream, // it is basically the length of Stacked Widgets.
itemBuilder: _itemBuilder, // Body of your Widget
flipDirection: AxisDirection.right, // flip up, down, left, right.
});
This package has many more methods and Widget types, to learn more about it vist the official page : - https://pub.dev/packages/flip_board#middle-flip
Complete Code : -
import 'dart:async';
import 'package:flip_board/flip_widget.dart';
import 'package:flutter/material.dart';
void main() => runApp(const MaterialApp(home: FlipPage()));
class FlipPage extends StatefulWidget {
const FlipPage({super.key});
#override
State<FlipPage> createState() => _FlipPageState();
}
class _FlipPageState extends State<FlipPage> {
#override
Widget build(BuildContext context) {
var spinController = StreamController<int>.broadcast();
bool containerClicked = false;
int nextSpinValue = 0;
int? widgetIndex = 0;
void spin() => spinController.add(++nextSpinValue);
return Scaffold(
body: Center(
child: FlipWidget(
initialValue: nextSpinValue,
itemStream: spinController.stream,
flipType: FlipType.spinFlip,
itemBuilder: (_, index) {
return GestureDetector(
onTap: (() async {
if (!containerClicked) {
containerClicked = true;
widgetIndex = index as int?;
if (widgetIndex! < 2) {
spin();
} else {
nextSpinValue = 0;
spinController.add(nextSpinValue);
}
await Future.delayed(const Duration(milliseconds: 500));
containerClicked = false;
}
}),
child: Container(
color: index == 0
? Colors.red[100]
: index == 1
? Colors.amber[100]
: Colors.blue[100],
height: 200,
width: 200,
alignment: Alignment.center,
child: Text(
'Widget $index',
style: const TextStyle(fontSize: 18),
)));
},
flipDirection: AxisDirection.up,
),
),
);
}
}
Output : -
As my opinion you should use animationOpacity like this :
i made one demo for you please check it.
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, this.title}) : super(key: key);
final String? title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _showFirstSide = false;
bool _showSecondSide = false;
bool _showThirdSide = false;
#override
void initState() {
super.initState();
_showFirstSide = true;
_showSecondSide = false;
_showThirdSide = false;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(""),
centerTitle: true,
),
body: Center(
child: SizedBox(
height: 100,
width: 100,
// constraints: BoxConstraints.tight(size),
child: _buildFlipAnimation(),
),
),
);
}
void _switchCard() {
setState(() {
if (_showFirstSide) {
_showFirstSide = !_showFirstSide;
_showSecondSide = true;
_showFirstSide = false;
} else if (_showSecondSide) {
_showSecondSide = !_showSecondSide;
_showThirdSide = true;
_showFirstSide = false;
} else if (_showThirdSide) {
_showThirdSide = !_showThirdSide;
_showFirstSide = true;
_showSecondSide = false;
}
});
}
Widget _buildFlipAnimation() {
return GestureDetector(
onTap: _switchCard,
child: Stack(
children: [
AnimatedOpacity(
curve: Curves.easeInBack,
duration: const Duration(milliseconds: 700),
opacity: _showFirstSide ? 1 : 0,
child: Container(
color: Colors.black,
height: MediaQuery.of(context).size.height / 2,
width: MediaQuery.of(context).size.width / 2,
),
),
AnimatedOpacity(
curve: Curves.easeIn.flipped,
duration: const Duration(milliseconds: 700),
opacity: _showSecondSide ? 1 : 0,
child: Container(
color: Colors.blue,
height: MediaQuery.of(context).size.height / 2,
width: MediaQuery.of(context).size.width / 2,
),
),
AnimatedOpacity(
curve: Curves.easeOut,
duration: const Duration(milliseconds: 400),
opacity: _showThirdSide ? 1 : 0,
child: Container(
color: Colors.pink,
height: MediaQuery.of(context).size.height / 2,
width: MediaQuery.of(context).size.width / 2,
),
),
],
),
);
}
}

Flutter dragAnchorStrategy keep center on virtual point on screen

I want the feedback icon to follow my cursor precisely everywhere on screen, but it's clear from this example that it doesn't:
According to my research this is however the purpose role of pointerDragAnchorStrategy.
This is my code:
import 'package:flutter/material.dart';
class DragTest extends StatelessWidget {
const DragTest({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(children: const <Widget>[
Draggable(
dragAnchorStrategy: pointerDragAnchorStrategy,
feedback: Icon(Icons.circle),
child: Text('DRAG ME'),
),
SizedBox(height: 700),
Draggable(
dragAnchorStrategy: pointerDragAnchorStrategy,
feedback: Icon(Icons.circle),
child: Text('DRAG ME 2'),
),
]),
));
}
}
Even setting an offset manually, I have the exact same behavior:
dragAnchorStrategy:
(Draggable<Object> _, BuildContext __, Offset ___) =>
const Offset(0, 0),
For dragAnchorStrategy use the below offset:
Offset dragAnchorStrategy(
Draggable<Object> d, BuildContext context, Offset point) {
return Offset(d.feedbackOffset.dx + 50, d.feedbackOffset.dy + 50);
}
example widget:
Widget _buildItem({
required String item,
}) {
return Draggable<String>(
data: item,
dragAnchorStrategy: dragAnchorStrategy,
feedback: Icon(Icons.circle),
child: Text(item),
);
}
Full example:
import 'dart:math' as math;
import 'package:flutter/material.dart';
class ExampleDragTarget extends StatefulWidget {
const ExampleDragTarget({super.key});
#override
ExampleDragTargetState createState() => ExampleDragTargetState();
}
class ExampleDragTargetState extends State<ExampleDragTarget> {
Color _color = Colors.grey;
void _handleAccept(Color data) {
setState(() {
_color = data;
});
}
#override
Widget build(BuildContext context) {
return DragTarget<Color>(
onAccept: _handleAccept,
builder: (BuildContext context, List<Color?> data, List<dynamic> rejectedData) {
return Container(
height: 100.0,
margin: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: data.isEmpty ? _color : Colors.grey.shade200,
border: Border.all(
width: 3.0,
color: data.isEmpty ? Colors.white : Colors.blue,
),
),
);
},
);
}
}
class Dot extends StatefulWidget {
const Dot({ super.key, this.color, this.size, this.child, this.tappable = false });
final Color? color;
final double? size;
final Widget? child;
final bool tappable;
#override
DotState createState() => DotState();
}
class DotState extends State<Dot> {
int taps = 0;
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: widget.tappable ? () { setState(() { taps += 1; }); } : null,
child: Container(
width: widget.size,
height: widget.size,
decoration: BoxDecoration(
color: widget.color,
border: Border.all(width: taps.toDouble()),
shape: BoxShape.circle,
),
child: widget.child,
),
);
}
}
class ExampleDragSource extends StatelessWidget {
const ExampleDragSource({
super.key,
this.color,
this.heavy = false,
this.under = true,
this.child,
});
final Color? color;
final bool heavy;
final bool under;
final Widget? child;
static const double kDotSize = 50.0;
static const double kHeavyMultiplier = 1.5;
static const double kFingerSize = 50.0;
#override
Widget build(BuildContext context) {
double size = kDotSize;
if (heavy) {
size *= kHeavyMultiplier;
}
final Widget contents = DefaultTextStyle(
style: Theme.of(context).textTheme.bodyMedium!,
textAlign: TextAlign.center,
child: Dot(
color: color,
size: size,
child: Center(child: child),
),
);
Widget feedback = Opacity(
opacity: 0.75,
child: contents,
);
Offset feedbackOffset;
DragAnchorStrategy dragAnchorStrategy;
if (!under) {
feedback = Transform(
transform: Matrix4.identity()
..translate(-size / 2.0, -(size / 2.0 + kFingerSize)),
child: feedback,
);
feedbackOffset = const Offset(0.0, -kFingerSize);
dragAnchorStrategy = (Draggable<Object> draggable,
BuildContext context, Offset position) {
return Offset.zero;
};
} else {
feedbackOffset = Offset.zero;
dragAnchorStrategy = childDragAnchorStrategy;
}
if (heavy) {
return LongPressDraggable<Color>(
data: color,
feedback: feedback,
feedbackOffset: feedbackOffset,
dragAnchorStrategy: dragAnchorStrategy,
child: contents,
);
} else {
return Draggable<Color>(
data: color,
feedback: feedback,
feedbackOffset: feedbackOffset,
dragAnchorStrategy: dragAnchorStrategy,
child: contents,
);
}
}
}
class DashOutlineCirclePainter extends CustomPainter {
const DashOutlineCirclePainter();
static const int segments = 17;
static const double deltaTheta = math.pi * 2 / segments; // radians
static const double segmentArc = deltaTheta / 2.0; // radians
static const double startOffset = 1.0; // radians
#override
void paint(Canvas canvas, Size size) {
final double radius = size.shortestSide / 2.0;
final Paint paint = Paint()
..color = const Color(0xFF000000)
..style = PaintingStyle.stroke
..strokeWidth = radius / 10.0;
final Path path = Path();
final Rect box = Offset.zero & size;
for (double theta = 0.0; theta < math.pi * 2.0; theta += deltaTheta) {
path.addArc(box, theta + startOffset, segmentArc);
}
canvas.drawPath(path, paint);
}
#override
bool shouldRepaint(DashOutlineCirclePainter oldDelegate) => false;
}
class MovableBall extends StatelessWidget {
const MovableBall(this.position, this.ballPosition, this.callback, {super.key});
final int position;
final int ballPosition;
final ValueChanged<int> callback;
static final GlobalKey kBallKey = GlobalKey();
static const double kBallSize = 50.0;
#override
Widget build(BuildContext context) {
final Widget ball = DefaultTextStyle(
style: Theme.of(context).primaryTextTheme.bodyMedium!,
textAlign: TextAlign.center,
child: Dot(
key: kBallKey,
color: Colors.blue.shade700,
size: kBallSize,
tappable: true,
child: const Center(child: Text('BALL')),
),
);
const Widget dashedBall = SizedBox(
width: kBallSize,
height: kBallSize,
child: CustomPaint(
painter: DashOutlineCirclePainter()
),
);
if (position == ballPosition) {
return Draggable<bool>(
data: true,
childWhenDragging: dashedBall,
feedback: ball,
maxSimultaneousDrags: 1,
child: ball,
);
} else {
return DragTarget<bool>(
onAccept: (bool data) { callback(position); },
builder: (BuildContext context, List<bool?> accepted, List<dynamic> rejected) {
return dashedBall;
},
);
}
}
}
class DragAndDropApp extends StatefulWidget {
const DragAndDropApp({super.key});
#override
DragAndDropAppState createState() => DragAndDropAppState();
}
class DragAndDropAppState extends State<DragAndDropApp> {
int position = 1;
void moveBall(int newPosition) {
setState(() { position = newPosition; });
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Drag and Drop Flutter Demo'),
),
body: Column(
children: <Widget>[
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
ExampleDragSource(
color: Colors.yellow.shade300,
child: const Text('under'),
),
ExampleDragSource(
color: Colors.green.shade300,
under: false,
heavy: true,
child: const Text('long-press above'),
),
ExampleDragSource(
color: Colors.indigo.shade300,
under: false,
child: const Text('above'),
),
],
),
),
Expanded(
child: Row(
children: const <Widget>[
Expanded(child: ExampleDragTarget()),
Expanded(child: ExampleDragTarget()),
Expanded(child: ExampleDragTarget()),
Expanded(child: ExampleDragTarget()),
],
),
),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
MovableBall(1, position, moveBall),
MovableBall(2, position, moveBall),
MovableBall(3, position, moveBall),
],
),
),
],
),
);
}
}
use it:
void main() {
runApp(const MaterialApp(
title: 'Drag and Drop Flutter Demo',
home: DragAndDropApp(),
));
}

how to perform drag operation on widget with Gesturedetector flutter

I want to drag and drop my custom widget with gesture detector. It is showing x- direction and y- direction values but not dragging to anywhere on screen.
Here is my code:
layoutpage:
SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Stack(
children: _layoutProvider.tables
.map(
(SectionTable sTable) => Positioned(
top: sTable.top,
left: sTable.left,
child: LayoutWidget(
width: sTable.width,
height: sTable.height,
type: sTable.type.index,
name: sTable.name,
left: sTable.left,
top: sTable.top,
rotate: sTable.rotate,
color: sTable.order != null
? Colors.green
: Colors.grey,
seats: sTable.seats,
),
),
)
.toList()),
),
LayoutWidget:
class LayoutWidget extends StatefulWidget {
late double width;
late double height;
late double left;
late double top;
LayoutWidget({
Key? key,
required this.width,
required this.height,
required this.left,
required this.top,
}) : super(key: key);
#override
State<StatefulWidget> createState() => _LayoutWidgetState();
}
class _LayoutWidgetState extends State<LayoutWidget> {
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
RotationTransition(
turns: widget.type == 0
? const AlwaysStoppedAnimation(0)
: AlwaysStoppedAnimation(rotationValue / 360),
child: GestureDetector(
onPanUpdate: (details) {
widget.top = widget.top+ details.delta.dy;
widget.left = widget.left+ details.delta.dx;
setState(() {
});
},
onTap: () {
setState(() {
showMenu = !showMenu;
});
},
child: myWidget()
}
Can anyone help why i am unable to drag on screen. Thanks.
I hope you you can get Idea from this code. In this code you can drag Container anywhere in the Screen and set it. And also check this Gesture Detector Overview for Gesture Detector detail.
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
class GestureDetectorPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.grey[100],
width: double.infinity,
height: double.infinity,
child: MainContent(),
),
);
}
}
class MainContent extends StatefulWidget {
#override
_MainContentState createState() => _MainContentState();
}
class _MainContentState extends State<MainContent> {
GlobalKey key = GlobalKey();
String dragDirection = '';
String startDXPoint = '50';
String startDYPoint = '50';
String dXPoint;
String dYPoint;
String velocity;
#override
Widget build(BuildContext context) {
return GestureDetector(
onHorizontalDragStart: _onHorizontalDragStartHandler,
onVerticalDragStart: _onVerticalDragStartHandler,
onHorizontalDragUpdate: _onDragUpdateHandler,
onVerticalDragUpdate: _onDragUpdateHandler,
onHorizontalDragEnd: _onDragEnd,
onVerticalDragEnd: _onDragEnd,
dragStartBehavior: DragStartBehavior.start, // default
behavior: HitTestBehavior.translucent,
child: Stack(
children: [
Positioned(
left: double.parse(this.startDXPoint),
top: double.parse(this.startDYPoint),
child: Container(
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(100)
),
child: Center(
child: Padding(
padding: const EdgeInsets.all(20),
child: Text('Draggable', style: TextStyle(fontSize: 14, color: Colors.white),),
),
),
)
),
],
),
);
}
void _onHorizontalDragStartHandler(DragStartDetails details) {
setState(() {
this.dragDirection = "HORIZONTAL";
this.startDXPoint = '${details.globalPosition.dx.floorToDouble()}';
this.startDYPoint = '${details.globalPosition.dy.floorToDouble()}';
});
}
/// Track starting point of a vertical gesture
void _onVerticalDragStartHandler(DragStartDetails details) {
setState(() {
this.dragDirection = "VERTICAL";
this.startDXPoint = '${details.globalPosition.dx.floorToDouble()}';
this.startDYPoint = '${details.globalPosition.dy.floorToDouble()}';
});
}
void _onDragUpdateHandler(DragUpdateDetails details) {
setState(() {
this.dragDirection = "UPDATING";
this.startDXPoint = '${details.globalPosition.dx.floorToDouble()}';
this.startDYPoint = '${details.globalPosition.dy.floorToDouble()}';
});
}
/// Track current point of a gesture
void _onHorizontalDragUpdateHandler(DragUpdateDetails details) {
setState(() {
this.dragDirection = "HORIZONTAL UPDATING";
this.dXPoint = '${details.globalPosition.dx.floorToDouble()}';
this.dYPoint = '${details.globalPosition.dy.floorToDouble()}';
this.velocity = '';
});
}
/// Track current point of a gesture
void _onVerticalDragUpdateHandler(DragUpdateDetails details) {
setState(() {
this.dragDirection = "VERTICAL UPDATING";
this.dXPoint = '${details.globalPosition.dx.floorToDouble()}';
this.dYPoint = '${details.globalPosition.dy.floorToDouble()}';
this.velocity = '';
});
}
/// What should be done at the end of the gesture ?
void _onDragEnd(DragEndDetails details) {
double result = details.velocity.pixelsPerSecond.dx.abs().floorToDouble();
setState(() {
this.velocity = '$result';
});
}
}
You can use the Draggable widget instead. Please try this
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const MyStatefulWidget(),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int acceptedData = 0;
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Draggable<int>(
// Data is the value this Draggable stores.
data: 10,
feedback: Container(
color: Colors.deepOrange,
height: 100,
width: 100,
child: const Icon(Icons.directions_run),
),
childWhenDragging: Container(
height: 100.0,
width: 100.0,
color: Colors.pinkAccent,
child: const Center(
child: Text('Child When Dragging'),
),
),
child: Container(
height: 100.0,
width: 100.0,
color: Colors.lightGreenAccent,
child: const Center(
child: Text('Draggable'),
),
),
),
DragTarget<int>(
builder: (
BuildContext context,
List<dynamic> accepted,
List<dynamic> rejected,
) {
return Container(
height: 100.0,
width: 100.0,
color: Colors.cyan,
child: Center(
child: Text('Value is updated to: $acceptedData'),
),
);
},
onAccept: (int data) {
setState(() {
acceptedData += data;
});
},
),
],
);
}
}

After Rotate Widget, GestureDetector not working on Stack

The widget cannot be touched or changed the position of the widget on the purple mark when I change the rotation of the widget.
and will be touched successfully when the rotation returns to 0 degrees or before change rotation.
working fine when not rotating
this is my code
import 'package:flutter/material.dart';
import 'dart:math' as math;
class View_Test_Design extends StatefulWidget {
#override
_View_Test_DesignState createState() => _View_Test_DesignState();
}
class _View_Test_DesignState extends State<View_Test_Design> {
double x = 100;
double y = 100;
double w = 200;
double h = 50;
double r=0; // Not Working fine when change to another number
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Rotate|Pos|Drag"),
),
body: Stack(
children: <Widget>[
Positioned(
left: x,
top: y,
child: GestureDetector(
onPanUpdate: (details) {
setState(() {
x = x + details.delta.dx;
y = y + details.delta.dy;
});
},
child: Transform.rotate(
angle: math.pi * r / 180,
child:
Container(width: w, height: h, color: Colors.red))))
],
),
);
}
}
any solution why this is not working?
This is because the Transform widget translates a widget outside the bounds of a parent widget. So you need to make the parent widget bigger.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Rotate|Pos|Drag"),
),
body: Stack(
overflow: Overflow.visible,
children: <Widget>[
Positioned(
left: x,
top: y,
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: GestureDetector(
onPanUpdate: (details) {
setState(() {
x = x + details.delta.dx;
y = y + details.delta.dy;
});
},
child: Transform.rotate(
angle: math.pi * r / 180,
child:
Container(width: w, height: h, color: Colors.red))))
],
),
);
}
Answer is from here https://github.com/flutter/flutter/issues/27587
Had a similar problem, this is my solution.
https://stackoverflow.com/a/74165192/12098106
import 'package:flutter/material.dart';
// -------------------------------------------------------------------
// THE ITEM TO BE DRAWN
// -------------------------------------------------------------------
class DrawContainer {
Color color;
Offset offset;
double width;
double height;
double scale;
double angle;
late double _baseScaleFactor;
late double _baseAngleFactor;
DrawContainer(this.color, this.offset, this.width, this.height, this.scale,
this.angle) {
onScaleStart();
}
onScaleStart() {
_baseScaleFactor = scale;
_baseAngleFactor = angle;
}
onScaleUpdate(double scaleNew) =>
scale = (_baseScaleFactor * scaleNew).clamp(0.5, 5);
onRotateUpdate(double angleNew) => angle = _baseAngleFactor + angleNew;
}
// -------------------------------------------------------------------
// APP
// -------------------------------------------------------------------
void main() {
runApp(const MaterialApp(home: GestureTest()));
}
class GestureTest extends StatefulWidget {
const GestureTest({Key? key}) : super(key: key);
#override
// ignore: library_private_types_in_public_api
_GestureTestState createState() => _GestureTestState();
}
// -------------------------------------------------------------------
// APP STATE
// -------------------------------------------------------------------
class _GestureTestState extends State<GestureTest> {
final List<DrawContainer> containers = [
DrawContainer(Colors.red, const Offset(50, 50), 100, 100, 1.0, 0.0),
DrawContainer(Colors.yellow, const Offset(100, 100), 200, 100, 1.0, 0.0),
DrawContainer(Colors.green, const Offset(150, 150), 50, 100, 1.0, 0.0),
];
void onGestureStart(DrawContainer e) => e.onScaleStart();
onGestureUpdate(DrawContainer e, ScaleUpdateDetails d) {
e.offset = e.offset + d.focalPointDelta;
if (d.rotation != 0.0) e.onRotateUpdate(d.rotation);
if (d.scale != 1.0) e.onScaleUpdate(d.scale);
setState(() {}); // redraw
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: SizedBox(
height: double.infinity,
width: double.infinity,
child: Stack(
children: [
...containers.map((e) {
return GestureDetector(
onScaleStart: (details) {
// detect two fingers to reset internal factors
if (details.pointerCount == 2) {
onGestureStart(e);
}
},
onScaleUpdate: (details) => onGestureUpdate(e, details),
child: DrawWidget(e));
}).toList(),
],
),
),
));
}
}
// -------------------------------------------------------------------
// POSITION, ROTATE AND SCALE THE WIDGET
// -------------------------------------------------------------------
class DrawWidget extends StatelessWidget {
final DrawContainer e;
const DrawWidget(this.e, {Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Stack(
children: [
Positioned(
left: e.offset.dx,
top: e.offset.dy,
child: Transform.rotate(
angle: e.angle,
child: Transform.scale(
scale: e.scale,
child: Container(
height: e.width,
width: e.height,
color: e.color,
),
),
),
),
],
);
}
}
Use DeferredPointer widget to detect gestures outside the bounds of a parent.

Flutter: How to move, rotate and zoom the container?

I want to make a container which can be dragged around, zoom and rotate. I am able to achieve a zoom. Below is my code:
//variable declaration
double _scale = 1.0;
double _previousScale;
var yOffset = 400.0;
var xOffset = 50.0;
var rotation = 0.0;
var lastRotation = 0.0;
//build method
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Center(
child: GestureDetector(
onScaleStart: (scaleDetails) {
_previousScale = _scale;
print(' scaleStarts = ${scaleDetails.focalPoint}');
},
onScaleUpdate: (scaleUpdates){
//ScaleUpdateDetails
rotation += lastRotation - scaleUpdates.rotation;
lastRotation = scaleUpdates.rotation;
print("lastRotation = $lastRotation");
print(' scaleUpdates = ${scaleUpdates.scale} rotation = ${scaleUpdates.rotation}');
setState(() => _scale = _previousScale * scaleUpdates.scale);
},
onScaleEnd: (scaleEndDetails) {
_previousScale = null;
print(' scaleEnds = ${scaleEndDetails.velocity}');
},
child:
Transform(
transform: Matrix4.diagonal3( Vector3(_scale, _scale, _scale))..rotateZ(rotation * math.pi/180.0),
alignment: FractionalOffset.center,
child: Container(
height: 200.0,
width: 200.0,
color: Colors.red,
),
)
,
),
),
),
);
}
Currently, there is no rotation and I can't move the container around.
use Matrix Gesture Detector package1 package, here you have the basic sample:
MatrixGestureDetector(
onMatrixUpdate: (m, tm, sm, rm) {
setState(() {
matrix = n;
});
},
child: Transform(
transform: matrix,
child: ....
),
),
for more sample code refer to example folder that contains 6 demos
You can use RotatedBox widget (for rotation) along with InteractiveViewer widget(for zoom in and zoom out).
panEnabled property in InteractiveViewer widget used for moving the container
Scaffold(
backgroundColor: Colors.black,
body: Center(
child: RotatedBox(
quarterTurns: 1,
child: InteractiveViewer(
boundaryMargin: EdgeInsets.zero,
minScale: 1,
maxScale: 4,
child: Container(
height: 200,
width: 200,
color: Colors.blue,
),
),
),
),
),
You can also use photo_viewer instead of matrix_gesture_detector. More info in this answer: https://stackoverflow.com/a/62426232/2942294
i had same issue. the problem is solved by Matrix gesture detetctor package. but i have solved this by another case:- the whole code is here.
// #dart=2.9
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class ContainerList {
double height;
double width;
double scale;
double rotation;
double xPosition;
double yPosition;
ContainerList({
this.height,
this.rotation,
this.scale,
this.width,
this.xPosition,
this.yPosition,
});
}
class HomePage extends StatefulWidget {
const HomePage({Key key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<ContainerList> list = [];
Offset _initPos;
Offset _currentPos = Offset(0, 0);
double _currentScale;
double _currentRotation;
Size screen;
#override
void initState() {
screen = Size(400, 500);
list.add(ContainerList(
height: 200.0,
width: 200.0,
rotation: 0.0,
scale: 1.0,
xPosition: 0.1,
yPosition: 0.1,
));
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
height: 500.0,
color: Colors.blue.withOpacity(0.8),
width: double.infinity,
child: Stack(
children: list.map((value) {
return GestureDetector(
onScaleStart: (details) {
if (value == null) return;
_initPos = details.focalPoint;
_currentPos = Offset(value.xPosition, value.yPosition);
_currentScale = value.scale;
_currentRotation = value.rotation;
},
onScaleUpdate: (details) {
if (value == null) return;
final delta = details.focalPoint - _initPos;
final left = (delta.dx / screen.width) + _currentPos.dx;
final top = (delta.dy / screen.height) + _currentPos.dy;
setState(() {
value.xPosition = Offset(left, top).dx;
value.yPosition = Offset(left, top).dy;
value.rotation = details.rotation + _currentRotation;
value.scale = details.scale * _currentScale;
});
},
child: Stack(
children: [
Positioned(
left: value.xPosition * screen.width,
top: value.yPosition * screen.height,
child: Transform.scale(
scale: value.scale,
child: Transform.rotate(
angle: value.rotation,
child: Container(
height: value.height,
width: value.width,
child: FittedBox(
fit: BoxFit.fill,
child: Listener(
onPointerDown: (details) {
// if (_inAction) return;
// _inAction = true;
// _activeItem = val;
_initPos = details.position;
_currentPos =
Offset(value.xPosition, value.yPosition);
_currentScale = value.scale;
_currentRotation = value.rotation;
},
onPointerUp: (details) {
// _inAction = false;
},
child: Container(
height: value.height,
width: value.width,
color: Colors.red,
),
// child: Image.network(value.name),
),
),
),
),
),
)
],
),
);
}).toList(),
),
),
);
}
}
so now i can khow the rotation angle value and scale value also. in matrix gesture detector package you can not khow about this values.
Had the same problem, here is my solution:
https://stackoverflow.com/a/74165192/12098106
import 'package:flutter/material.dart';
// -------------------------------------------------------------------
// THE ITEM TO BE DRAWN
// -------------------------------------------------------------------
class DrawContainer {
Color color;
Offset offset;
double width;
double height;
double scale;
double angle;
late double _baseScaleFactor;
late double _baseAngleFactor;
DrawContainer(this.color, this.offset, this.width, this.height, this.scale,
this.angle) {
onScaleStart();
}
onScaleStart() {
_baseScaleFactor = scale;
_baseAngleFactor = angle;
}
onScaleUpdate(double scaleNew) =>
scale = (_baseScaleFactor * scaleNew).clamp(0.5, 5);
onRotateUpdate(double angleNew) => angle = _baseAngleFactor + angleNew;
}
// -------------------------------------------------------------------
// APP
// -------------------------------------------------------------------
void main() {
runApp(const MaterialApp(home: GestureTest()));
}
class GestureTest extends StatefulWidget {
const GestureTest({Key? key}) : super(key: key);
#override
// ignore: library_private_types_in_public_api
_GestureTestState createState() => _GestureTestState();
}
// -------------------------------------------------------------------
// APP STATE
// -------------------------------------------------------------------
class _GestureTestState extends State<GestureTest> {
final List<DrawContainer> containers = [
DrawContainer(Colors.red, const Offset(50, 50), 100, 100, 1.0, 0.0),
DrawContainer(Colors.yellow, const Offset(100, 100), 200, 100, 1.0, 0.0),
DrawContainer(Colors.green, const Offset(150, 150), 50, 100, 1.0, 0.0),
];
void onGestureStart(DrawContainer e) => e.onScaleStart();
onGestureUpdate(DrawContainer e, ScaleUpdateDetails d) {
e.offset = e.offset + d.focalPointDelta;
if (d.rotation != 0.0) e.onRotateUpdate(d.rotation);
if (d.scale != 1.0) e.onScaleUpdate(d.scale);
setState(() {}); // redraw
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: SizedBox(
height: double.infinity,
width: double.infinity,
child: Stack(
children: [
...containers.map((e) {
return GestureDetector(
onScaleStart: (details) {
// detect two fingers to reset internal factors
if (details.pointerCount == 2) {
onGestureStart(e);
}
},
onScaleUpdate: (details) => onGestureUpdate(e, details),
child: DrawWidget(e));
}).toList(),
],
),
),
));
}
}
// -------------------------------------------------------------------
// POSITION, ROTATE AND SCALE THE WIDGET
// -------------------------------------------------------------------
class DrawWidget extends StatelessWidget {
final DrawContainer e;
const DrawWidget(this.e, {Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Stack(
children: [
Positioned(
left: e.offset.dx,
top: e.offset.dy,
child: Transform.rotate(
angle: e.angle,
child: Transform.scale(
scale: e.scale,
child: Container(
height: e.width,
width: e.height,
color: e.color,
),
),
),
),
],
);
}
}