百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

一起来学习跨平台 UI 框架 Flutter 的常用布局组件

wptr33 2024-12-13 16:35 20 浏览

Flutter 常用布局组件

Flutter 控件本身通常由许多小型、单用途的控件组成,结合起来产生强大的效果,例如:Container 是一种常用的控件,由负责布局、绘画、定位大小调整的几个控件组成

具体来说:Container 是由 LimitedBox、ConstrainedBox、 Align、Padding、DecoratedBoxTransform 控件组成,而不是将 Container 子类化来产生自定义效果,您可以用这种新颖的方式组合这些以及其他简单的控件

类的层次结构是扁平的,以最大化可能的组合数量

在写应用程序时,经常会使用 StatelessWidget 和 StatefulWidget 编写新控件,两者的差别在于你是否要管理控件的状态;一个控件的主要任务是实现 build 函数,定义控件中其他较低层次的控件;build 函数将依次构建这些控件,直到底层渲染对象

基本组件

Container

容器,一个常用的控件,由基本的绘制、位置大小控件组成;负责创建矩形的可视元素,可以用 BoxDecoration 来设计样式,比如背景、边框和阴影,Container 也有边距、填充和大小限制,另外,还可以在三维空间利用矩阵进行变换

没有子控件的容器尽可能大,除非传入的大小约束是无限的,在这种情况下,它们尽可能小。有子控件的容器将自己的尺寸给他们的孩子。我们可以通过 width、height 和 constraints 属性控制 size

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" cid="n12" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> 1 new Container(
 2   constraints: new BoxConstraints.expand(
 3     height: Theme.of(context).textTheme.display1.fontSize * 1.1 + 200.0,
 4   ),
 5   padding: const EdgeInsets.all(8.0),
 6   color: Colors.teal.shade700,
 7   alignment: Alignment.center,
 8   child: new Text('Hello World', style: Theme.of(context).textTheme.display1.copyWith(color: Colors.white)),
 9   foregroundDecoration: new BoxDecoration(
10     image: new DecorationImage(
11       image: new NetworkImage('https://www.example.com/images/frame.png'),
12       centerSlice: new Rect.fromLTRB(270.0, 180.0, 1360.0, 730.0),
13     ),
14   ),
15   transform: new Matrix4.rotationZ(0.1),
16 )</pre>

Row

flex 水平布局控件,能够将子控件水平排列,是基于 Web 的 flexbox 的布局模式设计的

Row 子控件有灵活与不灵活的两种,Row 首先列出不灵活的子控件,减去它们的总宽度,计算还有多少可用的空间。然后Row 按照 Flexible.flex 属性确定的比例在可用空间中列出灵活的子控件。要控制灵活子控件,需要使用 Expanded 控件

注意该控件不支持滑动,如果子控件超过剩余空间,会报错,如果想支持水平滑动,考虑使用 ListView

如果只有一个子控件,可以使用 Align or Center控件定义该子控件位置

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" cid="n22" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> 1 new Row(
 2   children: <Widget>[
 3     new Expanded(
 4       child: new Text('Deliver features faster', textAlign: TextAlign.center),
 5     ),
 6     new Expanded(
 7       child: new Text('Craft beautiful UIs', textAlign: TextAlign.center),
 8     ),
 9     new Expanded(
10       child: new FittedBox(
11         fit: BoxFit.contain, // otherwise the logo will be tiny
12         child: const FlutterLogo(),
13       ),
14     ),
15   ],
16 )</pre>

Column

flex 垂直布局控件,能够将子控件垂直排列

用法与 Row 控件一样

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" cid="n29" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> 1 new Column(
 2   crossAxisAlignment: CrossAxisAlignment.start,
 3   mainAxisSize: MainAxisSize.min,
 4   children: <Widget>[
 5     new Text('We move under cover and we move as one'),
 6     new Text('Through the night, we have one shot to live another day'),
 7     new Text('We cannot let a stray gunshot give us away'),
 8     new Text('We will fight up close, seize the moment and stay in it'),
 9     new Text('It’s either that or meet the business end of a bayonet'),
10     new Text('The code word is ‘Rochambeau,’ dig me?'),
11     new Text('Rochambeau!', style: DefaultTextStyle.of(context).style.apply(fontSizeFactor: 2.0)),
12   ],
13 )</pre>

