How to update Draggable child when entering DragTarget in Flutter? - drag-and-drop

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);
}
}

Related

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;
});
},
),
],
);
}
}

AnimationBuilder with FutureBuilder - How to execute animation after future returns and builds?

I am making an api call to get some data and then I want to animate in (from the bottom) the widget that displays it. The code for each piece works separately but I can't seem to figure out how to execute the animation after the FutureBuilder has returned from its builder function? Where or how do I call cardController.forward()?
Widget mapCard(Area area) {
return Align(
alignment: Alignment.bottomCenter,
child: AnimatedBuilder(
builder: (BuildContext context, Widget child) {
return InkWell(
onTap: () {
Navigator.of(context).push(_createHeroRoute(area));
},
child: Container(
margin: cardMargin.value,
width: 300,
height: 150,
child: Hero(
tag: 'location',
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: 274,
height: 150,
child: Image.network(area.images[0].url,
fit: BoxFit.cover)),
Align(
alignment: Alignment.centerRight,
child: Container(
height: 150,
//30, 42, 65
// 412A1E
decoration: new BoxDecoration(
color: Color.fromARGB(255, 30, 42, 65),
),
child:
Icon(Icons.arrow_right, color: Colors.white))),
],
),
),
),
);
},
animation: cardController,
),
);
}
In the build stack:
FutureBuilder(
future: fArea,
builder: (context, AsyncSnapshot<Area> snap) {
if (snap.hasData) {
return mapCard(snap.data);
} else {
return Text('here');
}
})
You can copy paste run full code below
You can make MapCard a StatefulWidget and call cardController.forward in initState
#override
void initState() {
super.initState();
cardController =
AnimationController(duration: const Duration(seconds: 10), vsync: this);
animation = Tween<double>(begin: 0, end: -300.0).animate(cardController)
..addStatusListener((state) => print('$state'));
cardController.forward();
}
working demo
full code
import 'package:flutter/material.dart';
import 'dart:math' as math;
class MapCard extends StatefulWidget {
Area area;
MapCard(Area data, {Key key}) : super(key: key);
#override
_MapCardState createState() => _MapCardState();
}
class _MapCardState extends State<MapCard> with TickerProviderStateMixin {
AnimationController cardController;
Animation<double> animation;
#override
void initState() {
super.initState();
cardController =
AnimationController(duration: const Duration(seconds: 10), vsync: this);
animation = Tween<double>(begin: 0, end: -300.0).animate(cardController)
..addStatusListener((state) => print('$state'));
cardController.forward();
}
#override
void dispose() {
cardController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Align(
alignment: AlignmentDirectional.bottomCenter,
child: AnimatedBuilder(
animation: cardController,
child: InkWell(
onTap: () {
//Navigator.of(context).push(_createHeroRoute(area));
},
child: Container(
//margin: cardMargin.value,
width: 300,
height: 150,
child: Hero(
tag: 'location',
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
width: 274,
height: 150,
child: Image.network("https://picsum.photos/250?image=9",
fit: BoxFit.cover)),
Align(
alignment: Alignment.centerRight,
child: Container(
height: 150,
//30, 42, 65
// 412A1E
decoration: new BoxDecoration(
color: Color.fromARGB(255, 30, 42, 65),
),
child: Icon(Icons.arrow_right, color: Colors.white))),
],
),
),
),
),
builder: (BuildContext context, Widget child) {
return Transform.translate(
offset: Offset(0, animation.value),
child: child,
);
},
),
);
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class Area {
String data;
Area(this.data);
}
class _MyHomePageState extends State<MyHomePage> {
Future<Area> fArea;
Future<Area> getArea() async {
await Future.delayed(Duration(seconds: 3), () {});
return Future.value(Area("data"));
}
#override
void initState() {
fArea = getArea();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: FutureBuilder(
future: fArea,
builder: (context, AsyncSnapshot<Area> snap) {
switch (snap.connectionState) {
case ConnectionState.none:
return Text('none');
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
case ConnectionState.active:
return Text('');
case ConnectionState.done:
if (snap.hasData) {
return MapCard(snap.data);
} else {
return Center(child: CircularProgressIndicator());
}
}
}));
}
}

how Can i make this Single selection Flutter?

I have an Apps which is having a listview with the reaction button in a flutter . I want to make this when a user clicked any of this love icon then it's filled with red color.
enter image description here
enter image description here
Like this image but the problem is when I clicked one of this love icon all of the icons turned into red color but I only want to change the color of love of icon which one is Selected.
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(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool like;
#override
List<String> user = ['Dipto', 'Dipankar', "Sajib", 'Shanto', 'Pranto'];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ListView Demu'),
),
body: Center(
child: Container(
child: ListView.builder(
itemCount: user.length,
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.all(10),
height: 50,
width: MediaQuery.of(context).size.width * 0.8,
color: Colors.yellowAccent,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
user[index],
),
Positioned(
child: IconButton(
icon: _iconControl(like),
onPressed: () {
if (like == false) {
setState(() {
like = true;
_iconControl(like);
});
} else {
setState(() {
like = false;
_iconControl(like);
});
}
},
),
),
],
),
);
},
),
)),
);
}
_iconControl(bool like) {
if (like == false) {
return Icon(Icons.favorite_border);
} else {
return Icon(
Icons.favorite,
color: Colors.red,
);
}
}
}
I also try with using parameter but Its failed Like that :
child: IconButton(
icon: _iconControl(true),
onPressed: () {
if (false) {
setState(() {
_iconControl(true);
});
} else {
setState(() {
_iconControl(false);
});
}
},
),
Can you help me Please. Thanks in advance
You can create a modal class to manage the selection of your list
Just create a modal class and add a boolean variable to maintaining selection using. that boolean variable
SAMPLE CODE
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(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool like;
List<Modal> userList = List<Modal>();
#override
void initState() {
userList.add(Modal(name: 'Dipto', isSelected: false));
userList.add(Modal(name: 'Dipankar', isSelected: false));
userList.add(Modal(name: 'Sajib', isSelected: false));
userList.add(Modal(name: 'Shanto', isSelected: false));
userList.add(Modal(name: 'Pranto', isSelected: false));
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ListView Demu'),
),
body: Center(
child: Container(
child: ListView.builder(
itemCount: userList.length,
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.all(10),
height: 50,
width: MediaQuery
.of(context)
.size
.width * 0.8,
color: Colors.yellowAccent,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
userList[index].name,
),
Positioned(
child: IconButton(
icon: _iconControl( userList[index].isSelected),
onPressed: () {
setState(() {
userList.forEach((element) {
element.isSelected = false;
});
userList[index].isSelected = true;
});
},
),
),
],
),
);
},
),
)),
);
}
_iconControl(bool like) {
if (like == false) {
return Icon(Icons.favorite_border);
} else {
return Icon(
Icons.favorite,
color: Colors.red,
);
}
}
}
class Modal {
String name;
bool isSelected;
Modal({this.name, this.isSelected = false});
}

Flutter DragTarget on hover

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;
});
},
),

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)),
);
}
}