Flutter 中在单个屏幕上实现多个列表

今天,我将提供一个实际的示例,演示如何在单个页面上实现多个列表,这些列表可以水平排列、网格格式、垂直排列,甚至是这些常用布局的组合。

下面是要做的:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

实现

让我们从创建一个包含产品所有属性的产品模型开始。

class Product {
  final String id;
  final String name;
  final double price;
  final String image;

  const Product({
    required this.id,
    required this.name,
    required this.price,
    required this.image,
  });

  factory Product.fromJson(Map json) {
    return Product(
      id: json['id'],
      name: json['name'],
      price: json['price'],
      image: json['image'],
    );
  }
}

现在,我们将设计我们的小部件以支持水平、垂直和网格视图。

创建一个名为 HorizontalRawWidget 的新窗口小部件类,定义水平列表的用户界面。

import 'package:flutter/material.dart';
import 'package:multiple_listview_example/models/product.dart';

class HorizontalRawWidget extends StatelessWidget {
  final Product product;

  const HorizontalRawWidget({Key? key, required this.product})
      : super(key: key);

  
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(
        left: 15,
      ),
      child: Container(
        width: 125,
        decoration: BoxDecoration(
            color: Colors.white, borderRadius: BorderRadius.circular(12)),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Padding(
              padding: const EdgeInsets.fromLTRB(5, 5, 5, 0),
              child: ClipRRect(
                borderRadius: BorderRadius.circular(12),
                child: Image.network(
                  product.image,
                  height: 130,
                  fit: BoxFit.contain,
                ),
              ),
            ),
            Expanded(
              child: Padding(
                padding: const EdgeInsets.all(8.0),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Expanded(
                      child: Text(product.name,
                          maxLines: 2,
                          overflow: TextOverflow.ellipsis,
                          style: const TextStyle(
                              color: Colors.black,
                              fontSize: 12,
                              fontWeight: FontWeight.bold)),
                    ),
                    Row(
                      crossAxisAlignment: CrossAxisAlignment.end,
                      children: [
                        Text("\$${product.price}",
                            style: const TextStyle(
                                color: Colors.black, fontSize: 12)),
                      ],
                    ),
                  ],
                ),
              ),
            )
          ],
        ),
      ),
    );
  }
}

设计一个名为 GridViewRawWidget 的小部件类,定义单个网格视图的用户界面。

import 'package:flutter/material.dart';
import 'package:multiple_listview_example/models/product.dart';

class GridViewRawWidget extends StatelessWidget {
  final Product product;

  const GridViewRawWidget({Key? key, required this.product}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(5),
      decoration: BoxDecoration(
          color: Colors.white, borderRadius: BorderRadius.circular(10)),
      child: Column(
        children: [
          AspectRatio(
            aspectRatio: 1,
            child: ClipRRect(
              borderRadius: BorderRadius.circular(10),
              child: Image.network(
                product.image,
                fit: BoxFit.fill,
              ),
            ),
          )
        ],
      ),
    );
  }
}

最后,让我们为垂直视图创建一个小部件类。

import 'package:flutter/material.dart';
import 'package:multiple_listview_example/models/product.dart';

class VerticalRawWidget extends StatelessWidget {
  final Product product;

  const VerticalRawWidget({Key? key, required this.product}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.symmetric(horizontal: 15, vertical: 5),
      padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
      color: Colors.white,
      child: Row(
        children: [
          Image.network(
            product.image,
            width: 78,
            height: 88,
          ),
          const SizedBox(
            width: 15,
          ),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  product.name,
                  style: const TextStyle(fontSize: 12, color: Colors.black, fontWeight: FontWeight.bold),
                ),
                SizedBox(
                  height: 5,
                ),
                Text("\$${product.price}",
                    style: const TextStyle(color: Colors.black, fontSize: 12)),
              ],
            ),
          )
        ],
      ),
    );
  }
}

现在是时候把所有的小部件合并到一个屏幕中了,我们先创建一个名为“home_page.dart”的页面,在这个页面中,我们将使用一个横向的 ListView、纵向的 ListView 和 GridView。

