Skip to content

Commit e21c6bf

Browse files
Drag and Drop (#62)
* Flutter 3.0.3 * Container click without ink passes coordinates in event * Drag and Drop implementation
1 parent c7333dc commit e21c6bf

File tree

10 files changed

+376
-5
lines changed

10 files changed

+376
-5
lines changed

.appveyor.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ for:
135135

136136
install:
137137
- brew install cocoapods
138-
- curl https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_3.0.2-stable.zip -o flutter_macos_stable.zip
138+
- curl https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_3.0.3-stable.zip -o flutter_macos_stable.zip
139139
- unzip -qq flutter_macos_stable.zip
140140
- export PATH="$PATH:`pwd`/flutter/bin"
141141
- flutter --version
@@ -204,7 +204,7 @@ for:
204204

205205
install:
206206
- export LANG=en_US.UTF-8
207-
- curl https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_3.0.2-stable.zip -o flutter_macos_stable.zip
207+
- curl https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_3.0.3-stable.zip -o flutter_macos_stable.zip
208208
- unzip -qq flutter_macos_stable.zip
209209
- export PATH="$PATH:`pwd`/flutter/bin"
210210
- flutter --version

ci/install_flutter.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
$distPath = "$env:TEMP\flutter_windows_stable.zip"
22

33
Write-Host "Downloading Flutter SDK..."
4-
(New-Object Net.WebClient).DownloadFile("https://storage.googleapis.com/flutter_infra_release/releases/stable/windows/flutter_windows_3.0.2-stable.zip", $distPath)
4+
(New-Object Net.WebClient).DownloadFile("https://storage.googleapis.com/flutter_infra_release/releases/stable/windows/flutter_windows_3.0.3-stable.zip", $distPath)
55

66
Write-Host "Unpacking Flutter SDK..."
77
7z x $distPath -o"$env:SystemDrive\" | Out-Null

client/lib/controls/container.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,13 @@ class ContainerControl extends StatelessWidget {
8181
cursor: SystemMouseCursors.click,
8282
child: GestureDetector(
8383
child: container,
84-
onTap: () {
84+
onTapDown: (details) {
8585
debugPrint("Container ${control.id} clicked!");
8686
ws.pageEventFromWeb(
8787
eventTarget: control.id,
8888
eventName: "click",
89-
eventData: control.attrs["data"] ?? "");
89+
eventData: control.attrString("data", "")! +
90+
"${details.localPosition.dx}:${details.localPosition.dy} ${details.globalPosition.dx}:${details.globalPosition.dy}");
9091
},
9192
),
9293
);

client/lib/controls/create_control.dart

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import 'clipboard.dart';
1414
import 'column.dart';
1515
import 'container.dart';
1616
import 'divider.dart';
17+
import 'drag_target.dart';
18+
import 'draggable.dart';
1719
import 'dropdown.dart';
1820
import 'elevated_button.dart';
1921
import 'floating_action_button.dart';
@@ -145,6 +147,18 @@ Widget createControl(Control? parent, String id, bool parentDisabled) {
145147
control: controlView.control,
146148
children: controlView.children,
147149
parentDisabled: parentDisabled);
150+
case ControlType.draggable:
151+
return DraggableControl(
152+
parent: parent,
153+
control: controlView.control,
154+
children: controlView.children,
155+
parentDisabled: parentDisabled);
156+
case ControlType.dragTarget:
157+
return DragTargetControl(
158+
parent: parent,
159+
control: controlView.control,
160+
children: controlView.children,
161+
parentDisabled: parentDisabled);
148162
case ControlType.card:
149163
return CardControl(
150164
parent: parent,
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import 'dart:convert';
2+
3+
import 'package:flet_view/controls/error.dart';
4+
import 'package:flutter/material.dart';
5+
6+
import '../models/control.dart';
7+
import '../web_socket_client.dart';
8+
import 'create_control.dart';
9+
10+
class DragTargetControl extends StatelessWidget {
11+
final Control? parent;
12+
final Control control;
13+
final List<Control> children;
14+
final bool parentDisabled;
15+
16+
const DragTargetControl(
17+
{Key? key,
18+
this.parent,
19+
required this.control,
20+
required this.children,
21+
required this.parentDisabled})
22+
: super(key: key);
23+
24+
@override
25+
Widget build(BuildContext context) {
26+
debugPrint("DragTarget build: ${control.id}");
27+
28+
var group = control.attrString("group", "");
29+
var contentCtrls =
30+
children.where((c) => c.name == "content" && c.isVisible);
31+
bool disabled = control.isDisabled || parentDisabled;
32+
33+
Widget? child = contentCtrls.isNotEmpty
34+
? createControl(control, contentCtrls.first.id, disabled)
35+
: null;
36+
37+
if (child == null) {
38+
return const ErrorControl("DragTarget should have content.");
39+
}
40+
41+
return DragTarget<String>(
42+
builder: (
43+
BuildContext context,
44+
List<dynamic> accepted,
45+
List<dynamic> rejected,
46+
) {
47+
debugPrint(
48+
"DragTarget.builder ${control.id}: accepted=${accepted.length}, rejected=${rejected.length}");
49+
return child;
50+
},
51+
onWillAccept: (data) {
52+
debugPrint("DragTarget.onAccept ${control.id}: $data");
53+
String srcGroup = "";
54+
if (data != null) {
55+
var jd = json.decode(data);
56+
srcGroup = jd["group"] as String;
57+
}
58+
var groupsEqual = srcGroup == group;
59+
ws.pageEventFromWeb(
60+
eventTarget: control.id,
61+
eventName: "will_accept",
62+
eventData:
63+
control.attrString("data", "")! + groupsEqual.toString());
64+
return groupsEqual;
65+
},
66+
onAccept: (data) {
67+
debugPrint("DragTarget.onAccept ${control.id}: $data");
68+
var jd = json.decode(data);
69+
var srcId = jd["id"] as String;
70+
ws.pageEventFromWeb(
71+
eventTarget: control.id,
72+
eventName: "accept",
73+
eventData: control.attrString("data", "")! + srcId);
74+
},
75+
// onAcceptWithDetails: (details) {
76+
// debugPrint(
77+
// "onAcceptWithDetails: ${details.data} ${details.offset}");
78+
// },
79+
onLeave: (data) {
80+
debugPrint("DragTarget.onLeave ${control.id}: $data");
81+
String srcId = "";
82+
if (data != null) {
83+
var jd = json.decode(data);
84+
srcId = jd["id"] as String;
85+
}
86+
ws.pageEventFromWeb(
87+
eventTarget: control.id,
88+
eventName: "leave",
89+
eventData: control.attrString("data", "")! + srcId);
90+
},
91+
);
92+
}
93+
}

client/lib/controls/draggable.dart

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import 'dart:convert';
2+
3+
import 'package:flet_view/controls/error.dart';
4+
import 'package:flutter/material.dart';
5+
6+
import '../models/control.dart';
7+
import 'create_control.dart';
8+
9+
class DraggableControl extends StatelessWidget {
10+
final Control? parent;
11+
final Control control;
12+
final List<Control> children;
13+
final bool parentDisabled;
14+
15+
const DraggableControl(
16+
{Key? key,
17+
this.parent,
18+
required this.control,
19+
required this.children,
20+
required this.parentDisabled})
21+
: super(key: key);
22+
23+
@override
24+
Widget build(BuildContext context) {
25+
debugPrint("DragTarget build: ${control.id}");
26+
27+
var group = control.attrString("group", "");
28+
var contentCtrls =
29+
children.where((c) => c.name == "content" && c.isVisible);
30+
var contentWhenDraggingCtrls =
31+
children.where((c) => c.name == "content_when_dragging" && c.isVisible);
32+
var contentFeedbackCtrls =
33+
children.where((c) => c.name == "content_feedback" && c.isVisible);
34+
bool disabled = control.isDisabled || parentDisabled;
35+
36+
Widget? child = contentCtrls.isNotEmpty
37+
? createControl(control, contentCtrls.first.id, disabled)
38+
: null;
39+
40+
Widget? childWhenDragging = contentWhenDraggingCtrls.isNotEmpty
41+
? createControl(control, contentWhenDraggingCtrls.first.id, disabled)
42+
: null;
43+
44+
Widget? childFeedback = contentFeedbackCtrls.isNotEmpty
45+
? createControl(control, contentFeedbackCtrls.first.id, disabled)
46+
: null;
47+
48+
if (child == null) {
49+
return const ErrorControl("Draggable should have content.");
50+
}
51+
52+
var data = json.encode({"id": control.id, "group": group});
53+
54+
return Draggable<String>(
55+
data: data,
56+
child: MouseRegion(
57+
cursor: SystemMouseCursors.grab,
58+
child: child,
59+
),
60+
childWhenDragging: childWhenDragging,
61+
feedback: MouseRegion(
62+
cursor: SystemMouseCursors.grabbing,
63+
child: childFeedback ?? Opacity(opacity: 0.5, child: child),
64+
),
65+
// dragAnchorStrategy: (d, context, offset) {
66+
// debugPrint("dragAnchorStrategy: ${offset.dx}, ${offset.dy}");
67+
// return offset;
68+
// }
69+
//feedbackOffset: const Offset(-30, -30),
70+
);
71+
}
72+
}

client/lib/models/control_type.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ enum ControlType {
99
column,
1010
container,
1111
divider,
12+
draggable,
13+
dragTarget,
1214
dropdown,
1315
dropdownOption,
1416
elevatedButton,

sdk/python/flet/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
from flet.container import Container
99
from flet.control import Control
1010
from flet.divider import Divider
11+
from flet.drag_target import DragTarget
12+
from flet.draggable import Draggable
1113
from flet.dropdown import Dropdown
1214
from flet.elevated_button import ElevatedButton
1315
from flet.filled_button import FilledButton

sdk/python/flet/drag_target.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
from beartype import beartype
2+
3+
from flet.control import Control
4+
from flet.ref import Ref
5+
6+
7+
class DragTarget(Control):
8+
def __init__(
9+
self,
10+
ref: Ref = None,
11+
disabled: bool = None,
12+
visible: bool = None,
13+
data: any = None,
14+
#
15+
# Specific
16+
#
17+
group: str = None,
18+
content: Control = None,
19+
on_will_accept=None,
20+
on_accept=None,
21+
on_leave=None,
22+
):
23+
24+
Control.__init__(
25+
self,
26+
ref=ref,
27+
disabled=disabled,
28+
visible=visible,
29+
data=data,
30+
)
31+
32+
self.__content: Control = None
33+
34+
self.group = group
35+
self.content = content
36+
self.on_will_accept = on_will_accept
37+
self.on_accept = on_accept
38+
self.on_leave = on_leave
39+
40+
def _get_control_name(self):
41+
return "dragtarget"
42+
43+
def _get_children(self):
44+
children = []
45+
if self.__content:
46+
self.__content._set_attr_internal("n", "content")
47+
children.append(self.__content)
48+
return children
49+
50+
# group
51+
@property
52+
def group(self):
53+
return self._get_attr("group")
54+
55+
@group.setter
56+
@beartype
57+
def group(self, value):
58+
self._set_attr("group", value)
59+
60+
# content
61+
@property
62+
def content(self):
63+
return self.__content
64+
65+
@content.setter
66+
def content(self, value):
67+
self.__content = value
68+
69+
# on_will_accept
70+
@property
71+
def on_will_accept(self):
72+
return self._get_event_handler("will_accept")
73+
74+
@on_will_accept.setter
75+
def on_will_accept(self, handler):
76+
self._add_event_handler("will_accept", handler)
77+
78+
# on_accept
79+
@property
80+
def on_accept(self):
81+
return self._get_event_handler("accept")
82+
83+
@on_accept.setter
84+
def on_accept(self, handler):
85+
self._add_event_handler("accept", handler)
86+
87+
# on_leave
88+
@property
89+
def on_leave(self):
90+
return self._get_event_handler("leave")
91+
92+
@on_leave.setter
93+
def on_leave(self, handler):
94+
self._add_event_handler("leave", handler)

0 commit comments

Comments
 (0)