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 giveListView.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(),
),
);