Skip to content

Commit

Permalink
feat: find widgets by click position
Browse files Browse the repository at this point in the history
  • Loading branch information
limwa committed Nov 23, 2023
1 parent 64d0a83 commit 1b415e7
Show file tree
Hide file tree
Showing 3 changed files with 258 additions and 1 deletion.
16 changes: 15 additions & 1 deletion uni/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import 'package:uni/view/locale_notifier.dart';
import 'package:uni/view/locations/locations.dart';
import 'package:uni/view/login/login.dart';
import 'package:uni/view/navigation_service.dart';
import 'package:uni/view/plausible_click_listener/plausible_click_listener.dart';
import 'package:uni/view/restaurant/restaurant_page_view.dart';
import 'package:uni/view/schedule/schedule.dart';
import 'package:uni/view/theme.dart';
Expand Down Expand Up @@ -106,6 +107,8 @@ Future<void> main() async {
? Plausible(plausibleUrl, plausibleDomain)
: null;

plausible?.enabled = false;

if (plausible == null) {
Logger().w('Plausible is not enabled');
}
Expand Down Expand Up @@ -207,7 +210,8 @@ class ApplicationState extends State<Application> {
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
return Consumer2<ThemeNotifier, LocaleNotifier>(

final app = Consumer2<ThemeNotifier, LocaleNotifier>(
builder: (context, themeNotifier, localeNotifier, _) => MaterialApp(
title: 'uni',
navigatorKey: Application.navigatorKey,
Expand Down Expand Up @@ -295,5 +299,15 @@ class ApplicationState extends State<Application> {
},
),
);

final plausible = widget.plausible;
if (plausible == null) {
return app;
}

return PlausibleClickListener(
plausible: plausible,
child: app,
);
}
}
67 changes: 67 additions & 0 deletions uni/lib/view/plausible_click_listener/find_element.dart
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 uni/lib/view/plausible_click_listener/plausible_click_listener.dart
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,
);
}
}

0 comments on commit 1b415e7

Please sign in to comment.