Flutter编程:如何以编程方式触发FutureBuilder

14

假设我有这样的一段代码:

return FutureBuilder(
  future: _loadingDeals,
  builder: (BuildContext context, AsyncSnapshot snapshot) {
    return RefreshIndicator(
      onRefresh: _handleRefresh,
        ...
    )
  }
 )

_handleRefresh方法中,我希望通过编程方式触发FutureBuilder的重新运行。

这种情况下:

当用户下拉refreshIndicator时,_handleRefresh只需使FutureBuilder重新运行即可。

编辑:

完整的代码段,不包括刷新部分。我已经切换到使用StreamBuilder,那么refreshIndicator部分将如何适应所有内容?

class DealList extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => new _DealList();
}

class _DealList extends State<DealList> with AutomaticKeepAliveClientMixin {
  // prevents refreshing of tab when switch to
  // Why? https://dev59.com/9lUK5IYBdhLWcg3w0St1
  bool get wantKeepAlive => true; 

  final RestDatasource api = new RestDatasource();
  String token;
  StreamController _dealsController;

  @override
  void initState() {
    super.initState();
    _dealsController = new StreamController();
    _loadingDeals();
  }

  _loadingDeals() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();

    this.token = prefs.getString('token');

    final res =
        this.api.checkInterests(this.token).then((interestResponse) async {
      _dealsController.add(interestResponse);
      return interestResponse;
    });
    return res;
  }

  _handleRefresh(data) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();

    final token = prefs.getString('token');
    await this.api.checkInterests(token).then((interestResponse) {
      _dealsController.add(interestResponse);
    });
    return null;
  }

  @override
  Widget build(BuildContext context) {
    super.build(context); // <-- this is with the wantKeepAlive thing
    return StreamBuilder(
      stream: _dealsController.stream,
      builder: (BuildContext context, AsyncSnapshot snapshot) {

        if (snapshot.hasError) {
          ...
        }

        if (snapshot.connectionState != ConnectionState.done) {
          return Center(
            child: CircularProgressIndicator(),
          );
        }

        if (!snapshot.hasData &&
            snapshot.connectionState == ConnectionState.done) {
          return Text('No deals');
        }

        if (snapshot.hasData) {
          return ListView.builder(
                physics: const AlwaysScrollableScrollPhysics(),
                itemCount: snapshot.data['deals'].length,
                itemBuilder: (context, index) {
                  final Map deal = snapshot.data['deals'][index];
                  return ListTile(
                      onTap: () {
                        Navigator.push(
                          context,
                          MaterialPageRoute(
                            builder: (context) => DealsDetailPage(
                                  dealDetail: deal,
                                ),
                          ),
                        );
                      },
                      title: Text(deal['name']),
                      subtitle: Text(deal['expires']),
                    );
                },
              ),
        }
      },
    );
  }
}

正如@Feu所说,你需要的不是“Future”,而是“Stream”。 - Rémi Rousselet
1
为什么不直接使用 setState - Jonah Williams
@JonahWilliams 目前我正在使用setState,但它无法正常工作。这可能是因为我正在使用with AutomaticKeepAliveClientMixin,所以整个小部件不会在状态更改时重新构建。为什么?请参见此处: https://dev59.com/9lUK5IYBdhLWcg3w0St1 - KhoPhi
3个回答

12

为什么不使用StreamBuilder和Stream来代替FutureBuilder呢?

就像这样...

class _YourWidgetState extends State<YourWidget> {
  StreamController<String> _refreshController;

  ...
  initState() {
    super...
    _refreshController = new StreamController<String>();
   _loadingDeals();
  }

  _loadingDeals() {
    _refreshController.add("");
  }

  _handleRefresh(data) {
    if (x) _refreshController.add("");
  }

  ...
  build(context) {
    ...
    return StreamBuilder(
      stream: _refreshController.stream,
      builder: (BuildContext context, AsyncSnapshot snapshot) {
        return RefreshIndicator(
          onRefresh: _handleRefresh(snapshot.data),
            ...
        )
      }
    );
  }
}

我使用StreamBuilder创建了一个Flutter主要示例的代码片段,请查看


好的,那是一些高级代码啊 :p。我会深呼吸并仔细琢磨它。等我试试看,再告诉你结果。 - KhoPhi
看起来是这样,但实际上并不是!试一下你就会发现它是有效的。 - Feu
1
我创建了一个Gist,其中包含使用StreamBuilder的Flutter主要示例,请查看:https://gist.github.com/ffeu/e77664dd322089649d9b6f0f8fb04d42 - Feu
在上面的例子中,您将snapshot.data发送回_handleRefresh方法。为什么?在_handleRefresh中,if (x) ..中的x是什么? - KhoPhi
snapshot.data: 如果您不需要它,就不必将其传递过去。 if (x) .. 是要触发刷新的条件。如果没有条件,您可以删除 if 并立即调用 _refreshController.add("") - Feu
显示剩余2条评论

5

使用StreamBuilder是一种解决方案,但是要通过编程方式触发FutureBuilder,只需调用setState,它将重新构建小部件。

return RefreshIndicator(
  onRefresh: () {
          setState(() {});
        },
    ...
)

0

我更喜欢使用FutureBuilder而不是StreamBuilder,因为我在项目中使用的是Firestore,而且按读取次数计费,所以我的解决方案是这样的

    _future??= getMyFuture();

    shouldReload(){
       setState(()=>_future = null)
    }
    
    FutureBuilder(
       future: _future, 
       builder: (context, snapshot){
    return Container();
    },
   )
 

任何需要获取新数据的用户活动都应该调用 shouldReload() 方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接