在更改选项卡栏中更改选项卡之后,构建计划在框架期间安排
如果这个问题的标题可能有些不正确,我深表歉意,但是我不确定该标题是什么,这大多是正确的。
我正在使用类似于典型的Web浏览器的标签导航系统。由于Yuriy Luchaninov在如何创建动态tabbarview/呈现带有函数的新标签?和一些自定义更改。但是,我不确定如何解决一个问题是,我需要一个嵌入到标签栏本身的“创建新标签按钮”。添加了一个, 我使用riverpod来管理标签列表的状态。我正在使用StatenotifierProvider和一个自定义通知器。我不认为这与我的主要问题有关,但我认为有价值的背景是有助于实现我的问题的根源。
也就是说,当我更改我当前选择的哪个选项卡时,我会遇到错误 -
此错误出现有些不一致,似乎并没有明确的原因。我以前的实现似乎没有使用RiverPod,而是内置的列表作为标签栏状态。
当我添加一个选项卡时,当当前打开选项卡不是“选项卡”栏中的最终索引时,会抛出异常,然后尝试打开“新”选项卡。如果打开选项卡是当前选择的选项卡是标签栏的上一端,则不会抛出错误。
img src =“ https://i.sstatic.net/o46si.gif” alt =“错误”>
现在,要解决实际问题 - 我完全丢失了。我不确定为什么所谓的特定索引会成为导致它破裂的问题,尤其不是为什么称为例外是来自动画库。实际上,我试图通过设置andationDuration:diser.zero)
在Tab Bar中禁用所有动画(不是作为解决问题的方法,而是因为我不想要他们为我的解决方案)。
这是自定义标签小部件,与上面链接的问题中的一个大致相同。
class CustomTabView extends StatefulWidget {
final int itemCount;
final IndexedWidgetBuilder tabBuilder;
final IndexedWidgetBuilder pageBuilder;
final Widget? stub;
final ValueChanged<int>? onPositionChange;
final int? initPosition;
final void Function(int tabIndexPosition)? onMiddleMouseButtonTapUp;
final VoidCallback onAddPressed;
/// referenced from https://stackoverflow.com/questions/50036546/how-to-create-a-dynamic-tabbarview-render-a-new-tab-with-a-function-in-flutter
/// with edits for our usage
const CustomTabView({
required this.itemCount,
required this.tabBuilder,
required this.pageBuilder,
this.stub,
this.onPositionChange,
this.initPosition,
this.onMiddleMouseButtonTapUp,
required this.onAddPressed,
});
@override
_CustomTabViewState createState() => _CustomTabViewState();
}
class _CustomTabViewState extends State<CustomTabView>
with TickerProviderStateMixin {
late TabController controller;
late int _currentCount;
late int _currentPosition;
@override
void initState() {
_currentPosition = widget.initPosition ?? 0;
controller = TabController(
length: widget.itemCount,
vsync: this,
initialIndex: _currentPosition,
animationDuration: Duration.zero);
controller.addListener(onPositionChange);
_currentCount = widget.itemCount;
super.initState();
}
@override
void didUpdateWidget(CustomTabView oldWidget) {
if (_currentCount != widget.itemCount) {
controller.removeListener(onPositionChange);
controller.dispose();
if (widget.initPosition != null) {
_currentPosition = widget.initPosition!;
}
if (_currentPosition > widget.itemCount - 1) {
_currentPosition = widget.itemCount - 1;
_currentPosition = _currentPosition < 0 ? 0 : _currentPosition;
if (widget.onPositionChange is ValueChanged<int>) {
WidgetsBinding.instance?.addPostFrameCallback((_) {
if (mounted) {
widget.onPositionChange!(_currentPosition);
}
});
}
}
_currentCount = widget.itemCount;
setState(() {
controller = TabController(
length: widget.itemCount,
vsync: this,
initialIndex: _currentPosition,
animationDuration: Duration.zero);
controller.addListener(onPositionChange);
});
} else if (widget.initPosition != null) {
controller.animateTo(widget.initPosition!);
}
super.didUpdateWidget(oldWidget);
}
@override
void dispose() {
controller.removeListener(onPositionChange);
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (widget.itemCount < 1) return widget.stub ?? Container();
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Row(
children: [
Container(
alignment: Alignment.centerLeft,
child: GestureDetector(
onTertiaryTapUp: (details) {
if (widget.onMiddleMouseButtonTapUp != null) {
widget.onMiddleMouseButtonTapUp!(_currentPosition);
}
},
child: TabBar(
padding: EdgeInsets.zero,
labelPadding: EdgeInsets.zero,
isScrollable: true,
controller: controller,
indicator: BoxDecoration(
color: Colors.grey.withOpacity(1),
),
tabs: List.generate(
widget.itemCount,
(index) => widget.tabBuilder(context, index),
),
),
),
),
TextButton(
onPressed: widget.onAddPressed,
child: const Icon(Icons.add),
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
minimumSize: Size.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.circular(5)),
),
)
],
),
Expanded(
child: TabBarView(
controller: controller,
children: List.generate(
widget.itemCount,
(index) => widget.pageBuilder(context, index),
),
),
),
],
);
}
onPositionChange() {
if (!controller.indexIsChanging) {
_currentPosition = controller.index;
if (widget.onPositionChange is ValueChanged<int>) {
widget.onPositionChange!(_currentPosition);
}
}
}
}
这是用于添加选项卡的提供商系统:
final tabsNotifierProvider =
StateNotifierProvider<TabsNotifier, List<TabObject>>(
(ref) => TabsNotifier(),
);
class TabsNotifier extends StateNotifier<List<TabObject>> {
TabsNotifier() : super([]);
void addTab(TabObject tab) {
state = [...state, tab];
}
void removeTabByIndex(int index) {
List<TabObject> newState = List.from(state);
newState.removeAt(index);
state = newState;
}
}
这是系统的其余部分:
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
FocusNode currentTabFocusNode = FocusNode();
@override
Widget build(BuildContext context) {
return Scaffold(
body: Consumer(builder: (context, ref, child) {
final tabs = ref.watch(tabsNotifierProvider);
if (tabs.isEmpty) {
Future.delayed(
Duration.zero,
() => ref.read(tabsNotifierProvider.notifier).addTab(
TabObject(tabType: TabOjectType.ENTRY, tabName: 'default')),
);
}
return CustomTabView(
onAddPressed: () {
Future.delayed(
Duration.zero,
() => ref
.read(tabsNotifierProvider.notifier)
.addTab(TabObject(tabType: TabOjectType.EMPTY)));
},
onMiddleMouseButtonTapUp: (index) {
Future.delayed(
Duration.zero,
(() => ref
.read(tabsNotifierProvider.notifier)
.removeTabByIndex(index)));
},
itemCount: tabs.length,
tabBuilder: (context, index) {
if (tabs[index].tabType == TabOjectType.CREATE_NEW) {
return tabs[index].prefixImagePlaceholder;
} else {
return Row(
children: [
ConstrainedBox(
constraints:
const BoxConstraints(maxHeight: 25, maxWidth: 25),
child: tabs[index].prefixImagePlaceholder),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text('${tabs[index].tabName}'),
),
TextButton(
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
minimumSize: Size.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.circular(5)),
),
onPressed: () {
if (index != 0) {
setState(() {
tabs.removeAt(index);
});
} else {
// TODO close whole project
}
},
child: const Icon(
Icons.close,
color: Colors.grey,
))
],
);
}
},
pageBuilder: (context, index) {
return RawKeyboardListener(
focusNode: currentTabFocusNode,
onKey: (value) {
if (value is RawKeyDownEvent) {
if (value.isControlPressed &&
value.logicalKey == LogicalKeyboardKey.keyW) {
print('ctrl w pressed');
setState(() {
tabs.removeAt(index);
});
}
}
},
child: Center(
child: Text('tab $index'),
),
);
});
}),
);
}
}
enum TabOjectType {
CREATE_NEW,
ENTRY,
HIGH_LEVEL,
HOME,
CURRENT_PROJECT,
EMPTY,
}
class TabObject {
static TabObject createNew() {
return TabObject(
tabType: TabOjectType.CREATE_NEW,
prefixImagePlaceholder: Icon(Icons.add),
tabName: '');
}
Widget prefixImagePlaceholder;
/// this is the optionally provided actual object which the tab is referencing -- such as the entry of an entry page or its id
dynamic object;
String? tabName;
TabOjectType tabType;
TabObject(
{required this.tabType,
this.prefixImagePlaceholder = const Placeholder(
fallbackHeight: 10,
fallbackWidth: 10,
),
this.object,
this.tabName});
}
有人知道可能导致此错误的原因吗?
---编辑---
这是有关该错误的一些其他信息。这是错误堆栈和称为异常。我不确定为什么动画控制器是称为异常的原因。
Exception caught by animation library ═════════════════════════════════
The following assertion was thrown while notifying listeners for AnimationController:
Build scheduled during frame.
While the widget tree was being built, laid out, and painted, a new frame was scheduled to rebuild the widget tree.
This might be because setState() was called from a layout or paint callback. If a change is needed to the widget tree, it should be applied as the tree is being built. Scheduling a change for the subsequent frame instead results in an interface that lags behind by one frame. If this was done to make your build dependent on a size measured at layout time, consider using a LayoutBuilder, CustomSingleChildLayout, or CustomMultiChildLayout. If, on the other hand, the one frame delay is the desired effect, for example because this is an animation, consider scheduling the frame in a post-frame callback using SchedulerBinding.addPostFrameCallback or using an AnimationController to trigger the animation.
When the exception was thrown, this was the stack
#0 WidgetsBinding._handleBuildScheduled.<anonymous closure>
package:flutter/…/widgets/binding.dart:747
#1 WidgetsBinding._handleBuildScheduled
package:flutter/…/widgets/binding.dart:770
#2 BuildOwner.scheduleBuildFor
package:flutter/…/widgets/framework.dart:2485
#3 Element.markNeedsBuild
package:flutter/…/widgets/framework.dart:4443
#4 State.setState
package:flutter/…/widgets/framework.dart:1141
#5 _AnimatedState._handleChange
package:flutter/…/widgets/transitions.dart:128
#6 AnimationLocalListenersMixin.notifyListeners
package:flutter/…/animation/listener_helpers.dart:155
#7 AnimationController.value=
package:flutter/…/animation/animation_controller.dart:365
#8 TabController._changeIndex
package:flutter/…/material/tab_controller.dart:201
#9 TabController.index=
package:flutter/…/material/tab_controller.dart:219
#10 _TabBarViewState._handleScrollNotification
package:flutter/…/material/tabs.dart:1458
#11 NotificationListener._dispatch
package:flutter/…/widgets/notification_listener.dart:151
#12 Notification.visitAncestor
package:flutter/…/widgets/notification_listener.dart:67
#13 ViewportNotificationMixin.visitAncestor
package:flutter/…/widgets/scroll_notification.dart:31
#14 Element.visitAncestorElements
package:flutter/…/widgets/framework.dart:4254
#15 Notification.dispatch
package:flutter/…/widgets/notification_listener.dart:83
#16 ScrollActivity.dispatchScrollEndNotification
package:flutter/…/widgets/scroll_activity.dart:104
#17 ScrollPosition.didEndScroll
package:flutter/…/widgets/scroll_position.dart:907
#18 ScrollPosition.beginActivity
package:flutter/…/widgets/scroll_position.dart:876
#19 ScrollPositionWithSingleContext.beginActivity
package:flutter/…/widgets/scroll_position_with_single_context.dart:114
#20 ScrollPositionWithSingleContext.goIdle
package:flutter/…/widgets/scroll_position_with_single_context.dart:129
#21 ScrollPositionWithSingleContext.goBallistic
package:flutter/…/widgets/scroll_position_with_single_context.dart:148
#22 BallisticScrollActivity.applyNewDimensions
package:flutter/…/widgets/scroll_activity.dart:549
#23 ScrollPosition.applyNewDimensions
package:flutter/…/widgets/scroll_position.dart:623
#24 ScrollPositionWithSingleContext.applyNewDimensions
package:flutter/…/widgets/scroll_position_with_single_context.dart:104
#25 ScrollPosition.applyContentDimensions
package:flutter/…/widgets/scroll_position.dart:553
#26 _PagePosition.applyContentDimensions
package:flutter/…/widgets/page_view.dart:473
#27 RenderViewport.performLayout
package:flutter/…/rendering/viewport.dart:1493
#28 RenderObject._layoutWithoutResize
package:flutter/…/rendering/object.dart:1731
#29 PipelineOwner.flushLayout
package:flutter/…/rendering/object.dart:887
#30 RendererBinding.drawFrame
package:flutter/…/rendering/binding.dart:497
#31 WidgetsBinding.drawFrame
package:flutter/…/widgets/binding.dart:883
#32 RendererBinding._handlePersistentFrameCallback
package:flutter/…/rendering/binding.dart:363
#33 SchedulerBinding._invokeFrameCallback
package:flutter/…/scheduler/binding.dart:1144
#34 SchedulerBinding.handleDrawFrame
package:flutter/…/scheduler/binding.dart:1081
#35 SchedulerBinding._handleDrawFrame
package:flutter/…/scheduler/binding.dart:995
#39 _invoke (dart:ui/hooks.dart:151:10)
#40 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:308:5)
#41 _drawFrame (dart:ui/hooks.dart:115:31)
(elided 3 frames from dart:async)
The AnimationController notifying listeners was: AnimationController#63a5c(▶ 3.000; paused)
I apologize if the title of this question might be slightly incorrect, however I was unsure what to title it and it is mostly correct.
I am working on a tabbed navigation system similar to that of a typical web browser. I have most of the actual tabbed system working, thanks to Yuriy Luchaninov's answer in How to create a dynamic TabBarView/ Render a new Tab with a function in Flutter? and a few custom changes. However, one issue I am unsure how to solve is that I need a 'create new tab button' that is embedded into the tab bar itself. Having added one, I use riverpod to manage the state of the list of tabs. I'm using a StateNotifierProvider and a custom notifier. I don't think much of this has anything to do with my primary question, but I think it is valuable context to help get to the root of my issue.
Namely, I am getting an error when I change which tab I have currently selected -
This error appears somewhat inconsistently, and does not seem to have a clear cause. I had a previous implementation that had seemingly the same (or worse) error which did not use riverpod but instead a built-in List to serve as the tab bar state.
The exception is thrown when I add a tab while the currently open tab is not the final index in the tab bar, and then attempt to open the new tab. The error is not thrown if the tab is opened while the currently selected tab was the previous end of the tab bar.
Here's an example of it working properly:
Here's an example of it breaking:
Now, to get to the actual issue - which I am completely lost on. I am not sure why the specific index that is called would be an issue causing it to break, and especially not why the exception that is called is from the animation library. In fact, I have attempted to disable all animations in the tab bar by setting animationDuration: Duration.zero)
in the TabControllers used (not as a method to fix the issue, but because I don't want them for my solution).
Here is the custom tab widget that is mostly the same as the one found in the question linked above.
class CustomTabView extends StatefulWidget {
final int itemCount;
final IndexedWidgetBuilder tabBuilder;
final IndexedWidgetBuilder pageBuilder;
final Widget? stub;
final ValueChanged<int>? onPositionChange;
final int? initPosition;
final void Function(int tabIndexPosition)? onMiddleMouseButtonTapUp;
final VoidCallback onAddPressed;
/// referenced from https://stackoverflow.com/questions/50036546/how-to-create-a-dynamic-tabbarview-render-a-new-tab-with-a-function-in-flutter
/// with edits for our usage
const CustomTabView({
required this.itemCount,
required this.tabBuilder,
required this.pageBuilder,
this.stub,
this.onPositionChange,
this.initPosition,
this.onMiddleMouseButtonTapUp,
required this.onAddPressed,
});
@override
_CustomTabViewState createState() => _CustomTabViewState();
}
class _CustomTabViewState extends State<CustomTabView>
with TickerProviderStateMixin {
late TabController controller;
late int _currentCount;
late int _currentPosition;
@override
void initState() {
_currentPosition = widget.initPosition ?? 0;
controller = TabController(
length: widget.itemCount,
vsync: this,
initialIndex: _currentPosition,
animationDuration: Duration.zero);
controller.addListener(onPositionChange);
_currentCount = widget.itemCount;
super.initState();
}
@override
void didUpdateWidget(CustomTabView oldWidget) {
if (_currentCount != widget.itemCount) {
controller.removeListener(onPositionChange);
controller.dispose();
if (widget.initPosition != null) {
_currentPosition = widget.initPosition!;
}
if (_currentPosition > widget.itemCount - 1) {
_currentPosition = widget.itemCount - 1;
_currentPosition = _currentPosition < 0 ? 0 : _currentPosition;
if (widget.onPositionChange is ValueChanged<int>) {
WidgetsBinding.instance?.addPostFrameCallback((_) {
if (mounted) {
widget.onPositionChange!(_currentPosition);
}
});
}
}
_currentCount = widget.itemCount;
setState(() {
controller = TabController(
length: widget.itemCount,
vsync: this,
initialIndex: _currentPosition,
animationDuration: Duration.zero);
controller.addListener(onPositionChange);
});
} else if (widget.initPosition != null) {
controller.animateTo(widget.initPosition!);
}
super.didUpdateWidget(oldWidget);
}
@override
void dispose() {
controller.removeListener(onPositionChange);
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (widget.itemCount < 1) return widget.stub ?? Container();
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Row(
children: [
Container(
alignment: Alignment.centerLeft,
child: GestureDetector(
onTertiaryTapUp: (details) {
if (widget.onMiddleMouseButtonTapUp != null) {
widget.onMiddleMouseButtonTapUp!(_currentPosition);
}
},
child: TabBar(
padding: EdgeInsets.zero,
labelPadding: EdgeInsets.zero,
isScrollable: true,
controller: controller,
indicator: BoxDecoration(
color: Colors.grey.withOpacity(1),
),
tabs: List.generate(
widget.itemCount,
(index) => widget.tabBuilder(context, index),
),
),
),
),
TextButton(
onPressed: widget.onAddPressed,
child: const Icon(Icons.add),
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
minimumSize: Size.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.circular(5)),
),
)
],
),
Expanded(
child: TabBarView(
controller: controller,
children: List.generate(
widget.itemCount,
(index) => widget.pageBuilder(context, index),
),
),
),
],
);
}
onPositionChange() {
if (!controller.indexIsChanging) {
_currentPosition = controller.index;
if (widget.onPositionChange is ValueChanged<int>) {
widget.onPositionChange!(_currentPosition);
}
}
}
}
Here is the provider system used to add tabs:
final tabsNotifierProvider =
StateNotifierProvider<TabsNotifier, List<TabObject>>(
(ref) => TabsNotifier(),
);
class TabsNotifier extends StateNotifier<List<TabObject>> {
TabsNotifier() : super([]);
void addTab(TabObject tab) {
state = [...state, tab];
}
void removeTabByIndex(int index) {
List<TabObject> newState = List.from(state);
newState.removeAt(index);
state = newState;
}
}
And here is the rest of the system:
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
FocusNode currentTabFocusNode = FocusNode();
@override
Widget build(BuildContext context) {
return Scaffold(
body: Consumer(builder: (context, ref, child) {
final tabs = ref.watch(tabsNotifierProvider);
if (tabs.isEmpty) {
Future.delayed(
Duration.zero,
() => ref.read(tabsNotifierProvider.notifier).addTab(
TabObject(tabType: TabOjectType.ENTRY, tabName: 'default')),
);
}
return CustomTabView(
onAddPressed: () {
Future.delayed(
Duration.zero,
() => ref
.read(tabsNotifierProvider.notifier)
.addTab(TabObject(tabType: TabOjectType.EMPTY)));
},
onMiddleMouseButtonTapUp: (index) {
Future.delayed(
Duration.zero,
(() => ref
.read(tabsNotifierProvider.notifier)
.removeTabByIndex(index)));
},
itemCount: tabs.length,
tabBuilder: (context, index) {
if (tabs[index].tabType == TabOjectType.CREATE_NEW) {
return tabs[index].prefixImagePlaceholder;
} else {
return Row(
children: [
ConstrainedBox(
constraints:
const BoxConstraints(maxHeight: 25, maxWidth: 25),
child: tabs[index].prefixImagePlaceholder),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Text('${tabs[index].tabName}'),
),
TextButton(
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
minimumSize: Size.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.circular(5)),
),
onPressed: () {
if (index != 0) {
setState(() {
tabs.removeAt(index);
});
} else {
// TODO close whole project
}
},
child: const Icon(
Icons.close,
color: Colors.grey,
))
],
);
}
},
pageBuilder: (context, index) {
return RawKeyboardListener(
focusNode: currentTabFocusNode,
onKey: (value) {
if (value is RawKeyDownEvent) {
if (value.isControlPressed &&
value.logicalKey == LogicalKeyboardKey.keyW) {
print('ctrl w pressed');
setState(() {
tabs.removeAt(index);
});
}
}
},
child: Center(
child: Text('tab $index'),
),
);
});
}),
);
}
}
enum TabOjectType {
CREATE_NEW,
ENTRY,
HIGH_LEVEL,
HOME,
CURRENT_PROJECT,
EMPTY,
}
class TabObject {
static TabObject createNew() {
return TabObject(
tabType: TabOjectType.CREATE_NEW,
prefixImagePlaceholder: Icon(Icons.add),
tabName: '');
}
Widget prefixImagePlaceholder;
/// this is the optionally provided actual object which the tab is referencing -- such as the entry of an entry page or its id
dynamic object;
String? tabName;
TabOjectType tabType;
TabObject(
{required this.tabType,
this.prefixImagePlaceholder = const Placeholder(
fallbackHeight: 10,
fallbackWidth: 10,
),
this.object,
this.tabName});
}
Does anyone know what might be causing this error?
--- EDIT ---
Here is some additional information on the error. This is the error stack and the called exception. I'm not sure why it seems that the animation controller is what is calling the exception.
Exception caught by animation library ═════════════════════════════════
The following assertion was thrown while notifying listeners for AnimationController:
Build scheduled during frame.
While the widget tree was being built, laid out, and painted, a new frame was scheduled to rebuild the widget tree.
This might be because setState() was called from a layout or paint callback. If a change is needed to the widget tree, it should be applied as the tree is being built. Scheduling a change for the subsequent frame instead results in an interface that lags behind by one frame. If this was done to make your build dependent on a size measured at layout time, consider using a LayoutBuilder, CustomSingleChildLayout, or CustomMultiChildLayout. If, on the other hand, the one frame delay is the desired effect, for example because this is an animation, consider scheduling the frame in a post-frame callback using SchedulerBinding.addPostFrameCallback or using an AnimationController to trigger the animation.
When the exception was thrown, this was the stack
#0 WidgetsBinding._handleBuildScheduled.<anonymous closure>
package:flutter/…/widgets/binding.dart:747
#1 WidgetsBinding._handleBuildScheduled
package:flutter/…/widgets/binding.dart:770
#2 BuildOwner.scheduleBuildFor
package:flutter/…/widgets/framework.dart:2485
#3 Element.markNeedsBuild
package:flutter/…/widgets/framework.dart:4443
#4 State.setState
package:flutter/…/widgets/framework.dart:1141
#5 _AnimatedState._handleChange
package:flutter/…/widgets/transitions.dart:128
#6 AnimationLocalListenersMixin.notifyListeners
package:flutter/…/animation/listener_helpers.dart:155
#7 AnimationController.value=
package:flutter/…/animation/animation_controller.dart:365
#8 TabController._changeIndex
package:flutter/…/material/tab_controller.dart:201
#9 TabController.index=
package:flutter/…/material/tab_controller.dart:219
#10 _TabBarViewState._handleScrollNotification
package:flutter/…/material/tabs.dart:1458
#11 NotificationListener._dispatch
package:flutter/…/widgets/notification_listener.dart:151
#12 Notification.visitAncestor
package:flutter/…/widgets/notification_listener.dart:67
#13 ViewportNotificationMixin.visitAncestor
package:flutter/…/widgets/scroll_notification.dart:31
#14 Element.visitAncestorElements
package:flutter/…/widgets/framework.dart:4254
#15 Notification.dispatch
package:flutter/…/widgets/notification_listener.dart:83
#16 ScrollActivity.dispatchScrollEndNotification
package:flutter/…/widgets/scroll_activity.dart:104
#17 ScrollPosition.didEndScroll
package:flutter/…/widgets/scroll_position.dart:907
#18 ScrollPosition.beginActivity
package:flutter/…/widgets/scroll_position.dart:876
#19 ScrollPositionWithSingleContext.beginActivity
package:flutter/…/widgets/scroll_position_with_single_context.dart:114
#20 ScrollPositionWithSingleContext.goIdle
package:flutter/…/widgets/scroll_position_with_single_context.dart:129
#21 ScrollPositionWithSingleContext.goBallistic
package:flutter/…/widgets/scroll_position_with_single_context.dart:148
#22 BallisticScrollActivity.applyNewDimensions
package:flutter/…/widgets/scroll_activity.dart:549
#23 ScrollPosition.applyNewDimensions
package:flutter/…/widgets/scroll_position.dart:623
#24 ScrollPositionWithSingleContext.applyNewDimensions
package:flutter/…/widgets/scroll_position_with_single_context.dart:104
#25 ScrollPosition.applyContentDimensions
package:flutter/…/widgets/scroll_position.dart:553
#26 _PagePosition.applyContentDimensions
package:flutter/…/widgets/page_view.dart:473
#27 RenderViewport.performLayout
package:flutter/…/rendering/viewport.dart:1493
#28 RenderObject._layoutWithoutResize
package:flutter/…/rendering/object.dart:1731
#29 PipelineOwner.flushLayout
package:flutter/…/rendering/object.dart:887
#30 RendererBinding.drawFrame
package:flutter/…/rendering/binding.dart:497
#31 WidgetsBinding.drawFrame
package:flutter/…/widgets/binding.dart:883
#32 RendererBinding._handlePersistentFrameCallback
package:flutter/…/rendering/binding.dart:363
#33 SchedulerBinding._invokeFrameCallback
package:flutter/…/scheduler/binding.dart:1144
#34 SchedulerBinding.handleDrawFrame
package:flutter/…/scheduler/binding.dart:1081
#35 SchedulerBinding._handleDrawFrame
package:flutter/…/scheduler/binding.dart:995
#39 _invoke (dart:ui/hooks.dart:151:10)
#40 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:308:5)
#41 _drawFrame (dart:ui/hooks.dart:115:31)
(elided 3 frames from dart:async)
The AnimationController notifying listeners was: AnimationController#63a5c(▶ 3.000; paused)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论