Flutter 学习 Basics Widget

网友投稿 724 2022-11-17

Flutter 学习 Basics Widget

Flutter 学习 Basics Widget

文章目录

​​1. 概述​​​​2. 常用组件​​

​​2.1 Text​​

​​2.1.1 TextStyle​​​​2.1.2 TextSpan​​​​2.1.3 DefaultTextStyle​​​​2.1.4 使用字体​​

​​2.2 Button​​

​​2.2.1 ElevatedButton​​​​2.2.2 TextButton​​​​2.2.3 OutlinedButton​​​​2.2.4 IconButton​​​​2.2.5 带图标的按钮​​

​​2.3 图片及Icon​​

​​2.3.1 图片​​

​​2.3.1.1 ImageProvider​​​​2.3.1.2 Image Widget​​​​2.3.1.3 Image 参数​​

​​2.3.2 Icon​​

​​2.4 单选开关和复选框​​

​​2.4.1 属性​​

​​2.5 输入框以及表单​​

​​2.5.1 输入框 TextField​​

​​2.5.1.1 属性​​​​2.5.1.2 通过 controller 获取输入内容​​​​2.5.1.3 通过 controller 监听文本内容变化​​​​2.5.1.4 控制焦点​​​​2.5.1.5 监听焦点状态改变事件​​

​​2.5.2 表单​​

​​2.5.2.1 FormField​​​​2.5.2.2 FormState​​​​2.5.2.3 示例​​

​​参考文章​​

1. 概述

上一篇说到,Basics Widget 并不是 Flutter 的一个专门的Widget类别,而是 Flutter 官方挑选一些开发常用的 Widget 构成的,希望我们掌握到一些最基本的开发能力。

包括:

文本 Text按钮 Button图片 Image单选框、复选框输入框、表单指示器Container…

2. 常用组件

2.1 Text

​​Text​​ 用于显示简单样式文本,然后可以填充一些文本显示样式的属性,如下例子:

Text("Hello World", textAlign: TextAlign.left, maxLines: 1, overflow: TextOverflow.ellipsis, textScaleFactor: 1.5);

​​textAlign​​ 文本对齐方式​​maxLines​​​ 、​​overflow​​​ maxLines 指定文本显示的最大行数。 当文本内容超过最大行数时, ​​overflow​​ 指定了阶段方式, 例如 ​​ellipsis​​ 就是将多余的文本用 “…” 表示​​textScaleFactor​​​ 代表文本相对于当前字体大小的缩放因子,想你对于去设置文本的样式 style 属性的 fontSize, 它是调整字体大小的一个快捷方式, 该属性的默认值可以通过 ​​MediaQueryData.textScaleFactor​​ 获得, 如果没有 ​​MediaQuery​​,那么会默认值为 1.0

2.1.1 TextStyle

​​TextStyle​​ 用于指定文本样式,例如颜色、字体、粗细、背景等,如下:

@override Widget build(BuildContext context) { return MaterialApp( title: "Flutter", home: Scaffold( appBar: AppBar( title: const Text("Basics Widget"), ), body: Text( "Hello World", style: TextStyle( color: Colors.blue, fontSize: 19.0, height: 2, fontFamily: "Courier", background: Paint()..color = Colors.yellow, decoration: TextDecoration.underline, decorationStyle: TextDecorationStyle.dashed), ))); }

效果如图:

一些属性:

​​height​​​ 行高,它不是一个绝定的值,因为具体的行高为 ​​height*fontSize​​ ,同理行宽也是​​fontFamily​​ 由于不同平台默认支持的字体集不同,所以在手动指定字体时一定要先在不同平台测试一下​​fontSize​​​ 改属性和 Text 的 ​​textScaleFactor​​ 都用于控制字体大小,但是有两个区别, ①:​​fontSize​​ 可以精确指定字体大小, 而 textScaleFactor 只能缩放比例 ②: ​​textScaleFactor​​ 主要是用于系统字体大小设置改变时,对Flutter 应用字体进行全局调整,而 fontSzie通常用于单个文本,字体大小不会跟随系统字体大小变化

2.1.2 TextSpan

如果我们需要对Text内容不同部分按照不同的样式显示,就可以使用 TextSpan,代表文本的一个“片段”,看看 TextSpan的定义:

