滚动带有ScrollController的CustomScrollView时,SliverAppbar仍然可见

亦余心之所善兮,虽九死其犹未悔。这篇文章主要讲述滚动带有ScrollController的CustomScrollView时,SliverAppbar仍然可见相关的知识,希望能为你提供帮助。
将ScrollController添加到函数timelineList()会导致SliverAppBar在滚动时保持可见(当滚动计数器列表时,SliverAppBar应该隐藏)。如果从列表中删除_scrollController(请参阅timelineList函数),问题就会消失,但这会引发一个新问题,我需要在滚动条到达底部时收听(获取更多内容)。
请参阅下面的示例应用,复制/粘贴并运行。

void main() => runApp(TestApp()); class TestApp extends StatelessWidget { final _scrollController = new ScrollController(); TestApp(){ _scrollController.addListener(() { if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) { print('Get more data'); } }); }@override Widget build(BuildContext context) { TestBloc bloc = TestBloc(); bloc.fetchTestTimeline(); bloc.fetchTestAppBarTxt1(); bloc.fetchTestAppBarTxt2(); return MaterialApp( home: new Scaffold( backgroundColor: Colors.grey[200], appBar: AppBar( backgroundColor: Colors.blueGrey, elevation: 0.0, ), body: NestedScrollView( headerSliverBuilder: (BuildContext contrxt, bool innerBoxIsScrolled) { return < Widget> [ buildSliverAppBar(context, bloc), ]; }, body: Column( children: < Widget> [ timelineList(bloc), ], ))), ); }buildSliverAppBar(context, TestBloc bloc){ return SliverAppBar( automaticallyImplyLeading: false, backgroundColor: Colors.grey[400], expandedHeight: 200.0, floating: true, snap: true, flexibleSpace: FlexibleSpaceBar( background: Column( children: < Widget> [ Container( padding: EdgeInsets.only(left: 2.0), height: 200, child: Column( children: < Widget> [ StreamBuilder( stream: bloc.testAppBarTxt1, initialData: null, builder: (BuildContext context, AsyncSnapshot< String> snapshot) { if (snapshot.data =https://www.songbingjia.com/android/= null) return buildProgressIndicator(true); return Expanded( child: Text('${snapshot.data}')); }), StreamBuilder( stream: bloc.testAppBarTxt2, initialData: null, builder: (BuildContext context, AsyncSnapshot< String> snapshot) { if (snapshot.data =https://www.songbingjia.com/android/= null) return buildProgressIndicator(true); return Expanded( child: Text('${snapshot.data}')); }), ], ), ) ], ), )); }timelineList(TestBloc bloc) { return StreamBuilder( stream: bloc.getTestTimeline, initialData: null, builder: (BuildContext context, AsyncSnapshot< List< int> > snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Expanded(child: buildProgressIndicator(true)); }List< int> val = snapshot.data; if (val.isNotEmpty) { addToTimelineList(val, bloc); return Expanded( child: CustomScrollView( controller: _scrollController, slivers: < Widget> [ SliverList( delegate: SliverChildListDelegate(new List< Widget> .generate(bloc.listTest.length, (int index) { if (index == bloc.listTest.length) { return buildProgressIndicator(bloc.isPerformingRequest); } else { return bloc.listTest[index]; } }) )) ],), ); } }); } void addToTimelineList(List< int> list, TestBloc bloc) { for (var val in list) { bloc.listTest.add(Text('$val')); } } }Widget buildProgressIndicator(showIndicator) { return new Padding( padding: const EdgeInsets.all(8.0), child: new Center( child: new Opacity( opacity: showIndicator ? 1.0 : 0.0, child: Container( width: 10.0, height: 10.0, child: new CircularProgressIndicator( )), ), ), ); } class TestBloc { String appbar1Val; String appbar2Val; List< Text> listTest = new List< Text> (); bool isPerformingRequest = false; final _testAppBarText1 = BehaviorSubject< String> (); Observable< String> get testAppBarTxt1 => _testAppBarText1.stream; final _testAppBarText2 = BehaviorSubject< String> (); Observable< String> get testAppBarTxt2 => _testAppBarText2.stream; final _testTimeline = PublishSubject< List< int> > (); Observable< List< int> > get getTestTimeline => _testTimeline.stream; fetchTestTimeline() async { List item = await Future.delayed( Duration(seconds: 2), () => List< int> .generate(100, (i) => i)); _testTimeline.sink.add(item); }fetchTestAppBarTxt1() async { appbar1Val = await Future.delayed(Duration(seconds: 2), () => "Text One"); _testAppBarText1.sink.add(appbar1Val); }fetchTestAppBarTxt2() async { appbar2Val = await Future.delayed(Duration(seconds: 2), () => "Text Two"); _testAppBarText2.sink.add(appbar2Val); } dispose() { _testAppBarText1.close(); _testAppBarText2.close(); _testTimeline.close(); } }

答案【滚动带有ScrollController的CustomScrollView时,SliverAppbar仍然可见】使用Notification Listener包装列表可以获得相同的结果。
NotificationListener< ScrollNotification> ( onNotification: (sn) { if (sn.metrics.pixels == sn.metrics.maxScrollExtent) { print('Get more data'); } }, child: CustomScrollView(...

编辑:由于我的初始答案没有涵盖animateTo用例,我通过删除外部NestedScrollView使其工作。这是修改后的例子。
void main() => runApp(TestApp()); class TestApp extends StatelessWidget { final _scrollController = new ScrollController(); TestApp() { _scrollController.addListener(() { if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) { print('Get more data'); } }); }@override Widget build(BuildContext context) { TestBloc bloc = TestBloc(); bloc.fetchTestTimeline(); bloc.fetchTestAppBarTxt1(); bloc.fetchTestAppBarTxt2(); return MaterialApp( home: new Scaffold( backgroundColor: Colors.grey[200], appBar: AppBar( backgroundColor: Colors.blueGrey, elevation: 0.0, ), body: Column( children: < Widget> [ timelineList(bloc), ], ), ), ); }buildSliverAppBar(context, TestBloc bloc) { return SliverAppBar( automaticallyImplyLeading: false, backgroundColor: Colors.grey[400], expandedHeight: 200.0, floating: true, snap: true, flexibleSpace: FlexibleSpaceBar( background: Column( children: < Widget> [ Container( padding: EdgeInsets.only(left: 2.0), height: 200, child: Column( children: < Widget> [ StreamBuilder( stream: bloc.testAppBarTxt1, initialData: null, builder: (BuildContext context, AsyncSnapshot< String> snapshot) { if (snapshot.data =https://www.songbingjia.com/android/= null) return buildProgressIndicator(true); return Expanded(child: Text('${snapshot.data}')); }), StreamBuilder( stream: bloc.testAppBarTxt2, initialData: null, builder: (BuildContext context, AsyncSnapshot< String> snapshot) { if (snapshot.data =https://www.songbingjia.com/android/= null) return buildProgressIndicator(true); return Expanded(child: Text('${snapshot.data}')); }), ], ), ) ], ), )); }timelineList(TestBloc bloc) { return StreamBuilder( stream: bloc.getTestTimeline, initialData: null, builder: (BuildContext context, AsyncSnapshot< List< int> > snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Expanded(child: buildProgressIndicator(true)); }List< int> val = snapshot.data; if (val.isNotEmpty) { addToTimelineList(val, bloc); return Expanded( child: CustomScrollView( controller: _scrollController, slivers: < Widget> [ buildSliverAppBar(context, bloc), SliverList( delegate: SliverChildListDelegate( new List< Widget> .generate(bloc.listTest.length, (int index) { if (index == bloc.listTest.length) { return buildProgressIndicator(bloc.isPerformingRequest); } else { return bloc.listTest[index]; } }))) ], ), ); } }); }void addToTimelineList(List< int> list, TestBloc bloc) { for (var val in list) { bloc.listTest.add(Text('$val')); } } }Widget buildProgressIndicator(showIndicator) { return new Padding( padding: const EdgeInsets.all(8.0), child: new Center( child: new Opacity( opacity: showIndicator ? 1.0 : 0.0, child: Container( width: 10.0, height: 10.0, child: new CircularProgressIndicator()), ), ), ); }class TestBloc { String appbar1Val; String appbar2Val; List< Text> listTest = new List< Text> (); bool isPerformingRequest = false; final _testAppBarText1 = BehaviorSubject< String> (); Observable< String> get testAppBarTxt1 => _testAppBarText1.stream; final _testAppBarText2 = BehaviorSubject< String> (); Observable< String> get testAppBarTxt2 => _testAppBarText2.stream; final _testTimeline = PublishSubject< List< int> > (); Observable< List< int> > get getTestTimeline => _testTimeline.stream; fetchTestTimeline() async { List item = await Future.delayed( Duration(seconds: 2), () => List< int> .generate(100, (i) => i)); _testTimeline.sink.add(item); }fetchTestAppBarTxt1() async { appbar1Val = await Future.delayed(Duration(seconds: 2), () => "Text One"); _testAppBarText1.sink.add(appbar1Val); }fetchTestAppBarTxt2() async { appbar2Val = await Future.delayed(Duration(seconds: 2), () => "Text Two"); _testAppBarText2.sink.add(appbar2Val); }dispose() { _testAppBarText1.close(); _testAppBarText2.close(); _testTimeline.close(); } }


    推荐阅读