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

如何使用Flutter从MySQL获取数据存入下拉列表并实现二级联动选择

Flutter 实现 MySQL 数据拉取与联动下拉列表的完整方案

核心逻辑说明

Flutter 是客户端框架,禁止直接连接 MySQL 数据库,会暴露数据库账号密码存在严重安全风险。你需要先搭建后端接口对接 MySQL,Flutter 端通过请求接口获取数据后再渲染下拉列表,联动逻辑在客户端状态层实现即可。


实现步骤

1. 后端接口准备

用任意后端语言(Node.js/Java/PHP 等)编写两个接口,对接 MySQL 做数据查询,返回 JSON 格式:

  • 接口1:无参数,返回第一个下拉框的全部一级选项数据(示例:省份列表,含id、name字段)
  • 接口2:接收一级选项的id作为请求参数,返回和该ID关联的二级选项列表(示例:对应省份的城市列表,含id、parent_id、name字段)

2. Flutter 端依赖引入

在pubspec.yaml中添加网络请求依赖:

dependencies:
  flutter:
    sdk: flutter
  http: ^1.1.0 # 用于发起接口请求

执行flutter pub get完成依赖安装。

3. 定义数据模型

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';

// 一级选项数据模型
class FirstLevelItem {
  final String id;
  final String name;
  FirstLevelItem({required this.id, required this.name});

  factory FirstLevelItem.fromJson(Map<String, dynamic> json) {
    return FirstLevelItem(id: json['id'], name: json['name']);
  }
}

// 二级选项数据模型
class SecondLevelItem {
  final String id;
  final String parentId; // 关联一级选项的id
  final String name;
  SecondLevelItem({required this.id, required this.parentId, required this.name});

  factory SecondLevelItem.fromJson(Map<String, dynamic> json) {
    return SecondLevelItem(
      id: json['id'],
      parentId: json['parent_id'],
      name: json['name'],
    );
  }
}

4. 页面状态与联动逻辑实现

class LinkDropdownPage extends StatefulWidget {
  const LinkDropdownPage({super.key});

  @override
  State<LinkDropdownPage> createState() => _LinkDropdownPageState();
}

class _LinkDropdownPageState extends State<LinkDropdownPage> {
  // 一级选项相关状态
  List<FirstLevelItem> firstLevelList = [];
  FirstLevelItem? selectedFirstItem;
  bool firstLoading = false;

  // 二级选项相关状态
  List<SecondLevelItem> secondLevelList = [];
  SecondLevelItem? selectedSecondItem;
  bool secondLoading = false;

  @override
  void initState() {
    super.initState();
    // 页面初始化拉取一级选项数据
    fetchFirstLevelData();
  }

  // 拉取一级选项接口请求
  Future<void> fetchFirstLevelData() async {
    setState(() => firstLoading = true);
    try {
      // 替换为你自己的后端接口地址
      final res = await http.get(Uri.parse("http://your-api.com/getFirstLevel"));
      if (res.statusCode == 200) {
        final List<dynamic> resData = json.decode(res.body);
        setState(() {
          firstLevelList = resData.map((e) => FirstLevelItem.fromJson(e)).toList();
        });
      }
    } catch (e) {
      debugPrint("一级数据拉取失败:$e");
    } finally {
      setState(() => firstLoading = false);
    }
  }

  // 选中一级选项后拉取对应二级数据
  Future<void> fetchSecondLevelData(String parentId) async {
    setState(() {
      secondLoading = true;
      // 切换一级选项时清空旧的二级数据
      selectedSecondItem = null;
      secondLevelList.clear();
    });
    try {
      // 替换为你自己的后端接口地址,携带一级选项id参数
      final res = await http.get(Uri.parse("http://your-api.com/getSecondLevel?parentId=$parentId"));
      if (res.statusCode == 200) {
        final List<dynamic> resData = json.decode(res.body);
        setState(() {
          secondLevelList = resData.map((e) => SecondLevelItem.fromJson(e)).toList();
        });
      }
    } catch (e) {
      debugPrint("二级数据拉取失败:$e");
    } finally {
      setState(() => secondLoading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("联动下拉示例")),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            // 第一个下拉列表
            firstLoading
                ? const CircularProgressIndicator()
                : DropdownButtonFormField<FirstLevelItem>(
                    decoration: const InputDecoration(labelText: "请选择一级选项"),
                    value: selectedFirstItem,
                    items: firstLevelList.map((item) {
                      return DropdownMenuItem(value: item, child: Text(item.name));
                    }).toList(),
                    onChanged: (val) {
                      if (val != null) {
                        setState(() => selectedFirstItem = val);
                        // 触发二级数据拉取,实现联动
                        fetchSecondLevelData(val.id);
                      }
                    },
                  ),
            const SizedBox(height: 20),
            // 第二个下拉列表
            secondLoading
                ? const CircularProgressIndicator()
                : DropdownButtonFormField<SecondLevelItem>(
                    decoration: const InputDecoration(labelText: "请选择二级选项"),
                    value: selectedSecondItem,
                    items: secondLevelList.map((item) {
                      return DropdownMenuItem(value: item, child: Text(item.name));
                    }).toList(),
                    onChanged: (val) {
                      setState(() => selectedSecondItem = val);
                    },
                    disabledHint: const Text("请先选择一级选项"),
                  ),
          ],
        ),
      ),
    );
  }
}

额外注意事项

  • 如果是纯离线本地场景,可以用 Flutter 的 sqlite 依赖代替 MySQL 存储本地数据,把网络请求逻辑替换成本地数据库查询即可,联动逻辑完全一致
  • 后端需要做好跨域配置,否则 Flutter Web 端会请求失败
  • 可以自行添加接口请求失败的 Toast 提示,优化用户体验

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 07:06:02