I am facting an issue related to scroll listview inside SingleChildScrollView which listview scroll controller is not working.
Getx Controller
var scrollController = ScrollController();
#override
void onInit() {
scrollController.addListener((){
if(scrollController.position.maxScrollExtent==scrollController.offset){
//fetch more data
}
});
}
Some code in Screen
Widget build(BuildContext context) {
return Obx(
() => SingleChildScrollView(
child: Column(
children: [
MyFormFilterWidget(),
Text("Found:${controller.aspList.length}"),
controller.aspIsLoading.value
? ProductLoading()
: controller.aspList.isNotEmpty
shrinkWrap: true,
padding: themePadding,
controller: controller.scrollController,
itemCount: controller.aspList.length + 1,
itemBuilder: (_, index) {
if (index < controller.aspList.length) {
return ProductCard(
product: controller.aspList[index]);
} else {
return controller.aspIsMoreLoading.value
? SizedBox(
width: 200,
child: Center(
child: Padding(
padding: EdgeInsets.all(10),
child: CircularProgressIndicator(
color: primaryColor),
),
),
)
: controller.aspLastPage.value
? SizedBox(
width: 200,
child: Padding(
padding: EdgeInsets.all(10),
child: Text("No more data",
textAlign: TextAlign.center),
),
)
: SizedBox.shrink();
}
},
separatorBuilder: (BuildContext context, int index) {
return const SizedBox(height: 10);
},
)
: const EmptyProduct(),
],
);
}
In this case, scrollController not working
If I remove SigleScrollView and only return ListView, it works as normal.
You can just have column instead of ListView here.
body: SingleChildScrollView(
child: Column(
children: [
Text("A"),
for (int i = 0; i < 10; i++)
Column(
children: [
Text("item $i"),
Text("Divider of $i"),
],
)
],
),
),
With shrinkWrap: true
SingleChildScrollView(
child: Column(
children: [
Text("A"),
ListView.builder(
shrinkWrap: true,
itemCount: 222,
itemBuilder: (context, index) => Text(" $index"),
)
],
),
),
Better and recomanded CustomScrollView
return Scaffold(
body: CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: Text("a"),
),
SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return Text("item $index");
}, childCount: 133),
)
],
),
);
Related
I am trying to create a horizontally scrollable list of cards but it breaks each. I am just not sure where I am going wrong. I tried adding Expandable in several places and I still get errors.
I am hoping this is resolvable or do I need to separate each element within the Container/Column (2nd one).
It is definitely some sort of sizing I am missing but just not able to pinpoint where.
body: SafeArea(
child: Column(
children: [
Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(...),
Padding(...),
Padding(...),
SizedBox(...),
Padding(...),
SizedBox(...),
Padding(...),
SizedBox(...),
Container(
height: 200,
color: Colors.blue[200],
child: ListView(
shrinkWrap: true,
physics: ScrollPhysics(),
scrollDirection: Axis.vertical,
children: [
FutureBuilder(
future: getAudioList(),
builder: (context, data) {
if (data.hasError) {
return Center(child: Text("${data.error}"));
} else if (data.hasData) {
var items = data.data as List<MyOtherFunc>;
var categories;
//Split into categories
if (items != null) {
categories = groupBy(
items, (MyOtherFunc ma) => ma.category);
Map<String, List<MindfulAudio>> catMap =
categories;
return new ListView.builder(
padding:
const EdgeInsets.fromLTRB(8, 0, 8, 0),
itemCount: catMap.length,
physics:
const AlwaysScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (context, index) {
String key = catMap.keys.elementAt(index);
return Card(
child: ListTile(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
MyFunc(
catMap[key], key),
),
);
},
leading: Icon(icons[index],
size: 25, color: colour[index]),
title: new Text("$key"),
trailing: new IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
MyFunc(
catMap[key], key),
),
);
},
icon: Icon(
FontAwesomeIcons.chevronRight,
color: PINK,
)),
),
);
},
);
}
return Center(
child: CircularProgressIndicator(),
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
],
),
),
],
),
),
],
),
),
Try this layout
SizedBox(
height: MediaQuery.of(context).size.height,
width : MediaQuery.of(context).size.width,
child: SingleChildScrollView (
child: Column(
children: [
//Other widgets which will scroll vertically in the column
SingleChildScrollView (
scrollDirection: Axis.horizontal,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
//Items you wish to scroll horizontally
]
)
),
//Some more widgets if you wish to add below the horizontal scroll
]
)
)
)
Flutter newbie here if my code looks too messy. Managed to figure out a few basic layouts and have implemented a TabBar. In 2nd Tab(COMICS). I have a horizontal ListView and a vertical one. I just can't figure out how to make the horizontal ListView scroll without changing tabs.
How i create the tabs:
return NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
title: Text('NestedScrollView'),
)
];
},
body: Scaffold(
appBar: AppBar(
toolbarHeight: 40,
centerTitle: true,
backgroundColor: Color(0xFFb92136),
title: Text('Kata Kata African Cartoons'),
// actions: [
// IconButton(onPressed: () {}, icon: Icon(Icons.search)),
// IconButton(onPressed: () {}, icon: Icon(Icons.more_vert))
// ],
bottom: TabBar(
controller: tabController,
tabs: [
Tab(
text: "VIDEOS",
),
Tab(
text: "COMICS",
),
Tab(
text: "MAGAZINE",
),
],
),
),
body: TabBarView(
controller: tabController,
children: [
VideosTab(),
ComicsTab(),
Magazines(),
],
),
));
Main body of comics tab
return Column(children: [
Container(child: Card(
elevation: 20,
child: Column(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: EdgeInsets.all(5),
child: Text("Katakata Long Comics"))
]),
Card(
child: StreamBuilder(
stream: ref.onValue,
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData &&
!snapshot.hasError &&
snapshot.data.snapshot.value != null) {
lists.clear();
DataSnapshot dataValues = snapshot.data.snapshot;
Map<dynamic, dynamic> values = dataValues.value as Map;
values.forEach((key, values) {
lists.add(values);
});
return Container(
height: 200,
child: new ListView.builder(
physics: const NeverScrollableScrollPhysics(),
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: lists.length,
itemBuilder: (BuildContext context, int index) {
return Card(
margin: EdgeInsets.fromLTRB(2, 2, 2, 2),
elevation: 20,
child: GestureDetector(
onTap: () {
String pdfurl = lists[index]["image"];
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PDFViewer(
url: pdfurl,
),
));
},
child: Padding(
padding: EdgeInsets.all(5),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Column(
children: [
Container(
height: 100,
width: 100,
child: Image(
image:
NetworkImage(lists[index]["image"]),
),
),
Text(lists[index]["name"]),
],
),
],
),
),
),
);
},
),
);
}
return Container(child: Text("Add Plants"));
},
),
),
],
),
)),
Container(child: Card(
elevation: 20,
child: Column(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: EdgeInsets.all(5),
child: Text("Continuous Humour"))
]),
Card(
child: StreamBuilder(
stream: secref.onValue,
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData &&
!snapshot.hasError &&
snapshot.data.snapshot.value != null) {
seclists.clear();
DataSnapshot dataValues = snapshot.data.snapshot;
Map<dynamic, dynamic> values = dataValues.value as Map;
values.forEach((key, values) {
seclists.add(values);
});
return new ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: seclists.length,
itemBuilder: (BuildContext context, int index) {
return Card(
margin: EdgeInsets.fromLTRB(2, 2, 2, 2),
elevation: 20,
child: GestureDetector(
onTap: () {
String pdfurl = seclists[index]["image"];
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PDFViewer(url: pdfurl,
),
));
},
child: Padding(
padding: EdgeInsets.all(5),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
height: 200,
width: 400,
child: Image(
image:
NetworkImage(seclists[index]["image"]),
),
),
Text(seclists[index]["name"]),
],
),
),
),
);
},
);
}
return Container(child: Text("Add Plants"));
},
),
),
],
),
)),
]);
You have NeverScrollableScrollPhysics() defined as the physics for your listviews. That means they won't scroll even when there is no tabview around them. Also they won't absorb the scroll event and defer them to the tabview.
The issue I have is adding a ReorderableList inside a ListView, the ReorderableList does not allow for reordering now. I can scroll the list but the long press for reodering is not active anymore. Here is my code:
Widget get _reorder => ReorderableList(
physics: ClampingScrollPhysics(),
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) => _cells[index],
itemCount: _cells?.length ?? 0,
onReorder: _setListOrder,
);
#override
Widget build(BuildContext context) {
return Container(
decoration: context.logoBackground,
child: SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: 1,
itemBuilder: (BuildContext context, int index) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
_reorder,
Text('End of List Widget')
],
);
}),
)
],
),
),
);
}
I did it with ReorderableListView instead of ReorderableList like this:
void _setListOrder(int oldIndex, int newIndex) {
print('old: $oldIndex, new: $newIndex'); // You need to implement the method!
}
var _cells = List.generate(30, (index) { // Your cells
return ListTile(
key: Key('$index'),
title: Text(
'$index',
),
);
});
Widget get _reorder => ReorderableListView.builder(
physics: ClampingScrollPhysics(),
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) => _cells[index],
itemCount: _cells?.length ?? 0,
onReorder: _setListOrder,
);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Expanded(
child: ListView( // I transformed it to simple ListView because you have one children
shrinkWrap: true,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height, // You need this height, or a custom height!
child: _reorder),
Text('End of List Widget')
],
)
],
),
)
],
),
),
),
);
}
If you want to check the documentation, you can do it here!
I am having a hard time figuring out how to get my FilterChips in a row.
I would like to display all FilterChips per category in a row and not in a seperated column.
Tried different approaches but non of them seems to work.
Hopefully someone can help me with this, thanks for help!
Here is my code:
Widget _buildListView(List<Category> categories) {
return Column(
children: [
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: categories.length,
itemBuilder: (context, index) {
return _buildSection(categories[index]);
},
),
),
],
);
}
Widget _buildSection(Category category) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
category.title,
style: TextStyle(fontSize: 18),
),
// How can I get my FilterChips side by side?
Row(
children: [
Flexible(
child: ListView.builder(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
itemCount: category.interests.length,
itemBuilder: (context, index) {
Interest interest = category.interests[index];
return FilterChip(label: Text(interest.interest), onSelected: (isSelected){
selectedInterests.add(interest.id);
});
return Text(interest.interest);
}),
),
],
),
],
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: categories,
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Category> categories = snapshot.data;
return _buildListView(categories);
}
return Center(child: CircularProgressIndicator());
},
),
);
}
class StackOver extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.only(
top: 60,
left: 10.0,
right: 10.0,
),
child: Wrap(
children: List.generate(
10,
(index) {
return FilterChip(
label: Text('index $index'),
onSelected: (val) {},
);
},
),
),
),
);
}
}
RESULT:
What is the preferred way to achieve a nested ListView, or in other words, ListView Widgets that could be included within a scrollable parent?
Imagine a "Reports" page, where a section is an itemized list.
For child ListView, use that parameter:
shrinkWrap: true,
physics: ClampingScrollPhysics(),
Adding physics: ClampingScrollPhysics() and shrinkWrap: true did the trick for me.
sample code:
#override
Widget build(BuildContext context) {
return Container(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: 123,
itemBuilder: (BuildContext context, int index) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Parent'),
ListView.builder(
itemCount: 2,
physics: ClampingScrollPhysics(),
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return Text('Child');
}),
],
);
}),
)
],
),
);
}
If you want to have the inner ListView be scrollable independently of the main scroll view, you should use NestedScrollView. Otherwise, use a CustomScrollView.
Here is some code illustrating the NestedScrollView approach.
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
new SliverAppBar(
pinned: true,
title: new Text('Flutter Demo'),
),
];
},
body: new Column(
children: <Widget>[
new FlutterLogo(size: 100.0, colors: Colors.purple),
new Container(
height: 300.0,
child: new ListView.builder(
itemCount: 60,
itemBuilder: (BuildContext context, int index) {
return new Text('Item $index');
},
),
),
new FlutterLogo(size: 100.0, colors: Colors.orange),
],
),
),
);
}
}
For inner Listview I have just added below code and it solved for me
shrinkWrap: true,
physics: ScrollPhysics(),
Screenshot:
Code:
var _container = Container(
height: 200,
color: Colors.blue,
margin: EdgeInsets.symmetric(vertical: 10),
);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("ListView")),
body: Padding(
padding: const EdgeInsets.all(40.0),
child: ListView( // parent ListView
children: <Widget>[
_container,
_container,
Container(
height: 200, // give it a fixed height constraint
color: Colors.teal,
// child ListView
child: ListView.builder(itemBuilder: (_, i) => ListTile(title: Text("Item ${i}"))),
),
_container,
_container,
_container,
],
),
),
);
}
Thanks to Serdar Polat:
ListView.builder( // outer ListView
itemCount: 4,
itemBuilder: (_, index) {
return Column(
children: [
Container(
color: Colors.blue,
alignment: Alignment.center,
child: Text('Header $index'),
),
ListView.builder( // inner ListView
shrinkWrap: true, // 1st add
physics: ClampingScrollPhysics(), // 2nd add
itemCount: 10,
itemBuilder: (_, index) => ListTile(title: Text('Item $index')),
)
],
);
},
)
shrinkWrap to wrap your content and ClampingScrollPhysics to use the parent scroll
ListView.builder(
shrinkWrap: true,
physics: const ClampingScrollPhysics(),
itemCount: yourList.length,
itemBuilder: (context, index) => YourWidget(items[index]),
),
I use this:
scrollController.addListener(onScroll);
void onScroll(){
if(scrollController.offset == 0.0
|| scrollController.position.extentBefore == 0.0
|| scrollController.position.extentAfter == 0.0){
scrollPhysics = NeverScrollableScrollPhysics();
Future.delayed(Duration(seconds: 1), (){
scrollPhysics = ClampingScrollPhysics();
setState((){});
});
setState((){});;
}
}
Expanded(
child: ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(8),
itemCount: requestList.length,
itemBuilder: (BuildContext context, int index) {
int que = index;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
padding: const EdgeInsets.only(
left: 20,
top: 10,
bottom: 10,
right: 20),
child: Text(
'${que++} . ${requestList[index].question}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
color: HexColor(HexColor.black),
fontFamily: 'montserrat_regular',
decoration: TextDecoration.none,
),
)),
ListView.builder(
itemCount: requestList[index].questionOptions!.length,
physics: ClampingScrollPhysics(),
shrinkWrap: true,
itemBuilder: (BuildContext context, int subindex) {
return Row(
children: <Widget>[
Radio(
value: 1,
groupValue: radio_value[index],
onChanged: (values) async {
setState(() {
radio_value[index] = 1;
qutionCheckModel[index].response =
"yes";
});
}),
Container(
child: Text(
requestList[index].questionOptions![subindex],
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
color: HexColor(HexColor.black),
fontFamily: 'montserrat_regular',
decoration: TextDecoration.none,
),
),
),
],
);
}),
],
);
}),
),