import 'package:flutter/material.dart';
import 'package:multiple_listview_example/models/product.dart';
import 'package:multiple_listview_example/utils/product_helper.dart';
import 'package:multiple_listview_example/views/widgets/gridview_raw_widget.dart';
import 'package:multiple_listview_example/views/widgets/horizontal_raw_widget.dart';
import 'package:multiple_listview_example/views/widgets/title_widget.dart';
import 'package:multiple_listview_example/views/widgets/vertical_raw_widget.dart';

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    List products = ProductHelper.getProductList();
    return Scaffold(
      backgroundColor: const Color(0xFFF6F5FA),
      appBar: AppBar(
        centerTitle: true,
        title: const Text("Home"),
      ),
      body: SingleChildScrollView(
        child: Container(
          padding: const EdgeInsets.symmetric(vertical: 20),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              const TitleWidget(title: "Horizontal List"),
              const SizedBox(
                height: 10,
              ),
              SizedBox(
                height: 200,
                child: ListView.builder(
                    shrinkWrap: true,
                    scrollDirection: Axis.horizontal,
                    itemCount: products.length,
                    itemBuilder: (BuildContext context, int index) {
                      return HorizontalRawWidget(
                        product: products[index],
                      );
                    }),
              ),
              const SizedBox(
                height: 10,
              ),
              const TitleWidget(title: "Grid View"),
              Container(
                padding:
                    const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
                child: GridView.builder(
                    gridDelegate:
                        const SliverGridDelegateWithFixedCrossAxisCount(
                            crossAxisCount: 2,
                            crossAxisSpacing: 13,
                            mainAxisSpacing: 13,
                            childAspectRatio: 1),
                    itemCount: products.length,
                    shrinkWrap: true,
                    physics: const NeverScrollableScrollPhysics(),
                    itemBuilder: (BuildContext context, int index) {
                      return GridViewRawWidget(
                        product: products[index],
                      );
                    }),
              ),
              const TitleWidget(title: "Vertical List"),
              ListView.builder(
                  itemCount: products.length,
                  shrinkWrap: true,
                  physics: const NeverScrollableScrollPhysics(),
                  itemBuilder: (BuildContext context, int index) {
                    return VerticalRawWidget(
                      product: products[index],
                    );
                  }),
            ],
          ),
        ),
      ),
    );
  }
}

我使用了一个 SingleChildScrollView widget 作为代码中的顶部根 widget,考虑到我整合了多个布局,如水平列表、网格视图和垂直列表,我将所有这些 widget 包装在一个 Column widget 中。

