滚动带有ScrollController的CustomScrollView时,SliverAppbar仍然可见
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了滚动带有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 == 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 == 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();
}
}
答案
使用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 == 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 == 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();
}
}
以上是关于滚动带有ScrollController的CustomScrollView时,SliverAppbar仍然可见的主要内容,如果未能解决你的问题,请参考以下文章
Flutter 可滚动组件:ScrollController
颤振错误:'ScrollController 未附加到任何滚动视图。'在滚动
如何使用 ScrollController 检查 ListView 滚动速度