【Flutter】列表组件ListView&网格布局组件GridView&Padding组件

🔥 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:Flutter学习
🌠 首发时间:2024年5月24日
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾

列表组件

列表布局是我们项目开发中最常用的一种布局方式。在 Flutter 中,我们可以通过 ListView 来定义列表项,支持垂直和水平方向展示,通过一个属性就可以控制列表的显示方向。列表有以下分类:

  1. 垂直列表
  2. 垂直图文列表
  3. 水平列表
  4. 动态列表

列表组件常用参数:

名称类型说明
scrollDirectionAxis默认为垂直列表:Axis.vertical;水平列表:Axis.horizontal
paddingEdgeInsetsGeometry内边距
resolvebool组件反向排序
childrenList列表元素

垂直列表

示例一

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(primarySwatch: Colors.yellow),
      home: Scaffold(
        appBar: AppBar(title: const Text("垂直列表")),
        body: const MyHomePage(),
      ),
    );
  }
}

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

  
  Widget build(BuildContext context) {
    return ListView(
      children: const [
        ListTile(
          title: Text("我是一个列表"),
        ),
        //一根线
        Divider(),
        ListTile(
          title: Text("我是一个列表"),
        ),
        Divider(),
        ListTile(
          title: Text("我是一个列表"),
        ),
        Divider(),
        ListTile(
          title: Text("我是一个列表"),
        ),
        Divider(),
        ListTile(
          title: Text("我是一个列表"),
        ),
        Divider(),
        ListTile(
          title: Text("我是一个列表"),
        ),
        Divider(),
        ListTile(
          title: Text("我是一个列表"),
        ),
        Divider(),
        ListTile(
          title: Text("我是一个列表"),
        ),
        Divider(),
        ListTile(
          title: Text("我是一个列表"),
        ),
        Divider(),
        ListTile(
          title: Text("我是一个列表"),
        ),
        Divider(),
        ListTile(
          title: Text("我是一个列表"),
        ),
        Divider(),
        ListTile(
          title: Text("我是一个列表"),
        ),
        Divider(),
      ],
    );
  }
}

垂直列表是可以上下滑动的

在这里插入图片描述

示例二

实现如下效果:

在这里插入图片描述

ListTile 组件的 leading 属性写在 title 属性前面,可以放图标或图片

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

  
  Widget build(BuildContext context) {
    return ListView(
      children: const [
        ListTile(
          leading: Icon(Icons.assignment, color: Colors.red),
          title: Text("全部订单"),
        ),
        //一根线
        Divider(),
        ListTile(
          leading: Icon(Icons.payment, color: Colors.green),
          title: Text("待付款"),
        ),
        Divider(),
        ListTile(
          leading: Icon(Icons.local_car_wash, color: Colors.orange),
          title: Text("待收货"),
        ),
        Divider(),
        ListTile(
          leading: Icon(Icons.favorite, color: Colors.lightGreen),
          title: Text("我的收藏"),
        ),
        Divider(),
        ListTile(
          leading: Icon(Icons.people, color: Colors.black54),
          title: Text("在线客服"),
        ),
        Divider(),
      ],
    );
  }
}

垂直图文列表

示例一

实现如下类似新闻页面的效果:

在这里插入图片描述

ListTile 组件的 trailing 属性写在 title 属性后面,可以放图标或图片;用 subtitle 属性来指定这个列表项的副标题。

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

  
  Widget build(BuildContext context) {
    return ListView(
      children: [
        ListTile(
          leading: Image.network(
              "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/1.jpg"),
          title: const Text("今天地磁暴活动趋于减弱"),
          subtitle:
              const Text("针对24日到26日发生的地磁暴现象,中国气象局国家卫星气象中心的专家分析,峰值已出现在25日凌晨。"),
        ),
        const Divider(),
        ListTile(
          leading: Image.network(
              "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/2.jpg"),
          title: const Text("不小心掉进黑洞会发生什么?能看到宇宙的尽头吗?"),
          subtitle: const Text("黑洞是宇宙中神秘的天体,由超新星爆炸形成。"),
          trailing: Image.network(
              "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/3.jpg"),
        ),
        const Divider(),
        ListTile(
          title: const Text("一枚钴弹真的可以消灭全人类吗?钴弹到底是什么?"),
          subtitle: const Text("钴炸弹是一种假设的特种核武器,通过释放放射性元素钴60造成巨大破坏。"),
          trailing: Image.network(
              "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/4.jpg"),
        ),
        const Divider(),
        ListTile(
          leading: Image.network(
              "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/5.jpg"),
          title: const Text("71年等一回!“天外来客”长什么样?"),
          subtitle: const Text(
              "近期,12P/Pons-Brooks彗星成为全球瞩目的对象,我国不少天文爱好者成功观测并拍摄到这位“天外来客”。"),
        ),
        const Divider(),
        ListTile(
          title: const Text("大地磁暴确定发生,东北局部已见极光!网友:因为它我都睡不醒"),
          subtitle: const Text("中国气象局国家空间天气监测预警中心发出地磁扰动预警,3月24日至26日将出现中等到大地磁暴"),
          trailing: Image.network(
              "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/6.jpg"),
        ),
        const Divider(),
      ],
    );
  }
}

