abdulghani / masonry_grid

Flutter masonry grid implementation
https://pub.dev/packages/masonry_grid
MIT License
15 stars 2 forks source link

[bug] Widget rebuilt everytime a textfield is focused #2

Open robertsLando opened 4 years ago

robertsLando commented 4 years ago

Everytime I focus a text field inside a child of the grid the keyboard shows and hide quickly and all widgets are redrawn

I have used the same example provided in the readme with the customscrollview

andreaselia commented 3 years ago

The same issue when anything state related happens, e.g. with Flutter Redux, dispatching an action will cause a re-render.

andreaselia commented 3 years ago

@robertsLando I resolved this by changing the approach to match that of ListView.builder and using the IndexedWidgetBuilder to create the children widgets.

robertsLando commented 3 years ago

I had to do a similar trick to make this work

kawi15 commented 8 months ago

@andreaselia Hi, can you post a sample code with your solution? I will be really grateful

andreaselia commented 8 months ago

@andreaselia Hi, can you post a sample code with your solution? I will be really grateful

Sorry @kawi15, I no longer have access to the code and unfortunately don't remember what my comment above even referred to. Perhaps @robertsLando could help?

andreaselia commented 8 months ago

@kawi15 just as I wrote that my iCloud search came back with something (I assumed I had nothing in there, but I have a copy of the code, I think). Give it a try and let me know, I haven't used this in years.

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

class MasonryGrid extends StatefulWidget {
  MasonryGrid({
    this.column = 1,
    this.mainAxisSpacing = 0,
    this.crossAxisSpacing = 0,
    this.crossAxisAlignment = CrossAxisAlignment.stretch,
    this.staggered = false,
    required this.data,
    required this.itemBuilder,
  });

  final int column;
  final double mainAxisSpacing;
  final double crossAxisSpacing;
  final CrossAxisAlignment crossAxisAlignment;
  final bool staggered;
  final List data;
  final IndexedWidgetBuilder itemBuilder;

  @override
  _MasonryGrid createState() => _MasonryGrid();
}

class _MasonryGrid extends State<MasonryGrid> {
  int renderId = 0;
  late List<List<Widget>> columnItem;
  late List<GlobalKey> columnKey;

  @override
  void initState() {
    assert(widget.column >= 1, 'column should be at least 1.');
    assert(widget.mainAxisSpacing >= 0, 'mainAxisSpacing should be positive.');
    assert(widget.crossAxisSpacing >= 0, 'crossAxisSpacing should be positive.');

    columnItem = List.generate(widget.column, (i) => []);
    columnKey = List.generate(widget.column, (i) => GlobalKey());
    super.initState();
  }

  @override
  void didUpdateWidget(prev) {
    if (!listEquals(prev.data, widget.data) || prev.column != widget.column || prev.mainAxisSpacing != widget.mainAxisSpacing || prev.crossAxisSpacing != widget.crossAxisSpacing || prev.crossAxisAlignment != widget.crossAxisAlignment || prev.staggered != widget.staggered) {
      setState(() {
        renderId = 0;
        columnItem = List.generate(widget.column, (i) => []);
        columnKey = List.generate(widget.column, (i) => GlobalKey());
      });
    }

    super.didUpdateWidget(prev);
  }

  int getSmallestColumnId() {
    var smallestColumnId = 0;

    try {
      final renderColumn = List<RenderBox?>.generate(columnKey.length, (i) => columnKey[i].currentContext?.findRenderObject() as RenderBox?);
      final columnHeight = List<double>.generate(renderColumn.length, (i) => renderColumn[i]!.size.height);

      columnHeight.asMap().forEach((i, item) {
        if (columnHeight[i] < columnHeight[smallestColumnId]) {
          smallestColumnId = i;
        }
      });
    } catch (err) {
      // TODO: handle err.
    }

    return smallestColumnId;
  }

  void renderItemStaggered() {
    var columnId = getSmallestColumnId();

    columnItem[columnId].add(Padding(
      padding: EdgeInsets.only(bottom: widget.mainAxisSpacing),
      child: widget.itemBuilder(context, renderId),
    ));

    setState(() {
      renderId = renderId + 1;
    });
  }

  void renderItemInOrder() {
    for (var i = renderId; i < widget.data.length; i++) {
      columnItem[i % widget.column].add(Padding(
        padding: EdgeInsets.only(bottom: widget.mainAxisSpacing),
        child: widget.itemBuilder(context, i),
      ));
    }

    setState(() {
      renderId = widget.data.length;
    });
  }

  void renderChildren() {
    if (!widget.staggered && renderId < widget.data.length) {
      renderItemInOrder();
    } else if (widget.staggered && renderId < widget.data.length) {
      Future.microtask(() => renderItemStaggered());
    }
  }

  @override
  Widget build(BuildContext context) {
    renderChildren();

    final column = widget.crossAxisSpacing == 0
        ? List.generate(
            widget.column,
            (i) => Expanded(
              child: Column(
                key: columnKey[i],
                crossAxisAlignment: widget.crossAxisAlignment,
                children: columnItem[i],
              ),
            ),
          )
        : List.generate(
            widget.column + (widget.column - 1),
            (i) => i.isEven
                ? Expanded(
                    child: Column(
                      key: columnKey[(i / 2).floor()],
                      crossAxisAlignment: widget.crossAxisAlignment,
                      children: columnItem[(i / 2).floor()],
                    ),
                  )
                : SizedBox(
                    width: widget.crossAxisSpacing,
                  ),
          );

    return Row(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: column,
    );
  }
}
andreaselia commented 8 months ago

@andreaselia Hi, can you post a sample code with your solution? I will be really grateful

Hopefully the code above is the right one for this.

kawi15 commented 8 months ago

@andreaselia Yes, it's the right one, it's working, thank you

andreaselia commented 8 months ago

@andreaselia Yes, it's the right one, it's working, thank you

Good to hear! You're welcome!