亦余心之所善兮,虽九死其犹未悔。这篇文章主要讲述滚动带有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();
}
}
推荐阅读
- 如何在android studio 3中使用photoview 2.0.0
- 如何使用USB和EFI Shell格式化Windows 10的Medion Akoya S2218笔记本电脑
- 使用TestOps优化来升级DevOps软件管道
- 如何将PHP脚本的执行限制为命令行(检测是否从CLI运行PHP)
- 如何在Symfony 1.4中使用SwiftMailer从任务(控制台命令)发送电子邮件
- Prepaway-有关CompTIA 220-902认证考试的有用信息
- 如何在C#中检索控制台应用程序的可执行路径
- 如何在Google FeedBurner中启用Awareness API(FeedCount)
- 如何在Symfony 4中使用Doctrine正确计算表中的所有行