Flutter DragTarget on hover - flutter

When exploring the Flutter's DragTarget class I could not find any mechanism to hook to the 'on hover event' if you will. Here is an example:
class DropZone extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Container(
height: 40,
color: Colors.green,
child: DragTarget(
builder: (context, List<int> candidateData, rejectedData) {
print("dragged");
return new Container(color: Colors.red,);
},
onWillAccept: (data) {
return true;
},),
);
}
}
I would like to change the color of the container when a Draggable object is hovered but not yet dropped. The builder method, in this case, only executes during the initial rendering and when the Draggable leaves the target. I suspect I have to do something in the onWillAccept method but not sure what. Does anybody have a solution?

here is the code for what I think you're looking for
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: App(),
),
);
}
}
class App extends StatefulWidget {
#override
_AppState createState() => _AppState();
}
class _AppState extends State<App> {
Color caughtColor = Colors.grey;
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
DragBox(Offset(0.0, 0.0), Colors.green),
Positioned(
left: 100,
bottom: 0.0,
child: DragTarget(onAccept: (Color color) {
caughtColor = color;
}, builder: (
BuildContext context,
List<dynamic> accepted,
List<dynamic> rejected,
) {
return Container(
width: 200,
height: 200,
color: accepted.isEmpty ? caughtColor : Colors.grey.shade200,
);
}))
],
);
}
}
class DragBox extends StatefulWidget {
final Offset initPos;
final Color itemColor;
DragBox(this.initPos, this.itemColor);
#override
_DragBoxState createState() => _DragBoxState();
}
class _DragBoxState extends State<DragBox> {
Offset position = Offset(0.0, 0.0);
#override
void initState() {
super.initState();
position = widget.initPos;
}
#override
Widget build(BuildContext context) {
return Positioned(
left: position.dx,
top: position.dy,
child: Draggable(
data: widget.itemColor,
child: Container(
width: 100,
height: 100,
color: widget.itemColor,
),
onDraggableCanceled: (velocity, offset) {
setState(() {
position = offset;
});
},
feedback: Container(
width: 120,
height: 120,
color: widget.itemColor.withOpacity(0.5),
),
),
);
}
}

You can use the candidateData parameter in the DragTarget.builder:
...
border: Border.all(
color: candidateData.isNotEmpty
? Colors.red
: Colors.black)),
The code above changes the border color to red upon hover.

You can make combination of onAccept, onMove and onLeave functions in DragTarget to achieve the result.
Here is how you do it:
// ...
bool isTargetGettingDroppedForDelete = false;
//...
DragTarget<String>(
builder: (
BuildContext context,
List<dynamic> accepted,
List<dynamic> rejected,
) {
return Container(
height: 100,
width: 100,
color: isTargetGettingDroppedForDelete ? Colors.red : Colors.green,
);
},
onWillAccept: (data) {
return data == Constants.draggable;
},
onAccept: (data) {
setState(() {
isTargetGettingDroppedForDelete = false;
});
},
onMove: (details) {
setState(() {
isTargetGettingDroppedForDelete = true;
});
},
onLeave: (data) {
setState(() {
isTargetGettingDroppedForDelete = false;
});
},
),

Related

flutter) How do I create a widgets in a tabbed position of screen?

I am developing a whiteboard app.
I would like to create text box widgets at the potision I want as shown in the image below.
May I know how to create a widget in the position(offset) where I've tapped?
As i comment before. I have an idea: using GestureDetector to detect Tap action and postion of Tap, then using Stack and Positioned to create an Editor like this.
* localPosition and globalPosition is differences, carefull.
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MaterialApp(home: MyApp()));
}
class MyApp extends StatefulWidget {
#override
State<MyApp> createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
var editorEnable = false;
var editorPosition = Offset.zero;
void closeEditor() {
print('editor closed');
setState(() {
editorEnable = false;
editorPosition = Offset.zero;
});
}
void newEditorAt(Offset position) {
print('editor created at $position');
setState(() {
editorEnable = true;
editorPosition = position;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: GestureDetector(
onTapUp: (details) {
if (editorEnable) {
closeEditor();
} else {
newEditorAt(details.localPosition);
}
},
child: Stack(
fit: StackFit.expand,
children: [
Container(color: Colors.grey),
if (editorEnable)
Positioned(
left: editorPosition.dx,
top: editorPosition.dy,
child: TextEditor(),
),
],
),
),
);
}
}
class TextEditor extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
width: 200,
height: 50,
child: TextField(
autofocus: true,
),
);
}
}
And here is result

