30天计划第9天-列表组件(ListView)

-
-
2025-11-03 14:47
  • 学习内容

    1. ListView 基础:静态列表、动态列表(ListView.builder 优化性能)

    2. 列表项:ListTile(自带图标、标题、副标题的组件)

  • 实践任务

    1. ListView.builder 做一个 “新闻列表”:循环显示 10 条新闻(标题 + 副标题)

    2. 点击列表项跳转到详情页,传当前新闻标题

一、ListView基础:静态列表与动态列表

1. 什么是ListView

ListView是Flutter中用于展示滚动列表的核心组件,适用于需要垂直或水平滚动的大量数据战术(如:新闻列表、商品列表、聊天记录等)。

2. 静态列表(ListView直接包含子组件)

当列表项数较少且固定时,可直接在ListView的children中添加子组件(如:Container、ListTile等)。

  • 代码示例:简单静态列表

import 'package:flutter/material.dart';


// 定义静态列表页面组件
class StaticListViewDemo extends StatelessWidget{
  const StaticListViewDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('静态列表示例'),),
      body: ListView(
        // 页内边距
        padding: const EdgeInsets.all(10),
        // 所有列表项
        children: [
          // 列表项1
          Container(
            height: 60,
            color: Colors.yellow[100],
            alignment: Alignment.center,
            child: const Text('静态列表项1'),
          ),
          const SizedBox(height: 10,), // 列表项间距
          // 列表项2
          Container(
            height: 60,
            color: Colors.blue[100],
            alignment: Alignment.center,
            child: const Text('静态列表项2'),
          ),
          const SizedBox(height: 10,),
          // 列表项3
          Container(
            height: 60,
            color: Colors.red[100],
            alignment: Alignment.center,
            child: const Text('静态列表项3'),
          )
        ],
      ),
    );
  }
}

void main() => runApp(App());

class App extends StatelessWidget{
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '静态列表Demo',
      home: const StaticListViewDemo(), // 将静态列表设为初始页面
    );
  }
}
  • 运行效果
  • 静态列表的优缺点
    • 优点:写法简单,适合少量固定数据
    • 缺点:无论列表项是否可见,都会一次性创建所有组件,数据量大时会严重影响性能

动态列表(ListView.builder按需构建)

当列表项数量较多或动态变化时(如:10条以上),必须用ListView.builder。它会只创建当前可见的列表项,滚动时再销毁不可见项,极大提升性能。

  • ListView.builder核心参数
参数名作用类型
itemCount列表项总数量(决定循环次数)int
itemBuilder列表项构建函数(每次滚动到可见区域时调用)Widget Function(BuildContext, int)
  • 代码示例:简单动态列表
import 'package:flutter/material.dart';


// 定义动态列表页面组件
class DynamicListViewDemo extends StatelessWidget{
  const DynamicListViewDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('动态列表示例'),),
      body: ListView.builder(
        // 页内边距
        padding: const EdgeInsets.all(10),
        // 列表项总数量(100条)
        itemCount: 100,
        // 构建列表项(index是当前项的索引,从0开始)
        itemBuilder: (context, index) {
          // 每次滚动到可见区域时,才会执行这里的代码
          return Container(
            height: 60,
            color: index % 2 == 0 ? Colors.yellow[100] : Colors.blue[100],
            alignment: Alignment.center,
            child: Text('动态列表项${index + 1}'),
          );
        },
      ),
    );
  }
}

void main() => runApp(App());

class App extends StatelessWidget{
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '动态列表Demo',
      home: const DynamicListViewDemo(), // 将动态列表设为初始页面
    );
  }
}
  • 运行效果
  • 动态列表的核心优势
    • 性能优化:只创建可见项,减少内存占用
    • 动态数据适配:配合itemCount可灵活适配数据长度

二、ListTile组件:快速构件列表项

1. 什么是ListTile

ListTile是Flutter提供的预定义列表项组件,内置了常用布局(图标、标题、副标题、箭头等),无需手动用Row拼接,适合快速开发列表。

2. ListTile常用属性

属性名作用类型
leading左侧图标/组件Widget?
title主标题(大号文本)Widget?
subTitle副标题(小号文本,在标题下方)Widget?
trailing右侧图标/组件(如:箭头)Widget?
onTap点击事件回调VoidCallback?
contentPadding内容内边距EdgeInsetsGeometry?

3. 代码示例:ListTile基本用法

import 'package:flutter/material.dart';


// 定义动态列表页面组件
class ListTileDemo extends StatelessWidget{
  const ListTileDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('ListTile示例'),),
      body: ListView(
        // 页内边距
        padding: const EdgeInsets.all(10),
        children: [
          // 基础列表项(标题 + 副标题)
          ListTile(
            title: const Text('主标题:新闻标题'),
            subtitle: const Text('副标题:这是新闻简要描述...'),
            // 点击事件
            onTap: () => print('点击了列表项1'),
          ),
          const Divider(height: 1,), // 分割线
          // 带左侧图标和右侧箭头的列表项
          ListTile(
            leading: const Icon(Icons.newspaper, color: Colors.yellow,), // 左侧图标
            title: const Text('带图标的标题'),
            subtitle: const Text('左侧是新闻图标,右侧是箭头'),
            trailing: const Icon(Icons.arrow_forward_ios, size: 16,), // 右侧箭头
            onTap: () => print('点击了列表项2'),
          ),
          const Divider(height: 1,)
        ],
      ),
    );
  }
}

void main() => runApp(App());

class App extends StatelessWidget{
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '动态列表Demo',
      home: const ListTileDemo(), // 将动态列表设为初始页面
    );
  }
}
  • 运行效果

