Home > OS >  Flutter - How to have a non-scrollable max height ListView.builder?
Flutter - How to have a non-scrollable max height ListView.builder?

Time:01-12

My problems with ListView.builder in my specific scenario:

  • A height has to be given to ListView.builder so that whatever is inside that can be infinitely scrollable.
  • The shrinkWrap property is only helpful if you are planning to give ListView.builder a predetermined height.

What I require:

A ListView.builder that acts just like a Column which will display all the widgets so that there is no scrolling, so it does not require a height but will be like an Expanded widget.

I want to use ListView.builder as my widgets are generated through Lists and Dictionaries, but if it's not necessary to use it, please give me an alternative where I can use my Lists and Dictionaries to generate widgets on demand and update the state.

Example:

List<Map> data = [{'data': 'here'}];

// ListView.builder solution:
ListView.builder(
    shrinkWrap: true,
    itemCount: data.length,
    itemBuilder: (context, index) {
        if (condition A) {
            return Text(data[index]['data']);
        } else if (condition B) {
            return Text(data[index]['data']);
        } else {
            return Container();
        }
    }
);
// This solution requires ListView.builder to have its own height
// constraints, so setting a height will cause it to be scrollable if
// the widgets take up space after those constraints are exceeded.
// NeverScrollablePhysics() is not the point here.

// Column solution:
// A widget and for loop is required to build before the build method is called:
List<Widget> widgetList = [];

for (var dataSet in data) {
    // If conditions here:
    widgetList.add(dataSet['data'];
}

return Column(
    children: widgetList,
);
// Problem: Widgets such as FilterChip which use state changes don't
// get updated as these come from a prebuilt Widget variable. Tapping
// on them does nothing.

CodePudding user response:

I'm not entirely sure if I understood it right, but if-conditions and loops are certainly also possible in Columns. Your example of

ListView.builder(
    shrinkWrap: true,
    itemCount: data.length,
    itemBuilder: (context, index) {
        if (condition A) {
            return Text(data[index]['data']);
        } else if (condition B) {
            return Text(data[index]['data']);
        } else {
            return Container();
        }
    }
);

can be rewritten as a Column like

Column(children: [
  for (final d in data)
    if (condition A)
      Text(d['data'])
    else if (condition B)
      Text(d['data'])
    else
      Container()
]);

But I'm not sure if it helps solve your problem.

EDIT: For your follow-up question, maybe this is something that you want?

Column(children: [
  for (final d in data.map((e) => e['data']))
    if (conditionA)
      Text(d)
    else if (conditionB)
      Text(d)
    else
      Container()
]);

CodePudding user response:

I created custom widget for this purpose only:

import 'package:flutter/material.dart';

class ScrollableColumn extends StatelessWidget {
  const ScrollableColumn(
      {Key? key,
      this.controller,
      required this.children,
      this.crossAxisAlignment = CrossAxisAlignment.center,
      this.textDirection,
      this.mainAxisAlignment = MainAxisAlignment.start,
      this.mainAxisSize = MainAxisSize.max,
      this.verticalDirection = VerticalDirection.down,
      this.textBaseline})
      : super(key: key);
  final ScrollController? controller;
  final List<Widget> children;
  final CrossAxisAlignment crossAxisAlignment;
  final TextDirection? textDirection;
  final MainAxisAlignment mainAxisAlignment;
  final MainAxisSize mainAxisSize;
  final VerticalDirection verticalDirection;
  final TextBaseline? textBaseline;
  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      controller: controller,
      slivers: [
        SliverFillRemaining(
          hasScrollBody: false,
          child: AnimatedPadding(
            padding: MediaQuery.of(context).viewInsets,
            duration: const Duration(milliseconds: 100),
            curve: Curves.decelerate,
            child: Column(
              children: children,
              crossAxisAlignment: crossAxisAlignment,
              textDirection: textDirection,
              mainAxisAlignment: mainAxisAlignment,
              mainAxisSize: mainAxisSize,
              verticalDirection: verticalDirection,
              textBaseline: textBaseline,
            ),
          ),
        ),
      ],
    );
  }
}

For general usage, put following code in body in Scaffold:

ScrollableColumn(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Expanded(
                  child: Padding(
                    padding: EdgeInsets.only(top: 12, left: 22, right: 22),
                    child: Text(
                      'My Text',
                      textAlign: TextAlign.center,
                    ),
                  ),
                ),
                Spacer(),
                Container(
                  width: MediaQuery.of(context).size.width,
                  decoration: BoxDecoration(
                    color: kcWhiteColor,
                    border: Border(
                      top: BorderSide(
                        width: 0.1,
                        color: kcButtonBorderColor,
                      ),
                    ),
                  ),
                  padding: EdgeInsets.fromLTRB(14, 10, 14, 24),
                  child: TextButton(
                    style: TextButton.styleFrom(
                      backgroundColor: Colors.blue,
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(12.0)),
                      padding: EdgeInsets.symmetric(vertical: 14),
                    ),
                    child: Text(
                      LocaleKeys.confirm,
                      style: ktsButtonWhite18Text,
                    ).tr(),
                    onPressed: () {},
                  ),
                ),
              ],
            ),

To use it in your case with changeable list, put the following a code of snippet in body parameter in Scaffold:

ScrollableColumn(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: myDictionaryList.map((myDictionary) {
                return Expanded(
                  child: Padding(
                    padding: EdgeInsets.only(top: 12, left: 22, right: 22),
                    child: Text(
                      myDictionary.toString(),
                      textAlign: TextAlign.center,
                    ),
                  ),
                );
              }).toList(),
            ),

Note: I haven't tested it but it should work.

CodePudding user response:

ListView widget contains a property named "Physics" which can accept "NeverScrollableScrollPhysics()" and that will disable scrolling in your ListView widget. here is an example,

return Scaffold(
  body: ListView.builder(
    physics: const NeverScrollableScrollPhysics(),
    itemCount: 10,
    itemBuilder: (context, index) => Container(),
  ),
);
  • Related