You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter(Getx)异步Future执行顺序控制及视图不更新问题排查

问题描述

我正在使用Getx库开发Flutter项目,由于项目交付日期临近,无法修改后端,只能基于现有后端响应在前端重组为UI模型并展示。

我需要保证两个异步Future的执行顺序:先获取Survey结果列表并提取其中的Survey ID存入列表,再获取Survey列表,通过对比ID列表判断每个Survey是否已完成。我原本以为使用await可以保证执行顺序,但视图并未发生任何变化。

相关代码

Controller代码

class ExampleController extends GetxController {
  final PreferenceManager _preferenceManager =
      Get.find(tag: (PreferenceManager).toString());

  /// ------------> related to survey values
  final RxList<SurveyListUiModel> _rxSurveyListUiModelList = RxList.empty();

  List<SurveyListUiModel> get surveyListUiModelList =
      _rxSurveyListUiModelList.toList();

  final List<String> _surveyResultIdList = [];

  void getSurveyConfigListWithSurveyResult() async{
    Future<String> surveyResultListFuture =
        _preferenceManager.getString('survey_result_list');

    callDataService(
      surveyResultListFuture,
      onSuccess: _handleSurveyResultListResponseSuccess,
    );

    Future<String> surveyListFuture =
        _preferenceManager.getString('survey_list');

    await callDataService(
      surveyConfigListFuture,
      onSuccess: _handleSurveyListResponseSuccess,
    );

    setListLoading(false);
  }

  void _handleSurveyResultListResponseSuccess(String response) {
    List<dynamic> list = jsonDecode(response) as List<dynamic>;

    for (var element in list) {
      SurveyConfigResponse surveyConfigResponse = SurveyConfigResponse.fromJson(
          element['survey_config'] as Map<String, dynamic>);

      _surveyResultIdList.add(surveyConfigResponse.id);
    }
  }

  void _handleSurveyListResponseSuccess(String response) {
    List<dynamic> list = jsonDecode(response) as List<dynamic>;

    for (var element in list) {
      SurveyConfigResponse surveyConfigResponse =
          SurveyConfigResponse.fromJson(element as Map<String, dynamic>);

      surveyListUiModelList.add(SurveyListUiModel(
        surveyConfigId: surveyConfigResponse.id,
        surveyConfigTitle: surveyConfigResponse.title,
        isDiagnosed: _surveyResultIdList.contains(surveyConfigResponse.id),
      ));
      _rxSurveyListUiModelList.refresh();
    }
  }

  /// ------------> related to survey values

  @override
  void onInit() {
    getSurveyConfigListWithSurveyResult();
    super.onInit();
  }
}

callDataService方法

dynamic callDataService<T>(
    Future<T> future, {
    Function(Exception exception)? onError,
    Function(T response)? onSuccess,
    Function? onStart,
    Function? onComplete,
  }) async {
    Exception? _exception;

    onStart == null ? showLoading() : onStart();

    try {
      final T response = await future;

      if (onSuccess != null) onSuccess(response);

      onComplete == null ? hideLoading() : onComplete();

      return response;
    } on ServiceUnavailableException catch (exception) {
      _exception = exception;
      showErrorMessage(exception.message);
    } on UnauthorizedException catch (exception) {
      _exception = exception;
      showErrorMessage(exception.message);
    } on TimeoutException catch (exception) {
      _exception = exception;
      showErrorMessage(exception.message);
    } on NetworkException catch (exception) {
      _exception = exception;
      showErrorMessage(exception.message);
    } on JsonFormatException catch (exception) {
      _exception = exception;
      showErrorMessage(exception.message);
    } on NotFoundException catch (exception) {
      _exception = exception;
      showErrorMessage(exception.message);
    } on ApiException catch (exception) {
      _exception = exception;
    } on AppException catch (exception) {
      _exception = exception;
      showErrorMessage(exception.message);
    } catch (error) {
      _exception = AppException(message: "$error");
      logger.e("Controller>>>>>>> error $error");
    }

    if (onError != null) onError(_exception);

    onComplete == null ? hideLoading() : onComplete();
  }

