dartflutterobservers

is it possible to observe metrics changes on components without WidgetsBindingObserver mixin using GlobalKeys?


I'm writing an overlay package for flutter for tutorial walk-through.

In order to know which holes to make in the overlay to make the widgets behind it fully visible, the function requires a list of global keys and I use them to fetch location and size.

While the overlay is still visible, the widgets can be animated and move to a different location. Is there a way to detect that in order to re-draw the overlay?

I know that if I add a mixin of WidgetsBindingObserver to the widgets, I can check that with didChangeMetrics(). The problem is that I don't want the user to modify is original code. Is there a way to observe widgets metrics without changing their code?

My code is at https://github.com/kfirufk/tuxin_tutorial_overlay


Solution

  • so thanks to @pskink amazing support I was able to properly resolve this issue.

    I already have the Global Keys of the widgets I want to monitor for changes in size and position, so I don't need to drill down through all the widgets in the app.

    so I'm adding a persistent frame callback and in there I check that that overlay being shown actually have holes to show widgets behind it, and if it doesת I use the _sizeVisitor() function to detect if any of the widgets position or size where modified, and if they where, I redraw the widget.

    again.. thank you so much @pskink!

    void _detectWidgetPositionNSizeChange() {
      var bindings = WidgetsFlutterBinding.ensureInitialized();
      bindings.addPersistentFrameCallback((d) {
        if (visibleOverlayPage != null &&
            (visibleOverlayPage.disabledVisibleWidgetsCount +
                    visibleOverlayPage.enabledVisibleWidgetsCount >
                0)) {
          for (final widgetGlobalKey in visibleOverlayPage.widgetsGlobalKeys) {
            if (_sizeVisitor(widgetGlobalKey)) {
              redrawCurrentOverlay();
              break;
            }
          }
        }
      });
    }
    
    bool _sizeVisitor(GlobalKey elementKey) {
      if (elementKey.currentContext != null) {
        final RenderBox renderBox = elementKey.currentContext.findRenderObject();
        bool isChanged = false;
        Rect newRect = renderBox.localToGlobal(Offset.zero) & renderBox.size;
        _rectMap.update(elementKey, (oldRect) {
          if (newRect != oldRect) {
            isChanged = true;
          }
          return newRect;
        }, ifAbsent: () => newRect);
        return isChanged;
      } else {
        return false;
      }
    }