美文网首页Flutter
Flutter UI - text 系 Widget

Flutter UI - text 系 Widget

作者: 前行的乌龟 | 来源:发表于2019-08-28 23:38 被阅读0次
    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<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正常,另外还提供了预制值:从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: <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 一样

    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: <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:TextSpanWidgetSpan

    • 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,
                    ),
                  ),
                ],
              ),
            ),
          ],
        );
      }
    }
    

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

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

    import 'package:flutter/material.dart';
    

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

    1. 闲鱼团队自己实现了富文本 widget,具体请看:
    1. 自己跳动开源的插件:RealRichText,具体使用了官方的其实一样,详情请看:

    我说这两个的目的是为了开拓大家的眼界,看看别人的自定义方案,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: <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 文件放置:

    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: <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+0000U+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

    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

        本文链接:https://www.haomeiwen.com/subject/lvueectx.html