import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

typedef OnWidgetSizeChanged = void Function(Size size);

/// See: https://stackoverflow.com/a/60868972
class MeasureSize extends SingleChildRenderObjectWidget {
  final OnWidgetSizeChanged onChange;

  const MeasureSize({
    Key? key,
    required this.onChange,
    required Widget child,
  }) : super(key: key, child: child);

  @override
  RenderObject createRenderObject(BuildContext context) {
    return _MeasureSizeRenderObject(onChange);
  }
}

class _MeasureSizeRenderObject extends RenderProxyBox {
  Size? oldSize;
  final OnWidgetSizeChanged onChange;

  _MeasureSizeRenderObject(this.onChange);

  @override
  void performLayout() {
    super.performLayout();

    var newSize = child?.size;
    if (newSize == null || oldSize == newSize) return;

    oldSize = newSize;
    WidgetsBinding.instance!.addPostFrameCallback((_) {
      onChange(newSize);
    });
  }
}

class SliverMeasureExtent extends SingleChildRenderObjectWidget {
  const SliverMeasureExtent({
    Key? key,
    required this.onChange,
    required Widget child,
  }) : super(key: key, child: child);

  @override
  RenderObject createRenderObject(BuildContext context) {
    return _SliverMeasureExtentRenderObject(onChange);
  }

  final void Function(double) onChange;
}

class _SliverMeasureExtentRenderObject extends RenderProxySliver {
  _SliverMeasureExtentRenderObject(this.onChange);

  @override
  void performLayout() {
    super.performLayout();

    var newExent = child?.geometry?.scrollExtent;
    if (newExent == null || _oldExtent == newExent) {
      return;
    }

    _oldExtent = newExent;
    WidgetsBinding.instance!.addPostFrameCallback((_) => onChange(newExent));
  }

  final void Function(double) onChange;

  double? _oldExtent;
}