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 大时才能起作用,直接看效果:
image
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,
),
);
}
}
image
注意点:
- 在颜色后面还可以操作以下的,比如加透明度
color: Colors.black..withAlpha(50),
-
TextAlign.justify = 两端贴边对齐,没用过的可能不熟悉
image
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,
),
);
}
}
image
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,
),
),
],
);
}
}
image
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,
),
),
],
);
}
}
image
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,
),
),
),
],
);
}
}
image
7. 分割线 - decoration
decoration
分割线算是常用的了吧,Flutter 里面可以对分割线设置:线段样式
、位置
、颜色
,具体的就是下面这3个属性
decoration: TextDecoration.lineThrough,
decorationStyle: TextDecorationStyle.dashed,
decorationColor: Colors.lightGreenAccent,
decorationStyle 的类型不好记:
-
solid
- 实线 -
double
- 双线 -
dotted
- 虚线,点间隔 -
dashed
- 虚线,短横线间隔 -
wavy
- 波浪线
先看看效果,下面是代码:
image
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,不过我就试了试图片
我不做过多解释,代码会帮我解释的
image
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 资源文件夹里,这种思路也是最推荐的,绝不会出任何问题的
image
-
tts 字体文件路径:
image -
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,会按照黑白来显示
image
阿里图片原样:
image
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 个 编码点
扇区图:没一个大块就是一个扇区
image
不同语言的字符,包括古今文字,上古语言,符号,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}";
image
显示多个,带文字混排
Runes input = new Runes(
'\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d} 哇哈哈哈哈!!!');
var index = String.fromCharCodes(input);
image
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);
网友评论