Flutter开发调试插件dokit的使用
Flutter开发调试插件dokit的使用
DoKit Flutter版
内测版本,目前提供了日志、method channel信息、路由信息、网络抓包、帧率、设备与内存信息查看、控件信息查看、颜色拾取、启动耗时、查看源码、查看widget的build链以及对齐标尺的功能。
支持flutter版本
1.17.5<=version<=1.22.4,其余版本未做过兼容性测试
Pub地址
接入
在pubspect.yaml
文件的dependencies
节点添加pub依赖:
dependencies:
dokit: ^0.6.1
在main
函数入口初始化。DoKit使用runZone
的方式进行日志捕获和方法通道捕获,如果你的app需要使用同样的方式可能会有冲突。
void main() => {
DoKit.runApp(
app: DoKitApp(MyApp()),
// 是否在release包内使用,默认release包会禁用
useInRelease: true,
releaseAction: () => {
// release模式下执行该函数,一些用到runZone之类实现的可以放到这里,该值为空则会直接调用系统的runApp(MyApp())
},
);
};
注:谷歌提供的DevTool会折叠非主工程内实例化的widget(根据source file是否属于当前工程),DoKit需要实例化一个wrapper widget用以展示各种overlay,如果在package内去声明这个wrapper,会导致左边树全部被折叠。故这里要求在main
文件内使用DoKitApp(MyApp())
的方式来初始化入口
另外提供了一个异步创建入口Widget的方式,需要异步构建widget的情况。(有些库会在异步构建Widget的时候调用WidgetFlutterBinding.ensureInitialized()
,影响DoKit的method channel监控和日志监控,需要延迟到runZone
内执行)
void main() => {
DoKit.runApp(
appCreator: () async => DoKitApp(await createApp()),
);
Widget createApp() async {
// 一些初始化操作
await ...;
return MyApp();
}
};
参数说明
参数 | 返回类型 | 说明 | 是否必须 |
---|---|---|---|
app | DoKitApp | 返回被DoKitApp类包装的根布局 | app和appCreator 至少需要设置一个,同时设置时app 参数生效 |
appCreator | DoKitAppCreator | 异步返回根布局 | 同上 |
useInRelease | bool | 是否在release模式下显示DoKit | x |
logCallback | LogCallback | 调用print 方法打印日志时被回调 |
x |
exceptionCallback | ExceptionCallback | 异常回调 | x |
methodChannelBlackList | List | 过滤方法通道的黑名单 | x |
releaseAction | Function | release模式下执行该函数,该值为空则会直接调用系统的runApp | x |
功能简介
全部组件
当前版本DoKit支持的所有功能全览。常驻工具为显示在底部tab栏的组件,可通过拖动将组件放置或移出常驻工具。
第三方业务入口
添加第三方业务入口,目前只支持跳转页面,对要跳转的页面只要求是Widget
即可,添加第三方业务入口的代码推荐写在main
函数中,下面是添加第三方入口的示例:
// 注册新的第三方业务入口,不可重复注册,否则报错
BizKitManager.instance.addKitWith(
name: 'test1',
group: 'biz',
kitBuilder: () => Container(color: Colors.orange),
);
BizKitManager.instance.addKitWith(name: 'noAction', group: 'biz');
BizKitManager.instance.addKitWith(
key: 'biz1_goBizPage1',
name: 'goBizPage1',
group: 'biz1',
kitBuilder: () => TestBizPage1(),
);
// 添加业务分组的tip信息(需先注册对应的group,否则报错)
BizKitManager.instance.addKitGroupTip('biz1', 'dokit test biz1');
// 通过注册的key来手动通过代码打开一个业务入口对应的页面
Future.delayed(Duration(seconds: 1), () {
BizKitManager.instance.open('biz1_goBizPage1');
// 安全打开一个kitPage,和open的区别在于不会报错
// BizKitManager.instance.safeOpen();
});
// 隐藏kitPage,不会删除上一次的打开记录
BizKitManager.instance.hide();
// 关闭kitPage,会删除上一次的打开记录
BizKitManager.instance.close();
// 如果传入的kitBuilder中的widget层级中没有包含Navigator(MaterialApp、WidgetApp等组件默认包含Navigator),则推荐使用,否则无法关闭
Navigator.of(context).pop();
日志查看
查看使用print
方式打印出来的日志,捕获的异常会以红色显示。超过7行的日志会自动折叠,点击可展开。长按复制日志到剪贴板。
网络请求
可以捕获通过flutter httpclient
发出的网络请求,主流的http
、dio
库底层也是通过httpclient
实现的,也能捕获。
Method Channel信息
可以展示从dart端到native和从native端到dart端的方法调用、参数、返回结果。
路由信息
展示当前页面的路由信息,当存在多层Navigator
组件嵌套时,会展示多层的路由信息。
注:当前查找栈顶widget是通过遍历整棵widget tree的方式,如果添加了overlay,栈顶widget会始终指向overlay,导致该功能读取数据异常。
帧率
展示最近240帧的耗时情况,每次进入该页面刷新。debug模式下帧率会普遍偏高,profile和release模式下会比较正常。
内存
当前已使用的内存和最大内存;底部搜索栏可以显示指定的类的对象数量和占用内存。
注:该功能通过VMService获取数据,release模式下无法使用
基本信息
展示当前dart虚拟机进程、CPU、版本信息;当前app包名和dart工程构建版本信息。
注:该功能通过VMService获取数据,release模式下无法使用。flutter版本号需要flutter attach后才可获取
控件检查
查看当前页面上的控件信息,包含位置、大小、源码信息、对齐标尺和查看build链等。
注:源码信息只有在debug模式下才可获取到。同路由功能,在存在Overlay的情况下功能会异常
颜色拾取
查看当前页面任何位置对应的像素点的RGBA颜色值,方便UI的调试和获取像素点的颜色。
Widget层级
查看当前选中widget的树层级,以及它renderObject
的详细build链等信息。
页面源码查看
查看当前所在页面的源代码,支持语法高亮显示。
注:源码信息只有在debug模式下才可获取到。同路由功能,在存在Overlay的情况下功能会异常
页面启动耗时
获取页面的启动耗时,框架已做无侵入的注入NavigatorObserver
。但是在较复杂的App构建时可能失效,需要手动添加DokitNavigatorObserver
。
注:页面启动耗时信息只有在profile或release模式下才有意义
示例代码
以下是完整的示例代码,展示了如何使用DoKit插件:
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:dokit/dokit.dart';
import 'package:dokit/kit/apm/vm/vm_helper.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import 'biz_page1.dart';
import 'page2.dart';
void main() {
List<String> blackList = [
'plugins.flutter.io/sensors/gyroscope',
'plugins.flutter.io/sensors/user_accel',
'plugins.flutter.io/sensors/accelerometer'
];
DoKit.runApp(
app: DoKitApp(MyApp()),
useInRelease: true,
logCallback: (log) {
String i = log;
},
methodChannelBlackList: blackList,
exceptionCallback: (dynamic obj, StackTrace trace) {
print('dokit exception callback: $obj');
});
BizKitManager.instance.addKitWith(
name: 'test1',
group: 'biz',
kitBuilder: () => Container(color: Colors.orange));
BizKitManager.instance.addKitWith(name: 'noAction', group: 'biz');
BizKitManager.instance.addKitWith(
key: 'biz1_goBizPage1',
name: 'goBizPage1',
group: 'biz1',
kitBuilder: () => TestBizPage1(),
);
BizKitManager.instance.addKitGroupTip('biz1', 'dokit test biz1');
Future.delayed(Duration(seconds: 1), () {
BizKitManager.instance.open('biz1_goBizPage1');
});
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'DoKit Test',
theme: ThemeData(primarySwatch: Colors.blue),
visualDensity: VisualDensity.adaptivePlatformDensity,
home: DoKitTestPage(),
);
}
}
class DoKitTestPage extends StatefulWidget {
[@override](/user/override)
_DoKitTestPageState createState() => _DoKitTestPageState();
}
class _DoKitTestPageState extends State<DoKitTestPage> {
Timer timer;
void testDownload() async {
String url =
'https://pt-starfile.didistatic.com/static/starfile/node20210220/895f1e95e30aba5dd56d6f2ccf768b57/GjzGU0Pvv11613804530384.zip';
String savePath = await getPhoneLocalPath();
String zipName = 'test.zip';
Dio dio = new Dio();
Response response = await dio.download(url, "$savePath/$zipName",
onReceiveProgress: (received, total) {
if (total != -1) {
if (received == total) {
print("下载完成 ✅ ");
}
}
});
}
Future<String> getPhoneLocalPath() async {
final directory = Theme.of(context).platform == TargetPlatform.android
? await getExternalStorageDirectory()
: await getApplicationDocumentsDirectory();
return directory.path;
}
void testMethodChannel() {
timer?.cancel();
timer = new Timer.periodic(new Duration(seconds: 2), (timer) async {
const MethodChannel _kChannel =
MethodChannel('plugins.flutter.io/package_info');
final Map<String, dynamic> map =
await _kChannel.invokeMapMethod<String, dynamic>('getAll');
});
}
void stopAll() {
print('stopAll');
timer?.cancel();
timer = null;
}
void mockHttpPost() async {
timer?.cancel();
timer = new Timer.periodic(new Duration(seconds: 2), (timer) async {
HttpClient client = new HttpClient();
String url = 'https://pinzhi.didichuxing.com/kop_stable/gateway?api=hhh';
HttpClientRequest request = await client.postUrl(Uri.parse(url));
Map<String, String> map1 = new Map();
map1["v"] = "1.0";
map1["month"] = "7";
map1["day"] = "25";
map1["key"] = "bd6e35a2691ae5bb8425c8631e475c2a";
request.add(utf8.encode(json.encode(map1)));
request.add(utf8.encode(json.encode(map1)));
HttpClientResponse response = await request.close();
String responseBody = await response.transform(utf8.decoder).join();
});
}
void mockHttpGet() async {
timer?.cancel();
timer = new Timer.periodic(new Duration(seconds: 2), (timer) async {
HttpClient client = new HttpClient();
String url = 'https://www.baidu.com';
HttpClientRequest request = await client.postUrl(Uri.parse(url));
HttpClientResponse response = await request.close();
String responseBody = await response.transform(utf8.decoder).join();
});
}
[@override](/user/override)
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
color: Colors.white,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(4)),
color: Color(0xffcccccc)),
margin: EdgeInsets.only(bottom: 30),
child: FlatButton(
child: Text('Mock Http Post',
style: TextStyle(color: Color(0xff000000), fontSize: 18)),
onPressed: mockHttpPost,
),
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(4)),
color: Color(0xffcccccc)),
margin: EdgeInsets.only(bottom: 30),
child: FlatButton(
child: Text('Mock Http Get',
style: TextStyle(color: Color(0xff000000), fontSize: 18)),
onPressed: mockHttpGet,
),
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(4)),
color: Color(0xffcccccc)),
margin: EdgeInsets.only(bottom: 30),
child: FlatButton(
child: Text('Test Download',
style: TextStyle(color: Color(0xff000000), fontSize: 18)),
onPressed: testDownload,
),
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(4)),
color: Color(0xffcccccc)),
margin: EdgeInsets.only(bottom: 30),
child: FlatButton(
child: Text('Test Method Channel',
style: TextStyle(color: Color(0xff000000), fontSize: 18)),
onPressed: testMethodChannel,
),
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(4)),
color: Color(0xffcccccc)),
margin: EdgeInsets.only(bottom: 30),
child: FlatButton(
child: Text('Open Route Page',
style: TextStyle(color: Color(0xff000000), fontSize: 18)),
onPressed: () {
Navigator.of(context, rootNavigator: false).push<void>(
new MaterialPageRoute(
builder: (context) {
return new TestPage2();
},
settings: new RouteSettings(name: 'page1')));
},
),
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(4)),
color: Color(0xffcccccc)),
margin: EdgeInsets.only(bottom: 30),
child: FlatButton(
child: Text('Test Get Page Script',
style: TextStyle(color: Color(0xff000000), fontSize: 18)),
onPressed: () {
VmHelper.instance.testPrintScript();
},
),
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(4)),
color: Color(0xffcccccc)),
margin: EdgeInsets.only(bottom: 30),
child: FlatButton(
child: Text('Stop Timer',
style: TextStyle(color: Color(0xff000000), fontSize: 18)),
onPressed: stopAll,
),
),
],
),
),
);
}
}
更多关于Flutter开发调试插件dokit的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter开发调试插件dokit的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
DoKit 是滴滴开源的一款面向移动端开发者的多功能调试工具,支持 Android 和 iOS 平台。它提供了丰富的调试功能,如网络请求监控、视图层级查看、日志查看、性能监控等。对于 Flutter 开发者来说,DoKit 也提供了 Flutter 插件,方便在 Flutter 应用中进行调试。
以下是使用 DoKit 进行 Flutter 开发调试的基本步骤:
1. 添加依赖
首先,你需要在 pubspec.yaml
文件中添加 DoKit 的依赖:
dependencies:
dokit: ^latest_version
然后运行 flutter pub get
来获取依赖。
2. 初始化 DoKit
在你的 Flutter 应用的 main.dart
文件中,初始化 DoKit:
import 'package:dokit/dokit.dart';
void main() {
// 初始化 DoKit
DoKit.init();
runApp(MyApp());
}
3. 启动 DoKit
在应用启动后,你可以通过以下方式启动 DoKit 的调试面板:
import 'package:dokit/dokit.dart';
void main() {
DoKit.init();
runApp(MyApp());
// 启动 DoKit 调试面板
DoKit.show();
}
4. 使用 DoKit 功能
启动应用后,你会在屏幕上看到一个悬浮的 DoKit 图标。点击该图标,可以打开 DoKit 的调试面板,里面包含了各种调试工具,如:
- 网络监控:查看应用中的网络请求,包括请求 URL、请求方法、响应时间、响应状态码等。
- 视图层级:查看当前页面的视图层级结构,方便调试 UI 布局。
- 日志查看:查看应用中的日志输出,支持过滤和搜索。
- 性能监控:监控应用的 CPU、内存、FPS 等性能指标。
- 自定义工具:你可以通过 DoKit 提供的 API 添加自定义的调试工具。
5. 自定义功能
DoKit 提供了丰富的 API,允许你自定义调试功能。例如,你可以添加自定义的日志输出:
import 'package:dokit/dokit.dart';
void logCustomMessage(String message) {
DoKit.log(message, tag: 'CustomTag');
}
6. 调试与发布模式
在开发阶段,你可以启用 DoKit 的所有功能。但在发布应用时,建议移除 DoKit 的初始化代码,或者通过条件编译来禁用 DoKit:
void main() {
// 仅在调试模式下启用 DoKit
if (kDebugMode) {
DoKit.init();
}
runApp(MyApp());
}