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

API分类数据转多级列表 实现联动DropdownButton技术求助

Hey there! Let's build that 4-level cascading DropdownButton you're looking for—exactly like the reference you shared. Since you already have the model generated via quicktype, we can jump straight into the implementation. I'll use Flutter for this example, since DropdownButton is a core Flutter widget.


Step 1: Confirm Your Model Structure

First, let's make sure your generated model matches the hierarchy you described. It should look something like this (adjust class names if your quicktype output differs):

class Category {
  final String name;
  final List<ChildCategory>? children;

  Category({required this.name, this.children});

  // Your quicktype-generated fromJson/toJson methods will live here
}

class ChildCategory {
  final String name;
  final List<ChildLvCategory>? childrenLv;

  ChildCategory({required this.name, this.childrenLv});
}

class ChildLvCategory {
  final String name;
  final List<FinalChildCategory>? children;

  ChildLvCategory({required this.name, this.children});
}

class FinalChildCategory {
  final String name;

  FinalChildCategory({required this.name});
}

Step 2: Track State for Selections & Options

We'll need state variables to keep track of the selected category at each level, plus the list of available options for each dropdown:

// Selected categories at each level
Category? _selectedLevel1;
ChildCategory? _selectedLevel2;
ChildLvCategory? _selectedLevel3;
FinalChildCategory? _selectedLevel4;

// Available options for each dropdown
List<Category> _level1Options = [];
List<ChildCategory> _level2Options = [];
List<ChildLvCategory> _level3Options = [];
List<FinalChildCategory> _level4Options = [];

In your widget's initState, load your parsed API data into the first level's options:

@override
void initState() {
  super.initState();
  // Replace this with your actual API fetch & parsing logic
  _level1Options = fetchAndParseCategoriesFromApi();
}

Step 3: Implement Cascading Logic

The magic happens in the onChanged handlers—we'll update lower-level options and reset their selected values whenever a higher-level option is chosen.

Level 1 Dropdown

DropdownButton<Category>(
  value: _selectedLevel1,
  hint: const Text('Select Level 1 Category'),
  items: _level1Options.map((cat) {
    return DropdownMenuItem(
      value: cat,
      child: Text(cat.name),
    );
  }).toList(),
  onChanged: (Category? newVal) {
    setState(() {
      _selectedLevel1 = newVal;
      // Update Level 2 options based on the selected Level 1 category
      _level2Options = newVal?.children ?? [];
      // Reset all lower levels to avoid invalid selections
      _selectedLevel2 = null;
      _level3Options = [];
      _selectedLevel3 = null;
      _level4Options = [];
      _selectedLevel4 = null;
    });
  },
  isExpanded: true,
  padding: const EdgeInsets.symmetric(horizontal: 12),
)

Level 2 Dropdown (Disabled until Level 1 is selected)

DropdownButton<ChildCategory>(
  value: _selectedLevel2,
  hint: const Text('Select Level 2 Category'),
  items: _level2Options.map((cat) {
    return DropdownMenuItem(
      value: cat,
      child: Text(cat.name),
    );
  }).toList(),
  onChanged: _selectedLevel1 == null 
      ? null // Disable if no Level 1 category is selected
      : (ChildCategory? newVal) {
          setState(() {
            _selectedLevel2 = newVal;
            _level3Options = newVal?.childrenLv ?? [];
            _selectedLevel3 = null;
            _level4Options = [];
            _selectedLevel4 = null;
          });
        },
  isExpanded: true,
  disabledHint: const Text('Please select Level 1 first'),
  padding: const EdgeInsets.symmetric(horizontal: 12),
)

Level 3 Dropdown (Disabled until Level 2 is selected)

DropdownButton<ChildLvCategory>(
  value: _selectedLevel3,
  hint: const Text('Select Level 3 Category'),
  items: _level3Options.map((cat) {
    return DropdownMenuItem(
      value: cat,
      child: Text(cat.name),
    );
  }).toList(),
  onChanged: _selectedLevel2 == null 
      ? null 
      : (ChildLvCategory? newVal) {
          setState(() {
            _selectedLevel3 = newVal;
            _level4Options = newVal?.children ?? [];
            _selectedLevel4 = null;
          });
        },
  isExpanded: true,
  disabledHint: const Text('Please select Level 2 first'),
  padding: const EdgeInsets.symmetric(horizontal: 12),
)

Level 4 Dropdown (Disabled until Level 3 is selected)

DropdownButton<FinalChildCategory>(
  value: _selectedLevel4,
  hint: const Text('Select Level 4 Category'),
  items: _level4Options.map((cat) {
    return DropdownMenuItem(
      value: cat,
      child: Text(cat.name),
    );
  }).toList(),
  onChanged: _selectedLevel3 == null 
      ? null 
      : (FinalChildCategory? newVal) {
          setState(() {
            _selectedLevel4 = newVal;
            // Add logic here to use the final selected category!
          });
        },
  isExpanded: true,
  disabledHint: const Text('Please select Level 3 first'),
  padding: const EdgeInsets.symmetric(horizontal: 12),
)

Step 4: Assemble the Full Widget

Wrap all dropdowns in a Column with spacing for a clean, user-friendly layout:

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: const Text('4-Level Category Selector')),
    body: Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          DropdownButton<Category>(...),
          const SizedBox(height: 16),
          DropdownButton<ChildCategory>(...),
          const SizedBox(height: 16),
          DropdownButton<ChildLvCategory>(...),
          const SizedBox(height: 16),
          DropdownButton<FinalChildCategory>(...),
        ],
      ),
    ),
  );
}

Pro Tips for Polish

  • State Management: For complex apps, use Provider or Riverpod to share selected category state across screens instead of local widget state.
  • Error Handling: Add loading indicators and error messages for when API fetching fails.
  • Styling: Customize dropdowns with Decoration to match your app's design (e.g., rounded corners, borders).
  • Empty States: If a level has no subcategories, show a message like "No subcategories available" instead of an empty dropdown.

内容的提问来源于stack exchange,提问作者Mohammed El-Barbeer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 21:07:29