Flutter学习四:Flutter开发基础(六)调试Flutter应用
目录
0 引言
1 调试Flutter应用
1.1 日志与断点
1.1.1 debugger() 声明
1.1.2 print和debugPrint
1.1.3 调试模式、中间模式、发布模式
1.1.4 断点
1.2 调试应用程序层
1.2.1 转储Widgets树
1.2.2 转储渲染树
1.2.3 转储Layer树
1.2.4 转储语义树
1.2.5 调度(打印帧的开始和结束)
1.2.6 可视化调试
1.2.7 调试动画
1.2.8 调试性能问题
1.2.9 统计应用启动时间
1.2.10 跟踪Dart代码性能
1.3 官方可视化调试工具DevTools
0 引言
本文是对第二版序 | 《Flutter实战·第二版》 (flutterchina.club)的学习和总结。
1 调试Flutter应用
1.1 日志与断点
1.1.1 debugger() 声明
当使用Dart Observatory(调试模式时自动启用),可以使用debugger()语句插入编程式断点。
import 'dart:developer';/*debugger()语句采用一个可选的when参数,可以指定该参数仅在特定条件为真时中断
*/
void someFunction(double offset) {debugger(when: offset > 30.0);// ...
}
1.1.2 print和debugPrint
- Dart
print()功能将输出到系统控制台,可以使用flutter logs来查看它。如果一次输出太多,Android有时会丢弃一些日志行。- 推荐使用Flutter的
foundation库中的debugPrint(),它封装了 print,将一次输出的内容长度限制在一个级别(内容过多时会分批输出),避免被Android内核丢弃。
1.1.3 调试模式、中间模式、发布模式
- 在Flutter应用调试过程中,Dart
assert语句被启用,当某个规则被违反时,就会在控制台打印错误日志,并带上一些上下文信息来帮助追踪问题的根源。- 发布模式会关闭Observatory调试器,使用
flutter run --release运行应用程序即可打开发布模式。- 中间模式“profile mode”可以关闭除Observatory之外所有的调试辅助工具,使用
flutter run --profile运行应用程序即可打开中间模式。
1.1.4 断点
开发过程中,断点是最实用的调试工具之一,以 Android Studio 为例:比如在 93 行打了一个断点,一旦代码执行到这一行就会暂停,这时我们可以看到当前上下文所有变量的值,然后可以选择一步一步的执行代码。

