点击上方蓝字关注我,知识会给你力量
写过Android的开发者都知道,关于状态栏高度的计算,Google改了一版又一版,依然还有很多兼容性问题,那么来到Flutter这边,问题突然变得简单了,通过下面的方法就可以很方便的获取。
MediaQuery.of(context).padding.top
本来很开心的完成任务了,直到有同事反馈这个方法有时获取的状态栏高度为0,但有时候又是对的,很奇怪,那么到底是什么原因,导致我们间歇性获取不到状态栏高度呢,事出反常必有妖,我们来看下这个方法的实现。从注释可以看出,这个方法其实并不是返回状态栏的高度,而是返回Flutter渲染页面被遮挡的部分的尺寸,但由于顶部的被遮挡区,通常就是状态栏或者刘海部分,所以这个方法就可以间接来获取状态栏高度了。
❝注释里有一句话非常关键,是一个重要的伏笔:If you consumed this padding (e.g. by building a widget that envelops or accounts for this padding in its layout in such a way that children areno longer exposed to this padding)。
再来看看MediaQuery这个类。这个类是个InheritedModel,这就意味着其子树是可以直接获取到这个数据的,它具体赋值的地方,就是这个fromView方法。
调用的方法。
❝老版本是MediaQueryData.fromWindow方法,3.7后废弃了。
看到这里就很奇怪了,不管怎么说,肯定是可以拿到padding的值的,那么为什么没有呢,我们注意到源码中有一个removePadding。唯一会将这个值移除的地方,就是这个removePadding,那么它会在哪些场景下调用呢?
注意到前面的注释了吗,If you consumed this padding (e.g. by building a widget that envelops or accounts for this padding in its layout in such a way that children areno longer exposed to this padding)。
所以,难道说是父布局消耗了这个padding?看不太懂,我们写一个简单的测试代码。
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
print('外层获取状态栏高度:${MediaQuery.of(context).padding.top}');
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Builder(builder: (context) {
print('内层获取状态栏高度:${MediaQuery.of(context).padding.top}');
return const Text('data');
}),
);
}
}
输出结果如下:
I/flutter (11316): 外层获取状态栏高度:32.0
I/flutter (11316): 内层获取状态栏高度:0.0
果然,在Scaffold内部,就拿不到padding了,我们进入Scaffold的源码看看,哪里有消耗这个padding。不出所料,Scaffold在构建的时候,会有很多的removeTopPadding处理,我们来看具体的逻辑。先看关键的,添加body属性的地方。
这里传入的removeTopPadding值,就是根据appBar判断的,原来如此,当我们传入appBar的时候,removeTopPadding就是true了,这时候,body内就拿不到padding了,我们在上面的测试代码中取得AppBar,再执行下代码,输出如下。
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
print('外层获取状态栏高度:${MediaQuery.of(context).padding.top}');
return Scaffold(
// appBar: AppBar(
// backgroundColor: Theme.of(context).colorScheme.inversePrimary,
// title: Text(widget.title),
// ),
body: Builder(builder: (context) {
print('内层获取状态栏高度:${MediaQuery.of(context).padding.top}');
return const Text('data');
}),
);
}
}
输出:
I/flutter (11316): 外层获取状态栏高度:32.0
I/flutter (11316): 内层获取状态栏高度:32.0
果不其然,现在可以获取了。
既然知道了原因,那么如何解决这个问题呢——「随时随地准确的获取状态栏的高度」。其实很简单了,毕竟之前获取不到是因为被手动remove了,所以我们拿它获取的方法,自己重新写一个就好了。看到前面获取padding的处理。所以,其实算法就是:
WidgetsBinding.instance.window.padding.top / WidgetsBinding.instance.window.devicePixelRatio
但新版本WidgetsBinding.instance.window已经被废弃了,所以最新的方法应该是下面的这样。
double height = MediaQueryData.fromView(PlatformDispatcher.instance.views.first).padding.top;
通过这个方式,可以在任意context下拿到当前状态栏的高度,而不用关心padding的影响。至此,我们终于搞清楚了在Flutter中获取状态栏高度的来龙去脉,不得不说,Flutter最大的好处就是源码透明,我们可以很方便的追踪问题的根源,甚至在发现问题时,可以将源码直接copy出来进行修改。
向大家推荐下我的网站 https://www.yuque.com/xuyisheng 点击原文一键直达
专注 Android-Kotlin-Flutter 欢迎大家访问
往期推荐
本文原创公众号:群英传,授权转载请联系微信(Tomcat_xu),授权后,请在原创发表24小时后转载。
< END >
作者:徐宜生
更文不易,点个“三连”支持一下👇