Flutter如何在ListView中实现多层嵌套导航避免逐页新建页面?
需求说明
- 待实现功能为三层列表导航:
- 顶层:主题列表页
- 二层:点击主题跳转对应子主题列表页
- 三层:点击子主题跳转对应内容展示页
- 核心约束:主题、子主题数据量极大,需要实现无需为每个页面单独新建文件的轻量化导航方案。
现有代码
主题列表构建代码
Expanded( child: ListView.builder( // scrollDirection: Axis.horizontal, itemCount: cryptoData.length, itemBuilder: (context, index) { return Container( padding: EdgeInsets.fromLTRB(20, 10, 16, 0), height: 100, width: double.maxFinite, child: Card( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), elevation: 5, child: Container( child: Padding( padding: EdgeInsets.all(7), child: Stack(children: <Widget>[ Align( alignment: Alignment.centerRight, child: Stack( children: <Widget>[ Padding( padding: const EdgeInsets.only( left: 5, top: 5), child: Column( children: <Widget>[ Row( children: <Widget>[ cryptoIcon( cryptoData[index]), SizedBox( height: 10, ), // Spacer(), cryptoChange( cryptoData[index]), SizedBox( width: 10, ), SizedBox( width: 20, ) ], ), ], )) ], ), ) ]), ), ), ), ); }), ),
现有数据源定义
import 'package:flutter/material.dart'; class CryptoData { static final getData = [ { 'image': "assets/images/card1.png", 'change': 'English', 'changeValue': '24 Chapters', }, { 'image': "assets/images/card1.png", 'change': 'Moral Education', 'changeValue': '32 Chapters', }, { 'image': "assets/images/card1.png", 'change': 'Maths', 'changeValue': '11 Chapters', }, { 'image': "assets/images/card1.png", 'change': 'Hindi', 'changeValue': '22 Chapters', }, { 'image': "assets/images/card1.png", 'change': 'Computer Science', 'changeValue': '10 Chapters', }, { 'image': "assets/images/card1.png", 'change': 'Sanskrit', 'changeValue': '103 Cards', }, { 'image': "assets/images/card1.png", 'change': 'Social Studies', 'changeValue': '12 Chapters', }, ]; }
功能层级结构示意图

实现方案
核心思路:仅需2个通用组件即可覆盖全量导航逻辑,所有页面差异通过传参控制,无需为每个主题/子主题单独新建页面文件
- 改造统一数据结构
把原来平铺的主题数据改造成嵌套结构,所有层级数据统一存在同一个静态类/JSON文件中维护:顶层主题条目增加children字段存储对应子主题列表,子主题条目增加content字段存储对应展示内容,示例结构如下:// 改造后统一嵌套数据结构 static final fullNavData = [ { 'image': "assets/images/card1.png", 'title': 'English', 'desc': '24 Chapters', 'children': [ { 'title': 'Chapter 1', 'desc': '12 Cards', 'content': '此处存储第一章需要展示的文本、图片路径等任意内容数据' }, // 其余English子主题直接追加在children数组中即可 ] }, // 其余顶层主题按相同格式嵌套追加 ]; - 封装通用列表页组件
新建一个通用列表组件GenericListPage,接收两个入参:pageTitle(当前页导航栏标题)、listData(当前页需要渲染的列表数组)。组件内部直接复用已经写好的ListView.builder列表渲染逻辑,给列表项加点击事件:- 如果当前点击项存在
children字段,说明是中间层级,跳转新的GenericListPage,把当前项的title作为新页面标题、children作为新页面的列表数据传入 - 如果当前点击项存在
content字段,说明是最后一层,跳转通用内容页,传入对应标题和内容数据
跳转逻辑示例:
onTap: () { if (item.containsKey('children')) { Navigator.push( context, MaterialPageRoute( builder: (context) => GenericListPage( pageTitle: item['title'], listData: item['children'], ), ), ); } else if (item.containsKey('content')) { Navigator.push( context, MaterialPageRoute( builder: (context) => GenericContentPage( pageTitle: item['title'], contentData: item['content'], ), ), ); } } - 如果当前点击项存在
- 封装通用内容页组件
新建通用内容组件GenericContentPage,接收pageTitle、contentData两个入参,内部只负责根据传入的内容数据渲染UI即可,不需要感知当前内容属于哪个主题/子主题。
这套方案下,不管后续新增多少主题、多少子层级,只需要在统一的数据源里追加对应数据即可,不需要新增任何页面文件,原有写好的列表UI代码可以直接复用,只需要把写死的字段名替换成改造后的通用字段名就行。顶层的主题列表页也可以直接用GenericListPage实现,传入顶层fullNavData作为初始数据即可。
内容的提问来源于stack exchange,提问作者mentos
相关产品推荐
相关产品推荐

