user15782390
user15782390

Reputation:

How to add a ScrollController in the body of a NestedScrollView

I have a page in an app that paginates data from firebase firestore. To paginate the data, I use a scroll controller which fetches the data once the user scrolls to the bottom.

The problem is that adding the ScrollController() to the NestedScrollView() doesn't work properly.

Adding it to a ListView.builder() widget in the body of the NestedScrollView() makes the SliverAppBar() pinned even though it's not. I also get the following exception:

════════ Exception caught by animation library ═════════════════════════════════
The Scrollbar's ScrollController has no ScrollPosition attached.
════════════════════════════════════════════════════════════════════════════════

The scrollbar is the parent of the ListView.builder() that has the ScrollController() attached.

Upvotes: 6

Views: 3419

Answers (2)

Fanisus
Fanisus

Reputation: 23

RawScrollbar(
  scrollbarOrientation: ScrollbarOrientation.right,
  radius: Radius.circular(100.h),
  mainAxisMargin: 2.h,
  thumbVisibility: true,
  controller: Scontroller,
  child: ListView.builder(
    // primary: true,
    controller: Scontroller,
    itemCount: _warehouseListedData.value?.map((e) => e["godown"]).toSet().toList().length,
    scrollDirection: Axis.vertical,
    shrinkWrap: true,
    padding: EdgeInsets.all(0.5.h),
    itemBuilder: (context, index) {
      return Container(
        width: double.infinity,
        margin: EdgeInsets.all(1.w),
        padding: EdgeInsets.symmetric(vertical: 1.h, horizontal: 2.w),
        decoration: BoxDecoration(color: Color.fromRGBO(34, 34, 34, 1), borderRadius: BorderRadius.circular(1.h)),
        child: Column(
          children: [
            IntrinsicHeight(
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  RichText(
                    text: TextSpan(
                      children: [
                        TextSpan(text: "GODOWN: ", style: TextStyle(fontSize: 18, decoration: TextDecoration.none, color: Colors.red, fontWeight: FontWeight.w500)),
                        TextSpan(text: "${_warehouseListedData.value?.map((e) => e["godown"]).toSet().toList()[index]}", style: TextStyle(fontSize: 20, decoration: TextDecoration.none, color: Colors.red, fontWeight: FontWeight.w600)),
                      ],
                    ),
                  ),
                  AspectRatio(
                    aspectRatio: 1,
                    child: Container(
                      decoration: BoxDecoration(borderRadius: BorderRadius.circular(0.5.h), border: Border.all(color: Color.fromARGB(255, 75, 75, 75), width: 2.5), color: Colors.white),
                      child: Center(
                        child: Padding(
                          padding: EdgeInsets.all(1.0.w),
                          child: Text(
                            "ALL",
                            style: TextStyle(fontWeight: FontWeight.w700, decoration: TextDecoration.none),
                          ),
                        ),
                      ),
                    ),
                  )
                ],
              ),
            ),
            SizedBox(
              height: 1.h,
            ),
            Column(
              children: () {
                List<Widget> widgets = [];

                var thisGodown = _warehouseListedData.value?.map((e) => e["godown"]).toSet().toList()[index];
                var thisGodownCompartments = _warehouseListedData.value
                    ?.map((e) {
                      if (e["godown"] == thisGodown) {
                        return e["compartment"];
                      }
                    })
                    .toSet()
                    .whereType<String>()
                    .toList();
                for (var compartment in thisGodownCompartments!) {
                  var thisCompartmentLocations = _warehouseListedData.value
                      ?.map((e) {
                        if (e["godown"] == thisGodown && e["compartment"] == compartment) {
                          return e["location"];
                        }
                      })
                      .toSet()
                      .whereType<String>()
                      .toList();
                  print(thisCompartmentLocations.toString());
                  ScrollController sc = ScrollController();
                  widgets.add(
                    Container(
                      height: 6.h,
                      margin: EdgeInsets.symmetric(horizontal: 0.5.w),
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(1.h),
                        color: Color.fromARGB(255, 69, 69, 69),
                      ),
                      child: RawScrollbar(
                        controller: sc,
                        thumbVisibility: true,
                        scrollbarOrientation: ScrollbarOrientation.bottom,
                        child: ListView.builder(
                          // primary: true,
                          controller: sc,
                          shrinkWrap: true,

                          scrollDirection: Axis.horizontal,
                          itemCount: thisCompartmentLocations?.length,
                          itemBuilder: (context, index) {
                            return AspectRatio(
                              aspectRatio: 1,
                              child: Container(
                                margin: EdgeInsets.symmetric(vertical: 2.w, horizontal: 2.w),
                                decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(1.w)),
                              ),
                            );
                          },
                        ),
                      ),
                    ), // TODO
                  );
                  widgets.add(SizedBox(
                    height: 1.h,
                  ));
                }
                // print(thisGodownCompartments.toString());
                return widgets;
              }(),
            )
          ],
        ),
      );
    },
  ),
)

Sorry I was lazy to destructure. Hope someone finds it useful

The Scrollbar and ListView must contain same ScrollController. The ListView Physics should not be defined

Upvotes: 0

MaNDOOoo
MaNDOOoo

Reputation: 1555

You can use innerControll by using GlobalKey like below.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: MyWidget());
  }
}

class MyWidget extends StatefulWidget {
  MyWidget({Key? key}) : super(key: key);

  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  final GlobalKey<NestedScrollViewState> globalKey = GlobalKey();

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
      globalKey.currentState!.innerController.addListener(() {
        print('notify');
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: NestedScrollView(
        key: globalKey,
        headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
          return const <Widget>[
            SliverAppBar(
              title: Text('NestedScrollViewState Demo!'),
            ),
          ];
        },
        body: CustomScrollView(
          slivers: [
            SliverList(
              delegate: SliverChildBuilderDelegate(
                (BuildContext context, int i) => Text(i.toString()),
                childCount: 500,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Upvotes: 10

Related Questions