Mohʌɱɱʌɗ
Mohʌɱɱʌɗ

Reputation: 351

Flutter NestedScrollView: Independent Scrolling for Nested Elements (Carousel, List, and Grid)

I'm working on a Flutter app where I want a screen layout with the following structure:

  1. A carousel widget with an indicator at the top.
  2. A list of cards beneath the carousel.
  3. A row containing a list and a grid, both with separate scroll behaviors.

My goal is to achieve the following scrolling behavior:

Currently, I'm facing two issues:

Below are the images:

enter image description here enter image description here

Below is a simplified version of my code:

 Scaffold(
      appBar: AppBar(
        title: Text('Sample'),
        centerTitle: true,
      ),
      body: SafeArea(
        child: NestedScrollView(
          floatHeaderSlivers: true,
          headerSliverBuilder: (context, innerBoxIsScrolled) {
            return [
              SliverOverlapAbsorber(
                handle:
                    NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                sliver: SliverAppBar(
                  automaticallyImplyLeading: false,
                  flexibleSpace: FlexibleSpaceBar(
                    background: sampleTopCard(),
                  ),
                  expandedHeight: 360,
                  floating: true,
                  snap: true,
                  forceElevated: innerBoxIsScrolled,
                ),
              ),
            ];
          },
          body: sampleBody(),
        ),
      ),
    );
  }

  Widget sampleBody() {
    return Row(
      mainAxisSize: MainAxisSize.max,
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Expanded(
          child: ListView.builder(
            padding: const EdgeInsets.symmetric(vertical: 10),
            shrinkWrap: true,
            itemCount: controller.list.length,
            itemBuilder: (context, index) {
              return SizedBox(
                height: 150,
                width: 80,
                child: Card(
                  color: Colors.purpleAccent.withOpacity(0.5),
                  child: Center(
                    child: Text(
                      'Left List Card',
                      textAlign: TextAlign.center,
                    ),
                  ),
                ),
              );
            },
          ),
        ),
        Expanded(
          flex: 3,
          child: GridView.builder(
            padding: const EdgeInsets.all(5),
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 2,
              crossAxisSpacing: Get.width * 0.02,
              mainAxisSpacing: 10,
              childAspectRatio: 0.60,
            ),
            itemCount: 10,
            itemBuilder: (context, index) {
              return Card(
                color: Colors.greenAccent.withOpacity(0.5),
                child: Center(
                  child: Text(
                    'Right Grid Card',
                    textAlign: TextAlign.center,
                  ),
                ),
              );
            },
          ),
        ),
      ],
    );
  }

  Widget sampleTopCard() {
    return Container(
      color: Colors.white,
      height: 420,
      width: Get.width,
      child: Column(
        children: [
          SizedBox(
            width: Get.width - 20,
            height: 100,
            child: CarouselWidget(
              controller: controller,
              carouselWidget: Card(
                color: Colors.pink.withOpacity(0.5),
                child: Center(child: Text('Carousel Card')),
              ),
            ),
          ),
          SizedBox(height: 10),
          DotIndicatorWidget(controller: controller),
          SizedBox(height: 20),
          Container(
            height: 220,
            padding: const EdgeInsets.only(left: 10),
            color: Colors.grey[200],
            width: Get.width,
            child: ListView.separated(
              separatorBuilder: (ctx, idx) => const SizedBox(width: 10),
              scrollDirection: Axis.horizontal,
              padding: const EdgeInsets.symmetric(vertical: 10),
              shrinkWrap: true,
              itemCount: controller.list.length,
              itemBuilder: (context, index) {
                return SizedBox(
                  height: 100,
                  width: 150,
                  child: Card(
                    color: Colors.blueAccent.withOpacity(0.5),
                    child: Center(child: Text('List Card')),
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );

Upvotes: 3

Views: 72

Answers (0)

Related Questions