FutureBuilder 使用 future 方法时出现问题

huangapple go评论66阅读模式
英文:

FutureBuilder with future method issue

问题

我正在尝试从电话簿获取联系人

List<Contact> contacts = [];

为此,我正在使用FutureBuilder方法

_buildContactsCardList(BuildContext context) {
    return Container(
      child: FutureBuilder(
        future: getContacts(),
        builder: (context, AsyncSnapshot snapshot) {
          if (snapshot.hasError) {
            return const Center(
              child: Text("Error Fetching Contacts",
                  style: TextStyle(
                    color: Colors.grey,
                    fontSize: 16,
                    fontWeight: FontWeight.bold,
                  )),
            );
          }
          if (snapshot.data == null) {
            return const Center(
              child: SizedBox(height: 50, child: CircularProgressIndicator()),
            );
          }
          if (snapshot.data.length == 0) {
            return const Center(
              child: SizedBox(height: 50, child: Text("No Contacts Found")),
            );
          }
          return ListView.separated(
              physics: const NeverScrollableScrollPhysics(),
              shrinkWrap: true,
              itemCount: snapshot.data!.length,
              separatorBuilder: (context, index) {
                return Divider(
                  color: Colors.grey.shade300,
                  thickness: 1,
                );
              },
              itemBuilder: (context, index) {
                Contact contact = snapshot.data![index];
                return ListViewItem(contact.phones.first.number);
              });
        },
      ),
    );
  }

为了从系统中获取联系人,有时正常获取,因为我正在使用fast_contacts插件。

Future<List<Contact>> getContacts() async {
    bool isGranted = await ContactsController.getPermission();

    if (isGranted) {
      try {
        return await FastContacts.getAllContacts();
      } catch (e) {
        log.d(RouteName.AddFriendsScreen + ": Failed to get contacts");
      }
    }
    log.d(RouteName.AddFriendsScreen + ": Failed to get contacts");
    return [];
  }

现在的挑战是,FutureBuilder多次调用了future方法,如果尝试多次读取getAllContacts,那么它会抛出StateError

因此,语句

return await FastContacts.getAllContacts();

正在抛出错误,即

StateError (Bad state: getAllContacts is already in progress.)

因此,根据我的逻辑,我得到一个空数组,这不是我想要的。

我希望异步语句要么返回完整的联系人列表,要么只返回系统中不存在联系人的空数组。

请帮助。

英文:

I am trying to fetch contacts from the phonebook

List&lt;Contact&gt; contacts = [];

For that I am using futureBuilder method

_buildContactsCardList(BuildContext context) {
    return Container(
      child: FutureBuilder(
        future: getContacts(),
        builder: (context, AsyncSnapshot snapshot) {
          if (snapshot.hasError) {
            return const Center(
              child: Text(&quot;Error Fetching Contacts&quot;,
                  style: TextStyle(
                    color: Colors.grey,
                    fontSize: 16,
                    fontWeight: FontWeight.bold,
                  )),
            );
          }
          if (snapshot.data == null) {
            return const Center(
              child: SizedBox(height: 50, child: CircularProgressIndicator()),
            );
          }
          if (snapshot.data.length == 0) {
            return const Center(
              child: SizedBox(height: 50, child: Text(&quot;No Contacts Found&quot;)),
            );
          }
          return ListView.separated(
              physics: const NeverScrollableScrollPhysics(),
              shrinkWrap: true,
              itemCount: snapshot.data!.length,
              separatorBuilder: (context, index) {
                return Divider(
                  color: Colors.grey.shade300,
                  thickness: 1,
                );
              },
              itemBuilder: (context, index) {
                Contact contact = snapshot.data![index];
                return ListViewItem(contact.phones.first.number);
              });
        },
      ),
    );
  }

To fetch the contacts from the system, which is being fetching correctly sometimes as I am using fast_contacts plugin.

Future&lt;List&lt;Contact&gt;&gt; getContacts() async {
    bool isGranted = await ContactsController.getPermission();

    if (isGranted) {
      try {
        return await FastContacts.getAllContacts();
      } catch (e) {
        log.d(RouteName.AddFriendsScreen + &quot;: Failed to get contacts&quot;);
      }
    }
    log.d(RouteName.AddFriendsScreen + &quot;: Failed to get contacts&quot;);
    return [];
  }

Now the challenge is that, Future Builder calls future method more than one time, and if getAllContacts are tried to be read more than one time, then it throws an StateError.
So statement

return await FastContacts.getAllContacts();

is throwing error i.e.
StateError (Bad state: getAllContacts is already in progress.)&quot;
due to which I am getting an empty array as per my logic. Which is not what I want.

I want either the async statement to return full contacts list or give empty array only is zero contacts exists in system.

