拥有一个SliverAppBar
和TabBar
一个项目列表会导致滚动控制器抱怨。
我已经通过以下方式实现了它,有一个包含NestedScrollView
a 的SliverAppBar
,一个SliverPersistentHeader
包含 theTabBar
的主体,然后NestedScrollView
是包含TabBarView
with SliverLists
inside的主体CustomScrollViews
。
滚动控制器似乎会导致问题。它说它附加到多个视图。所以我尝试为每个自定义滚动视图添加一个新的滚动控制器,但这会阻止 sliver 应用栏在滚动列表本身时折叠。
除此之外,它也不记得切换标签时的滚动状态......我试过使用AutomaticKeepAliveClientMixin
,但这似乎不起作用。关于如何解决滚动问题和滚动控制器状态的任何建议都会受到欢迎?:)
另请注意:我仅在 Flutter Web 上进行测试,而不是在移动设备上进行测试...
我不知道这是我的代码中的错误还是颤振中的错误。
请参阅下面的颤振医生:
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel unknown, 2.5.0, on Microsoft Windows [Version 10.0.22000.434], locale en-ZA)
[!] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
X cmdline-tools component is missing
Run `path/to/sdkmanager --install "cmdline-tools;latest"`
See https://developer.android.com/studio/command-line for more details.
X Android license status unknown.
Run `flutter doctor --android-licenses` to accept the SDK licenses.
See https://flutter.dev/docs/get-started/install/windows#android-setup for more details.
[√] Chrome - develop for the web
[√] Android Studio (version 4.0)
[√] Connected device (2 available)
! Doctor found issues in 1 category.
滚动控制器抛出的异常如下:
══╡ EXCEPTION CAUGHT BY ANIMATION LIBRARY ╞═════════════════════════════════════════════════════════
The following assertion was thrown while notifying status listeners for AnimationController:
The provided ScrollController is currently attached to more than one ScrollPosition.
The Scrollbar requires a single ScrollPosition in order to be painted.
When the scrollbar is interactive, the associated Scrollable widgets must have unique
ScrollControllers. The provided ScrollController must be unique to a Scrollable widget.
When the exception was thrown, this was the stack:
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 251:49 throw_
packages/flutter/src/widgets/scrollbar.dart 1315:9 <fn>
packages/flutter/src/widgets/scrollbar.dart 1338:14 [_debugCheckHasValidScrollPosition]
packages/flutter/src/widgets/scrollbar.dart 1257:14 [_validateInteractions]
packages/flutter/src/animation/listener_helpers.dart 233:27 notifyStatusListeners
packages/flutter/src/animation/animation_controller.dart 814:7 [_checkStatusChanged]
packages/flutter/src/animation/animation_controller.dart 748:5 [_startSimulation]
packages/flutter/src/animation/animation_controller.dart 611:12 [_animateToInternal]
packages/flutter/src/animation/animation_controller.dart 495:12 reverse
packages/flutter/src/widgets/scrollbar.dart 1412:37 <fn>
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/private/isolate_helper.dart 48:19 internalCallback
The AnimationController notifying status listeners was:
AnimationController#25a6e(◀ 1.000)
下面是我的代码:
class MyApp extends StatefulWidget {
MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> { // with AutomaticKeepAliveClientMixin
ScrollController _scrollController = ScrollController();
bool _isAppBarExpanded = true;
@override
void initState() {
super.initState();
_scrollController.addListener(() {
_isAppBarExpanded = _scrollController.hasClients && _scrollController.offset < (200 - kToolbarHeight);
setState(() { });
});
}
@override
Widget build(BuildContext context) {
// super.build(context); // AutomaticKeepAlive
const String title = "Floating App Bar";
return MaterialApp(
title: title,
home: Scaffold(
body: DefaultTabController(
length: 2,
child: NestedScrollView(
controller: _scrollController,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget> [
SliverAppBar(
backgroundColor: (_isAppBarExpanded) ? Colors.white : Colors.blue,
expandedHeight: 200.0,
floating: false,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: Text(
"Collapsing Toolbar",
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
),
),
background: ClipRRect(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(30.0),
bottomRight: Radius.circular(30.0),
),
child: Image.network(
"https://images.pexels.com/photos/396547/pexels-photo-396547.jpeg?auto=compress&cs=tinysrgb&h=350",
fit: BoxFit.cover,
),
),
),
),
SliverPersistentHeader(
delegate: _SliverAppBarDelegate(
TabBar(
labelColor: Colors.black87,
unselectedLabelColor: Colors.grey,
tabs: <Widget> [
Tab(
icon: Icon(Icons.info),
text: "Tab 1",
),
Tab(
icon: Icon(Icons.lightbulb_outline),
text: "Tab 2",
),
],
),
),
pinned: true,
),
];
},
body: TabBarView(
children: <Widget> [
CustomScrollView(
slivers: <Widget> [
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Text("Item " + index.toString());
},
childCount: 200,
// addAutomaticKeepAlives: true,
),
),
],
),
CustomScrollView(
slivers: <Widget> [
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Text("Test " + index.toString());
},
childCount: 100,
// addAutomaticKeepAlives: true,
),
),
],
),
],
),
),
),
),
);
}
// for AutomaticKeepAlive
// @override
// bool get wantKeepAlive {
// return true;
// }
}
class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
_SliverAppBarDelegate(this._tabBar);
final TabBar _tabBar;
@override
double get minExtent {
return _tabBar.preferredSize.height + 30;
}
@override
double get maxExtent {
return _tabBar.preferredSize.height + 30;
}
@override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
return Container(
alignment: Alignment.center,
color: Theme.of(context).scaffoldBackgroundColor,
child: Padding(
padding: EdgeInsets.only(top: 15.0, bottom: 15.0),
child: _tabBar,
),
);
}
@override
bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
return false;
}
}