热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Flutter:UI布局和控件

转载自:https:mp.weixin.qq.comsV0cL9bSTM65HTIJ4CU4Cow基本控件Widget在Flutter里,UI控件

转载自:https://mp.weixin.qq.com/s/V0cL9bSTM65HTIJ4CU4Cow


基本控件


Widget

在 Flutter 里,UI 控件就是所谓的 Widget。通过组合不同的 Widget,来实现我们用户交互界面。

Widget 分为两种,一种是无状态的,叫 StatelessWidget,它只能用来展示信息,不能有动作(用户交互);另一种是有状态的,叫 StatefulWidget,这种 Widget 可以通过改变状态使得 UI 发生变化,它可以包含用户交互。

StatelessWidget 的使用非常简单,我们只需要继承 StatelessWidget,然后实现 build 方法就可以了:

class FooWidget extends StatelessWidget {@overrideWidget build(BuildContext context) {// ...}
}

关于 build 方法的实现,在后面我们学习具体的控件时读者就会了解的,这里暂时忽略掉。

StatefulWidget 用起来麻烦一些,他还需要一个 State:

class BarWidget extends StatefulWidget {@overrideState createState() {return _BarWidgetState();}
}class _BarWidgetState extends State {@overrideWidget build(BuildContext context) {// ...}
}

这里看起来可能有些绕,BarWidget 依赖了 _BarWidgetState,而 _BarWidgetState 又继承了 State。如果读者不太理解,其实也没有什么关系,这只是一个样板代码,照着写就行了。

从 BarWidget 的实现来看,好像跟前面使用 StatelessWidget 没有什么区别,都是在 build 方法里面返回一个 Widget,只是 stateful widget 把这个方法挪到了 State 里面。实际上,两者的区别非常大。stateless widget 整个生命周期里都不会改变,所以 build 方法只会执行一次。而 stateful widget 只要状态改变,就会调用 build 方法重新创建 UI。

为了触发 UI 的重建,我们可以调用 setState 方法。下面的代码读者留意一下即可,在后面我们学习了相关的控件后再回过头来看。

class BarWidget extends StatefulWidget {@overrideState createState() {return _BarWidgetState();}
}class _BarWidgetState extends State {var i = 0;@overrideWidget build(BuildContext context) {return Row(children: [Text('i = $i'),RaisedButton(onPressed: () {setState(() {++i;});},child: Text('click'),)],);}
}

下面我们开始学习一些具体的控件。


文本

为了展示文本,我们使用 Text:

class TestWidget extends StatelessWidget {@overrideWidget build(BuildContext context) {return Text("Put your text here");}
}

这就是最简单的文本了,它使用的是默认的样式。很多情况下,我们都需要对文本的样式进行修改,这个时候,可以使用 TextStyle:

class TestWidget extends StatelessWidget {@overrideWidget build(BuildContext context) {return Text("Put your text here",style: TextStyle(color: Colors.blue,fontSize: 16.0,fontWeight: FontWeight.bold),);}
}

 


图片

使用 Image,可以让我们向用户展示一张图片。图片的来源可以是网络、文件、资源和内存,它们对应的构造函数分别是:

Image.asset(name);
Image.file(file);
Image.memory(bytes);
Image.network(src);

比方说,为了展示一张来自网络的图片,我们可以这样:

class TestWidget extends StatelessWidget {@overrideWidget build(BuildContext context) {return Image.network("http://www.example.com/xxx.png",width: 200.0,height: 150.0,);}
}

按钮

Flutter 提供了两个基本的按钮控件:FlatButton 和 RaisedButton,它们的使用方法是类似的:

class TestWidget extends StatelessWidget {@overrideWidget build(BuildContext context) {var flatBtn = FlatButton(onPressed: () => print('FlatButton pressed'),child: Text('BUTTON'),);var raisedButton = RaisedButton(onPressed: () => print('RaisedButton pressed'),child: Text('BUTTON'),);return raisedButton;}
}

通过设置 onPressed 回调,我们可以在按钮被点击的时候得到回调。child 参数用于设置按钮的内容。虽然我们给 child 传递的是 Text,但这不是必需的,它可以接受任意的 Widget,比方说,Image。

注意,由于我们只是在按钮点击的时候打印一个字符串,这里使用 StatelessWidget 是没有问题的。但如果有其他 UI 动作(比如弹出一个 dialog,则必须使用 StatefulWidget)。

它们的区别只是样式不同而已的:

FlatButton:

RaiseButton:


文本输入框

Flutter 的文本输入框叫 TextField。为了获取用户输入的文本,我们需要给他设置一个 controller。通过这个 controller,就可以拿到文本框里的内容:

class MessageForm extends StatefulWidget {@overrideState createState() {return _MessageFormState();}
}class _MessageFormState extends State {var editController = TextEditingController();@overrideWidget build(BuildContext context) {// Row、Expand 都是用于布局的控件,这里可以先忽略它们return Row(children: [Expanded(child: TextField(controller: editController,),),RaisedButton(child: Text("click"),onPressed: () => print('text inputted: ${editController.text}'),)],);}@overridevoid dispose() {super.dispose();// 手动调用 controller 的 dispose 方法以释放资源editController.dispose();}
}

显示弹框

在前面的 TextField 例子中,我们只是把用户的输入通过 print 打印出来,这未免也太无趣了。在这一小节,我们要把它显示在 dialog 里。为了弹出一个 dialog,我们需要调用 showDialog 方法并传递一个 builder:

class _MessageFormState extends State {var editController = TextEditingController();@overrideWidget build(BuildContext context) {return Row(children: [Expanded(child: TextField(controller: editController,),),RaisedButton(child: Text("click"),onPressed: () {showDialog(// 第一个 context 是参数名,第二个 context 是 State 的成员变量context: context,builder: (_) {return AlertDialog(// dialog 的内容content: Text(editController.text),// actions 设置 dialog 的按钮actions: [FlatButton(child: Text('OK'),// 用户点击按钮后,关闭弹框onPressed: () => Navigator.pop(context),)],);});})],);}@overridevoid dispose() {super.dispose();editController.dispose();}
}

最简单的布局——Container、Padding 和 Center:

我们经常说,Flutter 里面所有的东西都是 Widget,所以,布局也是 Widget。

控件 Container 可以让我们设置一个控件的尺寸、背景、margin 等:

class TestWidget extends StatelessWidget {@overrideWidget build(BuildContext context) {return Container(child: Text('text'),padding: EdgeInsets.all(8.0),margin: EdgeInsets.all(4.0),width: 80.0,decoration: BoxDecoration(// 背景色color: Colors.grey,// 圆角borderRadius: BorderRadius.circular(5.0),),);}
}

如果我们只需要 padding,可以使用控件 Padding:

class TestWidget extends StatelessWidget {@overrideWidget build(BuildContext context) {return Padding(padding: EdgeInsets.all(8.0),child: Text('text'),);}
}

Center 就跟它的名字一样,把一个控件放在中间:

class TestWidget extends StatelessWidget {@overrideWidget build(BuildContext context) {return Container(padding: EdgeInsets.all(8.0),margin: EdgeInsets.all(4.0),width: 200.0,height: 200.0,decoration: BoxDecoration(// 背景色color: Colors.grey,// 圆角borderRadius: BorderRadius.circular(5.0),),// 把文本放在 Container 的中间child: Center(child: Text('text'),),);}
}

水平、竖直布局和 Expand

我们经常说,Flutter 里面所有的东西都是 Widget,所以,布局也是 Widget。水平布局我们可以使用 Row,竖直布局使用 Column。

class TestWidget extends StatelessWidget {@overrideWidget build(BuildContext context) {return Row(// 只有一个子元素的 widget,一般使用 child 参数来设置;Row 可以包含多个子控件,// 对应的则是 children。children: [Text('text1'),Text('text2'),Text('text3'),Text('text4'),],);}
}

Column 的使用是一样的:

class TestWidget extends StatelessWidget {@overrideWidget build(BuildContext context) {return Column(children: [Text('text1'),Text('text2'),Text('text3'),Text('text4'),],);}
}

关于 Expand 控件,我们来看看 TextField 的那个例子:

class MessageForm extends StatefulWidget {@overrideState createState() {return _MessageFormState();}
}class _MessageFormState extends State {var editController = TextEditingController();@overrideWidget build(BuildContext context) {return Row(children: [// 占满一行里除 RaisedButton 外的所有空间Expanded(child: TextField(controller: editController,),),RaisedButton(child: Text("click"),onPressed: () => print('text inputted: ${editController.text}'),)],);}@overridevoid dispose() {super.dispose();editController.dispose();}
}

这里通过使用 Expand,TextField 才能够占满一行里除按钮外的所有空间。此外,当一行/列里有多个 Expand 时,我们还可以通过设置它的 flex 参数,在多个 Expand 之间按比例划分可用空间。

class TestWidget extends StatelessWidget {@overrideWidget build(BuildContext context) {return Row(children: [Expanded(// 占一行的 2/3flex: 2,child: RaisedButton(child: Text('btn1'),),),Expanded(// 占一行的 1/3flex: 1,child: RaisedButton(child: Text('btn2'),),),],);}
}

Stack 布局

有些时候,我们可能会希望一个控件叠在另一个控件的上面。于是,Stack 应运而生:

class TestWidget extends StatelessWidget {@overrideWidget build(BuildContext context) {return Stack(children: [Text('foobar'),Text('barfoo'),],);}
}

默认情况下,子控件都按 Stack 的左上角对齐,于是,上面的两个文本完全一上一下堆叠在一起。我们还可以通过设置 alignment 参数来改变这个对齐的位置:

class TestWidget extends StatelessWidget {@overrideWidget build(BuildContext context) {return Stack(// Aligment 的取值范围为 [-1, 1],Stack 中心为 (0, 0),// 这里设置为 (-0.5, -0.5) 后,可以让文本对齐到 Container 的 1/4 处alignment: const Alignment(-0.5, -0.5),children: [Container(width: 200.0,height: 200.0,color: Colors.blue,),Text('foobar'),],);}
}

通过组合 Row/Column 和 Stack,已经能够完成绝大部分的布局了,所以 Flutter 里没有相对布局之类的东西。更多的 Flutter 控件,读者可以参考 https://flutter.io/widgets/。

 

 

 

 

 

 

 

 


推荐阅读
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 有没有一种方法可以在不继承UIAlertController的子类或不涉及UIAlertActions的情况下 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • 纠正网上的错误:自定义一个类叫java.lang.System/String的方法
    本文纠正了网上关于自定义一个类叫java.lang.System/String的错误答案,并详细解释了为什么这种方法是错误的。作者指出,虽然双亲委托机制确实可以阻止自定义的System类被加载,但通过自定义一个特殊的类加载器,可以绕过双亲委托机制,达到自定义System类的目的。作者呼吁读者对网上的内容持怀疑态度,并带着问题来阅读文章。 ... [详细]
  • 在Oracle11g以前版本中的的DataGuard物理备用数据库,可以以只读的方式打开数据库,但此时MediaRecovery利用日志进行数据同步的过 ... [详细]
  • ***byte(字节)根据长度转成kb(千字节)和mb(兆字节)**parambytes*return*publicstaticStringbytes2kb(longbytes){ ... [详细]
  • 超级简单加解密工具的方案和功能
    本文介绍了一个超级简单的加解密工具的方案和功能。该工具可以读取文件头,并根据特定长度进行加密,加密后将加密部分写入源文件。同时,该工具也支持解密操作。加密和解密过程是可逆的。本文还提到了一些相关的功能和使用方法,并给出了Python代码示例。 ... [详细]
  • 带添加按钮的GridView,item的删除事件
    先上图片效果;gridView无数据时显示添加按钮,有数据时,第一格显示添加按钮,后面显示数据:布局文件:addr_manage.xml<?xmlve ... [详细]
  • Android源码中的Builder模式及其作用
    本文主要解释了什么是Builder模式以及其作用,并结合Android源码来分析Builder模式的实现。Builder模式是将产品的设计、表示和构建进行分离,通过引入建造者角色,简化了构建复杂产品的流程,并且使得产品的构建可以灵活适应变化。使用Builder模式可以解决开发者需要关注产品表示和构建步骤的问题,并且当构建流程发生变化时,无需修改代码即可适配新的构建流程。 ... [详细]
  • 本文详细介绍了在Linux虚拟化部署中进行VLAN配置的方法。首先要确认Linux系统内核是否已经支持VLAN功能,然后配置物理网卡、子网卡和虚拟VLAN网卡的关系。接着介绍了在Linux配置VLAN Trunk的步骤,包括将物理网卡添加到VLAN、检查添加的VLAN虚拟网卡信息以及重启网络服务等。最后,通过验证连通性来确认配置是否成功。 ... [详细]
  • 第一步:PyQt4Designer设计程序界面该部分设计类同VisvalStudio内的设计,改下各部件的objectName!设计 ... [详细]
  • 注:根据Qt小神童的视频教程改编概论:利用最新的Qt5.1.1在windows下开发的一个小的时钟程序,有指针与表盘。1.Qtforwindows开发环境最新的Qt已经集 ... [详细]
  • HTC EVO 4G+手机存储(SD卡)中各个文件夹功能说明(转载)
      HTCRider/X515E/EVO4G+手机存储(SD卡)中各个文件夹功能说明  HTCRider/X515E/EVO4G+  1、.android_s ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
author-avatar
红山村樵夫_799
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有