I am currently working on a ScreenView with features like draggable and resizable views with corners and sides like in the image above. The problem I have now is that I want to resize the view by touch gestures in the corners. Therefore, I thought of a Point which I add to a view on selection, which can be dragged to resize the selected view. Answer updated!!
Resizable-Widget ReactNative Demo: React Native PLUGIN example
Modified Workable Example:
import 'package:flutter/material.dart';
class ResizeWidget extends StatefulWidget {
@override
_ResizeWidgetState createState() => _ResizeWidgetState();
}
class _ResizeWidgetState extends State<ResizeWidget> {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.black,
body: Container(
// padding: EdgeInsets.only(top: 50),
child: ResizebleWidget(
child: Container(
padding: EdgeInsets.all(10),
child: Text(
'Waao!! you can really dance.',
style: TextStyle(
color: Colors.white,
fontStyle: FontStyle.italic,
fontSize: 18),
),
),
),
),
),
);
}
}
class ResizebleWidget extends StatefulWidget {
ResizebleWidget({this.child});
final Widget child;
@override
_ResizebleWidgetState createState() => _ResizebleWidgetState();
}
const ballDiameter = 10.0;
class _ResizebleWidgetState extends State<ResizebleWidget> {
double height = 100;
double width = 200;
bool isCorner = false;
double top = 0;
double left = 0;
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Positioned(
top: top,
left: left,
child: Container(
height: height,
width: width,
decoration: BoxDecoration(
color: Colors.blueGrey,
border: Border.all(
width: 2,
color: Colors.white70,
),
borderRadius: BorderRadius.circular(0.0),
),
// need tp check if draggable is done from corner or sides
child: isCorner
? FittedBox(
child: widget.child,
)
: Center(
child: widget.child,
),
),
),
// top left
Positioned(
top: top - ballDiameter / 2,
left: left - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var mid = (dx + dy) / 2;
var newHeight = height - 2 * mid;
var newWidth = width - 2 * mid;
setState(() {
isCorner = true;
height = newHeight > 0 ? newHeight : 0;
width = newWidth > 0 ? newWidth : 0;
top = top + mid;
left = left + mid;
});
},
handlerWidget: HandlerWidget.VERTICAL,
),
),
// top middle
Positioned(
top: top - ballDiameter / 2,
left: left + width / 2 - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var newHeight = height - dy;
setState(() {
isCorner = false;
height = newHeight > 0 ? newHeight : 0;
top = top + dy;
});
},
handlerWidget: HandlerWidget.HORIZONTAL,
),
),
// top right
Positioned(
top: top - ballDiameter / 2,
left: left + width - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var mid = (dx + (dy * -1)) / 2;
var newHeight = height + 2 * mid;
var newWidth = width + 2 * mid;
setState(() {
isCorner = true;
height = newHeight > 0 ? newHeight : 0;
width = newWidth > 0 ? newWidth : 0;
top = top - mid;
left = left - mid;
});
},
handlerWidget: HandlerWidget.VERTICAL,
),
),
// center right
Positioned(
top: top + height / 2 - ballDiameter / 2,
left: left + width - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var newWidth = width + dx;
setState(() {
isCorner = false;
width = newWidth > 0 ? newWidth : 0;
});
},
handlerWidget: HandlerWidget.HORIZONTAL,
),
),
// bottom right
Positioned(
top: top + height - ballDiameter / 2,
left: left + width - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var mid = (dx + dy) / 2;
var newHeight = height + 2 * mid;
var newWidth = width + 2 * mid;
setState(() {
isCorner = true;
height = newHeight > 0 ? newHeight : 0;
width = newWidth > 0 ? newWidth : 0;
top = top - mid;
left = left - mid;
});
},
handlerWidget: HandlerWidget.VERTICAL,
),
),
// bottom center
Positioned(
top: top + height - ballDiameter / 2,
left: left + width / 2 - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var newHeight = height + dy;
setState(() {
isCorner = false;
height = newHeight > 0 ? newHeight : 0;
});
},
handlerWidget: HandlerWidget.HORIZONTAL,
),
),
// bottom left
Positioned(
top: top + height - ballDiameter / 2,
left: left - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var mid = ((dx * -1) + dy) / 2;
var newHeight = height + 2 * mid;
var newWidth = width + 2 * mid;
setState(() {
isCorner = true;
height = newHeight > 0 ? newHeight : 0;
width = newWidth > 0 ? newWidth : 0;
top = top - mid;
left = left - mid;
});
},
handlerWidget: HandlerWidget.VERTICAL,
),
),
//left center
Positioned(
top: top + height / 2 - ballDiameter / 2,
left: left - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var newWidth = width - dx;
setState(() {
isCorner = false;
width = newWidth > 0 ? newWidth : 0;
left = left + dx;
});
},
handlerWidget: HandlerWidget.HORIZONTAL,
),
),
// center center
Positioned(
top: top + height / 2 - ballDiameter / 2,
left: left + width / 2 - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
setState(() {
isCorner = false;
top = top + dy;
left = left + dx;
});
},
handlerWidget: HandlerWidget.VERTICAL,
),
),
],
);
}
}
class ManipulatingBall extends StatefulWidget {
ManipulatingBall({Key key, this.onDrag, this.handlerWidget});
final Function onDrag;
final HandlerWidget handlerWidget;
@override
_ManipulatingBallState createState() => _ManipulatingBallState();
}
enum HandlerWidget { HORIZONTAL, VERTICAL }
class _ManipulatingBallState extends State<ManipulatingBall> {
double initX;
double initY;
_handleDrag(details) {
setState(() {
initX = details.globalPosition.dx;
initY = details.globalPosition.dy;
});
}
_handleUpdate(details) {
var dx = details.globalPosition.dx - initX;
var dy = details.globalPosition.dy - initY;
initX = details.globalPosition.dx;
initY = details.globalPosition.dy;
widget.onDrag(dx, dy);
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onPanStart: _handleDrag,
onPanUpdate: _handleUpdate,
child: Container(
width: ballDiameter,
height: ballDiameter,
decoration: BoxDecoration(
color: Colors.white,
shape: this.widget.handlerWidget == HandlerWidget.VERTICAL
? BoxShape.circle
: BoxShape.rectangle,
),
),
);
}
}
Output:
You can use LayoutBuilder to fetch the parent widget's dimension and adjust the children widgets' dimensions to fit. From your sample, I've added height and width parameter requirement to create calendarCarousel widgets. The height and width parameters used on the calendar widgets came from LayoutBuilder .
Draggable is a Flutter widget that you can drag or move around. As soon as the user click and starts dragging the Draggable widget, a new feedback widget appears and follows the user's finger or mouse pointer. When the user lifts the finger or mouse pointer, the feedback widget disappears.
Updated
I've made a simple prototype to show the idea.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Text Overflow Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: Demo(),
),
);
}
}
class Demo extends StatefulWidget {
@override
_DemoState createState() => _DemoState();
}
class _DemoState extends State<Demo> {
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(60),
child: ResizebleWidget(
child: Text(
'''I've just did simple prototype to show main idea.
1. Draw size handlers with container;
2. Use GestureDetector to get new variables of sizes
3. Refresh the main container size.''',
),
),
);
}
}
class ResizebleWidget extends StatefulWidget {
ResizebleWidget({this.child});
final Widget child;
@override
_ResizebleWidgetState createState() => _ResizebleWidgetState();
}
const ballDiameter = 30.0;
class _ResizebleWidgetState extends State<ResizebleWidget> {
double height = 400;
double width = 200;
double top = 0;
double left = 0;
void onDrag(double dx, double dy) {
var newHeight = height + dy;
var newWidth = width + dx;
setState(() {
height = newHeight > 0 ? newHeight : 0;
width = newWidth > 0 ? newWidth : 0;
});
}
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Positioned(
top: top,
left: left,
child: Container(
height: height,
width: width,
color: Colors.red[100],
child: widget.child,
),
),
// top left
Positioned(
top: top - ballDiameter / 2,
left: left - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var mid = (dx + dy) / 2;
var newHeight = height - 2 * mid;
var newWidth = width - 2 * mid;
setState(() {
height = newHeight > 0 ? newHeight : 0;
width = newWidth > 0 ? newWidth : 0;
top = top + mid;
left = left + mid;
});
},
),
),
// top middle
Positioned(
top: top - ballDiameter / 2,
left: left + width / 2 - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var newHeight = height - dy;
setState(() {
height = newHeight > 0 ? newHeight : 0;
top = top + dy;
});
},
),
),
// top right
Positioned(
top: top - ballDiameter / 2,
left: left + width - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var mid = (dx + (dy * -1)) / 2;
var newHeight = height + 2 * mid;
var newWidth = width + 2 * mid;
setState(() {
height = newHeight > 0 ? newHeight : 0;
width = newWidth > 0 ? newWidth : 0;
top = top - mid;
left = left - mid;
});
},
),
),
// center right
Positioned(
top: top + height / 2 - ballDiameter / 2,
left: left + width - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var newWidth = width + dx;
setState(() {
width = newWidth > 0 ? newWidth : 0;
});
},
),
),
// bottom right
Positioned(
top: top + height - ballDiameter / 2,
left: left + width - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var mid = (dx + dy) / 2;
var newHeight = height + 2 * mid;
var newWidth = width + 2 * mid;
setState(() {
height = newHeight > 0 ? newHeight : 0;
width = newWidth > 0 ? newWidth : 0;
top = top - mid;
left = left - mid;
});
},
),
),
// bottom center
Positioned(
top: top + height - ballDiameter / 2,
left: left + width / 2 - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var newHeight = height + dy;
setState(() {
height = newHeight > 0 ? newHeight : 0;
});
},
),
),
// bottom left
Positioned(
top: top + height - ballDiameter / 2,
left: left - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var mid = ((dx * -1) + dy) / 2;
var newHeight = height + 2 * mid;
var newWidth = width + 2 * mid;
setState(() {
height = newHeight > 0 ? newHeight : 0;
width = newWidth > 0 ? newWidth : 0;
top = top - mid;
left = left - mid;
});
},
),
),
//left center
Positioned(
top: top + height / 2 - ballDiameter / 2,
left: left - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var newWidth = width - dx;
setState(() {
width = newWidth > 0 ? newWidth : 0;
left = left + dx;
});
},
),
),
// center center
Positioned(
top: top + height / 2 - ballDiameter / 2,
left: left + width / 2 - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
setState(() {
top = top + dy;
left = left + dx;
});
},
),
),
],
);
}
}
class ManipulatingBall extends StatefulWidget {
ManipulatingBall({Key key, this.onDrag});
final Function onDrag;
@override
_ManipulatingBallState createState() => _ManipulatingBallState();
}
class _ManipulatingBallState extends State<ManipulatingBall> {
double initX;
double initY;
_handleDrag(details) {
setState(() {
initX = details.globalPosition.dx;
initY = details.globalPosition.dy;
});
}
_handleUpdate(details) {
var dx = details.globalPosition.dx - initX;
var dy = details.globalPosition.dy - initY;
initX = details.globalPosition.dx;
initY = details.globalPosition.dy;
widget.onDrag(dx, dy);
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onPanStart: _handleDrag,
onPanUpdate: _handleUpdate,
child: Container(
width: ballDiameter,
height: ballDiameter,
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.5),
shape: BoxShape.circle,
),
),
);
}
}
I did a discrete version (ie snaps every 50 units) of the code above if it helps anyone:
import 'package:flutter/material.dart';
class DiscreteResizableComponent extends StatefulWidget {
const DiscreteResizableComponent({Key key, this.child}):super(key:key);
final Widget child;
@override
_ResizebleWidgetState createState() => _ResizebleWidgetState();
}
const ballDiameter = 30.0;
const discreteStepSize = 50;
class _ResizebleWidgetState extends State<DiscreteResizableComponent> {
double height = 400;
double width = 200;
double top = 0;
double left = 0;
double cumulativeDy=0;
double cumulativeDx=0;
double cumulativeMid = 0;
void onDrag(double dx, double dy) {
var newHeight = height + dy;
var newWidth = width + dx;
setState(() {
height = newHeight > 0 ? newHeight : 0;
width = newWidth > 0 ? newWidth : 0;
});
}
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Positioned(
top: top,
left: left,
child: Container(
height: height,
width: width,
color: Colors.red[100],
child: widget.child,
),
),
// top left
Positioned(
top: top - ballDiameter / 2,
left: left - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var mid = (dx + dy) / 2;
cumulativeMid -= 2*mid;
if(cumulativeMid>=discreteStepSize)
{
setState(() {
var newHeight = height+discreteStepSize;
height = newHeight > 0 ? newHeight : 0;
var newWidth = width +discreteStepSize;
width = newWidth > 0 ? newWidth : 0;
cumulativeMid=0;
});
}
else if(cumulativeMid<=-discreteStepSize)
{
setState(() {
var newHeight = height - discreteStepSize;
height = newHeight > 0 ? newHeight : 0;
var newWidth = width - discreteStepSize;
width = newWidth > 0 ? newWidth : 0;
cumulativeMid=0;
});
}
},
),
),
// top middle
Positioned(
top: top - ballDiameter / 2,
left: left + width / 2 - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
cumulativeDy -= dy;
if(cumulativeDy>=discreteStepSize)
{
setState(() {
var newHeight = height+discreteStepSize;
height = newHeight > 0 ? newHeight : 0;
cumulativeDy=0;
});
}
else if(cumulativeDy<=-discreteStepSize)
{
setState(() {
var newHeight = height - discreteStepSize;
height = newHeight > 0 ? newHeight : 0;
cumulativeDy=0;
});
}
},
),
),
// top right
Positioned(
top: top - ballDiameter / 2,
left: left + width - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var mid = (dx + (dy * -1)) / 2;
cumulativeMid += 2*mid;
if(cumulativeMid>=discreteStepSize)
{
setState(() {
var newHeight = height+discreteStepSize;
height = newHeight > 0 ? newHeight : 0;
var newWidth = width +discreteStepSize;
width = newWidth > 0 ? newWidth : 0;
cumulativeMid=0;
});
}
else if(cumulativeMid<=-discreteStepSize)
{
setState(() {
var newHeight = height - discreteStepSize;
height = newHeight > 0 ? newHeight : 0;
var newWidth = width - discreteStepSize;
width = newWidth > 0 ? newWidth : 0;
cumulativeMid=0;
});
}
},
),
),
// center right
Positioned(
top: top + height / 2 - ballDiameter / 2,
left: left + width - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
cumulativeDx += dx;
if(cumulativeDx>=discreteStepSize)
{
setState(() {
var newWidth = width+discreteStepSize;
width = newWidth > 0 ? newWidth : 0;
cumulativeDx=0;
});
}
else if(cumulativeDx<=-discreteStepSize)
{
setState(() {
var newWidth = width-discreteStepSize;
width = newWidth > 0 ? newWidth : 0;
cumulativeDx=0;
});
}
},
),
),
// bottom right
Positioned(
top: top + height - ballDiameter / 2,
left: left + width - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var mid = (dx + dy) / 2;
cumulativeMid += 2*mid;
if(cumulativeMid>=discreteStepSize)
{
setState(() {
var newHeight = height+discreteStepSize;
height = newHeight > 0 ? newHeight : 0;
var newWidth = width +discreteStepSize;
width = newWidth > 0 ? newWidth : 0;
cumulativeMid=0;
});
}
else if(cumulativeMid<=-discreteStepSize)
{
setState(() {
var newHeight = height - discreteStepSize;
height = newHeight > 0 ? newHeight : 0;
var newWidth = width - discreteStepSize;
width = newWidth > 0 ? newWidth : 0;
cumulativeMid=0;
});
}
},
),
),
// bottom center
Positioned(
top: top + height - ballDiameter / 2,
left: left + width / 2 - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
cumulativeDy += dy;
if(cumulativeDy>=discreteStepSize)
{
setState(() {
var newHeight = height+discreteStepSize;
height = newHeight > 0 ? newHeight : 0;
cumulativeDy=0;
});
}
else if(cumulativeDy<=-discreteStepSize)
{
setState(() {
var newHeight = height-discreteStepSize;
height = newHeight > 0 ? newHeight : 0;
cumulativeDy=0;
});
}
},
),
),
// bottom left
Positioned(
top: top + height - ballDiameter / 2,
left: left - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var mid = ((dx * -1) + dy) / 2;
cumulativeMid += 2*mid;
if(cumulativeMid>=discreteStepSize)
{
setState(() {
var newHeight = height+discreteStepSize;
height = newHeight > 0 ? newHeight : 0;
var newWidth = width +discreteStepSize;
width = newWidth > 0 ? newWidth : 0;
cumulativeMid=0;
});
}
else if(cumulativeMid<=-discreteStepSize)
{
setState(() {
var newHeight = height - discreteStepSize;
height = newHeight > 0 ? newHeight : 0;
var newWidth = width - discreteStepSize;
width = newWidth > 0 ? newWidth : 0;
cumulativeMid=0;
});
}
},
),
),
//left center
Positioned(
top: top + height / 2 - ballDiameter / 2,
left: left - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
cumulativeDx -= dx;
if(cumulativeDx>=discreteStepSize)
{
setState(() {
var newWidth = width+discreteStepSize;
width = newWidth > 0 ? newWidth : 0;
cumulativeDx=0;
});
}
else if(cumulativeDx<=-discreteStepSize)
{
setState(() {
var newWidth = width-discreteStepSize;
width = newWidth > 0 ? newWidth : 0;
cumulativeDx=0;
});
}
},
),
),
],
);
}
}
class ManipulatingBall extends StatefulWidget {
ManipulatingBall({Key key, this.onDrag});
final Function onDrag;
@override
_ManipulatingBallState createState() => _ManipulatingBallState();
}
class _ManipulatingBallState extends State<ManipulatingBall> {
double initX;
double initY;
_handleDrag(details) {
setState(() {
initX = details.globalPosition.dx;
initY = details.globalPosition.dy;
});
}
_handleUpdate(details) {
var dx = details.globalPosition.dx - initX;
var dy = details.globalPosition.dy - initY;
initX = details.globalPosition.dx;
initY = details.globalPosition.dy;
widget.onDrag(dx, dy);
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onPanStart: _handleDrag,
onPanUpdate: _handleUpdate,
child: Container(
width: ballDiameter,
height: ballDiameter,
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.5),
shape: BoxShape.circle,
),
),
);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With