4. ListTile与ListView.builder结合

在动态列表中使用ListTile,可快速构件统一风格的列表项

import 'package:flutter/material.dart';


// 定义动态列表页面组件
class ListTileWithBuilderDemo extends StatelessWidget{
  const ListTileWithBuilderDemo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('ListTile + ListView.builder示例'),),
      body: ListView.builder(
        // 页内边距
        padding: const EdgeInsets.all(10),
        itemCount: 50,
        itemBuilder: (context, index) {
          return Column(
            children: [
              ListTile(
                leading: Icon(Icons.message, color: Colors.yellow[600],),
                title: Text('消息${index + 1}'),
                subtitle: const Text('点击查看详情'),
                trailing: const Icon(Icons.chevron_right),
                onTap: () => print('点击了消息${index + 1}'),
              ),
              const Divider(height: 1,), // 每条列表项下加分割线
            ],
          );
        },
      ),
    );
  }
}

void main() => runApp(App());

class App extends StatelessWidget{
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '动态列表Demo',
      home: const ListTileWithBuilderDemo(), // 将动态列表设为初始页面
    );
  }
}
  • 运行效果

三、列表项点击跳转与参数传递

1. 实现思路

在ListView.builder中,为每个ListTile的onTap事件绑定路由跳转逻辑,通过Navigator.push跳转到详情页,并传递当前列表项的参数(如:新闻标题)。

2. 实现步骤

  • 步骤1:创建详情页(接收参数)
// pages/news_detail_page.dart
import 'package:flutter/material.dart';

class NewsDetailPage extends StatelessWidget {
  // 接收从列表页传递的新闻标题
  final String newTitle;

  const NewsDetailPage({
    super.key,
    required this.newTitle
    });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("新闻详情")),
      body: Padding(
        padding: const EdgeInsets.all(20),
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              // 显示传过来的新闻标题
              Text(
                newTitle,
                style: const TextStyle(
                  fontSize: 22,
                  fontWeight: FontWeight.bold,
                ),
                textAlign: TextAlign.center,
              ),
              const SizedBox(height: 20,),
              const Text('这是新闻的详细内容...'),
              const SizedBox(height: 30,),
              ElevatedButton(onPressed: () => Navigator.pop(context), child: const Text('返回列表'))
            ],
          ),
        ),
      ),
    );
  }
}
  • 步骤2:在列表中实现点击跳转
// pages/news_list_page.dart
import 'package:flutter/material.dart';
import 'news_detail_page.dart'; // 导入详情页

class NewsListPage extends StatelessWidget {
  const NewsListPage({super.key});

  @override
  Widget build(BuildContext context) {
    // 模拟新闻数据(标题 + 副标题)
    final List<Map<String, String>> newsData = [
      {
        "title": " Flutter 3.24 版本发布,新增多项性能优化",
        "subtitle": "官方宣布 Flutter 3.24 稳定版发布,重点提升列表渲染性能..."
      },
      {
        "title": "2025 移动开发趋势报告:跨平台框架成主流",
        "subtitle": "据行业分析,跨平台开发框架市场占比已达 65%,Flutter 居首..."
      },
      // 更多数据...
    ];

    return Scaffold(
      appBar: AppBar(title: const Text("新闻列表")),
      body: ListView.builder(
        itemCount: newsData.length,
        itemBuilder: (context, index) {
          // 获取当前索引的新闻数据
          final news = newsData[index];
          return Column(
            children: [
              ListTile(
                leading: const Icon(Icons.newspaper, color: Colors.yellow,),
                title: Text(
                  news['title']??'未知标题',
                  style: const TextStyle(fontWeight: FontWeight.w500),
                  // 标题过长时显示...
                  overflow: TextOverflow.ellipsis,
                  maxLines: 1,
                ),
                subtitle: Text(
                  news['subtitle']??'', // 新闻副标题
                  style: const TextStyle(color: Colors.blue, fontSize: 14),
                  overflow: TextOverflow.ellipsis,
                  maxLines: 1,
                ),
                trailing: const Icon(Icons.arrow_forward_ios, size: 16,),
                // 点击事件:跳转到详情页并传递标题
                onTap: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => NewsDetailPage(newTitle: news['title']??'未知标题'), // 传递新闻标题
                    ));
                },
              ),
              const Divider(height: 1,) // 分割线
            ],
          );
        },
      )
    );
  }
}
  • 步骤3:配置入口文件
import 'package:flutter/material.dart';
import 'pages/news_list_page.dart';


void main() => runApp(App());

class App extends StatelessWidget{
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '新闻列表Demo',
      theme: ThemeData(primarySwatch: Colors.yellow),
      home: const NewsListPage(), // 初始页为新闻列表
    );
  }
}
  • 运行效果

四、常见问题与解决方案

1. 列表项内容显示不全或溢出

  • 问题:标题或副标题过长时,文字被截断或溢出屏幕。
  • 解决:设置maxLines和overflow属性
Text(
  "很长很长的标题...",
  maxLines: 1, // 最多显示1行
  overflow: TextOverflow.ellipsis, // 超出部分显示...
)

2. 列表滚动时性能卡顿

  • 问题:数据量大时,滚动不流畅。
  • 解决:
    • 确保使用ListView.builder而非静态ListView
    • 避免在itemBuilder中执行复杂逻辑或创建大量临时对象
    • 列表项高度固定时,设置itemExtent优化性能

      ListView.builder(
        itemExtent: 80, // 固定每条列表项高度为80px
        // ...其他参数
      )


目录