我有许多Draggables和DragTargets。在Draggable上,我指定了 child 和 feedback ,但是我也希望它更改当Draggable进入DragTarget时的外观。我看不到任何方法可以做到这一点。
每当我拖动Draggable时,我都会将其颜色更改为红色,但是,一旦它进入DragTarget,我想将Draggable颜色更新为绿色。
我知道DragTarget.OnWillAccept,只要Draggable进入DragTarget就会调用此方法,但是我只有 data 。我尝试用新颜色更新数据,然后调用setState,但这似乎不起作用。
关于如何获得这种行为的任何建议?
我想要类似以下回调Draggable.onEnteringDragTarget和的内容Draggable.onLeavingDragTarget。
Draggable.onEnteringDragTarget
Draggable.onLeavingDragTarget
我能想到的唯一方法是使用streambuilder和将承载可拖动对象是否在拖动目标上的信息的流。此代码提供了基本的解决方案。
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); }, ), ], ), ); } }
编辑:第一个示例不会同时处理多个拖动,这更干净一些。
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)); } }
编辑2:不使用流和streambuilder的解决方案。重要的是反馈小部件是有状态的。
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); } }