首页 > 解决方案 > 如何在多个视图中同时使用 PrimaryScrollController

问题描述

PrimaryScrollController在自定义有状态小部件中使用 to,它基本上是ListView在用户滚动时从服务器加载项目的代码。我需要PrimaryScrollController这样才能在 iOS 上点击应用栏的顶部滚动回顶部。

我在多个页面中使用相同的小部件,这些小部件由BottomNavigationBar. 我也在尝试保持状态,这样当用户滚动,然后在底部导航栏中切换到另一个选项卡,然后返回到第一个选项卡时,滚动位置和从服务器加载的所有项目仍然存在。

为了保持我正在使用的页面的状态IndexedStack。但是,由于两个页面同时使用,PrimaryScrollController我在控制台中收到一长串相同的错误消息:flutter: Another exception was thrown: ScrollController attached to multiple scroll views. 我理解这告诉我我不应该ScrollController在多个视图中使用相同的错误消息。简单的解决方案是为每个视图创建一个新实例。但是,我必须使用相同ScrollController的,因为我需要PrimaryScrollController点击 iOS 上的应用栏才能工作。

如果我只是忽略错误,实际代码会按照我想要的方式工作,每个页面都会保留自己的状态。 实际上,它并没有按照我想要的方式工作。侦听器在单独的页面上时不起作用。

我创建了一个示例应用程序来显示问题:

import 'package:flutter/material.dart';
import 'dart:math';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

List<Widget> _pages = [
  BodyWidget(key: PageStorageKey(1)),
  BodyWidget(key: PageStorageKey(2)),
];

class _MyHomePageState extends State<MyHomePage> {
  int _currentPage = 0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: IndexedStack(
        index: _currentPage,
        children: _pages,
      ),
      bottomNavigationBar: BottomNavigationBar(
          currentIndex: _currentPage,
          onTap: (int index) {
            setState(() {
              _currentPage = index;
            });
          },
          items: [
            BottomNavigationBarItem(
                icon: Icon(Icons.star_border), title: Text('page 1')),
            BottomNavigationBarItem(
                icon: Icon(Icons.crop_square), title: Text('page 2'))
          ]),
    );
  }
}

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

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

class _BodyWidgetState extends State<BodyWidget> {
  ScrollController _scrollController;
  List<int> numbers = [];

  void dataGenerator() {
    // simulate loading items from server
    var rng = new Random();
    for (var i = 0; i < 100; i++) {
      if (this.mounted) {
        setState(() {
          numbers.add(rng.nextInt(100));
        });
      }
    }
    print('loaded more items');
  }

  @override
  void initState() {
    super.initState();

    // This delay is required to get the build context
    Future.delayed(
      Duration.zero,
      () {
        _scrollController = PrimaryScrollController.of(context);
        _scrollController.addListener(() {
          if (_scrollController.position.pixels >=
              (_scrollController.position.maxScrollExtent - 50)) {
            dataGenerator();
          }
        });
        dataGenerator();
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: numbers.length,
      controller: _scrollController,
      itemBuilder: (BuildContext context, int index) {
        return Container(
          child: Card(
            child: Column(
              children: <Widget>[
                SizedBox(
                  height: 15,
                ),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  children: <Widget>[
                    Text('Random number: ' + numbers[index].toString()),
                    Text('Index: ' + index.toString())
                  ],
                ),
                SizedBox(
                  height: 15,
                ),
              ],
            ),
          ),
        );
      },
    );
  }
}

这是引发的错误:

