Skip to main content

第3章 - 基础组件

3.1 文本及样式

3.1.1 Text

语法

const Text(
String this.data, {
Key? key,
this.style,
this.strutStyle,
this.textAlign,
this.textDirection,
this.locale,
this.softWrap,
this.overflow,
this.textScaleFactor,
this.maxLines,
this.semanticsLabel,
this.textWidthBasis,
this.textHeightBehavior,
})
  • maxLines, overflow 指定文本显示的最大行数,默认情况下,文本是自动折行的,如果指定此参数,则文本最多不会超过指定的行. 如果有多余的文本,可以通过overflow来指定截断方式,默认是直接截断
  • textScaleFactor 代表文本相对于当前字体大小的缩放因子,相对于去设置文本的样式style属性的fontSize,它是调整字体大小的一个快捷方式. 该属性的默认值可以通过MediaQueryData.textScaleFactor获得,如果没有MediaQuery,那么会默认值将为1.0.

示例

Text("Hello world "*6,  //字符串重复六次
textAlign: TextAlign.center,
);

TextStyle

TextStyle 用于指定文本显示的样式如颜色、字体、粗细、背景等:

Text("Hello world",
style: TextStyle(
color: Colors.blue,
fontSize: 18.0,
height: 1.2,
fontFamily: "Courier",
background: Paint()..color=Colors.yellow,
decoration:TextDecoration.underline,
decorationStyle: TextDecorationStyle.dashed
),
);
  • height 该属性用于指定行高,但它并不是一个绝对值,而是一个因子,具体的行高等于 fontSize * height.
  • fontFamily 由于不同平台默认支持的字体集不同,所以在手动指定字体时一定要先在不同平台测试一下.
  • fontSize 该属性和 Text 的 textScaleFactor 都用于控制字体大小. 但是有两个主要区别:
    1. fontSize 可以精确指定字体大小,而textScaleFactor只能通过缩放比例来控制.
    2. textScaleFactor 主要是用于系统字体大小设置改变时对 Flutter 应用字体进行全局调整,而fontSize通常用于单个文本,字体大小不会跟随系统字体大小变化.

TextSpan

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

const TextSpan({
TextStyle style,
Sting text,
List<TextSpan> children,
GestureRecognizer recognizer,
});

children 是一个TextSpan的数组,也就是说TextSpan可以包括其他TextSpan. 而recognizer用于对该文本片段上用于手势进行识别处理:

