【问题标题】:Flutter FutureBuilder gets constantly calledFlutter FutureBuilder 不断被调用
【发布时间】:2020-01-07 15:32:31
【问题描述】:

我遇到了有趣的行为。我在 Stateful 小部件中有一个 FutureBuilder。如果我单独返回 FutureBuilder,一切正常。我的 API 只被调用一次。 但是,如果我添加额外的逻辑,并在两个小部件之间做出选择 - 我可以在 chrome 中看到我的 API 被调用了数十次。我知道build 方法随时都会执行,但是这个额外的逻辑是如何完全破坏Future 的行为的呢?

这里是 api 调用一次的例子。

@override
  Widget build(BuildContext context) {
    return FutureBuilder(..);
}

如果someBooleanFlagfalse,以下是多次调用api 的示例。

@override
  Widget build(BuildContext context) {
    if(someBooleanFlag){
      return Text('Hello World');
    }
    else{
    return FutureBuilder(..);
}

谢谢

【问题讨论】:

    标签: api flutter dart


    【解决方案1】:

    即使您的代码一开始就可以正常工作,但您并没有正确执行。如FutureBuilder的官方文档所述,

    future一定要提前获取,因为如果future与FutureBuilder同时创建,那么每次FutureBuilder的父级重建时,异步任务都会重新启动。

    以下是正确的做法。使用其中任何一个:

    • 懒惰地初始化你的Future

      // Create a late instance variable and assign your `Future` to it. 
      late final Future? myFuture = getFuture();
      
      @override
      Widget build(BuildContext context) {
        return FutureBuilder(
          future: myFuture, // Use that variable here.
          builder: (context, snapshot) {...},
        );
      }
      
    • initState 中初始化您的Future

      // Create an instance variable.
      late final Future? myFuture;
      
      @override
      void initState() {
        super.initState();
      
        // Assign that variable your Future.
        myFuture = getFuture();
      }
      
      @override
      Widget build(BuildContext context) {
        return FutureBuilder(
          future: myFuture, // Use that variable here.
          builder: (context, snapshot) {},
        );
      }
      

    【讨论】:

    • 我的代码中有这个,但也被if 语句包围。然而,今天它似乎奏效了。很奇怪。我需要花更多的时间来理解这一点。谢谢!
    • 如果 getFuture() 有像 getFuture(id, param) 这样的参数,在 build 方法中初始化了 id 和 param,你怎么能调用 initState()? @zeromaro
    • 请看我的回答。上面的答案没有错,但也不对。它不能解释你做错了什么。它只是向您展示了一种使代码工作的方法,这不是使用堆栈溢出的好方法。你只是把括号留在里面。让颤振决定什么时候调用你传入的未来。
    • @PatrickKelly 不用担心,我们都会犯错,现在您介意删除您的评论并投反对票吗?
    • 我有一个 listview builder 从我的 futurebuilder 中返回,带有 CheckboxListTile。检查列表项时,我必须调用 setState 以显示它已被选中。这导致我的 FutureBuilder 一次又一次地触发。这解决了这个问题。非常感谢。
    【解决方案2】:

    使用AsyncMemoizer 用于只运行一次异步函数并缓存其结果的类。

     AsyncMemoizer _memoizer;
    
      @override
      void initState() {
        super.initState();
        _memoizer = AsyncMemoizer();
      }
    
      @override
      Widget build(BuildContext context) {
        if (someBooleanFlag) {
             return Text('Hello World');
        } else {
          return FutureBuilder(
          future: _fetchData(),
          builder: (ctx, snapshot) {
            if (snapshot.hasData) {
              return Text(snapshot.data.toString());
            }
            return CircularProgressIndicator();
          },
         );
        }
      }
    
      _fetchData() async {
        return this._memoizer.runOnce(() async {
          await Future.delayed(Duration(seconds: 2));
          return 'DATA';
        });
      }                
    

    未来方法:

     _fetchData() async {
        return this._memoizer.runOnce(() async {
          await Future.delayed(Duration(seconds: 2));
          return 'REMOTE DATA';
        });
      }
    

    这个memoizer 正是我们想要的!它接受一个异步函数,在第一次调用时调用它并缓存它的结果。对于该函数的所有后续调用,memoizer 返回之前计算的相同未来。

    详细说明:

    https://medium.com/flutterworld/why-future-builder-called-multiple-times-9efeeaf38ba2

    【讨论】:

    • Future getUid() async { await Future.delayed(Duration(seconds: 3)); var firebaseUser = 等待 FirebaseAuth.instance.currentUser; var uid = userRef.doc(firebaseUser.uid).get();返回uid; }
    【解决方案3】:
    class QuizScreen extends StatefulWidget {
      const QuizScreen({Key? key}) : super(key: key);
    
      @override
      _QuizScreenState createState() => _QuizScreenState();
    }
    
    class _QuizScreenState extends State<QuizScreen> {
      static int count = 0;  
    
      initFunction() async {
        if (count > 0) return;
        else "your async function"
        count++;
      }
    
      @override
      void initState() {
        super.initState();
        count = 0;
      }
    
     
    
      @override
      void dispose() {
        super.dispose();
        count = 0;
      }
    
      @override
      Widget build(BuildContext context) {
        return  Scaffold(
          body: FutureBuilder(
              future: initFunction(),
              builder: (context, snapshot) {
                return SafeArea(
                  child: InfiniteCarousel.builder(
                    center: true,
    -
    -
    -
    

    【讨论】:

      猜你喜欢
      • 2023-03-05
      • 2021-12-25
      • 1970-01-01
      • 2021-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-03
      • 2021-11-30
      相关资源
      最近更新 更多