Horizontal ListView doesn't show children with no fixed width

220 Views Asked by At

I'm trying to replicate this design: image

These 3 are progress indicators and their number is dynamic. That's why I thought of creating a list view and populating it depending on their number. Plus their size should NOT be fixed and they must, as a group, fill the size of the screen but this isn't happening.

Container(
                          height: 10,
                          width: 400,
                          child: ListView.builder(
                            physics: NeverScrollableScrollPhysics(),
                            scrollDirection: Axis.horizontal,
                            shrinkWrap: true,
                            itemCount: 3,
                            itemBuilder: (BuildContext context, int index) {
                              return Container(
                                child: LinearPercentIndicator(
                                    padding: index == 0 ? EdgeInsets.only(right: 4.0, left: 2.0) : EdgeInsets.symmetric(horizontal: 4.0),
                                    alignment: MainAxisAlignment.start,
                                    lineHeight: 5.0,
                                    percent: 1.0,
                                    progressColor: index == 2 ? theme.inboxListItemShadowColor : theme.userProfileTierBadgeColor,
                                    backgroundColor: theme.inboxListItemShadowColor,
                                    barRadius: Radius.circular(50)
                                ),
                              );
                            },
                          ),
                        )

And I thought this might create the design I needed but it always generates the error below:

RenderFlex children have non-zero flex but incoming width constraints are unbounded.

And that's because the Container widget containing the LinearPercentIndicator has no fixed width. And I don't want fixed width cause it won't support different screens.

So any idea on how I might do this? All i need is to have the children of the list view have dynamic widths depending on the screen and all fit in it.

Tried to give each container width like this:

width: (MediaQuery.of(context).size.width - 30) / n,

Where n is the number of progress indicators to create but it always messes up the design.

2

There are 2 best solutions below

1
Md. Yeasin Sheikh On BEST ANSWER

The issue is LinearPercentIndicator is trying to get as much width as possible and horizontal ListView provides infinite width(you can think this way). You can provide fixed 1/3 screen width. for as for the ui, Row is enough.

Row(
  children: [
    for (int i = 0; i < 3; i++)
      Expanded(
        child: LinearPercentIndicator(
            padding: i == 0
                ? EdgeInsets.only(right: 4.0, left: 2.0)
                : EdgeInsets.symmetric(horizontal: 4.0),
            alignment: MainAxisAlignment.start,
            lineHeight: 5.0,
            percent: 1.0,
            barRadius: Radius.circular(50)),
      ),
  ],
),

Using fixed width

Container(
  height: 10,
  width: 400,
  child: ListView.builder(
    physics: NeverScrollableScrollPhysics(),
    scrollDirection: Axis.horizontal,
    shrinkWrap: true,
    itemCount: 3,
    itemBuilder: (BuildContext context, int index) {
      return Container(
        width: 400 / 3, //400 was your top widget width
        child: LinearPercentIndicator(
            padding: index == 0
                ? EdgeInsets.only(right: 4.0, left: 2.0)
                : EdgeInsets.symmetric(horizontal: 4.0),
            alignment: MainAxisAlignment.start,
            lineHeight: 5.0,
            percent: 1.0,
            barRadius: Radius.circular(50)),
      );
    },
  ),
)

1
Yes Dev On

There is a very informative video about how ListViews try to take up space in your layout that I'd recommend watching: https://www.youtube.com/watch?v=jckqXR5CrPI

To summarize, ListViews will try to take up as much space as possible. In your case, when using scrollDirection: Axis.horizontal, the ListView will try to take up as much horizontal space as it can get.

Since you want the LinearProgressIndicators to have equal but dynamic sizes, I would recommend against using a ListView and instead use a Row with three Expanded widgets. A Row will only take up as much horizontal space as the screen will allow:

Row(
            children: const [
              Expanded(
                child: Padding(
                  padding: EdgeInsets.all(8.0),
                  child: LinearProgressIndicator(),
                ),
              ),
              Expanded(
                child: Padding(
                  padding: EdgeInsets.all(8.0),
                  child: LinearProgressIndicator(),
                ),
              ),
              Expanded(
                child: Padding(
                  padding: EdgeInsets.all(8.0),
                  child: LinearProgressIndicator(),
                ),
              ),
            ],
          ),