1.2 调试应用程序层
Flutter框架的每一层都提供了将其当前状态或事件转储(dump)到控制台(使用debugPrint)的功能。
1.2.1 转储Widgets树
- 要转储Widgets树的状态,请调用debugDumpApp()
- 需要在调用
runApp()之后调用- 不能在
build()方法内调用- 不要在布局或绘制阶段调用
- 从frame 回调或事件处理器中调用是最佳方案
import 'package:flutter/material.dart';void main() {runApp(MaterialApp(home: AppHome(),),);
}class AppHome extends StatelessWidget {@overrideWidget build(BuildContext context) {return Material(child: Center(child: TextButton(onPressed: () {// 当按钮从被按下变为被释放时debugDumpApp()被调用debugDumpApp();},child: Text('Dump App'),),),);}
}
调用后输出这样的内容(精确的细节会根据框架的版本、设备的大小等等而变化):
I/flutter ( 6559): WidgetsFlutterBinding - CHECKED MODE
I/flutter ( 6559): RenderObjectToWidgetAdapter<RenderBox>([GlobalObjectKey RenderView(497039273)]; renderObject: RenderView)
I/flutter ( 6559): └MaterialApp(state: _MaterialAppState(1009803148))
I/flutter ( 6559): └ScrollConfiguration()
I/flutter ( 6559): └AnimatedTheme(duration: 200ms; state: _AnimatedThemeState(543295893; ticker inactive; ThemeDataTween(ThemeData(Brightness.light Color(0xff2196f3) etc...) → null)))
I/flutter ( 6559): └Theme(ThemeData(Brightness.light Color(0xff2196f3) etc...))
I/flutter ( 6559): └WidgetsApp([GlobalObjectKey _MaterialAppState(1009803148)]; state: _WidgetsAppState(552902158))
I/flutter ( 6559): └CheckedModeBanner()
I/flutter ( 6559): └Banner()
I/flutter ( 6559): └CustomPaint(renderObject: RenderCustomPaint)
I/flutter ( 6559): └DefaultTextStyle(inherit: true; color: Color(0xd0ff0000); family: "monospace"; size: 48.0; weight: 900; decoration: double Color(0xffffff00) TextDecoration.underline)
I/flutter ( 6559): └MediaQuery(MediaQueryData(size: Size(411.4, 683.4), devicePixelRatio: 2.625, textScaleFactor: 1.0, padding: EdgeInsets(0.0, 24.0, 0.0, 0.0)))
I/flutter ( 6559): └LocaleQuery(null)
I/flutter ( 6559): └Title(color: Color(0xff2196f3))
... #省略剩余内容
1.2.2 转储渲染树
- 调试布局问题时,转储Widget树可能不够详细,需要转储渲染树
- 要转储渲染树,请调用
debugDumpRenderTree()- 要调用
debugDumpRenderTree(),需要添加import'package:flutter/rendering.dart'
对于上面的例子,它会输出:
I/flutter ( 6559): RenderView
I/flutter ( 6559): │ debug mode enabled - android
I/flutter ( 6559): │ window size: Size(1080.0, 1794.0) (in physical pixels)
I/flutter ( 6559): │ device pixel ratio: 2.625 (physical pixels per logical pixel)
I/flutter ( 6559): │ configuration: Size(411.4, 683.4) at 2.625x (in logical pixels)
I/flutter ( 6559): │
I/flutter ( 6559): └─child: RenderCustomPaint
I/flutter ( 6559): │ creator: CustomPaint ← Banner ← CheckedModeBanner ←
I/flutter ( 6559): │ WidgetsApp-[GlobalObjectKey _MaterialAppState(1009803148)] ←
I/flutter ( 6559): │ Theme ← AnimatedTheme ← ScrollConfiguration ← MaterialApp ←
I/flutter ( 6559): │ [root]
I/flutter ( 6559): │ parentData: <none>
I/flutter ( 6559): │ constraints: BoxConstraints(w=411.4, h=683.4)
I/flutter ( 6559): │ size: Size(411.4, 683.4)
... # 省略
1.2.3 转储Layer树
- 调试合成问题,调用debugDumpLayerTree()
- 可以理解为渲染树是可以分层的,而最终绘制需要将不同的层合成起来,而Layer则是绘制时需要合成的层
对于上面的例子,它会输出下图内容。这是根Layer的toStringDeep输出的。根部的变换是应用设备像素比的变换; 在这种情况下,每个逻辑像素代表3.5个设备像素。
I/flutter : TransformLayer
I/flutter : │ creator: [root]
I/flutter : │ offset: Offset(0.0, 0.0)
I/flutter : │ transform:
I/flutter : │ [0] 3.5,0.0,0.0,0.0
I/flutter : │ [1] 0.0,3.5,0.0,0.0
I/flutter : │ [2] 0.0,0.0,1.0,0.0
I/flutter : │ [3] 0.0,0.0,0.0,1.0
I/flutter : │
I/flutter : ├─child 1: OffsetLayer
I/flutter : │ │ creator: RepaintBoundary ← _FocusScope ← Semantics ← Focus-[GlobalObjectKey MaterialPageRoute(560156430)] ← _ModalScope-[GlobalKey 328026813] ← _OverlayEntry-[GlobalKey 388965355] ← Stack ← Overlay-[GlobalKey 625702218] ← Navigator-[GlobalObjectKey _MaterialAppState(859106034)] ← Title ← ⋯
I/flutter : │ │ offset: Offset(0.0, 0.0)
I/flutter : │ │
I/flutter : │ └─child 1: PictureLayer
I/flutter : │
I/flutter : └─child 2: PictureLayer
1.2.4 转储语义树
- 语义树:呈现给系统可访问性API的树
- 转储语义树,要调用debugDumpSemanticsTree()
- 要调用debugDumpSemanticsTree() ,必须首先启用辅助功能,例如启用系统辅助工具或
SemanticsDebugger。
对于上面的例子,它会输出:
I/flutter : SemanticsNode(0; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))
I/flutter : ├SemanticsNode(1; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))
I/flutter : │ └SemanticsNode(2; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4); canBeTapped)
I/flutter : └SemanticsNode(3; Rect.fromLTRB(0.0, 0.0, 411.4, 683.4))
I/flutter : └SemanticsNode(4; Rect.fromLTRB(0.0, 0.0, 82.0, 36.0); canBeTapped; "Dump App")
1.2.5 调度(打印帧的开始和结束)
- 切换debugPrintBeginFrameBanner和debugPrintEndFrameBanner布尔值可以将帧的开始和结束打印到控制台,找出相对于帧的开始/结束事件发生的位置。
- debugPrintScheduleFrameStacks可以用来打印导致当前帧被调度的调用堆栈
例如:
I/flutter : ▄▄▄▄▄▄▄▄ Frame 12 30s 437.086ms ▄▄▄▄▄▄▄▄
I/flutter : Debug print: Am I performing this work more than once per frame?
I/flutter : Debug print: Am I performing this work more than once per frame?
I/flutter : ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
1.2.6 可视化调试
所有这些标志只能在调试模式下工作。
通常,Flutter框架中以“debug...” 开头的任何内容都只能在调试模式下工作。
设置
debugPaintSizeEnabled为true以可视方式调试布局问题:
- 这是来自
rendering库的布尔值。- 它可以在任何时候启用,并在为true时影响绘制。
- 设置它的最简单方法是在
void main()的顶部设置。当它被启用时,所有的盒子都会得到一个明亮的深青色边框:
- padding(来自widget如Padding)显示为浅蓝色,
- 子widget周围有一个深蓝色框,
- 对齐方式(来自widget如Center和Align)显示为黄色箭头,
- 空白(如没有任何子节点的Container)以灰色显示。
debugPaintBaselinesEnabled做了类似的事情,但对于具有基线的对象,文字基线以绿色显示,表意(ideographic)基线以橙色显示。
debugPaintPointersEnabled标志打开一个特殊模式,任何正在点击的对象都会以深青色突出显示。 这可以帮助我们确定某个对象是否以某种不正确的方式进行hit测试(Flutter检测点击的位置是否有能响应用户操作的widget),例如,如果它实际上超出了其父项的范围,首先不会考虑通过hit测试。
debugPaintLayerBordersEnabled标志调试合成图层,例如以确定是否以及在何处添加
RepaintBoundarywidget。该标志用橙色或轮廓线标出每个层的边界,或者使用debugRepaintRainbowEnabled标志重绘时,该层会被一组旋转色所覆盖。
1.2.7 调试动画
调试动画最简单的方法是减慢它们的速度:
- 将timeDilation (opens new window)变量(在scheduler库中)设置为大于1.0的数字,例如50.0。
- 最好在应用程序启动时只设置一次。
- 如果在运行中更改它,尤其是在动画运行时将其值改小,则在观察时可能会出现倒退。
1.2.8 调试性能问题
- 要了解应用程序导致重新布局或重新绘制的原因,分别设置debugPrintMarkNeedsLayoutStacks和 debugPrintMarkNeedsPaintStacks标志。
- 每当渲染盒被要求重新布局和重新绘制时,这些都会将堆栈跟踪记录到控制台。
- 可以使用
services库中的debugPrintStack()方法按需打印堆栈痕迹。
1.2.9 统计应用启动时间
- 要收集有关Flutter应用程序启动所需时间的详细信息,
- 可以在运行
flutter run时使用trace-startup和profile选项。
$ flutter run --trace-startup --profile
跟踪输出保存为
start_up_info.json,在Flutter工程目录在build目录下。输出列出了从应用程序启动到这些跟踪事件(以微秒捕获)所用的时间:
- 进入Flutter引擎时.
- 展示应用第一帧时.
- 初始化Flutter框架时.
- 完成Flutter框架初始化时.
例如 :
{"engineEnterTimestampMicros": 96025565262,"timeToFirstFrameMicros": 2171978,"timeToFrameworkInitMicros": 514585,"timeAfterFrameworkInitMicros": 1657393
}
1.2.10 跟踪Dart代码性能
跟踪和测量Dart任意代码段的wall/CPU时间(类似Android上使用systrace):
- 运行
flutter run时带有--profile标志,- 使用
dart:developer的Timeline工具来包含你想测试的代码块,- 打开你应用程序的Observatory timeline页面,在“Recorded Streams”中选择‘Dart’复选框,并执行你想测量的功能。
- 刷新页面将在Chrome的跟踪工具中显示应用按时间顺序排列的timeline记录。
Timeline.startSync('interesting function');
// iWonderHowLongThisTakes();
Timeline.finishSync();
1.3 官方可视化调试工具DevTools
Flutter DevTools 是一套 Dart 和 Flutter 的性能调试工具。
它将各种调试工具和能力集成在一起,并提供可视化调试界面。
可以用 Flutter DevTools 开发工具来实现的操作:
检查 Flutter 应用程序的 UI 组件布局和状态;
在 Flutter 应用程序中诊断 UI 性能过低的问题;
Flutter 和 Dart 应用的 CPU 性能检测;
为 Flutter 应用进行网络性能检测;
为 Flutter 或 Dart 应用进行源码级的调试;
在 Flutter 或 Dart 命令行应用中测试内存问题;
- 查看有关正在运行的Flutter或Dart命令行应用程序的常规日志和诊断信息。
查看正在运行的 Flutter 或 Dart 的命令行应用程序相关的常规日志和诊断信息。
在 Android Studio 上安装和运行开发者工具
在 VS Code 里安装和使用开发者工具
相关文章:
Flutter学习四:Flutter开发基础(六)调试Flutter应用
目录 0 引言 1 调试Flutter应用 1.1 日志与断点 1.1.1 debugger() 声明 1.1.2 print和debugPrint 1.1.3 调试模式、中间模式、发布模式 1.1.4 断点 1.2 调试应用程序层 1.2.1 转储Widgets树 1.2.2 转储渲染树 1.2.3 转储Layer树 1.2.4 转储语义树 1.2.5 调度&…...
新的开始(开始更新笔记)
首先感谢关注我的小伙伴,以后在求职或者选择方向的时候,感觉迷茫的时候,可以加我聊聊。 一路走来,跌跌撞撞,磕磕碰碰,经历了很多,记得上一次的更新笔记还是2021年。 首先说一下我的经历&#…...
爬虫工具-替换js文件ReRes插件/Gores插件
目录 一、ReRes插件二、Gores插件 一、ReRes插件 用途:爬虫逆向过程中一些文件需要替换时 ① 原始网站js文件有无限debugger,复制原始网站js文件,删掉无限debugger相关代码保存为新的js文件;用ReRes插件进行替换② 原始网站js文件…...
多任务学习用于多模态生物数据分析
目前的生物技术可以同时测量来自同一细胞的多种模态数据(例如RNA、DNA可及性和蛋白质)。这需要结合不同的分析任务(如多模态整合和跨模态分析)来全面理解这些数据,推断基因调控如何驱动生物多样性。然而,目…...
使用less命令搜索文件中的关键字
目录 介绍常用搜索技巧实例 介绍 less 与 more 类似,less 可以随意浏览文件,支持翻页和搜索,支持向上翻页和向下翻页。 语法 less [参数] 文件 参数说明: -b <缓冲区大小> 设置缓冲区的大小 -e 当文件显示结束后ÿ…...
【kubernetes系列】Kubernetes之Taints和tolerations
概述 节点亲和性是pod的一种属性(优先选择或硬性要求),它使 pod 被优先分配到一类特定的节点上。而Taint则相反,它使节点能够排斥一类特定的 pod。 Taints(污点)与tolerations(容忍度…...
宝剑锋从磨砺出 梅花香自苦寒来(高考志愿篇)
各省高考成绩已出,又到一年高考季。张雪峰提到:“普通家庭不要光谈理想,也要谈落地。”志愿怎样填报、选专业还是选学校、什么专业好就业、高考志愿主要看什么?针对这些疑问,你对正在选志愿的毕业生们有什么建议吗&…...
Jtti:怎样进行sql server2000 日志传送
在 SQL Server 2000 中,日志传送是指将事务日志从一个主服务器传送到一个或多个备份服务器的过程。这个过程确保备份服务器上的数据库保持与主服务器上的数据库同步。 要进行 SQL Server 2000 的日志传送,需要进行以下步骤: 配置主服务器&…...
MyBatis-Plus:条件构造器Wrapper
目录 1.Wrapper概述 1.1.Wrapper的继承关系 1.2.Wapper介绍 1.3.各个构造器使用区别 1.4.构造器常用方法 2.Wrapper常用构造器介绍 2.1.QueryWrapper 2.2.UpdateWrapper 2.3.LambdaQueryWrapper 2.4.AbstractWrapper 3. Lambda条件构造器 3.1.示例 4.鸣谢 MyBati…...
SNMP 计算机网络管理 实验1(二) 练习与使用Wireshark抓取SNMP数据包抓包之 任务三分析并验证TCP三次握手建立连接时三次握手工作过程
⬜⬜⬜ 🐰🟧🟨🟩🟦🟪(*^▽^*)欢迎光临 🟧🟨🟩🟦🟪🐰⬜⬜⬜ ✏️write in front✏️ 📝个人主页:陈丹宇jmu &am…...
【UE5 Cesium】03-Cesium for Unreal 添加本地数据集
上一篇:【UE5 Cesium】02-Cesium for Unreal 添加在线数据集 步骤 1. 在官网(Adding Datasets – Cesium)上下载一个示例 下载的是一个名为“Tileset.zip”的压缩文件 解压后文件内容如下 2. 打开虚幻编辑器,点击“Blank 3D Tiles…...
数通王国历险记之地址分析协议(ARP)
系列文章目录 数通王国历险记(4) 目录 前言 一,什么是地址解析协议(ARP) 二,封装和解封装 三,为什么需要地址解析协议(ARP) 四,ARP的验证实验 4.1&#x…...
Mac端显示服务器上show的内容
Mac端显示服务器上show的内容 1. 需求描述 在Mac端(终端和PyCharm中)编写代码,在服务器端运行程序。需要在Mac端显示服务器端运行的内容,比如,运行的视频等。 2. 常见报错 SSH 运行命令时报错示例。 (cv) czjing…...
【SQL】每类视频近一个月的转发量/率
【问题】 统计在有用户互动的最近一个月(按包含当天在内的近30天算,比如10月31日的近30天为10.2~10.31之间的数据)中,每类视频的转发量和转发率(保留3位小数)。 【数据】 用户-视频互动表 tb_user_video…...
chatgpt读论文
在当今的数字化时代,人工智能(AI)正在颠覆我们的生活方式。 OpenAI的GPT系列模型——尤其是最新一代的GPT-4和它的聊天机器人版本ChatGPT——是最近AI进步的显著代表。通过深度学习和海量数据训练,这些模型已经可以生成出色的人类般的文本。它们可以被用…...
关于visual studio 2010 及以上版本 引入boost库的最新解决方法
之前没有怎么用到boost库,出来实习需要去编译一些代码,需要引入boost第三方库,在这过程中,一直出现 LINK : fatal error LNK1104: 无法打开文件“libboost_filesystem-vc100-mt-gd-x3 错误, 但是也确实是跟其他教程学过…...
SpringBoot+ Dubbo + Mybatis + Nacos +Seata整合来实现Dubbo分布式事务
1.简介 “ 本文主要介绍SpringBoot2.1.5 Dubbo 2.7.3 Mybatis 3.4.2 Nacos 1.1.3 Seata 0.8.0整合来实现Dubbo分布式事务管理,使用Nacos 作为 Dubbo和Seata的注册中心和配置中心,使用 MySQL 数据库和 MyBatis来操作数据。 ” 如果你还对SpringBoot、Dubbo、Nacos…...
MongoDB使用
文档连接: link 创建表 不需要创建表的语句,当插入表的第一条语句时,会隐式的创建表。 增 插入一条 db.people.insertOne({ user_id: "bcd001", age: 45, status: "A" } )插入多条 db.collection.insertMany([ <document 1&g…...
C#文件安全管理解析
在实际的项目开发中,我们经常需要使用到文件的I/O操作,主要包含对文件的增改删查等操作,这些基本的操作我们都是很熟悉,但是较少的人去考虑文件的安全和操作的管理等方面,例如文件的访问权限管理,文件数据的…...
基于Dubbo分布式学校信息管理系统设计与实现
一、引言 1.1 课题背景 随着时代的发展与进步,计算机网络也随之日益完善,渐渐覆盖了我们生活的各个方面。在信息化和数字化的时代背景下,使用计算机管理学校信息来提升教育工作的质量和效率,是大势所趋,所以近年来,随着网络技术的不断发展,使用信息管理系统的学校越来…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...
深入浅出Diffusion模型:从原理到实践的全方位教程
I. 引言:生成式AI的黎明 – Diffusion模型是什么? 近年来,生成式人工智能(Generative AI)领域取得了爆炸性的进展,模型能够根据简单的文本提示创作出逼真的图像、连贯的文本,乃至更多令人惊叹的…...
go 里面的指针
指针 在 Go 中,指针(pointer)是一个变量的内存地址,就像 C 语言那样: a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10,通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...
【堆垛策略】设计方法
堆垛策略的设计是积木堆叠系统的核心,直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法,涵盖基础规则、优化算法和容错机制: 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则: 大尺寸/重量积木在下…...
