Flutter调用API时使用setState与FutureBuilder snapshot的区别是什么?
核心差异点
状态管理逻辑不同
手动setState方案属于全手动状态管理,所有状态(加载中、成功、失败)都需要开发者自行定义变量维护,每次状态变更必须手动调用setState()触发页面重建。你当前给出的setState示例甚至没有处理加载、错误状态,只覆盖了请求成功的场景。
FutureBuilder+snapshot属于框架封装的半自动状态管理,FutureBuilder会自动监听绑定的Future执行状态,状态变更时自动触发builder重建,不需要手动维护状态变量,也不需要手动调用setState(),snapshot对象会自动携带完整的执行状态与返回结果/错误信息。异常处理能力不同
手动setState方案默认无异常捕获逻辑,如果请求超时、链路异常、JSON解析失败,会直接抛出未捕获异常导致应用崩溃,要实现异常处理必须手动加try/catch包裹请求逻辑,还要额外定义错误状态变量,请求失败时调用setState()更新错误提示。
FutureBuilder内部已经封装了异常捕获逻辑,所有请求、解析过程中抛出的异常都会自动存入snapshot,直接通过snapshot.hasError即可判断异常状态,通过snapshot.error就能拿到异常信息,无需额外写捕获逻辑。加载状态处理成本不同
手动setState方案需要单独定义加载状态变量,请求发起前手动调用setState()标记为加载中,展示加载组件;请求结束后再修改状态。你给出的示例中完全没有处理加载状态,请求发起后到返回前的这段时间,页面只会展示默认的空Container,没有任何加载提示。
FutureBuilder天然支持加载状态判断,请求未完成时snapshot既无数据也无错误,直接在这个分支返回加载组件即可,不需要额外维护任何加载相关的状态变量。鲁棒性不同
手动setState方案如果请求还未返回页面就被销毁,此时调用setState()会触发setState called after dispose的运行时报错,必须手动加if(mounted)判断才能避免该问题。
FutureBuilder内部已经做了生命周期适配,页面销毁时会自动取消对Future的状态监听,不会出现上述报错。可维护性不同
如果页面存在多个异步请求,手动setState方案需要定义多套状态变量,写多次setState()调用,状态多了很容易出现漏更新、状态冲突的问题,代码冗余度高。
FutureBuilder方案每个异步请求对应一个FutureBuilder即可,状态完全隔离,不需要额外定义变量,代码逻辑更清晰,后续迭代维护成本更低。
内容的提问来源于stack exchange,提问作者Jed

