当前位置: 首页 > news >正文

Flutter 实现按位置大小比例布局的控件

文章目录

  • 前言
  • 一、如何实现?
    • 1、数值转成分数
    • 2、Row+Flexible布局横向
    • 3、Column+Flexible布局纵向
  • 二、完整代码
  • 三、使用示例
    • 1、基本用法
    • 2、四分屏
    • 3、六分屏
    • 4、八分屏
    • 5、九分屏
    • 6、414分屏
  • 总结


前言

做视频监控项目时需要需要展示多分屏,比如2x2、3x3、414等等,如果每一种分屏都单独实现会很麻烦,而且不能支持用户定制。最好的方式还是实现一个通用的分屏容器,而且采样比例计算位置大小,可以适配任意尺寸。


一、如何实现?

最直观的实现方式是获取控件宽高然后按比例计算,但是flutter在build的时候无法获取位置宽高信息,只有绘制之后才能获取,所以这种方式并不容易实现,比较简单的方式应该是使用Row、Column结合Flexible。

1、数值转成分数

需要转换的数值

 final Rect rect; //子控件位置大小,比例值范围0-1

定义一个分数对象

//分数
class Rational {int den = 1; //分母int num = 0; //分子Rational(this.num, this.den);//通过double构造,accuracy小数点后精度factory Rational.fromDouble(double d, {int accuracy = 5}) {int den = 1;while (d > d.toInt() && accuracy-- > 0) {d *= 10;den *= 10;}return Rational(d.toInt(), den);}
}

转成分数并对齐分母

    //将位置大小转成分数final width = Rational.fromDouble(rect.width);final x = Rational.fromDouble(rect.left);final height = Rational.fromDouble(rect.height);final y = Rational.fromDouble(rect.top);//对齐分母if (width.den != x.den) {final den = width.den;width.den *= x.den;width.num *= x.den;x.den *= den;x.num *= den;}//对齐分母if (height.den != y.den) {final den = height.den;height.den *= y.den;height.num *= y.den;y.den *= den;y.num *= den;}

2、Row+Flexible布局横向

我们利用Row的自动布局,以及Flexible的比例布局的特性,根据上面的分数计算出控件比例的位置大小对应的flex值即可。

 Row(children: [Flexible(flex: x.num,child: Container(),),Flexible(flex: width.num,child: child/*子控件,加上纵向布局则是Column*/),Flexible(flex: width.den - width.num - x.num, child: Container()),],);}

3、Column+Flexible布局纵向

我们利用Column的自动布局,以及Flexible的比例布局的特性,根据上面的分数计算出控件比例的位置大小对应的flex值即可。

Column(children: [Flexible(flex: y.num,child: Container(),),Flexible(flex: height.num, child: child/*子控件*/),Flexible(flex: height.den - height.num - y.num,child: Container(),),],)

二、完整代码

proportion.dart

import 'package:flutter/material.dart';//比例布局控件,
class Proportion extends StatelessWidget {final Rect rect; //位置大小,比例值范围0-1final Widget child;const Proportion({super.key,this.rect = const Rect.fromLTWH(0, 0, 1, 1),required this.child,});Widget build(BuildContext context) {//实现按比例显示布局final width = Rational.fromDouble(rect.width);final x = Rational.fromDouble(rect.left);final height = Rational.fromDouble(rect.height);final y = Rational.fromDouble(rect.top);if (width.den != x.den) {final den = width.den;width.den *= x.den;width.num *= x.den;x.den *= den;x.num *= den;}if (height.den != y.den) {final den = height.den;height.den *= y.den;height.num *= y.den;y.den *= den;y.num *= den;}return Row(children: [Flexible(flex: x.num,child: Container(),),Flexible(flex: width.num,child: Column(children: [Flexible(flex: y.num,child: Container(),),Flexible(flex: height.num, child: child),Flexible(flex: height.den - height.num - y.num,child: Container(),),],),),Flexible(flex: width.den - width.num - x.num, child: Container()),],);}
}//分数
class Rational {int den = 1; //分母int num = 0; //分子Rational(this.num, this.den);//通过double构造,accuracy小数点后精度factory Rational.fromDouble(double d, {int accuracy = 5}) {int den = 1;while (d > d.toInt() && accuracy-- > 0) {d *= 10;den *= 10;}return Rational(d.toInt(), den);}
}

