如何在从后端API等待数据时初始显示RefreshIndicator?

时间:2017-05-17 17:33:07

标签: flutter

所以,我有这个“通知”屏幕,显示用户的通知。导航到此屏幕时,它将变为空白,因为通知是从后端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()的适当位置是什么?

5 个答案:

答案 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)

Iiro Krankka 提出的解决方案对我没有用,但是他在种族条件上是正确的。因此,我尝试在initState上添加Future.delayed,并且成功了。这看起来很丑,但您知道,它确实有效。

@override
void initState() {
  super.initState();

  Future.delayed(Duration(milliseconds: 200)).then((_) {
    _refreshIndicatorKey.currentState?.show();
  });
}

更新

基本上.show()将触发RefreshIndicator的onRefresh。因此,实际上,通过在.show()内部调用_loadItems将触发_loadItems两次。我们不想要那个。所以我将.show()移到initState,现在我们很好了,它只调用了一次。

答案 3 :(得分:0)

这不适用于 CupertinoActivityIndi​​cator

我最终将它添加到了 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,类似于 SwitchCheckbox 小部件。

与其他解决方案不同,它还可以允许指标在不执行刷新回调的情况下初始显示,从而允许在其他地方请求数据。

这是一个在小部件初始化时加载的示例:

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 */,
    );
  }
}