Kindly help.

答案1

得分: 1

是的,FutureBuilder在每次状态更改时都会重新构建自身并调用future函数。

你可以做一些事情,创建一个可空的Future<List<Contact>>类型变量。首先,将其赋值为你的future函数,并将其传递给FutureBuilder的future属性。在future函数执行时将该变量设为null。当FutureBuilder的future属性变为null时,它将不再调用future函数并且不会重新构建自身。

你可以参考下面的代码:

Future&lt;List&lt;Contact&gt;&gt;? getContactsVar;

void initState() {
  super.initState();
  getContactsVar = getContacts();
}

Future&lt;List&lt;Contact&gt;&gt; getContacts() async {
  getContactsVar = null;
  bool isGranted = await ContactsController.getPermission();

  if (isGranted) {
    try {
      return await FastContacts.getAllContacts();
    } catch (e) {
      log.d(RouteName.AddFriendsScreen + &quot;: 获取联系人失败&quot;);
    }
  }
  log.d(RouteName.AddFriendsScreen + &quot;: 获取联系人失败&quot;);
  return [];
}

_buildContactsCardList(BuildContext context) {
  return Container(
    child: FutureBuilder(
      future: getContactsVar,
      builder: (context, AsyncSnapshot snapshot) {
        if (snapshot.hasError) {
          return const Center(
            child: Text(&quot;获取联系人错误&quot;,
                style: TextStyle(
                  color: Colors.grey,
                  fontSize: 16,
                  fontWeight: FontWeight.bold,
                )),
          );
        }
        if (snapshot.data == null) {
          return const Center(
            child: SizedBox(height: 50, child: CircularProgressIndicator()),
          );
        }
        if (snapshot.data.length == 0) {
          return const Center(
            child: SizedBox(height: 50, child: Text(&quot;未找到联系人&quot;)),
          );
        }
        return ListView.separated(
            physics: const NeverScrollableScrollPhysics(),
            shrinkWrap: true,
            itemCount: snapshot.data!.length,
            separatorBuilder: (context, index) {
              return Divider(
                color: Colors.grey.shade300,
                thickness: 1,
              );
            },
            itemBuilder: (context, index) {
              Contact contact = snapshot.data![index];
              return ListViewItem(contact.phones.first.number);
            });
      },
    ),
  );
}

希望对你有所帮助。

英文:

Yes, the FutureBuilder builds itself on every state change and calls the future function.

You can do a things, make a nullable Future<List<Contact>> type variable. Firstly, assign it with your future function and pass it to the future of the FutureBuilder. At the execution of the future function make that variable null. As the the future of FutureBuilder gets null it will not call the future function again and will not rebuild itself.

You can refer to the below code:

  Future&lt;List&lt;Contact&gt;&gt;? getContactsVar;

  void initState() {
    super.initState();
    getContactsVar = getContacts();
  }
  
  Future&lt;List&lt;Contact&gt;&gt; getContacts() async {
    getContactsVar = null;
    bool isGranted = await ContactsController.getPermission();

    if (isGranted) {
      try {
        return await FastContacts.getAllContacts();
      } catch (e) {
        log.d(RouteName.AddFriendsScreen + &quot;: Failed to get contacts&quot;);
      }
    }
    log.d(RouteName.AddFriendsScreen + &quot;: Failed to get contacts&quot;);
    return [];
  }

  _buildContactsCardList(BuildContext context) {
    return Container(
      child: FutureBuilder(
        future: getContactsVar,
        builder: (context, AsyncSnapshot snapshot) {
          if (snapshot.hasError) {
            return const Center(
              child: Text(&quot;Error Fetching Contacts&quot;,
                  style: TextStyle(
                    color: Colors.grey,
                    fontSize: 16,
                    fontWeight: FontWeight.bold,
                  )),
            );
          }
          if (snapshot.data == null) {
            return const Center(
              child: SizedBox(height: 50, child: CircularProgressIndicator()),
            );
          }
          if (snapshot.data.length == 0) {
            return const Center(
              child: SizedBox(height: 50, child: Text(&quot;No Contacts Found&quot;)),
            );
          }
          return ListView.separated(
              physics: const NeverScrollableScrollPhysics(),
              shrinkWrap: true,
              itemCount: snapshot.data!.length,
              separatorBuilder: (context, index) {
                return Divider(
                  color: Colors.grey.shade300,
                  thickness: 1,
                );
              },
              itemBuilder: (context, index) {
                Contact contact = snapshot.data![index];
                return ListViewItem(contact.phones.first.number);
              });
        },
      ),
    );
  }

huangapple
  • 本文由 发表于 2023年6月19日 12:31:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/76503619.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定