常用布局(可选)
proportions.dart

import 'package:flutter/material.dart';import 'proportion.dart';//常用布局,需配合stack作为父容器使用
class Proportions {Proportions._();//全屏static List<Proportion> fullScreen({required Widget child,}) =>[Proportion(rect: const Rect.fromLTWH(0, 0, 1, 1),child: child,)];//二分屏static List<Proportion> halfScreen({required Widget left,required Widget right,}) =>[Proportion(rect: const Rect.fromLTWH(0, 0, 0.5, 1),child: left,),Proportion(rect: const Rect.fromLTWH(0.5, 0, 0.5, 1),child: right,),];//四分屏static List<Proportion> quadScreen({required List<Widget> children,}) {return [Proportion(rect: const Rect.fromLTWH(0, 0, 0.5, 0.5),child: children[0],), //左上Proportion(rect: const Rect.fromLTWH(0.5, 0, 0.5, 0.5),child: children[1],), //右上Proportion(rect: const Rect.fromLTWH(0, 0.5, 0.5, 0.5),child: children[2],), //左下Proportion(rect: const Rect.fromLTWH(0.5, 0.5, 0.5, 0.5),child: children[3],), //右下];}//6  分屏static List<Proportion> sixScreen({required List<Widget> children,}) {return [Proportion(rect: const Rect.fromLTWH(0, 0, 0.666, 0.666),child: children[0],), //左上Proportion(rect: const Rect.fromLTWH(0.666, 0, 0.333, 0.333),child: children[1],), //右上Proportion(rect: const Rect.fromLTWH(0.666, 0.333, 0.333, 0.333),child: children[2],), //右中Proportion(rect: const Rect.fromLTWH(0.666, 0.666, 0.333, 0.333),child: children[3],), //右下Proportion(rect: const Rect.fromLTWH(0.333, 0.666, 0.333, 0.333),child: children[4],), //中下Proportion(rect: const Rect.fromLTWH(0, 0.666, 0.333, 0.333),child: children[5],), //左下];}//8  分屏static List<Proportion> eightScreen({required List<Widget> children,}) {return [Proportion(rect: const Rect.fromLTWH(0, 0, 0.75, 0.75),child: children[0],), //左上Proportion(rect: const Rect.fromLTWH(0.75, 0, 0.25, 0.25),child: children[1],), //右上Proportion(rect: const Rect.fromLTWH(0.75, 0.25, 0.25, 0.25),child: children[2],), //右中1Proportion(rect: const Rect.fromLTWH(0.75, 0.5, 0.25, 0.25),child: children[3],), //右中2Proportion(rect: const Rect.fromLTWH(0.75, 0.75, 0.25, 0.25),child: children[4],), //右下Proportion(rect: const Rect.fromLTWH(0.5, 0.75, 0.25, 0.25),child: children[5],), //中下2Proportion(rect: const Rect.fromLTWH(0.25, 0.75, 0.25, 0.25),child: children[6],), //中下1Proportion(rect: const Rect.fromLTWH(0, 0.75, 0.25, 0.25),child: children[7],), //左下];}//9  分屏static List<Proportion> nightScreen({required List<Widget> children,}) {int n = 0;return [...children.getRange(0, 9).map((element) {final i = n++;return Proportion(rect: Rect.fromLTWH((i % 3) * 0.333,(i ~/ 3) * 0.333,0.333,0.333,),child: element,);},)];}//16  分屏static List<Proportion> sixteenScreen({required List<Widget> children,}) {int n = 0;return [...children.getRange(0, 16).map((element) {final i = n++;return Proportion(rect: Rect.fromLTWH((i % 4) * 0.25, (i ~/ 4) * 0.25, 0.25, 0.25),child: element,);},)];}//414分屏static List<Proportion> fourOneFourScreen({required List<Widget> children,}) {int n = 0;return [//左4...children.getRange(0, 4).map((element) {final i = n++;return Proportion(rect: Rect.fromLTWH((i ~/ 4) * 0.25, (i % 4) * 0.25, 0.25, 0.25),child: element,);},),//中间Proportion(rect: const Rect.fromLTWH(0.25, 0, 0.5, 1),child: children[4],),//右边4...children.getRange(5, 9).map((element) {final i = n++ + 8;return Proportion(rect: Rect.fromLTWH((i ~/ 4) * 0.25, (i % 4) * 0.25, 0.25, 0.25),child: element,);},)];}
}

