SliverAppBar
とアイテムのリストがあるTabBar
と、スクロールコントローラーが文句を言います。
私はNestedScrollView
それを次SliverAppBar
のように実装しました。SliverPersistentHeader
TabBar
NestedScrollView
TabBarView
SliverLists
CustomScrollViews
スクロール コントローラが問題を引き起こしているようです。複数のビューに関連付けられていることを示しています。そこで、各カスタム スクロール ビューに新しいスクロール コントローラーを追加しようとしましたが、これにより、リスト自体をスクロールするときにスライバー アプリ バーが折りたたまれなくなります。
これとは別に、タブを切り替えたときのスクロール状態も記憶していません... を使用してみました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;
}
}