flutter: ══╡ EXCEPTION CAUGHT BY FOUNDATION LIBRARY ╞════════════════════════════════════════════════════════
flutter: The following assertion was thrown while dispatching notifications for ScrollController:
flutter: ScrollController attached to multiple scroll views.
flutter: 'package:flutter/src/widgets/scroll_controller.dart': Failed assertion: line 111 pos 12:
flutter: '_positions.length == 1'
flutter:
flutter: Either the assertion indicates an error in the framework itself, or we should provide substantially
flutter: more information in this error message to help you determine and fix the underlying cause.
flutter: In either case, please report this assertion by filing a bug on GitHub:
flutter:   https://github.com/flutter/flutter/issues/new?template=BUG.md
flutter:
flutter: When the exception was thrown, this was the stack:
flutter: #2      ScrollController.position 
package:flutter/…/widgets/scroll_controller.dart:111
flutter: #3      _BodyWidgetState.initState.<anonymous closure>.<anonymous closure>
package:primaryscrollcontroller_test/main.dart:97
flutter: #4      ChangeNotifier.notifyListeners 
package:flutter/…/foundation/change_notifier.dart:206
flutter: #5      ChangeNotifier.notifyListeners 
package:flutter/…/foundation/change_notifier.dart:206
flutter: #6      ScrollPosition.notifyListeners 
package:flutter/…/widgets/scroll_position.dart:696
flutter: #7      ScrollPosition.setPixels 
package:flutter/…/widgets/scroll_position.dart:218
flutter: #8      ScrollPositionWithSingleContext.setPixels 
package:flutter/…/widgets/scroll_position_with_single_context.dart:84
flutter: #9      ScrollPositionWithSingleContext.applyUserOffset 
package:flutter/…/widgets/scroll_position_with_single_context.dart:127
flutter: #10     ScrollDragController.update 
package:flutter/…/widgets/scroll_activity.dart:372
flutter: #11     ScrollableState._handleDragUpdate 
package:flutter/…/widgets/scrollable.dart:496
flutter: #12     DragGestureRecognizer.handleEvent.<anonymous closure> 
flutter: #13     GestureRecognizer.invokeCallback 
package:flutter/…/gestures/recognizer.dart:166
flutter: #14     DragGestureRecognizer.handleEvent 
package:flutter/…/gestures/monodrag.dart:182
flutter: #15     PointerRouter._dispatch 
package:flutter/…/gestures/pointer_router.dart:73
flutter: #16     PointerRouter.route 
package:flutter/…/gestures/pointer_router.dart:101
flutter: #17     _WidgetsFlutterBinding&BindingBase&GestureBinding.handleEvent 
package:flutter/…/gestures/binding.dart:221
flutter: #18     _WidgetsFlutterBinding&BindingBase&GestureBinding.dispatchEvent 
package:flutter/…/gestures/binding.dart:199
flutter: #19     _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerEvent 
package:flutter/…/gestures/binding.dart:156
flutter: #20     _WidgetsFlutterBinding&BindingBase&GestureBinding._flushPointerEventQueue 
package:flutter/…/gestures/binding.dart:102
flutter: #21     _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerDataPacket 
package:flutter/…/gestures/binding.dart:86
flutter: #25     _invoke1  (dart:ui/hooks.dart:233:10)
flutter: #26     _dispatchPointerDataPacket  (dart:ui/hooks.dart:154:5)
flutter: (elided 5 frames from class _AssertionError and package dart:async)
flutter:
flutter: The ScrollController sending notification was:
flutter:   ScrollController#be798(2 clients)
flutter: ════════════════════════════════════════════════════════════════════════════════════════════════════
flutter: Another exception was thrown: ScrollController attached to multiple scroll views.
flutter: Another exception was thrown: ScrollController attached to multiple scroll views.
flutter: Another exception was thrown: ScrollController attached to multiple scroll views.
flutter: Another exception was thrown: ScrollController attached to multiple scroll views.

这条线flutter: Another exception was thrown: ScrollController attached to multiple scroll views.只是不断重复很多次

我该如何解决这个问题?

标签: dartflutter

解决方案


旧的,那里有很多答案。几个月前我有类似的问题:

问题指出,您有多个职位(几个可滚动视图)。

像往常一样声明:

ScrollController scrollController;

//注意,位置,包含有关滚动偏移、物理等的信息,这个“位置”是一个可迭代的。你可能会猜到,这里存储了所有滚动“位置”(每个为这个@附加的可滚动视图一个)滚动控制器”。

final scrollPositions = scrollController.positions;

//给定位置长度我抓取了最新的,在我的用例中总是最后一个(来回做新列表),您可以根据需要对其进行调整以获取位置信息。

final scrollPositionImInterested = scrollPositions
        .elementAt(scrollController.positions.length - 1);

希望这可以帮助有相同需求的人


推荐阅读