Image

显示图像的控件,Image 控件有多种构造函数:

  • new Image,用于从 ImageProvider 获取图像
  • new Image.asset,用于使用 key 从 AssetBundle 获取图像
  • new Image.network,用于从 URL 地址获取图像
  • new Image.file,用于从 File 获取图像

为了自动执行像素密度感知资源分辨率,使用 AssetImage 指定图像,需要确保在控件树中的图片控件上方存在 MaterialApp、WidgetsApp 和 MediaQuery 控件

不同的手机有不同的像素比率,这时就需要根据手机的像素比率来加载不同图片,做法很简单,只需要在图片同级目录下创建 2.0x/… 和 3.0x/… 的目录就可以了

我们在 pubspec.yaml 这个文件里指定本地图片路径

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" cid="n46" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"># To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg</pre>

Text

用来显示文本的控件

下面的实例有7个不同样式的文本控件:

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" cid="n52" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> 1 import 'package:flutter/material.dart';
 2 class TextDemo extends StatelessWidget {
 3   @override
 4   Widget build(BuildContext context) {
 5     return new Scaffold(
 6       appBar: new AppBar(
 7         title: new Text('文本控件'),
 8       ),
 9       body: new Column(
10         children: <Widget>[
11           new Text(
12             '红色+黑色删除线+25号',
13             style: new TextStyle(
14               color: const Color(0xffff0000),
15               decoration: TextDecoration.lineThrough,
16               decorationColor: const Color(0xff000000),
17               fontSize: 25.0,
18             ),
19           ),
20           new Text(
21             '橙色+下划线+24号',
22             style: new TextStyle(
23               color: const Color(0xffff9900),
24               decoration: TextDecoration.underline,
25               fontSize: 24.0,
26             ),
27           ),
28           new Text(
29             '虚线上划线+23号+倾斜',
30             style: new TextStyle(
31               decoration: TextDecoration.overline,
32               decorationStyle: TextDecorationStyle.dashed,
33               fontSize: 23.0,
34               fontStyle: FontStyle.italic,
35             ),
36           ),
37           new Text(
38             'serif字体+24号',
39             style: new TextStyle(
40               fontFamily: 'serif',
41               fontSize: 26.0,
42             ),
43           ),
44           new Text(
45             'monospace字体+24号+加粗',
46             style: new TextStyle(
47               fontFamily: 'monospace',
48               fontSize: 24.0,
49               fontWeight: FontWeight.bold,
50             ),
51           ),
52           new Text(
53             '天蓝色+25号+2行跨度',
54             style: new TextStyle(
55               color: const Color(0xff4a86e8),
56               fontSize: 25.0,
57               height: 2.0,
58             ),
59           ),
60           new Text(
61             '24号+2个字母间隔',
62             style: new TextStyle(
63               fontSize: 24.0,
64               letterSpacing: 2.0,
65             ),
66           ),
67         ]
68       ),
69     );
70   }
71 }
72 void main() {
73   runApp(
74     new MaterialApp(
75       title: 'Flutter教程',
76       home: new TextDemo(),
77     ),
78   );
79 }</pre>

Icon

  • 图标控件,按照IconData中所描述的规则绘制,如Material中预定义的IconDatas
  • 该控件不可交互,要实现可交互的图标,可以考虑使用Material中的 IconButton
  • 该控件必须在 Directionality控件里使用,通常这是由 WidgetsApp 或 MaterialApp 自动引入的

RaisedButton

Material Design 风格的浮动按钮,以方形纸片样式悬停在界面上,点击后会产生墨水扩散效果。

避免在 dialog 和 card 控件里使用,一般弹出式的控件建议使用扁平化按钮,减少布局层次叠加

使用时,要实现 onPressed 回调方法,否则按钮处于禁用状态,默认显示 disabledColor 样式的扁平化按钮,并且此时更改按钮的颜色不会生效

  • 注意该控件的父控件必须是 Material 控件
  • 如果你只需要点击后产生墨水扩散效果,但不想使用按钮,请考虑直接使用 InkWell 控件
  • 如有必要,该按钮将拉伸以适应子控件大小

