我正在尝试使用API的结果填充ListView。从共享首选项中检索值之后,必须进行API调用。但是在执行时,我的API调用函数会运行无限循环,并且UI不会呈现。我通过调试语句跟踪了此行为。
“未来”构建器构建UI时应显示的圆形指示器也未显示。
我该如何解决?
我的代码:
class _MyHomePageState extends State<MyHomePage>{
@override MyHomePage get widget => super.widget;
String userID = "";
String authID = "";
//Retrieving values from Shared Preferences
Future<List<String>> loadData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
List<String> l= new List<String>();
if(prefs.getString("ID") == null){
l.add("null");
}
else{
l.add(prefs.getString("ID"));
}
if(prefs.getString("authID") == null){
l.add("null");
}
else{
l.add(prefs.getString("authID"));
}
return l;
}
//Setting values retrieved from Shared Pref
setData() async{
await loadData().then((value) {
setState(() {
userID = value[0];
print('the user ID is' + userID);
authID = value[1];
print('the authID is' + authID);
});
// getAllTasks(userID, authID);
});
print("Set data execution completed ");
}
//FUNCTION to use values from Shared Pref and make API Call
Future<List<Task>> getAllTasks() async{
await setData();
//Waiting for Set Data to complete
print('Ive have retrived the values ' + userID + authID );
List<Task> taskList;
await getTasks(userID, authID, "for_me").then((value){
final json = value;
if(json!="Error"){
Tasks tasks = tasksFromJson(json); //of Class Tasks
taskList = tasks.tasks; //getting the list of tasks from class
}
});
if(taskList != null) return taskList;
else {
print('Tasklist was null ');
throw new Exception('Failed to load data ');
}
}
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context){
_signedOut(){
widget.onSignedOut();
}
//To CREATE LIST VIEW
Widget createTasksListView(BuildContext context, AsyncSnapshot snapshot) {
var values = snapshot.data;
return ListView.builder(
itemCount: values == null ? 0 : values.length,
itemBuilder: (BuildContext context, int index) {
return values.isNotEmpty ? Ink(....
) : CircularProgressIndicator();
},
);
}
//MY COLUMN VIEW
Column cardsView = Column(
children: <Widget>[
....
Expanded(
child: FutureBuilder(
future: getAllTasks(),
initialData: [],
builder: (context, snapshot) {
return createTasksListView(context, snapshot);
}),
),
],
);
return Scaffold(
body: cardsView,
);
}
}
而不是一次被调用...我的setData函数被重复调用..我该如何解决..请帮助
答案 0 :(得分:1)
您将在每次重新构建窗口小部件时创建Future
对象。而且,由于您是在setState
方法内调用setData
,因此它将以递归方式触发重建。
要解决此问题,您必须保留对Future
对象的引用。并将该引用用于FutureBuilder
,则可以理解它是以前使用过的引用。
例如:
Future<List<Task>> _tasks;
@override
void initState() {
_tasks = getAllTasks();
super.initState();
}
然后在小部件树中像这样使用它:
Expanded(
child: FutureBuilder(
future: _tasks,
initialData: [],
builder: (context, snapshot) {
return createTasksListView(context, snapshot);
}),
),
答案 1 :(得分:0)
您需要将Future保存在状态中,因为进行getAllTasks()
会触发每个构建回调上的调用。
在initState
中:
this.getAllTasksFuture = getAllTasks();
然后,您将在FutureBuilder中使用此Future属性。
答案 2 :(得分:0)
Flutter提供给我们的FutureBuilder小部件可根据某些将来的状态来创建小部件,每次重建时都会不断刷新该未来! 每次我们调用setState时,FutureBuilder都会再次经历其整个生命周期!
一个选项是记住:
简而言之,记忆化是缓存一个函数的返回值,并在再次调用该函数时重新使用它。 记忆主要用于函数式语言中,在这些语言中,函数是确定性的(对于相同的输入,它们总是返回相同的输出),但是我们可以在此处针对问题使用简单的记忆,以确保FutureBuilder始终接收相同的未来实例。>
为此,我们将使用 Dart的AsyncMemoizer 。 这个备忘录器正是我们想要的!它需要一个异步函数,第一次调用它,然后缓存其结果。对于此函数的所有后续调用,记忆器将返回相同的先前计算的未来。 因此,要解决我们的问题,我们首先在窗口小部件中创建一个AsyncMemoizer实例:
final AsyncMemoizer _memoizer = AsyncMemoizer();
注意:您不应该在StatelessWidget内实例化备忘录,因为Flutter在每次重建时都会丢弃StatelessWidgets,您应该在StatefulWidget或可以持久保存的地方实例化它。 然后,我们将修改我们的 _fetchData函数以使用该备忘录:
_fetchData() {
return this._memoizer.runOnce(() async {
await Future.delayed(Duration(seconds: 2));
return 'REMOTE DATA';
});
}
注意:您必须仅将runOnce()包裹在主体内,而不是将函数调用包裹在内
特别感谢AbdulRahman AlHamali。