【问题标题】:Flutter - Pull to refresh with RefreshIndicator in FutureBuilderFlutter - 在 FutureBuilder 中使用 RefreshIndicator 拉动刷新
【发布时间】:2020-09-18 11:18:38
【问题描述】:

编辑:Minimal reproducible example。请查看main.dart中的刷新方法。

我使用FutureBuilder 来显示网络请求的结果。我在initstate 中调用我的 fetch 方法(如建议的here):

Future<List<CheckIn>> futureCheckIn;

@override
void initState() {
  super.initState();
  _eventArg = Provider.of<EventArg>(context, listen: false);

  _helper = ApiHelper(context);
  futureCheckIn = _helper.fetchCheckIns(_eventArg.getEvent().id.toString());
}

我现在想使用RefreshIndicator 来允许用户刷新屏幕。 这很好用,直到我的网络请求以异常结束(例如 404)。通常,异常会被FutureBuilder 捕获,但在执行RefreshIndicator 中的请求后,异常未处理...

这是我的代码:

RefreshIndicator buildResult(List<CheckIn> checkIns, BuildContext context) {
return RefreshIndicator(
  key: _refreshKey,
  onRefresh: () async {
    setState(() {});
    futureCheckIn =
        _helper.fetchCheckIns(_eventArg.getEvent().id.toString()); // this could end with an exception
  },
  child: Container(
    height: MediaQuery.of(context).size.height,
    child: SingleChildScrollView(
      physics: AlwaysScrollableScrollPhysics(),
      scrollDirection: Axis.vertical,
      child: Column(
        children: [
          Container(
            margin: EdgeInsets.all(10),
            child: Text(
              constants.CHECK_IN_SCREEN_DESCRIPTION,
              style: TextStyle(fontSize: 16),
              textAlign: TextAlign.justify,
            ),
          ),
          ListView(
            physics: NeverScrollableScrollPhysics(),
            padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
            scrollDirection: Axis.vertical,
            shrinkWrap: true,
            children: checkIns.map((el) {
              return Card(
                elevation: 4,
                child: ListTile(
                  title: Text(el.name),
                  onTap: () async {
                   ...
                  },
                  trailing: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      Icon(
                        Icons.arrow_forward_ios,
                        color: Colors.black,
                      )
                    ],
                  ),
                ),
              );
            }).toList(),
          )
        ],
      ),
    ),
  ),
);

}

如何正确使用RefreshIndicatorFutureBuilder

编辑@Shri Hari:抛出此异常

class AppException implements Exception {
 final _message;
 final _prefix;

 AppException([this._message, this._prefix]);

 String toString() {
   return "$_prefix$_message";
 }
}

class NotFoundException extends AppException {
  NotFoundException([String message]) : super(message, "Data not found: ");
}

编辑@Mhark Ba​​toctoy:是的,它是FutureBuilder的一部分:

@override
Widget build(BuildContext context) {
return FutureBuilder(
  future: futureCheckIn,
  builder: (context, snapshot) {
    if (snapshot.hasData) {
      List<CheckIn> checkIns = snapshot.data;

      return buildResult(checkIns, context); // RefreshIndicator is nested in here
    } else if (snapshot.hasError) {
      // Error handling
      ... 
    }

    // Spinner während der Datenabfrage
    return Center(
      child: CircularProgressIndicator(),
    );
  },
);

不幸的是,将 onRefresh-Method 更改为

 onRefresh: () async {
  setState(() {});
  futureCheckIn =
    _helper.fetchCheckIns(_eventArg.getEvent().id.toString()); // this could end with an exception
  },

onRefresh: () async {
  setState(() {});
  return futureCheckIn =
    _helper.fetchCheckIns(_eventArg.getEvent().id.toString()); // this could end with an exception
  },

onRefresh: () async {
  setState(() {});
  return _helper.fetchCheckIns(_eventArg.getEvent().id.toString()); // this could end with an exception
  },

不改错误...

【问题讨论】:

  • 你能发布异常吗?

标签: flutter dart networking futuretask


【解决方案1】:

不确定这是否是最佳答案,但我遇到了同样的问题,必须继续前进。所以,这就是我学到和做的。

RefreshIndicator 不喜欢异常。即使在将它们移动到 try / catchonError 下并成功处理异常之后,RefreshIndicator 仍然会为未处理的异常而哭泣(有点奇怪,但我将不得不进行更多调查)。

我所做的是确保我的 API 调用不会引发任何异常。这意味着,API 调用本身(在本例中为 fetchCheckIns() 函数调用)捕获所有类型的已知/未知异常 [catch (e)] 并在您从 API 返回的响应对象中设置一个标志(在本例中为fetchCheckIns()) 的返回类型对象。

现在,在您的FutureBuilder 中,当您调用buildResult(checkIns, context) 时,检查snapshot.data 并查找任何失败标志。如果失败,请以不同的方式渲染屏幕。那就是:

    if (snapshot.hasData) {
      if (snapshot.data.error == true) {
         return ...; // Render error message
      } else {
         return buildResult(snapshot.data, context);
      }
    }

这也意味着,您可能无法返回对象列表,并且可能必须将其包装在一个包含对象列表的对象中。

意味着,您的 CheckIn 对象可能会被类似

class AllCheckIn {
   List<CheckIn> checkIns;
   bool error;
   String errorMsg;
}

或者,您还可以根据您在 API 调用中接收和捕获的异常添加更多错误消息描述,并在您的 FutureBuilder 中使用。

希望这有帮助。

【讨论】:

  • 是的,我也这么认为,但找不到任何能够处理异常的示例。你的方法听起来很有趣。下次需要 RefreshIndicator 时我会使用它。到时候我会给你反馈。现在 +1 并感谢您的回复@Virendra。
【解决方案2】:
 _helper.fetchCheckIns(_eventArg.getEvent().id.toString()); 

它返回一个未来。您需要的dataerror。您可以在try catch finally 块内使用async await,也可以使用Future.then() / Future.catchError / Future.whenComplete()

例子:

onRefresh: () async {
try {
   //You can use the await keyword here.
   return futureCheckIn =
    await _helper.fetchCheckIns(_eventArg.getEvent().id.toString());
}catch (err){
  //You can display the error here using a snackbar.
  }finally {
    //This executes whether there is an error or not. 
 }

},

【讨论】:

  • 感谢您的回复。我已经尝试过使用await,但由于futureCheckIn 来自Future&lt;List&lt;CheckIn&gt;&gt; 类型,它不起作用,因为await _helper.fetchCheckIns(_eventArg.getEvent().id.toString()); 来自List&lt;CheckIn&gt; 类型...
  • 您的 RefreshIndicator 在 FutureBuilder 中吗?如果是这样,请尝试在 onRefresh() 中返回 _helper.fetchCheckIns()
  • 您可以尝试将您的 futureCheckIn 放入 setState() 中吗?
  • 不幸的是异常仍然发生..我将尝试在第二天写一个最小的可重现示例。
  • 好的。尝试将 futureCheckIn = _helper.... 放在 try catch 块中。
猜你喜欢
  • 2023-04-01
  • 2021-05-04
  • 2021-04-17
  • 2020-07-07
  • 2018-12-21
  • 2022-01-01
  • 2021-10-23
  • 2020-06-02
  • 2020-05-24
相关资源
最近更新 更多