示例二

实现如下效果:

在这里插入图片描述

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

  
  Widget build(BuildContext context) {
    return ListView(
      padding: const EdgeInsets.all(10),
      children: [
        Image.network(
            "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/1.jpg"),
        Container(
          height: 44,
          padding: const EdgeInsets.fromLTRB(0, 10, 0, 10),
          child: const Text(
            '我是一个标题',
            textAlign: TextAlign.center,
            style: TextStyle(
              fontSize: 18,
            ),
          ),
        ),
        Image.network(
            "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/2.jpg"),
        Container(
          height: 44,
          padding: const EdgeInsets.fromLTRB(0, 10, 0, 10),
          child: const Text(
            '我是一个标题',
            textAlign: TextAlign.center,
            style: TextStyle(
              fontSize: 18,
            ),
          ),
        ),
        Image.network(
            "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/3.jpg"),
        Container(
          height: 44,
          padding: const EdgeInsets.fromLTRB(0, 10, 0, 10),
          child: const Text(
            '我是一个标题',
            textAlign: TextAlign.center,
            style: TextStyle(
              fontSize: 18,
            ),
          ),
        ),
      ],
    );
  }
}

水平列表

实现如下效果,可左右滑动:

在这里插入图片描述

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

  
  Widget build(BuildContext context) {
    return
        //用SizedBox来限制水平列表的高度
        SizedBox(
      height: 140,
      child: ListView(
        //设置为水平方向
        scrollDirection: Axis.horizontal,
        children: [
          SizedBox(
            width: 130,
            child: Column(
              children: [
                Image.network(
                  "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/1.jpg",
                  width: 100,
                  height: 100,
                  fit: BoxFit.cover,
                ),
                const SizedBox(height: 3),
                const Text('我是一个文本')
              ],
            ),
          ),
          SizedBox(
            width: 130,
            child: Column(
              children: [
                Image.network(
                  "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/2.jpg",
                  width: 100,
                  height: 100,
                  fit: BoxFit.cover,
                ),
                const SizedBox(height: 3),
                const Text('我是一个文本')
              ],
            ),
          ),
          SizedBox(
            width: 130,
            child: Column(
              children: [
                Image.network(
                  "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/3.jpg",
                  width: 100,
                  height: 100,
                  fit: BoxFit.cover,
                ),
                const SizedBox(height: 3),
                const Text('我是一个文本')
              ],
            ),
          ),
          SizedBox(
            width: 130,
            child: Column(
              children: [
                Image.network(
                  "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/4.jpg",
                  width: 100,
                  height: 100,
                  fit: BoxFit.cover,
                ),
                const SizedBox(height: 3),
                const Text('我是一个文本')
              ],
            ),
          ),
        ],
      ),
    );
  }
}

动态列表及循环动态数据

for循环实现动态列表

在前面,我们是一个个手动添加列表中的列表项,这是很繁琐的,下面我们用 for 循环来实现同样的效果

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

  List<Widget> _initListData() {
    List<Widget> list = [];
    for (var i = 0; i < 10; i++) {
      list.add(ListTile(
        title: Text("我是第${i + 1}个列表"),
      ));
    }
    return list;
  }

  
  Widget build(BuildContext context) {
    return ListView(
      children: _initListData(),
    );
  }
}

在这里插入图片描述

在正式开发中,我们创建列表的数据都是从服务器获取的,现在我们准备了一个文件,里面包含了列表项的信息,模拟这个过程:

lib 目录下新建一个 res 文件夹,res 下新建一个 listData.dart 文件:

