Flutter Widget中如何将函数返回的Future类型结果赋值给变量
问题根本原因
- build方法是高频调用的,你在里面执行
setJsessionId,函数内部的setState会触发Widget重建,重建又会再次执行setJsessionId,形成死循环,导致无限调用认证接口。 - FutureBuilder的
future参数直接传入getTasks(),每次build都会生成新的Future实例,触发重复请求,加上ListTile使用UniqueKey每次重建都会生成新的key,导致列表项无限重复渲染。
解决方案
步骤1:将异步初始化逻辑放到Widget生命周期回调中
使用StatefulWidget的initState方法执行sessionId初始化,该方法只会在Widget第一次创建时执行一次,只要Widget不销毁重建就不会重复触发:
class YourPageState extends State<YourPage> { String? jsessionId; late Future<List<Task>> _tasksFuture; // 提前缓存任务列表的Future实例 @override void initState() { super.initState(); _initSessionAndData(); } Future<void> _initSessionAndData() async { // 仅执行一次认证请求 jsessionId = await getJsessionId(); // 拿到sessionId后初始化任务请求的Future _tasksFuture = getTasks(jsessionId!); // 仅触发一次重建 setState(() {}); }
步骤2:修复FutureBuilder和列表渲染逻辑
使用提前缓存的Future实例,同时替换掉UniqueKey避免列表强制重绘:
@override Widget build(BuildContext context) { return Scaffold( // 其他布局代码省略 child: jsessionId == null // sessionId未获取到前展示加载态 ? const Center(child: CircularProgressIndicator()) : FutureBuilder<List<Task>>( // 传入缓存的Future实例,不会重复发起请求 future: _tasksFuture, builder: (BuildContext context, AsyncSnapshot<List<Task>> snapshot){ if(snapshot.hasError){ return const Text('Erreur'); } if(snapshot.hasData){ // 直接使用snapshot返回的数据,避免和外部变量混淆导致重复 final taskList = snapshot.data!; return ReorderableListView( buildDefaultDragHandles: false, header: Center( child: Container( child: Text( 'Listes des tâches', style: Theme.of(context).textTheme.headline5, ), padding: EdgeInsets.symmetric(vertical: 20) ) ), children: taskList.map((e) => ListTile( // 使用任务自身的唯一标识作为key,不要用UniqueKey key: ValueKey(e.id), // 假设Task类存在唯一的id字段 leading: Image.asset("assets/images/Task-Bleu_0.png", height: 30), title: Text("Tache: " + e.name), subtitle: Text("Projet: " + e.projectName), trailing: Wrap( spacing: 5, children: [ Image.asset("assets/images/Task-Validated_0.png", height: 30), GestureDetector( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => TaskInfoPage(task: e) ), ); }, child: Icon(BeoticIcons.simply_right, size: 28) ) ] ) )).toList(), onReorder: _onReorder, ); } return const Center(child: CircularProgressIndicator()); } ) ); }
额外优化建议
如果sessionId是全应用通用的,可以将它存储到全局单例、状态管理容器(如Provider、GetX)中,整个应用生命周期只请求一次,无需每个页面重复获取认证。
内容的提问来源于stack exchange,提问作者JS1
相关产品推荐
相关产品推荐

