我正在从一些API获取一个随机值,并且我有一个条件。如果条件为真,我将返回一个小部件,否则我想更改随机值,并再次从API获取另一个随机值。我只需要重建FutureBuilder
小部件,但我不知道如何做到这一点,我在使用setState
(即setState() or markNeedsBuild() called during build.
(时出错这是我迄今为止的代码:
FutureBuilder<List<dynamic>>(
future: API.get_pets(randomly_select_URL()),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<dynamic>? pet_data = snapshot.data;
if (dropDownIsSelected == true) {
var number_of_parameters = snapshot.data!.length;
var random_pet = random.nextInt(number_of_parameters);
var category =
pet_data![random_pet].category.toString();
var photoURL =
pet_data![random_pet].photoUrls.toString();
if (notEqualsIgnoreCase(category, "Kitty") ||
notEqualsIgnoreCase(category, "Puppy") ||
notEqualsIgnoreCase(category, "Fish") ||
notEqualsIgnoreCase(category, "Hedgehog") ||
notEqualsIgnoreCase(category, "Bunny") ||
photoURL.length == 0) {
print("NOT IN CATEGORY");
random_pet = random.nextInt(number_of_parameters);
}
else {
return Random_Card();
}
}
我不确定这是否是您所需要的,但我看到了两个潜在的错误。
FutureBuilder
应与StatefulWidget
一起使用,其中Future
是在initState
中初始化的属性- 你收到的错误告诉你:;你让我进行重建,尽管我甚至还没有完成渲染这个帧,这就是awkard"。通过设计,Flutter框架引入了这些场景
这比说的容易,所以,下面是我要尝试的(我没有测试这个(:
// Warning: pseudocode ahead!
class MyWidget extends StatefulWidget {
MyWidget({Key? key}) : super(key: key);
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
Future<int>? myFuture;
@override
void initState() {
super.initState();
myFuture = // your API call
}
void requestAgain() {
setState(() {
myFuture = // another API call
});
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: myFuture,
builder: (context, snapshot) {
var output = "";
if (snapshot.connectionState == ConnectionState.waiting) output = "loading...";
if (snapshot.hasData) {
output = "Now we have: ${snapshot.data}";
if (someCondition) {
// !
WidgetsBinding.instance.addPostFrameCallback((_){
requestAgain();
});
}
}
if (snapshot.hasError) output = "Woops, something went wrong";
},
);
}
}
正如您所看到的,当someCondition
为真时,我们将在Flutter完成渲染当前帧后附加您的API调用;不过,这是一个棘手的解决方案,可能是也可能不是理想的解决方案。
我强烈建议使用Riverpod
来处理这个问题(请参阅FutureProvider(。
在Statefull或Stateless小部件中,混合StatefullBuilder和FutureBuilder以仅重置StatefullBuilder内容。
下面是一个使用StatelessWidget的工作示例:
class MyWidget extends StatelessWidget {
Future myFuture() async {
print("reloading");
await Future.delayed(Duration(seconds: 1));
return true;
}
@override
Widget build(BuildContext context) {
return StatefulBuilder(builder: (context, subState) {
return Column(children: [
FutureBuilder(
future: myFuture(),
builder: (context, snapShot) {
return Text('hasData:${snapShot.hasData} | at: ${DateTime.now()}');
}),
InkWell(
onTap: () {
subState(() {});
},
child: Text("Call Future"))
]);
});
}
}
只需在Future函数的顶部使用Future.delayed(Durationzero(,所有事情都会很好地进行
Future<void> testFunction(){
Future.delayed(Duration.zero);
//then type your future code here
}
或者如果你正在使用警报对话框或其他任何东西,你必须在代码的顶部使用延迟持续时间