Fabrizio
Fabrizio

Reputation: 1074

Programmatically creating widgets during the performlayout function of a render object in flutter?

I am trying to build a custom version of a column (call it CustomColumn) that lays out the children lazily, similar to what a ListView would do but with more control on how exactly they are laid out. The most basic point I am stuck on is: How can I create a widget while in the performLayout phase of the CustomColumn. I am struggling to find a minimal example on how to do that. The only other widget I could find that does something like this is ListView but I could not create a minimal version of that.

During layout, I am trying to check the available space and, only if needed create a child (using an IndexedWidgetBuilder) and lay that out. I suppose that there should a functionality like 'adoptChild' or 'discardChild' to manage the child widget's lifecycle, but I could not find the correct API for that.

I am specifically not interested in scrolling, debug overflow painting, or anything beyond just dynamic (or lazy) widget creation during layout (for now). What could be the minimal set of classes needed to achieve this behaviour?

Upvotes: 1

Views: 163

Answers (2)

Randal Schwartz
Randal Schwartz

Reputation: 44186

You want Boxy:

Boxy is a Flutter package created to overcome the limitations of built-in layout widgets, it provides utilities for flex, custom multi-child layouts, dynamic widget inflation, slivers, and more!

The package is ready for production use, it has excellent documentation, test coverage, and passes strict analysis.

Upvotes: 2

Rana sharjeel Ali
Rana sharjeel Ali

Reputation: 107

Without having a look at code it gonna be little hard to answer exact solution.I think you need to create 2 widgets first named as CustomColumn second you need LayoutWidget. some thing like that.

   class CustomLazyColumn extends RenderObjectWidget {
  final IndexedWidgetBuilder builder;
  final int itemCount;

  CustomLazyColumn({
    Key? key,
    required this.builder,
    required this.itemCount,
  }) : super(key: key);

  @override
  RenderObject createRenderObject(BuildContext context) {
    return CustomLazyColumnRenderObject(
      builder: builder,
      itemCount: itemCount,
    );
  }

  @override
  void updateRenderObject(
      BuildContext context, CustomLazyColumnRenderObject renderObject) {
    renderObject
      ..builder = builder
      ..itemCount = itemCount;
  }
}

after that use this rendering part

    class CustomLazyColumnRenderObject extends RenderBox
    with ContainerRenderObjectMixin<RenderBox, LazyColumnParentData> {
  IndexedWidgetBuilder builder;
  int itemCount;

  CustomLazyColumnRenderObject({
    required this.builder,
    required this.itemCount,
  });

  double _currentOffset = 0.0;

  @override
  void performLayout() {
    double usedHeight = 0.0;
    double maxWidth = 0.0;

    RenderBox? child = firstChild;

    for (int index = 0; index < itemCount; index++) {
      if (usedHeight >= constraints.maxHeight) break;

      if (child == null) {
        // Create the child lazily
        final newChild = builder(null, index).createRenderObject(context!);
        adoptChild(newChild);
        insert(newChild);
        child = newChild;
      }

      child.layout(constraints, parentUsesSize: true);

      final childParentData = child.parentData as LazyColumnParentData;
      childParentData.offset = Offset(0, _currentOffset);

      _currentOffset += child.size.height;
      usedHeight += child.size.height;
      maxWidth = maxWidth.max(child.size.width);

      child = childAfter(child);
    }

    size = constraints.constrain(Size(maxWidth, usedHeight));
  }

  @override
  void paint(PaintingContext context, Offset offset) {
    RenderBox? child = firstChild;
    while (child != null) {
      final childParentData = child.parentData as LazyColumnParentData;
      context.paintChild(child, offset + childParentData.offset);
      child = childAfter(child);
    }
  }

  @override
  void setupParentData(RenderBox child) {
    if (child.parentData is! LazyColumnParentData) {
      child.parentData = LazyColumnParentData();
    }
  }
}

class LazyColumnParentData extends ContainerBoxParentData {}

hopefully it gonna help

    CustomLazyColumn(
  itemCount: 1000,
  builder: (context, index) {
    return Text('Item $index', style: TextStyle(fontSize: 18));
  },
);

Upvotes: 0

Related Questions