List listData = [
  {
    "title": 'Candy Shop',
    "author": 'Mohamed Chahin',
    "imageUrl": 'https://www.itying.com/images/flutter/1.png',
  },
  {
    "title": 'Childhood in a picture',
    "author": 'Google',
    "imageUrl": 'https://www.itying.com/images/flutter/2.png',
  },
  {
    "title": 'Alibaba Shop',
    "author": 'Alibaba',
    "imageUrl": 'https://www.itying.com/images/flutter/3.png',
  },
  {
    "title": 'Candy Shop',
    "author": 'Mohamed Chahin',
    "imageUrl": 'https://www.itying.com/images/flutter/4.png',
  },
  {
    "title": 'Tornado',
    "author": 'Mohamed Chahin',
    "imageUrl": 'https://www.itying.com/images/flutter/5.png',
  },
  {
    "title": 'Undo',
    "author": 'Mohamed Chahin',
    "imageUrl": 'https://www.itying.com/images/flutter/6.png',
  },
  {
    "title": 'white-dragon',
    "author": 'Mohamed Chahin',
    "imageUrl": 'https://www.itying.com/images/flutter/7.png',
  }
];
import 'package:flutter/material.dart';
import './res/listData.dart';

void main() {
  runApp(const MyApp());
}

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(primarySwatch: Colors.yellow),
      home: Scaffold(
        appBar: AppBar(title: const Text("for循环实现动态列表")),
        body: const MyHomePage(),
      ),
    );
  }
}

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

  List<Widget> _initListData() {
    List<Widget> list = [];
    for (var i = 0; i < listData.length; i++) {
      list.add(ListTile(
        leading: Image.network(listData[i]["imageUrl"]),
        title: Text(listData[i]["title"]),
        subtitle: Text(listData[i]["author"]),
      ));
    }
    return list;
  }

  
  Widget build(BuildContext context) {
    return ListView(
      children: _initListData(),
    );
  }
}

在这里插入图片描述

ListView.builder实现动态列表

class MyHomePage extends StatelessWidget {
  List<String> list = [];
  MyHomePage({super.key}) {
    for (var i = 0; i < 10; i++) {
      list.add("我是第${i + 1}条数据");
    }
  }

  
  Widget build(BuildContext context) {
    //ListView.builder和for循环差不多, itemCount是循环次数
    return ListView.builder(
      itemCount: list.length,
      itemBuilder: (context, index) {
        return ListTile(
          title: Text(list[index]),
        );
      },
    );
  }
}

在这里插入图片描述

和前面,从 listData.dart 读取数据展示在列表项中:

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

  List<Widget> _initListData() {
    var tempList = listData.map((value) {
      return ListTile(
        leading: Image.network(value["imageUrl"]),
        title: Text(value["title"]),
        subtitle: Text(value["author"]),
      );
    });
    return tempList.toList();
  }

  
  Widget build(BuildContext context) {
    return ListView(
      children: _initListData(),
    );
  }
}

在这里插入图片描述

GridView网格布局组件

组件介绍

在这里插入图片描述

GridView 网格布局在实际项目中用的也是非常多的,当我们想让可以滚动的元素使用矩阵方式排列的时候,此时我们可以用网格列表组件 GridView 实现布局。

GridView 创建网格列表主要有下面三种方式

  1. 可以通过 GridView.count 实现网格布局
  2. 可以通过 GridView.extent 实现网格布局
  3. 通过 GridView.builder 实现动态网格布局

常用属性:

在这里插入图片描述

GridView.count实现网格布局

GridView.count 构造函数内部使用了 SliverGridDelegateWithFixedCrossAxisCount,我们通过它可以
快速的创建横轴固定数量子元素GridView

实现如下效果:

在这里插入图片描述

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

  
  Widget build(BuildContext context) {
    return GridView.count(
      crossAxisCount: 3,
      children: const [
        Icon(Icons.home),
        Icon(Icons.ac_unit),
        Icon(Icons.search),
        Icon(Icons.settings),
        Icon(Icons.airport_shuttle),
        Icon(Icons.all_inclusive),
        Icon(Icons.beach_access),
        Icon(Icons.cake),
        Icon(Icons.circle),
      ],
    );
  }
}

GridView.extent实现网格布局

GridView.extent 构造函数内部使用了 SliverGridDelegateWithMaxCrossAxisExtent,我们通过它可以
快速的创建横轴子元素为固定最大长度GridView

实现如下效果:

