Flutter-自适用高度PageView
需求
在 Flutter 中,PageView 是一个非常常用的组件,能够实现多个页面的滑动切换。然而,默认的 PageView 高度是固定的,这在展示不同高度的页面时,可能会导致不必要的空白或内容裁剪问题。为了使 PageView 能够根据每个页面的内容高度动态调整,我们需要一个自适应高度的 PageView 实现。
效果

本方案的 PageView 可以根据每个页面内容的高度自动调整,保证每个页面的内容在其实际高度内完整显示,并且在页面滑动时,使用平滑的过渡效果。具体来说:
- 每个页面的内容高度不同,
PageView能够动态调整高度。 - 页面切换时,高度变化平滑,不会造成突兀的视觉效果。
实现思路
1. 测量每个页面的高度
首先,我们需要为每个页面添加一个高度测量的机制,测量页面内容的高度。在 Flutter 中,我们可以通过 GlobalKey 和 RenderBox 获取每个 Widget 的实际高度。
2. 动态调整 PageView 高度
在页面滑动时,我们需要根据滑动的进度动态计算当前 PageView 的高度。这就需要在 PageView 的滑动过程中实时更新高度,使得高度随滑动位置逐步过渡。
3. 动态高度过渡
我们可以使用 AnimatedContainer 来平滑过渡 PageView 的高度,避免切换时高度变化过于突兀。通过监听 PageView 的滑动状态,实时调整容器高度。
实现代码
1. 高度测量组件 HeightMeasureWidget
这个组件负责测量每个页面的高度,并将测量结果通过回调传递出去。
import 'package:flutter/material.dart';class HeightMeasureWidget extends StatefulWidget {final Widget child;final Function(double height) onHeightChanged;const HeightMeasureWidget({super.key, required this.child, required this.onHeightChanged});HeightMeasureState createState() => HeightMeasureState();
}class HeightMeasureState extends State<HeightMeasureWidget> {final GlobalKey _key = GlobalKey();void initState() {super.initState();WidgetsBinding.instance.addPostFrameCallback((_) {_measureHeight();});}void _measureHeight() {final RenderBox? renderBox =_key.currentContext!.findRenderObject() as RenderBox?;if (renderBox != null) {widget.onHeightChanged(renderBox.size.height);}}Widget build(BuildContext context) {return Container(key: _key,child: widget.child,);}
}
2. 自适应高度的 AutoHeightPageView
这个组件使用了前面创建的 HeightMeasureWidget 来测量每个页面的高度,然后根据滑动进度调整高度。
import 'package:flutter/material.dart';
import 'measure_height_widget.dart';class AutoHeightPageView extends StatefulWidget {final List<Widget> children;final PageController pageController;const AutoHeightPageView({Key? key,required this.children,required this.pageController,}) : super(key: key);AutoHeightPageViewState createState() => AutoHeightPageViewState();
}class AutoHeightPageViewState extends State<AutoHeightPageView> {final List<double> _heights = [];double _currentHeight = 0;void initState() {super.initState();widget.pageController.addListener(_updateHeight);}void _updateHeight() {if (widget.pageController.position.haveDimensions && _heights.isNotEmpty) {double page = widget.pageController.page ?? 0.0;int index = page.floor();int nextIndex = (index + 1) < _heights.length ? index + 1 : index;double percent = page - index;double height =_heights[index] + (_heights[nextIndex] - _heights[index]) * percent;setState(() {_currentHeight = height;});}}Widget build(BuildContext context) {var isMeasureHeight =_heights.length == widget.children.length ? false : true;return Column(children: [Stack(children: [Visibility(visible: isMeasureHeight,child: Stack(children: widget.children.map((e) => HeightMeasureWidget(child: e,onHeightChanged: (height) {_heights.add(height);if (_heights.length == widget.children.length) {setState(() {_currentHeight = _heights[0];});}},)).toList(),),),if (!isMeasureHeight)AnimatedContainer(duration: const Duration(milliseconds: 200),height: _currentHeight,curve: Curves.easeOut,child: PageView(controller: widget.pageController,children: widget.children,),),],)],);}void dispose() {widget.pageController.dispose();super.dispose();}
}
3. 使用示例 AutoHeightPageViewPage
该页面演示了如何使用自适应高度的 PageView,通过内容高度的动态调整,确保 PageView 始终适应当前页面的高度。
import 'package:flutter/material.dart';
import 'package:flutter_xy/r.dart';
import 'package:flutter_xy/xydemo/vp/pageview/auto_height_page_view.dart';class AutoHeightPageViewPage extends StatefulWidget {const AutoHeightPageViewPage({Key? key}) : super(key: key);State<StatefulWidget> createState() => AutoHeightPageViewState();
}class AutoHeightPageViewState extends State<AutoHeightPageViewPage> {final PageController _pageController = PageController();Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('自适用高度PageView'),),body: Container(color: Colors.white,child: SingleChildScrollView(child: Column(children: [AutoHeightPageView(pageController: _pageController,children: [Container(color: Colors.white,child: ListView(shrinkWrap: true,physics: const NeverScrollableScrollPhysics(),children: [Container(color: Colors.red,height: 50,alignment: Alignment.center,child: const Text("第一个界面"),),Container(color: Colors.yellow,height: 50,alignment: Alignment.center,child: const Text("第一个界面"),),Container(color: Colors.blue,height: 50,alignment: Alignment.center,child: const Text("第一个界面"),),],),),Container(color: Colors.green,height: 250,child: const Center(child: Text('第二个界面'))),],),Image.asset(R.vp_content_jpg,width: MediaQuery.of(context).size.width,fit: BoxFit.fill,),],),),),);}
}
总结
通过本方案,我们实现了一个自适应高度的 PageView,它能够根据每个页面的内容高度进行动态调整。该实现依赖于对每个页面内容的测量,并使用 AnimatedContainer 来平滑地过渡高度变化。这样可以确保页面切换时,用户体验更加自然流畅,避免了内容被裁剪或空白区域的出现。这种自适应高度的 PageView 非常适合用于不同页面内容高度不一致的场景。
详情:github.com/yixiaolunhui/flutter_xy
相关文章:
Flutter-自适用高度PageView
需求 在 Flutter 中,PageView 是一个非常常用的组件,能够实现多个页面的滑动切换。然而,默认的 PageView 高度是固定的,这在展示不同高度的页面时,可能会导致不必要的空白或内容裁剪问题。为了使 PageView 能够根据每…...
群晖NAS本地搭建可远程交互的大型语言模型LLM聊天机器人
文章目录 前言1. 拉取相关的Docker镜像2. 运行Ollama 镜像3. 运行Chatbot Ollama镜像4. 本地访问5. 群晖安装Cpolar6. 配置公网地址7. 公网访问8. 固定公网地址 前言 本文主要分享如何在群晖NAS本地部署并运行一个基于大语言模型Llama 2的个人本地聊天机器人并结合内网穿透工具…...
TypeScript 构建工具之 webpack
在实际开发中,直接使用TypeScript 编译器的情况不多。 在项目中,需要使用构建工具对代码进行打包,不可能脱离项目使用TypeScript 编译器单独打包TypeScript 。 那如何将 webpack 和 TypeScript 进行集成? 参考文档: w…...
conda环境下在pycharm中调试scrapy项目
前提条件 已经创建好了conda环境已经安装好了scrapy框架项目初始化完成 编写一个爬虫脚本 import scrapyclass StackOverflowSpider(scrapy.Spider):name stackoverflowstart_urls [http://stackoverflow.com/questions?sortvotes]def parse(self, response):print("…...
contenteditable=“true“的标签限制字数的时候修改光标位置
contenteditable"true"的标签限制字数的时候修改光标位置 有时候input和textarea并不能完全满足ui需求,这个时候我们就用contenteditable"true"来将别的标签修改为可编辑状态,但当我们通过js修改了内容之后光标的位置就是一个问题&…...
51单片机-LED灯蜂鸣器数码管按键DS18B20温度传感器
LDE灯的相关程序 LED灯闪烁 LED流水灯 方法1 方法二: 因为P1口可以直接控制P1^0~P1^7的8个led灯,利用一个8位的二进制数字来进行控制即可。如果要点亮P1^0 只需要给P1口传递 1111 1110即可。 蜂鸣器的使用 什么是蜂鸣器? 蜂鸣器是一种一…...
笔记本一线品牌有哪些
笔记本电脑的一线品牌通常指的是在市场上具有较高市场份额、良好口碑、较强的技术实力和服务能力的品牌。根据目前的信息,笔记本电脑市场的一线品牌主要包括以下几个: 联想 (Lenovo):联想在全球笔记本市场上的占有率较高,其产品线…...
mysql聚合函数和分组
我最近开了几个专栏,诚信互三! > |||《算法专栏》::刷题教程来自网站《代码随想录》。||| > |||《C专栏》::记录我学习C的经历,看完你一定会有收获。||| > |||《Linux专栏》࿱…...
ubuntu20.04+RealSenseD455
ubuntu20.04安装驱动双目相机RealSenseD455 安装环境安装RealSense SDK 2.0ROS包安装启动Realsense摄像头存在的 bugD455标定安装环境 系统:Ubuntu20.04 ROS:Noetic 视觉传感器:Intel RealSense D455 安装RealSense SDK 2.0 该安装有两种方式,一个是用命令安装,另一个是…...
WAF绕过技巧
WAF绕过技巧 WAF(Web Application Firewall)是一种安全系统,旨在监控和控制网络流量,以防止攻击,如SQL 注入、跨站脚本(XSS)和拒绝服务(DoS)。 WAF 可以通过多种方式绕过…...
HarmonyOS应用三之组件生命周期和参数传递
目录: 1、生命周期的执行顺序2、页面数据传递3、图片的读取4、数据的备份和恢复5、轮播图6、页面布局图 1、生命周期的执行顺序 /** Copyright (c) 2023 Huawei Device Co., Ltd.* Licensed under the Apache License, Version 2.0 (the "License");* yo…...
[Qt][Qt 网络][上]详细讲解
目录 0.概述1.UDP Socket1.核心API概览2.回显服务器3.回显客户端 0.概述 要使用Qt中有关网络编程的API,需要添加network模块 1.UDP Socket 1.核心API概览 主要的类有两个:QUdpSocket和QNetworkDatagramQUdpSocket表⽰⼀个UDP的socket⽂件 bind(const …...
读零信任网络:在不可信网络中构建安全系统21读后总结与感想兼导读
1. 基本信息 零信任网络:在不可信网络中构建安全系统 道格巴斯(Doug Barth) 著 人民邮电出版社,2019年8月出版 1.1. 读薄率 书籍总字数252千字,笔记总字数73194字。 读薄率73194252000≈29.5% 这个读薄率是最高的吧&#x…...
Java基础——注释
在开发中注释是必不可少的,帮助我们更好的标记阅读代码,下面介绍几种常用的注释方式。 一、注释种类 1. 单行注释 使用//一行代码来进行注释,只能注释一行内容 2. 多行注释 使用斜杠星号的方式 /*注释多行代码*/,注释多行代…...
Redis未授权访问漏洞利用合集
一、基本信息 靶机:IP:192.168.100.40 攻击机:IP:192.168.100.60 二、漏洞 & 过程 Redis 未授权访问漏洞利用无口令远程登录靶机 靶机 cd redis-4.0.8/src./redis-server ../redis.conf 攻击机 ./redis-cli -h 192.168.100.40 Redis 未授权访问…...
基于asp.net的在线考试系统、基于c#的在线考试管理系统
摘 要 伴随着社会以及科学技术的发展,互联网已经渗透在人们的身边,网络慢慢的变成了人们的生活必不可少的一部分,紧接着网络飞速的发展,管理系统这一名词已不陌生,越来越多的学校、公司等机构都会定制一款属于自己个…...
将 hugo 博客搬迁到服务器
1. 说明 在 Ubuntu 22.04 上使用 root 账号,创建普通账号,并赋予 root 权限。 演示站点:https://woniu336.github.io/ 魔改hugo主题: https://github.com/woniu336/hugo-magic 2. 服务器配置 建立 git 用户 adduser git安装 git sudo apt …...
【Datawhale AI夏令营第四期】 魔搭-大模型应用开发方向笔记 Task04 RAG模型 人话八股文Bakwaan_Buddy项目创空间部署
【Datawhale AI夏令营第四期】 魔搭-大模型应用开发方向笔记 Task04 RAG模型 人话八股文Bakwaan_Buddy项目创空间部署 什么是RAG: 我能把这个过程理解为Kimi.ai每次都能列出的一大堆网页参考资料吗?Kimi学了这些资料以后,根据这里面的信息综…...
CTF密码学小结
感觉没啥好总结的啊 基础的永远是RSA、流密码、哈希、对称密码、古典密码那一套(密码学上过课都会),其他的就是数论的一些技巧 似乎格密码也很流行,以及一些奇奇怪怪的性质利用也很多 1、random设置种子后随机的性质:…...
Vue快速入门(七)——Vue3 状态管理 - Pinia(二)
目录 六、核心概念——Getter 1、基本操作 定义getter 访问getter 2、访问其他 getter 3、向 getter 传递参数 4、访问其他 store 的 getter 使用 setup() 时的用法 使用选项式 API 的用法 使用 setup() 不使用 setup() 七、核心概念——Action 1、基本操作 定义a…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
c# 局部函数 定义、功能与示例
C# 局部函数:定义、功能与示例 1. 定义与功能 局部函数(Local Function)是嵌套在另一个方法内部的私有方法,仅在包含它的方法内可见。 • 作用:封装仅用于当前方法的逻辑,避免污染类作用域,提升…...
vue3 daterange正则踩坑
<el-form-item label"空置时间" prop"vacantTime"> <el-date-picker v-model"form.vacantTime" type"daterange" start-placeholder"开始日期" end-placeholder"结束日期" clearable :editable"fal…...
从物理机到云原生:全面解析计算虚拟化技术的演进与应用
前言:我的虚拟化技术探索之旅 我最早接触"虚拟机"的概念是从Java开始的——JVM(Java Virtual Machine)让"一次编写,到处运行"成为可能。这个软件层面的虚拟化让我着迷,但直到后来接触VMware和Doc…...
