-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: find widgets by click position
- Loading branch information
Showing
3 changed files
with
258 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import 'dart:collection'; | ||
|
||
import 'package:flutter/gestures.dart'; | ||
import 'package:flutter/material.dart'; | ||
import 'package:flutter/rendering.dart'; | ||
|
||
Element? _searchElementTreeUntil(Element root, bool Function(Element) predicate) { | ||
final elementsToSearch = Queue<Element>()..add(root); | ||
|
||
while (elementsToSearch.isNotEmpty) { | ||
final currentElement = elementsToSearch.removeFirst(); | ||
if (predicate.call(currentElement)) { | ||
return currentElement; | ||
} | ||
|
||
currentElement.visitChildElements(elementsToSearch.add); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
Element? _findClickedElement(Element root, HitTestResult hitTestResult) { | ||
final searchOrder = hitTestResult.path | ||
.map((e) => e.target) | ||
.whereType<RenderObject>() | ||
.toList(); | ||
|
||
var currentElement = root; | ||
while (searchOrder.isNotEmpty) { | ||
final currentRenderObject = searchOrder.removeLast(); | ||
final currentRenderObjectElement = _searchElementTreeUntil(currentElement, (element) { | ||
return element is RenderObjectElement && element.renderObject == currentRenderObject; | ||
}); | ||
|
||
if (currentRenderObjectElement == null) { | ||
return null; | ||
} | ||
|
||
currentElement = currentRenderObjectElement; | ||
} | ||
|
||
return currentElement; | ||
} | ||
|
||
Element? findElementAt(Element root, Offset position) { | ||
final contextObject = root.findRenderObject(); | ||
if (contextObject is! RenderBox) { | ||
return null; | ||
} | ||
|
||
final hits = BoxHitTestResult(); | ||
final hasHit = contextObject.hitTest(hits, position: position); | ||
|
||
if (!hasHit) { | ||
return null; | ||
} | ||
|
||
var element = _findClickedElement(root, hits); | ||
if (element == null) { | ||
return null; | ||
} | ||
|
||
element.visitAncestorElements((ancestor) { | ||
print(ancestor); | ||
return true; | ||
}); | ||
} |
176 changes: 176 additions & 0 deletions
176
uni/lib/view/plausible_click_listener/plausible_click_listener.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
import 'dart:ui'; | ||
|
||
import 'package:flutter/gestures.dart'; | ||
import 'package:flutter/material.dart'; | ||
import 'package:flutter/rendering.dart'; | ||
import 'package:path/path.dart'; | ||
import 'package:plausible_analytics/plausible_analytics.dart'; | ||
import 'package:uni/view/plausible_click_listener/find_element.dart'; | ||
|
||
class PlausibleClickListener extends StatelessWidget { | ||
|
||
const PlausibleClickListener({required this.child, required this.plausible, super.key}); | ||
|
||
final Plausible plausible; | ||
final Widget child; | ||
|
||
// Element? findElementAt(BuildContext context, Offset position) { | ||
// final contextObject = context.findRenderObject(); | ||
// if (contextObject is! RenderBox) { | ||
// print("RIP 2"); | ||
// return null; | ||
// } | ||
|
||
// final hits = BoxHitTestResult(); | ||
// final hasHit = contextObject.hitTest(hits, position: position); | ||
|
||
// if (!hasHit) { | ||
// return null; | ||
// } | ||
|
||
// T? tryCast<T>(dynamic value) { | ||
// return value is T ? value : null; | ||
// } | ||
|
||
// final renderObjects = hits.path | ||
// .map((e) => e.target) | ||
// .whereType<RenderObject>() | ||
// // .nonNulls | ||
// .toList(); | ||
|
||
// if (renderObjects.isEmpty) { | ||
// return null; | ||
// } | ||
|
||
// // Code inspired by https://github.com/flutter/flutter/blob/be3a4b37b3e9ab4e80d45b59bed53708b96d211f/packages/flutter/lib/src/widgets/framework.dart#L3005-L3021 | ||
// RenderObjectElement? descendUntilRenderObjectElement(BuildContext parent) { | ||
|
||
// Element? getSingleChildOf(BuildContext context) { | ||
// Element? result; | ||
// context.visitChildElements((child) { | ||
// assert(result == null, 'element has a single child'); | ||
// result = child; | ||
// }); | ||
|
||
// return result; | ||
// } | ||
|
||
// BuildContext? result = parent; | ||
// while (result is! RenderObjectElement?) { | ||
// print(result); | ||
// result = getSingleChildOf(result); | ||
// } | ||
|
||
// print(result); | ||
// return result; | ||
// } | ||
// print(renderObjects.reversed.toList()); | ||
|
||
// var currentElement = descendUntilRenderObjectElement(context); | ||
// if (currentElement?.renderObject != renderObjects.removeLast()) { | ||
// return null; | ||
// } | ||
|
||
// print("\nINITIAL"); | ||
|
||
// while (currentElement != null && renderObjects.isNotEmpty) { | ||
// print(""); | ||
// print(""); | ||
// print(""); | ||
// print('On $currentElement'); | ||
// print("Next two objects: ${renderObjects.reversed.take(2)}"); | ||
// RenderObjectElement? nextElement; | ||
// currentElement.visitChildElements((element) { | ||
// print("Searching in $element"); | ||
// if (nextElement != null) { | ||
// print("Ignoring..."); | ||
// return; | ||
// } | ||
// final descendedElement = descendUntilRenderObjectElement(element); | ||
// print("Using $descendedElement with ${descendedElement?.renderObject}. Looking for ${renderObjects.last}"); | ||
// print(descendedElement?.mounted); | ||
// if (descendedElement?.renderObject == renderObjects.last) { | ||
// nextElement = descendedElement; | ||
// print("Found!"); | ||
// } | ||
// }); | ||
|
||
// currentElement = nextElement; | ||
// renderObjects.removeLast(); | ||
// } | ||
|
||
// print("\nRESULT"); | ||
// print(currentElement); | ||
// print(renderObjects); | ||
|
||
// // for (final renderObject in renderObjects) { | ||
|
||
// // RenderObjectElement? selectedChild; | ||
// // currentElement.visitChildElements((element) { | ||
|
||
// // }) | ||
// // currentElement = descendUntilRenderObjectElement(context); | ||
// // } | ||
|
||
// print(renderObjects); | ||
|
||
|
||
|
||
// return null; | ||
// } | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
print(context.runtimeType); | ||
return Listener( | ||
behavior: HitTestBehavior.opaque, | ||
onPointerDown: (event) { | ||
if (!context.mounted) { | ||
return; | ||
} | ||
|
||
|
||
findElementAt(context as Element, event.position); | ||
|
||
// event. | ||
// context.(recursiveTraversal('')); | ||
/// TODO send: | ||
/// if success: | ||
/// - name | ||
/// - x, y | ||
/// if not sucess: | ||
/// - x, y | ||
// plausible.event( | ||
// name: 'pointerdown', | ||
// props: { | ||
// 'payload': { | ||
// 'x': event.position.dx.toString(), | ||
// 'y': event.position.dy.toString(), | ||
// 'localX': event.localPosition.dx.toString(), | ||
// 'localY': event.localPosition.dy.toString(), | ||
// 'timestamp': event.timeStamp.toString(), | ||
// 'pointer': event.pointer.toString(), | ||
// }.toString(), | ||
// } | ||
// ); | ||
}, | ||
onPointerUp: (event) { | ||
// plausible.event( | ||
// name: 'pointerup', | ||
// props: { | ||
// 'payload': { | ||
// 'x': event.position.dx.toString(), | ||
// 'y': event.position.dy.toString(), | ||
// 'localX': event.localPosition.dx.toString(), | ||
// 'localY': event.localPosition.dy.toString(), | ||
// 'timestamp': event.timeStamp.toString(), | ||
// 'pointer': event.pointer.toString(), | ||
// }.toString(), | ||
// } | ||
// ); | ||
}, | ||
child: child, | ||
); | ||
} | ||
} |