挑战在于如何处理多个滚动部件,因为在上述示例中有两个垂直滚动部件:一个网格视图和一个垂直列表视图。为了禁用单个部件的滚动行为, physics 属性被设置为 const NeverScrollableScrollPhysics()。取而代之的是,使用顶层根 SingleChildScrollView`` 来启用整个内容的滚动。此外,SingleChildScrollView上的shrinkWrap属性被设置为true`,以确保它能紧紧包裹其内容,只占用其子控件所需的空间。

Github 链接:https://github.com/tarunaronno005/flutter-multiple-listview

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/155448.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

云ES使用集群限流插件(aliyun-qos)

aliyun-qos插件是阿里云Elasticsearch团队自研的插件,能够提高集群的稳定性。该插件能够实现集群级别的读写限流,在关键时刻对指定索引降级,将流量控制在合适范围内。例如当上游业务无法进行流量控制时,尤其对于读请求业务,可根据aliyun-qos插件设置的规则,按照业务的优先…

矿区安全检查VR模拟仿真培训系统更全面、生动有效

矿山企业岗位基数大,生产过程中会持续有新入矿的施工人员及不定期接待的参观人员,下井安全须知培训需求量大。传统实景拍摄的视频剪辑表达方式有限,拍摄机位受限,难以生动表达安全须知的内容,且井下现场拍摄光线不理想…

【算法】复习搜索与图论

🍎 博客主页:🌙披星戴月的贾维斯 🍎 欢迎关注:👍点赞🍃收藏🔥留言 🍇系列专栏:🌙 蓝桥杯 🌙请不要相信胜利就像山坡上的蒲公英一样唾手…

Mysql分组查询每组最新的一条数据

在工作中遇到一个问题,需要查出每个公司最新的那条数据。 所以需根据公司进行分组: 未进行分组时: select a.id, b.name companyName, result_asset ,result_liability ,result_net_asset, a.create_time ,a.is_deleted from bus_proper…

【Linux】环境变量--PATH环境变量/环境变量的操作/命令行参数

文章目录 一、PATH环境变量1.什么是PATH环境变量2.如何添加PATH环境变量3.系统中的其他环境变量4.环境变量的来源 二、环境变量的操作1.设置环境变量2.通过getenv获取环境变量3.环境变量的意义 三、命令行参数 一、PATH环境变量 1.什么是PATH环境变量 这里我们先提出一个问题…

一起学docker系列之三docker的详细安装步骤

目录 前言1. 准备环境2. 卸载已有的Docker3. 安装编译工具4. 安装必需的软件5. 配置镜像仓库6. 更新YUM软件包索引7. 安装Docker CE8. 启动Docker9. 测试Docker10. 卸载Docker结语 前言 安装Docker是一项重要的任务,因为它为应用程序提供了容器化的环境&#xff0c…

uni-app开发微信小程序 vue3写法添加pinia

说明 使用uni-app开发,选择vue3语法,开发工具是HBliuderX。虽然内置有vuex,但是个人还是喜欢用Pinia,所以就添加进去了。 Pinia官网连接 添加步骤 第一步: 在项目根目录下执行命令: npm install pinia …

【无标题】chapter6卷积

此例以说明全连接层处理图片的时候会遇到参数过多 模型过大的问题 参数比要研究的物体总数还多 卷积,特殊的全联接层 平移不变形,局部性 原本权重为二维(输入和输出全联接,想想下表组合,就是个二维的矩阵)…

Scrapy----Scrapy简介

文章目录 概述与应用背景架构和组件功能和特点社区生态概述与应用背景 Scrapy,一个高效、灵活、且强大的Web爬取框架,被广泛应用于数据抓取和网页内容的结构化提取。它是用Python编写的,支持多平台运行,适用于数据挖掘、在线零售信息收集、历史数据存档等多种场景。Scrapy…

idea运行项目之后一直卡在Writing classes… 解决方案

最近遇到idea里直接运行一个Spring boot项目后,idea一直慢悠悠的parsing java,然后就writing classes,然后就一直卡着不动了,运气好10几分钟能把项目启动起来。 多年的摸鱼经验告诉我,事出反常必有妖,赶紧…

【Java】详解多线程通信

🌺个人主页:Dawn黎明开始 🎀系列专栏:Java ⭐每日一句:什么都不做,才会来不及 📢欢迎大家:关注🔍点赞👍评论📝收藏⭐️ 文章目录 🔐多…

求10的阶乘之和

这个问题很简单&#xff0c;我们用for循环就可以做到&#xff01; 目录 1.用两个for循环求值 2.用一个for循环求值 1.用两个for循环求值 int main() {int i 1;int ret 1;int sum 0;int n 0;for (n 1; n < 10; n){ret 1;for (i 1; i < n; i){ret ret * i;}sum …

文件传输客户端 SecureFX mac中文版支持多种协议

SecureFX mac是一款功能强大的文件传输客户端&#xff0c;可在 Mac 操作系统上使用。它由 VanDyke Software 公司开发&#xff0c;旨在为用户提供安全、可靠、高效的文件传输服务。 SecureFX 支持多种协议&#xff0c;包括 SFTP、SCP、FTP、FTP over SSL/TLS 和 HTTP/S。它使用…

Django 配置 Email Admin 详细指南

概要 Django 是一个高级的 Python Web 框架&#xff0c;它鼓励快速开发和清洁、实用的设计。当你正在开发一个 Django 项目时&#xff0c;监控网站的运行情况是非常必要的。Django 提供了一个功能强大的 admin 界面&#xff0c;但同时也可以通过配置 email admin 来获取网站的…

第9章 K8s进阶篇-持久化存储入门

9.1 k8s存储Volumes介绍 Container&#xff08;容器&#xff09;中的磁盘文件是短暂的&#xff0c;当容器崩溃时&#xff0c;kubelet会重新启动容器&#xff0c;但最初的文件将丢失&#xff0c;Container会以最干净的状态启动。另外&#xff0c;当一个Pod运行多个Container时&…

设计模式解码:软件工程架构的航标

引言 软件工程领域的设计模式&#xff0c;就像是建筑师手中的设计蓝图&#xff0c;它们是经验的总结&#xff0c;指导开发者如何在面对层出不穷的编程难题时&#xff0c;构建出既稳固又灵活的软件结构。就像一座经过精心设计的大厦能够经受住风雨的考验一样&#xff0c;一个利用…

MAC地址注册的网络安全影响和措施分析

MAC地址注册对网络安全具有重要影响&#xff0c;同时也需要采取相应的措施来应对潜在的安全风险。以下是有关MAC地址注册的网络安全影响和应对措施的分析&#xff1a; 影响&#xff1a; 1. 身份验证&#xff1a;MAC地址注册可用于设备的身份验证&#xff0c;但MAC地址本身并不…

MATLAB 机械臂逆运动学进行轨迹控制建模

系列文章目录 文章目录 系列文章目录前言一、模型概览1.1 Target Pose Generation 目标姿势生成1.2 Inverse Kinematics 逆运动学1.3 Manipulator Dynamics 机械手动力学1.4 Pose Measurement 姿势测量 二、机械手定义三、生成航点四、模型设置五、模拟机械手运动六、将结果可视…

how to find gcc openbug

https://gcc.gnu.org/bugzilla/query.cgi?formatadvanced

“开源 vs. 闭源:大模型的未来发展趋势预测“——探讨大模型未来的发展方向

文章目录 每日一句正能量前言什么是大模型的开源与闭源开源与闭源的定义和特点开源的意义开源和闭源的优劣势比较不同的大模型企业&#xff0c;开源、闭源的策略不尽相同。开源vs 闭源&#xff1a;两者并非选择题后记 每日一句正能量 依赖别人的人等于折断了自己的翅膀&#xf…