Flutter text 系 Widget 没几个:
-
text
- 属性和 android 的 textview 一样,麻烦的是需要记忆其中文字样式 TextStyle 的属性设置 -
Directionality
- 文字方向,text 的单属性拓展 Widget -
DefaultTextStyle
- 给多个 text 设置相同的文字样式 -
RichText
- 富文本,需要记住相关设置,文字样式部分是仰仗 TextStyle 的 更换字体
阿里巴巴矢量图标字体
Text Weight 属性
Text Weight 是 Flutter 中 文本系 Weight 的正根,使用上感觉比 android 的用法要方便很多,尤其是富文本书写
先来看看 Text 支持的属性:
const Text(
this.data, {
Key key,
this.style, // 字体样式
this.strutStyle,
this.textAlign, // 文字对齐
this.textDirection, // 文字方向
this.locale,
this.softWrap, // 自动换行,默认是 true,自动换行
this.overflow, // 溢出样式
this.textScaleFactor, // 字体倍数,字体大小 * textScaleFactor
this.maxLines, // 最大行数
this.semanticsLabel,
this.textWidthBasis,
})
一般的属性经过 android 的熏陶大家都知道啥意思,比如最大行数,文字溢出样式等
textDirection 可能大家不熟悉,这个是文字方向,从左向右,或是从右向左,只有外层容器的宽比 text 大时才能起作用,直接看效果:
textAlign 同样是文字方向,效果和 textDirection 一样的
剩下没做中文解释的,一般也没啥用,有兴趣的自己去看
TextStyle 文字样式
android 里面我们需要把写样式文件写在style.xml
文件里再根据 id 引用,flutter 支持直接写,另外 TextStyle 也是 Weight,同样可以写在一个地方供所有控件使用
另外 android 里富文本
要借助代码给文本加上各种span
才行,flutter 直接就可以把span
写在配置中,span 中承载文字样式的还是TextStyle
TextStyle 的属性很多,一个一个看吧,设置和概念上和 android 差不多
const TextStyle({
this.inherit = true, // 为false 的时候不显示
this.color, // 颜色
this.backgroundColor,
this.fontSize, // 字号
this.fontWeight, // 字重,加粗也用这个字段 FontWeight.w700
this.fontStyle, // FontStyle.normal FontStyle.italic斜体
this.letterSpacing, // 字符间距 就是单个字母或者汉字之间的间隔,可以是负数
this.wordSpacing, // 段落间距,以空格为准
this.textBaseline, // 基线,两个值,字面意思是一个用来排字母的,一人用来排表意字的(类似中文)
this.height, // 当用来Text控件上时,行高(会乘以fontSize,所以不以设置过大)
this.locale,
this.foreground,
this.background,
this.shadows,
this.fontFeatures,
this.decoration, // 添加上划线,下划线,删除线
this.decorationColor, // 划线的颜色
this.decorationStyle, // 这个style可能控制画实线,虚线,两条线,点, 波浪线等
this.decorationThickness,
this.debugLabel,
String fontFamily, // 字体
List<String> fontFamilyFallback,
String package,
})
1. 常规设置
文字最常见的就是换颜色,换字号了,这个很简单,大家直接看效果
class DD extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(
"30号字-加粗-换颜色",
style: TextStyle(
color: Colors.lightBlue,
fontSize: 30,
fontWeight: FontWeight.bold,
),
);
}
}
注意点:
- 在颜色后面还可以操作以下的,比如加透明度
color: Colors.black..withAlpha(50),
-
TextAlign.justify = 两端贴边对齐,没用过的可能不熟悉
2. 加粗 - fontWeight
fontWeight
属性是操作加粗的,Flutter 提供了预制模式:bold
加粗、normal
正常,另外还提供了预制值:从W100
到 W900
bold
、normal
也是对应了其中的一个值
/// The default font weight.
static const FontWeight normal = w400;
/// A commonly used font weight that is heavier than normal.
static const FontWeight bold = w700;
lerp
方法可以自定义,需要出入3个参数,这个具体的我就不知道了
3. 字体样式 - fontStyle
字体样式 Flutter 只提供2种:italic
斜体、normal
正常
class DD extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(
"30号字-斜体-换颜色",
style: TextStyle(
color: Colors.lightBlue,
fontSize: 30,
fontStyle: FontStyle.italic,
),
);
}
}
4. 字符间距 - letterSpacing
letterSpacing
是每个字符之间都加间距,不论是中文、字母、还是数字中间都会加间距,具体的大家看图感受,像这种效果不看图不好理解
class DD extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"30号正常字",
style: TextStyle(
color: Colors.lightBlue,
fontSize: 30,
),
),
Text(
"30号+5字符间距字",
style: TextStyle(
color: Colors.lightBlue,
fontSize: 30,
letterSpacing: 5,
),
),
],
);
}
}
5. 段落间距 - wordSpacing
wordSpacing
应该理解为单词间距,这是外国人写的,外国热那都是英文嘛,单词之间否是有间隔的,所以英文的时候非常好。但是一到中文就有点水土不服了,中文没法区分单词,只能识别空格
class DD extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"30,AAA。我们是 害虫~",
style: TextStyle(
color: Colors.lightBlue,
fontSize: 30,
),
),
Text(
"30,AAA。我们是 害虫~",
style: TextStyle(
color: Colors.lightBlue,
fontSize: 30,
wordSpacing: 10,
),
),
],
);
}
}
6. 行高 - height
这里的行高是指:原行高*这个 height
,所以 height 我们只能当系数来看,这点和 word 一样
class DD extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
color: Colors.red,
child: Text(
"正常行高",
style: TextStyle(
color: Colors.lightBlue,
fontSize: 30,
),
),
),
Container(
margin: EdgeInsets.only(left: 10),
color: Colors.red,
child: Text(
"1.2X 行高",
style: TextStyle(
color: Colors.lightBlue,
fontSize: 30,
height: 1.2,
),
),
),
],
);
}
}
7. 分割线 - decoration
decoration
分割线算是常用的了吧,Flutter 里面可以对分割线设置:线段样式
、位置
、颜色
,具体的就是下面这3个属性
decoration: TextDecoration.lineThrough,
decorationStyle: TextDecorationStyle.dashed,
decorationColor: Colors.lightGreenAccent,
decorationStyle 的类型不好记:
-
solid
- 实线 -
double
- 双线 -
dotted
- 虚线,点间隔 -
dashed
- 虚线,短横线间隔 -
wavy
- 波浪线
先看看效果,下面是代码:
class DD extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"删除线",
style: TextStyle(
decoration: TextDecoration.lineThrough,
color: Colors.lightBlue,
fontSize: 30,
),
),
Text(
"上滑线",
style: TextStyle(
decoration: TextDecoration.overline,
color: Colors.lightBlue,
fontSize: 30,
height: 1.2,
),
),
Text(
"下划线",
style: TextStyle(
decoration: TextDecoration.underline,
color: Colors.lightBlue,
fontSize: 30,
height: 1.2,
),
),
Text(
"删除线 + 波浪线",
style: TextStyle(
decoration: TextDecoration.lineThrough,
decorationStyle: TextDecorationStyle.wavy,
decorationColor: Colors.yellowAccent,
color: Colors.black,
fontSize: 30,
),
),
Text(
"删除线 + 双线段",
style: TextStyle(
decoration: TextDecoration.lineThrough,
decorationStyle: TextDecorationStyle.double,
decorationColor: Colors.redAccent,
color: Colors.black,
fontSize: 30,
),
),
Text(
"删除线 + 点线",
style: TextStyle(
decoration: TextDecoration.lineThrough,
decorationStyle: TextDecorationStyle.dotted,
decorationColor: Colors.lightBlueAccent,
color: Colors.black,
fontSize: 30,
),
),
Text(
"删除线 + 虚线",
style: TextStyle(
decoration: TextDecoration.lineThrough,
decorationStyle: TextDecorationStyle.dashed,
decorationColor: Colors.lightGreenAccent,
color: Colors.black,
fontSize: 30,
),
),
],
);
}
}
Directionality
Directionality
是 text 的文字方向单独抽象出来的 widget ,就是控制文字方向,从左到右还是从右到左
Directionality(
textDirection: TextDirection.rtl,
child: Text("AAA"),
),
不上图了,没意义
DefaultTextStyle
DefaultTextStyle
用于同意设置文本样式,若是有多个相同的 text 时,每个 text 里面逗设置一遍文本样式的话很麻烦,所以 DefaultTextStyle 横空出世,DefaultTextStyle 子孙 widget 全部遵循 DefaultTextStyle 中的文本设置,具体的设置和 text 一样
class DD extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DefaultTextStyle(
style: TextStyle(
color: Colors.lightBlue,
fontSize: 30,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"AAA",
),
Text(
"我是 demo1",
),
Text(
"下划线",
),
Text(
"我不受控制",
style: TextStyle(
color: Colors.red,
fontSize: 15,
),
),
],
),
);
}
}
RichText 富文本
RichText
富文本是每个客户端都应该支持的,目前 Flutter 是1.7 版本,可能是 Flutter 之前对富文本支持不太好,网上好多关于 Flutter 富文本解决思路,字节跳动还开源了相关的富文本组件,咸鱼还发文介绍自行实现富文本方案。不过我看 Flutter 对富文本支持应该是 OK 了,用 Flutter 自身的方案就可以了
RichText
是 Flutter 富文本的 widget,但是 RichText
只负责 layout,具体的配置还要看 Flutter 提供的2个类型 span:TextSpan
、WidgetSpan
-
TextSpan
- 配合 textStyle 实现各种文字效果,可以添加点击事件 -
WidgetSpan
- 可以添加其他类型的 widget,不过我就试了试图片
我不做过多解释,代码会帮我解释的
class DD extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RichText(
text: TextSpan(
children: <InlineSpan>[
TextSpan(
text: "AA",
style: TextStyle(
fontSize: 20,
color: Colors.redAccent,
),
),
TextSpan(
text: " 我是谁",
style: TextStyle(
fontSize: 25,
color: Colors.blue,
),
),
TextSpan(
text: " 我在哪 ",
style: TextStyle(
wordSpacing: 0,
fontSize: 30,
color: Colors.lightGreen,
),
recognizer: TapGestureRecognizer()
..onTap = () {
print("AA");
},
),
WidgetSpan(
child: Icon(
Icons.aspect_ratio,
color: Colors.lightBlue,
textDirection: TextDirection.ltr,
),
),
],
),
),
],
);
}
}
想要文本和图片混用的话,必须同时支持:TextSpan
、WidgetSpan
,RichText 的 text 属性我们一般都是给一个 TextSpan,这个 TextSpan 只代表富文本 viewTree,具体的富文本怎么样还得看我们在 children 里面怎么写。TextSpan 的 children[] 要图文混用的话,泛型需要写 TextSpan
、WidgetSpan
公共父类:InlineSpan
,只是文字的话泛型写 TextSpan 就 OK 了
蛋疼的是点击对象类型 TapGestureRecognizer 得自己导包
import 'package:flutter/material.dart';
然后介绍下其他的富文本思路:
- 闲鱼团队自己实现了富文本 widget,具体请看:
- 自己跳动开源的插件:
RealRichText
,具体使用了官方的其实一样,详情请看:
我说这两个的目的是为了开拓大家的眼界,看看别人的自定义方案,Flutter 毕竟是个新东西,自定义 view 还是需要重新熟悉走起的
更换字体
换字体这是 UI 最爱干的事,每个 UI 必有自己独爱的字体
1. asset 字体
一般我们都是把字体内置在 app 的 asset 资源文件夹里,这种思路也是最推荐的,绝不会出任何问题的
-
tts 字体文件路径:
yaml
配置 tts 字体,注意一个也不能写错
flutter:
uses-material-design: true
# 图片资源配置
assets:
- assets/icons/
fonts:
- family: font1
fonts:
- asset: assets/fonts/font_fangzheng_duhei.ttf
- 在 TextStyle 使用字体
class EE extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"原生字体 AAA aaa",
style: TextStyle(fontSize: 30),
),
Text(
"测试字体 AAA aaa",
style: TextStyle(
fontSize: 30,
fontFamily: "font1"
),
),
],
);
}
}
2. 依赖包字体
Flutter Package 可以像 android module 一样,打成组件包提供给更多的工程使用,字体也是一样的。我们可以把字体放在一个公共的资源 Flutter Package 里,然后实现一处声明,处处使用的效果
1.
在公共 Flutter Package 里面,放置字体文件,并在 yaml
中正常声明字体
2.
公共文件中,在 yaml
中加上 Package
声明就可以使用 Flutter Package 中的字体了
flutter:
fonts:
- family: Raleway
fonts:
- asset: packages/my_package/fonts/Raleway-Medium.ttf
weight: 500
- my_package 是 Flutter Package 的库名
-
lib/fonts/Raleway-Medium.ttf
是 Flutter Package 中字体存储路径
3.
也可以直接在 Flutter Package 中定义一个全局的 TextStyle 设置,然后业务库引用使用
const textStyle = const TextStyle(
fontFamily: 'Raleway',
package: 'my_package', //指定包名
);
3. 远程字体
有时蛋疼的要求可以动态换字体,这样我们必须吧字体放到服务器上或是下载下放到 SD 卡里,这个暂时没研究,大家看下别人的方案:
阿里巴巴矢量图标字体
这个大家在 android 里应该都熟悉,阿里巴爸爸矢量图可以打包成ttf
字体格式下载下来,当做字体来显示,原理和emoil
表情一样
不过实测过后发现 Flutter 不支持阿里矢量库里的彩色 icon,会按照黑白来显示
阿里图片原样:
ttf 文件放置:
yaml
flutter:
uses-material-design: true
# 图片资源配置
assets:
- assets/icons/
fonts:
- family: font1
fonts:
- asset: assets/fonts/font_fangzheng_duhei.ttf
- family: font2
fonts:
- asset: assets/fonts/font_ali_f1.ttf
创建阿里图标对象:
import 'package:flutter/cupertino.dart';
class ALiFonts{
static const IconData realTime = IconData(0xe74b,fontFamily: "font2");
static const IconData computing = IconData(0xe743,fontFamily: "font2");
}
使用:
// Icon 承载阿里图标
class EE extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
ALiFonts.realTime,
size: 30,
),
Icon(
ALiFonts.computing,
size: 30,
),
],
);
}
}
注意:
- 阿里图标对应的
16进制
标准样式:0xe74b
,字幕自己写一遍,网站复制的话显示不出来,数值部分只取最后4位,0x
是固定添加打头的
Flutter 对 emoji 的支持
对于 emoji
表情,平时也是会碰到的,比如接入微信登录,有大把的人蛋疼的名字里带 emoji
表情,你不处理显示的就是乱码,不管是我们过滤他还是支持他,我们都应该对 emoji
有足够认知
Unicode 简介
Unicode
编码是国际最通用的字符编码了,Unicode
里给不同语言的每个字符,其他各种符号,包括 emoji 表情都设置有自己独立的编号,所以就不会再出现乱的问题了,但是随着各种符号越来越多,尤其是 emoji 表情大行其道之后,这符号的数量与日俱增,为了装的下这么多符号,目前 Unicode 最多已经采用到 32位来存储了
-
UTF-8
- 8 位的 Unicode 编码,主要通统一显示文字 -
UTF-16
- 16 位的 Unicode 编码 -
UTF-32
- 32 位的 Unicode 编码,要兼容 emoji 表情最好用这个编码
UTF 有好几种,不是说只用最大编码的,不同的系统根据实际需求会选择自己默认支持的 UTF,一般文字的话 UTF-8 就足够了,但是处理 emoji 就得 UTF-32 了,具体在于使用场景
Unicode 储存图
UTF-32 储存值从 U+0000
到 U+10FFFF
,分成14个扇区存储,每一个扇区有 256 个小块,每个小块有 16×16 = 256 个编码点,总体下来每个扇区有 65536 个 编码点
扇区图:没一个大块就是一个扇区
不同语言的字符,包括古今文字,上古语言,符号,emoji 都存储在不同的扇区内
- Unicode 0 号平面(0000-FFFF)
- Unicode 1 号平面(10000-1FFFF)
Unicode 所有的字符可以在官方网站上查询到:
所以这 Unicode 数值也挺乱的,大家要注意,对于不同的数值范围 Dart 中有不同的保存格式:
-
\u2665
- 4个16进制的数的这么写 -
\u{1f600}
- 但要不是4位的就得在数值前加{}
了
Flutter 上显示 emoji
Flutter 上想要正确显示 emoji 表情,请示就是给不同数值范围的 Unicode 编码套整个的格式
显示单个 emoji
var index = "\u{1f44f}";
显示多个,带文字混排
Runes input = new Runes(
'\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d} 哇哈哈哈哈!!!');
var index = String.fromCharCodes(input);
Runes
Dart 字符串默认是 UTF-16 的,我们兼容 emoji 的话最好用 UTF-32,对此 Dart 提供了 Runes
这个类,Runes
可以让我们按照 UTF-32 存储展示字符
看上面的代码就行了,很简单
Dart 字符编码的 API
dart:core
库提供了获取字符编码的 API:
-
String.codeUnitAt(index)
- 返回指定字符的 10 进制 Unicode 索引 -
String.codeUnits
- 返回所有字符的 10 进制 Unicode 索引,结果是个集合 -
String.runes
- 返回所有字符的 10 进制 Unicode 编码值,结果是个集合
var index1 = "我";
var index2 = "我是富翁,我老有钱了";
print("index1:${index1.codeUnitAt(0)}");
print("index1:${index2.codeUnits}");
print("index1:${index1.runes}");
I/flutter ( 6849): index1:25105
I/flutter ( 6849): index1:[25105, 26159, 23500, 32705, 65292, 25105, 32769, 26377, 38065, 20102]
I/flutter ( 6849): index1:(25105)
我详细解释下,有点绕,不容易搞清楚
例子:
Unicode 编码都是 16 进制的,通常表示为 \uXXXX
,其中这个 xxxx
就是具体的 16进制值
比如这个符号:他的 16进制 Unicode 编码是 \u2665
,2665 的 10 进制 = 9829
我们来看下:
Runes input = new Runes('\u2665');
var index = String.fromCharCodes(input);
print("index1:${index.codeUnitAt(0)}");
print("index1:${index.codeUnits}");
print("index1:${index.runes}");
I/flutter ( 6849): index1:9829
I/flutter ( 6849): index1:[9829]
I/flutter ( 6849): index1:(9829)
拿到的结果正好对的上 10进制数值,我们转成 16进制加上 \u
就是 Unicode 编码了
-
16 进制
Unicode 编码显示,使用Runes
类包裹数据
Runes input = new Runes('\u2665');
var index = String.fromCharCodes(input);
-
10 进制
就不用Runes
了,直接走
var index = String.fromCharCode(9829);