【问题标题】:ScrollController.jumpTo() "ScrollController not attached to any scroll views"ScrollController.jumpTo() "ScrollController 未附加到任何滚动视图"
【发布时间】:2019-02-17 03:59:42
【问题描述】:

我正在收听 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(),
      ),
    );
  }
}

【问题讨论】:

  • 我的错误是我将控制器连接到 Scrollbar 而不是 SingleChildScrollView child: Scrollbar( child: SingleChildScrollView( controller: _scrollController, 。我想我应该分享一下,以防你们中的任何人有类似的情况。

标签: flutter


【解决方案1】:

正如我上面的答案所述,当 ScrollController 尚未附加到 ListView 或其他 ScrollView 时,您正在使用它。您可以使用 hasClients 属性进行检查。

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

【讨论】:

  • 'await' 可能没有定义,但我可以在没有 await 功能的情况下使用上述解决方案解决此类问题。
  • @bks 我的意思是我可以使用 Sam Smets 给出的解决方案,而无需“等待”这个词
  • @Raycherr 好的,我明白了——对我来说它最终没有用。
  • @SamSmets 这个hasClients 检查解决了上述出现的问题。
【解决方案2】:

检查控制器有客户端蚂蚁然后延迟跳转:

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

【讨论】:

  • 避免使用Future.delayed()。相反,请使用WidgetBinding.instance.addPostFrameCallback()
【解决方案3】:

问题是您正在重新创建_controller 并在每个build 上订阅它。控制器必须作为最终类范围属性创建一次。此外,您应该使用StatefulWidgetdispose 控制器并订阅initState 方法中的流。

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

【讨论】:

    【解决方案4】:

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

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

    yourFunction会在布局完成后执行。

    【讨论】:

    • 这样好多了。
    【解决方案5】:

    检查控制器没有客户端蚂蚁然后延迟跳转:

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

    【讨论】:

      【解决方案6】:

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

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

      这与DispatchQueue.main.async 非常相似,因为持续时间为零。执行顺序为task1、task3、task2

      【讨论】:

      • 这对我来说没有意义。流,以及 jumpTo 事件,在页面构建很久之后触发并抛出断言。它似乎为每一帧抛出断言。该解决方案也不起作用。
      猜你喜欢
      • 2019-02-06
      • 2019-07-30
      • 1970-01-01
      • 2019-10-30
      • 2020-01-27
      • 2021-06-24
      • 1970-01-01
      • 2019-02-09
      • 2021-08-12
      相关资源
      最近更新 更多