Skip to content

Commit a014222

Browse files
committed
added continous drag tracking example
1 parent bdca623 commit a014222

File tree

2 files changed

+217
-2
lines changed

2 files changed

+217
-2
lines changed
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
import 'dart:developer';
2+
3+
import 'package:flutter/material.dart';
4+
import 'package:flutter/rendering.dart';
5+
import 'package:vector_math/vector_math.dart' as vmath;
6+
7+
class ContinousDragGesturesDetection extends StatefulWidget {
8+
const ContinousDragGesturesDetection({super.key});
9+
10+
@override
11+
State<ContinousDragGesturesDetection> createState() => _ContinousDragGesturesDetectionState();
12+
}
13+
14+
class _ContinousDragGesturesDetectionState extends State<ContinousDragGesturesDetection> {
15+
final GlobalKey _parentKey = GlobalKey();
16+
17+
_detectTapedItem(PointerEvent event) {
18+
dragPoint = event.localPosition;
19+
20+
hitTestItems(event);
21+
}
22+
23+
void hitTestItems(PointerEvent event) {
24+
final RenderBox box = _parentKey.currentContext!.findRenderObject() as RenderBox;
25+
26+
final result = BoxHitTestResult();
27+
Offset local = box.globalToLocal(event.position);
28+
29+
if (box.hitTest(result, position: local)) {
30+
for (final hit in result.path) {
31+
final target = hit.target;
32+
33+
if (target is _RenderBoxItemDraggable) {
34+
setState(() {
35+
activeIndex = target.index;
36+
selectedIndex.add(target.index);
37+
log(selectedIndex.length.toString());
38+
});
39+
}
40+
}
41+
}
42+
}
43+
44+
int? activeIndex;
45+
46+
Offset dragPoint = Offset.zero;
47+
48+
// Calculates distance from center of the item to the drag point
49+
// and returns it as a value between 0 and 1 to use for various animations/effects
50+
double calculateDistanceValueFromDragPoint(GlobalKey key) {
51+
if (key.currentContext == null) {
52+
return 0;
53+
}
54+
if (_parentKey.currentContext == null) {
55+
return 0;
56+
}
57+
final RenderBox parent = _parentKey.currentContext!.findRenderObject() as RenderBox;
58+
final child = key.currentContext!.findRenderObject() as _RenderBoxItemDraggable;
59+
final childCenter = child.localToGlobal(Offset.zero) + Offset(child.size.width / 2, child.size.height / 2);
60+
final childPositionWithingParent = parent.globalToLocal(childCenter);
61+
62+
final dragPostionWithinParent = dragPoint;
63+
64+
final childPositionVector = vmath.Vector2(childPositionWithingParent.dx, childPositionWithingParent.dy);
65+
final dragPositionVector = vmath.Vector2(dragPostionWithinParent.dx, dragPostionWithinParent.dy);
66+
final childCenterToDragPointCircleRadius = childPositionVector.distanceTo(dragPositionVector);
67+
final childCoverageArea = child.size.width;
68+
final valueRatioToCenter = 1 - (childCenterToDragPointCircleRadius / childCoverageArea);
69+
if (child.index == 4) {
70+
log(valueRatioToCenter.toString());
71+
}
72+
73+
return valueRatioToCenter;
74+
}
75+
76+
List<GlobalKey> keys = List.generate(9, (index) => GlobalKey());
77+
final Set<int> selectedIndex = {};
78+
79+
@override
80+
Widget build(BuildContext context) {
81+
return Scaffold(
82+
backgroundColor: const Color(0xFF383838),
83+
body: Column(
84+
mainAxisAlignment: MainAxisAlignment.center,
85+
children: [
86+
const Text(
87+
"Selected Index",
88+
style: TextStyle(
89+
fontSize: 24,
90+
color: Colors.white,
91+
),
92+
),
93+
Text(
94+
activeIndex == null ? "None" : activeIndex.toString(),
95+
style: const TextStyle(
96+
fontSize: 24,
97+
color: Colors.white,
98+
),
99+
),
100+
const SizedBox(
101+
height: 30,
102+
),
103+
Listener(
104+
key: _parentKey,
105+
onPointerMove: _detectTapedItem,
106+
onPointerDown: _detectTapedItem,
107+
onPointerUp: _detectTapedItem,
108+
child: Padding(
109+
padding: const EdgeInsets.all(10),
110+
child: GridView.builder(
111+
shrinkWrap: true,
112+
itemCount: 9,
113+
padding: const EdgeInsets.all(0),
114+
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
115+
crossAxisCount: 3,
116+
crossAxisSpacing: 10,
117+
mainAxisSpacing: 10,
118+
childAspectRatio: 1,
119+
),
120+
physics: const NeverScrollableScrollPhysics(),
121+
itemBuilder: (context, index) {
122+
final isActive = activeIndex == index;
123+
final isSelected = selectedIndex.contains(index);
124+
final key = keys[index];
125+
final value = calculateDistanceValueFromDragPoint(key);
126+
return ItemDraggable(
127+
index: index,
128+
key: key,
129+
child: SizedBox(
130+
height: 90,
131+
child: Stack(
132+
children: [
133+
AnimatedContainer(
134+
duration: const Duration(
135+
milliseconds: 300,
136+
),
137+
foregroundDecoration: BoxDecoration(
138+
gradient: RadialGradient(
139+
colors: [
140+
Colors.transparent,
141+
const Color.fromARGB(255, 0, 0, 0).withOpacity(value.clamp(0, 1)),
142+
],
143+
stops: const [
144+
0,
145+
1.0,
146+
],
147+
),
148+
border: Border.all(
149+
color: const Color.fromARGB(255, 172, 230, 255),
150+
width: 2,
151+
),
152+
borderRadius: const BorderRadius.all(
153+
Radius.circular(
154+
8,
155+
),
156+
),
157+
),
158+
decoration: BoxDecoration(
159+
gradient: RadialGradient(
160+
colors: [
161+
isActive ? const Color(0xFFD3F2FF) : const Color.fromARGB(255, 64, 186, 239),
162+
const Color.fromARGB(255, 185, 227, 255),
163+
],
164+
stops: const [
165+
0,
166+
1,
167+
],
168+
),
169+
border: Border.all(
170+
color: const Color.fromARGB(255, 172, 230, 255),
171+
width: 2,
172+
),
173+
borderRadius: const BorderRadius.all(
174+
Radius.circular(
175+
10,
176+
),
177+
),
178+
),
179+
),
180+
],
181+
),
182+
),
183+
);
184+
},
185+
),
186+
),
187+
),
188+
],
189+
),
190+
);
191+
}
192+
}
193+
194+
class ItemDraggable extends SingleChildRenderObjectWidget {
195+
const ItemDraggable({
196+
super.key,
197+
super.child,
198+
required this.index,
199+
});
200+
final int index;
201+
202+
@override
203+
RenderObject createRenderObject(BuildContext context) {
204+
return _RenderBoxItemDraggable()..index = index;
205+
}
206+
207+
@override
208+
void updateRenderObject(BuildContext context, _RenderBoxItemDraggable renderObject) {
209+
renderObject.index = index;
210+
}
211+
}
212+
213+
class _RenderBoxItemDraggable extends RenderProxyBox {
214+
late int index;
215+
}

lib/main.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import 'package:flutter/material.dart';
2-
import 'package:flutter_animations/flutter_design_challenges/rolling_donuts/rolling_donuts.dart';
2+
import 'package:flutter_animations/flutter_gestures/continous_drag_selection/continous_drag_selection.dart';
33

44
void main() {
55
runApp(const MyApp());
@@ -18,7 +18,7 @@ class MyApp extends StatelessWidget {
1818
primarySwatch: Colors.blue,
1919
// textTheme: GoogleFonts.rubikTextTheme(Theme.of(context).textTheme),
2020
),
21-
home: const ScrollingDonuts(),
21+
home: const ContinousDragGesturesDetection(),
2222
);
2323
}
2424
}

0 commit comments

Comments
 (0)