视图代码

class ExampleView extends GetView<ExampleController> {
  @override
  Widget body(BuildContext context) {
    return Obx(() => Text(controller.surveyListUiModelList.length.toString()));
  }
}

已确认回调方法中的列表值是正确的,但视图仍无更新,请问我的代码中遗漏了什么?


问题分析与修复方案

核心问题点

  1. 异步执行顺序未保证:第一个callDataService调用未加await,导致两个异步请求并行执行,_surveyResultIdList还未填充完成就开始处理Survey列表,不仅判断逻辑错误,还会导致UI更新混乱。
  2. Getter语法错误:surveyListUiModelList的getter使用=而非=>,这会导致该列表仅在初始化时获取一次_rxSurveyListUiModelList的副本,后续RxList更新时,getter返回的始终是旧数据,Obx无法感知变化。
  3. 操作错误的列表:在_handleSurveyListResponseSuccess中,往RxList的副本surveyListUiModelList添加元素,而非直接操作_rxSurveyListUiModelList,导致RxList未实际更新,手动调用refresh()也无法触发UI刷新。
  4. 变量名错误:第二个callDataService传入的surveyConfigListFuture未定义,实际应为surveyListFuture。

修复后的Controller代码

class ExampleController extends GetxController {
  final PreferenceManager _preferenceManager =
      Get.find(tag: (PreferenceManager).toString());

  /// ------------> related to survey values
  final RxList<SurveyListUiModel> _rxSurveyListUiModelList = RxList.empty();

  // 修复Getter写法,每次调用返回最新的RxList副本
  List<SurveyListUiModel> get surveyListUiModelList =>
      _rxSurveyListUiModelList.toList();

  final List<String> _surveyResultIdList = [];

  void getSurveyConfigListWithSurveyResult() async {
    Future<String> surveyResultListFuture =
        _preferenceManager.getString('survey_result_list');

    // 添加await保证第一个请求执行完成后再执行第二个
    await callDataService(
      surveyResultListFuture,
      onSuccess: _handleSurveyResultListResponseSuccess,
    );

    Future<String> surveyListFuture =
        _preferenceManager.getString('survey_list');

    // 修复变量名错误
    await callDataService(
      surveyListFuture,
      onSuccess: _handleSurveyListResponseSuccess,
    );

    setListLoading(false);
  }

  void _handleSurveyResultListResponseSuccess(String response) {
    List<dynamic> list = jsonDecode(response) as List<dynamic>;

    for (var element in list) {
      SurveyConfigResponse surveyConfigResponse = SurveyConfigResponse.fromJson(
          element['survey_config'] as Map<String, dynamic>);

      _surveyResultIdList.add(surveyConfigResponse.id);
    }
  }

  void _handleSurveyListResponseSuccess(String response) {
    List<dynamic> list = jsonDecode(response) as List<dynamic>;

    // 直接操作RxList,自动触发响应式更新
    for (var element in list) {
      SurveyConfigResponse surveyConfigResponse =
          SurveyConfigResponse.fromJson(element as Map<String, dynamic>);

      _rxSurveyListUiModelList.add(SurveyListUiModel(
        surveyConfigId: surveyConfigResponse.id,
        surveyConfigTitle: surveyConfigResponse.title,
        isDiagnosed: _surveyResultIdList.contains(surveyConfigResponse.id),
      ));
    }
    // RxList添加元素后自动通知更新,无需手动调用refresh()
  }

  /// ------------> related to survey values

  @override
  void onInit() {
    getSurveyConfigListWithSurveyResult();
    super.onInit();
  }
}

说明

  • 修复后两个异步请求会按顺序执行,_surveyResultIdList填充完成后再处理Survey列表,保证isDiagnosed判断正确。
  • 直接操作RxList会自动触发Getx的响应式更新,Obx能感知到surveyListUiModelList的变化并刷新视图。

内容的提问来源于stack exchange,提问作者Jun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 05:06:28