如何仅在点击ExpansionTile时让FutureBuilder执行Future
实现ExpansionTile展开后再加载FutureBuilder
默认情况下,ExpansionTile的子组件会和它一同构建,导致FutureBuilder提前执行_future异步操作。要实现先构建ExpansionTile,展开后再加载FutureBuilder并触发异步任务,可以按以下方式处理:
核心思路
- 通过
onExpansionChanged回调监听ExpansionTile的展开状态,更新本地状态变量。 - 仅当ExpansionTile展开时,才初始化并执行异步任务
_future。 - 根据展开状态决定是否渲染FutureBuilder,未展开时用占位组件代替。
完整代码示例
class _ExpansionTileDemoState extends State<ExpansionTileDemo> { bool isExpanded = false; Future<String>? _future; // 模拟异步请求 Future<String> _fetchData() async { await Future.delayed(const Duration(seconds: 2)); return "加载完成的内容"; } @override Widget build(BuildContext context) { return ExpansionTile( maintainState: true, expandedCrossAxisAlignment: CrossAxisAlignment.start, title: const Text("点击展开加载数据"), onExpansionChanged: (value) { setState(() { isExpanded = value; // 仅在展开时触发异步任务 if (value) { _future = _fetchData(); } }); }, children: [ Builder( builder: (context) { if (isExpanded && _future != null) { return FutureBuilder<String>( future: _future, builder: (context, snapshot) { switch (snapshot.connectionState) { case ConnectionState.waiting: return const Padding( padding: EdgeInsets.all(16), child: CircularProgressIndicator(), ); case ConnectionState.done: if (snapshot.hasError) { return Padding( padding: const EdgeInsets.all(16), child: Text("加载失败: ${snapshot.error}"), ); } else if (snapshot.hasData) { return Padding( padding: const EdgeInsets.all(16), child: Text(snapshot.data!), ); } default: return const SizedBox(); } }, ); } return const SizedBox(); }, ), ], ); } }
关键细节说明
- 异步任务触发时机:把
_future的初始化放在onExpansionChanged的展开分支中,避免组件build时就提前执行异步操作。 - 状态保持:
maintainState: true确保ExpansionTile展开后,即使父组件重建也不会丢失展开状态。 - 占位组件:未展开时返回
SizedBox,避免子组件区域出现布局异常。
内容的提问来源于stack exchange,提问作者faccio
相关产品推荐
相关产品推荐

