Flutter笔记:使用Flutter构建响应式PC客户端/Web页面-案例
作者:李俊才 (jcLee95):https://blog.csdn.net/qq_28550263
邮箱 :291148484@163.com
本文地址:https://blog.csdn.net/qq_28550263/article/details/134339945
【简介】也不需要什么简介,使用 Flutter 框架完成下面的响应式PC/Web端效果,你会怎么做呢:
本文就是介绍相关响应式上的一些工具和实践。
目 录
1. Flutter框架中的尺寸工具
在 Flutter 中,你可以使用 MediaQuery、LayoutBuilder、AspectRatio 等Widget实现 尺寸测量与约束,进而创建响应式UI。例如,你可以使用 MediaQuery 来获取屏幕的尺寸和方向,然后根据这些信息来动态调整组件的布局和样式。你也可以使用 LayoutBuilder 来根据父组件的尺寸来调整子组件的布局。
1.1 MediaQuery
MediaQuery 组件可以获取当前媒体(例如屏幕)的一些属性,如尺寸、方向、亮度等。你可以使用 MediaQuery.of(context) 来获取一个 MediaQueryData 对象,然后通过这个对象来获取媒体的属性。
例如,你可以使用以下代码来获取屏幕的宽度和高度:
double screenWidth = MediaQuery.of(context).size.width;
double screenHeight = MediaQuery.of(context).size.height;
你也可以使用以下代码来判断当前是否为横屏:
bool isLandscape = MediaQuery.of(context).orientation == Orientation.landscape;
1.2 LayoutBuilder
LayoutBuilder 组件可以根据父组件的约束来生成不同的布局。
LayoutBuilder 的构造函数接受一个回调函数,这个回调函数有两个参数:BuildContext 和 BoxConstraints。 其中 BoxConstraints 对象描述了父组件对子组件的约束,如最大/最小宽度和高度。
例如,你可以使用以下代码来创建一个宽度为父Widget一半的子组件:
LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) {return Container(width: constraints.maxWidth / 2,child: ...,);},
)
LayoutBuilder 和 MediaQuery 都是 Flutter 中用于获取和处理布局信息的工具,但它们的用途和工作方式有所不同:
-
MediaQuery 主要用于获取媒体(通常是屏幕)的信息,如尺寸、方向、亮度等。你可以使用 MediaQuery 来创建响应式布局,即根据屏幕的尺寸和方向来调整布局。例如,你可以使用 MediaQuery 来获取屏幕的宽度,然后根据宽度来决定显示一个列布局还是行布局;
-
LayoutBuilder主要用于获取父组件对子组件的约束,如最大/最小宽度和高度。你可以使用 LayoutBuilder 来创建自适应布局,即根据父组件的约束来调整子组件的布局。例如,你可以使用 LayoutBuilder来获取父组件的最大宽度,然后根据最大宽度来决定子组件的宽度。
-
MediaQuery 获取的是一个 静态的快照,它获取的屏幕信息在获取时就已经固定,不会随着屏幕尺寸的变化而变化。如果你需要响应屏幕尺寸的变化,你需要在屏幕尺寸变化时重新获取MediaQuery的信息。相比之下, LayoutBuilder 则是动态的,它会在父组件的约束变化时重新构建。这意味着如果父 组件 的尺寸变化了,LayoutBuilder 会自动重新获取约束并重新构建子组件。
可见,如果你需要创建一个能够响应屏幕尺寸变化的布局,你可能需要使用 LayoutBuilder。如果你只需要获取屏幕的信息,并不需要响应屏幕尺寸的变化,那么 MediaQuery 可能更适合你;反之,则需要使用 LayoutBuilder。
1.3 AspectRatio
AspectRatio 用于强制子组件具有特定的宽高比。AspectRatio 的构造函数接受一个 aspectRatio
参数,这个参数表示宽度和高度的比例。比如:
AspectRatio(aspectRatio: 16 / 9,child: ...,
)
2. 实践:构建响应式Header
我们实现响应式Header的基本思路是,根据屏幕的宽度来选择显示不同的顶部导航栏。整体上,依据这个
class ResponsiveHeaderNavbar extends StatelessWidget {const ResponsiveHeaderNavbar({Key? key}) : super(key: key);Widget build(BuildContext context) {return LayoutBuilder(builder: (context, constraints) {// 使用LayoutBuilder来获取父Widget的尺寸约束// 如果最大宽度小于800像素,返回垂直布局的顶部导航栏if (constraints.maxWidth < 800) {return const VerticalTopNav();} // 否则,返回原始的顶部导航栏else {return const OriginalTopNav();}},);}
}
在这个组件中,LayoutBuilder被用来获取父Widget的尺寸约束,然后根据最大宽度来选择显示哪种顶部导航栏。如果最大宽度小于800像素,就显示垂直布局的顶部导航栏;否则,就显示原始的顶部导航栏。这样,无论屏幕的尺寸如何变化,都能保证顶部导航栏的布局适应屏幕尺寸。
接下来,我们该具体实现 VerticalTopNav 和 OriginalTopNav了。
先看 OriginalTopNav 部分:
class OriginalTopNav extends StatelessWidget {const OriginalTopNav({super.key});Widget build(BuildContext context) {return Container(color: Colors.black,child: const Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly,children: [HoverTextWidget(66),HoverTextWidget(66),HoverTextWidget(66),HoverTextWidget(66),Spacer(),HoverTextWidget(66),],),);}
}
其中的文本组件HoverTextWidget代码为:
class HoverTextWidget extends StatefulWidget {const HoverTextWidget(this.width, {Key? key}) : super(key: key);final double width;State<HoverTextWidget> createState() => _HoverTextWidgetState();
}class _HoverTextWidgetState extends State<HoverTextWidget> {// 定义一个状态变量,用于记录鼠标是否悬停在该组件上bool isHovered = false;// 定义一个方法,用于更新isHovered状态void onHover(bool hover) {setState(() {isHovered = hover;});}Widget build(BuildContext context) {return MouseRegion(// 当鼠标进入该组件时,调用onHover方法并传入trueonEnter: (_) => onHover(true),// 当鼠标离开该组件时,调用onHover方法并传入falseonExit: (_) => onHover(false),// 根据isHovered状态来改变鼠标光标,如果isHovered为true,光标为click,否则为basiccursor: isHovered ? SystemMouseCursors.click : SystemMouseCursors.basic,child: GestureDetector(child: Container(height: 40,alignment: Alignment.center,// 根据isHovered状态来改变背景颜色,如果isHovered为true,背景颜色为grey,否则为blackcolor: isHovered ? Colors.grey : Colors.black,width: widget.width,child: Text('链接',style: TextStyle(// 根据isHovered状态来改变文字颜色,如果isHovered为true,文字颜色为black,否则为whitecolor: isHovered ? Colors.black : Colors.white,),),),),);}
}
似乎没什么难点需要说明的,那就接着 VelticalTopNav 部分:
class VelticalTopNav extends StatelessWidget {const VelticalTopNav({super.key});Widget build(BuildContext context) {// return const LinkedTabGroup(Axis.vertical);return ExpansionTile(// title: Text(''),title: SvgPicture.asset('/assets/svgs/jcstudio-v2-color.svg',width: 50, // 设置宽度height: 50,),leading: const Icon(Icons.menu, color: Colors.amber),backgroundColor: Colors.black,collapsedBackgroundColor: Colors.black,collapsedIconColor: Colors.amber,collapsedTextColor: Colors.amber,children: [ListTile(title: const Text('链接',style: TextStyle(color: Colors.white),),onTap: () {// 处理子菜单项点击},),ListTile(title: const Text('链接',style: TextStyle(color: Colors.white),),onTap: () {// 处理子菜单项点击},),ListTile(title: const Text('链接',style: TextStyle(color: Colors.white),),onTap: () {// 处理子菜单项点击},),ListTile(title: const Text('链接',style: TextStyle(color: Colors.white),),onTap: () {// 处理子菜单项点击},),],);}
}
这样就完成了整体效果中的Header效果,它将在后面被我们的响应式页面所调用。
4. 实践:完成一个响应式Web骨架
同样的思路,我们可以用以完成页面主体部分。
class ExampleHomePage extends StatelessWidget {final String url = '/windows_page_view_2';const ExampleHomePage({super.key});Widget build(BuildContext context) {return Scaffold(body: Column(children: [// 调用上一节写好的 Header(appbar)部分const ResponsiveHeaderNavbar(),Expanded(child: CustomScrollView(slivers: [SliverAppBar(automaticallyImplyLeading: false,pinned: true,snap: false,floating: false,expandedHeight: 460.0,flexibleSpace: FlexibleSpaceBar(background: Image.asset('assets/images/it_curtoon_1.png',fit: BoxFit.cover,),title: const Text('flutter-online.top'),),),// 同样的思路在这里再来一次SliverToBoxAdapter(child: LayoutBuilder(builder: (context, constraints) {if (constraints.maxWidth < 800) {// 当屏幕宽度小于800像素时,改为上下布局return const Column(children: [Part1(),Part2(),],);} else {// 屏幕宽度大于等于800像素时,保持原有布局return Column(children: [Container(padding: const EdgeInsets.only(left: 100, right: 100, top: 30),child: const Row(children: [Expanded(child: Part1(),),SizedBox(width: 260,child: Part2(),),],),),],);}},),),],),),],),);}
}
效果如开头我所展示的那样:
相关文章:

Flutter笔记:使用Flutter构建响应式PC客户端/Web页面-案例
Flutter笔记 使用Flutter构建响应式PC客户端/Web页面-案例 作者:李俊才 (jcLee95):https://blog.csdn.net/qq_28550263 邮箱 :291148484163.com 本文地址:https://blog.csdn.net/qq_28550263/article/detai…...
聊聊LogbackMDCAdapter
序 本文主要研究一下LogbackMDCAdapter MDCAdapter org/slf4j/spi/MDCAdapter.java public interface MDCAdapter {/*** Put a context value (the <code>val</code> parameter) as identified with* the <code>key</code> parameter into the cur…...

spring命名空间注入和XML自动装配、引入外部配置文件
Spring p命名空间注入util命名空间注入基于XML的自动装配根据名称自动装配 Spring引入外部属性配置文件 p命名空间注入 作用:简化配置。 使用p命名空间注入的前提条件包括两个: ● 第一:在XML头部信息中添加p命名空间的配置信息:…...

【2024年11月份--2024精灵云校招C++笔试题】
考试形式 笔试考了三道算法题,笔试形式为阅读题目,然后用中文给出算法思路,最后给出算法例程,分数各占一半,简单,中等,复杂各一道题。我看9月份有人也是考这3道题,一模一样。 第…...

Visual Studio 2019下编译OpenCV 4.7 与OpenCV 4.7 contrib
一、环境 使用的环境是Win10,Visual Studio 2019,Cmake3.28,cdua 11.7,cudnn 8.5,如果只是在CPU环境下使用,则不用安装CUDA。要使用GPU处理,安装好CUDA之后,要测试安装的CUDA是否能用。不能正常使用的话,添加一下系统…...

【Linux网络】系统调优之聚合链路bonding,可以实现高可用和负载均衡
一、什么是多网卡绑定 二、聚合链路的工作模式 三、实操创建bonding设备(mode1) 1、实验 2、配置文件解读 3、查看bonding状态,验证bonding的高可用效果 三、nmcli实现bonding 一、什么是多网卡绑定 将多块网卡绑定同一IP地址对外提供服务…...

k8s持久化存储PV、PVC
容器磁盘上的文件的生命周期是短暂的,这就使得在容器中运行重要应用时会出现一些问题。首先,当容器崩溃时,kubelet 会重启它,但是容器中的文件将丢失——容器以干净的状态(镜像最初的状态)重新启动。其次&a…...

CocosCreator3.8原生引擎源码研究
1. Cocos Creator引擎架构图 2. 原始引擎源码流程图 下图中包含Android native层引擎到js适配层的启动和主循环的启用流程和必要说明,本猿比较懒,暂时不细述了,各位看官直接看图吧,还在细化扩充,后续逐渐更新。。。 版…...
高二英语上
unit 1 1.yarn三种意思 1.码; 2.庭院,天井; 3.花园;down**down 在这里是介词,也可以作副词,与 down 相对的是 up。请比较下列两句: 1.Look! Hes driving down the street . 2.Look! Hes driving up the street .这两例…...

JavaWeb Day10 案例 准备工作
目录 一、需求说明 二、环境搭建 (一)数据库 (二)后端 ①controller层 1.DeptController.java 2.EmpController.java ②mapper层 1.DeptMapper.java 2.EmpMapper.java ③pojo层 1.Dept.java 2.Emp.jav…...

Nginx:不同域名访问同一台机器的不同项目
Nginx很简单就可以解决同一台机器同时跑两个或者多个项目,而且都通过域名从80端口走。 以Windows环境下nginx服务为例,配置文件nginx.conf中,http中加上 include /setup/nginx-1.20.1/conf/conf.d/*.conf;删除server部分,完整如…...
C++(20):new数组时元素个数自动推到
C20在new数组时可以根据初始化列表,自动推到元素个数: #include <iostream> using namespace std;int main() {int *pd new int[]{1,2,3,4};for(auto i 0; i < 4; i){cout<<pd[i]<<endl;}return 0; }运行程序输出: 1…...

使用visualStudio发布可执行文件
编译成功后会在程序项目的路径下创建一个debug文件夹和一个release文件夹 文件夹中的具体文件入下所示 生成32位的可执行文件 32位的可执行文件可以在64位的计算机中执行,而64位的操作系统程序只能在64位的计算机中执行安装运行库的安装包根据电脑的版本选择合适的…...

yolo系列报错(持续补充ing)
文章目录 export GIT_PYTHON_REFRESHquiet解决 没有pt权重文件解决 python文件路径报错解决 读取文件列名报错解决 导入不同文件夹出错解决 megengine没有安装解决然后你发现它竟然还没有用 export GIT_PYTHON_REFRESHquiet 设置环境变量 GIT_PYTHON_REFRESH ,这个…...

Technology Strategy Patterns 学习笔记9 - bringing it all together
1 Patterns Map 2 Creating the Strategy 2.1 Ansoff Growth Matrix 和owth-share Matrix 区别参见https://fourweekmba.com/bcg-matrix-vs-ansoff-matrix/ 3 Communicating...

Redis(12)| 过期删除策略和内存淘汰策略
Redis 是可以对 key 设置过期时间的,因此需要有相应的机制将已过期的键值对删除,而做这个工作的就是过期键值删除策略。 如何设置过期时间 先说一下对 key 设置过期时间的命令。 设置 key 过期时间的命令一共有 4 个: expire key n&#x…...

Go-服务注册和发现,负载均衡,配置中心
文章目录 什么是服务注册和发现技术选型 Consul 的安装和配置1. 安装2. 访问3. 访问dns Consul 的api接口go操作consulgrpc下的健康检查grpc的健康检查规范动态获取可用端口号 负载均衡策略1. 什么是负载均衡2. 负载均衡策略1. 集中式load balance2. 进程内load balance3. 独立…...

k8s-实验部署 1
1、k8s集群部署 更改所有主机名称和解析 开启四台实验主机,k8s1 仓库;k8s2 集群控制节点; k8s3 和k8s4集群工作节点; 集群环境初始化 使用k8s1作为仓库,将所有的镜像都保存在本地,不要将集群从外部走 仓库…...

Git的原理与使用(一)
目录 Git初始 Git安装 Git基本操作 创建git本地仓库 配置git 工作区,暂存区,版本库 添加文件,提交文件 查看.git文件 修改文件 版本回退 小结 Git初始 git是一个非常强大的版本控制工具.可以快速的将我们的文档和代码等进行版本管理. 下面这个实例看理解下为什么需…...

1204. 错误票据
题目: 1204. 错误票据 - AcWing题库 思路: 将输入的数据存入数组,从小到大排序后遍历,若 (a[i] a[i - 1])res1 a[i]--->重号;若(a[i] - a[i - 1] > 2)res2 a[i] - 1--->断号。 难点:题目只告诉我们输入…...

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...

家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...

【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...