theme: cyanosis
1. 前言
很多朋友可能在布局过程中、或者组件使用过程中,会遇到诸如颜色、尺寸、约束、定位等问题,可能会让你抓耳挠腮。俗话说,磨刀不误砍柴工,会使用工具是非常重要的,其实 Flutter 提供了强大的调试工具,可以辅助我们去查看界面布局中的一切细节。
基于这些细节,可以很轻松地去解决布局相关的疑难杂症。 也能让我们对界面的布局有更深刻的认知,这就是 : Flutter Inspector ,如果你使用 AndroidStudio,可以在如下的侧栏选项卡中打开:
光秃秃地介绍如何使用的话,或许太过无聊;下面就通过一个实际的小问题的解决过程,来讲述一下如何通过 Flutter Inspector 来分析界面结构和寻找关键源码。如下所示,左下角的菜单弹框,在 useMaterial3
下呈紫色,而 PopupMenuButton
并没有直接修改弹出框背景色的配置项。接下来将通过布局分析器,来解决如下几个问题:
- PopupMenuButton 弹出菜单,其背景是何时、如何着色的。
- 如何修改弹出菜单的背景色。
- 弹出菜单在界面树形结构中,处于哪个层级。
本文具体源码就不贴了,跑起来之后和本文一起使用 Flutter Inspector 来分析。源码地址在:
LoveNote520/LoveNote: 提交节点
2. Flutter Inspector 窗口基本介绍
首先,需要将应用运行起来, Flutter Inspector 才能展示信息。如下所示,映入眼帘的主要有三个部分
[1]. 顶部的操作工具栏。
[2]. 左侧的组件树信息。
[3]. 右侧的选中组件的详情信息。
首先强调一下,左侧的树形结构中每个组件条目,对应着右侧的一个面板。也就是说,你每当点击一个左侧组件树中的节点,右侧的面板信息就会更新:
其中右侧面板 Layout Explorer 可视化地展示出:
[1]. 当前组件对应的渲染对象树,受到的 父级约束
[2]. 当前组件对应的渲染对象树,其在界面中的 尺寸
[3]. 当前组件对应的渲染对象树,向子级的 传递约束
如果看过 《Flutter 布局探索 - 薪火相传》 的朋友,不难理解,对于布局来说 父级约束、尺寸、传递约束 这三者是何其重要。而 Layout Explorer 可视化地将这些信息展示出来,就非常便于我们去分析布局的细节。
在面板右侧,有一个 Widget Datails Tree 的选项卡,是极其重要而有用的。同样,你每当点击一个左侧组件树中的节点,Widget Datails Tree 信息也会更新。其中可以展示某个 Widget 构建过程中的所有细节,包括 dependencies
依赖、state
状态类、properties
调试属性、Widget
派生类的所有属性。
以及最重要的 某个 Widget 对应的渲染对象 renderObject,从渲染对象中,可以进一步分析约束、尺寸、数据等信息。细致入微地去了解当前界面中展示的逻辑,这样从内部寻找病因,就能更精准地对症下药。
3. 选择模式与具体组件分析
选择模式 Select Widget Model 是一个非常好用的工具。如下所示,点击之后,可以在应用界面中点一下,面板在就可以自动选中被点击的组件。这就可以大大提高查看界面视图中每个组件构建信息的效率,不必一个个自己手动去找。
如下所示,往上翻一下,就可以很容易定位到颜色的来源,PopupMenuButton 弹出框的视图,由源码内部的 _PopupMenu
组件所构建,其中背景色由 Material
组件所设置。
所以,此时全局搜索一下 _PopupMenu
组件,看看使用 Material
组件时颜色怎么传递的就可以了。如下所示,颜色值是三个:依次取用 路由颜色、弹框主题色、默认颜色。
从源码中不难看出,PopupMenuTheme
可以设置这里的背景颜色;如果没有主题色,将会取用 defaults
主题数据,这里根据 useMaterial3
来确定的,这就是主题中 useMaterial3
可以影响弹框颜色的根本原因。
dart ---->[_PopupMenu#build]---- final ThemeData theme = Theme.of(context); final PopupMenuThemeData popupMenuTheme = PopupMenuTheme.of(context); final PopupMenuThemeData defaults = theme.useMaterial3 ? _PopupMenuDefaultsM3(context) : _PopupMenuDefaultsM2(context);
3. 修改弹框颜色
有了上面的布局、源码分析之后,弹出框的背景色就很好修改了: 如下所示,提供 popupMenuTheme
,设置白色背景即可:
dart theme: ThemeData( fontFamily: 'aldk', colorSchemeSeed: const Color(0xff6750a4), popupMenuTheme: PopupMenuThemeData( color: Colors.white, ), useMaterial3: true, ),
如下所示,color 虽然设成了白色,但是弹框背景肉眼看起来很明显不是白色。不过看起来没有之前那么紫了,也就是说只设置 popupMenuTheme 的颜色,有一点用,但是不多。这是什么原因呢?
前面说了,一切界面展示中存在的问题,都可以通过 Flutter Inspector 来分析。可以先用拾色器看一下,颜色是 F3F1F7,确实不是白色。
如下所示,可以看出 Material 内部使用了 PhysicalShape 组件,其颜色恰是 F3F1F7 。这就说明 Material
的颜色并不仅仅是由 color 属性决定的,肯定和其他颜色混合了一下。下面就看一下源码中对颜色的处理逻辑。
下面是 Material
组件中的源码实现,可以很清晰的看出 useMaterial3
下,AnimatedPhysicalModel
的颜色是由 backgroundColor
(Material#color) 和 surfaceTintColor 以及 elevation 共同决定的。
颜色的换算过程由 ElevationOverlay.applySurfaceTint 处理,通过如下代码不难看出 surfaceTint 设为 null 或透明,就可以避免其对颜色的影响。
dart static Color applySurfaceTint(Color color, Color? surfaceTint, double elevation) { if (surfaceTint != null && surfaceTint != Colors.transparent) { return Color.alphaBlend(surfaceTint.withOpacity(_surfaceTintOpacityForElevation(elevation)), color); } return color; }
对于任何界面展示效果的问题,都可以通过 Flutter Inspector 来分析、定位问题所在,再查看相关的源码来解决。这就是通过 解决问题 ,进行探索和学习。也许有时候解决方案很简单,但过程中你会学得的更多。比如通过布局分析器查看时,你会发现:弹框是一个在 MaterialApp 下的独立路由,通过 _OverlayEntryWidget
挂在 _Theater
下显示,我们的应用界面也是一个 _OverlayEntryWidget
。这就不再展开了,有机会专门介绍一下。
这种解决问题流程中积攒的经验,将是非常宝贵的,它可以让你看清问题的根源所在,对整体有更好的把握。那本文就到这了,谢谢观看 ~