Skip to content

Instantly share code, notes, and snippets.

@slightfoot
Last active July 6, 2024 03:28
Show Gist options
  • Save slightfoot/a75d6c368f1b823b594d9f04bf667231 to your computer and use it in GitHub Desktop.
Save slightfoot/a75d6c368f1b823b594d9f04bf667231 to your computer and use it in GitHub Desktop.
Column Builder for Flutter. Can be used instead of a ListView with shrinkWrap.
class ColumnBuilder extends StatelessWidget {
final IndexedWidgetBuilder itemBuilder;
final MainAxisAlignment mainAxisAlignment;
final MainAxisSize mainAxisSize;
final CrossAxisAlignment crossAxisAlignment;
final TextDirection textDirection;
final VerticalDirection verticalDirection;
final int itemCount;
const ColumnBuilder({
Key key,
@required this.itemBuilder,
@required this.itemCount,
this.mainAxisAlignment: MainAxisAlignment.start,
this.mainAxisSize: MainAxisSize.max,
this.crossAxisAlignment: CrossAxisAlignment.center,
this.textDirection,
this.verticalDirection: VerticalDirection.down,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return new Column(
children: new List.generate(this.itemCount,
(index) => this.itemBuilder(context, index)).toList(),
);
}
}
@carman247
Copy link

OMG!!!!

@slightfoot
Copy link
Author

Anytime. Your welcome 🤗

@readytopark
Copy link

readytopark commented Feb 6, 2020

I changed the code to apply Column properties in build() method.

class ColumnBuilder extends StatelessWidget {
  final IndexedWidgetBuilder itemBuilder;
  final MainAxisAlignment mainAxisAlignment;
  final MainAxisSize mainAxisSize;
  final CrossAxisAlignment crossAxisAlignment;
  final VerticalDirection verticalDirection;
  final int itemCount;

