Search code examples
flutterdartflutter-gridview

Flutter GridView with PageScrollPhysics: Prevent Grid Items from Cropping on Page Scroll


I'm working on a Flutter app where I need to display items in a horizontally scrolling . I want the [tag:GridView ]to behave like a paginated view, where each "page" scrolls exactly by one set of items (like a ), but I’m running into issues with items getting cropped.

PageviewPhysic scroll to item crop issue image

I set up my with to achieve a paginated scroll effect, and my grid is set to display items in a 3x3 format. However, when I scroll, the next "page" starts midway through an item, resulting in the items getting partially cropped on the left side.

Here's my current code:

GridView.builder(
            // controller: controller,
            physics: PageScrollPhysics(),
            scrollDirection: Axis.horizontal,
            itemCount: 9,
            gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
              childAspectRatio: 2 / 8.80,
              mainAxisSpacing: 10,
              crossAxisSpacing: 0,
              crossAxisCount: 3,
            ),
            itemBuilder: (context, index) {
              return Row(
                children: [
                  ClipRRect(
                    borderRadius: BorderRadius.circular(8),
                    child: Image(
                      image: AssetImage('assets/images/appicon.png'),
                      height: 50,
                      width: 50,
                    ),
                  ),
                  SizedBox(width: 5),
                  Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        "App Name",
                        style: TextStyle(
                          fontSize: 16,
                          height: 1,
                        ),
                      ),
                      Text(
                        "App Type",
                        style: TextStyle(
                          fontWeight: FontWeight.w400,
                        ),
                      ),
                    ],
                  )
                ],
              );
            },
          ),

Expected Behavior

I want each "page" scroll in the GridView to display exactly 3 columns per page and snap neatly to the beginning of the next set of grid items, without any items being cropped or partially displayed.


Solution

  • What you seek I think is a PageView. PageView animates between pages in similar fashion with the example in the link you provided.

    Wrap your GridView with a PageView like in this example.

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Horizontal GridView Pagination',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: Scaffold(
            appBar: AppBar(
              title: Text('Horizontal GridView Pagination'),
            ),
            body: PaginatedHorizontalGrid(),
          ),
        );
      }
    }
    
    class PaginatedHorizontalGrid extends StatelessWidget {
      // Sample list of items for the grid
      final List<String> items = List.generate(20, (index) => 'Item ${index + 1}');
    
      @override
      Widget build(BuildContext context) {
        // Calculate the number of pages needed, with 3 items per page
        int pageCount = (items.length / 3).ceil();
    
        return PageView.builder(
          scrollDirection: Axis.horizontal,
          itemCount: pageCount,
          itemBuilder: (context, pageIndex) {
            // Calculate the starting index for each page
            int startIndex = pageIndex * 3;
            // Get the subset of items for the current page
            List<String> pageItems = items.skip(startIndex).take(3).toList();
    
            return GridView.builder(
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 1, // 1 row
                mainAxisSpacing: 10,
                crossAxisSpacing: 10,
                childAspectRatio: 1.5, // Adjust as needed for item size
              ),
              itemCount: pageItems.length,
              padding: EdgeInsets.all(20),
              itemBuilder: (context, index) {
                return Container(
                  color: Colors.blueAccent,
                  alignment: Alignment.center,
                  child: Text(
                    pageItems[index],
                    style: TextStyle(color: Colors.white, fontSize: 18),
                  ),
                );
              },
            );
          },
        );
      }
    }