一、背景
项目里需要单独替换几个页面的字体。于是找到了全局指定字体的方法,参考文章:Flutter 指定字体(全局指定、局部指定),该文章提到了三种替换方法:
①全局替换,在MaterialApp的theme属性中,指定fontFamily。
②单个替换,单独在Text的style中指定fontFamily。
③多处替换,定义公共TextStyle ,调用它的copyWith方法。
为了方便理解,在此搬运一下:
1.全局配置— 就在主题theme中配置
MaterialApp(
theme: ThemeData(
fontFamily: "PingFang", // 统一指定应用的字体。
primarySwatch: Colors.green,
primaryColor: Colors.white),
。。。
2.局部配置—指定TextStyle中的fontFamily
TextStyle(
fontFamily:"PingFang", // 指定该Text的字体。
fontSize: SizeUtil.getFontSize(Dimens.font_sp12),
color: CustomColors.colorPrimary,
fontWeight: FontWeight.bold)
3.如果多处用到指定的字体,但又不是全局用到, 你可以定义一个公共的 textStyle .如:
var textFontStyle = TextStyle(
fontFamily:"PingFang", // 指定该Text的字体。
)
用到的地方用 .copyWith 这个方法, 如:
Text(
"显示我想要的字体",
style: textFontStyle.copyWith(
fontSize: 18.0,
color: Colors.red,
fontWeight: FontWeight.bold,
),
)
TextStyle的copyWith如下:
TextStyle copyWith({
bool inherit,
Color color,
Color backgroundColor,
String fontFamily,
List<String> fontFamilyFallback,
double fontSize,
FontWeight fontWeight,
FontStyle fontStyle,
double letterSpacing,
double wordSpacing,
TextBaseline textBaseline,
double height,
Locale locale,
Paint foreground,
Paint background,
List<ui.Shadow> shadows,
TextDecoration decoration,
Color decorationColor,
TextDecorationStyle decorationStyle,
double decorationThickness,
String debugLabel,
})
我们项目是需要替换几个页面,不是全App,第一种方法直接pass。第二种和第三种差不多,都是需要给每个Text控件设置字体,区别只是第三种不用每次指定字体名称。我们页面里面Text控件蛮多的,每个指定,也有较大的工作量。
那么,有没有单独指定某个页面的字体的方法呢?
以下是几种尝试:
二、验证后淘汰的方法
1、在页面外包裹Theme()控件(无效)
全局指定的方法很眼熟,那么直接在某一块控件外,包裹一个Theme控件,不就和在MaterialApp中指定的一样吗?想到这里,我不得不佩服我的机智,这么简单的方法,居然只被我一个人想出来!
于是我兴冲冲的去尝试了,在Column控件外包裹Theme,在Theme中指定字体,期待这整个Column控件都变成我指定的字体。代码如下:
@override
Widget build(BuildContext context) {
return Theme(
data: ThemeData(
fontFamily: "MaoTi",
),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Text("12345"),
Text("678910"),
],
),
],
)
);
}
然而,该方法无效,Text的字体并没有随之改变!
2、在页面外包裹MaterialApp控件(某些全局设定会冲突)
MaterialApp也是一个控件,在整个页面外嵌套一层MaterialApp,然后在该MaterialApp的theme属性中设置fontFamily。代码如下:
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
fontFamily: "MaoTi",
),
home: RealPage(),
);
}
该方法是有效的,字体确实改变了。不过,之前的一些全局配置,比如滑动控件的波纹效果、配置的首选颜色等,被MaterialApp层拦截了,页面中使用的是Flutter默认的MaterialApp配置,没有使用整个App入口指定的配置。需要把这些全局配置重新指定一遍,才能达到一致的效果。
三、正确的使用方式
3.在Scaffold外包裹Theme控件
为啥在MaterialApp的theme指定有效,直接在控件外包裹Theme控件却无效了?我感到很奇怪。查看源码,源码里也是直接用Theme包裹着child控件啊?真是太神奇了!
我注释掉部分代码,希望找到让Text字体生效的控件,经过fromwork层绕了一圈,发现生效的控件居然不在fromwork层,而是从外面传进去的!!!又经过一番排查,终于锁定,令theme生效的控件是Scaffold。
验证发现,字体是否生效与MaterialApp没有半毛钱关系,只与Scaffold有关。而Scaffold几乎是每个页面都有的,在它外层包裹Theme,指定fontFamily,即可达到单独设置某个页面的效果。而且,它不会阻隔MaterialApp中指定的全局配置,完美!
@override
Widget build(BuildContext context) {
return Theme(
data: ThemeData(
fontFamily: "MaoTi",
),
child: Scaffold(
body:
Column(
children: <Widget>[
Row(
children: <Widget>[
Text("12345"),
Text("678910"),
],
),
],
)
),
);
}
四、依然存在的坑
Theme结合Scaffold,能指定页面级别的Text控件字体。但是,如果页面中有使用富文本,或者自定义的Text控件,字体不会生效,还得在style中单独设置。在MaterialApp的theme属性中指定fontFamily,同样会有这个问题。因为字体只和Scaffold有关,和MaterialApp没有关系。
@override
Widget build(BuildContext context) {
return Theme(
data: ThemeData(
fontFamily: "MaoTi",
),
child: Scaffold(
body:
Center(child: Column(
children: <Widget>[
Row(
children: <Widget>[
// 富文本,ThemeData中的指定无效,需在TextStyle中单独指定
RichText(
text: TextSpan(
text: "123456",
style: TextStyle(color: Colors.blue,fontSize: 30,fontFamily: "MaoTi",)
)),
// 自定义文本,同上。能不能通过改变自定义方法来使用Theme字体不太清楚,我们项目中的自定义字体需单独指定
CustomText("123456",style: TextStyle(color: Colors.red,fontFamily: "MaoTi",),)
],
),
],
)
)
),
);
}
五、总结
字体的指定,从大到小,目前有以下几种方法:
①全App,在MateralApp中指定,只要每个页面都有Scaffold,需注意一下富文本和自定义文本。
②全页面,Theme结合Scaffold使用,同样需注意富文本和自定义文本。
③多处使用,定义公共TextStyle,调用copyWith方法。
④单个控件,直接在TextStyle中设置fontFamily。
技术是在不断发展的,我当前使用的版本是1.9,或许后面高的版本会有更多的使用方法,又或者现在我遇到的问题在高版本并不会用到,这篇文章只能提供一个思路,给大家参考,感谢阅读!
最后,也谢谢@emdd2016大大,他的文章给我指了一个验证的方向。本文只提供了使用字体的方法,引入字体的方法网上太多,不再赘述,可以自行查找。
参考文章:https://blog.csdn.net/emdd2016/article/details/93312143