nicover
nicover

Reputation: 2643

Disable scrolling of CustomScrollView while scrolling without setState Flutter

I have multiple widget and lists within CustomScrollView and I would like to stop CustomScrollView to scroll while scrolling on some pixels bound condition.

I can use NeverScrollPhysics() to stop it but I don't want to use setState() function here because the CustomScrollview content with lists is big enough to make the screen laggy while reloading on scroll.

Also tried with Provider but the builder is providing only a child widget which is not working with sliver list.

Here is the code using setState() :

              NotificationListener(
                  onNotification: (ScrollNotification notif) {
                    if(notif is ScrollUpdateNotification) {
                      if (canScroll && notif.metrics.pixels > 100) {
                        canScroll = false;
                        setState(() {});
                      }
                    }
                    if(notif is ScrollEndNotification) {
                      if(!canScroll) {
                        canScroll = true;
                        setState(() {});
                      }
                    }
                    return true;
                  },
                  child: CustomScrollView(
                      shrinkWrap: true,
                      physics: canScroll ? BouncingScrollPhysics() : NeverScrollableScrollPhysics(), 
                      slivers: [
                        SliverToBoxAdapter(),                                              
                        List(),
                        List(),
                      ],
                    ),
                ),

Is there a way to reload only the CustomScrollView without its child ? Otherwise any workaround to prevent scrolling in this case ?

Thanks for help

Upvotes: 1

Views: 3104

Answers (3)

Mohammed Alfateh
Mohammed Alfateh

Reputation: 3514

When the build method is called, all widgets in that build method will be rebuild except for const widgets, but const widget cannot accept dynamic arguments (only a constant values).

Riverpod provides a very good solution in this case, With ProviderScope you can pass arguments by inherited widget instead of widget constructor (as when passing arguments using navigation) so the contractor can be const.

Example :

Data module

TLDR you need to use Freezed package or override the == operator and the hashCode almost always because of dart issue.

class DataClass {
  final int age;
  final String name;

  const DataClass(this.age, this.name);

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) return true;

    return other is DataClass && other.age == age && other.name == name;
  }

  @override
  int get hashCode => age.hashCode ^ name.hashCode;
}

setting our ScopedProvider as a global variable

final dataClassScope = ScopedProvider<DataClass>(null);

The widget we use in our list

class MyChildWidget extends ConsumerWidget {
  const MyChildWidget();

  @override
  Widget build(BuildContext context, ScopedReader watch) {
    final data = watch(dataClassScope);

    // Note for better optimaization
    // in case you are sure the data you are passing to this widget wouldn't change
    // you can just use StatelessWidget and set the data as:
    // final data = context.read(dataClassScope);
    // use ConsumerWidget (or Consumer down in this child widget tree) if the data has to change

    print('widget with name\n${data.name} rebuild');

    return SliverToBoxAdapter(
      child: Padding(
        padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 20),
        child: Text(
          'Name : ${data.name}\nAge ${data.age}',
          textAlign: TextAlign.center,
        ),
      ),
    );
  }
}

finally the main CustomScrollView widget

class MyMainWidget extends StatefulWidget {
  const MyMainWidget();

  @override
  State<MyMainWidget> createState() => _MyMainWidgetState();
}

class _MyMainWidgetState extends State<MyMainWidget> {
  bool canScroll = true;

  void changeCanScrollState() {
    setState(() => canScroll = !canScroll);
    print('canScroll $canScroll');
  }

  final dataList = List.generate(
    20,
    (index) => DataClass(10 * index, '$index'),
  );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: GestureDetector(
        behavior: HitTestBehavior.opaque,
        onTap: () {
          changeCanScrollState();
        },
        child: CustomScrollView(
          shrinkWrap: true,
          physics: canScroll
              ? BouncingScrollPhysics()
              : NeverScrollableScrollPhysics(),
          slivers: [
            for (int i = 0; i < dataList.length; i++)
              ProviderScope(
                overrides: [
                  dataClassScope.overrideWithValue(dataList[i]),
                ],
                child: const MyChildWidget(),
              ),
          ],
        ),
      ),
    );
  }
}

Don't forget to wrap the MaterialApp with ProviderScope.

  runApp(
    ProviderScope(
      child: MyApp(),
    ),
  );

Upvotes: 2

yellowgray
yellowgray

Reputation: 4509

Are you just need to stop the user from scrolling it? I think you can try to controller the list to a fixed position by using jumoTo.

  ...
  final _controller = ScrollController();

  @override
  Widget build(BuildContext context) {
    return NotificationListener(
      onNotification: (ScrollNotification notif) {
        if (notif is ScrollUpdateNotification) {
          if (notif.metrics.pixels > 100) {
            _controller.jumpTo(100)
          }
        }
        return true;
      },
      child: CustomScrollView(
        controller: _controller,
        ...

Upvotes: 0

Dhruvan Bhalara
Dhruvan Bhalara

Reputation: 257

Try this solution use const constructor for child widget so it won't rebuild unless widget changed
class MyHomePage extends StatelessWidget {
  ValueNotifier<ScrollPhysics> canScroll =
      ValueNotifier(const BouncingScrollPhysics());

  MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: NotificationListener(
        onNotification: (ScrollNotification notif) {
          if (notif is ScrollUpdateNotification) {
            if (canScroll.value.runtimeType == BouncingScrollPhysics &&
                notif.metrics.pixels > 100) {
              canScroll.value = const NeverScrollableScrollPhysics();
              debugPrint("End false");
            }
          }
          if (notif is ScrollEndNotification) {
            if (canScroll.value.runtimeType == NeverScrollableScrollPhysics) {
              debugPrint("End");
              Future.delayed(const Duration(milliseconds: 300),
                  () => canScroll.value = const BouncingScrollPhysics());

              debugPrint("End1");
            }
          }
          return true;
        },
        child: ValueListenableBuilder(
          valueListenable: canScroll,
          builder:
              (BuildContext context, ScrollPhysics scrollType, Widget? child) =>
                  CustomScrollView(
            physics: scrollType,
            slivers: [
              SliverToBoxAdapter(
                child: Container(
                  height: 200,
                  color: Colors.black,
                ),
              ),
              SliverToBoxAdapter(
                child: Column(
                  children: [
                    Container(
                      height: 100,
                      color: Colors.blue,
                    ),
                    Container(
                      height: 200,
                      color: Colors.grey,
                    ),
                    Container(
                      height: 200,
                      color: Colors.blue,
                    ),
                    Container(
                      height: 200,
                      color: Colors.grey,
                    ),
                    Container(
                      height: 200,
                      color: Colors.blue,
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Upvotes: 2

Related Questions