如果接触过原生开发的朋友可能知道

最初几年官方推荐使用 sp 作为字体大小的单位,但是事实上经过长时间实践的情况下,大家都开始逐渐使用 dp 作为单位

这样使用者调整系统字体大小,app 中的文字大小就不会受到影响,出现错误等情况

flutter 中修改

iOS 中叫动态字体大小,对应辅助功能中的字体大小

20190312152451.png

android 中叫字体大小

20190312152635.png

当你开发完成,又遇到用户修改系统字体大小导致某些地方错位,甚至按钮被挤出屏幕看不见了就是个问题了

而 flutter 中没有单位的概念,我们应该如何实现这个功能呢

在 flutter 中,是由 MediaQuery 来实现对应功能的

var data = MediaQuery.of(context);
data.textScaleFactor; //这个就是对应的动态字体大小,我们只需要『修改』这个值就可以了

修改自然是不可能的,这东西都是 final 的,我们要做的就是 flutter 中的通用做法

class NoScaleTextWidget extends StatelessWidget {
  final Widget child;

  const NoScaleTextWidget({
    Key key,
    @required this.child,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaxScaleTextWidget(
      max: 1.0,
      child: child,
    );
  }
}

class MaxScaleTextWidget extends StatelessWidget {
  final double max;
  final Widget child;

  const MaxScaleTextWidget({
    Key key,
    this.max = 1.2,
    @required this.child,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    var data = MediaQuery.of(context);
    var scale = math.min(max, data.textScaleFactor);
    return MediaQuery(
      data: data.copyWith(textScaleFactor: scale),
      child: child,
    );
  }
}

我这里的做法就是这样,创建一个组件,在内部修改这个值,然后把你的控件『包』起来

这里可以是你的 Scaffold,Text 等等的 widget

不过这样要修改的地方太多,而且后面不好改

我们可以用一个小技巧,使用 MaterialApp 的 builder 属性

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      ....
      builder: (ctx, w) {
        return MaxScaleTextWidget(
            max: 1.0,
            child: w,
        );
      },
    );
  }
}

在这个 builder 中这么写,就可以修改你 app 中的所有控件不受动态字体大小的影响了

我在 github 有一个 gisthttps://gist.github.com/CaiJingLong/d28208f569b44f39dd572a7e8f455912 也可查看到代码,需要自备梯子

后记

动态字体的事情就是这样了