Flutter UI - text 系 Widget

image

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 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正常,另外还提供了预制值:从W100W900

image

boldnormal也是对应了其中的一个值

  /// 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: [
        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: [
        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: [
        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: [
        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 一样

image
class DD extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return DefaultTextStyle(
      style: TextStyle(
        color: Colors.lightBlue,
        fontSize: 30,
      ),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          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:TextSpanWidgetSpan

  • TextSpan - 配合 textStyle 实现各种文字效果,可以添加点击事件
  • WidgetSpan - 可以添加其他类型的 widget,不过我就试了试图片

我不做过多解释,代码会帮我解释的


image
class DD extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        RichText(
          text: TextSpan(
            children: [
              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,
                ),
              ),
            ],
          ),
        ),
      ],
    );
  }
}

想要文本和图片混用的话,必须同时支持:TextSpanWidgetSpan,RichText 的 text 属性我们一般都是给一个 TextSpan,这个 TextSpan 只代表富文本 viewTree,具体的富文本怎么样还得看我们在 children 里面怎么写。TextSpan 的 children[] 要图文混用的话,泛型需要写 TextSpanWidgetSpan 公共父类:InlineSpan,只是文字的话泛型写 TextSpan 就 OK 了

蛋疼的是点击对象类型 TapGestureRecognizer 得自己导包

import 'package:flutter/material.dart';

然后介绍下其他的富文本思路:

  1. 闲鱼团队自己实现了富文本 widget,具体请看:
  • 如何低成本实现Flutter富文本,看这一篇就够了!
  1. 自己跳动开源的插件:RealRichText,具体使用了官方的其实一样,详情请看:
  • 字节跳动开源的 Flutter 富文本展示效果

我说这两个的目的是为了开拓大家的眼界,看看别人的自定义方案,Flutter 毕竟是个新东西,自定义 view 还是需要重新熟悉走起的


更换字体

换字体这是 UI 最爱干的事,每个 UI 必有自己独爱的字体

1. asset 字体

一般我们都是把字体内置在 app 的 asset 资源文件夹里,这种思路也是最推荐的,绝不会出任何问题的


image
  1. tts 字体文件路径:


    image
  2. yaml 配置 tts 字体,注意一个也不能写错

flutter:

  uses-material-design: true

  # 图片资源配置
  assets:
    - assets/icons/

  fonts:
    - family: font1
      fonts:
        - asset: assets/fonts/font_fangzheng_duhei.ttf
  1. 在 TextStyle 使用字体
class EE extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        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 卡里,这个暂时没研究,大家看下别人的方案:

  • flutter 使用自定义字体

阿里巴巴矢量图标字体

这个大家在 android 里应该都熟悉,阿里巴爸爸矢量图可以打包成ttf字体格式下载下来,当做字体来显示,原理和emoil表情一样

不过实测过后发现 Flutter 不支持阿里矢量库里的彩色 icon,会按照黑白来显示


image

阿里图片原样:


image

ttf 文件放置:

image

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: [
        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+0000U+10FFFF,分成14个扇区存储,每一个扇区有 256 个小块,每个小块有 16×16 = 256 个编码点,总体下来每个扇区有 65536 个 编码点

扇区图:没一个大块就是一个扇区


image

不同语言的字符,包括古今文字,上古语言,符号,emoji 都存储在不同的扇区内

  • Unicode 0 号平面(0000-FFFF)
  • Unicode 1 号平面(10000-1FFFF)

Unicode 所有的字符可以在官方网站上查询到:

  • 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

image

我们来看下:

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);

你可能感兴趣的:(Flutter UI - text 系 Widget)