问题如图所示,我写了一个小例子,来学习SingleChildScrollView
。代码很简单,这里给出:
class _ScrollTest extends StatelessWidget{
Widget build(BuildContext context){
String str = "asdafdsfsdgee";
return Scrollbar(
interactive: true,
child: SingleChildScrollView(
primary: true, //
padding: const EdgeInsets.all(16.0),
child: Center(
child: Column(
children: str.split("").map((e) => Text(e,)).toList(),
),
),
),
);
}
}
执行后不报错,然后,当鼠标在界面上滑动时,它会报错如下:
flutter: Interceptor: ══╡ EXCEPTION CAUGHT BY ANIMATION LIBRARY ╞══════════════════════
The following assertion was thrown while notifying status
listeners for AnimationController:
The Scrollbar's ScrollController has no ScrollPosition attached.
A Scrollbar cannot be painted without a ScrollPosition.
The Scrollbar attempted to use the PrimaryScrollController. This
ScrollController should be associated with the ScrollView that
the Scrollbar is being applied to.When ScrollView.scrollDirection
is Axis.vertical on mobile platforms will automatically use the
PrimaryScrollController if the user has not provided a
ScrollController. To use the PrimaryScrollController explicitly,
set ScrollView.primary to true for the Scrollable widget.
When the exception was thrown, this was the stack:
#0 RawScrollbarState._debugCheckHasValidScrollPosition. (package:flutter/src/widgets/scrollbar.dart:1459:9)
#1 RawScrollbarState._debugCheckHasValidScrollPosition (package:flutter/src/widgets/scrollbar.dart:1484:6)
#2 RawScrollbarState._validateInteractions (package:flutter/src/widgets/scrollbar.dart:1428:14)
#3 AnimationLocalStatusListenersMixin.notifyStatusListeners (package:flutter/src/animation/listener_helpers.dart:240:19)
#4 AnimationController._checkStatusChanged (package:flutter/src/animation/animation_controller.dart:850:7)
#5 AnimationController._startSimulation (package:flutter/src/animation/animation_controller.dart:781:5)
#6 AnimationController._animateToInternal (package:flutter/src/animation/animation_controller.dart:644:12)
#7 AnimationController.forward (package:flutter/src/animation/animation_controller.dart:493:12)
#8 RawScrollbarState._handleScrollNotification (package:flutter/src/widgets/scrollbar.dart:1822:37)
#9 _NotificationElement.onNotification (package:flutter/src/widgets/notification_listener.dart:130:38)
#10 _NotificationNode.dispatchNotification (package:flutter/src/widgets/framework.dart:3320:18)
#11 _NotificationNode.dispatchNotification (package:flutter/src/widgets/framework.dart:3323:13)
#12 _NotificationNode.dispatchNotification (package:flutter/src/widgets/framework.dart:3323:13)
#13 _NotificationNode.dispatchNotification (package:flutter/src/widgets/framework.dart:3323:13)
#14 Element.dispatchNotification (package:flutter/src/widgets/framework.dart:4942:24)
#15 Notification.dispatch (package:flutter/src/widgets/notification_listener.dart:60:13)
#16 ScrollActivity.dispatchScrollUpdateNotification (package:flutter/src/widgets/scroll_activity.dart:104:92)
#17 ScrollPosition.didUpdateScrollPositionBy (package:flutter/src/widgets/scroll_position.dart:1023:15)
#18 ScrollPositionWithSingleContext.pointerScroll (package:flutter/src/widgets/scroll_position_with_single_context.dart:234:7)
#19 ScrollableState._handlePointerScroll (package:flutter/src/widgets/scrollable.dart:938:16)
#20 PointerSignalResolver.resolve (package:flutter/src/gestures/pointer_signal_resolver.dart:103:32)
#21 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:501:29)
#22 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:475:22)
#23 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:425:11)
#24 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:420:7)
#25 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:383:5)
#26 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:330:7)
#27 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:299:9)
#31 _invoke1 (dart:ui/hooks.dart:330:10)
#32 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:429:7)
#33 _dispatchPointerDataPacket (dart:ui/hooks.dart:262:31)
(elided 3 frames from dart:async)
The AnimationController notifying status listeners was:
AnimationController#cdfbd(▶ 0.000)
═════════════════════════════════════════════════════════════════
先说问题出现的原因和解决,错误提示这是Flutter
中涉及到AnimationController
和Scrollbar
的一个异常。通常情况下,Scrollbar
需要一个与之关联的ScrollPosition
来进行绘制,但在这个情况下,没有找到ScrollController
或ScrollPosition
。
解决方法:
方法一:手动添加一个ScrollController
,使SingleChildScrollView
可以得到正确的ScrollController
。如果SingleChildScrollView.scrollDirection
设置为垂直方向(Axis.vertical
)且没有显式提供 ScrollController
,那么它会去寻找PrimaryScrollController
。
方法二:如果想显式地使用 PrimaryScrollController
,设置SingleChildScrollView.primary
为true
。
这里,我用了最简单的法子,在SingleChildScrollView
组件中,设置primary
为true
。即:
class _ScrollTest extends StatelessWidget{
Widget build(BuildContext context){
String str = "asdafdsfsdgee";
return Scrollbar(
interactive: true,
child: SingleChildScrollView(
primary: true, //新增加的
padding: const EdgeInsets.all(16.0),
child: Center(
child: Column(
children: str.split("").map((e) => Text(e,)).toList(),
),
),
),
);
}
}
最后,补充一点SingleChildScrollView
的相关内容,它类似于Android
中的ScrollView
,只能接收一个子组件,定义如下:
SingleChildScrollView({
this.scrollDirection = Axis.vertical, //滚动方向,默认是垂直方向this.reverse = false,
this.padding,
bool primary,
this.physics,
this.controller,
this.child,
})
其中,primary
属性表示是否使用widget
树中默认的PrimaryScrollController
(MaterialApp
组件树中已经默认包含一个PrimaryScrollController
了);当滑动方向为垂直方向(scrollDirection
值为Axis.vertical
)并且没有指定controller
时,primary
默认为true
。
而通常SingleChildScrollView
应在期望的内容不会超过屏幕太多时使用,这是因为SingleChildScrollView
不支持基于Sliver
的延迟加载模型,所以如果预计Viewport
可能包含超出屏幕尺寸太多的内容时,那么使用SingleChildScrollView
将会使性能变差,此时可以换成支持Sliver
延迟加载的可滚动组件,如ListView
。
注:其中涉及到的Sliver、Viewport等概念是来自Flutter的两种布局模型。