  const ColumnBuilder({
    Key key,
    @required this.itemBuilder,
    @required this.itemCount,
    this.mainAxisAlignment: MainAxisAlignment.start,
    this.mainAxisSize: MainAxisSize.max,
    this.crossAxisAlignment: CrossAxisAlignment.center,
    this.verticalDirection: VerticalDirection.down,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return new Column(
      crossAxisAlignment: this.crossAxisAlignment,
      mainAxisSize: this.mainAxisSize,
      mainAxisAlignment: this.mainAxisAlignment,
      verticalDirection: this.verticalDirection,
      children:
          new List.generate(this.itemCount, (index) => this.itemBuilder(context, index)).toList(),
    );
  }
}

@miguelicass
Copy link

just what i needed for a CustomScrollView()
thank u

@alfianakbar
Copy link

Not all heroes wear capes. Just like you, sir!

@exavolt
Copy link

exavolt commented Jul 15, 2020

One of the benefits for a builder like in ListView.builder is that an item will only be built if it's about to be visible. That's it when there are 10 items in the viewport, the ListView will build 11 items and cache them (try logging in the initState and dispose in the item widgets to see how it works). When the user is scrolling down so that the eleventh is now visible, ListView will ask the builder to build the twelfth item, and prepare to dispose of the first item.

Looking and these codes, they don't replicate the same behavior, so that when you have 10k items, all will be rebuilt each time the widget (ColumnBuilder) is rebuild even though most of them are not visible. Try making a simple page with two pages, one for ListView.builder and the other with the above code, all with 10k items and compare them.

If it's not a problem to rebuild all the items each time, it can be done in such a simpler way:

// ...
Column(
  children: myItems.map<Widget>((item) {
    // do the build here
  }).toList(),
),
// ...

@samtheory
Copy link

Love You So much Bro!

@jcarboro94
Copy link

YOU ARE THE MAN!!!!!

@AgentDavid
Copy link

Excelent :D

@supermakra
Copy link

Works for me. Thank you!!!

@fnoceda
Copy link

fnoceda commented Mar 20, 2021

Awesome, Works for me. Thank you!!!

@xobe19
Copy link

xobe19 commented May 10, 2021

One of the benefits for a builder like in ListView.builder is that an item will only be built if it's about to be visible. That's it when there are 10 items in the viewport, the ListView will build 11 items and cache them (try logging in the initState and dispose in the item widgets to see how it works). When the user is scrolling down so that the eleventh is now visible, ListView will ask the builder to build the twelfth item, and prepare to dispose of the first item.

Looking and these codes, they don't replicate the same behavior, so that when you have 10k items, all will be rebuilt each time the widget (ColumnBuilder) is rebuild even though most of them are not visible. Try making a simple page with two pages, one for ListView.builder and the other with the above code, all with 10k items and compare them.

If it's not a problem to rebuild all the items each time, it can be done in such a simpler way:

// ...
Column(
  children: myItems.map<Widget>((item) {
    // do the build here
  }).toList(),
),
// ...

that is exactly what i am concerned about too

@Gene-Dana
Copy link

Hey !!! This is EXACTLY what I needed. Why not add Row.builder(.. to the framework??

@Gene-Dana
Copy link

Also, for those who read this in 2021 (and need null safety)

import 'package:flutter/material.dart';

class RowBuilder extends StatelessWidget {
  const RowBuilder({
    Key? key,
    @required this.itemBuilder,
    @required this.itemCount,
    this.mainAxisAlignment = MainAxisAlignment.start,
    this.mainAxisSize = MainAxisSize.max,
    this.crossAxisAlignment = CrossAxisAlignment.center,
    this.textDirection,
    this.verticalDirection = VerticalDirection.down,
  }) : super(key: key);

  final IndexedWidgetBuilder? itemBuilder;
  final MainAxisAlignment? mainAxisAlignment;
  final MainAxisSize? mainAxisSize;
  final CrossAxisAlignment? crossAxisAlignment;
  final TextDirection? textDirection;
  final VerticalDirection? verticalDirection;
  final int? itemCount;

  @override
  Widget build(BuildContext context) {
    return Row(
      children:
          List.generate(itemCount!, (index) => itemBuilder!(context, index))
              .toList(),
    );
  }
}

@batuhankrbb
Copy link

Null safety column builder;

    import 'package:flutter/material.dart';

    class ColumnBuilder extends StatelessWidget {
final IndexedWidgetBuilder itemBuilder;
final MainAxisAlignment mainAxisAlignment;
final MainAxisSize mainAxisSize;
final CrossAxisAlignment crossAxisAlignment;
final TextDirection? textDirection;
final VerticalDirection verticalDirection;
final int itemCount;

const ColumnBuilder({
	Key? key,
	required this.itemBuilder,
	required this.itemCount,
	this.mainAxisAlignment: MainAxisAlignment.start,
	this.mainAxisSize: MainAxisSize.max,
	this.crossAxisAlignment: CrossAxisAlignment.center,
	this.textDirection,
	this.verticalDirection: VerticalDirection.down,
}) : super(key: key);

@override
Widget build(BuildContext context) {
	return new Column(
		children: new List.generate(this.itemCount,
				(index) => this.itemBuilder(context, index)).toList(),
	);
}
      }

@ahndwon
Copy link

ahndwon commented Jun 1, 2022

The options are not passed on above codes.
Added passing arguments.

class ColumnBuilder extends StatelessWidget {
  final IndexedWidgetBuilder itemBuilder;
  final MainAxisAlignment mainAxisAlignment;
  final MainAxisSize mainAxisSize;
  final CrossAxisAlignment crossAxisAlignment;
  final TextDirection? textDirection;
  final VerticalDirection verticalDirection;
  final int itemCount;

  const ColumnBuilder({
    Key? key,
    required this.itemBuilder,
    required this.itemCount,
    this.mainAxisAlignment = MainAxisAlignment.start,
    this.mainAxisSize = MainAxisSize.max,
    this.crossAxisAlignment = CrossAxisAlignment.center,
    this.textDirection,
    this.verticalDirection = VerticalDirection.down,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisSize: mainAxisSize,
      mainAxisAlignment: mainAxisAlignment,
      crossAxisAlignment: crossAxisAlignment,
      textDirection: textDirection,
      verticalDirection: verticalDirection,
      children: List.generate(itemCount, (index) => itemBuilder(context, index))
          .toList(),
    );
  }
}


class RowBuilder extends StatelessWidget {
  final IndexedWidgetBuilder itemBuilder;
  final MainAxisAlignment mainAxisAlignment;
  final MainAxisSize mainAxisSize;
  final CrossAxisAlignment crossAxisAlignment;
  final TextDirection? textDirection;
  final VerticalDirection verticalDirection;
  final int itemCount;

  const RowBuilder({
    Key? key,
    required this.itemBuilder,
    required this.itemCount,
    this.mainAxisAlignment = MainAxisAlignment.start,
    this.mainAxisSize = MainAxisSize.max,
    this.crossAxisAlignment = CrossAxisAlignment.center,
    this.textDirection,
    this.verticalDirection = VerticalDirection.down,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisSize: mainAxisSize,
      mainAxisAlignment: mainAxisAlignment,
      crossAxisAlignment: crossAxisAlignment,
      textDirection: textDirection,
      verticalDirection: verticalDirection,
      children: List.generate(itemCount, (index) => itemBuilder(context, index))
          .toList(),
    );
  }
}

@Tyler-Petrov
Copy link

Works great, thanks for the code!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment