34

I'm trying to create a Horizontal scrolling listview.builder() with no pre-set height.

I've tried setting shrinkwrap to true and wrapping it inside an Expanded/Flexible.

The only way (that i have found) to currently achieve the desired effect is to wrap a row inside a singlechildscrollview inside a column, as per this answer (Flutter: Minimum height on horizontal list view).

The problem with that method is that there is no builder method to load dynamic data into the Cards inside the singlechildscrollview.

My question is how do i create a Horizontal listview that that generates the output by the row nested inside the singlechildscrollview (Flutter: Minimum height on horizontal list view) but with a builder method?

With Flexible

Scaffold(
  body: Container(
    child: Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        Flexible(
          child: ListView.builder(
            scrollDirection: Axis.horizontal,
            itemCount: 3,
            itemBuilder: (BuildContext context, int index) {
              return FeaturedCard();
            },
          ),
        ),
        Flexible(
          child: ListView.builder(
            shrinkWrap: true,
            itemCount: 10,
            itemBuilder: (BuildContext context, int index) {
              return FeaturedCard();
            },
          ),
        ),
      ],
    ),
  ),
)

Result: https://i.stack.imgur.com/XKiWo.jpg

With nested row inside singlechildscrollview (The method that works)

 Container(
  padding: EdgeInsets.only(top: 16, bottom: 8),
  child: Column(
    mainAxisSize: MainAxisSize.min,
    children: <Widget>[
      SingleChildScrollView(
        scrollDirection: Axis.horizontal,
        child: Row(
          children: <Widget>[
            FeaturedCard(),
            FeaturedCard(),
          ],
        ),
      ),
    ],
  ),
)

Result: https://i.stack.imgur.com/va3TY.jpg

Notice the added space inside the card when using flexible (this actually renders worse on different devices)

Christopher Moore
  • 15,626
  • 10
  • 42
  • 52
Mathiew Abbas
  • 823
  • 1
  • 8
  • 17

2 Answers2

23

Posting answer for OP who edited their answer into their question

Solved the problem by creating a custom builder method like so:

Widget _buildFeaturedCards(List<Product> product) {
  final cards = <Widget>[];
  Widget FeautredCards;

  if (product.length > 0) {
    for (int i = 0; i < product.length; i++) {
      cards.add(FeaturedCard(product[i]));
      print(product.length);
    }
    FeautredCards = Container(
      padding: EdgeInsets.only(top: 16, bottom: 8),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          SingleChildScrollView(
            scrollDirection: Axis.horizontal,
            child: Row(children: cards),
          ),
        ],
      ),
    );
  } else {
    FeautredCards = Container();
  }
  return FeautredCards;
}

This creates the necessary scrolling widgets upfront instead of lazily like ListView.builder would.

Christopher Moore
  • 15,626
  • 10
  • 42
  • 52
  • The expression doesn't evaluate to a function, so it can't be invoked. – John Joe Jan 31 '21 at 17:52
  • @JohnJoe Not sure what you mean by "expression". You'll have to be a lot more specific. You're also messaging the wrong person. This is the *OP's* solution. – Christopher Moore Jan 31 '21 at 17:53
  • @JohnJoe `FeaturedCard` is not defined within this scope. What did you expect? – Christopher Moore Jan 31 '21 at 17:55
  • cards is List `widget`, but `FeaturedCard` is object. Are you sure the code can work? Error : The argument type 'FeaturedCard' can't be assigned to the parameter type 'Widget' – John Joe Jan 31 '21 at 18:00
  • @JohnJoe Why do you think `FeaturedCard` is not a `Widget`? Everything in the OP's code would say it is. – Christopher Moore Jan 31 '21 at 19:07
  • 1
    @ChristopherMoore Thank you for posting this! @JohnJoe `FeaturedCard` is a `Widget` the confusion could be as a result of the non-camel case naming sorry about that! – Mathiew Abbas Jun 25 '21 at 09:42
16

The Flutter framework can only know the height of a widget once it's been built.

If you're building ListView children dynamically, it can't calculate the required height of the ListView until all it's children have been built, which might never happen (infinite ListView).

You can either give the ListView a fixed height and build its children dynamically or have the ListView's height depend on it's children, in which case you'd need to build all it's children upfront.

Jordan Davies
  • 9,925
  • 6
  • 40
  • 51
  • 3
    What does build a children upfront means? – TSR Aug 13 '20 at 14:39
  • @TSR It means the children cannot be built lazily(on demand). Basically, you can't use `ListView.builder`. The OP posted a solution in their question, which I have edited out and posted in a community wiki. They use a `SingleChildScrollView` with a `Row` as an alternative. – Christopher Moore Aug 13 '20 at 17:08