Reputation: 1079
I currently have a SingleChildScrollView as a parent and a GridView as one of the child widgets. Everything is working fine but when the GridView finishes scrolling, the scroll does not pass on to parent ScrollView. In Landscape orientation, the GridView occupies the entire screen so user gets stuck scrolling only the GridView. How can I pass the scroll?
SingleChildScrollView(
controller: _hideButtonController,
child: Container(
padding: EdgeInsets.only(bottom: 80.0), //padding for fab
child: Column(
children: <Widget>[
_previousWidgets(),
_gridWidget(),
],
),
),
);
Upvotes: 10
Views: 23314
Reputation: 79
You can use physics: NeverScrollableScrollPhysics() and shrinkWrap: true inside GridView widget like this
SingleChildScrollView(
padding: const EdgeInsets.symmetric(
vertical: 16,
horizontal: 16,
),
child: Column(
children: [
Text('Some Text Here'),
GridView(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisExtent: 170,
),
children:[],
),
],),)
Upvotes: 0
Reputation: 45
I've answered one similar questions here: https://stackoverflow.com/a/76192690/11976596
You can find the same below-
I had a similar issue, where I had a GridView.builder
inside a SingleChildScrollView
. Now the problem is you can't create a GridView
inside SingleChildScrollView
because both will try to take as much space available which here makes height unbounded/infinte.
So the solution is to wrap the GridView
with a SizedBox
and I always prefer to use GridView.Builder
.
Now the actual question, "How do I know the size of SizedBox
when the list is dynamic ?"
So I wrote this code with a logic to calculate the height of SizedBox
dynamically based on the childAspectRatio
attribute of GridView
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return const MaterialApp(title: 'Dynamic GridView', home: Home());
}
}
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
final double width = MediaQuery.of(context).size.width;
final double height = MediaQuery.of(context).size.height;
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: [
itemGrid(width),
],
),
),
);
}
Widget itemGrid(double width) {
const int count = 16;
const int itemsPerRow = 2;
const double ratio = 1 / 1;
const double horizontalPadding = 0;
final double calcHeight = ((width / itemsPerRow) - (horizontalPadding)) *
(count / itemsPerRow).ceil() *
(1 / ratio);
return SizedBox(
width: width,
height: calcHeight,
child: GridView.builder(
padding: const EdgeInsets.symmetric(horizontal: horizontalPadding),
itemCount: count,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
mainAxisSpacing: 0,
crossAxisSpacing: 0,
crossAxisCount: itemsPerRow,
childAspectRatio: ratio),
itemBuilder: (context, index) {
return SizedBox(
child: Card(
clipBehavior: Clip.hardEdge,
child: Column(
children: [
Expanded(
child: Image.network(
"https://picsum.photos/200?${DateTime.now().millisecondsSinceEpoch.toString()}")),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 4.0),
child: Text(
"Lorem Ipsum is a dummy text, lorem ipsum",
maxLines: 3,
overflow: TextOverflow.ellipsis,
style:
TextStyle(fontSize: 10, fontWeight: FontWeight.bold),
textAlign: TextAlign.start,
),
),
],
),
),
);
},
),
);
}
}
Link to working Dart gist - https://dartpad.dev/?id=05ba8804b19a2978a087c68622000a01
Explanation
count
is the total number of items or itemCount
.itemsPerRow
is the number of items in a row or crossAxisCount
.ratio
is the childAspectRatio
attribute of GridView
that usually is used to set size of an item inside grid. ratio
is calculated as width/height.horizontalPadding
is the horizontal padding given to GridView
(in case of vertical list)calcHeight
is the calculated height of the SizedBox
just so you don't need to give a fixed height in case of dynamic lists.Upvotes: 0
Reputation: 10699
Inside your GridView set physics like this and you are good to go :
physics: NeverScrollableScrollPhysics()
Edit:
Doing this can be resource intensive as it generates all the items in a single go instead of inflating/generating items on scrolling, so make sure you don't have large amount of data in your grid or list.
Upvotes: 28
Reputation: 276911
You should use CustomScrollView
instead of SingleChildScrollView
.
CustomScrollView
is a scrollview that can combine multiple type of content. Such as list, grid, or plain widget.
CustomScrollView(
slivers: <Widget>[
SliverToBoxAdapter(
child: Container(
height: 50.0,
width: double.infinity,
color: Colors.yellow,
),
),
SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0),
delegate: SliverChildBuilderDelegate(
(context, index) {
return Container(
color: Colors.red,
);
},
childCount: 10,
),
),
SliverPadding(
padding: const EdgeInsets.only(bottom: 80.0),
)
],
),
Upvotes: 29