const TextSpan({ this.text, this.children, TextStyle? style, this.recognizer, MouseCursor? mouseCursor, this.onEnter, this.onExit, this.semanticsLabel, this.locale, this.spellOut, })

其中 ​​style​​​ 和 ​​text​​​ 代表样式和文本内容, children是 ​​List?​​ 类型,也就说 TextSpan 可以包含其他 Span

​​reconizer​​​ 用于表示该文本片段上用于手势进行识别处理,下面我们看一个效果图,然后用 ​​TextSpan​​ 来实现:

body: const Text.rich(TextSpan(children: [ TextSpan(text: "Home: "), TextSpan( text: " style: TextStyle(color: Colors.blue), recognizer: _recognizer ), ]))));

这里的代码,用 TextSpan实现了一个基础文本和一个链接片段

2.1.3 DefaultTextStyle

在 Widget 树中, 文本的样式默认是可以被继承的,因此如果 Widget树的某一个节点处设置一个默认的文本样式,那么该节点的子树所有的文本都会默认使用这个样式,而 ​​DefaultTextStyle​​ 正是用于设置默认文本样式的,看下面例子:

DefaultTextStyle( //1.设置文本默认样式 style: TextStyle( color:Colors.red, fontSize: 20.0, ), textAlign: TextAlign.start, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("hello world"), Text("I am Jack"), Text("I am Jack", style: TextStyle( inherit: false, //2.不继承默认样式 color: Colors.grey ), ), ], ),);

这里的代码首先设置了一个默认的样式,字体大小为20,、颜色为红色,然后将 ​​DefaultTextStyle​​​ 设置给了子树,这样一来 Column 所有子孙 Text 默认都会继承该样式, 除非 Text 设置 ​​inherit: false​​,如下所示:

2.1.4 使用字体

在 Flutter 中可以使用自定义的字体,或者其他第三方字体, 这里就不介绍配置了,具体可以看官方文档:​​字体​​

2.2 Button

Material 组件库提供了多种多样的按钮,他们都是直接或间接对 ​​RawMaterialButton​​ 的包装定制,所以大部分属性都一样。另外 Marterial 库中的按钮都有以下共同点:

2.2.1 ElevatedButton

即 带阴影的按钮, 默认带有阴影和灰色背景,按下后阴影会变大,如下所示:

代码如下:

: ElevatedButton( child: const Text("i am ElevatedButton"), onPressed: () {}, ), ),

2.2.2 TextButton

文本按钮,按下后会有背景色,如下图所示:

2.2.3 OutlinedButton

默认有一个边框,不带阴影且背景透明,按下后,边框颜色会变亮、同时出现背景和阴影,如下图所示:

2.2.4 IconButton

代码设置为:

IconButton( icon: Icon(Icons.eleven_mp), onPressed: () {},),

2.2.5 带图标的按钮

上面学到的 ​​ElevatedButton​​​、 ​​TextButton​​​、 ​​OutlinedButton​​​ 都有一个 ​​icon()​​ 的构造函数,这样就可以代入一个图片进去,例如设置:

ElevatedButton.icon( icon: const Icon(Icons.send), label: const Text("发送"), onPressed: () {}, ),

效果为(这里有编码问题,可以无视):

2.3 图片及Icon

2.3.1 图片

可以通过 ​​Image​​​ 组件来加载并显示布局, ​​Image​​ 的数据源可以是

asset文件内存网络

2.3.1.1 ImageProvider

​​ImageProvider​​​ 是抽象类,主要定义了图片的获取接口 ​​load()​​​,从不同的数据源获取图片需要实现不同的 ​​ImageProvider​​​,如 ​​AssetImage​​​ 是实现了从 Asset 中加载图片, ​​NetworkImage​​ 则实现了从网络中加载图片。

2.3.1.2 Image Widget

​​Image​​​ 组件在构建时有一个必选的 ​​image​​​ 参数,它对应一个 ​​ImageProvier​​,下面分别演示一下如何从 asset 和 网络中加载图片。

1.从 asset 中加载图片 在工程根目录下创建一个 images 目录,并将图片拷贝到该目录。 接下来在 ​​​pubspec.yaml​​ 文件的 flutter部分 中,写入(注意缩进):

flutter: .. assets: - assets/images/bobo.jpg

最后在代码中使用:

Image( image: AssetImage("images/bobo.jpg"), width: 100.0,)

就能展示图片。

(不过我这里遇到一个问题,使用手机运行Flutter应用能正常展示图片,但是使用 Chrome 模拟器会报错,不知道是什么原因造成的

2.从网络URL中加载图片 直接使用代码:

Image( image: NetworkImage(" width: 100.0,)

可以正常展示图片。

(不过这里出现了很上面一样的问题,但是使用官方使用的url又能正常展示图片

2.3.1.3 Image 参数

我们可以来看下 ​​Image​​ 的参数,通过这些参数可以控制图片外观、大小、混合效果等。

const Image({ Key? key, required this.image, this.frameBuilder, this.loadingBuilder, this.errorBuilder, this.semanticLabel, this.excludeFromSemantics = false, this.width, this.height, this.color, this.opacity, this.colorBlendMode, this.fit, this.alignment = Alignment.center, this.repeat = ImageRepeat.noRepeat, this.centerSlice, this.matchTextDirection = false, this.gaplessPlayback = false, this.isAntiAlias = false, this.filterQuality = FilterQuality.low, })

2.3.2 Icon

Android中有 svg 矢量图, 而 Flutter 中的也有,就是 ​​Icon​​,它有下面这些优点:

体积小因为是矢量图,所以拉伸不会影响清晰程度可以通过 TextSpan 和 文本混用可以引用到文本样式

Flutter 默认实现了一套Icon,在 ​​pubspec.yaml​​ 的配置文件可以看到:

flutter: uses-material-design: true

来看下官方的示例代码:

String icons = "";// accessible: 0xe03eicons += "\uE03e";// error: 0xe237icons += " \uE237";// fingerprint: 0xe287icons += " \uE287";Text( icons, style: TextStyle( fontFamily: "MaterialIcons", fontSize: 24.0, color: Colors.green, ),);

效果为:

为了不让开发者码点,Flutter 封装了 ​​IconData​​​ 和 ​​Icon​​来专门显示字体图片,上面的例子也可以用下面方式实现:

Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.accessible,color: Colors.green), Icon(Icons.error,color: Colors.green), Icon(Icons.fingerprint,color: Colors.green), ],)

我们也可以使用自定义的字体图标,这里就不赘述了,可以看看官方示例:​​Icon自定义字体图标​​

2.4 单选开关和复选框

class SwitchAndCheckBoxTestRoute extends StatefulWidget { @override _SwitchAndCheckBoxTestRouteState createState() => _SwitchAndCheckBoxTestRouteState();}class _SwitchAndCheckBoxTestRouteState extends State { bool _switchSelected=true; //维护单选开关状态 bool _checkboxSelected=true;//维护复选框状态 @override Widget build(BuildContext context) { return Column( children: [ Switch( value: _switchSelected,//当前状态 onChanged:(value){ //重新构建页面 setState(() { _switchSelected=value; }); }, ), Checkbox( value: _checkboxSelected, activeColor: Colors.red, //选中时的颜色 onChanged:(value){ setState(() { _checkboxSelected=value!; }); } , ) ], ); }}

为什么要这样子设计,我的理解是:

将开关、复选框的状态抛给父组件,可以更加灵活,比如在勾选时候做一些网络请求,即异步的操作一般来说,这些item是否选中,是和用户数据关联的,用户数据也不可能是他们的私有状态,所以放在一起管理更好

2.4.1 属性

它们的属性比较简单,常用的有:

​​activeColor​​:设置激活状态的颜色​​tristate​​​: 是否为三态,仅 Checbox有,一般情况下只有 “true” 和 “false”,表示选中和非选中,如果设置了​​tristate​​ 后,还会增加一个 “null” 状态

此外, Checkbox 不可设置宽高,其大小是自定义的,而 Switch 也仅能设置宽度而已。

2.5 输入框以及表单

Flutter Material组件提供了 ​​输入款TextField​​​ 和 ​​表单Form​​

2.5.1 输入框 TextField

2.5.1.1 属性

来看下 ​​TextField​​ 提供的属性:

const TextField({ ... this.controller, this.focusNode, this.decoration = const InputDecoration(), TextInputType? keyboardType, this.textInputAction, this.textCapitalization = TextCapitalization.none, this.style, this.strutStyle, this.textAlign = TextAlign.start, this.textAlignVertical, this.textDirection, this.readOnly = false, ToolbarOptions? toolbarOptions, this.showCursor, this.autofocus = false, this.obscuringCharacter = '•', this.obscureText = false, this.autocorrect = true, SmartDashesType? smartDashesType, SmartQuotesType? smartQuotesType, this.enableSuggestions = true, this.maxLines = 1, this.minLines, this.expands = false, this.maxLength, this.maxLengthEnforcement, this.onChanged, this.onEditingComplete, this.onSubmitted, this.onAppPrivateCommand, this.inputFormatters, this.enabled, this.cursorWidth = 2.0, this.cursorHeight, this.cursorRadius, this.cursorColor, this.selectionHeightStyle = ui.BoxHeightStyle.tight, this.selectionWidthStyle = ui.BoxWidthStyle.tight, this.keyboardAppearance, this.scrollPadding = const EdgeInsets.all(20.0), this.dragStartBehavior = DragStartBehavior.start, this.enableInteractiveSelection = true, this.selectionControls, this.onTap, this.mouseCursor, this.buildCounter, this.scrollController, this.scrollPhysics, this.autofillHints, this.restorationId, this.enableIMEPersonalizedLearning = true, })

属性比较多,列几个关键的讲解:

一个简单的设置代码如下:

Column(children: const [ TextField( autofocus: true, decoration: InputDecoration( labelText: "用户名", hintText: "请输入用户名或密码", prefixIcon: Icon(Icons.person) ), ), TextField( decoration: InputDecoration( labelText: "密码", hintText: "请输入密码", prefixIcon: Icon(Icons.lock) ), obscureText: true, ) ]),

2.5.1.2 通过 controller 获取输入内容

我们可以通过 onChange 拿到内容。 当然也可以使用 controller 来获取

步骤为:

定义一个​​controller​​:

final TextEditingController _tfController = TextEditingController();

然后在 TextFiled 中传入这个 controller

TextField( controller: _tfController, ...)

最后就可以通过 : ​​print(_tfController.text)​​ 来获得输入框的内容

2.5.1.3 通过 controller 监听文本内容变化

可以通过 ​​onChange​​ 来监听文本, controller 可以通过设置-来监听文本,如下:

@override void initState() { super.initState(); _tfController.addListener(() { print(_tfController.text); }); }

controller 的功能更多,除了监听文本,还可以设置默认值、选择文本等,这里就不多赘述。

2.5.1.4 控制焦点

可以使用 ​​FocusNode​​​ 和 ​​FocusScopeNode​​​ 来控制焦点。默认情况下是由 ​​FocusScope​​​ 来管理,可以在这个范围内通过 ​​FocusScopeNode​​ 在输入框之间移动焦点、设置默认焦点。

我们可以通过下面代码来获取当前 Widget 树中默认的 FocusScopeNode:

focusScopeNode = FocusScope.of(context)

拿到句柄后,可以使用下面代码来获取焦点:

focusScopeNode.requestFocus(focusNode);

其中 focucsNode 是为 TextField 创建的 FocusNode, 这个操作可以让该 TextField 获取焦点。 调用 ​​focusNode.unfocus()​​ 可以取消焦点。

2.5.1.5 监听焦点状态改变事件

通过 ​​FocusNode​​ 可以监听焦点改变的事件:

focusNode.addListener((){ print(focusNode.hasFocus);})

true为获取焦点,false为失去焦点

2.5.2 表单

​​表单Form​​​ 对输入框进行分组和统一操作。 就像 Android 的原生组件 RadioGroup 之于 RadioButton 一样, ​​Form​​ 可以管理内容校验、输入框重置等。

Form 继承自 ​​StatefulWidget​​​,其状态管理在 ​​FormState​​ 里面,来看看 From 的定义:

class Form extends StatefulWidget { const Form({ Key? key, required this.child, @Deprecated( 'Use autovalidateMode parameter which provides more specific ' 'behavior related to auto validation. ' 'This feature was deprecated after v1.19.0.', ) this.autovalidate = false, this.onWillPop, this.onChanged, AutovalidateMode? autovalidateMode, }) ...

​​autovalidate​​​ 是否自动校验输入内容,当为true时,每一个 FormField 内容发生变化时都会校验合法性,并直接显示错误信息,否则就需要通过调用 ​​FormState.validate()​​ 来手动校验 v1.19 已经废弃了,改成使用 ​​AutovalidateMode​​​​autovalidateMode​​​ 自动校验模式,是上面的替换,它有三个枚举值: ①​​disable​​:当 FormField 内容改变时不做校验 ②​​always​​:即使用户没有用户交互也要校验合法性 ③​​onUserInteraction​​:只有在用户交互时才会去校验合法性​​onWillPop​​​ 决定 ​​Form​​ 所在的路由是否可以直接返回。该回调返回一个 ​​Future​​ 对象,如果 Future 的最终结果是 false,则当前路由不会返回,如果为 true,则会返回到上一个路由。 这个属性通常是用于拦截返回按钮的​​onChanged​​ Form 的任意一个 FormField 内容发生改变时就会调用该方法

2.5.2.1 FormField

Form 的子孙元素是 ​​FormField​​ 类型,FormField 是一个抽象类,定义了几个属性, FormState 内部通过他们来完成操作, FormField 部分定义如下:

const FormField({ Key? key, required this.builder, this.onSaved, this.validator, this.initialValue, @Deprecated( 'Use autovalidateMode parameter which provides more specific ' 'behavior related to auto validation. ' 'This feature was deprecated after v1.19.0.', ) this.autovalidate = false, this.enabled = true, AutovalidateMode? autovalidateMode, this.restorationId, })

​​onSaved​​ 保存时的回调​​validator​​ 验证合法性的回调​​initValue​​ 初始值

为了方便使用, Flutter 提供了一个 ​​TextFormFild​​​ 组件,继承自 ​​FormField​​ 类,还包装了 TextFileld ,可以直接当成 Form 的 FormField 来使用, 相当于用 Form 来管理 TextField

2.5.2.2 FormState

Form 表单的状态类就是 ​​FormState​​​, 可以通过 ​​Form.of​​​ 或者 ​​GlobalKey​​ 获得,通过获得它来对 Form 的子孙 FormField 进行统一操作。

FormState 常用的三个方法:

​​FormState.validate()​​​:调用此方法后, 会调用​​Form​​​ 子孙​​FormField.validate()​​ 回调,如果有一个检验失败,那么会返回 false,这样所有校验失败的 Widget 都会给出错误提示​​FormState.save()​​​:调用此方法后,会调用 子孙的​​FormFild.save()​​ 回调,用于保存表单内容​​FormState.reset()​​: 会将子孙 FormField 的内容清空

2.5.2.3 示例

用户名不能为空,如果为空则提示“用户名不能为空”密码不能小于6位,如果小于6位则提示 “密码不能少于6位”

代码如下:

import 'package:flutter/material.dart';class FormTestRoute extends StatefulWidget { const FormTestRoute({Key? key}) : super(key: key); @override State createState() => _FormTestRouteState();}class _FormTestRouteState extends State { final TextEditingController _usernameController = TextEditingController(); final TextEditingController _passwordController = TextEditingController(); final GlobalKey _formKey = GlobalKey(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Form demo'), ), body: Form( key: _formKey, autovalidateMode: AutovalidateMode.onUserInteraction, child: Column( children: [ TextFormField( autofocus: true, controller: _usernameController, decoration: const InputDecoration( labelText: "username", hintText: "username or email", icon: Icon(Icons.person)), validator: (username) { return username!.trim().isNotEmpty ? null : "username cannot empty"; }, ), TextFormField( controller: _passwordController, decoration: const InputDecoration( labelText: "password", hintText: "please input your password", icon: Icon(Icons.lock)), obscureText: true, validator: (pwd) { return pwd!.trim().length >= 6 ? null : "password digit cannot less than 6!"; }, ), // login button Padding( padding: const EdgeInsets.only(top: 28.0), child: Row( children: [ Expanded( child: ElevatedButton( onPressed: () { if ((_formKey.currentState as FormState).validate()) { print("Loing success"); } }, child: const Padding( padding: EdgeInsets.all(16.0), child: Text("Login"), ), )) ], ), ) ], ))); }}

效果如下图所示:

参考文章

​​官方学习文档​​​​Basics Widgets​​

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Servlet容器与Web服务器
下一篇:使用Mybatis实现分页效果示例
相关文章

 发表评论

暂时没有评论,来抢沙发吧~