所以,我有这个“通知”屏幕,显示用户的通知。导航到此屏幕时,它将变为空白,因为通知是从后端API实时加载的。
以下是一些用于说明问题的代码:
class _MyItemsPageState extends State<MyItemsPage> {
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
new GlobalKey<RefreshIndicatorState>();
List<MyItem> _items = [];
@override
void initState() {
super.initState();
// Nothing is displaying on screen initially, since the items are loaded from API on startup.
// Preferably in this state, the refresh indicator would be shown while the items load.
// It's not currently possible in this place, since it seems that the Widget hasn't been built yet.
_refreshIndicatorKey.currentState.show(); // currentState null at this time, so the app crashes.
_loadItems();
}
// (unrelated code removed)
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new RefreshIndicator(
key: _refreshIndicatorKey,
onRefresh: _loadItems,
child: new ListView(
padding: new EdgeInsets.symmetric(vertical: 8.0),
children: _buildItemWidgets(),
),
),
);
}
}
问题是,当 initState()函数被调用时, _refreshIndicator.currentState 为空,因为尚未构建Widget。
在这种情况下,在RefreshIndicator上调用 show()的适当位置是什么?
答案 0 :(得分:5)
事实证明,将_refreshIndicator.currentState.show()
置于我的_loadItems()
函数中就可以了。如下所示:
Future _loadItems() async {
_refreshIndicatorKey.currentState?.show();
var items = await getItems();
setState(() {
_items = items;
});
}
然后我在_loadItems()
方法中调用initState()
函数,但从那里删除了_refreshIndicatorKey.currentState.show()
行。
我认为这只会因为一些可能的竞争条件而起作用,因为loadItems函数的async
性质,并且觉得应该有一些与生命周期相关的其他解决方案小部件状态。
答案 1 :(得分:1)
我认为最好的选择是使用https://docs.flutter.io/flutter/scheduler/SchedulerBinding/addPostFrameCallback.html来调度_refreshIndicatorKey.currentState.show()
,这样在小部件完成构建时就可以安全地调用show()方法,而不必依赖于固定方法。未来的时间量。
这里是示例:
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((_){ _refreshIndicatorKey.currentState?.show(); } );
}
但是,可以肯定的是,我确实认为小部件本身可以具有额外的参数,以允许立即进行操作。
答案 2 :(得分:0)
@override
void initState() {
super.initState();
Future.delayed(Duration(milliseconds: 200)).then((_) {
_refreshIndicatorKey.currentState?.show();
});
}
更新
基本上.show()
将触发RefreshIndicator的onRefresh
。因此,实际上,通过在.show()
内部调用_loadItems
将触发_loadItems
两次。我们不想要那个。所以我将.show()
移到initState
,现在我们很好了,它只调用了一次。
答案 3 :(得分:0)
这不适用于 CupertinoActivityIndicator。
我最终将它添加到了 SliverList 上方的我的 CustomScrollView slivers 列表中,并包含了一个动画容器,用于检查是否从我的提供者加载。运行良好,流畅,没有问题。
SliverToBoxAdapter(
child: AnimatedContainer(
width: checkingForNewData || _images.isEmpty ? 70 : 0,
height: checkingForNewData || _images.isEmpty ? 70 : 0,
duration: Duration(milliseconds: 350),
curve: Curves.ease,
child: Visibility(
visible: checkingForNewData || _images.isEmpty,
child: SizedBox(
height: 150,
width: 50,
child: CupertinoActivityIndicator(
radius: 12,
),
),
),
),
),
答案 4 :(得分:0)
我编写了一个包 (declarative_refresh_indicator),为 RefreshIndicator
提供声明式 API,类似于 Switch
和 Checkbox
小部件。
与其他解决方案不同,它还可以允许指标在不执行刷新回调的情况下初始显示,从而允许在其他地方请求数据。
这是一个在小部件初始化时加载的示例:
class _MyWidgetState extends State<MyWidget> {
var _loading = false;
void _load() async {
setState(() => _loading = true);
await _getData();
if (mounted) setState(() => _loading = false);
}
@override
void initState() {
super.initState();
_load();
}
@override
Widget build(BuildContext context) {
return DeclarativeRefreshIndicator(
refreshing: _loading,
onRefresh: _load,
child: /* a scrollable widget */,
);
}
}