首页 > 解决方案 > 在没有 setState Flutter 的情况下滚动时禁用 CustomScrollView 的滚动

问题描述

我有多个小部件和列表CustomScrollView,我想CustomScrollView在滚动某些像素绑定条件时停止滚动。

我可以用NeverScrollPhysics()它来停止它,但我不想setState()在这里使用函数,因为带有列表的 CustomScrollview 内容足够大,以至于在滚动时重新加载时屏幕会变得迟钝。

也尝试过,Provider但构建器只提供了一个不与条子列表一起使用的子小部件。

这是使用的代码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(),
                      ],
                    ),
                ),

有没有办法只重新加载CustomScrollView没有它的孩子?否则在这种情况下防止滚动的任何解决方法?

感谢帮助

标签: flutterdartscrollviewphysicsreload

解决方案


调用 build 方法时,该 build 方法中的所有小部件都将被重建,但小const部件除外,但const小部件不能接受动态参数(只能是常量值)。

Riverpod在这种情况下提供了一个非常好的解决方案,ProviderScope您可以通过inherited widget而不是小部件构造函数传递参数(就像使用导航传递参数时一样),因此承包商可以const.

例子 :

数据模块

TLDR 你需要使用Freezedpackage 或覆盖== operator并且hashCode几乎总是因为 dart 问题。

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;
}

将我们设置ScopedProvider为全局变量

final dataClassScope = ScopedProvider<DataClass>(null);

我们在列表中使用的小部件

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,
        ),
      ),
    );
  }
}

最后是主要的CustomScrollView小部件

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(),
              ),
          ],
        ),
      ),
    );
  }
}

不要忘记包装MaterialAppwith ProviderScope

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

推荐阅读