我想要2个带有ListView的标签页共享一个RefreshIndicator。但是,一个RefreshIndicator必须具有Scrollable作为子级(不是TabBarView),因此我改为尝试每个标签制作2个RefreshIndicators,如下面的代码所示。
但这带来了一个不同的问题,我还想要一个浮动的AppBar,这意味着我必须使用NestedScrollView。因此,无论何时向下滚动,我最终都会触发两个RefreshIndicators的onRefresh方法。而我只需要刷新一下即可。
import 'package:flutter/material.dart';
main() {
runApp(
MaterialApp(
home: DefaultTabController(
length: 2,
child: Scaffold(
body: NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) {
return [
SliverAppBar(
floating: true,
snap: true,
bottom: TabBar(
tabs: [
Tab(text: 'Page1'),
Tab(text: 'Page2'),
],
),
),
];
},
body: TabBarView(
children: [
Page(1),
Page(2),
],
),
),
),
),
),
);
}
class Page extends StatefulWidget {
final pageNumber;
Page(this.pageNumber);
createState() => PageState();
}
class PageState extends State<Page> with AutomaticKeepAliveClientMixin {
get wantKeepAlive => true;
build(context){
super.build(context);
return RefreshIndicator(
onRefresh: () => Future(() async {
print('Refreshing page no. ${widget.pageNumber}'); // This prints twice once both tabs have been opened
await Future.delayed(Duration(seconds: 5));
}),
child: ListView.builder(
itemBuilder: ((context, index){
return ListTile(
title: Text('Item $index')
);
}),
)
);
}
}
AutomaticKeepAliveClientMixin可以防止每次切换标签时页面重建,因为在我的实际应用中这将是一个昂贵的过程。
对于两个选项卡都使用单个RefreshIndicator的解决方案将是最理想的,但是可以提供任何帮助。
答案 0 :(得分:1)
如果要浮动应用程序栏,则必须使用嵌套滚动视图和条状应用程序栏。当您尝试在列表选项卡视图的子列表中使用刷新指示器时,刷新指示器不起作用。这仅仅是因为嵌套的滚动视图。
如果您假设有两个列表作为选项卡视图的子级,则您一次只想刷新一个或两个,则请遵循以下代码。
用刷新指示器将嵌套的滚动视图包裹起来,然后在刷新部分
RefreshIndicator(
color: Colors.red,
displacement: 70,
onRefresh: _refreshGeneralList,
key: _refreshIndicatorKey,
child: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
centerTitle: true,
title: Text(
"App Bar",
style: TextStyle(
color: Colors.black,
fontSize: 14,
),
leading: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: IconButton(
icon: Icon(Icons.profile),
onPressed: () {
Navigator.of(context).pop();
},
),
),
actions: [
InkWell(
onTap: () {
setState(() {
isPremium = !isPremium;
});
},
child: Icon(
Icons.monetization_on,
color: isPremium ? Colors.green : Colors.blueGrey,
size: 33,
)),
SizedBox(
width: 25,
)
],
elevation: 0,
backgroundColor: Colors.white,
pinned: true,
floating: true,
forceElevated: innerBoxIsScrolled,
bottom: isPremium
? TabBar(
labelStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600),
labelColor: Colors.blueGrey,
indicatorColor:Colors.red,
unselectedLabelColor:
Colors.green,
labelPadding: EdgeInsets.symmetric(vertical: 13.5),
controller: _tabController,
tabs: [
Text(
"General",
),
Text(
"Visitors",
),
])
: null,
)
];
},
body: isPremium
? TabBarView(
controller: _tabController,
children: [
generalNotificationsList(context),
visitorsNotificationsList(context),
])
: generalNotificationsList(context),
),
),
添加一个调用未来的函数。在以后的部分中,如果滚动标签栏视图的一个或两个子级,我们将编写代码。
Future _refreshGeneralList() async{
print('refreshing ');
GeneralNotificationBloc().add(LoadGeneralNotificationEvent(context));
PremiumNotificationBloc().add(LoadPremiumNotificationEvent(context));
return Future.delayed(Duration(seconds: 1));
}
答案 1 :(得分:0)
可以用 RefreshIndicator 包裹整个 NestedScrollView 并更新 notificationPredicate:
DefaultTabController(
length: tabs.length,
child: RefreshIndicator(
notificationPredicate: (notification) {
// with NestedScrollView local(depth == 0) OverscrollNotification are not sent
if (notification is OverscrollNotification) {
return notification.depth == 2;
}
return notification.depth == 0;
},
onRefresh: () => Future.value(null),
child: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return [
SliverAppBar(...)
];
},
body: TabBarView(
children: tabs,
),
),
),
)
或者使用
答案 2 :(得分:0)
此代码对我有用,它主要建立在@Nuts 答案之上,但需要进行一些调整,我的编辑被拒绝了
DefaultTabController(
length: tabs.length,
child: RefreshIndicator(
notificationPredicate: (notification) {
// with NestedScrollView local(depth == 2) OverscrollNotification are not sent
return notification.depth == 2;
},
onRefresh: () => Future.value(null),
child: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return [
SliverAppBar(...)
];
},
body: TabBarView(
children: tabs,
),
),
),
)