Text.rich(TextSpan(
children: [
TextSpan(
text: "Home: "
),
TextSpan(
text: "https://flutterchina.club",
style: TextStyle(
color: Colors.blue
),
recognizer: _tapRecognizer
),
]

DefaultTextStyle

文本的样式默认是可以被继承的, 如果在 Widget 树的某一个节点处设置一个默认的文本样式(DefaultTextStyle),那么该节点的子树中所有文本都会默认使用这个样式:

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

3.2 按钮

Material 提供的按钮组件有:

  • ElevatedButton
  • TextButton
  • OutlineButton
  • IconButton

它们大部分都是对RawMaterialButton组件的包装定制

示例 :

// ElevatedButton, TextButton, OutlineButton 使用方式相同, 仅是样式不同
ElevatedButton(
child: Text("normal"),
onPressed: () {},
);
// icon+文案按钮
ElevatedButton.icon(
icon: Icon(Icons.send),
label: Text("发送"),
onPressed: _onPressed,
)

// Icon按钮
IconButton(
icon: Icon(Icons.thumb_up),
onPressed: () {},
)

3.3 图片及ICON

图片

Flutter中使用 Image 组件加载并显示组件:

const Image({
ImageProvider image,
})

Image的属性image对应的类型ImageProvider是一个抽象类, 主要定义了图片数据获取的方法load(), 其实现类有:

  • AssetImage 实现了从Asset中加载图片的 ImageProvider
  • NetworkImage 实现了从网络加载图片的 ImageProvider

除了常规的构造函数初始化图片, Image还提供了对应的便捷的构造方法:

Image(
image: AssetImage("images/avatar.png"),
width: 100.0
);
// 等价于
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.network(
"https://avatars2.githubusercontent.com/u/20411648?s=460&v=4",
width: 100.0,
);

其他参数:

const Image({
...
this.width, //图片的宽
this.height, //图片高度
this.color, //图片的混合色值
this.colorBlendMode, //混合模式
this.fit,//缩放模式
this.alignment = Alignment.center, //对齐方式
this.repeat = ImageRepeat.noRepeat, //重复方式
...
});
  • width height: 用于设置图片的宽、高,当不指定宽高时,图片会根据当前父容器的限制,尽可能的显示其原始大小,如果只设置width、height的其中一个,那么另一个属性默认会按比例缩放,但可以通过下面介绍的 fit 属性来指定适应规则.
  • `fit:该属性用于在图片的显示空间和图片本身大小不同时指定图片的适应模式. 适应模式是在BoxFit中定义,它是一个枚举类型,有如下值:
    • fill: 会拉伸填充满显示空间,图片本身长宽比会发生变化,图片会变形.
    • cover: 会按图片的长宽比放大后居中填满显示空间,图片不会变形,超出显示空间部分会被剪裁.
    • contain: 这是图片的默认适应规则,图片会在保证图片本身长宽比不变的情况下缩放以适应当前显示空间,图片不会变形.
    • fitWidth: 图片的宽度会缩放到显示空间的宽度,高度会按比例缩放,然后居中显示,图片不会变形,超出显示空间部分会被剪裁.
    • fitHeight: 图片的高度会缩放到显示空间的高度,宽度会按比例缩放,然后居中显示,图片不会变形,超出显示空间部分会被剪裁.
    • none: 图片没有适应策略,会在显示空间内显示图片,如果图片比显示空间大,则显示空间只会显示图片中间部分.
  • colorcolorBlendMode: 在图片绘制时可以对每一个像素进行颜色混合处理,color指定混合色,而colorBlendMode指定混合模式
  • repeat: 当图片本身大小小于显示空间时,指定图片的重复规则

3.4 单选开关和复选框

单选开关Switch, 复选框Checkbox, 当 SwitchCheckbox 被点击时,会触发 onChanged 回调, 我们可以在此回调中处理选中状态改变逻辑.

Checkbox 有一个属性tristate, 表示是否为三态, tristate 为fase时, Checkbox 有两种状态即“选中”和“不选中”,对应的 value 值为true和false; 如果tristate值为true时,value 的值会增加一个状态null. 默认情况下该属性值为false:

bool? _checked;

Checkbox(
value: _checked,
activeColor: Colors.red,
tristate: true,
onChanged: (val) {
setState(() {
_checked = val;
});
}),
Text("$_checked") // 会有三种值: null, true, false

3.5 输入框及表单

TextField

TextField 用于文本输入, 关键属性如下:

const TextField({
...
// 编辑框的控制器,通过它可以设置/获取编辑框的内容、选择编辑内容、监听编辑文本改变事件. 如果没有提供controller,则TextField内部会自动创建一个.
TextEditingController controller,
// 用于控制TextField是否占有当前键盘的输入焦点. 它是我们和键盘交互的一个句柄(handle)
// 比如收起键盘
FocusNode focusNode,
// 用于控制TextField的外观显示,如提示文本、背景颜色、边框等
InputDecoration decoration = const InputDecoration(),
TextInputType keyboardType, // 用于设置该输入框默认的键盘输入类型
TextInputAction textInputAction, // 键盘动作按钮图标(即回车键位图标),它是一个枚举值
TextStyle style, // 正在编辑的文本样式
TextAlign textAlign = TextAlign.start, // 输入框内编辑文本在水平方向的对齐方式
bool autofocus = false, // 是否自动获取焦点
bool obscureText = false, // 是否隐藏正在编辑的文本,如用于输入密码的场景等
int maxLines = 1, // 输入框的最大行数,默认为1;如果为null,则无行数限制
int maxLength, // 代表输入框文本的最大长度,设置后输入框右下角会显示输入的文本计数
this.maxLengthEnforcement, // 决定当输入文本长度超过maxLength时如何处理,如截断、超出等
// 长按或鼠标右击时出现的菜单
// 包括 copy、cut、paste 以及 selectAll
ToolbarOptions? toolbarOptions,
// 输入框内容改变时的回调函数, 内容改变事件也可以通过controller来监听
ValueChanged<String> onChanged,
// 输入框输入完成时触发, 回调函数不接受参数
VoidCallback onEditingComplete,
// 输入框输入完成时触发, 回调是ValueChanged<String>类型
ValueChanged<String> onSubmitted,
// 用于指定输入格式;当用户输入内容改变时,会根据指定的格式来校验
List<TextInputFormatter> inputFormatters,
// 如果为false,则输入框会被禁用,禁用状态不接收输入和事件,同时显示禁用态样式(在其decoration中定义)
bool enabled,
// 以下三个 cursorXXX 属性是用于自定义输入框光标宽度、圆角和颜色的
this.cursorWidth = 2.0,
this.cursorRadius,
this.cursorColor,
this.onTap,
...
})

keyboardType 可选值如下:

TextInputType 枚举值含义
text文本输入键盘
multiline多行文本,需和maxLines配合使用(设为null或大于1)
number数字;会弹出数字键盘
phone优化后的电话号码输入键盘;会弹出数字键盘并显示: * #
datetime优化后的日期输入键盘;Android上会显示: : -
emailAddress优化后的电子邮件地址, 会显示: @ .
url优化后的url输入键盘, 会显示: / .

示例:

String _text = ""; // 存放用户在文本框输入的内容

TextField(
style: TextStyle(color: Colors.green),
toolbarOptions: ToolbarOptions(
copy: true,
),
decoration: InputDecoration(
labelText: "用户名",
hintText: "手机号或邮箱",
prefixIcon: Icon(Icons.person)),
onChanged: (val) {
setState(() {
_text = val;
});
},
)

控制焦点

焦点可以通过FocusNodeFocusScopeNode来控制:

FocusNode focusNode1 = FocusNode();

TextField(
autofocus: true,
focusNode: focusNode1, // 关联focusNode1
)

// 焦点移动到文本框, 同时会打开键盘
FocusScope.of(context).requestFocus(focusNode1);
// 从文本框移除焦点, 同时会收起键盘
focusNode1.unfocus();

实例 - 隐藏手机号中间4位

下面演示一个手机号中间加*的文本框状态实现:

class _TextFieldWidgetState extends State<TextFieldWidget> {
String _fullPhone = "";
final FocusNode _focus = FocusNode();
final TextEditingController _controller = TextEditingController();
@override
void initState() {
this._focus.addListener(() {
if (this._focus.hasFocus) {
// 用户光标聚焦在文本框
this._controller.text = this._fullPhone;
} else {
// 用户光标离开文本框
// 如果文本框内容是11位, 把中间4位隐藏掉, 并更新到文本框内容上
// 这里应该用正则判断手机号更加严谨
final String currentValue = this._controller.text;
setState(() {
this._fullPhone = currentValue;
});
if (currentValue.length == 11) {
final String maskPhone =
currentValue.substring(0, 3) + "****" + currentValue.substring(7);
this._controller.text = maskPhone;
}
}
});
}

@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
padding: EdgeInsets.only(left: 50, right: 50),
child: TextField(
controller: this._controller,
focusNode: this._focus,
keyboardType: TextInputType.phone,
decoration: InputDecoration(
labelText: "用户名",
hintText: "请输入手机号",
prefixIcon: Icon(Icons.phone)),
),
),
],
);
}
}