How to call function "changeColor" in this case in Flutter?

I want to call changeColor() in the class ColorContainer when ListTile tapped.
How to achieve this?
Code:
class Parent extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return ParentState();
}
}
class ParentState extends State<Parent> {
Color selectedColor = Colors.grey;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
Container(
width: 300.0,
height: double.infinity,
child: ListView.builder(
itemCount: 5,
itemBuilder: (_, int i) {
return ListTile(
onTap: () {
// I want to call changeColor();
},
);
}
),
),
Expanded(
child: ColorContainer(selectedColor)
)
],
),
);
}
}
class ColorContainer extends StatefulWidget {
final Color color;
const ColorContainer(this.color);
#override
_ColorContainerState createState() => _ColorContainerState();
}
class _ColorContainerState extends State<ColorContainer> {
Color color;
void changeColor() {
color = widget.color;
setState(() {});
}
#override
Widget build(BuildContext context) {
return Container(
color: color,
);
}
}
Maybe this is work for you
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
Container(
width: 300.0,
height: double.infinity,
child: ListView.builder(
itemCount: 5,
itemBuilder: (_, int i) {
return ListTile(
title: Text(i.toString()),
onTap: () {
setState(() {
selectedColor = Colors.white;
});
},
);
}),
),
Expanded(child: ColorContainer(selectedColor))
],
),
);
}
}
class ColorContainer extends StatefulWidget {
final Color color;
const ColorContainer(this.color);
#override
_ColorContainerState createState() => _ColorContainerState();
}
class _ColorContainerState extends State<ColorContainer> {
#override
Widget build(BuildContext context) {
return Container(
color: widget.color,
);
}
}

How to have the drawer push the content instead of going on top of it?

I'm trying to build something similar to the slack app (see screenshot below) where the navigation drawer pushes the screen away instead of going on top.
I've been trying with the Drawer component without success. I've also looked at PageView but it seems that the children need to take 100% of the width.
Does someone have an idea of how to implement it?
EDIT
A similar result can be achieved with a Stack and AnimatedPositioned
class SlidingDrawer extends StatefulWidget {
final Widget drawer;
final Widget child;
final int swipeSensitivity;
final double drawerRatio;
final Color overlayColor;
final double overlayOpacity;
final int animationDuration;
final Curve animationCurve;
SlidingDrawer({
Key key,
#required this.drawer,
#required this.child,
this.swipeSensitivity = 25,
this.drawerRatio = 0.8,
this.overlayColor = Colors.black,
this.overlayOpacity = 0.5,
this.animationDuration = 500,
this.animationCurve = Curves.ease,
}) : super(key: key);
#override
_SlidingDrawerState createState() => _SlidingDrawerState();
}
class _SlidingDrawerState extends State<SlidingDrawer> {
bool _opened = false;
void open() {
setState(() {
_opened = true;
});
}
void close() {
setState(() {
_opened = false;
});
}
#override
Widget build(BuildContext context) {
final width = MediaQuery.of(context).size.width;
final height = MediaQuery.of(context).size.height;
final drawerWidth = width * widget.drawerRatio;
return GestureDetector(
onHorizontalDragUpdate: (details) {
if (details.delta.dx > widget.swipeSensitivity) {
open();
} else if (details.delta.dx < -widget.swipeSensitivity) {
close();
}
},
child: SizedBox(
width: width,
height: height,
child: Stack(
children: [
AnimatedPositioned(
width: drawerWidth,
height: height,
left: _opened ? 0 : -drawerWidth,
duration: Duration(milliseconds: widget.animationDuration),
curve: widget.animationCurve,
child: Container(
color: Colors.amber,
child: widget.drawer,
),
),
AnimatedPositioned(
height: height,
width: width,
left: _opened ? drawerWidth : 0,
duration: Duration(milliseconds: widget.animationDuration),
curve: widget.animationCurve,
child: Stack(
fit: StackFit.expand,
children: [
widget.child,
AnimatedSwitcher(
duration: Duration(milliseconds: widget.animationDuration),
switchInCurve: widget.animationCurve,
switchOutCurve: widget.animationCurve,
child: _opened
? GestureDetector(
onTap: () {
setState(() {
_opened = false;
});
},
child: Container(
color: widget.overlayColor.withOpacity(
widget.overlayOpacity,
),
),
)
: null,
)
],
),
),
],
),
),
);
}
}
ORIGINAL ANSWER
As pointed out by #Yadu in the comment
you could use Single child horizontal scroll view (with disabled scroll physics) with Scrollable.ensureVisible(context) to show the menu
using an horizontal scroll view is working.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _drawerOpened = false;
final drawerKey = new GlobalKey();
final mainKey = new GlobalKey();
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
physics: NeverScrollableScrollPhysics(),
scrollDirection: Axis.horizontal,
child: Row(
children: [
Container(
key: drawerKey,
color: Colors.green,
width: MediaQuery.of(context).size.width * 0.8,
),
SizedBox(
key: mainKey,
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Scaffold(
appBar: AppBar(
title: Text("My Page"),
leading: IconButton(
icon: Icon(Icons.menu),
onPressed: _toggleDrawer,
),
),
body: Container(
color: Colors.yellow,
width: MediaQuery.of(context).size.width,
),
),
),
],
),
);
}
void _toggleDrawer() {
setState(() {
_drawerOpened = !_drawerOpened;
});
if (_drawerOpened) {
Scrollable.ensureVisible(drawerKey.currentContext);
} else {
Scrollable.ensureVisible(mainKey.currentContext);
}
}
}

