Shashi Kiran
Shashi Kiran

Reputation: 1079

How to implement continuous scroll with SingleChildScrollView and GridView in Flutter

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

Answers (4)

shereef hamed
shereef hamed

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

Rishabh Sehgal
Rishabh Sehgal

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

Jaswant Singh
Jaswant Singh

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

R&#233;mi Rousselet
R&#233;mi Rousselet

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

Related Questions