在前面搭好环境后以及快速了解的dart的语法后,开始学习Flutter的基础Widget!!
目录
在Flutter中大部分类皆继承Widget,Widget的功能是“描述一个UI元素的配置数据”,它就是说,Widget其实并不是表示最终绘制在设备屏幕上的显示元素,而只是显示元素的一个配置数据。实际上,Flutter中真正代表屏幕上显示元素的类是Element
,也就是说Widget只是描述Element
的一个配置。
Flutter Widget库介绍
Flutter提供了一套丰富、强大的基础widget,在基础widget库之上Flutter又提供了一套Material风格(Android默认的视觉风格)和一套Cupertino风格(iOS视觉风格)的widget库。要使用基础widget库,需要先导入:
// Android
import 'package:flutter/widgets.dart';
// iOS
import 'package:flutter/cupertino.dart';
Widget
下面是Widget类的源代码:
abstract class Widget extends DiagnosticableTree {
/// Initializes [key] for subclasses.
const Widget({ this.key });
final Key key;
@protected
Element createElement();
@override
String toStringShort() {
return key == null ? '$runtimeType' : '$runtimeType-$key';
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;
}
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
}
- 类Key:当前Widget的唯一标示符,决定Widget是否复用。在canUpdate方法中判断。
- 方法createElement:创建显示的对象。
StatelessWidget
StatelessWidget用于不需要维护状态的场景,它通常在build
方法中通过嵌套其它Widget来构建UI,在构建过程中会递归的构建其嵌套的Widget
StatefulWidget
abstract class StatefulWidget extends Widget {
const StatefulWidget({ Key key }) : super(key: key);
@override
StatefulElement createElement() => new StatefulElement(this);
@protected
State createState();
}
-
StatefulElement
间接继承自Element
类,与StatefulWidget相对应(作为其配置数据)。StatefulElement
中可能会多次调用createState()
来创建状态(State)对象。 -
createState()
用于创建和Stateful widget相关的状态,它在Stateful widget的生命周期中可能会被多次调用。例如,当一个Stateful widget同时插入到widget树的多个位置时,Flutter framework就会调用该方法为每一个位置生成一个独立的State实例,其实,本质上就是一个StatefulElement
对应一个State实例。
State
一个StatefulWidget类会对应一个State类,State表示与其对应的StatefulWidget要维护的状态,State中的保存的状态信息可以:
- 在widget build时可以被同步读取。
- 在widget生命周期中可以被改变,当State被改变时,可以手动调用其
setState()
方法通知Flutter framework状态发生改变,Flutter framework在收到消息后,会重新调用其build
方法重新构建widget树,从而达到更新UI的目的
State生命周期
// 初始化状态,只执行一次
void initState() { }
// 当widget依赖的对象发生变化时调用,例如全局的主题或语言
void didChangeDependencies() { }
// 当widget重新构建时调用,根据canUpdate方法判断
void didUpdateWidget(covariant T oldWidget) { }
// 构建UI
void build() { }
// 调试模式下,热重载执行,Release模式不会执行
void reassemble() { }
// 当widget被移除时调用
void deactivate() { }
// 当widget被永久移除时,可以做释放资源
void dispose() { }
widget被加载显示的时候会依次调用initState,didChangeDependencies,build等方法。
点击热重载时会依次调用reassemble,didUpdateWidget,build等方法。
发生界面切换时会依次调用deactivate,dispose等方法。
Text
显示文本,其一些样式属性和Android的TextView类似,比如,文本对齐方式,大小,颜色等等。
使用字体
在Flutter中使用字体分两步完成。首先在pubspec.yaml
中声明它们,以确保它们会打包到应用程序中。然后通过TextStyle
属性使用字体。
在asset中声明
要将字体打文件打包到应用中,和使用其它资源一样,要先在pubspec.yaml
中声明它。然后将字体文件复制到在pubspec.yaml
中指定的位置。如
flutter:
fonts:
- family: Raleway
fonts:
- asset: assets/fonts/Raleway-Regular.ttf
- asset: assets/fonts/Raleway-Medium.ttf
weight: 500
- asset: assets/fonts/Raleway-SemiBold.ttf
weight: 600
- family: AbrilFatface
fonts:
- asset: assets/fonts/abrilfatface/AbrilFatface-Regular.ttf
使用字体
// 声明文本样式
const textStyle = const TextStyle(
fontFamily: 'Raleway',
);
// 使用文本样式
var buttonText = const Text(
"Use the font for this text",
style: textStyle,
);
Package中的字体
要使用Package中定义的字体,必须提供package
参数。例如,假设上面的字体声明位于my_package
包中。然后创建TextStyle的过程如下
const textStyle = const TextStyle(
fontFamily: 'Raleway',
package: 'my_package', //指定包名
);
如果在package包内部使用它自己定义的字体,也应该在创建文本样式时指定package
参数,如上例所示。
一个包也可以只提供字体文件而不需要在pubspec.yaml中声明。 这些文件应该存放在包的lib/
文件夹中。字体文件不会自动绑定到应用程序中,应用程序可以在声明字体时有选择地使用这些字体。假设一个名为my_package的包中有一个字体文件:
lib/fonts/Raleway-Medium.ttf
然后,应用程序可以声明一个字体,如下面的示例所示:
flutter:
fonts:
- family: Raleway
fonts:
- asset: assets/fonts/Raleway-Regular.ttf
- asset: packages/my_package/fonts/Raleway-Medium.ttf
weight: 500
lib/
是隐含的,所以它不应该包含在asset路径中。
在这种情况下,由于应用程序本地定义了字体,所以在创建TextStyle时可以不指定package
参数:
const textStyle = const TextStyle(
fontFamily: 'Raleway',
);
按钮
Material widget库中提供了多种按钮Widget如RaisedButton、FlatButton、OutlineButton等,它们都是直接或间接对RawMaterialButton的包装定制,所以他们大多数属性都和RawMaterialButton
一样。在介绍各个按钮时我们先介绍其默认外观,而按钮的外观大都可以通过属性来自定义,我们在后面统一介绍这些属性。另外,所有Material 库中的按钮都有如下相同点:
- 按下时都会有“水波动画”。
- 有一个
onPressed
属性来设置点击回调,当按钮按下时会执行该回调,如果不提供该回调则按钮会处于禁用状态,禁用状态不响应用户点击。
自定义按钮外观
const FlatButton({
...
@required this.onPressed, //按钮点击回调
this.textColor, //按钮文字颜色
this.disabledTextColor, //按钮禁用时的文字颜色
this.color, //按钮背景颜色
this.disabledColor,//按钮禁用时的背景颜色
this.highlightColor, //按钮按下时的背景颜色
this.splashColor, //点击时,水波动画中水波的颜色
this.colorBrightness,//按钮主题,默认是浅色主题
this.padding, //按钮的填充
this.shape, //外形
@required this.child, //按钮的内容
})
图片
Flutter中,我们可以通过Image来加载并显示图片,Image的数据源可以是asset、文件、内存以及网络。
ImageProvider
ImageProvider
是一个抽象类,主要定义了图片数据获取的接口load()
,从不同的数据源获取图片需要实现不同的ImageProvider
,如AssetImage
是实现了从Asset中加载图片的ImageProvider,而NetworkImage
实现了从网络加载图片的ImageProvider。
从asset中加载图片
-
在工程根目录下创建一个
images目录
,并将图片avatar.png拷贝到该目录。 -
在
pubspec.yml
中的flutter
部分添加如下内容:assets: - images/avatar.png
-
加载图片
Image(
image: AssetImage("images/avatar.png"),
width: 100.0
);
Image也提供了一个快捷的构造函数Image.asset
用于从asset中加载、显示图片:
Image.asset("images/avatar.png",
width: 100.0,
)
从网络加载图片
Image(
image: NetworkImage(
"https://avatars2.githubusercontent.com/u/20411648?s=460&v=4"),
width: 100.0,
)
Image也提供了一个快捷的构造函数Image.network
用于从网络加载、显示图片:
Image.network(
"https://avatars2.githubusercontent.com/u/20411648?s=460&v=4",
width: 100.0,
)
TextField
TextField用于文本输入,它提供了很多属性。
const TextField({
...
TextEditingController controller,
FocusNode focusNode,
InputDecoration decoration = const InputDecoration(),
TextInputType keyboardType,
TextInputAction textInputAction,
TextStyle style,
TextAlign textAlign = TextAlign.start,
bool autofocus = false,
bool obscureText = false,
int maxLines = 1,
int maxLength,
bool maxLengthEnforced = true,
ValueChanged<String> onChanged,
VoidCallback onEditingComplete,
ValueChanged<String> onSubmitted,
List<TextInputFormatter> inputFormatters,
bool enabled,
this.cursorWidth = 2.0,
this.cursorRadius,
this.cursorColor,
...
})
-
controller:编辑框的控制器,通过它可以设置/获取编辑框的内容、选择编辑内容、监听编辑文本改变事件。大多数情况下我们都需要显式提供一个controller来与文本框交互。如果没有提供controller,则TextField内部会自动创建一个。
-
focusNode:用于控制TextField是否占有当前键盘的输入焦点。它是我们和键盘交互的一个handle。
-
InputDecoration:用于控制TextField的外观显示,如提示文本、背景颜色、边框等。
-
keyboardType:用于设置该输入框默认的键盘输入类型,取值如下:
| TextInputType枚举值 | 含义 | | ------------------- | --------------------------------------------------- | | text | 文本输入键盘 | | multiline | 多行文本,需和maxLines配合使用(设为null或大于1) | | number | 数字;会弹出数字键盘 | | phone | 优化后的电话号码输入键盘;会弹出数字键盘并显示"* #" | | datetime | 优化后的日期输入键盘;Android上会显示“: -” | | emailAddress | 优化后的电子邮件地址;会显示“@ .” | | url | 优化后的url输入键盘; 会显示“/ .” |
-
textInputAction:键盘动作按钮图标(即回车键位图标),它是一个枚举值,有多个可选值,全部的取值列表读者可以查看API文档
- style:正在编辑的文本样式。
- textAlign: 输入框内编辑文本在水平方向的对齐方式。
- autofocus: 是否自动获取焦点。
- obscureText:是否隐藏正在编辑的文本,如用于输入密码的场景等,文本内容会用“•”替换。
- maxLines:输入框的最大行数,默认为1;如果为
null
,则无行数限制。 - maxLength和maxLengthEnforced :maxLength代表输入框文本的最大长度,设置后输入框右下角会显示输入的文本计数。maxLengthEnforced决定当输入文本长度超过maxLength时是否阻止输入,为true时会阻止输入,为false时不会阻止输入但输入框会变红。
- onChange:输入框内容改变时的回调函数;注:内容改变事件也可以通过controller来监听。
- onEditingComplete和onSubmitted:这两个回调都是在输入框输入完成时触发,比如按了键盘的完成键(对号图标)或搜索键(?图标)。不同的是两个回调签名不同,onSubmitted回调是
ValueChanged<String>
类型,它接收当前输入内容做为参数,而onEditingComplete不接收参数。 - inputFormatters:用于指定输入格式;当用户输入内容改变时,会根据指定的格式来校验。
- enable:如果为
false
,则输入框会被禁用,禁用状态不接收输入和事件,同时显示禁用态样式(在其decoration中定义)。 - cursorWidth、cursorRadius和cursorColor:这三个属性是用于自定义输入框光标宽度、圆角和颜色的。