How to update Draggable child when entering DragTarget in Flutter?

I have a number of Draggables and DragTargets. On the Draggables I have specified child and feedback, however I also want it to change it's look when the Draggable enters the DragTarget. I can't see any way to do this.
Whenever I drag the Draggable around, I change it's color to be red, however as soon as it enters the DragTarget I want to update the Draggable color to green.
I am aware of DragTarget.OnWillAccept, this method is called whenever the Draggable enters the DragTarget, but I only have the data. I tried updating the data with new color and then calling setState, but that seemed not to work.
Any suggestions on how to get this behaviour?
I want something like the following callback Draggable.onEnteringDragTarget and Draggable.onLeavingDragTarget.
The only way I can think of is using a streambuilder and a stream that will carry the information of whether the draggable is on a drag target. This code provides a basic solution.
class _MyHomePageState extends State<MyHomePage> {
BehaviorSubject<bool> willAcceptStream;
#override
void initState() {
willAcceptStream = new BehaviorSubject<bool>();
willAcceptStream.add(false);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
children: <Widget>[
Container(
height: 400,
width: double.infinity,
child: Container(
width: 100,
height: 100,
child: Center(
child: Draggable(
feedback: StreamBuilder(
initialData: false,
stream: willAcceptStream,
builder: (context, snapshot) {
return Container(
height: 100,
width: 100,
color: snapshot.data ? Colors.green : Colors.red,
);
},
),
childWhenDragging: Container(),
child: Container(
height: 100,
width: 100,
color: this.willAcceptStream.value ?? false
? Colors.green
: Colors.blue,
),
onDraggableCanceled: (v, f) => setState(
() {
this.willAcceptStream.add(false);
},
),
),
),
),
),
DragTarget(
builder: (context, list, list2) {
return Container(
height: 50,
width: double.infinity,
color: Colors.blueGrey,
child: Center(child: Text('TARGET ZONE'),),
);
},
onWillAccept: (item) {
debugPrint('will accept');
this.willAcceptStream.add(true);
return true;
},
onLeave: (item) {
debugPrint('left the target');
this.willAcceptStream.add(false);
},
),
],
),
);
}
}
Edit: The first example doesn't handle multiple drags at the same time this on does and it's a little cleaner.
import 'package:rxdart/rxdart.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Draggable Test',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
MyDraggableController<String> draggableController;
#override
void initState() {
this.draggableController = new MyDraggableController<String>();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Draggable Test'),
),
body: Column(
children: <Widget>[
Container(
height: 400,
width: double.infinity,
child: Container(
width: 100,
height: 100,
child: Center(
child: Stack(
children: <Widget>[
Positioned(
left: 30,
top: 30,
child: MyDraggable<String>(draggableController, 'Test1'),
),
Positioned(
left: 230,
top: 230,
child: MyDraggable<String>(draggableController, 'Test2'),
)
],
),
),
),
),
DragTarget<String>(
builder: (context, list, list2) {
return Container(
height: 50,
width: double.infinity,
color: Colors.blueGrey,
child: Center(
child: Text('TARGET ZONE'),
),
);
},
onWillAccept: (item) {
debugPrint('draggable is on the target');
this.draggableController.onTarget(true, item);
return true;
},
onLeave: (item) {
debugPrint('draggable has left the target');
this.draggableController.onTarget(false, item);
},
),
],
),
);
}
}
class MyDraggable<T> extends StatefulWidget {
final MyDraggableController<T> controller;
final T data;
MyDraggable(this.controller, this.data);
#override
_MyDraggableState createState() =>
_MyDraggableState<T>(this.controller, this.data);
}
class _MyDraggableState<T> extends State<MyDraggable> {
BehaviorSubject<DraggableInfo<T>> willAcceptStream;
MyDraggableController<T> controller;
T data;
_MyDraggableState(this.controller, this.data);
#override
void initState() {
willAcceptStream = this.controller._isOnTarget;
willAcceptStream.add(new DraggableInfo<T>(false, this.data));
super.initState();
}
#override
Widget build(BuildContext context) {
return Draggable<T>(
data: this.data,
feedback: StreamBuilder<DraggableInfo<T>>(
initialData: DraggableInfo<T>(false, this.data),
stream: willAcceptStream,
builder: (context, snapshot) {
return Container(
height: 100,
width: 100,
color: snapshot.data.isOnTarget && snapshot.data.data == this.data ? Colors.green : Colors.red,
);
},
),
childWhenDragging: Container(),
child: Container(
height: 100,
width: 100,
color: (this.willAcceptStream.value.isOnTarget ?? this.willAcceptStream.value.data == this.data)
? Colors.green
: Colors.blue,
),
onDraggableCanceled: (v, f) => setState(
() {
this.willAcceptStream.add(DraggableInfo(false, null));
},
),
);
}
}
class DraggableInfo<T> {
bool isOnTarget;
T data;
DraggableInfo(this.isOnTarget, this.data);
}
class MyDraggableController<T> {
BehaviorSubject<DraggableInfo<T>> _isOnTarget;
MyDraggableController() {
this._isOnTarget = new BehaviorSubject<DraggableInfo<T>>();
}
void onTarget(bool onTarget, T data) {
_isOnTarget.add(new DraggableInfo(onTarget, data));
}
}
Edit 2: Solution without using streams and streambuilder. The important part is the feedback widget is stateful.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Draggable Test',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
MyDraggableController<String> draggableController;
#override
void initState() {
this.draggableController = new MyDraggableController<String>();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Draggable Test'),
),
body: Column(
children: <Widget>[
Container(
height: 400,
width: double.infinity,
child: Container(
width: 100,
height: 100,
child: Center(
child: Stack(
children: <Widget>[
Positioned(
left: 30,
top: 30,
child: MyDraggable<String>(
draggableController,
'Test1',
),
),
Positioned(
left: 230,
top: 230,
child: MyDraggable<String>(
draggableController,
'Test2',
),
),
],
),
),
),
),
DragTarget<String>(
builder: (context, list, list2) {
return Container(
height: 100,
width: double.infinity,
color: Colors.blueGrey,
child: Center(
child: Text('TARGET ZONE'),
),
);
},
onWillAccept: (item) {
debugPrint('draggable is on the target $item');
this.draggableController.onTarget(true, item);
return true;
},
onLeave: (item) {
debugPrint('draggable has left the target $item');
this.draggableController.onTarget(false, item);
},
),
],
),
);
}
}
class MyDraggable<T> extends StatefulWidget {
final MyDraggableController<T> controller;
final T data;
MyDraggable(this.controller, this.data, {Key key}) : super(key: key);
#override
_MyDraggableState createState() =>
_MyDraggableState<T>(this.controller, this.data);
}
class _MyDraggableState<T> extends State<MyDraggable> {
MyDraggableController<T> controller;
T data;
bool isOnTarget;
_MyDraggableState(this.controller, this.data);
FeedbackController feedbackController;
#override
void initState() {
feedbackController = new FeedbackController();
this.controller.subscribeToOnTargetCallback(onTargetCallbackHandler);
super.initState();
}
void onTargetCallbackHandler(bool t, T data) {
this.isOnTarget = t && data == this.data;
this.feedbackController.updateFeedback(this.isOnTarget);
}
#override
void dispose() {
this.controller.unSubscribeFromOnTargetCallback(onTargetCallbackHandler);
super.dispose();
}
#override
Widget build(BuildContext context) {
return Draggable<T>(
data: this.data,
feedback: FeedbackWidget(feedbackController),
childWhenDragging: Container(
height: 100,
width: 100,
color: Colors.blue[50],
),
child: Container(
height: 100,
width: 100,
color: (this.isOnTarget ?? false) ? Colors.green : Colors.blue,
),
onDraggableCanceled: (v, f) => setState(
() {
this.isOnTarget = false;
this.feedbackController.updateFeedback(this.isOnTarget);
},
),
);
}
}
class FeedbackController {
Function(bool) feedbackNeedsUpdateCallback;
void updateFeedback(bool isOnTarget) {
if (feedbackNeedsUpdateCallback != null) {
feedbackNeedsUpdateCallback(isOnTarget);
}
}
}
class FeedbackWidget extends StatefulWidget {
final FeedbackController controller;
FeedbackWidget(this.controller);
#override
_FeedbackWidgetState createState() => _FeedbackWidgetState();
}
class _FeedbackWidgetState extends State<FeedbackWidget> {
bool isOnTarget;
#override
void initState() {
this.isOnTarget = false;
this.widget.controller.feedbackNeedsUpdateCallback = feedbackNeedsUpdateCallbackHandler;
super.initState();
}
void feedbackNeedsUpdateCallbackHandler(bool t) {
setState(() {
this.isOnTarget = t;
});
}
#override
Widget build(BuildContext context) {
return Container(
height: 100,
width: 100,
color: this.isOnTarget ?? false ? Colors.green : Colors.red,
);
}
#override
void dispose() {
this.widget.controller.feedbackNeedsUpdateCallback = null;
super.dispose();
}
}
class DraggableInfo<T> {
bool isOnTarget;
T data;
DraggableInfo(this.isOnTarget, this.data);
}
class MyDraggableController<T> {
List<Function(bool, T)> _targetUpdateCallbacks = new List<Function(bool, T)>();
MyDraggableController();
void onTarget(bool onTarget, T data) {
if (_targetUpdateCallbacks != null) {
_targetUpdateCallbacks.forEach((f) => f(onTarget, data));
}
}
void subscribeToOnTargetCallback(Function(bool, T) f) {
_targetUpdateCallbacks.add(f);
}
void unSubscribeFromOnTargetCallback(Function(bool, T) f) {
_targetUpdateCallbacks.remove(f);
}
}

