ScrollController.jumpTo() "ScrollController not attached to any scroll views" 滚动控制器.jumpTo() "滚动控制器未附加到任何滚动视图"

11

我正在监听一个PageController以获取位置信息,然后将其与ListView同步。当PageView被操作时,ListView同时被操作。

示例:https://github.com/Ramotion/cardslider-android

然而,在v0.6.0之后,我会得到一个断言错误,指出我的ScrollController未附加到任何视图上。每次有流事件触发.jumpTo()方法时都会引发此错误。它仍然可以工作,但是这个断言错误让我感到疯狂。

[VERBOSE-2:shell.cc(181)] Dart Error: Unhandled exception:
'package:flutter/src/widgets/scroll_controller.dart': Failed assertion: line 169 pos 12: '_positions.isNotEmpty': ScrollController not attached to any scroll views.
#0      _AssertionError._doThrowNew (dart:core/runtime/liberrors_patch.dart:40:39)
#1      _AssertionError._throwNew (dart:core/runtime/liberrors_patch.dart:36:5)
#2      ScrollController.jumpTo (package:flutter/src/widgets/scroll_controller.dart:169:12)
#3      MyTitle.build.<anonymous closure> (file:///Users/lukepighetti/code/when-coin/when_coin_2/lib/screens/rate/widgets/title.dart:19:19)
#4      _RootZone.runUnaryGuarded (dart:async/zone.dart:1314:10)
#5      _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:336:11)
#6      _DelayedData.perform (dart:async/stream_impl.dart:584:14)
#7      _StreamImplEvents.handleNext (dart:async/stream_impl.dart:700:11)
#8      _PendingEvents.schedule.<anonymous closure> (dart:async/stream_impl.dart:660:7)
#9      _microtaskLoop (dart:async/schedule_microtask.dart:41:21)
#10     _startMicrotaskLoop (dart:async/schedule_microtask.dart:50:5)

如何在不遇到异常情况的情况下使用ScrollController.jumpTo()函数?

class MyTitle extends StatelessWidget {
  final List<Category> categories;
  MyTitle({this.categories});

  @override
  Widget build(BuildContext context) {
    final _controller = ScrollController();
    double height = 36.0;
    // double width = MediaQuery.of(context).size.width * 0.8;

    BlocProvider.of(context).page.listen((page) {
      _controller.jumpTo(height * page);
    });

    return Container(
      height: height,
      // width: width,
      child: ListView(
        controller: _controller,
        scrollDirection: Axis.vertical,
        physics: NeverScrollableScrollPhysics(),
        children: categories
            .map((c) => _Title(
                  title: c.title,
                  index: categories.indexOf(c),
                ))
            .toList(),
      ),
    );
  }
}

1
我的错误是我将控制器附加到了Scrollbar而不是SingleChildScrollView``child: Scrollbar( child: SingleChildScrollView( controller: _scrollController,。 我想分享这个错误以防您们中的任何人遇到类似的情况。 - Adrian Moisa
6个回答

18

正如我上面的答案所述,当你尚未将滚动控制器附加到 ListView 或其他 ScrollView 时,你正在使用 ScrollController。你可以使用 hasClients 属性来检查这一点。

if (_scrollController.hasClients) {
  await _scrollController.animateTo(
    0.0,
    curve: Curves.easeOut,
    duration: const Duration(milliseconds: 300),
  );
}

1
“await” 可能未定义,但是我能够通过上述解决方案解决此类问题而不需要使用 await 功能。 - Raycherr
@bks 我的意思是我可以使用Sam Smets提供的解决方案,只是不需要使用'await'这个词。 - Raycherr
@Raycherr 好的,我理解了 - 对我来说最终它没有起作用。 - bks
@SamSmets 这个 hasClients 检查解决了上述出现的问题。 - ArifMustafa

5

检查控制器是否有客户端,然后延迟跳转:

if (_scrollController.hasClients) {
      Future.delayed(Duration(milliseconds: 50), () {
        _scrollController?.jumpTo(_scrollController.position.maxScrollExtent);
      });
    }

避免使用 Future.delayed()。相反,使用 WidgetBinding.instance.addPostFrameCallback() - zepolyerf

4
问题在于你每次在build方法中重新创建了_controller并订阅它。应该将控制器创建一次作为最终的类作用域属性。此外,你应该使用StatefulWidgetinitState方法中处理控制器并订阅流的释放。
class MyTitle extends StatefullWidget {
 MyTitle({this.categories});

 final List<Category> categories;

_MyTitleState createState() => _MyTitleState();
}

class _MyTitleState extends State<MyTitle> {
  final _controller = ScrollController(); // <--
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  void initState() {
    super.initState();
    BlocProvider.of(context, listen: false).page.listen((page) {
      if(_constoller.hasClients) {
      _controller.jumpTo(height * page);
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    double height = 36.0;
    // double width = MediaQuery.of(context).size.width * 0.8;

    return Container(
      height: height,
      // width: width,
      child: ListView(
        controller: _controller,
        scrollDirection: Axis.vertical,
        physics: NeverScrollableScrollPhysics(),
        children: categories
            .map((c) => _Title(
                  title: c.title,
                  index: categories.indexOf(c),
                ))
            .toList(),
      ),
    );
  }
}

0

对于想要另一种方法的人,可以使用Flutter After Layout

void initState() {
    super.initState();
    WidgetsBinding.instance
        .addPostFrameCallback((_) => yourFunction(context));
  }

yourFunction 将在布局完成后执行。


这样做要好得多。 - supernerd

-1

检查控制器是否有客户端,然后延迟跳转:

if (!_scrollController.hasClients) {
      Future.delayed(Duration(milliseconds: 50), () {
        _scrollController?.jumpTo(_scrollController.position.maxScrollExtent);
      });
    }

-3

在将 scrollController 添加到 ScrollView(列表视图)之前,您正在尝试使用它进行跳转。我们必须在添加到控制器后再进行跳转。请参考下面的代码。

 // task1
 Future.delayed(Duration.zero, () => 
 { // task2
  BlocProvider.of(context).page.listen((page) { _controller.jumpTo(height * page); }); 
 });
// task3

这与DispatchQueue.main.async非常相似,因为持续时间为。执行顺序将是任务1、任务3、任务2。


1
这对我来说没有意义。流,因此是jumpTo事件,在页面构建之后很长时间才会触发和抛出断言。它似乎会为每个框架抛出断言。解决方案也不起作用。 - Luke Pighetti

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接