在这里插入图片描述

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

  
  Widget build(BuildContext context) {
    return GridView.extent(
      maxCrossAxisExtent: 150,
      children: const [
        Icon(Icons.home),
        Icon(Icons.ac_unit),
        Icon(Icons.search),
        Icon(Icons.settings),
        Icon(Icons.airport_shuttle),
        Icon(Icons.all_inclusive),
        Icon(Icons.beach_access),
        Icon(Icons.cake),
        Icon(Icons.circle),
      ],
    );
  }
}

GridView.count和GridView.extent属性详解

实现如下效果:

在这里插入图片描述

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

  List<Widget> _getListData() {
    List<Widget> list = [];
    for (var i = 0; i < 10; i++) {
      list.add(
        Container(
          alignment: Alignment.center,
          color: Colors.blue,
          child: Text(
            '第${i + 1}条数据',
            style: const TextStyle(
              color: Colors.white,
              fontSize: 20,
            ),
          ),
        ),
      );
    }

    return list;
  }

  
  Widget build(BuildContext context) {
    return GridView.count(
      crossAxisCount: 2, //一行的widget数量
      padding: const EdgeInsets.all(10),
      crossAxisSpacing: 20, //水平子组件之间的间距
      mainAxisSpacing: 20, //垂直子组件之间的间距
      childAspectRatio: 1, //宽高比
      children: _getListData(),
    );
  }
}

GridView.count实现动态列表

实现如下效果:

在这里插入图片描述

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

  List<Widget> _getListData() {
    var tempList = listData.map((value) {
      return Container(
        decoration: BoxDecoration(
            border: Border.all(
                color: const Color.fromRGBO(233, 233, 233, 0.9), width: 1)),
        child: Column(
          children: [
            Image.network(value["imageUrl"]),
            const SizedBox(height: 10),
            Text(
              value["title"],
              textAlign: TextAlign.center,
              style: const TextStyle(fontSize: 16),
            )
          ],
        ),
      );
    });

    return tempList.toList();
  }

  
  Widget build(BuildContext context) {
    return GridView.count(
      crossAxisCount: 2, //一行的widget数量
      padding: const EdgeInsets.all(10),
      crossAxisSpacing: 10, //水平子组件之间的间距
      mainAxisSpacing: 10, //垂直子组件之间的间距
      childAspectRatio: 1, //宽高比
      children: _getListData(),
    );
  }
}

GridView.builder实现动态列表

SliverGridDelegateWithFixedCrossAxisCount

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

  Widget _getListData(context, index) {
    return Container(
      decoration: BoxDecoration(
          border: Border.all(
              color: const Color.fromRGBO(233, 233, 233, 0.9), width: 1)),
      child: Column(
        children: [
          Image.network(listData[index]["imageUrl"]),
          const SizedBox(height: 10),
          Text(
            listData[index]["title"],
            textAlign: TextAlign.center,
            style: const TextStyle(fontSize: 16),
          )
        ],
      ),
    );
  }

  
  Widget build(BuildContext context) {
    return GridView.builder(
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 2, //一行的widget数量
        crossAxisSpacing: 10, //水平子组件之间的间距
        mainAxisSpacing: 10, //垂直子组件之间的间距
      ),
      itemCount: listData.length,
      itemBuilder: _getListData,
    );
  }
}

在这里插入图片描述

SliverGridDelegateWithMaxCrossAxisExtent

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

  Widget _getListData(context, index) {
    return Container(
      decoration: BoxDecoration(
          border: Border.all(
              color: const Color.fromRGBO(233, 233, 233, 0.9), width: 1)),
      child: Column(
        children: [
          Image.network(listData[index]["imageUrl"]),
          const SizedBox(height: 10),
          Text(
            listData[index]["title"],
            textAlign: TextAlign.center,
            style: const TextStyle(fontSize: 18),
          )
        ],
      ),
    );
  }

  
  Widget build(BuildContext context) {
    return GridView.builder(
      gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
        maxCrossAxisExtent: 300,
        crossAxisSpacing: 10, //水平子组件之间的间距
        mainAxisSpacing: 10, //垂直子组件之间的间距
      ),
      itemCount: listData.length,
      itemBuilder: _getListData,
    );
  }
}

在这里插入图片描述

Padding组件

html 中常见的布局标签都有 padding 属性,但是 Flutter 中很多 Widget 是没有 padding 属性的。这个时候我们可以用 Padding 组件处理容器与子元素之间的间距。

属性说明
paddingpadding 值,EdgeInsets 设置填充的值
child子组件
class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(20),
      child: Image.network(
          "https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/2.jpg"),
    );
  }
}

在这里插入图片描述

  • 22
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序喵正在路上

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值