第5章 文本显示Text组件 在Flutter中,Text组件用于显示文本,类似 Android 里的TextView,以及iOS里的UILabel,最简单的使用方式可以直接用new Text这样的代码来展示文本,代码如下: new Text('这里是文本') 5.1文本显示组件的基本使用 在实际应用项目开发中,文本显示是视图内容的主要表现方式之一,Flutter中使用Text组件来显示文本,如图51所示,页面的AppBar使用了Text,页面的主体内容也使用了Text,代码如下: //5.2 /lib/code3/main_data66.dart //文本显示组件Text Widget buildBodyFunction() { return Scaffold( appBar: AppBar( title: Text("这是一个标题"), ), body: Center(child: Text("内容主体"),),); } 图51Text的基本使用 对于文本组件Text来讲,创建及使用比较简单,直接使用new Text()就可创建,在实际项目开发中,每一行显示的文本都有对应的样式来配置显示,在这里,通过new Text()来创建文本组件,Text的属性style用来配置当前文本的样式,style属性配置的是样式组件TextStyle。 5.2样式组件TextStyle的使用分析 在文本显示组件Text中,TextStyle用来配置显示的文本样式,如图52所示AppBar中title配置的文本Text的字体颜色为红色,实现代码如下: //5.2.2 /lib/code3/main_data67.dart //文本显示组件Text Widget buildBodyFunction() { return Scaffold( appBar: AppBar( title: Text( "这是一个标题", style: TextStyle( //用来配置文字的颜色 color: Colors.red, ), ), ), body: Center(child: Text("内容主体"),),); } 图52Text文本的颜色配置使用 通过TextStyle,可以配置文本的颜色,同样也可以设置文字大小、斜体、粗细等一系列样式,如表51所示,列出了TextStyle属性配置说明。 表51TextStyle属性配置说明 取值类型说明 inheritbool为true时,表示把当前样式合并defaultStyle样式一起使用 colorColor文本的颜色 backgroundColorColorText文本的背景色 fontSizedouble字体大小配置 fontWeightFontWeight字体粗细配置 fontStyleFontStyle字体样式配置,如常规体、斜体 letterSpacingdouble字符之间的间隔 wordSpacingdouble单词之间的间隔 textBaselineTextBaseline文本绘制时对齐的基线 heightdouble文本的高度 foregroundPaint通过Paint来设置文本的颜色,不能与color属性同时配置,属于互斥关系 backgroundPaint用来绘制Text文本的背景颜色,不能与backgroundColor同时配置 shadowsList字体阴影设置 decorationTextDecorationText的装饰线配置,如下画线、删除线 decorationColorColor装饰线的颜色配置 decorationStyleTextDecorationStyle装饰线的样式,如波浪式、实线、虚线等 decorationThicknessdouble装饰线的粗细配置 debugLabelString此属性仅在调试构建中维护,配置对文本样式可读的描述,用来辅助理解,目前在开发中无实际用处 fontFamilyString字体设置 fontFamilyFallbackfontFamilyFallbackfontFamily字体加载后,优先选择从这里配置的字体 5.2.1样式组件TextStyle的inherit 在英文的意思中,inherit为继承,在这里可理解为是否继承父组件的属性样式,默认为true,在Text里使用时,默认会加载一个默认样式DefaultTextStyle,文本样式为白色,10像素,sansserif 字体,如果设置inherit为true,程序在执行时会把默认样式DefaultTextStyle与当前设置的文本样式进行合并,Text使用样式的部分源码如下: ...//省略 if (style == null || style.inherit) effectiveTextStyle = defaultTextStyle.style.merge(style); ...//省略 如图53所示Text设置了inherit一个为true,另一个为false的效果图,对应的代码如下: //5.2.2 /lib/code3/main_data67.dart //文本显示组件Text Widget buildBodyFunction3() { return Scaffold( appBar: AppBar( title: Text( "这是一个标题" ), ), body: Container(child: Center( child: Column(children: [ Text("inherit: true", style: TextStyle( //文字的大小 fontSize: 16, inherit: true,),), Text("inherit: false ", style: TextStyle( //文字的大小 fontSize: 16, inherit: false,),), ],),),color: Colors.grey,)); } 图53inherit 属性的配置 5.2.2样式组件TextStyle的颜色配置 属性color用来设置文字的颜色,backgroundColor用来设置Text的背景,是一个Color类型的配置。background也是用来设置Text的背景,是一个Paint类型的配置,在TextStyle中,两者是互斥的,不可同时使用。 对于background与backgroundColor来讲,在CSS中,background可以设置背景颜色、背景图片等,而backgroundColor只能设置一种颜色,同理TextStyle中的backgroundColor只能配置一种Color,而background可以通过Paint绘制比较复杂的背景样式。 5.2.3文字大小fontSize TextStyle的fontSize是用来配置文字显示大小的,是一个double类型的配置,一般使用时的代码如下: Text("设置文字大小", style: TextStyle(fontSize: 16.0,), 这里设置的16.0是逻辑像素大小,当TextStyle没有指定fontSize时,默认值是14.0。 设备独立像素,device independent pixel,简称dip,在CSS中,有 CSS像素 =设备独立像素 = 逻辑像素 在Flutter中有 Flutter像素 =设备独立像素 = 逻辑像素 在分析尺寸时,首先需要明确一些概念,如表52所示。 表52像素概念简述 类别简称全称 物理像素dpphysical pixel 逻辑像素/设备独立像素dipdevice independent pixels 设备像素比dprdevice pixel ratio 每英寸的像素dpidots per inch 屏幕像素密度ppipixel per inch 物理像素又被称为设备像素,单位就是用户常说的pixel,简写成 PX,物理像素也就是实际手机中所描述的屏幕分辨率,如图54所示,一个设备的物理像素在手机出厂时就固定了。 逻辑像素或者称为设备独立像素,通常也被称设备无关像素,单位是 PT,可以认为逻辑像素是程序运行平台坐标系统的一个点,这个点代表一个可以由程序使用的虚拟像素,在实际开发中,所提到的一倍屏、 图54手机中的屏幕尺寸效果图 二倍屏、三倍屏,指的是一个虚拟像素可以由多少个物理像素来表示,一倍屏就是用一个物理像素来表示一个虚拟像素,多倍屏就是由多个物理像素来表示一个虚拟像素。 设备像素比,在手机平台中,Android程序在开启第1个Activity前,或者Android程序在启动时会先创建一个window,然后在window中渲染Activity。在iOS中,在启动的时候开发者常常也会创建一个自定义的window来渲染view,对于window,有一个devicePixelRatio属性,官方的解释为: 设备物理像素和设备独立像素的比例,也就是说设备像素比,devicePixelRatio=物理像素/独立像素。 在Flutter中,这里设置的fontSize大小为16.0,那么它实际在不同平台不同手机上显示出来的物理像素应该是16.0与devicePixelRatio的乘积,在Flutter中,可通过MediaQuery来获取devicePixelRatio的值,以及屏幕尺寸相关的值,代码如下: //5.2.2 /lib/code3/main_data67.dart //文本显示组件Text Widget buildBodyFunction5() { //获取devicePixelRatio double devicePixelRatio = MediaQuery.of(context).devicePixelRatio; //获取当前Widget的宽度,因为是填充,所以可理解为是屏幕宽度 double width = MediaQuery.of(context).size.width; //屏幕高度 double height = MediaQuery.of(context).size.height; //日志输出 print("当前设备的 width为 $width height为 $height devicePixelRatio为$devicePixelRatio"); //定义 double fontSize =16.0; //设置中的文字的实际大小计算 double practicalFontSize = fontSize * devicePixelRatio; print("设备中的实际文字大小为 $practicalFontSize"); ...//省略 } 运行程序,输出的日志信息如下: Performing hot reload... Syncing files to device iPhone 11 Pro Max... flutter: 当前设备的 width为 414.0 height为 896.0 devicePixelRatio为3.0 flutter: 设备中的实际文字大小为 48.0 Reloaded 1 of 499 libraries in 300ms. 5.2.4文字粗细设置fontWeight 属性fontWeight用来设置文字的粗细,接收的值是FontWeight枚举类型,基本使用代码如下: Text("设置文字的字体规格", style: TextStyle( //文字的大小 fontSize: 16, //设置文字的粗细规则为常规体 fontWeight: FontWeight.normal ),), 在这里调用了FontWeight.normal,直接取用的值为FontWeight.w400的值,FontWeight.w400是程序默认使用的常规字体,代码如下: //The default font weight. static const FontWeight normal = w400; FontWeight所定义的取值范围如下: //A list of all the font weights. static const List values = [ w100, w200, w300, w400, w500, w600, w700, w800, w900 ]; 其中w100为最细的字体,w400为常规的字体,w900为粗体。 5.2.5文字斜体设置 在TextStyle中,属性fontStyle用来设置文字是否为斜体,取值为FontStyle.normal常规体或者FontStyle.italic斜体,基本使用代码如下: Text("文字斜体设置", style: TextStyle( //文字的大小 fontSize: 16, //设置文字的粗细规则为常规体 fontWeight: FontWeight.normal, //设置文字为斜体 fontStyle: FontStyle.italic ),), 5.2.6文字间距设置 使用Text显示文本,文本中包含中文与英文,如图55所示,代码如下: //5.2.7 /lib/code3/main_data68.dart //文字间距设置 Widget buildBodyFunction4() { return Scaffold( appBar: AppBar( title: Text("Text组件使用分析"),), body: Container(child: Center( child: Column(children: [ Text("天高水长,小编与你奋斗每一天, Small make up and you struggle every day", style: TextStyle( letterSpacing: 12.0, wordSpacing: 20.0, ),), ] ),),),); } 图55图文混合显示效果图 文本样式TextStyle中的letterSpacing属性用来配置字符与字符之间的间隔,wordSpacing用来配置单词之间的间隔,如图56所示效果是指定这两个属性后运行程序结果,代码如下: //5.2.7 /lib/code3/main_data68.dart //文字间距设置 Widget buildBodyFunction4() { return Scaffold( appBar: AppBar( title: Text("Text组件使用分析"),), body: Container(child: Center( child: Column(children: [ Text("天高水长,小编与你奋斗每一天, Small make up and you struggle every day", style: TextStyle( //字符之间的间隔 letterSpacing: 12.0, //单词之间的间隔 wordSpacing: 32.0, ),), ] ),),),); } 图56设置字符和单词间隔显示效果图 5.2.7文字基线textBaseline分析 文本样式TextStyle中的textBaseline用来设置绘制文本时的当前文本基线,关于文本基线的分析如图57所示,在Flutter中使用代码如下: Text("天高水长,小编与你奋斗每一天,Small make up and you struggle every day", style: TextStyle( //设置文字绘制对齐的基线 textBaseline: TextBaseline.alphabetic ),), 图57文本绘制基线效果图 在Flutter的取值与描述代码如下: //A horizontal line used for aligning text. enumTextBaseline { //The horizontal line used to align the bottom of glyphs for alphabetic characters. alphabetic, //The horizontal line used to align ideographic characters. ideographic, } 所以在实际开发中,这里的textBaseline作用不大。 5.2.8装饰decoration分析 在英文中decoration的意思为装饰,在Text的TextStyle中,其实就是在文字上绘制一些线,例如下画线和删除线,默认的TextStyle是不使用装饰的,代码如下: //5.2.2 /lib/code3/main_data70.dart //文本显示组件Text样式TextStyle中decoration Widget buildBodyFunction() { return Scaffold( appBar: AppBar( title: Text( "Text文本样式" ),), body: Container( margin: EdgeInsets.all(30.0), child: Column(children: [ Text("inherit: true", style: TextStyle( //文字的大小 fontSize: 22, //设置无装饰样式 decoration: TextDecoration.none ),), ] ),)); } 这里decoration配置的是枚举TextDecoration类型,取值效果如图58所示,源码说明如下: //无样式 static const TextDecoration none = TextDecoration._(0x0); //在每行文本下面画一条线 static const TextDecoration underline = TextDecoration._(0x1); //在每行文本上面画一条线 static const TextDecoration overline = TextDecoration._(0x2); //在每行文本上画一条线 static const TextDecoration lineThrough = TextDecoration._(0x4); 图58TextDecoration各取值效果图 如图59所示效果,可以通过TextDecoration提供的方法combine同时设置文本的上画线overline与下画线underline,或者如图58所示的任意的组合效果,代码如下: //5.2.2 /lib/code3/main_data70.dart //文本显示组件Text样式TextStyle中decoration 组合多组装饰样式 Widget buildBodyFunction2() { return Scaffold( appBar: AppBar( title: Text( "Text文本样式" ),), body: Container( margin: EdgeInsets.all(30.0), child: Column(children: [ Text("inherit: true", style: TextStyle( //文字的大小 fontSize: 22, //设置文本的装饰样式 //TextDecoration.combine接收的是一个List参数,用来组合多个样式 decoration: TextDecoration.combine([ TextDecoration.overline, TextDecoration.underline, ]), ),), ] ),)); } 图59TextDecoration组合使用效果图 属性TextDecoration设置的装饰线还可以通过各种decoration相关属性来配置颜色等样式,如图510所示中文字中间的删除线为红色的双线样式,代码如下: //5.2.2 /lib/code3/main_data70.dart //文本显示组件Text样式TextStyle中decoration 的样式 Widget buildBodyFunction() { return Scaffold( appBar: AppBar(title: Text("Text文本样式"),), body: Container( margin: EdgeInsets.all(30.0), child: Column(children: [ Text("inherit: true", style: TextStyle( //文字的大小 fontSize: 25, //设置中间通过装饰样式 decoration: TextDecoration.lineThrough, //配置中间通过线为红色 decorationColor: Colors.red, //双线样式 decorationStyle: TextDecorationStyle.double, ),), ] ),)); } 图510TextDecoration设置样式效果图 TextDecorationStyle用来配置装饰线的样式,可取值如表53所示,效果对比如图511所示。 表53TextDecorationStyle属性取值说明 取值说明 TextDecorationStyle.solid单线,实心线 TextDecorationStyle.double双线,实心线 TextDecorationStyle.dotted单线,点点虚线 TextDecorationStyle.dashed单线,短横线,虚线 TextDecorationStyle.wavy单线,连续波浪线 图511TextDecorationStyle取值对比 如图512所示效果,可通过TextStyle的属性decorationThickness来设置装饰线的粗细,它接收的是double类型,使用代码如下: //5.2.9 /lib/code3/main_data70.dart //文本显示组件Text样式TextStyle中decoration 的粗线 Widget buildBodyFunction3() { return Scaffold( appBar: AppBar( title: Text( "Text文本样式" ),), body: Container( margin: EdgeInsets.all(30.0), child: Column(children: [ Text("inherit: true", style: TextStyle( //文字的大小 fontSize: 25, //设置中间通过装饰样式 decoration: TextDecoration.lineThrough, //配置中间通过线为红色 decorationColor: Colors.red, //双线样式波浪线 decorationStyle: TextDecorationStyle.wavy, //加粗 decorationThickness: 3, ),), ] ),));} 图512TextDecoration的粗细设置 5.2.9自定义字体fontFamily配置 在实际项目开发中,使用字体配置,可实现多样式美化效果,如图513所示,使用圆滑的字体样式,一般这样的字体在手机中是不支持的,所以需要在应用内配置字体。 在Flutter项目中,使用自定义的字体,首先需要配置自定义字体,第1步,导入字体样式文件ttf或者otf文件,在Flutter项目的根目录下,创建fonts文件夹,然后将字体文件复制到fonts文件夹,如图514所示。 图513自定义字体使用效果 图514配置自定义字体项目目录结构图 第2步,在Flutter项目配置文件pubspec.yaml中配置fonts文件夹中已导入的字体,代码如下: //pubspec.yaml配置文件 #配置图片的目录结构 assets: - images/2.0/ - images/3.0/ #配置字体的目录结构 fonts: - family: Schyler fonts: - asset: fonts/SourceHanSansCN-Bold.otf - family: Medium fonts: - asset: fonts/SourceHanSansCN-Medium.otf - family: UniTortred fonts: - asset: fonts/UniTortred.ttf 在pubspec.yaml文件中,fonts节点用来配置自定义字体文件,family指定的是在TextStyle中fontFamily引用的名字,asset配置的是对应字体文件的路径与文件名称,如果需要配置多个自定义字体,可以依次向下排列。 在pubspec.yaml文件中,一定要注意空格的缩进,不能多也不能少,如图515所示。在Text组件中通过fontFamily属性引用pubspec.yaml文件中配置的family所对应的别名就可使用自定义字体,代码如下: //5.2.10 /lib/code3/main_data70.dart //文本显示组件Text样式TextStyle //字体fontFamily配置 Widget buildBodyFunction4() { return Scaffold( appBar: AppBar( title: Text( "Text文本样式" ),), body: Container( margin: EdgeInsets.all(30.0), child: Column(children: [ Text("UniTortred字体", style: TextStyle( //文字的大小 fontSize: 25, //引用圆滑的自定义字体 fontFamily: "UniTortred" ),), ] ),)); } 图515配置文件自定义字体配置分析说明 5.2.10字体列表fontFamilyFallback配置 对于fontFamilyFallback属性来说,它接收的配置是一个List,这个List定义的是应用程序内支持的字体配置,配置代码如下: Text("Regular 字体", style: TextStyle( //文字的大小 fontSize: 25, //引用圆滑的自定义字体 fontFamily: "Regular", //支持的字体列表 fontFamilyFallback: ['Ubuntu', 'Cantarell', 'DejaVu Sans', 'Liberation Sans', 'Arial'], ),), 也就是说当fontFamily中配置了指定加载的字体时,应用程序会优先加载这个字体,如果找不到或者加载失败,那么系统会加载fontFamilyFallback列表中配置的字体,如果还是找不到对应的字体文件或者加载失败,就使用当前系统默认的字体样式。 对fontFamilyFallback的官方源码解析如下: //The ordered list of font families to fall back on when a glyph cannot be //found in a higher priority font family. //The value provided in [fontFamily] will act as the preferred/first font //family that glyphs are looked for in, followed in order by the font families //in [fontFamilyFallback]. If all font families are exhausted and no match //was found, the default platform font family will be used instead. // //When [fontFamily] is null or not provided, the first value in [fontFamilyFallback] ...//省略 //大致的意思就是如果描述系统在优先级高的family配置的字体中找不到时,会优先加载 //fontFamilyFallback这个列表中配置的字体 5.2.11字体阴影shadows配置 在TextStyle中,通过shadows配置字体文本的阴影,如图516所示,代码如下: //5.2.11 /lib/code3/main_data70.dart //文本显示组件Text样式TextStyle //阴影样式配置 Widget buildBodyFunction5() { return Scaffold( appBar: AppBar(title: Text("Text文本样式"),), body: Container( margin: EdgeInsets.all(30.0), child: Column(children: [ Text("Regular 字体", style: TextStyle( //文字的大小 fontSize: 25, //引用圆滑的自定义字体 fontFamily: "Regular", //配置字体阴影 shadows:[ BoxShadow( //阴影的颜色 color: Color(0xffff0000), //偏移量 offset: Offset(2,1), //模糊度 spreadRadius: 5.0, ) ], ),), ] ),)); } 图516字体阴影配置效果图 在这里的shadows接收的是一个List集合,对于图516所示效果的代码片段buildBodyFunction5中使用了BoxShadow,它继承于ui.Shadow,对于阴影偏移量offset,构造Offset(x,y),x设置的是阴影相对于当前位置在x轴上的偏移量,y设置的是阴影相对于当前位置在y轴上的偏移量。 5.2.12文本高度height配置 对于TextStyle中的height属性来说,可以简单理解为文本高度,这里设置的height可影响实际中Text文本的高度,实际中Text文本的高度等于height与fontSize的乘积。不指定时,Text在显示多行文本时,有默认的文本高度,当指定为1.0时,文本的高度就与fontSize的大小一致,下面会详细分析。 在源码中描述如下: //The height of this text span, as a multiple of the font size. // //When [height] is null or omitted, the line height will be determined //by the font's metrics directly, which may differ from the fontSize. //When [height] is non-null, the line height of the span of text will be a ...//省略 //![Text height comparison diagram](https://flutter.github.io/assets-for-api-docs/assets/painting/text_height_comparison_diagram.png) // //See [StrutStyle] for further control of line height at the paragraph level. final double height; 读者可以注意到,在案例中使用的Text的内容文本大部分是单行的。如图517所示,是Text显示多行文本的效果,对应代码如下: //5.2.11 /lib/code3/main_data70.dart //文本显示组件Text样式TextStyle //多行文本显示 Widget buildBodyFunction6() { return Scaffold( appBar: AppBar( title: Text( "Text文本样式" ),), body: Container( margin: EdgeInsets.all(30.0), child: Column(children: [ Text("人生就有许多这样的奇迹,看似比登天还难的事, 有时轻而易举就可以做到,其中的差别就在于非凡的信念", style: TextStyle( //文字的大小 fontSize: 25, ),), ] ),)); } 图517多行文本显示视图效果 在图517的效果中,没有指定TextStyle中的height。当指定height为1.0时,效果如图518右图所示。 图518height为null与1.0的效果对比图 如图519所示,当height没有指定时,默认值为null,实际文本的高度为左侧的Font metrics。当指定height时,实际文本的高度为右侧的EMsquare标准,也就是指定height的值后,文本的高度为height与FontSize的乘积。 图519字体阴影配置效果图 如图520所示分析效果,是源自于谷歌官方提供的文字height距离分析图。在使用TextStyle时,当指定的字体大小为50并且指定height为10时,当前文本的高度为500px。当不指定height时,当前文本高度为59.0px,结合图58中提到文本绘制的各种基线,在这里前两者相差9.0px是top基线到文本之间的间隔。指定height后,文本实际的高度为height与fontSize的乘积。 图520指定不同height值显示文本高度效果对比图 5.3Text中文字对齐方式 在Text组件中通过textAlign配置文字的对齐方式,取值类型为TextAlign枚举类型,可取值如表54所示。 表54文本对齐方式取值简述 类别全称 TextAlign.left左对齐 TextAlign.right右对齐 TextAlign.center居中对齐 TextAlign.justify拉伸结束文本行以填充容器的宽度,即使用decorationStyle才生效 TextAlign.start开始方向对齐 TextAlign.end结束方向对齐 如图521所示效果,为程序默认的文字对齐方式,在这里显示的是左对齐,其实默认的文字对齐方式是TextAlign.start,如图522所示是设置文字居中对齐的效果,textAlign的基本使用代码如下: //5.3 /lib/code3/main_data71.dart //文本对齐方式textAlign Widget buildBodyFunction() { return Scaffold( appBar: AppBar( title: Text( "Text文本样式" ),), body: Container( margin: EdgeInsets.all(30.0), child: Column(children: [ Text("人生就有许多这样的奇迹,看似比登天还难的事, 有时轻而易举就可以做到,其中的差别就在于非凡的信念", //居中对齐 textAlign: TextAlign.center, style: TextStyle( //文字的颜色 foreground:Paint()..color=Colors.blue, //文字的大小 fontSize: 16, ),), ] ),)); } 图521文本的默认对齐方式 图522文本的居中对齐方式 在对齐方式中,left、center、right都容易理解,分别为左对齐、居中、右对齐。对于TextAlign.start与TextAlign.end来讲,需要结合文本的绘制方向textDirection来分析,textDirection有两个取值,一个是TextDirection.ltr从左向右,另一个是TextDirection.rtl从右向左,在中文环境下,文本绘制方向是从左向右,此时textDirection的方向就是ltr值,这种情况与left左对齐效果是一致的,如图523所示。 图523默认情况下的文字方向与文本对齐 图523所示效果代码如下: //5.3 /lib/code3/main_data71.dart //文本对齐,文字方向从左向右以开始位置对齐 Widget buildBodyFunction3() { return Scaffold( ...//省略 body: Container( margin: EdgeInsets.all(30.0), child: Column(children: [ Container( //设置Text的宽度 width: MediaQuery.of(context).size.width, child: Text("人生就有许多", textDirection: TextDirection.ltr, //开始位置对齐 textAlign: TextAlign.start, style: TextStyle( //文字的颜色 foreground: Paint()..color = Colors.blue, //文字的大小 fontSize: 16, ),),), ] ),)); } 图524文字方向从左向右以end方式对齐 图524所示效果代码如下: //5.3 /lib/code3/main_data71.dart //文本对齐,文字方向从左向右以结束位置对齐 Widget buildBodyFunction4() { return Scaffold( ...//省略 body: Container( margin: EdgeInsets.all(30.0), child: Column(children: [ Container( //设置Text的宽度 width: MediaQuery.of(context).size.width, child: Text("人生就有许多", textDirection: TextDirection.ltr, //结束位置对齐 textAlign: TextAlign.end, style: TextStyle( //文字的颜色 foreground: Paint()..color = Colors.blue, //文字的大小 fontSize: 16, ),),), ] ),)); } 图525所示代码如下: //5.3 /lib/code3/main_data71.dart //文本对齐,文字方向从右向左以开始位置对齐 Widget buildBodyFunction5() { return Scaffold( ...//省略 body: Container( margin: EdgeInsets.all(30.0), child: Column(children: [ Container( //设置Text的宽度 width: MediaQuery.of(context).size.width, child: Text("人生就有许多", textDirection: TextDirection.rtl, //开始位置对齐 textAlign: TextAlign.start, style: TextStyle( //文字的颜色 foreground: Paint()..color = Colors.blue, //文字的大小 fontSize: 16, ),),), ] ),)); } 图525文字方向从右向左以start方式对齐 图526所示效果代码如下: //5.3 /lib/code3/main_data71.dart //文本对齐,文字方向从右向左以结束位置对齐 Widget buildBodyFunction6() { return Scaffold( ...//省略 body: Container( margin: EdgeInsets.all(30.0), child: Column(children: [ Container( //设置Text的宽度 width: MediaQuery.of(context).size.width, child: Text("人生就有许多", textDirection: TextDirection.rtl, //结束位置对齐 textAlign: TextAlign.end, ,),), ] ),)); } 图526文字方向从右向左以end方式对齐 5.3.1文字过长显示省略号 在实际项目开发中,常常会遇到在设计中最多只显示一行字体,当文字过长时,在末尾显示省略号,这样在视觉上会达成一个好的效果。 在Android项目中文字显示TextView中添加两个配置就可以达到这样的设计效果,代码如下: android:ellipsize="end" android:lines="1" 在iOS中将文本显示UILabel设定显示的行数numberOfLines后,再设置UILabel的lineBreakMode属性为NSLineBreakByTruncatingTail,也可以达到预期的效果。 在Flutter中,则可通过设置Text组件的overflow与maxLines来达到预期效果。如图527所示为在限定Text只显示一行时,配置overflow后的效果,对应代码如下: //5.3 /lib/code3/main_data71.dart //文本超出后显示省略号 Widget buildBodyFunction7() { return Scaffold( appBar: AppBar( title: Text( "Text文本样式" ),), body: Container( margin: EdgeInsets.all(30.0), child: Column(children: [ Container( //设置Text的宽度 width: MediaQuery.of(context).size.width, height: 20, child: Text("人生就有许多这样的奇迹,看似比登天还难的事, 有时轻而易举就可以做到,其中的差别就在于非凡的信念", //设置省略号 overflow: TextOverflow.ellipsis, //设置最大显示行数 maxLines: 1, style: TextStyle( //文字的颜色 foreground: Paint()..color = Colors.blue, //文字的大小 fontSize: 18, ),),), ] ),)); } 图527文字超出字数后显示省略号效果图 TextOverflow是一个枚举类型,其各取值类型效果如图528所示,代码如下: enumTextOverflow { //Clip the overflowing text to fix its container. //超出宽度的文本部分直接裁剪 clip, //Fade the overflowing text to transparent. //超出宽度的文本部分以透明渐变方式结尾 fade, //Use an ellipsis to indicate that the text has overflowed. //超出宽度的文本内容部分以省略号的方式结尾 ellipsis, //Render overflowing text outside of its container. //超出的部分照常显示,效果与clip的效果差不多,只不过clip直接裁剪掉了多余的部分 visible, } 图528TextOverflow效果示意图 5.3.2文字自动换行设置 如图529所示,在Flutter中可以通过设置Text组件的softWrap属性来控制是否自动换行。当softWrap为false时,Text中的文本超出父组件配置的宽度时,不会自动换行,而会直接被裁剪掉。 图529softWrap效果示意图 自动换行属性softWrap配置基本使用代码如下: Text("", //true 为自动换行,默认为true //false为不自动换行 softWrap:true, ), 在实际项目开发中,常会以如图530所示的布局效果进行开发,代码如下: //5.3 /lib/code3/main_data71.dart //常用布局 Widget buildBodyFunction9() { return Scaffold( appBar: AppBar( title: Text( "Text文本样式" ),), body: Container( margin: EdgeInsets.all(30.0), child: Row(children: [ Text("个性签名: ",style: TextStyle(fontWeight: FontWeight.w500),), SizedBox(width: 14,), Text("人生就有许多这样的奇迹") ] ),)); } 图530常用的文本布局排列 图530中右侧的文本显示得比较少时,可以正常显示。当把右侧的文本内容加多后,即使设置了softWrap为true自动换行,在程序实际运行中,依然没有换行。当实际绘制的文本宽度超出了父组件所允许的宽度时,则会出现如图531所示的异常效果,Android Studio的控制台输出的异常日志如下: ════════ Exception caught by rendering library ═════════ The following assertion was thrown during layout: A RenderFlex overflowed by 388 pixels on the right. The relevant error-causing widget was: Row file://Users/.../code/flutter_book_code/flutter_book_code/lib/code4/main_data71.dart:267:18 The overflowing RenderFlex has an orientation of Axis.horizontal. The edge of the RenderFlex that is overflowing has been marked in the rendering with a yellow and black striped pattern. This is usually caused by the contents being too big for the RenderFlex. Consider applying a flex factor (e.g. using an Expanded widget) to force the children of the RenderFlex to fit within the available space instead of being sized to their natural size. This is considered an error condition because it indicates that there is content that cannot be seen. If the content is legitimately bigger than the available space, consider clipping it with a ClipRect widget before putting it in the flex, or using a scrollable container rather than a Flex, like a ListView. The specific RenderFlex in question is: RenderFlex#2a2a0 relayoutBoundary=up2 OVERFLOWING ...parentData: offset=Offset(30.0, 30.0) (can use size) ...constraints: BoxConstraints(0.0<=w<=354.0, 0.0<=h<=736.0) ...size: Size(354.0, 20.0) ...direction: horizontal ...mainAxisAlignment: start ...mainAxisSize: max ...crossAxisAlignment: center ...textDirection: ltr ...verticalDirection: down ◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤ 图531文本超出限定宽度后异常 在异常日志信息中,也提示出了异常的原因描述及解决方案,日志中提示,可以使用flex布局来解决这种情况。在CSS中,2009年W3C提出了弹性布局flex这一概念,在Flutter中,flex布局理念与W3C的flex理念一致,与Android中的线性布局使用weight 属性进行权重布局排列的理念一致。 如图532所示效果,使用弹性布局Flexible或者Expanded来解决显示多余文本的超出限制问题。 图532使用弹性布局的解决方案 图532所示效果对应的代码如下: //5.3 /lib/code3/main_data71.dart //弹性布局解决文本超出限制宽度问题 Widget buildBodyFunction10() { return Scaffold( appBar: AppBar( title: Text( "Text文本样式" ),), body: Container( margin: EdgeInsets.all(30.0), child: Row( //内容顶部对齐 crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("个性签名: ", style: TextStyle(fontWeight: FontWeight.w500),), SizedBox(width: 14,), //使用弹性布局 Flexible(child: Text("人生就有许多这样的奇迹,看似比登天还难的事,有时轻而易举就可以做到,其中的差别就在于非凡的信念"),) ] ),)); } 5.3.3弹性布局综述 对于弹性布局来讲,在Flutter中有Flexible、Expanded、Spacer这3个Widget,Spacer常用于调节两个Widget之间的间距。 Expanded用在Row、Column、Flex,它会限定在这些Widget的主轴方向上占满剩余的可用空间,如图533所示效果,Expanded所修饰的内容区域的宽度会自动占满在水平方向上除标题文字以外的所有空间,代码如下: //5.3 /lib/code3/main_data72.dart //弹性布局 Expanded Widget buildBodyFunction() { return Scaffold( appBar: AppBar( title: Text( "Text文本样式" ),), body: Container( margin: EdgeInsets.all(30.0), child: Row(children: [ Text("标题",), Expanded(child: Container(color: Colors.grey,height: 20,),) ] ),)); } 图533Expanded 权重适配 如图534中,通过Expanded进行权重适配,使用Row的两个子Widget在水平方向上平分Row的宽度,对应代码如下: //5.3 /lib/code3/main_data72.dart //弹性布局 Expanded Widget buildBodyFunction2() { return Scaffold( appBar: AppBar( title: Text( "Text文本样式" ),), body: Container( margin: EdgeInsets.all(30.0), child: Row(children: [ Expanded(child: Text("标题",),), Expanded(child: Container(color: Colors.grey,height: 20,),) ] ),)); } 图534Expanded 权重适配平分宽度 如图535所示,通过设置Expanded的flex属性按照比例分配Row在水平方向上的宽度,在这里,配置左侧文本的Expanded的flex为1,右侧Container的Expanded的flex为2,那么整体来讲,应用程序会把Row主方向也就是水平方向的宽度平均分成3份,然后根据flex配置的值再分配给每个Expanded对应的宽度,代码如下: //5.3 /lib/code3/main_data72.dart //弹性布局 Expanded Widget buildBodyFunction3() { return Scaffold( appBar: AppBar( title: Text( "Text文本样式" ),), body: Container( margin: EdgeInsets.all(30.0), child: Row(children: [ //水平方向上的空间占1份 Expanded(flex: 1,child: Text("标题",),), //水平方向上的空间占2份 Expanded(flex: 2,child: Container(color: Colors.grey,height: 20,),) ] ),)); } 图535Expanded 权重分配 Flexible允许子Widget弹性地使用它的空间,不必填满可用空间,它的构造函数代码如下: const Flexible({ Key key, this.flex = 1, this.fit = FlexFit.loose, @required Widget child, }) : super(key: key, child: child); Flexible比Expanded多了一个配置fit属性。 5.4富文本RichText组件的使用分析 在Flutter中,使用RichText实现多种文本风格的功能,类似Android中的SpannableString/SpannableStringBuilder为不同的文本片段指定不同的span; 类似iOS中的NSMutableAttributedString,通过向里面添加文字样式,然后通过控件的AttributedText赋值显示。 在Flutter中,RichText也有对应的TextSpan,TextSpan指定同一文本在不同区域的文字样式,如图536所示效果分析,对应的RichText基本使用代码如下: //5.4 /lib/code3/main_data73.dart //富文本的基本使用 RichText( //文字区域 text: TextSpan( text: "登录即代表同意",style: TextStyle(color: Colors.grey), children: [ TextSpan( text: "《用户注册协议》", style: TextStyle(color: Colors.blue) ), TextSpan( text: "与",style: TextStyle(color: Colors.grey), ), TextSpan( text: "《隐私协议》", style: TextStyle(color: Colors.blue) ) ] ), ), 图536RichText富文本的基本使用 正如图536所示效果,一行文字通过一个RichText组件实现,这个RichText组件包含了4个TextSpan文本片段,TextSpan的构造函数代码如下: const TextSpan({ //显示的文本 this.text, //可包含的子Widget this.children, //当前文本片段TextSpan的片段 TextStyle style, //手势识别 this.recognizer, //标签内容 this.semanticsLabel, }) : super(style: style,); RichText只有一个text属性配置它所需要显示的文本,而TextSpan的text属性用来配置当前文本片段显示的内容,它的属性children用来配置其他的TextSpan片段,可以理解为TextSpan可以无限地使用TextSpan嵌套下去。 TextSpan的recognizer用来配置当前显示的文本片段的手势识别功能,在实际项目开发中,如图536的效果,常用于监听用户对文本片段的单击事件,代码如下: //5.4 /lib/code3/main_data73.dart //富文本单击事件添加 RichText( //文字区域 text: TextSpan( text: "登录即代表同意",style: TextStyle(color: Colors.grey), children: [ TextSpan( text: "《用户注册协议》", style: TextStyle(color: Colors.blue), //单击事件 recognizer:TapGestureRecognizer()..onTap=(){ print("单击用户协议"); } ), TextSpan( text: "与",style: TextStyle(color: Colors.grey), ), TextSpan( text: "《隐私协议》", style: TextStyle(color: Colors.blue), //单击事件 recognizer:TapGestureRecognizer()..onTap=(){ print("单击隐私协议"); } ) ] ), ), TapGestureRecognizer与iOS中的UITapGestureRecognizer类似,与Android中的OnGestureListener手势监听类似,所以可以理解为在Flutter中TapGestureRecognizer也相当于设置了一个手势监听,因此建议在Widget页面创建时创建TapGestureRecognizer手势识别类,然后在页面销毁中注销TapGestureRecognizer手势监听,以此很好地控制资源的有效利用,代码如下: //5.4 /lib/code3/main_data74.dart //富文本中手势识别的综合使用 class _FirstPageState extends State { TapGestureRecognizer _registProtocolRecognizer; TapGestureRecognizer _privacyProtocolRecognizer; //生命周期函数页面创建时执行一次 @override void initState() { super.initState(); //注册协议的手势 _registProtocolRecognizer =TapGestureRecognizer(); //隐私协议的手势 _privacyProtocolRecognizer =TapGestureRecognizer(); } //生命周期函数页面销毁时执行一次 @override void dispose() { super.dispose(); //销毁 _registProtocolRecognizer.dispose(); _privacyProtocolRecognizer.dispose(); } @override Widget build(BuildContext context) { return buildBodyFunction(); } //5.4 /lib/code3/main_data74.dart //富文本中手势识别的综合使用 //在TextSpan中使用声明创建的手势识别监听 Widget buildBodyFunction() { ...//省略 TextSpan( text: "登录即代表同意",style: TextStyle(color: Colors.grey), children: [ TextSpan( text: "《用户注册协议》", style: TextStyle(color: Colors.blue), //单击事件 recognizer:_registProtocolRecognizer..onTap=(){ print("单击用户协议"); } ), TextSpan( text: "与",style: TextStyle(color: Colors.grey), ), TextSpan( text: "《隐私协议》", style: TextStyle(color: Colors.blue), //单击事件 recognizer:_privacyProtocolRecognizer..onTap=(){ print("单击隐私协议"); } ) ] ...//省略 } } 5.5富文本RichText使用案例 在实际项目开发中,一个常见的需求就是如果搜索框搜索出的列表显示内容中有与关键字相同的内容就使用高亮颜色显示,在6.7.3节搜索输入框使用案例会有详细的搜索框搜索逻辑使用分析,在这里实现将一段文本中的关键词筛选出来,然后高亮显示,代码如下: //lib/code4/main_data401.dart class RichTextTagPage extends StatefulWidget { @override _FirstPageStatecreateState() => _FirstPageState(); } class _FirstPageState extends State { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("文本标签"), ), body: Container( margin: EdgeInsets.all(30.0), //参数一为显示的文本段落 //参数二为筛选的关键词 child:RichTextTag("adadfafeaaefagcvae32455aa565eza","a"), )); } } 运行效果如图537所示,搜索关键字为字符a,在文本段落中检索出关键字,并使用高亮颜色蓝色标记。 图537RichText富文本显示高亮关键字 这里将用于筛选高亮显示内容的功能封装到RichTextTag中,代码如下: //lib/demo/rich_text_tag.dart //富文本标签,关键词高亮显示 class RichTextTagextends StatelessWidget{ //显示的内容文本 String contentText; //关键词 String keyWordText; RichTextTag(this.contentText, this.keyWordText); @override Widget build(BuildContext context) { return buildKeyWordRichText(contentText,keyWordText); } //包含关键词的内容高亮显示 //[title] 内容体 //[keyWord] 关键词 buildKeyWordRichText(String title, String keyWord) { List spans = []; spans.addAll(keyWordSpan(title, keyWord)); return RichText(text: TextSpan(children: spans)); } keyWordSpan(String word, String keyWord) { List spans = []; if (word == null || word.length == 0) return spans; //按关键词分隔 List strings = word.split(keyWord); for (int i = 0; i 0) { spans.add(TextSpan(text: value, style: TextStyle(color: Colors.grey))); } } return spans; } } 笔者已将RichTextTag封装在依赖库flutter_tag_layout中,在实际项目开发中,读者可直接添加此依赖库并使用,代码如下: flutter_tag_layout: ^0.0.3 在使用的地方导入依赖包的方式如下: import 'package:flutter_tag_layout/flutter_tag_layout.dart'; 然后就可以直接使用RichTextTag组件。 5.6文本标签 在实际项目开发中,一个常见的需求就是在搜索框下方会以文本标签的方式展示最近搜索的关键词记录,文本标签如图538所示。 图538流式布局展示文本标签 5.6.1文本标签构建 一个小的文本标签由两部分组成,一部分用于文本显示的Text组件,一部分用于装饰效果的Container,在这里,笔者将这部分代码封装到TextTagWidget中,并且已添加至依赖库flutter_tag_layout中,读者在使用时可添加依赖库,如5.6节所讲的添加方法添加依赖库后即可直接使用。 TextTagWidget继承于StatefulWidget,并且会设置一些默认使用的样式,代码如下: //lib/demo/text_tag_widget.dart class TextTagWidgetextends StatefulWidget{ //显示的文本 String text; //外边距 EdgeInsets margin; //内边距 EdgeInsets padding; //文本显示样式 TextStyletextStyle; //标签背景颜色 Color backgroundColor; //标签边框颜色 Color borderColor; //标签边框圆角 double borderRadius; TextTagWidget(this.text, {this.margin = const EdgeInsets.all(4), this.padding = const EdgeInsets.only(left: 6, right: 6, top: 3, bottom: 3), this.textStyle, this.backgroundColor, this.borderColor, this.borderRadius = 20.0}) { //定义边框的颜色 if (this.borderColor == null) { //当边框颜色没有指定时取用背景颜色 if (this.backgroundColor != null) { this.borderColor = this.backgroundColor; } else { //当背景颜色没有指定时取随机生成颜色 this.borderColor = getRandomColor(); } } //当没有指定文本样式时 if (this.textStyle == null) { //默认文本颜色与边框颜色一致 Color textColor = this.borderColor; //当指定了背景颜色时 //使用文本颜色为白色 if (backgroundColor != null) { textColor = Colors.white; } //创建使用的文本样式 this.textStyle = TextStyle(fontSize: 12, color: textColor); } //当背景颜色未指定时 //设置为透明色 if (this.backgroundColor == null) { this.backgroundColor = Colors.transparent; } } @override _TestPageStatecreateState() => _TestPageState(); } 然后构建具体的文本标签,在这里使用容器Container将文本Text包裹起来,代码如下: //lib/code/main_data.dart class _TestPageState extends State { @override Widget build(BuildContext context) { //构建边框装饰 return Container( margin: widget.margin, padding: widget.padding, decoration: BoxDecoration( color: widget.backgroundColor, borderRadius: BorderRadius.all(Radius.circular(widget.borderRadius)), border: Border.all(color: widget.borderColor)), child: buildTextWidget(), ); } //构建文本 Text buildTextWidget() { return Text( widget.text, style: widget.textStyle, textAlign: TextAlign.center, ); } } 图539默认样式的文本标签 最后直接使用TextTagWidget实现文本标签效果,如图539所示效果为默认生成的样式,文本颜色与边框颜色是随机生成的,当然也可以通过borderColor来指定边框的颜色。 图539对应的代码如下: //lib/code4/main_data401.dart //默认样式的文本标签 buildTextTagWidget() { return TextTagWidget("文本标签"); } 图540是设置了背景颜色的文本标签,此时默认的文本颜色为白色。 图540设置了背景颜色的文本标签 图540对应的代码如下: //lib/code4/main_data401.dart //指定了背景颜色的文本标签 buildTextTagWidget2() { return TextTagWidget( "文本标签", backgroundColor: Colors.brown, ); } 当分别指定边框颜色borderColor与背景颜色backgroundColor时,标签同时显示背景与边框颜色。 当需要指定标签中显示文本的颜色时,只需要重新指定textStyle属性并重新配置TextStyle即可。 5.6.2文本标签结合流式布局使用 如图538所示就是流式布局Wrap结合文本标签实现的效果,在实际项目开发中,只需要将这个效果放到搜索框的下方,然后把每次使用的关键词数据保存在本地磁盘中,这样就可以实现常见的搜索记录流式布局展示效果,代码如下: //lib/code4/main_data402.dart class TextWarpTagPage extends StatefulWidget { @override _FirstPageStatecreateState() => _FirstPageState(); } class _FirstPageState extends State { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("文本标签"), ), body: Container( margin: EdgeInsets.only(top: 30.0, left: 10, right: 10), //流式布局 child: Wrap( //横向每个标签之间的距离 spacing: 8.0, //纵向每个标签之间的距离 runSpacing: 8.0, //子标签 children: buildItemWidgetList()), ),); } //文本标签集合 ListtagList = ["文本标签", "测试", "这是什么", "早上好","吃饭", "再来一次"]; //构建文本标签数据 buildItemWidgetList() { ListitemWidgetList = []; for (var i = 0; i _FirstPageState(); } class _FirstPageState extends State { @override Widget build(BuildContext context) { return buildBodyFunction(); } bool isSelected = false; //5.7 /lib/code4/main_data404.dart //文本显示组件Text Widget buildBodyFunction() { return Scaffold( appBar: AppBar( title: Text("动画样式"), ), body: Container( padding: EdgeInsets.all(16), child: Column( children: [ //动画样式组件 buildAnimatedDefaultTextStyle(), SizedBox( height: 55, ), FlatButton( child: Text("切换样式"), onPressed: () { setState(() { isSelected = !isSelected; }); }, ) ], ), ), ); } } 小结 在本章中详细分析了文本组件Text的各种属性配置与样式设置,在实际项目开发中经常会遇到Text文字的自适应包裹,时常会遇到超出边界的情况,需要组件Column或者Row来解决,灵活组合应用样式配置与交互设置,可开发出令人舒适的视觉效果。