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

Flutter中API数据填充Dropdown列表为空问题求助

问题分析与解决方案

核心问题

  1. 异步请求未触发UI更新:_getProductList是异步函数,initState里调用后直接print(allProducts),此时请求还未完成,打印结果必然为空;且数据获取完成后未调用setState通知Flutter更新UI,导致Dropdown无法渲染数据。
  2. 构造函数写法不规范:自定义State构造函数传递参数的方式不符合Flutter最佳实践,应通过widget访问StatefulWidget的参数。
  3. 冗余API调用:Dropdown的onChanged里重复调用_getProductList完全没必要,反而会重复发起无效请求。
  4. 初始值可能不匹配:如果portProduct和API返回的productId.toString()格式不一致,Dropdown会因找不到对应值报错。

修正后的完整代码

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:gateway_device/flutter_flow/flutter_flow_theme.dart';
import 'package:gateway_device/models/products_list_model.dart';
import 'package:gateway_device/routes/route_helper.dart';
import 'package:gateway_device/utils/app_constants.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;

class EditDevicePortWidget extends StatefulWidget {
  final String deviceId;
  final String portId;
  final String publicId;
  final String label;
  final String portProduct;

  const EditDevicePortWidget({
    Key? key,
    required this.deviceId,
    required this.portId,
    required this.publicId,
    required this.label,
    required this.portProduct,
  }) : super(key: key);

  @override
  State<EditDevicePortWidget> createState() => _EditDevicePortWidgetState();
}

class _EditDevicePortWidgetState extends State<EditDevicePortWidget> {
  late String _selectedProduct;
  List<TankProduct> allProducts = [];
  bool _isLoading = true; // 增加加载状态优化体验

  @override
  void initState() {
    super.initState();
    _selectedProduct = widget.portProduct;
    _fetchProductList();
  }

  // 异步获取产品列表并更新UI
  Future<void> _fetchProductList() async {
    try {
      final allProductsUrl = Uri.parse(AppConstants.BASE_URL + AppConstants.TANK_PRODUCTS_LIST_URI);
      final response = await http.get(allProductsUrl, headers: {
        'Authorization': 'Bearer ' + AppConstants.TOKEN,
        'X-API-KEY': AppConstants.API_KEY,
      });

      if (response.statusCode == 200) {
        Map<String, dynamic> body = json.decode(response.body);
        List<TankProduct> fetchedProducts = [];
        (body["tank_products"]).forEach((product) {
          fetchedProducts.add(TankProduct.fromJson(product));
        });

        setState(() {
          allProducts = fetchedProducts;
          _isLoading = false;
          // 校验初始值是否在列表中,避免Dropdown报错
          if (!allProducts.any((item) => item.productId.toString() == _selectedProduct)) {
            _selectedProduct = allProducts.isNotEmpty ? allProducts.first.productId.toString() : '';
          }
        });
      } else {
        setState(() => _isLoading = false);
        Get.snackbar('Error', 'Failed to load products');
      }
    } catch (e) {
      setState(() => _isLoading = false);
      Get.snackbar('Error', e.toString());
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        title: Text(
          '${widget.publicId} - ${widget.label}',
          style: FlutterFlowTheme.of(context).bodyText1.override(
                fontFamily: 'Heebo',
                fontSize: 18,
                fontWeight: FontWeight.w500,
              ),
        ),
        centerTitle: true,
        elevation: 0,
        backgroundColor: Colors.white,
        automaticallyImplyLeading: false,
        actions: [
          IconButton(
            hoverColor: Colors.transparent,
            iconSize: 40,
            icon: const Icon(
              Icons.close,
              color: Colors.black,
              size: 30,
            ),
            onPressed: () {
              Get.offNamed(RouteHelper.getPortProfile(
                widget.deviceId,
                widget.portId,
                widget.publicId,
                widget.label,
              ));
            },
          ),
        ],
        iconTheme: const IconThemeData(color: Colors.black),
      ),
      body: SafeArea(
        child: Container(
          width: MediaQuery.of(context).size.width,
          height: MediaQuery.of(context).size.height.h * 1,
          decoration: const BoxDecoration(color: Colors.white),
          child: Column(
            children: [
              const Divider(
                height: 10,
                thickness: 2,
                color: Colors.black,
              ),
              Column(
                children: [
                  Row(
                    mainAxisSize: MainAxisSize.max,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: [
                      Padding(
                        padding: const EdgeInsetsDirectional.fromSTEB(10, 10, 0, 0),
                        child: Text(
                          'Product:',
                          style: FlutterFlowTheme.of(context).bodyText1.override(
                                fontFamily: 'Heebo',
                                fontSize: 18,
                                fontWeight: FontWeight.w500,
                              ),
                        ),
                      ),
                    ],
                  ),
                  // 根据加载状态切换显示内容
                  _isLoading
                      ? const Center(child: CircularProgressIndicator())
                      : Column(
                          mainAxisSize: MainAxisSize.max,
                          children: <Widget>[
                            SizedBox(
                              child: ButtonTheme(
                                alignedDropdown: true,
                                child: DropdownButton<String>(
                                  value: allProducts.isNotEmpty ? _selectedProduct : null,
                                  iconSize: 30,
                                  icon: null,
                                  style: TextStyle(color: Colors.black, fontSize: 16.sp),
                                  hint: const Text('Select Product'),
                                  onChanged: allProducts.isNotEmpty
                                      ? (newValue) {
                                          setState(() {
                                            _selectedProduct = newValue!;
                                          });
                                          print(_selectedProduct);
                                        }
                                      : null, // 无数据时禁用选择
                                  items: allProducts.map((item) {
                                    return DropdownMenuItem(
                                      child: Text(item.productName),
                                      value: item.productId.toString(),
                                    );
                                  }).toList(),
                                ),
                              ),
                            )
                          ],
                        ),
                ],
              )
            ],
          ),
        ),
      ),
    );
  }
}

关键修改点说明

  1. 规范构造函数:移除State的自定义构造函数,通过widget.xxx访问父组件参数,符合Flutter官方规范。
  2. 异步请求与UI更新:在_fetchProductList中获取数据后调用setState,更新产品列表和加载状态,触发UI重建。
  3. 增加加载状态:添加_isLoading变量,请求过程中显示加载指示器,避免页面空白。
  4. 校验初始值:确保初始选中值存在于返回列表中,避免Dropdown出现"Value not in items"错误。
  5. 移除冗余调用:删除onChanged里的API请求,选择产品时仅更新选中值即可。
  6. 错误处理:添加try-catch和状态码判断,通过GetX显示错误提示,提升容错性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 06:05:24