How to move a widget in the screen in Flutter

I am using flutter and i have a container with the shape of a circle using this code
new Container(
width: 50.0,
height: 50.0,
decoration: new BoxDecoration(
shape: BoxShape.circle)
I want to make this circle move on the screen like this
how can I do this?
Here it is:
import 'package:flutter/material.dart';
class SecondScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("Drag app"),
),
body: HomePage(),
),
);
}
}
class HomePage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _HomePageState();
}
}
class _HomePageState extends State<HomePage> {
double width = 100.0, height = 100.0;
Offset position ;
#override
void initState() {
super.initState();
position = Offset(0.0, height - 20);
}
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Positioned(
left: position.dx,
//top: position.dy - height + 20,
child: Draggable(
child: Container(
width: width,
height: height,
color: Colors.blue,
child: Center(child: Text("Drag", style: Theme.of(context).textTheme.headline,),),
),
feedback: Container(
child: Center(
child: Text("Drag", style: Theme.of(context).textTheme.headline,),),
color: Colors.red[800],
width: width,
height: height,
),
onDraggableCanceled: (Velocity velocity, Offset offset){
setState(() => position = offset);
},
),
),
],
);
}
}
What you are looking for is Draggable widget. You can then handle the translation using onDraggableCanceled which is passed and offset that you can be used to update the placement
onDraggableCanceled :(velocity,offset){
//update the position here
}
Update
After checking the image you will need "Drop me here" part to be a DragTarget that has a method onAccept which will handles the logic when you drag and drop your Draggable
First, wrap your Container inside the Stack with Positioned.
Then, use Pan Gesture to implement a Pan in your Container and use onPan... methods to handle Pan Gesture
Here is code:
Offset position;
#override
void initState() {
super.initState();
position = Offset(10, 10);
}
#override
Widget build(BuildContext context) {
double _width = MediaQuery.of(context).size.width;
double _height = _width * 9 / 16;
return GestureDetector(
onPanStart: (details) => _onPanStart(context, details),
onPanUpdate: (details) => _onPanUpdate(context, details, position),
onPanEnd: (details) => _onPanEnd(context, details),
onPanCancel: () => _onPanCancel(context),
child: SafeArea(
child: Stack(
children: <Widget>[
Positioned(
top: position.dy,
child: Container(
color: Colors.red,
width: _width,
height: _height,
),
),
],
),
),
);
}
void _onPanStart(BuildContext context, DragStartDetails details) {
print(details.globalPosition.dy);
}
void _onPanUpdate(BuildContext context, DragUpdateDetails details, Offset offset) {
setState(() {
position = details.globalPosition;
});
}
void _onPanEnd(BuildContext context, DragEndDetails details) {
print(details.velocity);
}
void _onPanCancel(BuildContext context) {
print("Pan canceled !!");
}
Hope this helps!
You can use Draggable class for dragging the item which you want to drag and for placing it or sticking it to somewhere on the screen you have to wrap that item with DragTarget class. In DragTarget class onAccept method is there where you can write the logic. You can also take a reference to my code here it is
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.indigo,
),
home: new MyHomePage(title: 'Flutter Demo Drag Box'),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(title),
),
body:
new DragGame(), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class DragGame extends StatefulWidget {
#override
_DragGameState createState() => new _DragGameState();
}
class _DragGameState extends State<DragGame> {
int boxNumberIsDragged;
#override
void initState() {
boxNumberIsDragged = null;
super.initState();
}
#override
Widget build(BuildContext context) {
return new Container(
constraints: BoxConstraints.expand(),
color: Colors.grey,
child: new Stack(
children: <Widget>[
buildDraggableBox(1, Colors.red, new Offset(30.0, 100.0)),
buildDraggableBox(2, Colors.yellow, new Offset(30.0, 200.0)),
buildDraggableBox(3, Colors.green, new Offset(30.0, 300.0)),
],
));
}
Widget buildDraggableBox(int boxNumber, Color color, Offset offset) {
return new Draggable(
maxSimultaneousDrags: boxNumberIsDragged == null || boxNumber == boxNumberIsDragged ? 1 : 0,
child: _buildBox(color, offset),
feedback: _buildBox(color, offset),
childWhenDragging: _buildBox(color, offset, onlyBorder: true),
onDragStarted: () {
setState((){
boxNumberIsDragged = boxNumber;
});
},
onDragCompleted: () {
setState((){
boxNumberIsDragged = null;
});
},
onDraggableCanceled: (_,__) {
setState((){
boxNumberIsDragged = null;
});
},
);
}
Widget _buildBox(Color color, Offset offset, {bool onlyBorder: false}) {
return new Container(
height: 50.0,
width: 50.0,
margin: EdgeInsets.only(left: offset.dx, top: offset.dy),
decoration: BoxDecoration(
color: !onlyBorder ? color : Colors.grey,
border: Border.all(color: color)),
);
}
}