Scaffold

Scaffold 实现了基本的Material Design布局结构;也就是说, MaterialApp 的 child 是 Scaffold Widget

在 Material 设计中定义的单个界面上的各种布局元素,在 Scaffold 中都有支持,比如 左边栏(Drawers)、snack bars、以及 bottom sheets

Scaffold 有下面几个主要属性:

  • appBar:显示在界面顶部的一个 AppBar,也就是 Android 中的 ActionBar 、Toolbar
  • body:当前界面所显示的主要内容 Widget
  • floatingActionButton:Material设计中所定义的 FAB,界面的主要功能按钮
  • persistentFooterButtons:固定在下方显示的按钮,比如对话框下方的确定、取消按钮
  • drawer:侧边栏控件
  • backgroundColor: 内容的背景颜色,默认使用的是 ThemeData.scaffoldBackgroundColor 的值
  • bottomNavigationBar: 显示在页面底部的导航栏
  • resizeToAvoidBottomPadding:类似于 Android 中的 android:windowSoftInputMode=”adjustResize”,控制界面内容 body 是否重新布局来避免底部被覆盖了,比如当键盘显示的时候,重新布局避免被键盘盖住内容。默认值为 true

显示 snackbar 或者 bottom sheet 的时候,需要使用当前的 BuildContext 参数调用 Scaffold.of 函数来获取 ScaffoldState 对象,然后使用 ScaffoldState.showSnackBar 和 ScaffoldState.showBottomSheet 函数来显示

要特别注意 Scaffold.of 的参数 BuildContext, 如果包含该 BuildContext 的 Widget 是 Scaffold 的父 Widget,则 Scaffold.of 是无法查找到对应的 ScaffoldState 对象的,Scaffold.of 返回的是父对象中最近的 Scaffold 中的 ScaffoldState 对象

比如,如果在 Scaffold 的 build 函数中,使用 build 的 BuildContext 参数是可以的:

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" cid="n95" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> 1 @override
 2 Widget build(BuildContext context) {
 3   return new RaisedButton(
 4     child: new Text('SHOW A SNACKBAR'),
 5     onPressed: () {
 6       Scaffold.of(context).showSnackBar(new SnackBar(
 7         content: new Text('Hello!'),
 8       ));
 9     },
10   );
11 }</pre>

如果 build 函数返回一个 Scaffold 对象,则由于 Scaffold 对象是这个 Widget 的子对象,所以使用这个 build 的 BuildContext 参数是不能查找到 ScaffoldState 对象的

这个时候,通过在 Scaffold 中使用一个 Builder 来提供一个新的 BuildConext :

<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" cid="n101" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> 1 @override
 2 Widget build(BuildContext context) {
 3   return new Scaffold(
 4     appBar: new AppBar(
 5       title: new Text('Demo')
 6     ),
 7     body: new Builder(
 8       // Create an inner BuildContext so that the onPressed methods
 9       // can refer to the Scaffold with Scaffold.of().
10       builder: (BuildContext context) {
11         return new Center(
12           child: new RaisedButton(
13             child: new Text('SHOW A SNACKBAR'),
14             onPressed: () {
15               Scaffold.of(context).showSnackBar(new SnackBar(
16                 content: new Text('Hello!'),
17               ));
18             },
19           ),
20         );
21       },
22     ),
23   );
24 }</pre>

另外还可以把 build 函数中的 Widget 分别创建,分别引入新的 BuildContext 来获取 Scaffold

结语

至此,我们今天关于 Flutter 框架 中的常用布局组件就介绍到这里了;希望以上的内容能够对大家有所帮助,关于 Flutter 的相关技术问题,我们还要去好好的学习剖析;因此我把工作中遇到的 Flutter 组件开源库相关问题,以及对网上大部分的资料的收集和整理,最终整合出了一份 《Flutter 高级开发学习手册》

有需要这份学习手册的朋友,可以私信发送“手册”即可 免费获取;希望大家通过阅读这份学习手册,能够查漏补缺;早日精通 Flutter

