Flutter开发:如何实现含动态数据的嵌套列表UI布局
Flutter实现嵌套动态列表的UI方案与数据集成
一、可用的核心Widget
- ListView.builder:外层和内层都采用这个Widget,因为数据是动态可变的,builder模式能按需渲染列表项,提升性能,避免一次性加载所有元素。
- ExpansionTile:作为外层列表的每个项,自带展开/收起交互,完美适配“词性分类-展开显示释义”的需求,还能自定义头部标题样式。
- Card:给外层的ExpansionTile包裹一层Card,让每个分类项拥有独立的卡片样式,和示例UI的视觉效果匹配。
- Container/Row/Column:用来排版内层的释义和例句,调整间距、文字样式,让内容布局更清晰易读。
二、完整代码实现(含JSON数据集成)
1. 定义数据实体类
先把JSON数据转换成Dart实体类,方便数据管理与类型安全:
// 释义模型 class Meaning { final String description; final String example; Meaning({required this.description, required this.example}); // 从Map转换为实体对象 factory Meaning.fromMap(Map<String, dynamic> map) { return Meaning( description: map['description'], example: map['example'], ); } } // 词性分类模型 class WordCategory { final String type; final List<Meaning> meanings; WordCategory({required this.type, required this.meanings}); // 从Map转换为实体对象 factory WordCategory.fromMap(Map<String, dynamic> map) { return WordCategory( type: map['type'], meanings: (map['meaning'] as List).map((m) => Meaning.fromMap(m)).toList(), ); } }
2. 集成示例JSON数据
将你提供的JSON数据转换成Dart可直接使用的列表:
List<WordCategory> getMockData() { final rawData = [ { 'type': 'adjective', 'meaning': [ {'description': 'description 1', 'example': 'example 1'}, {'description': 'description 2', 'example': 'example 2'}, {'description': 'description 3', 'example': 'example 3'}, {'description': 'description 4', 'example': 'example 4'}, ] }, { 'type': 'noun', 'meaning': [ {'description': 'description 1', 'example': 'example 1'}, {'description': 'description 2', 'example': 'example 2'}, ] }, { 'type': 'verb', 'meaning': [ {'description': 'description 1', 'example': 'example 1'}, ] }, { 'type': 'adverb', 'meaning': [ {'description': 'description 1', 'example': 'example 1'}, {'description': 'description 2', 'example': 'example 2'}, {'description': 'description 3', 'example': 'example 3'}, ] } ]; return rawData.map((item) => WordCategory.fromMap(item)).toList(); }
3. 页面UI完整实现
import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Nested List Demo', theme: ThemeData(primarySwatch: Colors.blue), home: const NestedListPage(), ); } } class NestedListPage extends StatelessWidget { const NestedListPage({super.key}); @override Widget build(BuildContext context) { final categories = getMockData(); return Scaffold( appBar: AppBar(title: const Text('词性释义列表')), body: ListView.builder( padding: const EdgeInsets.symmetric(vertical: 8), itemCount: categories.length, itemBuilder: (context, index) { final category = categories[index]; return Card( margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), child: ExpansionTile( title: Text( category.type.toUpperCase(), style: const TextStyle(fontWeight: FontWeight.bold), ), children: [ ListView.builder( shrinkWrap: true, // 让内层列表自适应内容高度 physics: const NeverScrollableScrollPhysics(), // 禁用内层滚动,交由外层统一控制 itemCount: category.meanings.length, itemBuilder: (context, meaningIndex) { final meaning = category.meanings[meaningIndex]; return Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '释义:${meaning.description}', style: const TextStyle(fontSize: 16), ), const SizedBox(height: 6), Text( '例句:${meaning.example}', style: TextStyle(fontSize: 14, color: Colors.grey[600]), ), if (meaningIndex != category.meanings.length - 1) const Divider(height: 24), ], ), ); }, ), ], ), ); }, ), ); } } // 此处粘贴前面定义的Meaning、WordCategory类和getMockData函数
代码说明
- 外层通过
ListView.builder加载所有词性分类,每个分类用Card+ExpansionTile组合,实现可展开的卡片效果。 - 内层用
ListView.builder加载对应分类的释义和例句,通过shrinkWrap: true让内层列表自适应内容高度,避免布局溢出;禁用内层滚动后,整个页面的滚动体验更统一。 - 数据部分通过实体类解析,无论后续JSON数据的数量如何变化,都能自动适配渲染。
内容的提问来源于stack exchange,提问作者Sreejith Sree
相关产品推荐
相关产品推荐