三、使用示例

1、基本用法

设置子控件位置大小。一般配合stack作为父容器使用

    Proportion(rect: Rect.fromLTRB(0, 0, 0.5, 0.5), //子控件位置大小,(0, 0, 0.5, 0.5)表示左上1/4的区域child: ColoredBox(color: Colors.red), //子控件);

2、四分屏

final List<int> _nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
 Stack(children: Proportions.quadScreen(children: [..._nums.map((e) => Container(constraints: const BoxConstraints.expand(),decoration: BoxDecoration(border: Border.all(color: Colors.deepPurple.shade300)),child: Center(child: Text("video $e")),))

在这里插入图片描述

3、六分屏

final List<int> _nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
 Stack(children: Proportions.sixScreen(children: [..._nums.map((e) => Container(constraints: const BoxConstraints.expand(),decoration: BoxDecoration(border: Border.all(color: Colors.deepPurple.shade300)),child: Center(child: Text("video $e")),))

在这里插入图片描述

4、八分屏

final List<int> _nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
 Stack(children: Proportions.eightScreen(children: [..._nums.map((e) => Container(constraints: const BoxConstraints.expand(),decoration: BoxDecoration(border: Border.all(color: Colors.deepPurple.shade300)),child: Center(child: Text("video $e")),))

在这里插入图片描述

5、九分屏

final List<int> _nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
 Stack(children: Proportions.nightScreen(children: [..._nums.map((e) => Container(constraints: const BoxConstraints.expand(),decoration: BoxDecoration(border: Border.all(color: Colors.deepPurple.shade300)),child: Center(child: Text("video $e")),))

在这里插入图片描述

6、414分屏

final List<int> _nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
 Stack(children: Proportions.fourOneFourScreen(children: [..._nums.map((e) => Container(constraints: const BoxConstraints.expand(),decoration: BoxDecoration(border: Border.all(color: Colors.deepPurple.shade300)),child: Center(child: Text("video $e")),))

在这里插入图片描述
始终保持比例
在这里插入图片描述


总结

以上就是今天要讲的内容,本文用的是比较简单的方式实现了比例布局控件,其主要特点是可以灵活使用,尤其是方便视频分屏预览的实现。本质上也是对一类布局规则的总结得出的一个通用的控件,因为考虑到2x2、3x3还是可以写死的,但是到了4x4、5x5写死则需要16、25个参数,那就必须改用数组,也就意味着需要根据规则计算位置,那和本文一样了。所以本文的控件是有实际使用意义的。

相关文章:

Flutter 实现按位置大小比例布局的控件

文章目录 前言一、如何实现&#xff1f;1、数值转成分数2、RowFlexible布局横向3、ColumnFlexible布局纵向 二、完整代码三、使用示例1、基本用法2、四分屏3、六分屏4、八分屏5、九分屏6、414分屏 总结 前言 做视频监控项目时需要需要展示多分屏&#xff0c;比如2x2、3x3、414…...

ES6 - 对象新增的一些常用方法

文章目录 1&#xff0c;Object.is()2&#xff0c;Object.asign()3&#xff0c;Object.getOwnPropertyDescriptors()4&#xff0c;Object.setPrototypeOf()和getPrototypeOf()5&#xff0c;Object.keys()、values() 和 entries()6&#xff0c;Object.fromEntries()7&#xff0c;…...

半导体存储电路

存储电路 存储单元&#xff1a;只能存储一位数据 寄存器&#xff1a;存储一组数据 存储单元 静态存储单元&#xff1a;包含锁存器和触发器&#xff0c;只要不断电&#xff0c;静态存储单元的状态会一直保持下去。 动态存储单元&#xff1a;利用电容的电荷存储效应来存储数据。…...

web前端之CSS操作

文章目录 一、CSS操作1.1 html元素的style属性1.2 元素节点的style属性1.3 cssText属性 二、事件2.1 事件处理程序2.1.1 html事件2.1.2 DOM0事件&#xff08;适合单个事件&#xff09;2.1.3 DOM2事件&#xff08;适合多个事件&#xff09; 2.2 事件之鼠标事件2.3 事件之Event事…...

Python SQLAlchemy ( ORM )

From Python中强大的通用ORM框架&#xff1a;SQLAlchemy&#xff1a;https://zhuanlan.zhihu.com/p/444930067Python ORM之SQLAlchemy全面指南&#xff1a;https://zhuanlan.zhihu.com/p/387078089 SQLAlchemy 文档&#xff1a;https://www.sqlalchemy.org/ SQLAlchemy入门和…...

鉴源实验室丨汽车网络安全运营

作者 | 苏少博 上海控安可信软件创新研究院汽车网络安全组 来源 | 鉴源实验室 社群 | 添加微信号“TICPShanghai”加入“上海控安51fusa安全社区” 01 概 述 1.1 背景 随着车辆技术的不断进步和智能化水平的提升&#xff0c;车辆行业正经历着快速的变革和技术进步。智能化…...

分布式链路追踪之SkyWalking详解和实战

SkyWalking 文章目录 SkyWalking1.SkyWalking概述2.SkyWalking架构设计3.SkyWalking部署4.应用程序接入SkyWalking5.SkyWalking配置应用告警5.1.告警规则5.2.Webhook&#xff08;网络钩子&#xff09;5.3.邮件告警实践 6.项目自动化部署接入SkyWalking6.1 整体思路6.2 启动参数…...

【工程实践】使用EDA(Easy Data Augmentation)做数据增强

工程项目中&#xff0c;由于数据量不够&#xff0c;经常需要用到数据增强技术&#xff0c;尝试使用EDA进行数据增强。 1.EDA简介 EDA是一种简单但是非常有效的文本数据增强方法&#xff0c;是由美国Protago实验室发表于 EMNLP-IJCNLP 2019 会议。EDA来自论文《EDA: Easy Data…...

ClickHouse(十三):Clickhouse MergeTree系列表引擎 - ReplicingMergeTree

进入正文前&#xff0c;感谢宝子们订阅专题、点赞、评论、收藏&#xff01;关注IT贫道&#xff0c;获取高质量博客内容&#xff01; &#x1f3e1;个人主页&#xff1a;含各种IT体系技术&#xff0c;IT贫道_Apache Doris,大数据OLAP体系技术栈,Kerberos安全认证-CSDN博客 &…...

机器学习笔记之优化算法(十)梯度下降法铺垫:总体介绍

机器学习笔记之优化算法——梯度下降法铺垫&#xff1a;总体介绍 引言回顾&#xff1a;线搜索方法线搜索方法的方向 P k \mathcal P_k Pk​线搜索方法的步长 α k \alpha_k αk​ 梯度下降方法整体介绍 引言 从本节开始&#xff0c;将介绍梯度下降法 ( Gradient Descent,GD ) …...

Selenium 根据元素文本内容定位

使用xpath定位元素时&#xff0c;有时候担心元素位置会变&#xff0c;可以考虑使用文本内容来定位的方式。 例如图中的【股市】按钮&#xff0c;只有按钮文本没变&#xff0c;即使位置变化也可以定位到该元素。 xpath内容样例&#xff1a; # 文本内容完全匹配 //button[text(…...

第17章-Spring AOP经典应用场景

文章目录 一、日志处理二、事务控制三、参数校验四、自定义注解五、AOP 方法失效问题1. ApplicationContext2. AopContext3. 注入自身 六、附录1. 示例代码 AOP 提供了一种面向切面操作的扩展机制&#xff0c;通常这些操作是与业务无关的&#xff0c;在实际应用中&#xff0c;可…...

Leetcode周赛 | 2023-8-6

2023-8-6 题1体会我的代码 题2我的超时代码题目体会我的代码 题3体会我的代码 题1 体会 这道题完全就是唬人&#xff0c;只要想明白了&#xff0c;只要有两个连续的数的和&#xff0c;大于target&#xff0c;那么一定可以&#xff0c;两边一次切一个就好了。 我的代码 题2 我…...

ts中interface自定义结构约束和对类的约束

一、interface自定义结构约束对后端接口返回数据 // interface自定义结构 一般用于较复杂的结构数据类型限制 如后端返回的接口数据// 首字母大写;用分割号隔开 interface Iobj{a:number;b:string } let obj:Iobj {a:1,b:2 }// 复杂类型 模拟后端返回的接口数据 interface Il…...

Oracle单实例升级补丁

目录 1.当前DB环境2.下载补丁包和opatch的升级包3.检查OPatch的版本4.检查补丁是否冲突5.关闭数据库实例&#xff0c;关闭监听6.应用patch7.加载变化的SQL到数据库8.ORACLE升级补丁查询 oracle19.3升级补丁到19.18 1.当前DB环境 [oraclelocalhost ~]$ cat /etc/redhat-releas…...

力扣初级算法(二分查找)

力扣初级算法(二分法)&#xff1a; 每日一算法&#xff1a;二分法查找 学习内容&#xff1a; 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 2.二分查找流程&…...

探索未来:直播实时美颜SDK在增强现实(AR)直播中的前景

在AR直播中&#xff0c;观众可以与虚拟元素实时互动&#xff0c;为用户带来更加丰富、沉浸式的体验。那么&#xff0c;直播美颜SDK在AR中有哪些应用呢&#xff1f;下文小编将于大家一同探讨美颜SDK与AR有哪些关联。 一、AR直播与直播实时美颜SDK的结合 增强现实技术在直播中…...

SQL 单行子查询 、多行子查询、单行函数、聚合函数 IN 、ANY 、SOME 、ALL

单行子查询 子查询结果是 一个列一行记录 select a&#xff0c;b&#xff0c;c from table where a >(select avg(xx) from table ) 还支持这种写法,这种比较少见 select a&#xff0c;b&#xff0c;c from table where (a ,b)(select xx,xxx from table where col‘000’ )…...

【第一阶段】kotlin的range表达式

range:范围&#xff1a;从哪里到哪里的意思 in:表示在 !in&#xff1a;表示不在 … :表示range表达式 代码示例&#xff1a; fun main() {var num:Int20if(num in 0..9){println("差劲")}else if(num in 10..59){println("不及格")}else if(num in 60..89…...

网络防御(5)

一、结合以下问题对当天内容进行总结 1. 什么是恶意软件&#xff1f; 2. 恶意软件有哪些特征&#xff1f; 3. 恶意软件的可分为那几类&#xff1f; 4. 恶意软件的免杀技术有哪些&#xff1f; 5. 反病毒技术有哪些&#xff1f; 6. 反病毒网关的工作原理是什么&#xff1f; 7. 反…...

网络编程(Modbus进阶)

思维导图 Modbus RTU&#xff08;先学一点理论&#xff09; 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议&#xff0c;由 Modicon 公司&#xff08;现施耐德电气&#xff09;于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

剑指offer20_链表中环的入口节点

链表中环的入口节点 给定一个链表&#xff0c;若其中包含环&#xff0c;则输出环的入口节点。 若其中不包含环&#xff0c;则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

管理学院权限管理系统开发总结

文章目录 &#x1f393; 管理学院权限管理系统开发总结 - 现代化Web应用实践之路&#x1f4dd; 项目概述&#x1f3d7;️ 技术架构设计后端技术栈前端技术栈 &#x1f4a1; 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 &#x1f5c4;️ 数据库设…...

人机融合智能 | “人智交互”跨学科新领域

本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

无人机侦测与反制技术的进展与应用

国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机&#xff08;无人驾驶飞行器&#xff0c;UAV&#xff09;技术的快速发展&#xff0c;其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统&#xff0c;无人机的“黑飞”&…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作&#xff1a;验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化&#xff1a;测试aof和aof持久化机制&#xff0c;确保数据在开启后正确恢复。 事务&#xff1a;检查事务的原子性和回滚机制。 发布订阅&#xff1a;确保消息正确传递。 2、性…...

永磁同步电机无速度算法--基于卡尔曼滤波器的滑模观测器

一、原理介绍 传统滑模观测器采用如下结构&#xff1a; 传统SMO中LPF会带来相位延迟和幅值衰减&#xff0c;并且需要额外的相位补偿。 采用扩展卡尔曼滤波器代替常用低通滤波器(LPF)&#xff0c;可以去除高次谐波&#xff0c;并且不用相位补偿就可以获得一个误差较小的转子位…...