Flutter 编程原理

Flutter3.3 项目实战

文章篇幅有限;手册内容就不完全展示了,有需要的小伙伴可以私信发送“手册”,即可 免费获取


资料整理不易,如果觉得内容对你有所帮助的话,可以点赞转发分享一下哦~

最后祝各位开发者早日精通 Flutter ,攀登上更高的高峰

相关推荐

每天一个AI姬,AMD核显用户有福了,AI绘画打破 NVIDIA 显卡垄断

使用StableDiffusion进行AI绘画,并不一定只能使用NVIDIA英伟达显卡,甚至,也不一定只能使用独立显卡。今天我们使用AMD6800H核显,并安装了StableDif...

NETworkManager:功能强大的网络管理与问题排除工具

关于NETworkManagerNETworkManager是一款功能强大的网络管理与问题排除工具,该工具完全开源,可以帮助广大研究人员轻松管理目标网络系统并排除网络疑难问题。该工具使用远程桌面、Po...

AMD也能深度学习+免费AI绘画:StableDiffusion+ROCm部署教程!

某国政客扇扇嘴皮子,CN玩硬件和深度学习的圈子里就掀起了一场风暴,这就是著名的嘴皮子效应(误)。没了高性能计算的A100H100倒也能理解,但是美利坚这波把RTX4090禁售了就让人无语了,所以不少做...

windows 下编译 python_rtmpstream

最近在研究数字人,看了大咖的项目(https://github.com/lipku/metahuman-stream),尝试编译此项目的依赖项目python_rtmpstream(https://gi...

如何使用 Python 操作 Git 代码?GitPython 入门介绍

花下猫语:今天,我在查阅如何用Python操作Gitlab的时候,看到这篇文章,觉得还不错,特分享给大家。文中还提到了其它几种操作Git的方法,后续有机会的话,再陆续分享之~~作者:匿蟒...

网上看了不少,终于把ZlmediaKit流媒体框架搭建起来啦

你都站在2023年代了,视频通话、视频直播、视频会议、视频监控就是风口浪尖上的猪师兄,只要你学那么一丁点,拿个高薪的工作不过分吧!我也是半瓶子晃荡的,所以路人呀,共学习,同进步!本篇开始,只讲在Lin...

MacDown:一款 macOS 的强大 Markdown 编辑器

大家好,很高兴又见面了,我是"...

ZLMediaKit安装配置和推拉流

一、ZLMediaKit库简介ZLMediaKit是一个基于...

大神赞过的:学习 WebAssembly 汇编语言程序设计

文/阿里淘系F(x)Team-旭伦随着前端页面变得越来越复杂,javascript的性能问题一再被诟病。而Javascript设计时就不是为了性能优化设计的,这使得浏览器上可以运行的本地语言一...

【Docker】部署WVP视频监控平台

回来Docker系列,今天将会跟大家分享一则关于开源WVP视频监控平台的搭建。先说结论吧,一开始按照网上说的一步一步搭建没有搭建成功,不知道是版本太旧还是我这边机器有问题,尝试了好几个不同方式的搭建都...

MongoDB+GridFS存储文件方案

GridFS是MongoDB的一个内置功能,它提供一组文件操作的API以利用MongoDB存储文件,GridFS的基本原理是将文件保存在两个Collection中,一个保存文件索引,一个保存文...

【开源】强大、创新且直观的 EDA套件

今天分享的LibrePCB是...

Ollama如何制作自己的大模型?

背景Llama3发布了,这次用了...

Ollama使用指南【超全版】

一、Ollama快速入门Ollama是一个用于在本地运行大型语言模型的工具,下面将介绍如何在不同操作系统上安装和使用Ollama。官网:https://ollama.comGithub:http...

基于区块链的价值共享互联网即时通讯应用平台源码免费分享

——————关注转发之后私信回复【源码】即可免费获取到本项目所有源码基于区块链的价值共享互联网即时通讯应用平台,是一个去中心化的任何人都可以使用的通讯网络,是一款基于区块链的价值共享互联网即时通讯AP...