新增网络可视化请求工具
@@ -0,0 +1,79 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.pub-cache/
|
||||
.pub/
|
||||
build/
|
||||
*pubspec.lock
|
||||
|
||||
# Android related
|
||||
**/android/**/gradle-wrapper.jar
|
||||
**/android/.gradle
|
||||
**/android/captures/
|
||||
**/android/gradlew
|
||||
**/android/gradlew.bat
|
||||
**/android/local.properties
|
||||
**/android/**/GeneratedPluginRegistrant.java
|
||||
|
||||
# iOS/XCode related
|
||||
**/ios/**/*.mode1v3
|
||||
**/ios/**/*.mode2v3
|
||||
**/ios/**/*.moved-aside
|
||||
**/ios/**/*.pbxuser
|
||||
**/ios/**/*.perspectivev3
|
||||
**/ios/**/*sync/
|
||||
**/ios/**/.sconsign.dblite
|
||||
**/ios/**/.tags*
|
||||
**/ios/**/.vagrant/
|
||||
**/ios/**/DerivedData/
|
||||
**/ios/**/Icon?
|
||||
**/ios/**/Pods/
|
||||
**/ios/**/.symlinks/
|
||||
**/ios/**/profile
|
||||
**/ios/**/xcuserdata
|
||||
**/ios/.generated/
|
||||
**/ios/Flutter/App.framework
|
||||
**/ios/Flutter/Flutter.framework
|
||||
**/ios/Flutter/Flutter.podspec
|
||||
**/ios/Flutter/Generated.xcconfig
|
||||
**/ios/Flutter/app.flx
|
||||
**/ios/Flutter/app.zip
|
||||
**/ios/Flutter/flutter_assets/
|
||||
**/ios/Flutter/flutter_export_environment.sh
|
||||
**/ios/ServiceDefinitions.json
|
||||
**/ios/Runner/GeneratedPluginRegistrant.*
|
||||
|
||||
# Exceptions to above rules.
|
||||
!**/ios/**/default.mode1v3
|
||||
!**/ios/**/default.mode2v3
|
||||
!**/ios/**/default.pbxuser
|
||||
!**/ios/**/default.perspectivev3
|
||||
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
|
||||
|
||||
# Coverage
|
||||
**/coverage/output/
|
||||
**/coverage/new_lcov.info
|
||||
@@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: 02c026b03cd31dd3f867e5faeb7e104cce174c5f
|
||||
channel: unknown
|
||||
|
||||
project_type: package
|
||||
@@ -0,0 +1,84 @@
|
||||
# Changelog
|
||||
|
||||
[简体中文](./CHANGELOG_cn.md)
|
||||
|
||||
## [1.1.2]
|
||||
|
||||
* #82 Fix flutter_logo.dart error in Flutter 3.0.5
|
||||
|
||||
## [1.1.1+1]
|
||||
|
||||
* Update latest dependencies.
|
||||
|
||||
## [1.1.1]
|
||||
|
||||
* #66 [fix] toolbar initial position is incorrect
|
||||
|
||||
## [1.1.0+3]
|
||||
|
||||
* Fix static analyze issues.
|
||||
|
||||
## [1.1.0+2]
|
||||
|
||||
* Fix static analyze issues.
|
||||
|
||||
## [1.1.0]
|
||||
|
||||
* #76 Introduce `UMEWidget.closeActivatedPlugin()`. Issue #35
|
||||
* #75 Remove overlay entry only when it's been inserted. Issue #65
|
||||
* #72 [Android] Migrate the example to the v2 embedding
|
||||
|
||||
## [1.0.2+1]
|
||||
|
||||
* Dart format.
|
||||
|
||||
## [1.0.2]
|
||||
|
||||
* Fix error in code static analysis.
|
||||
|
||||
## [1.0.1]
|
||||
|
||||
* Fix error in pubspec.yaml in example
|
||||
|
||||
## [1.0.0]
|
||||
|
||||
* Normal version with adaption of Flutter 3.
|
||||
* Feature: Anywhere door (Route)
|
||||
|
||||
## [1.0.0-dev.0]
|
||||
|
||||
* Adapt Flutter 3.
|
||||
|
||||
## [0.3.0+1]
|
||||
|
||||
* Fix the version error
|
||||
|
||||
## [0.3.0]
|
||||
|
||||
* Remove static function. Use the `UMEWidget`.
|
||||
* Allow insert `Widget` into Widget tree, in order to access new plugin easily.
|
||||
* Fix the issue of multiple instances of FloatingWidget caused by the refresh state.
|
||||
* Fix the isseue that the plugin is not displayed due to the first layout exception in AOT mode
|
||||
|
||||
## [0.3.0]
|
||||
|
||||
* 移除静态方法,更换为壳 Widget
|
||||
* 允许在 Widget tree 增加自定义嵌套结构组件,从而快速接入新插件
|
||||
* 修复刷新状态引发的浮窗组件出现多实例的问题
|
||||
* 修复在 AOT 模式下首次布局异常导致插件不展示的问题
|
||||
|
||||
## [0.2.1]
|
||||
|
||||
* Remove the extra MaterialApp Widget
|
||||
|
||||
## [0.2.0-dev.0]
|
||||
|
||||
* Adapted Null-Safety.
|
||||
|
||||
## [0.1.0+1]
|
||||
|
||||
* Add some docs comments, modify description in pubspec.yaml.
|
||||
|
||||
## [0.1.0]
|
||||
|
||||
* Open source.
|
||||
@@ -0,0 +1,77 @@
|
||||
# Changelog
|
||||
|
||||
[English](./CHANGELOG.md)
|
||||
|
||||
## [1.1.2]
|
||||
|
||||
* #82 修复 flutter_logo.dart 在 Flutter 3.0.5 上的错误
|
||||
|
||||
## [1.1.1+1]
|
||||
|
||||
* 更新依赖版本
|
||||
|
||||
## [1.1.1]
|
||||
|
||||
* #66 [fix] toolbar initial position is incorrect
|
||||
|
||||
## [1.1.0+3]
|
||||
|
||||
* 修复静态分析问题
|
||||
|
||||
## [1.1.0+2]
|
||||
|
||||
* 修复静态分析问题
|
||||
|
||||
## [1.1.0]
|
||||
|
||||
* #76 新增 `UMEWidget.closeActivatedPlugin()`。 Issue #35
|
||||
* #75 修复重复插入 Overlay 的问题。 Issue #65
|
||||
* #72 [Android] 迁移 example 到 v2 embedding。
|
||||
|
||||
## [1.0.2+1]
|
||||
|
||||
* Dart format
|
||||
|
||||
## [1.0.2]
|
||||
|
||||
* 修复静态分析错误
|
||||
|
||||
## [1.0.1]
|
||||
|
||||
* 修复 example 工程的 pubspec.yaml 错误
|
||||
|
||||
## [1.0.0]
|
||||
|
||||
* 适配 Flutter 3 正式版
|
||||
* 新功能:任意门(Route)
|
||||
|
||||
## [1.0.0-dev.0]
|
||||
|
||||
* 适配 Flutter 3
|
||||
|
||||
## [0.3.0+1]
|
||||
|
||||
* 修复版本号错误
|
||||
|
||||
## [0.3.0]
|
||||
|
||||
* 移除静态方法,更换为壳 Widget
|
||||
* 允许在 Widget tree 增加自定义嵌套结构组件,从而快速接入新插件
|
||||
* 修复刷新状态引发的浮窗组件出现多实例的问题
|
||||
* 修复在 AOT 模式下首次布局异常导致插件不展示的问题
|
||||
|
||||
## [0.2.1]
|
||||
|
||||
* 移除独立的 MaterialApp Widget
|
||||
|
||||
## [0.2.0-dev.0]
|
||||
|
||||
* 适配 null-safety
|
||||
|
||||
## [0.1.0+1]
|
||||
|
||||
* 增加一些 docs comment,修改 pubspec.yaml 的描述信息
|
||||
|
||||
## [0.1.0]
|
||||
|
||||
* 开源
|
||||
@@ -0,0 +1,26 @@
|
||||
# Changelog
|
||||
|
||||
[简体中文](./CHANGELOG.md)
|
||||
|
||||
## [0.3.0]
|
||||
|
||||
* Remove static function. Use the `UMEWidget`.
|
||||
* Allow insert `Widget` into Widget tree, in order to access new plugin easily.
|
||||
* Fix the issue of multiple instances of FloatingWidget caused by the refresh state.
|
||||
* Fix the isseue that the plugin is not displayed due to the first layout exception in AOT mode
|
||||
|
||||
## [0.2.1]
|
||||
|
||||
* Remove the extra MaterialApp Widget
|
||||
|
||||
## [0.2.0-dev.0]
|
||||
|
||||
* Adapted Null-Safety.
|
||||
|
||||
## [0.1.0+1]
|
||||
|
||||
* Add some docs comments, modify description in pubspec.yaml.
|
||||
|
||||
## [0.1.0]
|
||||
|
||||
* Open source.
|
||||
@@ -0,0 +1,79 @@
|
||||
# Contributing
|
||||
|
||||
[简体中文](./CONTRIBUTING_cn.md)
|
||||
|
||||
Thank you for your interest in open source contributions.
|
||||
Not only the code, but also contributions such as issues and rich documents are also welcome.
|
||||
|
||||
Please follow the guidelines in this article to make open source contributions to the UME project.
|
||||
|
||||
- [Contributing](#contributing)
|
||||
- [How to contact author](#how-to-contact-author)
|
||||
- [How to raise an Issue](#how-to-raise-an-issue)
|
||||
- [How to raise a Pull Request](#how-to-raise-a-pull-request)
|
||||
- [Commit Message specification](#commit-message-specification)
|
||||
|
||||
## How to contact author
|
||||
|
||||
**Maybe...**
|
||||
|
||||
- Found a bug in the code, or an error in the documentation
|
||||
- Produces an exception when you use the UME
|
||||
- UME is not compatible with the new version Flutter
|
||||
- Have a good idea or suggestion
|
||||
|
||||
You can [submit an issue](#how-to-raise-an-issue) in any of the above situations。
|
||||
|
||||
**Maybe...**
|
||||
|
||||
- Communicate with the author
|
||||
- Communicate with more community developers
|
||||
- Cooperate with UME
|
||||
|
||||
Welcome to [Join the ByteDance Flutter Exchange Group](https://applink.feishu.cn/client/chat/chatter/add_by_link?link_token=b07u55bb-68f0-4a4b-871d-687637766a68).
|
||||
|
||||
Or contact [author](mailto:sunkai.dev@bytedance.com).
|
||||
|
||||
## How to raise an Issue
|
||||
|
||||
1. Go to [Issues](https://github.com/bytedance/flutter_ume/issues).
|
||||
2. Search for similar situations, if there is a match, directly feedback in it.
|
||||
3. If there is not, press [New issue](https://github.com/bytedance/flutter_ume/issues/new/choose) to raise a new one.
|
||||
4. Select a template.
|
||||
5. Describe your situation, and fill in the template.
|
||||
6. It is better to attach a demo that can reproduce the problem.
|
||||
|
||||
## How to raise a Pull Request
|
||||
|
||||
1. Fork the repository.
|
||||
2. Clone your fork repository.
|
||||
3. Checkout to the correct develop branch, and then create a new brnach based on the develop branch.
|
||||
4. Edit code.
|
||||
5. Edit test code in example project, and test it manually.
|
||||
6. Edit unit test in test directory.
|
||||
7. Commit your changes and push it. Please follow the [Commit Message specification](#commit-message-specification) to write the commit message.
|
||||
8. Create Pull Request in GitHub, and fill in the template.
|
||||
|
||||
> Now, UME support null-safety and non-null-safety.
|
||||
> Null-safety version corresponds to `develop_nullsafety` branch, non-null-safety version corresponds to `develop` branch.
|
||||
> PR should be merged into the corresponding branch.
|
||||
|
||||
## Commit Message specification
|
||||
|
||||
1. Please use english.
|
||||
2. If you have references, please attach a link.
|
||||
3. Format: `[tags] description`
|
||||
1. `tags` is the type of PR, such as `fix`, `feat`, `improve`.
|
||||
2. `description` is used to describe changes.
|
||||
|
||||
The following is a standard Commit message example:
|
||||
|
||||
``` plaintext
|
||||
[fix] README.md document syntax error
|
||||
```
|
||||
|
||||
``` plaintext
|
||||
[feat] New feature description
|
||||
|
||||
[https://flutter.dev/dash](https://flutter.dev/dash)
|
||||
```
|
||||
@@ -0,0 +1,79 @@
|
||||
# Contributing
|
||||
|
||||
[English](./CONTRIBUTING.md)
|
||||
|
||||
感谢你对开源贡献感兴趣。
|
||||
不止是代码,提 issue、补充和扩展文档等贡献也都欢迎。
|
||||
|
||||
请根据本文的指引,对 UME 项目进行开源贡献。
|
||||
|
||||
- [Contributing](#contributing)
|
||||
- [如何联系开发者](#如何联系开发者)
|
||||
- [如何提 Issue](#如何提-issue)
|
||||
- [如何提 Pull Request](#如何提-pull-request)
|
||||
- [Commit Message 规范](#commit-message-规范)
|
||||
|
||||
## 如何联系开发者
|
||||
|
||||
**可能你:**
|
||||
|
||||
- 发现文档错误、代码有 bug
|
||||
- 使用 UME 后应用运行产生异常
|
||||
- 发现新版本 Flutter 无法兼容
|
||||
- 有好的点子或产品建议
|
||||
|
||||
上述情况均可以[提一个 issue](#how-to-issue)。
|
||||
|
||||
**可能你:**
|
||||
|
||||
- 想与开发者交流
|
||||
- 想与更多 Flutter 开发者交流
|
||||
- 想与 UME 开展交流或合作
|
||||
|
||||
欢迎[加入字节跳动 Flutter 交流群](https://applink.feishu.cn/client/chat/chatter/add_by_link?link_token=b07u55bb-68f0-4a4b-871d-687637766a68)
|
||||
|
||||
或随时[联系开发者](mailto:sunkai.dev@bytedance.com)
|
||||
|
||||
## 如何提 Issue
|
||||
|
||||
1. 点击本仓库的 [Issue 页面](https://github.com/bytedance/flutter_ume/issues)
|
||||
2. 先搜索是否有和你类似情况的 issue,若有请直接在该 issue 中反馈问题
|
||||
3. 若没有类似情况 issue,点击 [New issue 按钮](https://github.com/bytedance/flutter_ume/issues/new/choose)
|
||||
4. 选择一个适合你的 issue 模板
|
||||
5. 在模板中填写对应信息
|
||||
6. 如果有能复现问题的最简 Demo 就再好不过了
|
||||
|
||||
## 如何提 Pull Request
|
||||
|
||||
1. Fork 本仓库
|
||||
2. 将你 fork 的仓库 clone 到本地
|
||||
3. 切换到对应开发分支,并 checkout 出新分支
|
||||
4. 在本地修改代码
|
||||
5. 修改 example 工程的测试代码,并进行手工测试
|
||||
6. 在 test 目录下,修改单元测试
|
||||
7. 在本地提交改动并推送到你 fork 的仓库,commit message 格式请遵循本文 [Commit Message 规范](#commit-message) 部分
|
||||
8. 在 GitHub 上创建 Pull Request,在模板中填写对应信息
|
||||
|
||||
> 目前,UME 同时支持 null-safety 版本与非 null-safety 版本。
|
||||
> null-safety 版本开发分支为 `develop_nullsafety`,非 null-safety 版本开发分支为 `develop`。
|
||||
> PR 需要合入对应的开发分支中。
|
||||
|
||||
## Commit Message 规范
|
||||
|
||||
1. 原则上请尽量使用英文
|
||||
2. 涉及到参考资料的,请附链接
|
||||
3. 格式:`[tags] description`
|
||||
1. `tags` 为 PR 的类型,如 `fix` 修复错误、`feat` 新增功能、`improve` 改进代码或文档
|
||||
2. `description` 为具体的改动描述
|
||||
|
||||
以下为标准的 Commit message 示例:
|
||||
|
||||
``` plaintext
|
||||
[fix] README.md document syntax error
|
||||
```
|
||||
|
||||
``` plaintext
|
||||
[feat] New feature description
|
||||
|
||||
[https://flutter.dev/dash](https://flutter.dev/dash)
|
||||
```
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 ByteDance Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,471 @@
|
||||
# flutter_ume
|
||||
|
||||
[简体中文](./README_cn.md)
|
||||
|
||||
UME is an in-app debug kits platform for Flutter apps.
|
||||
|
||||
[](https://pub.dev/packages/flutter_ume) [](https://github.com/bytedance/flutter_ume/blob/master/LICENSE)
|
||||
|
||||
[](https://pub.dev/packages/flutter_ume)
|
||||
[](https://pub.dev/packages/flutter_ume)
|
||||
[](https://pub.dev/packages/flutter_ume)
|
||||
[](https://pub.dev/packages/flutter_ume)
|
||||
[](https://pub.dev/packages/flutter_ume)
|
||||
|
||||
**Since `^1.0.0`, flutter_ume starts adapting to the Flutter 3. See [Quick Start] to learn more.**
|
||||
|
||||
<img src="https://github.com/bytedance/flutter_ume/raw/master/apk_qrcode.png" width = "128" height = "128" alt="banner" />
|
||||
|
||||
Scan QR code or click link to download apk. Try it now!
|
||||
https://github.com/bytedance/flutter_ume/releases/download/v0.2.1.0/app-debug.apk
|
||||
|
||||
There are 13 plugin kits built in the latest open source version of UME.
|
||||
Developer could create custom plugin kits, and integrate them into UME.
|
||||
Visit [Develop plugin kits for UME](#develop-plugin-kits-for-ume) for more details.
|
||||
|
||||
**Please see [Plugins from community](#plugins-from-community) to make your flutter_ume stronger.**
|
||||
|
||||
- [flutter_ume](#flutter_ume)
|
||||
- [Quick Start](#quick-start)
|
||||
- [IMPORTANT](#important)
|
||||
- [Features](#features)
|
||||
- [Develop plugin kits for UME](#develop-plugin-kits-for-ume)
|
||||
- [Access the nested widget debug kits quickly](#access-the-nested-widget-debug-kits-quickly)
|
||||
- [How to use UME in Release/Profile mode](#how-to-use-ume-in-releaseprofile-mode)
|
||||
- [About version](#about-version)
|
||||
- [Compatibility](#compatibility)
|
||||
- [Coverage](#coverage)
|
||||
- [Version upgrade rules](#version-upgrade-rules)
|
||||
- [Null-safety](#null-safety)
|
||||
- [Change log](#change-log)
|
||||
- [Contributing](#contributing)
|
||||
- [Contributors](#contributors)
|
||||
- [Plugins from community](#plugins-from-community)
|
||||
- [About the third-party open-source project dependencies](#about-the-third-party-open-source-project-dependencies)
|
||||
- [LICENSE](#license)
|
||||
- [Contact the author](#contact-the-author)
|
||||
|
||||
## Quick Start
|
||||
|
||||
**All packages whose names are prefixed with `flutter_ume_kit_` are function**
|
||||
**plug-ins of UME, and users can access them according to demand**
|
||||
|
||||
1. Edit `pubspec.yaml`, and add dependencies.
|
||||
|
||||
**Compatible with Flutter 3 since version `1.0.0`.**
|
||||
|
||||
``` yaml
|
||||
dev_dependencies:
|
||||
flutter_ume: ^1.0.1
|
||||
flutter_ume_kit_ui: ^1.0.0
|
||||
flutter_ume_kit_device: ^1.0.0
|
||||
flutter_ume_kit_perf: ^1.0.0
|
||||
flutter_ume_kit_show_code: ^1.0.0
|
||||
flutter_ume_kit_console: ^1.0.0
|
||||
flutter_ume_kit_dio: ^1.0.0
|
||||
```
|
||||
|
||||
|
||||
**↓ Null-safety version, compatible with Flutter 2.x**
|
||||
|
||||
``` yaml
|
||||
dev_dependencies: # Don't use UME in release mode
|
||||
flutter_ume: ^0.3.0+1
|
||||
flutter_ume_kit_ui: ^0.3.0+1
|
||||
flutter_ume_kit_device: ^0.3.0
|
||||
flutter_ume_kit_perf: ^0.3.0
|
||||
flutter_ume_kit_show_code: ^0.3.0
|
||||
flutter_ume_kit_console: ^0.3.0
|
||||
flutter_ume_kit_dio: ^0.3.0
|
||||
```
|
||||
|
||||
**↓ Non-null-safety version, compatible with Flutter 1.x**
|
||||
|
||||
``` yaml
|
||||
dev_dependencies: # Don't use UME in release mode
|
||||
flutter_ume: ^0.1.1
|
||||
flutter_ume_kit_ui: ^0.1.1
|
||||
flutter_ume_kit_device: ^0.1.1
|
||||
flutter_ume_kit_perf: ^0.1.1
|
||||
flutter_ume_kit_show_code: ^0.1.1
|
||||
flutter_ume_kit_console: ^0.1.1
|
||||
```
|
||||
|
||||
2. Run `flutter pub get`
|
||||
3. Import packages
|
||||
|
||||
``` dart
|
||||
import 'package:flutter_ume/flutter_ume.dart'; // UME framework
|
||||
import 'package:flutter_ume_kit_ui/flutter_ume_kit_ui.dart'; // UI kits
|
||||
import 'package:flutter_ume_kit_perf/flutter_ume_kit_perf.dart'; // Performance kits
|
||||
import 'package:flutter_ume_kit_show_code/flutter_ume_kit_show_code.dart'; // Show Code
|
||||
import 'package:flutter_ume_kit_device/flutter_ume_kit_device.dart'; // Device info
|
||||
import 'package:flutter_ume_kit_console/flutter_ume_kit_console.dart'; // Show debugPrint
|
||||
import 'package:flutter_ume_kit_dio/flutter_ume_kit_dio.dart'; // Dio Inspector
|
||||
```
|
||||
|
||||
4. Edit main method of your app, register plugin kits and initial UME
|
||||
|
||||
``` dart
|
||||
void main() {
|
||||
if (kDebugMode) {
|
||||
PluginManager.instance // Register plugin kits
|
||||
..register(WidgetInfoInspector())
|
||||
..register(WidgetDetailInspector())
|
||||
..register(ColorSucker())
|
||||
..register(AlignRuler())
|
||||
..register(ColorPicker()) // New feature
|
||||
..register(TouchIndicator()) // New feature
|
||||
..register(Performance())
|
||||
..register(ShowCode())
|
||||
..register(MemoryInfoPage())
|
||||
..register(CpuInfoPage())
|
||||
..register(DeviceInfoPanel())
|
||||
..register(Console())
|
||||
..register(DioInspector(dio: dio)); // Pass in your Dio instance
|
||||
// After flutter_ume 0.3.0
|
||||
runApp(UMEWidget(child: MyApp(), enable: true));
|
||||
// Before flutter_ume 0.3.0
|
||||
runApp(injectUMEWidget(child: MyApp(), enable: true));
|
||||
} else {
|
||||
runApp(MyApp());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
5. `flutter run` for running
|
||||
or `flutter build apk --debug`、`flutter build ios --debug` for building productions.
|
||||
|
||||
> Some functions rely on VM Service, and additional parameters need to be added for local operation to ensure that it can connect to the VM Service.
|
||||
>
|
||||
> Flutter 2.0.x, 2.2.x and other versions run on real devices, `flutter run` needs to add the `--disable-dds` parameter.
|
||||
> After [Pull Request #80900](https://github.com/flutter/flutter/pull/80900) merging, `--disable-dds` was renamed to `--no-dds`.
|
||||
|
||||
## IMPORTANT
|
||||
|
||||
**From `0.1.1`/`0.2.1` version,we don't need set `useRootNavigator: false`.**
|
||||
The following section only applies to versions before version `0.1.1`/`0.2.1` .
|
||||
|
||||
<s>
|
||||
|
||||
Since UME manages the routing stack at the top level, methods such as `showDialog` use `rootNavigator` to pop up by default,
|
||||
therefore **must** pass in the parameter `useRootNavigator: false` in `showDialog`, `showGeneralDialog` and other 'show dialog' methods to avoid navigator errors.
|
||||
|
||||
``` dart
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: const Text('Dialog'),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('OK'))
|
||||
],
|
||||
),
|
||||
useRootNavigator: false); // <===== It's very IMPORTANT!
|
||||
```
|
||||
|
||||
</s>
|
||||
|
||||
## Features
|
||||
|
||||
There are 13 plugin kits built in the current open source version of UME.
|
||||
|
||||
<table border="1" width="100%">
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><p>UI kits</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/widget_info.png" width="100%" alt="Widget Info" /></br>Widget Info</td>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/widget_detail.png" width="100%" alt="Widget Detail" /></br>Widget Detail</td>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/align_ruler.png" width="100%" alt="Align Ruler" /></br>Align Ruler</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/color_picker.png" width="100%" alt="Color Picker" /></br>Color Picker</td>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/color_sucker.png" width="100%" alt="Color Sucker" /></br>Color Sucker</td>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/touch_indicator.png" width="100%" alt="Touch Indicator" /></br>Touch Indicator</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><p>Performance Kits</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/memory_info.png" width="100%" alt="Memory Info" /></br>Memory Info</td>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/perf_overlay.png" width="100%" alt="Perf Overlay" /></br>Perf Overlay</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><p>Device Info Kits</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/cpu_info.png" width="100%" alt="CPU Info" /></br>CPU Info</td>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/device_info.png" width="100%" alt="Device Info" /></br>Device Info</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><p>Show Code</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/show_code.png" width="100%" alt="Show Code" /></br>Show Code</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><p>Console</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/console.png" width="100%" alt="Console" /></br>Console</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><p>Dio Inspector</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/dio_inspector.png" width="100%" alt="Dio Inspector" /></br>Dio Inspector</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Develop plugin kits for UME
|
||||
|
||||
> UME plugins are located in the `./kits` directory, and each one is a `package`.
|
||||
> You can refer to the example in [`./custom_plugin_example`](./custom_plugin_example/) about this chapter.
|
||||
|
||||
1. Run `flutter create -t package custom_plugin` to create your custom plugin kit, it could be `package` or `plugin`.
|
||||
2. Edit `pubspec.yaml` of the custom plugin kit to add UME framework dependency.
|
||||
|
||||
``` yaml
|
||||
dependencies:
|
||||
flutter_ume: '>=0.3.0 <0.4.0'
|
||||
```
|
||||
|
||||
3. Create the class of the plugin kit which should implement `Pluggable`.
|
||||
|
||||
``` dart
|
||||
import 'package:flutter_ume/flutter_ume.dart';
|
||||
|
||||
class CustomPlugin implements Pluggable {
|
||||
CustomPlugin({Key key});
|
||||
|
||||
@override
|
||||
Widget buildWidget(BuildContext context) => Container(
|
||||
color: Colors.white
|
||||
width: 100,
|
||||
height: 100,
|
||||
child: Center(
|
||||
child: Text('Custom Plugin')
|
||||
),
|
||||
); // The panel of the plugin kit
|
||||
|
||||
@override
|
||||
String get name => 'CustomPlugin'; // The name of the plugin kit
|
||||
|
||||
@override
|
||||
String get displayName => 'CustomPlugin';
|
||||
|
||||
@override
|
||||
void onTrigger() {} // Call when tap the icon of plugin kit
|
||||
|
||||
@override
|
||||
ImageProvider<Object> get iconImageProvider => NetworkImage('url'); // The icon image of the plugin kit
|
||||
}
|
||||
```
|
||||
|
||||
4. Use your custom plugin kit in project
|
||||
|
||||
1. Edit `pubspec.yaml` of host app project to add `custom_plugin` dependency.
|
||||
|
||||
``` yaml
|
||||
dev_dependencies:
|
||||
custom_plugin:
|
||||
path: path/to/custom_plugin
|
||||
```
|
||||
|
||||
2. Run `flutter pub get`
|
||||
|
||||
3. Import package
|
||||
|
||||
``` dart
|
||||
import 'package:custom_plugin/custom_plugin.dart';
|
||||
```
|
||||
|
||||
5. Edit main method of your app, register your custom_plugin plugin kit
|
||||
|
||||
``` dart
|
||||
if (kDebugMode) {
|
||||
PluginManager.instance
|
||||
..register(CustomPlugin());
|
||||
runApp(
|
||||
UMEWidget(
|
||||
child: MyApp(),
|
||||
enable: true
|
||||
)
|
||||
);
|
||||
} else {
|
||||
runApp(MyApp());
|
||||
}
|
||||
```
|
||||
|
||||
6. Run your app
|
||||
|
||||
### Access the nested widget debug kits quickly
|
||||
|
||||
We introduce the `PluggableWithNestedWidget` from `0.3.0`. It is used to insert nested Widgets in the Widget tree and quickly access embedded kits with nested widget.
|
||||
|
||||
For more details, see [./kits/flutter_ume_kit_ui/lib/components/color_picker/color_picker.dart](https://github.com/bytedance/flutter_ume/blob/master/kits/flutter_ume_kit_ui/lib/components/color_picker/color_picker.dart) and [./kits/flutter_ume_kit_ui/lib/components/touch_indicator/touch_indicator.dart](https://github.com/bytedance/flutter_ume/blob/master/kits/flutter_ume_kit_ui/lib/components/touch_indicator/touch_indicator.dart).
|
||||
|
||||
The key steps are as follows:
|
||||
|
||||
1. The class of your plugin should implement `PluggableWithNestedWidget`.
|
||||
2. Implements `Widget buildNestedWidget(Widget child)`. Handling the nested widgets and returning the new Widget.
|
||||
|
||||
## How to use UME in Release/Profile mode
|
||||
|
||||
**Once you use flutter_ume in Release/Profile mode, you agree that you will**
|
||||
**bear the relevant risks by yourself.**
|
||||
|
||||
**The maintainer of flutter_ume does not assume any responsibility for the accident**
|
||||
**caused by this.**
|
||||
|
||||
**We recommend not to use it in Release/Profile mode for the following reasons:**
|
||||
|
||||
1. VM Service is not available in these environments, so some functions are not available
|
||||
2. In this environment, developers need to isolate the app distribution channels by themselves to avoid submitting relevant debugging code to the production environment
|
||||
|
||||
In order to use in Release/Profile mode, the details that need to be adjusted in the normal access process:
|
||||
|
||||
1. In `pubspec.yaml`, `flutter_ume` and plugins should be write below `dependencies` rather than `dev_dependencies`.
|
||||
2. Don't put the code which call `PluginManager.instance.register()` and `UMEWidget(child: App())` into conditionals which represent debug mode. (Such as `kDebugMode`)
|
||||
3. Ensure the above details, run `flutter clean` and `flutter pub get`, then build your app.
|
||||
|
||||
## About version
|
||||
|
||||
### Compatibility
|
||||
|
||||
| UME version | 1.12.13 | 1.22.3 | 2.0.1 | 2.2.3 | 2.5.3 | 2.8.0 | 3.0.5 | 3.3.1
|
||||
| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |
|
||||
| 0.1.x | ✅ | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | ❌ | ❌ |
|
||||
| 0.2.x | ❌ | ❌ | ✅ | ✅ | ✅ | ⚠️ | ❌ | ❌ |
|
||||
| 0.3.x | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| 1.0.x | ❌ | ❌ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ✅ | ✅ |
|
||||
| 1.1.x | ❌ | ❌ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ✅ | ✅ |
|
||||
|
||||
⚠️ means the version has not been fully tested for compatibility.
|
||||
|
||||
### Special case
|
||||
|
||||
- Please use `flutter_ume_kit_ui: ^1.1.0` and above version when you are using Flutter 3.7 and above.
|
||||
|
||||
### Coverage
|
||||
|
||||
| Package | master | develop | develop_nullsafety |
|
||||
| ---- | ---- | ---- | ---- |
|
||||
| flutter_ume |  |  |  |
|
||||
| flutter_ume_kit_device |  |  |  |
|
||||
| flutter_ume_kit_perf |  |  |  |
|
||||
| flutter_ume_kit_show_code |  |  |  |
|
||||
| flutter_ume_kit_ui |  |  |  |
|
||||
| flutter_ume_kit_console |  |  |  |
|
||||
| flutter_ume_kit_dio |  | N/A |  |
|
||||
|
||||
### Version upgrade rules
|
||||
|
||||
Please refer to [Semantic versions](https://dart.dev/tools/pub/versioning#semantic-versions) for details.
|
||||
|
||||
### Change log
|
||||
|
||||
[Changelog](./CHANGELOG.md)
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributing rules: [Contributing](./CONTRIBUTING_en.md)
|
||||
|
||||
### Contributors
|
||||
|
||||
Thanks to the following contributors (names not listed in order):
|
||||
|
||||
| | |
|
||||
| ---- | ---- |
|
||||
|  | [ShirelyC](https://github.com/smileShirely) |
|
||||
|  | [lpylpyleo](https://github.com/lpylpyleo) |
|
||||
|  | [Alex Li](https://github.com/AlexV525) |
|
||||
|  | [Swain](https://github.com/talisk) |
|
||||
|  | [mengdouer](https://github.com/mengdouer) |
|
||||
|  | [LAIIIHZ](https://github.com/laiiihz) |
|
||||
|  | [XinLei](https://github.com/Vadaski) |
|
||||
|  | [suli](https://github.com/suli1) |
|
||||
|  | [wei-spring](https://github.com/wei-spring) |
|
||||
|
||||
### Plugins from community
|
||||
|
||||
- [flutter_ume_kit_channel_monitor](https://pub.dev/packages/flutter_ume_kit_channel_monitor)
|
||||
- Channel communication monitor
|
||||
- Cource code: https://github.com/bytedance/flutter_ume/tree/master/kits/flutter_ume_kit_channel_monitor
|
||||
- [flutter_ume_kit_slow_animation](https://pub.dev/packages/flutter_ume_kit_slow_animation)
|
||||
- Animation speed control
|
||||
- Cource code: https://github.com/cfug/flutter_ume_kits
|
||||
- [flutter_ume_kit_shared_preferences](https://pub.dev/packages/flutter_ume_kit_shared_preferences)
|
||||
- shared_preferences tool
|
||||
- Cource code: https://github.com/cfug/flutter_ume_kits
|
||||
- [flutter_ume_kit_designer_check](https://pub.dev/packages/)
|
||||
- Comparing tool for Design UI and real UI
|
||||
- Cource code: https://github.com/cfug/flutter_ume_kits
|
||||
- [flutter_ume_kit_clean_local_data](https://pub.dev/packages/flutter_ume_kit_clean_local_data)
|
||||
- Clean local data
|
||||
- Cource code: https://github.com/cfug/flutter_ume_kits 。
|
||||
- [flutter_ume_kit_database_kit](https://pub.dev/packages/flutter_ume_kit_database_kit)
|
||||
- DB tool
|
||||
- Cource code: https://github.com/cfug/flutter_ume_kits 。
|
||||
- [ume_kit_monitor](https://pub.dev/packages/ume_kit_monitor)
|
||||
- Parameters monitor tools
|
||||
- Cource code: https://github.com/fastcode555/ume_kit_monitor 。
|
||||
- [json2dart_viewerffi](https://pub.dev/packages/json2dart_viewerffi)
|
||||
- DB tool
|
||||
- Cource code: https://github.com/fastcode555/Json2Dart_Null_Safety 。
|
||||
- [json2dart_viewer](https://pub.dev/packages/json2dart_viewer)
|
||||
- DB tool
|
||||
- Cource code: https://github.com/fastcode555/Json2Dart_Null_Safety 。
|
||||
- [memory_detector_of_kit](https://github.com/bladeofgod/memory_detector_of_kit)
|
||||
- Leaks tool
|
||||
- [channel_observer_of_kit](https://github.com/bladeofgod/channel_observer_of_kit)
|
||||
- Channel communication monitor
|
||||
- [flutter-ume-kit-dio-enhance](https://github.com/linversion/flutter-ume-kit-dio-enhance)
|
||||
- Plugin base on flutter_ume_kit_dio
|
||||
|
||||
### About the third-party open-source project dependencies
|
||||
|
||||
- The TouchIndicator use the pub [touch_indicator](https://pub.dev/packages/touch_indicator), the ColorPicker use the pub [cyclop](https://pub.dev/packages/cyclop).
|
||||
- We [fork](https://github.com/talisk/cyclop) the package [cyclop](https://pub.dev/packages/cyclop) and modify some code meet our functional needs. We should depend cyclop by pub version after the [PR](https://github.com/rxlabz/cyclop/pull/11) being merged.
|
||||
|
||||
## LICENSE
|
||||
|
||||
This project is licensed under the MIT License - visit the [LICENSE](./LICENSE) for details.
|
||||
|
||||
## Contact the author
|
||||
|
||||
**Maybe...**
|
||||
|
||||
- Found a bug in the code, or an error in the documentation
|
||||
- Produces an exception when you use the UME
|
||||
- UME is not compatible with the new version Flutter
|
||||
- Have a good idea or suggestion
|
||||
|
||||
You can [submit an issue](./CONTRIBUTING_en.md#how-to-raise-an-issue) in any of the above situations.
|
||||
|
||||
**Maybe...**
|
||||
|
||||
- Communicate with the author
|
||||
- Communicate with more community developers
|
||||
- Cooperate with UME
|
||||
|
||||
Welcome to [Join the ByteDance Flutter Exchange Group](https://applink.feishu.cn/client/chat/chatter/add_by_link?link_token=67au2f75-3783-41b0-8868-0fc0178f1fd8).
|
||||
|
||||
Or contact [author](mailto:sunkai.dev@bytedance.com).
|
||||
@@ -0,0 +1,470 @@
|
||||
# flutter_ume
|
||||
|
||||
[English](./README.md)
|
||||
|
||||
Flutter 应用内调试工具平台
|
||||
|
||||
[](https://pub.dev/packages/flutter_ume) [](https://github.com/bytedance/flutter_ume/blob/master/LICENSE)
|
||||
|
||||
[](https://pub.dev/packages/flutter_ume)
|
||||
[](https://pub.dev/packages/flutter_ume)
|
||||
[](https://pub.dev/packages/flutter_ume)
|
||||
[](https://pub.dev/packages/flutter_ume)
|
||||
[](https://pub.dev/packages/flutter_ume)
|
||||
|
||||
**Since `^1.0.0`, flutter_ume starts adapting to the Flutter 3. See [Quick Start] to learn more.**
|
||||
|
||||
<img src="https://github.com/bytedance/flutter_ume/raw/master/apk_qrcode.png" width = "128" height = "128" alt="banner" />
|
||||
|
||||
扫码或点击链接下载 apk,快速体验 UME。
|
||||
https://github.com/bytedance/flutter_ume/releases/download/v0.2.1.0/app-debug.apk
|
||||
|
||||
最新版本(1.0.1)内置 13 个插件,
|
||||
开发者可以创建自己的插件,并集成进 UME 平台。
|
||||
详见本文[为 UME 开发插件](#为-ume-开发插件)部分。
|
||||
|
||||
**更多开源社区贡献的调试插件,请见[社区插件](#社区插件)部分。**
|
||||
|
||||
- [flutter_ume](#flutter_ume)
|
||||
- [快速接入](#快速接入)
|
||||
- [特别说明](#特别说明)
|
||||
- [功能介绍](#功能介绍)
|
||||
- [为 UME 开发插件](#为-ume-开发插件)
|
||||
- [快速集成嵌入式插件](#快速集成嵌入式插件)
|
||||
- [如何在 Release/Profile mode 下使用 UME](#如何在-releaseprofile-mode-下使用-ume)
|
||||
- [版本说明](#版本说明)
|
||||
- [兼容性](#兼容性)
|
||||
- [单测覆盖率](#单测覆盖率)
|
||||
- [版本号规则](#版本号规则)
|
||||
- [Null-safety 版本](#null-safety-版本)
|
||||
- [更新日志](#更新日志)
|
||||
- [开源贡献](#开源贡献)
|
||||
- [贡献者](#贡献者)
|
||||
- [社区插件](#社区插件)
|
||||
- [第三方开源项目说明](#第三方开源项目说明)
|
||||
- [开源协议](#开源协议)
|
||||
- [联系开发者](#联系开发者)
|
||||
|
||||
## 快速接入
|
||||
|
||||
**所有名称前缀为 `flutter_ume_kit_` 的 package 都是 UME 的功能插件,**
|
||||
**用户可按需接入。**
|
||||
|
||||
1. 修改 `pubspec.yaml`,添加依赖
|
||||
|
||||
**自 `1.0.0` 版本开始适配 Flutter 3。**
|
||||
|
||||
``` yaml
|
||||
dev_dependencies:
|
||||
flutter_ume: ^1.0.1
|
||||
flutter_ume_kit_ui: ^1.0.0
|
||||
flutter_ume_kit_device: ^1.0.0
|
||||
flutter_ume_kit_perf: ^1.0.0
|
||||
flutter_ume_kit_show_code: ^1.0.0
|
||||
flutter_ume_kit_console: ^1.0.0
|
||||
flutter_ume_kit_dio: ^1.0.0
|
||||
```
|
||||
|
||||
**↓ Null-safety 版本,适用于 Flutter 2.x**
|
||||
|
||||
``` yaml
|
||||
dev_dependencies:
|
||||
flutter_ume: ^0.3.0+1
|
||||
flutter_ume_kit_ui: ^0.3.0+1
|
||||
flutter_ume_kit_device: ^0.3.0
|
||||
flutter_ume_kit_perf: ^0.3.0
|
||||
flutter_ume_kit_show_code: ^0.3.0
|
||||
flutter_ume_kit_console: ^0.3.0
|
||||
flutter_ume_kit_dio: ^0.3.0
|
||||
```
|
||||
|
||||
**↓ 非 Null-safety 版本,适用于 Flutter 1.x**
|
||||
|
||||
``` yaml
|
||||
dev_dependencies:
|
||||
flutter_ume: ^0.1.1
|
||||
flutter_ume_kit_ui: ^0.1.1
|
||||
flutter_ume_kit_device: ^0.1.1
|
||||
flutter_ume_kit_perf: ^0.1.1
|
||||
flutter_ume_kit_show_code: ^0.1.1
|
||||
flutter_ume_kit_console: ^0.1.1
|
||||
```
|
||||
|
||||
2. 执行 `flutter pub get`
|
||||
3. 引入包
|
||||
|
||||
``` dart
|
||||
import 'package:flutter_ume/flutter_ume.dart'; // UME 框架
|
||||
import 'package:flutter_ume_kit_ui/flutter_ume_kit_ui.dart'; // UI 插件包
|
||||
import 'package:flutter_ume_kit_perf/flutter_ume_kit_perf.dart'; // 性能插件包
|
||||
import 'package:flutter_ume_kit_show_code/flutter_ume_kit_show_code.dart'; // 代码查看插件包
|
||||
import 'package:flutter_ume_kit_device/flutter_ume_kit_device.dart'; // 设备信息插件包
|
||||
import 'package:flutter_ume_kit_console/flutter_ume_kit_console.dart'; // debugPrint 插件包
|
||||
import 'package:flutter_ume_kit_dio/flutter_ume_kit_dio.dart'; // Dio 网络请求调试工具
|
||||
```
|
||||
|
||||
4. 修改程序入口,增加初始化方法及注册插件代码
|
||||
|
||||
``` dart
|
||||
void main() {
|
||||
if (kDebugMode) {
|
||||
PluginManager.instance // 注册插件
|
||||
..register(WidgetInfoInspector())
|
||||
..register(WidgetDetailInspector())
|
||||
..register(ColorSucker())
|
||||
..register(AlignRuler())
|
||||
..register(ColorPicker()) // 新插件
|
||||
..register(TouchIndicator()) // 新插件
|
||||
..register(Performance())
|
||||
..register(ShowCode())
|
||||
..register(MemoryInfoPage())
|
||||
..register(CpuInfoPage())
|
||||
..register(DeviceInfoPanel())
|
||||
..register(Console())
|
||||
..register(DioInspector(dio: dio)); // 传入你的 Dio 实例
|
||||
// flutter_ume 0.3.0 版本之后
|
||||
runApp(UMEWidget(child: MyApp(), enable: true)); // 初始化
|
||||
// flutter_ume 0.3.0 版本之前
|
||||
runApp(injectUMEWidget(child: MyApp(), enable: true)); // 初始化
|
||||
} else {
|
||||
runApp(MyApp());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
5. `flutter run` 运行代码
|
||||
或 `flutter build apk --debug`、`flutter build ios --debug` 构建产物
|
||||
|
||||
> 部分功能依赖 VM Service,本地运行需要添加额外参数,以确保能够连接到 VM Service。
|
||||
>
|
||||
> Flutter 2.0.x、2.2.x 等版本在真机上运行,`flutter run` 需要添加 `--disable-dds` 参数。
|
||||
> 在 [Pull Request #80900](https://github.com/flutter/flutter/pull/80900) 合入之后,`--disable-dds` 参数被更名为 `--no-dds`。
|
||||
|
||||
## 特别说明
|
||||
|
||||
**自 `0.1.1`/`0.2.1` 版本起,已经不需要设置 `useRootNavigator: false`。**
|
||||
以下部分仅适用于 `0.1.1`/`0.2.1` 之前的版本。
|
||||
|
||||
<s>
|
||||
|
||||
由于 UME 在顶层管理了路由栈,`showDialog` 等方法默认使用 `rootNavigator` 弹出,
|
||||
所以**必须**在 `showDialog`、`showGeneralDialog` 等弹窗方法,传入参数 `useRootNavigator: false` 避免路由栈错误。
|
||||
|
||||
``` dart
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: const Text('Dialog'),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('OK'))
|
||||
],
|
||||
),
|
||||
useRootNavigator: false); // <===== 非常重要
|
||||
```
|
||||
|
||||
</s>
|
||||
|
||||
## 功能介绍
|
||||
|
||||
当前开源版 UME 内置了 13 个插件
|
||||
|
||||
<table border="1" width="100%">
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><p>UI 工具包</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/widget_info.png" width="100%" alt="Widget 信息" /></br>Widget 信息</td>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/widget_detail.png" width="100%" alt="Widget 详情" /></br>Widget 详情</td>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/align_ruler.png" width="100%" alt="对齐标尺" /></br>对齐标尺</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/color_picker.png" width="100%" alt="颜色吸管(新)" /></br>颜色吸管(新)</td>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/color_sucker.png" width="100%" alt="颜色吸管" /></br>颜色吸管</td>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/touch_indicator.png" width="100%" alt="触控标记" /></br>触控标记</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><p>性能工具包</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/memory_info.png" width="100%" alt="内存信息" /></br>内存信息</td>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/perf_overlay.png" width="100%" alt="性能浮层" /></br>性能浮层</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><p>设备信息工具包</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/cpu_info.png" width="100%" alt="CPU 信息" /></br>CPU 信息</td>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/device_info.png" width="100%" alt="设备信息" /></br>设备信息</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><p>代码查看</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/show_code.png" width="100%" alt="代码查看" /></br>代码查看</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><p>日志展示</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/console.png" width="100%" alt="日志展示" /></br>日志展示</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><p>Dio 网络请求调试工具</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/dio_inspector.png" width="100%" alt="Dio 网络请求调试工具" /></br>Dio 网络请求调试工具</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## 为 UME 开发插件
|
||||
|
||||
> UME 插件位于 `./kits` 目录下,每个插件包都是一个 `package`
|
||||
> 本小节示例可参考 [`./custom_plugin_example`](./custom_plugin_example/)
|
||||
|
||||
1. `flutter create -t package custom_plugin` 创建一个插件包,可以是 `package`,也可以是 `plugin`
|
||||
2. 修改插件包的 `pubspec.yaml`,添加依赖
|
||||
|
||||
``` yaml
|
||||
dependencies:
|
||||
flutter_ume: '>=0.3.0 <0.4.0'
|
||||
```
|
||||
|
||||
3. 创建插件配置,实现 `Pluggable` 虚类
|
||||
|
||||
``` dart
|
||||
import 'package:flutter_ume/flutter_ume.dart';
|
||||
|
||||
class CustomPlugin implements Pluggable {
|
||||
CustomPlugin({Key key});
|
||||
|
||||
@override
|
||||
Widget buildWidget(BuildContext context) => Container(
|
||||
color: Colors.white
|
||||
width: 100,
|
||||
height: 100,
|
||||
child: Center(
|
||||
child: Text('Custom Plugin')
|
||||
),
|
||||
); // 返回插件面板
|
||||
|
||||
@override
|
||||
String get name => 'CustomPlugin'; // 插件名称
|
||||
|
||||
@override
|
||||
String get displayName => 'CustomPlugin';
|
||||
|
||||
@override
|
||||
void onTrigger() {} // 点击插件面板图标时调用
|
||||
|
||||
@override
|
||||
ImageProvider<Object> get iconImageProvider => NetworkImage('url'); // 插件图标
|
||||
}
|
||||
```
|
||||
|
||||
4. 在工程中引入自定义插件
|
||||
|
||||
1. 修改 `pubspec.yaml`,添加依赖
|
||||
|
||||
``` yaml
|
||||
dev_dependencies:
|
||||
custom_plugin:
|
||||
path: path/to/custom_plugin
|
||||
```
|
||||
|
||||
2. 执行 `flutter pub get`
|
||||
|
||||
3. 引入包
|
||||
|
||||
``` dart
|
||||
import 'package:custom_plugin/custom_plugin.dart';
|
||||
```
|
||||
|
||||
5. 在工程中注册插件
|
||||
|
||||
``` dart
|
||||
if (kDebugMode) {
|
||||
PluginManager.instance
|
||||
..register(CustomPlugin());
|
||||
runApp(
|
||||
UMEWidget(
|
||||
child: MyApp(),
|
||||
enable: true
|
||||
)
|
||||
);
|
||||
} else {
|
||||
runApp(MyApp());
|
||||
}
|
||||
```
|
||||
|
||||
6. 运行代码
|
||||
|
||||
### 快速集成嵌入式插件
|
||||
|
||||
自 `0.3.0` 版本起引入了 `PluggableWithNestedWidget`,用以实现在 Widget tree 中插入嵌套 Widget,快速接入嵌入式插件。
|
||||
|
||||
可参考 [./kits/flutter_ume_kit_ui/lib/components/color_picker/color_picker.dart](https://github.com/bytedance/flutter_ume/blob/master/kits/flutter_ume_kit_ui/lib/components/color_picker/color_picker.dart) 与 [./kits/flutter_ume_kit_ui/lib/components/touch_indicator/touch_indicator.dart](https://github.com/bytedance/flutter_ume/blob/master/kits/flutter_ume_kit_ui/lib/components/touch_indicator/touch_indicator.dart)。
|
||||
|
||||
集成重点如下:
|
||||
|
||||
1. 插件主体类实现 `PluggableWithNestedWidget`
|
||||
2. 实现 `Widget buildNestedWidget(Widget child)`,在该方法中处理嵌套结构并返回 Widget
|
||||
|
||||
## 如何在 Release/Profile mode 下使用 UME
|
||||
|
||||
**开发者一旦在 Release/Profile mode 下使用 flutter_ume,**
|
||||
**即认同将自行承担相关风险,**
|
||||
|
||||
**对于由此引发的事故,flutter_ume 维护方不承担**
|
||||
**任何责任。**
|
||||
|
||||
**不建议在 Release/Profile mode 下使用,原因如下:**
|
||||
|
||||
1. 在该环境下 VM Service 不可用,因此部分插件功能不可用
|
||||
2. 在该环境下开发者需要自行隔离分发渠道,避免将相关调试代码提交到生产环境
|
||||
|
||||
为在 Release/Profile mode 下使用,正常接入流程中需要调整的细节:
|
||||
|
||||
1. `pubspec.yaml` 中,`flutter_ume` 及相关插件包需要在 `dependencies` 中引入,而不是 `dev_dependencies`
|
||||
2. 调用 `PluginManager.instance.register()` 及 `UMEWidget(child: App())` 初始化方法的代码,不得由于 debug 标记剪枝(如 `kDebugMode`)
|
||||
3. 确保以上细节后,依次执行 `flutter clean`、`flutter pub get` 后再进行构建
|
||||
|
||||
## 版本说明
|
||||
|
||||
### 兼容性
|
||||
|
||||
| UME 版本 | 1.12.13 | 1.22.3 | 2.0.1 | 2.2.3 | 2.5.3 | 2.8.0 | 3.0.5 | 3.3.1
|
||||
| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |
|
||||
| 0.1.x | ✅ | ✅ | ✅ | ✅ | ⚠️ | ⚠️ | ❌ | ❌ |
|
||||
| 0.2.x | ❌ | ❌ | ✅ | ✅ | ✅ | ⚠️ | ❌ | ❌ |
|
||||
| 0.3.x | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| 1.0.x | ❌ | ❌ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ✅ | ✅ |
|
||||
| 1.1.x | ❌ | ❌ | ⚠️ | ⚠️ | ⚠️ | ⚠️ | ✅ | ✅ |
|
||||
|
||||
⚠️ 意为未经过完整的兼容性测试,不建议使用。
|
||||
|
||||
### 特例
|
||||
|
||||
- Flutter 3.7 及以上版本请使用 `flutter_ume_kit_ui: ^1.1.0` 及以上版本
|
||||
|
||||
### 单测覆盖率
|
||||
|
||||
| 包 | master | develop | develop_nullsafety |
|
||||
| ---- | ---- | ---- | ---- |
|
||||
| flutter_ume |  |  |  |
|
||||
| flutter_ume_kit_device |  |  |  |
|
||||
| flutter_ume_kit_perf |  |  |  |
|
||||
| flutter_ume_kit_show_code |  |  |  |
|
||||
| flutter_ume_kit_ui |  |  |  |
|
||||
| flutter_ume_kit_console |  |  |  |
|
||||
| flutter_ume_kit_dio |  | N/A |  |
|
||||
|
||||
### 版本号规则
|
||||
|
||||
请参考 [Semantic versions](https://dart.dev/tools/pub/versioning#semantic-versions)
|
||||
|
||||
### 更新日志
|
||||
|
||||
[Changelog](./CHANGELOG_cn.md)
|
||||
|
||||
## 开源贡献
|
||||
|
||||
贡献文档:[Contributing](./CONTRIBUTING.md)
|
||||
|
||||
### 贡献者
|
||||
|
||||
感谢以下贡献者(排名不分先后):
|
||||
|
||||
| | |
|
||||
| ---- | ---- |
|
||||
|  | [ShirelyC](https://github.com/smileShirely) |
|
||||
|  | [lpylpyleo](https://github.com/lpylpyleo) |
|
||||
|  | [Alex Li](https://github.com/AlexV525) |
|
||||
|  | [Swain](https://github.com/talisk) |
|
||||
|  | [mengdouer](https://github.com/mengdouer) |
|
||||
|  | [LAIIIHZ](https://github.com/laiiihz) |
|
||||
|  | [XinLei](https://github.com/Vadaski) |
|
||||
|  | [suli](https://github.com/suli1) |
|
||||
|  | [wei-spring](https://github.com/wei-spring) |
|
||||
|
||||
### 社区插件
|
||||
|
||||
- [flutter_ume_kit_channel_monitor](https://pub.dev/packages/flutter_ume_kit_channel_monitor)
|
||||
- channel 通信监控工具
|
||||
- 源代码托管于 https://github.com/bytedance/flutter_ume/tree/master/kits/flutter_ume_kit_channel_monitor
|
||||
- [flutter_ume_kit_slow_animation](https://pub.dev/packages/flutter_ume_kit_slow_animation)
|
||||
- 动画速度调节插件
|
||||
- 源代码托管于 https://github.com/cfug/flutter_ume_kits
|
||||
- [flutter_ume_kit_shared_preferences](https://pub.dev/packages/flutter_ume_kit_shared_preferences)
|
||||
- shared_preferences 调试工具
|
||||
- 源代码托管于 https://github.com/cfug/flutter_ume_kits
|
||||
- [flutter_ume_kit_designer_check](https://pub.dev/packages/)
|
||||
- 设计稿比对工具
|
||||
- 源代码托管于 https://github.com/cfug/flutter_ume_kits
|
||||
- [flutter_ume_kit_clean_local_data](https://pub.dev/packages/flutter_ume_kit_clean_local_data)
|
||||
- 清理本地数据插件
|
||||
- 源代码托管于 https://github.com/cfug/flutter_ume_kits 。
|
||||
- [flutter_ume_kit_database_kit](https://pub.dev/packages/flutter_ume_kit_database_kit)
|
||||
- 数据库调试插件
|
||||
- 源代码托管于 https://github.com/cfug/flutter_ume_kits 。
|
||||
- [ume_kit_monitor](https://pub.dev/packages/ume_kit_monitor)
|
||||
- 参数监控插件
|
||||
- 源代码托管于 https://github.com/fastcode555/ume_kit_monitor 。
|
||||
- [json2dart_viewerffi](https://pub.dev/packages/json2dart_viewerffi)
|
||||
- 数据库调试插件
|
||||
- 源代码托管于 https://github.com/fastcode555/Json2Dart_Null_Safety 。
|
||||
- [json2dart_viewer](https://pub.dev/packages/json2dart_viewer)
|
||||
- 数据库调试插件
|
||||
- 源代码托管于 https://github.com/fastcode555/Json2Dart_Null_Safety 。
|
||||
- [memory_detector_of_kit](https://github.com/bladeofgod/memory_detector_of_kit)
|
||||
- 内存泄漏检测插件
|
||||
- [channel_observer_of_kit](https://github.com/bladeofgod/channel_observer_of_kit)
|
||||
- channel 调用记录监控插件
|
||||
- [flutter-ume-kit-dio-enhance](https://github.com/linversion/flutter-ume-kit-dio-enhance)
|
||||
- 基于 flutter_ume_kit_dio 扩展了一些功能的插件
|
||||
|
||||
### 第三方开源项目说明
|
||||
|
||||
- 触控标记使用了 [touch_indicator](https://pub.dev/packages/touch_indicator),颜色吸管插件使用了 [cyclop](https://pub.dev/packages/cyclop)。
|
||||
- 对 [cyclop](https://pub.dev/packages/cyclop) 进行了 [fork](https://github.com/talisk/cyclop) 并修改代码以满足需要。当 [PR](https://github.com/rxlabz/cyclop/pull/11) 合入后,我们将通过 pub 的形式依赖。
|
||||
|
||||
## 开源协议
|
||||
|
||||
该项目遵循 MIT 协议,详情请见 [LICENSE](./LICENSE)。
|
||||
|
||||
## 联系开发者
|
||||
|
||||
**可能你:**
|
||||
|
||||
- 发现文档错误、代码有 bug
|
||||
- 使用 UME 后应用运行产生异常
|
||||
- 发现新版本 Flutter 无法兼容
|
||||
- 有好的点子或产品建议
|
||||
|
||||
上述情况均可以[提一个 issue](./CONTRIBUTING.md#如何提-issue)。
|
||||
|
||||
**可能你:**
|
||||
|
||||
- 想与开发者交流
|
||||
- 想与更多 Flutter 开发者交流
|
||||
- 想与 UME 开展交流或合作
|
||||
|
||||
欢迎[加入字节跳动 Flutter 交流群](https://applink.feishu.cn/client/chat/chatter/add_by_link?link_token=67au2f75-3783-41b0-8868-0fc0178f1fd8)
|
||||
|
||||
或随时[联系开发者](mailto:sunkai.dev@bytedance.com)
|
||||
@@ -0,0 +1,419 @@
|
||||
# flutter_ume
|
||||
|
||||
[简体中文](./README.md)
|
||||
|
||||
UME is an in-app debug kits platform for Flutter apps.
|
||||
|
||||
[](https://pub.dev/packages/flutter_ume) [](https://github.com/bytedance/flutter_ume/blob/master/LICENSE) [](https://pub.dev/packages/flutter_ume) ](https://pub.dev/packages/flutter_ume/score) ](https://pub.dev/packages/flutter_ume/score) ](https://pub.dev/packages/flutter_ume/score)
|
||||
|
||||
<img src="https://github.com/bytedance/flutter_ume/raw/master/ume_logo_256.png" width = "128" height = "128" alt="banner" />
|
||||
|
||||
**UME Kits competition is in full swing!** Rich prizes are waiting for you.
|
||||
|
||||
See https://mp.weixin.qq.com/s/RuwiiQAdrGqI00fDhUO77g for more details.
|
||||
|
||||
<img src="https://github.com/bytedance/flutter_ume/raw/master/apk_qrcode.png" width = "256" height = "256" alt="banner" />
|
||||
|
||||
Scan QR code or click link to download apk. Try it now!
|
||||
https://github.com/bytedance/flutter_ume/releases/download/v0.2.1.0/app-debug.apk
|
||||
|
||||
There are 13 plugin kits built in the latest open source version of UME.
|
||||
Developer could create custom plugin kits, and integrate them into UME.
|
||||
Visit [Develop plugin kits for UME](#develop-plugin-kits-for-ume) for more details.
|
||||
|
||||
- [flutter_ume](#flutter_ume)
|
||||
- [Quick Start](#quick-start)
|
||||
- [IMPORTANT](#important)
|
||||
- [Features](#features)
|
||||
- [Develop plugin kits for UME](#develop-plugin-kits-for-ume)
|
||||
- [Access the nested widget debug kits quickly](#access-the-nested-widget-debug-kits-quickly)
|
||||
- [How to use UME in Release/Profile mode](#how-to-use-ume-in-releaseprofile-mode)
|
||||
- [About version](#about-version)
|
||||
- [Compatibility](#compatibility)
|
||||
- [Coverage](#coverage)
|
||||
- [Version upgrade rules](#version-upgrade-rules)
|
||||
- [Null-safety](#null-safety)
|
||||
- [Change log](#change-log)
|
||||
- [Contributing](#contributing)
|
||||
- [Contributors](#contributors)
|
||||
- [About the third-party open-source project dependencies](#about-the-third-party-open-source-project-dependencies)
|
||||
- [LICENSE](#license)
|
||||
- [Contact the author](#contact-the-author)
|
||||
|
||||
## Quick Start
|
||||
|
||||
**All packages whose names are prefixed with `flutter_ume_kit_` are function**
|
||||
**plug-ins of UME, and users can access them according to demand**
|
||||
|
||||
1. Edit `pubspec.yaml`, and add dependencies.
|
||||
|
||||
**↓ Null-safety version, compatible with Flutter 2.x**
|
||||
|
||||
``` yaml
|
||||
dev_dependencies: # Don't use UME in release mode
|
||||
flutter_ume: ^0.3.0+1
|
||||
flutter_ume_kit_ui: ^0.3.0+1
|
||||
flutter_ume_kit_device: ^0.3.0
|
||||
flutter_ume_kit_perf: ^0.3.0
|
||||
flutter_ume_kit_show_code: ^0.3.0
|
||||
flutter_ume_kit_console: ^0.3.0
|
||||
flutter_ume_kit_dio: ^0.3.0
|
||||
```
|
||||
|
||||
**↓ Non-null-safety version, compatible with Flutter 1.x**
|
||||
|
||||
``` yaml
|
||||
dev_dependencies: # Don't use UME in release mode
|
||||
flutter_ume: ^0.1.1
|
||||
flutter_ume_kit_ui: ^0.1.1.1
|
||||
flutter_ume_kit_device: ^0.1.1
|
||||
flutter_ume_kit_perf: ^0.1.1
|
||||
flutter_ume_kit_show_code: ^0.1.1
|
||||
flutter_ume_kit_console: ^0.1.1
|
||||
```
|
||||
|
||||
2. Run `flutter pub get`
|
||||
3. Import packages
|
||||
|
||||
``` dart
|
||||
import 'package:flutter_ume/flutter_ume.dart'; // UME framework
|
||||
import 'package:flutter_ume_kit_ui/flutter_ume_kit_ui.dart'; // UI kits
|
||||
import 'package:flutter_ume_kit_perf/flutter_ume_kit_perf.dart'; // Performance kits
|
||||
import 'package:flutter_ume_kit_show_code/flutter_ume_kit_show_code.dart'; // Show Code
|
||||
import 'package:flutter_ume_kit_device/flutter_ume_kit_device.dart'; // Device info
|
||||
import 'package:flutter_ume_kit_console/flutter_ume_kit_console.dart'; // Show debugPrint
|
||||
import 'package:flutter_ume_kit_dio/flutter_ume_kit_dio.dart'; // Dio Inspector
|
||||
```
|
||||
|
||||
4. Edit main method of your app, register plugin kits and initial UME
|
||||
|
||||
``` dart
|
||||
void main() {
|
||||
if (kDebugMode) {
|
||||
PluginManager.instance // Register plugin kits
|
||||
..register(WidgetInfoInspector())
|
||||
..register(WidgetDetailInspector())
|
||||
..register(ColorSucker())
|
||||
..register(AlignRuler())
|
||||
..register(ColorPicker()) // New feature
|
||||
..register(TouchIndicator()) // New feature
|
||||
..register(Performance())
|
||||
..register(ShowCode())
|
||||
..register(MemoryInfoPage())
|
||||
..register(CpuInfoPage())
|
||||
..register(DeviceInfoPanel())
|
||||
..register(Console())
|
||||
..register(DioInspector(dio: dio)); // Pass in your Dio instance
|
||||
// After flutter_ume 0.3.0
|
||||
runApp(UMEWidget(child: MyApp(), enable: true));
|
||||
// Before flutter_ume 0.3.0
|
||||
runApp(injectUMEWidget(child: MyApp(), enable: true));
|
||||
} else {
|
||||
runApp(MyApp());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
5. `flutter run` for running
|
||||
or `flutter build apk --debug`、`flutter build ios --debug` for building productions.
|
||||
|
||||
> Some functions rely on VM Service, and additional parameters need to be added for local operation to ensure that it can connect to the VM Service.
|
||||
>
|
||||
> Flutter 2.0.x, 2.2.x and other versions run on real devices, `flutter run` needs to add the `--disable-dds` parameter.
|
||||
> After [Pull Request #80900](https://github.com/flutter/flutter/pull/80900) merging, `--disable-dds` was renamed to `--no-dds`.
|
||||
|
||||
## IMPORTANT
|
||||
|
||||
**From `0.1.1`/`0.2.1` version,we don't need set `useRootNavigator: false`.**
|
||||
The following section only applies to versions before version `0.1.1`/`0.2.1` .
|
||||
|
||||
<s>
|
||||
|
||||
Since UME manages the routing stack at the top level, methods such as `showDialog` use `rootNavigator` to pop up by default,
|
||||
therefore **must** pass in the parameter `useRootNavigator: false` in `showDialog`, `showGeneralDialog` and other 'show dialog' methods to avoid navigator errors.
|
||||
|
||||
``` dart
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: const Text('Dialog'),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('OK'))
|
||||
],
|
||||
),
|
||||
useRootNavigator: false); // <===== It's very IMPORTANT!
|
||||
```
|
||||
|
||||
</s>
|
||||
|
||||
## Features
|
||||
|
||||
There are 13 plugin kits built in the current open source version of UME.
|
||||
|
||||
<table border="1" width="100%">
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><p>UI kits</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/widget_info.png" width="100%" alt="Widget Info" /></br>Widget Info</td>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/widget_detail.png" width="100%" alt="Widget Detail" /></br>Widget Detail</td>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/align_ruler.png" width="100%" alt="Align Ruler" /></br>Align Ruler</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/color_picker.png" width="100%" alt="Color Picker" /></br>Color Picker</td>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/color_sucker.png" width="100%" alt="Color Sucker" /></br>Color Sucker</td>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/touch_indicator.png" width="100%" alt="Touch Indicator" /></br>Touch Indicator</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><p>Performance Kits</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/memory_info.png" width="100%" alt="Memory Info" /></br>Memory Info</td>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/perf_overlay.png" width="100%" alt="Perf Overlay" /></br>Perf Overlay</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><p>Device Info Kits</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/cpu_info.png" width="100%" alt="CPU Info" /></br>CPU Info</td>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/device_info.png" width="100%" alt="Device Info" /></br>Device Info</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><p>Show Code</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/show_code.png" width="100%" alt="Show Code" /></br>Show Code</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><p>Console</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/console.png" width="100%" alt="Console" /></br>Console</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><p>Dio Inspector</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="33.33%" align="center"><img src="https://github.com/bytedance/flutter_ume/raw/master/screenshots/dio_inspector.png" width="100%" alt="Dio Inspector" /></br>Dio Inspector</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Develop plugin kits for UME
|
||||
|
||||
> UME plugins are located in the `./kits` directory, and each one is a `package`.
|
||||
> You can refer to the example in [`./custom_plugin_example`](./custom_plugin_example/) about this chapter.
|
||||
|
||||
1. Run `flutter create -t package custom_plugin` to create your custom plugin kit, it could be `package` or `plugin`.
|
||||
2. Edit `pubspec.yaml` of the custom plugin kit to add UME framework dependency.
|
||||
|
||||
``` yaml
|
||||
dependencies:
|
||||
flutter_ume: '>=0.3.0 <0.4.0'
|
||||
```
|
||||
|
||||
3. Create the class of the plugin kit which should implement `Pluggable`.
|
||||
|
||||
``` dart
|
||||
import 'package:flutter_ume/flutter_ume.dart';
|
||||
|
||||
class CustomPlugin implements Pluggable {
|
||||
CustomPlugin({Key key});
|
||||
|
||||
@override
|
||||
Widget buildWidget(BuildContext context) => Container(
|
||||
color: Colors.white
|
||||
width: 100,
|
||||
height: 100,
|
||||
child: Center(
|
||||
child: Text('Custom Plugin')
|
||||
),
|
||||
); // The panel of the plugin kit
|
||||
|
||||
@override
|
||||
String get name => 'CustomPlugin'; // The name of the plugin kit
|
||||
|
||||
@override
|
||||
String get displayName => 'CustomPlugin';
|
||||
|
||||
@override
|
||||
void onTrigger() {} // Call when tap the icon of plugin kit
|
||||
|
||||
@override
|
||||
ImageProvider<Object> get iconImageProvider => NetworkImage('url'); // The icon image of the plugin kit
|
||||
}
|
||||
```
|
||||
|
||||
4. Use your custom plugin kit in project
|
||||
|
||||
1. Edit `pubspec.yaml` of host app project to add `custom_plugin` dependency.
|
||||
|
||||
``` yaml
|
||||
dev_dependencies:
|
||||
custom_plugin:
|
||||
path: path/to/custom_plugin
|
||||
```
|
||||
|
||||
2. Run `flutter pub get`
|
||||
|
||||
3. Import package
|
||||
|
||||
``` dart
|
||||
import 'package:custom_plugin/custom_plugin.dart';
|
||||
```
|
||||
|
||||
5. Edit main method of your app, register your custom_plugin plugin kit
|
||||
|
||||
``` dart
|
||||
if (kDebugMode) {
|
||||
PluginManager.instance
|
||||
..register(CustomPlugin());
|
||||
runApp(
|
||||
UMEWidget(
|
||||
child: MyApp(),
|
||||
enable: true
|
||||
)
|
||||
);
|
||||
} else {
|
||||
runApp(MyApp());
|
||||
}
|
||||
```
|
||||
|
||||
6. Run your app
|
||||
|
||||
### Access the nested widget debug kits quickly
|
||||
|
||||
We introduce the `PluggableWithNestedWidget` from `0.3.0`. It is used to insert nested Widgets in the Widget tree and quickly access embedded kits with nested widget.
|
||||
|
||||
For more details, see [./kits/flutter_ume_kit_ui/lib/components/color_picker/color_picker.dart](https://github.com/bytedance/flutter_ume/blob/master/kits/flutter_ume_kit_ui/lib/components/color_picker/color_picker.dart) and [./kits/flutter_ume_kit_ui/lib/components/touch_indicator/touch_indicator.dart](https://github.com/bytedance/flutter_ume/blob/master/kits/flutter_ume_kit_ui/lib/components/touch_indicator/touch_indicator.dart).
|
||||
|
||||
The key steps are as follows:
|
||||
|
||||
1. The class of your plugin should implement `PluggableWithNestedWidget`.
|
||||
2. Implements `Widget buildNestedWidget(Widget child)`. Handling the nested widgets and returning the new Widget.
|
||||
|
||||
## How to use UME in Release/Profile mode
|
||||
|
||||
**Once you use flutter_ume in Release/Profile mode, you agree that you will**
|
||||
**bear the relevant risks by yourself.**
|
||||
|
||||
**The maintainer of flutter_ume does not assume any responsibility for the accident**
|
||||
**caused by this.**
|
||||
|
||||
**We recommend not to use it in Release/Profile mode for the following reasons:**
|
||||
|
||||
1. VM Service is not available in these environments, so some functions are not available
|
||||
2. In this environment, developers need to isolate the app distribution channels by themselves to avoid submitting relevant debugging code to the production environment
|
||||
|
||||
In order to use in Release/Profile mode, the details that need to be adjusted in the normal access process:
|
||||
|
||||
1. In `pubspec.yaml`, `flutter_ume` and plugins should be write below `dependencies` rather than `dev_dependencies`.
|
||||
2. Don't put the code which call `PluginManager.instance.register()` and `UMEWidget(child: App())` into conditionals which represent debug mode. (Such as `kDebugMode`)
|
||||
3. Ensure the above details, run `flutter clean` and `flutter pub get`, then build your app.
|
||||
|
||||
## About version
|
||||
|
||||
### Compatibility
|
||||
|
||||
| UME version | Flutter 1.12.13 | Flutter 1.22.3 | Flutter 2.0.1 | Flutter 2.2.3 | Flutter 2.5.3 |
|
||||
| ---- | ---- | ---- | ---- | ---- | ---- |
|
||||
| 0.1.x | ✅ | ✅ | ✅ | ✅ | ⚠️ |
|
||||
| 0.2.x | ❌ | ❌ | ✅ | ✅ | ✅ |
|
||||
| 0.3.x | ❌ | ❌ | ✅ | ✅ | ✅ |
|
||||
|
||||
⚠️ means the version has not been fully tested for compatibility.
|
||||
|
||||
⚠️ means the version has not been fully tested for compatibility.
|
||||
### Coverage
|
||||
|
||||
| Package | master | develop | develop_nullsafety |
|
||||
| ---- | ---- | ---- | ---- |
|
||||
| flutter_ume |  |  |  |
|
||||
| flutter_ume_kit_device |  |  |  |
|
||||
| flutter_ume_kit_perf |  |  |  |
|
||||
| flutter_ume_kit_show_code |  |  |  |
|
||||
| flutter_ume_kit_ui |  |  |  |
|
||||
| flutter_ume_kit_console |  |  |  |
|
||||
| flutter_ume_kit_dio |  | N/A |  |
|
||||
|
||||
### Version upgrade rules
|
||||
|
||||
Please refer to [Semantic versions](https://dart.dev/tools/pub/versioning#semantic-versions) for details.
|
||||
|
||||
### Null-safety
|
||||
|
||||
| Package | Suggest version |
|
||||
| ---- | ---- |
|
||||
| flutter_ume | 0.3.0+1 |
|
||||
| flutter_ume_kit_ui | 0.3.0+1 |
|
||||
| flutter_ume_kit_device | 0.3.0 |
|
||||
| flutter_ume_kit_perf | 0.3.0 |
|
||||
| flutter_ume_kit_show_code | 0.3.0 |
|
||||
| flutter_ume_kit_console | 0.3.0 |
|
||||
| flutter_ume_kit_dio | 0.3.0 |
|
||||
|
||||
### Change log
|
||||
|
||||
[Changelog](./CHANGELOG.md)
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributing rules: [Contributing](./CONTRIBUTING_en.md)
|
||||
|
||||
### Contributors
|
||||
|
||||
Thanks to the following contributors (names not listed in order):
|
||||
|
||||
| | |
|
||||
| ---- | ---- |
|
||||
|  | [ShirelyC](https://github.com/smileShirely) |
|
||||
|  | [lpylpyleo](https://github.com/lpylpyleo) |
|
||||
|  | [Alex Li](https://github.com/AlexV525) |
|
||||
|  | [Swain](https://github.com/talisk) |
|
||||
|  | [harbor](https://github.com/zzm990321) |
|
||||
|
||||
### About the third-party open-source project dependencies
|
||||
|
||||
- The TouchIndicator use the pub [touch_indicator](https://pub.dev/packages/touch_indicator), the ColorPicker use the pub [cyclop](https://pub.dev/packages/cyclop).
|
||||
- We [fork](https://github.com/talisk/cyclop) the package [cyclop](https://pub.dev/packages/cyclop) and modify some code meet our functional needs. We should depend cyclop by pub version after the [PR](https://github.com/rxlabz/cyclop/pull/11) being merged.
|
||||
|
||||
## LICENSE
|
||||
|
||||
This project is licensed under the MIT License - visit the [LICENSE](./LICENSE) for details.
|
||||
|
||||
## Contact the author
|
||||
|
||||
**Maybe...**
|
||||
|
||||
- Found a bug in the code, or an error in the documentation
|
||||
- Produces an exception when you use the UME
|
||||
- UME is not compatible with the new version Flutter
|
||||
- Have a good idea or suggestion
|
||||
|
||||
You can [submit an issue](./CONTRIBUTING_en.md#how-to-raise-an-issue) in any of the above situations.
|
||||
|
||||
**Maybe...**
|
||||
|
||||
- Communicate with the author
|
||||
- Communicate with more community developers
|
||||
- Cooperate with UME
|
||||
|
||||
Welcome to [Join the ByteDance Flutter Exchange Group](https://applink.feishu.cn/client/chat/chatter/add_by_link?link_token=67au2f75-3783-41b0-8868-0fc0178f1fd8).
|
||||
|
||||
Or contact [author](mailto:sunkai.dev@bytedance.com).
|
||||
@@ -0,0 +1 @@
|
||||
include: package:lints/core.yaml
|
||||
|
After Width: | Height: | Size: 9.9 KiB |
@@ -0,0 +1,20 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="94" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<clipPath id="a">
|
||||
<rect width="94" height="20" rx="3" fill="#fff"/>
|
||||
</clipPath>
|
||||
<g clip-path="url(#a)">
|
||||
<path fill="#555" d="M0 0h59v20H0z"/>
|
||||
<path fill="#88ca03" d="M59 0h35v20H59z"/>
|
||||
<path fill="url(#b)" d="M0 0h94v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110">
|
||||
<text x="305" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="490">coverage</text>
|
||||
<text x="305" y="140" transform="scale(.1)" textLength="490">coverage</text>
|
||||
<text x="755" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="250">91%</text>
|
||||
<text x="755" y="140" transform="scale(.1)" textLength="250">91%</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,3 @@
|
||||
## 0.0.1
|
||||
|
||||
* First version.
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 ByteDance Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,3 @@
|
||||
# flutter_ume_kit_channel_monitor
|
||||
|
||||
Used to monitor channel transmissions in Flutter.
|
||||
@@ -0,0 +1,4 @@
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
@@ -0,0 +1,3 @@
|
||||
library flutter_ume_kit_channel_monitor;
|
||||
|
||||
export 'src/channel_plugin.dart';
|
||||
@@ -0,0 +1,30 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_ume/flutter_ume.dart';
|
||||
import 'package:flutter_ume_kit_channel_monitor/src/ui/channel_pages.dart';
|
||||
import 'dart:convert';
|
||||
import 'core/channel_binding.dart';
|
||||
import 'icon.dart' as icon;
|
||||
|
||||
class ChannelPlugin extends Pluggable {
|
||||
ChannelPlugin() {
|
||||
ChannelBinding.ensureInitialized();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildWidget(BuildContext? context) {
|
||||
return const ChannelPages();
|
||||
}
|
||||
|
||||
@override
|
||||
String get displayName => 'Channel Monitor';
|
||||
|
||||
@override
|
||||
ImageProvider<Object> get iconImageProvider =>
|
||||
MemoryImage(base64Decode(icon.iconData));
|
||||
|
||||
@override
|
||||
String get name => 'Channel Monitor';
|
||||
|
||||
@override
|
||||
void onTrigger() {}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_ume_kit_channel_monitor/src/core/ume_binary_messenger.dart';
|
||||
|
||||
class ChannelBinding extends WidgetsFlutterBinding {
|
||||
static WidgetsBinding? ensureInitialized() {
|
||||
if (WidgetsBinding.instance == null) {
|
||||
// make sure init this before WidgetsFlutterBinding ensureInitialized called
|
||||
ChannelBinding();
|
||||
}
|
||||
return WidgetsBinding.instance;
|
||||
}
|
||||
|
||||
@override
|
||||
@protected
|
||||
// 替换 BinaryMessenger
|
||||
BinaryMessenger createBinaryMessenger() {
|
||||
return UmeBinaryMessenger.binaryMessenger;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter_ume_kit_channel_monitor/src/core/channel_info_model.dart';
|
||||
import 'package:flutter_ume_kit_channel_monitor/src/core/channel_store.dart';
|
||||
|
||||
class _ChannelController {
|
||||
final StandardMethodCodec codec = const StandardMethodCodec();
|
||||
|
||||
void trackChannelEvent(String channel, DateTime sendTime, bool send,
|
||||
{ByteData? data,
|
||||
MessageHandler? handler,
|
||||
ui.PlatformMessageResponseCallback? callback}) {
|
||||
MethodCall call = const MethodCall('unknown');
|
||||
try {
|
||||
call = codec.decodeMethodCall(data);
|
||||
} catch (e) {
|
||||
debugPrint('decode data failed, caused by: $e');
|
||||
debugPrint('data: ${data.toString()}');
|
||||
}
|
||||
final ChannelInfoModel model = ChannelInfoModel(
|
||||
type: ChannelType.method,
|
||||
channelName: channel,
|
||||
direction: send
|
||||
? TransDirection.flutterToNative
|
||||
: TransDirection.nativeToFlutter,
|
||||
methodName: call.method,
|
||||
timestamp: sendTime,
|
||||
duration: DateTime.now().difference(sendTime),
|
||||
sendDataSize: send ? (data?.elementSizeInBytes ?? 0) : 0,
|
||||
sendData: send ? call.arguments : null,
|
||||
receiveData: send ? null : call.arguments,
|
||||
receiveDataSize: send ? 0 : (data?.elementSizeInBytes ?? 0),
|
||||
);
|
||||
channelStore.saveChannelInfo(model);
|
||||
}
|
||||
}
|
||||
|
||||
_ChannelController channelController = _ChannelController();
|
||||
@@ -0,0 +1,45 @@
|
||||
class ChannelInfoModel {
|
||||
// channel 传输方向
|
||||
final TransDirection direction;
|
||||
// channel 名称
|
||||
final String channelName;
|
||||
// 方法名称
|
||||
final String methodName;
|
||||
// channel 类型(Method / Event / Basic)
|
||||
final ChannelType type;
|
||||
// 发送时间
|
||||
final DateTime timestamp;
|
||||
// 发送耗时
|
||||
final Duration duration;
|
||||
// 输入数据大小(可能为空)
|
||||
final int? sendDataSize;
|
||||
// 返回数据大小(可能为空)
|
||||
final int? receiveDataSize;
|
||||
// 是否为系统channel
|
||||
bool get isSystemChannel => methodName.contains('flutter/');
|
||||
// 发送数据内容(可能为空)
|
||||
final dynamic sendData;
|
||||
// 接收的数据内容(可能为空)
|
||||
final dynamic receiveData;
|
||||
|
||||
ChannelInfoModel({
|
||||
required this.channelName,
|
||||
required this.direction,
|
||||
required this.methodName,
|
||||
required this.timestamp,
|
||||
required this.duration,
|
||||
required this.sendDataSize,
|
||||
required this.type,
|
||||
this.receiveDataSize,
|
||||
this.sendData,
|
||||
this.receiveData,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ChannelInfoModel{direction: $direction, channelName: $channelName, methodName: $methodName, type: $type, timestamp: $timestamp, duration: $duration, sendDataSize: $sendDataSize, receiveDataSize: $receiveDataSize, sendData: $sendData, receiveData: $receiveData}';
|
||||
}
|
||||
}
|
||||
|
||||
enum TransDirection { flutterToNative, nativeToFlutter }
|
||||
enum ChannelType { event, method, basic }
|
||||
@@ -0,0 +1,39 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_ume_kit_channel_monitor/src/core/channel_info_model.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
|
||||
class _ChannelStore {
|
||||
final BehaviorSubject<List<String>> _orderedChannelNamePublisher =
|
||||
BehaviorSubject();
|
||||
|
||||
final Map<String, List<ChannelInfoModel>> _orderedChannelEvents = {};
|
||||
|
||||
Stream<List<String>> get channelNamePublisher =>
|
||||
_orderedChannelNamePublisher.stream;
|
||||
|
||||
void saveChannelInfo(ChannelInfoModel model) {
|
||||
if (_orderedChannelEvents[model.channelName] == null) {
|
||||
_orderedChannelEvents[model.channelName] = [];
|
||||
}
|
||||
_orderedChannelEvents[model.channelName]!.add(model);
|
||||
refresh();
|
||||
}
|
||||
|
||||
void getChannelByName(String name, Sink sink) {
|
||||
if (name == '') {
|
||||
return;
|
||||
}
|
||||
sink.add(_orderedChannelEvents[name]);
|
||||
}
|
||||
|
||||
void clearChannelRecords() {
|
||||
refresh();
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
_orderedChannelNamePublisher.add(_orderedChannelEvents.keys.toList());
|
||||
}
|
||||
}
|
||||
|
||||
_ChannelStore channelStore = _ChannelStore();
|
||||
@@ -0,0 +1,88 @@
|
||||
import 'dart:async';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_ume_kit_channel_monitor/src/core/channel_controller.dart';
|
||||
|
||||
// 在 _DefaultBinaryMessenger 的基础上增加数据监控
|
||||
class UmeBinaryMessenger extends BinaryMessenger {
|
||||
static UmeBinaryMessenger binaryMessenger = UmeBinaryMessenger._();
|
||||
|
||||
UmeBinaryMessenger._();
|
||||
|
||||
@override
|
||||
Future<void> handlePlatformMessage(String channel, ByteData? data,
|
||||
ui.PlatformMessageResponseCallback? callback) async {
|
||||
DateTime now = DateTime.now();
|
||||
ui.channelBuffers.push(channel, data, (ByteData? data) {
|
||||
if (callback != null) {
|
||||
callback(data);
|
||||
}
|
||||
channelController.trackChannelEvent(channel, now, false,
|
||||
data: data, callback: callback);
|
||||
// print(
|
||||
// '\n handlePlatformMessage: channel: $channel \n data:${data.toString()} \n');
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ByteData?>? send(String channel, ByteData? message) {
|
||||
DateTime now = DateTime.now();
|
||||
final Completer<ByteData?> completer = Completer<ByteData?>();
|
||||
// ui.PlatformDispatcher.instance is accessed directly instead of using
|
||||
// ServicesBinding.instance.platformDispatcher because this method might be
|
||||
// invoked before any binding is initialized. This issue was reported in
|
||||
// #27541. It is not ideal to statically access
|
||||
// ui.PlatformDispatcher.instance because the PlatformDispatcher may be
|
||||
// dependency injected elsewhere with a different instance. However, static
|
||||
// access at this location seems to be the least bad option.
|
||||
// TODO(ianh): Use ServicesBinding.instance once we have better diagnostics
|
||||
// on that getter.
|
||||
ui.PlatformDispatcher.instance.sendPlatformMessage(channel, message,
|
||||
(ByteData? reply) {
|
||||
try {
|
||||
completer.complete(reply);
|
||||
} catch (exception, stack) {
|
||||
FlutterError.reportError(FlutterErrorDetails(
|
||||
exception: exception,
|
||||
stack: stack,
|
||||
library: 'services library',
|
||||
context:
|
||||
ErrorDescription('during a platform message response callback'),
|
||||
));
|
||||
}
|
||||
});
|
||||
channelController.trackChannelEvent(channel, now, true, data: message);
|
||||
// print('\n send \n channel: $channel \n message: ${message.toString()} \n}');
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
@override
|
||||
void setMessageHandler(String channel, MessageHandler? handler) {
|
||||
DateTime now = DateTime.now();
|
||||
if (handler == null) {
|
||||
ui.channelBuffers.clearListener(channel);
|
||||
} else {
|
||||
ui.channelBuffers.setListener(channel,
|
||||
(ByteData? data, ui.PlatformMessageResponseCallback callback) async {
|
||||
ByteData? response;
|
||||
try {
|
||||
response = await handler(data);
|
||||
} catch (exception, stack) {
|
||||
FlutterError.reportError(FlutterErrorDetails(
|
||||
exception: exception,
|
||||
stack: stack,
|
||||
library: 'services library',
|
||||
context: ErrorDescription('during a platform message callback'),
|
||||
));
|
||||
} finally {
|
||||
callback(response);
|
||||
}
|
||||
});
|
||||
}
|
||||
channelController.trackChannelEvent(channel, now, false, handler: handler);
|
||||
// print(
|
||||
// '\n setMessageHandler \n channel: $channel \n handler: ${handler.toString()} \n');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_ume_kit_channel_monitor/src/core/channel_info_model.dart';
|
||||
import 'package:flutter_ume_kit_channel_monitor/src/core/channel_store.dart';
|
||||
import 'package:flutter_ume_kit_channel_monitor/src/ui/template_ui.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
|
||||
class ChannelPages extends StatefulWidget {
|
||||
const ChannelPages({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<ChannelPages> createState() => _ChannelPagesState();
|
||||
}
|
||||
|
||||
class _ChannelPagesState extends State<ChannelPages> {
|
||||
int currentIndex = 0;
|
||||
String currentChannel = '';
|
||||
ChannelInfoModel? currentModel;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return IndexedStack(
|
||||
index: currentIndex,
|
||||
children: [
|
||||
buildOrderedChannels(context),
|
||||
buildSingleChannelPage(),
|
||||
buildChannelInfoPage(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
ChannelInfoPage buildChannelInfoPage() {
|
||||
return ChannelInfoPage(
|
||||
channelInfoModel: currentModel,
|
||||
onBackPressed: () {
|
||||
currentIndex = 1;
|
||||
currentModel = null;
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
SingleChannelPage buildSingleChannelPage() {
|
||||
return SingleChannelPage(
|
||||
title: currentChannel,
|
||||
onBackPressed: () {
|
||||
currentIndex = 0;
|
||||
currentChannel = '';
|
||||
setState(() {});
|
||||
},
|
||||
onTap: (model) {
|
||||
currentIndex = 2;
|
||||
currentModel = model;
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildOrderedChannels(BuildContext context) {
|
||||
return TemplatePageWidget(
|
||||
title: 'Ordered Channels',
|
||||
body: StreamBuilder<List<String>>(
|
||||
stream: channelStore.channelNamePublisher,
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
final List<String> channels = snapshot.data as List<String>;
|
||||
return ListView.builder(
|
||||
padding: EdgeInsets.zero,
|
||||
itemCount: channels.length,
|
||||
itemBuilder: (context, index) {
|
||||
return TemplateItemWidget(
|
||||
title: channels[index],
|
||||
onTap: () {
|
||||
currentChannel = channels[index];
|
||||
currentIndex = 1;
|
||||
setState(() {});
|
||||
});
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SingleChannelPage extends StatefulWidget {
|
||||
final String title;
|
||||
final VoidCallback onBackPressed;
|
||||
final OnChannelModelSelected onTap;
|
||||
|
||||
const SingleChannelPage({
|
||||
Key? key,
|
||||
required this.title,
|
||||
required this.onBackPressed,
|
||||
required this.onTap,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<SingleChannelPage> createState() => _SingleChannelPageState();
|
||||
}
|
||||
|
||||
class _SingleChannelPageState extends State<SingleChannelPage> {
|
||||
final BehaviorSubject<List<ChannelInfoModel>> _publisher = BehaviorSubject();
|
||||
|
||||
@override
|
||||
void didUpdateWidget(SingleChannelPage oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
channelStore.getChannelByName(widget.title, _publisher.sink);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TemplatePageWidget(
|
||||
onBackPressed: widget.onBackPressed,
|
||||
title: widget.title,
|
||||
body: StreamBuilder<List<ChannelInfoModel>>(
|
||||
stream: _publisher,
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
final List<ChannelInfoModel> channels =
|
||||
snapshot.data as List<ChannelInfoModel>;
|
||||
return ListView.builder(
|
||||
padding: EdgeInsets.zero,
|
||||
itemCount: channels.length,
|
||||
itemBuilder: (context, index) {
|
||||
return TemplateItemWidget(
|
||||
title: channels[index].methodName,
|
||||
onTap: () => widget.onTap(channels[index]),
|
||||
);
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelInfoPage extends StatelessWidget {
|
||||
final ChannelInfoModel? channelInfoModel;
|
||||
final VoidCallback onBackPressed;
|
||||
const ChannelInfoPage(
|
||||
{Key? key, this.channelInfoModel, required this.onBackPressed})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (channelInfoModel == null) {
|
||||
return Container();
|
||||
}
|
||||
ChannelInfoModel model = channelInfoModel!;
|
||||
bool isFlutterToNative = model.direction == TransDirection.flutterToNative;
|
||||
return TemplatePageWidget(
|
||||
title: 'Method : ${model.methodName}',
|
||||
body: SingleChildScrollView(
|
||||
child: Table(
|
||||
border: TableBorder.all(color: Colors.black),
|
||||
children: [
|
||||
buildCell('Channel Name', model.channelName),
|
||||
buildCell('Channel Type',
|
||||
'${model.type.toString().substring(12)} channel'),
|
||||
buildCell(
|
||||
'Is System Channel', model.isSystemChannel ? 'yes' : 'no'),
|
||||
buildCell(
|
||||
'Trans Direction', model.direction.toString().substring(15)),
|
||||
buildCell('Time Cost \n(millisecond)',
|
||||
model.timestamp.millisecond.toString()),
|
||||
if (isFlutterToNative)
|
||||
buildCell('Send Data Size', model.sendDataSize.toString()),
|
||||
if (isFlutterToNative)
|
||||
...buildDataTable('Send Data', model.sendData),
|
||||
if (!isFlutterToNative)
|
||||
buildCell('Receive Data Size', model.receiveDataSize.toString()),
|
||||
if (!isFlutterToNative)
|
||||
...buildDataTable('Receive Data', model.receiveData),
|
||||
],
|
||||
),
|
||||
),
|
||||
onBackPressed: onBackPressed,
|
||||
);
|
||||
}
|
||||
|
||||
List<TableRow> buildDataTable(String title, dynamic data) {
|
||||
if (data is Map) {
|
||||
return (data.keys
|
||||
.map((keyword) => TableRow(children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(keyword.toString())),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(data[keyword].toString()),
|
||||
),
|
||||
]))
|
||||
.toList())
|
||||
..insert(0, buildCell(title, ''));
|
||||
}
|
||||
return [buildCell(title, data.toString())];
|
||||
}
|
||||
|
||||
TableRow buildCell(String type, String desc) {
|
||||
return TableRow(children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Align(alignment: Alignment.centerLeft, child: Text(type)),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(desc.toString()),
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
typedef OnChannelModelSelected = void Function(ChannelInfoModel model);
|
||||
@@ -0,0 +1,98 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class TemplatePageWidget extends StatelessWidget {
|
||||
final String title;
|
||||
final Widget body;
|
||||
final VoidCallback? onBackPressed;
|
||||
|
||||
const TemplatePageWidget({
|
||||
Key? key,
|
||||
required this.title,
|
||||
required this.body,
|
||||
this.onBackPressed,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
body: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: MediaQuery.of(context).padding.top),
|
||||
if (onBackPressed != null)
|
||||
GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: onBackPressed,
|
||||
child: const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 4),
|
||||
child: Icon(
|
||||
Icons.navigate_before_rounded,
|
||||
size: 42,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
buildTitle(),
|
||||
const SizedBox(height: 20),
|
||||
buildBody(),
|
||||
],
|
||||
),
|
||||
))
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Expanded buildBody() {
|
||||
return Expanded(
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.95),
|
||||
borderRadius: BorderRadius.circular(12)),
|
||||
child: body),
|
||||
);
|
||||
}
|
||||
|
||||
Text buildTitle() {
|
||||
return Text(title,
|
||||
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold));
|
||||
}
|
||||
}
|
||||
|
||||
class TemplateItemWidget extends StatelessWidget {
|
||||
final String title;
|
||||
final VoidCallback onTap;
|
||||
const TemplateItemWidget({Key? key, required this.title, required this.onTap})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: 54,
|
||||
decoration: BoxDecoration(
|
||||
border: Border(bottom: BorderSide(color: Colors.grey.shade400))),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () => onTap(),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(child: Text(title, overflow: TextOverflow.clip)),
|
||||
const Icon(
|
||||
Icons.navigate_next_rounded,
|
||||
color: Colors.grey,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
name: flutter_ume_kit_channel_monitor
|
||||
description: channel monitor kit for flutter_ume
|
||||
version: 0.0.1
|
||||
homepage: https://github.com/bytedance/flutter_ume
|
||||
|
||||
environment:
|
||||
sdk: ">=2.12.0 <4.0.0"
|
||||
flutter: ">=1.17.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_ume: ^1.0.1
|
||||
rxdart: ^0.27.3
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ">=1.0.0 <3.0.0"
|
||||
|
||||
flutter:
|
||||
@@ -0,0 +1,4 @@
|
||||
# melos_managed_dependency_overrides: flutter_ume
|
||||
dependency_overrides:
|
||||
flutter_ume:
|
||||
path: ../..
|
||||
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 134 KiB |
@@ -0,0 +1,75 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.packages
|
||||
.pub-cache/
|
||||
.pub/
|
||||
build/
|
||||
|
||||
# Android related
|
||||
**/android/**/gradle-wrapper.jar
|
||||
**/android/.gradle
|
||||
**/android/captures/
|
||||
**/android/gradlew
|
||||
**/android/gradlew.bat
|
||||
**/android/local.properties
|
||||
**/android/**/GeneratedPluginRegistrant.java
|
||||
|
||||
# iOS/XCode related
|
||||
**/ios/**/*.mode1v3
|
||||
**/ios/**/*.mode2v3
|
||||
**/ios/**/*.moved-aside
|
||||
**/ios/**/*.pbxuser
|
||||
**/ios/**/*.perspectivev3
|
||||
**/ios/**/*sync/
|
||||
**/ios/**/.sconsign.dblite
|
||||
**/ios/**/.tags*
|
||||
**/ios/**/.vagrant/
|
||||
**/ios/**/DerivedData/
|
||||
**/ios/**/Icon?
|
||||
**/ios/**/Pods/
|
||||
**/ios/**/.symlinks/
|
||||
**/ios/**/profile
|
||||
**/ios/**/xcuserdata
|
||||
**/ios/.generated/
|
||||
**/ios/Flutter/App.framework
|
||||
**/ios/Flutter/Flutter.framework
|
||||
**/ios/Flutter/Flutter.podspec
|
||||
**/ios/Flutter/Generated.xcconfig
|
||||
**/ios/Flutter/ephemeral
|
||||
**/ios/Flutter/app.flx
|
||||
**/ios/Flutter/app.zip
|
||||
**/ios/Flutter/flutter_assets/
|
||||
**/ios/Flutter/flutter_export_environment.sh
|
||||
**/ios/ServiceDefinitions.json
|
||||
**/ios/Runner/GeneratedPluginRegistrant.*
|
||||
|
||||
# Exceptions to above rules.
|
||||
!**/ios/**/default.mode1v3
|
||||
!**/ios/**/default.mode2v3
|
||||
!**/ios/**/default.pbxuser
|
||||
!**/ios/**/default.perspectivev3
|
||||
@@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: 02c026b03cd31dd3f867e5faeb7e104cce174c5f
|
||||
channel: unknown
|
||||
|
||||
project_type: package
|
||||
@@ -0,0 +1,43 @@
|
||||
# Changelog
|
||||
|
||||
## 1.1.0
|
||||
|
||||
* 增加 `consolePrint` 方法,用于适配其他日志库。
|
||||
|
||||
* Add `consolePrint` method to adapt other log libraries.
|
||||
|
||||
## 1.0.0
|
||||
|
||||
* 正式版
|
||||
|
||||
* Normal version.
|
||||
|
||||
## 1.0.0-dev.0
|
||||
|
||||
* 适配 Flutter 3
|
||||
|
||||
* Adapt Flutter 3
|
||||
|
||||
## 0.3.0
|
||||
|
||||
* 更新版本号
|
||||
|
||||
* Update version
|
||||
|
||||
## 0.2.1
|
||||
|
||||
* null-safety 正式版本
|
||||
|
||||
* Null-Safety formal version.
|
||||
|
||||
## 0.2.0-dev.0
|
||||
|
||||
* 适配 null-safety
|
||||
|
||||
* Adapted Null-Safety.
|
||||
|
||||
## 0.1.0
|
||||
|
||||
* 发布开源版本。
|
||||
|
||||
* Release opensource version.
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 ByteDance Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,57 @@
|
||||
# flutter_ume_kit_console
|
||||
|
||||
[flutter_ume](https://pub.dev/packages/flutter_ume) 是由字节跳动 Flutter Infra 团队出品的应用内调试工具平台。
|
||||
|
||||
flutter_ume_kit_console 是 flutter_ume 的日志查看插件包。接入方式请见 [flutter_ume](https://pub.dev/packages/flutter_ume)。
|
||||
|
||||
此插件无法直接监听 `print` 或 `developer.log`,需要使用 `debugPrint` 方法打印日志,或者结合 [logging](https://pub.dev/packages/logging)、[logger](https://pub.dev/packages/logger) 等日志库使用。
|
||||
|
||||
如果使用其他日志库,可以调用 `consolePrint` 将日志输出到应用内控制台。
|
||||
|
||||
```dart
|
||||
// logging
|
||||
Logger.root.onRecord.listen((record) {
|
||||
consolePrint(record.message);
|
||||
});
|
||||
```
|
||||
|
||||
```dart
|
||||
// logger
|
||||
class UmeConsoleOutput extends LogOutput {
|
||||
@override
|
||||
void output(OutputEvent event) {
|
||||
for (var line in event.lines) {
|
||||
consolePrint(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
----
|
||||
|
||||
[flutter_ume](https://pub.dev/packages/flutter_ume) is an in-app debug kits platform produced for Flutter apps by ByteDance Flutter Infra team.
|
||||
|
||||
flutter_ume_kit_console is the Console kits package of flutter_ume. Please visit [flutter_ume](https://pub.dev/packages/flutter_ume) for details.
|
||||
|
||||
This plugin cannot listen to `print` or `developer.log` directly. You need to use the `debugPrint` method to print logs, or use it with another log library such as [logging](https://pub.dev/packages/logging) or [logger](https://pub.dev/packages/logger).
|
||||
|
||||
If you use another log library, you can call `consolePrint` to print the log to the in-app console.
|
||||
|
||||
```dart
|
||||
// logging
|
||||
Logger.root.onRecord.listen((record) {
|
||||
consolePrint(record.message);
|
||||
});
|
||||
```
|
||||
|
||||
```dart
|
||||
// logger
|
||||
class UmeConsoleOutput extends LogOutput {
|
||||
@override
|
||||
void output(OutputEvent event) {
|
||||
for (var line in event.lines) {
|
||||
consolePrint(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,170 @@
|
||||
SF:lib/console/show_date_time_style.dart
|
||||
DA:1,3
|
||||
DA:3,2
|
||||
DA:5,2
|
||||
DA:7,2
|
||||
DA:9,2
|
||||
DA:11,2
|
||||
DA:18,2
|
||||
DA:20,2
|
||||
DA:22,2
|
||||
DA:24,2
|
||||
DA:26,2
|
||||
LF:11
|
||||
LH:11
|
||||
end_of_record
|
||||
SF:lib/console/console_manager.dart
|
||||
DA:10,6
|
||||
DA:14,4
|
||||
DA:16,4
|
||||
DA:20,2
|
||||
DA:22,2
|
||||
DA:24,2
|
||||
DA:25,2
|
||||
DA:26,2
|
||||
DA:27,2
|
||||
DA:28,4
|
||||
DA:30,0
|
||||
DA:34,8
|
||||
DA:35,6
|
||||
DA:36,4
|
||||
DA:38,2
|
||||
DA:45,2
|
||||
DA:47,2
|
||||
DA:48,2
|
||||
DA:49,6
|
||||
DA:51,2
|
||||
DA:56,2
|
||||
DA:57,4
|
||||
DA:58,2
|
||||
DA:61,2
|
||||
LF:24
|
||||
LH:23
|
||||
end_of_record
|
||||
SF:lib/console/console_panel.dart
|
||||
DA:16,1
|
||||
DA:17,1
|
||||
DA:20,1
|
||||
DA:21,1
|
||||
DA:23,1
|
||||
DA:26,1
|
||||
DA:28,2
|
||||
DA:30,1
|
||||
DA:33,0
|
||||
DA:36,1
|
||||
DA:39,0
|
||||
DA:40,0
|
||||
DA:42,0
|
||||
DA:43,0
|
||||
DA:55,1
|
||||
DA:57,2
|
||||
DA:58,1
|
||||
DA:59,1
|
||||
DA:60,1
|
||||
DA:61,1
|
||||
DA:62,1
|
||||
DA:65,1
|
||||
DA:67,1
|
||||
DA:68,1
|
||||
DA:69,3
|
||||
DA:70,0
|
||||
DA:71,0
|
||||
DA:73,1
|
||||
DA:74,2
|
||||
DA:75,2
|
||||
DA:77,2
|
||||
DA:79,2
|
||||
DA:80,3
|
||||
DA:81,5
|
||||
DA:82,1
|
||||
DA:83,1
|
||||
DA:84,0
|
||||
DA:85,0
|
||||
DA:86,0
|
||||
DA:87,0
|
||||
DA:89,3
|
||||
DA:92,2
|
||||
DA:93,2
|
||||
DA:94,4
|
||||
DA:99,1
|
||||
DA:100,1
|
||||
DA:101,4
|
||||
DA:102,4
|
||||
DA:103,3
|
||||
DA:104,1
|
||||
DA:106,3
|
||||
DA:110,1
|
||||
DA:112,1
|
||||
DA:113,1
|
||||
DA:115,10
|
||||
DA:117,1
|
||||
DA:119,9
|
||||
DA:120,2
|
||||
DA:122,1
|
||||
DA:124,9
|
||||
DA:126,1
|
||||
DA:135,1
|
||||
DA:137,1
|
||||
DA:138,1
|
||||
DA:140,2
|
||||
DA:141,1
|
||||
DA:142,1
|
||||
DA:143,2
|
||||
DA:144,1
|
||||
DA:145,1
|
||||
DA:148,1
|
||||
DA:149,2
|
||||
DA:150,1
|
||||
DA:151,1
|
||||
DA:152,1
|
||||
DA:158,1
|
||||
DA:160,8
|
||||
DA:161,1
|
||||
DA:172,1
|
||||
DA:173,1
|
||||
DA:177,1
|
||||
DA:178,1
|
||||
DA:179,1
|
||||
DA:180,1
|
||||
DA:181,2
|
||||
DA:183,0
|
||||
DA:185,2
|
||||
DA:186,1
|
||||
DA:188,1
|
||||
DA:191,1
|
||||
DA:195,1
|
||||
DA:196,1
|
||||
DA:197,1
|
||||
DA:200,1
|
||||
DA:204,1
|
||||
DA:210,1
|
||||
DA:211,1
|
||||
DA:213,1
|
||||
DA:217,1
|
||||
DA:218,1
|
||||
DA:220,1
|
||||
DA:224,2
|
||||
DA:225,1
|
||||
DA:227,1
|
||||
DA:231,1
|
||||
DA:232,1
|
||||
DA:234,1
|
||||
DA:238,1
|
||||
DA:243,1
|
||||
DA:244,6
|
||||
DA:245,2
|
||||
DA:246,2
|
||||
DA:247,2
|
||||
DA:250,1
|
||||
DA:251,2
|
||||
DA:252,2
|
||||
DA:253,1
|
||||
DA:254,1
|
||||
DA:257,1
|
||||
DA:260,1
|
||||
DA:261,2
|
||||
DA:264,8
|
||||
DA:265,3
|
||||
LF:123
|
||||
LH:111
|
||||
end_of_record
|
||||
@@ -0,0 +1,20 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="94" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<clipPath id="a">
|
||||
<rect width="94" height="20" rx="3" fill="#fff"/>
|
||||
</clipPath>
|
||||
<g clip-path="url(#a)">
|
||||
<path fill="#555" d="M0 0h59v20H0z"/>
|
||||
<path fill="#88ca03" d="M59 0h35v20H59z"/>
|
||||
<path fill="url(#b)" d="M0 0h94v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110">
|
||||
<text x="305" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="490">coverage</text>
|
||||
<text x="305" y="140" transform="scale(.1)" textLength="490">coverage</text>
|
||||
<text x="755" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="250">91%</text>
|
||||
<text x="755" y="140" transform="scale(.1)" textLength="250">91%</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,66 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
const int maxLine = 1000;
|
||||
|
||||
class ConsoleManager {
|
||||
static final Queue<Tuple2<DateTime, String>> _logData = Queue();
|
||||
// ignore: close_sinks
|
||||
static StreamController? _logStreamController;
|
||||
|
||||
static Queue<Tuple2<DateTime, String>> get logData => _logData;
|
||||
|
||||
static StreamController? get streamController => _getLogStreamController();
|
||||
|
||||
static DebugPrintCallback? _originalDebugPrint;
|
||||
|
||||
static StreamController? _getLogStreamController() {
|
||||
if (_logStreamController == null) {
|
||||
_logStreamController = StreamController.broadcast();
|
||||
var transformer =
|
||||
StreamTransformer<dynamic, Tuple2<DateTime, String>>.fromHandlers(
|
||||
handleData: (str, sink) {
|
||||
final now = DateTime.now();
|
||||
if (str is String) {
|
||||
sink.add(Tuple2(now, str));
|
||||
} else {
|
||||
sink.add(Tuple2(now, str.toString()));
|
||||
}
|
||||
});
|
||||
|
||||
_logStreamController!.stream.transform(transformer).listen((value) {
|
||||
if (_logData.length < maxLine) {
|
||||
_logData.addFirst(value);
|
||||
} else {
|
||||
_logData.removeLast();
|
||||
}
|
||||
});
|
||||
}
|
||||
return _logStreamController;
|
||||
}
|
||||
|
||||
static redirectDebugPrint() {
|
||||
if (_originalDebugPrint != null) return;
|
||||
_originalDebugPrint = debugPrint;
|
||||
debugPrint = (String? message, {int? wrapWidth}) {
|
||||
ConsoleManager.streamController!.sink.add(message);
|
||||
if (_originalDebugPrint != null) {
|
||||
_originalDebugPrint!(message, wrapWidth: wrapWidth);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static clearLog() {
|
||||
logData.clear();
|
||||
_logStreamController!.add('UME CONSOLE == ClearLog');
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
static clearRedirect() {
|
||||
debugPrint = _originalDebugPrint!;
|
||||
_originalDebugPrint = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:share/share.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
import 'package:flutter_ume/flutter_ume.dart';
|
||||
import 'package:flutter_ume_kit_console/console/console_manager.dart';
|
||||
import 'package:flutter_ume_kit_console/console/icon.dart' as icon;
|
||||
import 'package:flutter_ume/util/floating_widget.dart';
|
||||
import 'package:flutter_ume/util/store_mixin.dart';
|
||||
import 'package:flutter_ume_kit_console/console/show_date_time_style.dart';
|
||||
|
||||
class Console extends StatefulWidget implements PluggableWithStream {
|
||||
Console({Key? key}) {
|
||||
ConsoleManager.redirectDebugPrint();
|
||||
}
|
||||
|
||||
@override
|
||||
ConsoleState createState() => ConsoleState();
|
||||
|
||||
@override
|
||||
Widget buildWidget(BuildContext? context) => this;
|
||||
|
||||
@override
|
||||
ImageProvider<Object> get iconImageProvider => MemoryImage(icon.iconBytes);
|
||||
|
||||
@override
|
||||
String get name => 'Console';
|
||||
|
||||
@override
|
||||
String get displayName => 'Console';
|
||||
|
||||
@override
|
||||
void onTrigger() {}
|
||||
|
||||
@override
|
||||
Stream get stream => ConsoleManager.streamController!.stream;
|
||||
|
||||
@override
|
||||
StreamFilter get streamFilter => (e) => true;
|
||||
}
|
||||
|
||||
class ConsoleState extends State<Console>
|
||||
with WidgetsBindingObserver, StoreMixin {
|
||||
List<Tuple2<DateTime, String>> _logList = <Tuple2<DateTime, String>>[];
|
||||
StreamSubscription? _subscription;
|
||||
ScrollController? _controller;
|
||||
ShowDateTimeStyle? _showDateTimeStyle;
|
||||
bool _showFilter = false;
|
||||
RegExp? _filterExp;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_subscription?.cancel();
|
||||
_subscription = null;
|
||||
_controller = null;
|
||||
_showDateTimeStyle = ShowDateTimeStyle.datetime;
|
||||
_showFilter = false;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_showDateTimeStyle = ShowDateTimeStyle.none;
|
||||
fetchWithKey('console_panel_datetime_style').then((value) async {
|
||||
if (value != null && value is int) {
|
||||
_showDateTimeStyle = styleById(value);
|
||||
} else {
|
||||
_showDateTimeStyle = ShowDateTimeStyle.datetime;
|
||||
await storeWithKey(
|
||||
'console_panel_datetime_style', idByStyle(_showDateTimeStyle!));
|
||||
}
|
||||
setState(() {});
|
||||
});
|
||||
_controller = ScrollController();
|
||||
_logList = ConsoleManager.logData.toList();
|
||||
_subscription = ConsoleManager.streamController!.stream.listen((onData) {
|
||||
if (mounted) {
|
||||
if (_filterExp != null) {
|
||||
_logList = ConsoleManager.logData.where((e) {
|
||||
return _filterExp!.hasMatch(e.item1.toString()) ||
|
||||
_filterExp!.hasMatch(e.item2);
|
||||
}).toList();
|
||||
} else {
|
||||
_logList = ConsoleManager.logData.toList();
|
||||
}
|
||||
|
||||
setState(() {});
|
||||
_controller!.jumpTo(
|
||||
_controller!.position.maxScrollExtent + 22); // 22 is a magic number
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _refreshConsole() {
|
||||
if (_filterExp != null) {
|
||||
_logList = ConsoleManager.logData.where((e) {
|
||||
return _filterExp!.hasMatch(e.item1.toString()) ||
|
||||
_filterExp!.hasMatch(e.item2);
|
||||
}).toList();
|
||||
} else {
|
||||
_logList = ConsoleManager.logData.toList();
|
||||
}
|
||||
}
|
||||
|
||||
String _dateTimeString(int logIndex) {
|
||||
String result = '';
|
||||
switch (_showDateTimeStyle) {
|
||||
case ShowDateTimeStyle.datetime:
|
||||
result =
|
||||
'${_logList[_logList.length - logIndex - 1].item1.toString().padRight(26, '0')}';
|
||||
break;
|
||||
case ShowDateTimeStyle.time:
|
||||
result =
|
||||
'${_logList[_logList.length - logIndex - 1].item1.toString().padRight(26, '0')}'
|
||||
.substring(11);
|
||||
break;
|
||||
case ShowDateTimeStyle.timestamp:
|
||||
result =
|
||||
'${_logList[_logList.length - logIndex - 1].item1.millisecondsSinceEpoch}';
|
||||
break;
|
||||
case ShowDateTimeStyle.none:
|
||||
result = '';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FloatingWidget(
|
||||
contentWidget: Container(
|
||||
color: Colors.black,
|
||||
child: Stack(children: [
|
||||
ListView.builder(
|
||||
controller: _controller,
|
||||
itemCount: _logList.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 8, right: 8, top: 3, bottom: 3),
|
||||
child: RichText(
|
||||
text: TextSpan(children: [
|
||||
TextSpan(
|
||||
text: _dateTimeString(index),
|
||||
style: TextStyle(
|
||||
color: Colors.white60,
|
||||
fontFamily: 'Courier',
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
)),
|
||||
TextSpan(
|
||||
text:
|
||||
'${_logList[_logList.length - index - 1].item2}',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontFamily: 'Courier',
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
)),
|
||||
]),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
if (_showFilter)
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
child: Container(
|
||||
child: TextField(
|
||||
onChanged: (value) {
|
||||
if (value.isNotEmpty) {
|
||||
_filterExp = RegExp(value);
|
||||
} else {
|
||||
_filterExp = null;
|
||||
}
|
||||
setState(() {});
|
||||
_refreshConsole();
|
||||
},
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
fillColor: Colors.white,
|
||||
filled: true,
|
||||
hintText: 'RegExp',
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(50),
|
||||
),
|
||||
),
|
||||
contentPadding: EdgeInsets.only(
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
),
|
||||
prefixIcon: Icon(Icons.search),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
])),
|
||||
toolbarActions: [
|
||||
Tuple3(
|
||||
'Style',
|
||||
Icon(
|
||||
Icons.access_time,
|
||||
size: 20,
|
||||
),
|
||||
_triggerShowDate),
|
||||
Tuple3(
|
||||
'Clear',
|
||||
Icon(
|
||||
Icons.do_not_disturb,
|
||||
size: 20,
|
||||
),
|
||||
() => ConsoleManager.clearLog()),
|
||||
Tuple3(
|
||||
'Filter',
|
||||
Icon(
|
||||
Icons.search,
|
||||
size: 20,
|
||||
),
|
||||
_triggerFilter),
|
||||
Tuple3(
|
||||
'Share',
|
||||
Icon(
|
||||
Icons.share,
|
||||
size: 20,
|
||||
),
|
||||
_share),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _triggerShowDate() async {
|
||||
_showDateTimeStyle = styleById((idByStyle(_showDateTimeStyle!) + 1) % 4);
|
||||
await storeWithKey(
|
||||
'console_panel_datetime_style', idByStyle(_showDateTimeStyle!));
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void _triggerFilter() {
|
||||
setState(() {
|
||||
_showFilter = !_showFilter;
|
||||
if (!_showFilter) {
|
||||
_filterExp = null;
|
||||
}
|
||||
});
|
||||
_refreshConsole();
|
||||
}
|
||||
|
||||
Future<void> _share() async {
|
||||
if (_logList.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final l = _logList.map((e) => '${e.item1.toString()} ${e.item2}').toList();
|
||||
return Share.share("${l.join('\n')}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import 'console_manager.dart';
|
||||
|
||||
/// Print the message to the console.
|
||||
void consolePrint(String message) {
|
||||
ConsoleManager.streamController!.add(message);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
enum ShowDateTimeStyle { datetime, time, timestamp, none }
|
||||
|
||||
ShowDateTimeStyle styleById(int id) {
|
||||
switch (id) {
|
||||
case 0:
|
||||
return ShowDateTimeStyle.datetime;
|
||||
case 1:
|
||||
return ShowDateTimeStyle.time;
|
||||
case 2:
|
||||
return ShowDateTimeStyle.timestamp;
|
||||
case 3:
|
||||
return ShowDateTimeStyle.none;
|
||||
default:
|
||||
return ShowDateTimeStyle.datetime;
|
||||
}
|
||||
}
|
||||
|
||||
int idByStyle(ShowDateTimeStyle style) {
|
||||
switch (style) {
|
||||
case ShowDateTimeStyle.datetime:
|
||||
return 0;
|
||||
case ShowDateTimeStyle.time:
|
||||
return 1;
|
||||
case ShowDateTimeStyle.timestamp:
|
||||
return 2;
|
||||
case ShowDateTimeStyle.none:
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
library flutter_ume_kit_console;
|
||||
|
||||
export 'console/console_panel.dart';
|
||||
export 'console/console_print.dart';
|
||||
@@ -0,0 +1,27 @@
|
||||
name: flutter_ume_kit_console
|
||||
description: Show debugPrint kit for flutter_ume.
|
||||
version: 1.1.0
|
||||
homepage: https://github.com/bytedance/flutter_ume
|
||||
|
||||
environment:
|
||||
sdk: ">=2.12.0 <4.0.0"
|
||||
flutter: ">=2.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
tuple: ^2.0.0
|
||||
share: ^2.0.4
|
||||
shared_preferences: ^2.0.6
|
||||
flutter_ume: ">=1.0.0 <2.0.0"
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
mockito: ^5.0.12
|
||||
flutter_coverage_badge:
|
||||
git:
|
||||
url: https://github.com/smileShirely/flutter_coverage_badge.git
|
||||
ref: 59b7580f406bb712e9d9049c8c99212946e34f65
|
||||
|
||||
flutter:
|
||||
@@ -0,0 +1,4 @@
|
||||
# melos_managed_dependency_overrides: flutter_ume
|
||||
dependency_overrides:
|
||||
flutter_ume:
|
||||
path: ../..
|
||||
@@ -0,0 +1,75 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.packages
|
||||
.pub-cache/
|
||||
.pub/
|
||||
build/
|
||||
|
||||
# Android related
|
||||
**/android/**/gradle-wrapper.jar
|
||||
**/android/.gradle
|
||||
**/android/captures/
|
||||
**/android/gradlew
|
||||
**/android/gradlew.bat
|
||||
**/android/local.properties
|
||||
**/android/**/GeneratedPluginRegistrant.java
|
||||
|
||||
# iOS/XCode related
|
||||
**/ios/**/*.mode1v3
|
||||
**/ios/**/*.mode2v3
|
||||
**/ios/**/*.moved-aside
|
||||
**/ios/**/*.pbxuser
|
||||
**/ios/**/*.perspectivev3
|
||||
**/ios/**/*sync/
|
||||
**/ios/**/.sconsign.dblite
|
||||
**/ios/**/.tags*
|
||||
**/ios/**/.vagrant/
|
||||
**/ios/**/DerivedData/
|
||||
**/ios/**/Icon?
|
||||
**/ios/**/Pods/
|
||||
**/ios/**/.symlinks/
|
||||
**/ios/**/profile
|
||||
**/ios/**/xcuserdata
|
||||
**/ios/.generated/
|
||||
**/ios/Flutter/App.framework
|
||||
**/ios/Flutter/Flutter.framework
|
||||
**/ios/Flutter/Flutter.podspec
|
||||
**/ios/Flutter/Generated.xcconfig
|
||||
**/ios/Flutter/ephemeral
|
||||
**/ios/Flutter/app.flx
|
||||
**/ios/Flutter/app.zip
|
||||
**/ios/Flutter/flutter_assets/
|
||||
**/ios/Flutter/flutter_export_environment.sh
|
||||
**/ios/ServiceDefinitions.json
|
||||
**/ios/Runner/GeneratedPluginRegistrant.*
|
||||
|
||||
# Exceptions to above rules.
|
||||
!**/ios/**/default.mode1v3
|
||||
!**/ios/**/default.mode2v3
|
||||
!**/ios/**/default.pbxuser
|
||||
!**/ios/**/default.perspectivev3
|
||||
@@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: 02c026b03cd31dd3f867e5faeb7e104cce174c5f
|
||||
channel: unknown
|
||||
|
||||
project_type: package
|
||||
@@ -0,0 +1,37 @@
|
||||
# Changelog
|
||||
|
||||
## 1.0.0
|
||||
|
||||
* 正式版
|
||||
|
||||
* Normal version.
|
||||
|
||||
## 1.0.0-dev.0
|
||||
|
||||
* 适配 Flutter 3
|
||||
|
||||
* Adapt Flutter 3
|
||||
|
||||
## 0.3.0
|
||||
|
||||
* 更新版本号
|
||||
|
||||
* Update version
|
||||
|
||||
## 0.2.1
|
||||
|
||||
* null-safety 正式版本
|
||||
|
||||
* Null-Safety formal version.
|
||||
|
||||
## 0.2.0-dev.0
|
||||
|
||||
* 适配 null-safety
|
||||
|
||||
* Adapted Null-Safety.
|
||||
|
||||
## 0.1.0
|
||||
|
||||
* 发布开源版本。
|
||||
|
||||
* Release opensource version.
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 ByteDance Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,11 @@
|
||||
# flutter_ume_kit_device
|
||||
|
||||
[flutter_ume](https://pub.dev/packages/flutter_ume) 是由字节跳动 Flutter Infra 团队出品的应用内调试工具平台。
|
||||
|
||||
flutter_ume_kit_device 是 flutter_ume 的设备信息插件包。接入方式请见 [flutter_ume](https://pub.dev/packages/flutter_ume)。
|
||||
|
||||
----
|
||||
|
||||
[flutter_ume](https://pub.dev/packages/flutter_ume) is an in-app debug kits platform produced for Flutter apps by ByteDance Flutter Infra team.
|
||||
|
||||
flutter_ume_kit_device is the Device Info kits package of flutter_ume. Please visit [flutter_ume](https://pub.dev/packages/flutter_ume) for details.
|
||||
@@ -0,0 +1,160 @@
|
||||
SF:lib/components/device_info/device_info_panel.dart
|
||||
DA:11,1
|
||||
DA:13,1
|
||||
DA:14,1
|
||||
DA:16,1
|
||||
DA:19,1
|
||||
DA:21,2
|
||||
DA:23,1
|
||||
DA:26,0
|
||||
DA:29,1
|
||||
DA:36,1
|
||||
DA:38,1
|
||||
DA:39,1
|
||||
DA:42,1
|
||||
DA:43,1
|
||||
DA:44,1
|
||||
DA:45,3
|
||||
DA:46,2
|
||||
DA:47,1
|
||||
DA:48,3
|
||||
DA:49,2
|
||||
DA:50,1
|
||||
DA:52,1
|
||||
DA:53,2
|
||||
DA:54,2
|
||||
DA:56,2
|
||||
DA:57,2
|
||||
DA:60,1
|
||||
DA:61,1
|
||||
DA:62,2
|
||||
DA:63,2
|
||||
DA:64,2
|
||||
DA:65,2
|
||||
DA:66,2
|
||||
DA:67,2
|
||||
DA:68,2
|
||||
DA:69,1
|
||||
DA:70,1
|
||||
DA:71,1
|
||||
DA:72,1
|
||||
DA:73,1
|
||||
DA:74,1
|
||||
DA:75,1
|
||||
DA:76,1
|
||||
DA:77,1
|
||||
DA:78,1
|
||||
DA:79,1
|
||||
DA:80,1
|
||||
DA:81,1
|
||||
DA:82,1
|
||||
DA:83,1
|
||||
DA:84,1
|
||||
DA:85,1
|
||||
DA:86,1
|
||||
DA:87,1
|
||||
DA:91,1
|
||||
DA:92,1
|
||||
DA:93,1
|
||||
DA:94,1
|
||||
DA:95,1
|
||||
DA:96,1
|
||||
DA:97,1
|
||||
DA:98,1
|
||||
DA:99,1
|
||||
DA:100,2
|
||||
DA:101,2
|
||||
DA:102,2
|
||||
DA:103,2
|
||||
DA:104,2
|
||||
DA:108,1
|
||||
DA:110,1
|
||||
DA:113,1
|
||||
DA:114,1
|
||||
DA:115,1
|
||||
DA:117,1
|
||||
DA:120,1
|
||||
DA:121,1
|
||||
DA:123,1
|
||||
DA:131,1
|
||||
DA:132,1
|
||||
DA:133,4
|
||||
DA:134,1
|
||||
DA:135,1
|
||||
DA:136,2
|
||||
LF:83
|
||||
LH:82
|
||||
end_of_record
|
||||
SF:lib/components/cpu_info/cpu_info_page.dart
|
||||
DA:9,1
|
||||
DA:10,1
|
||||
DA:16,1
|
||||
DA:17,1
|
||||
DA:19,1
|
||||
DA:22,1
|
||||
DA:25,0
|
||||
DA:28,1
|
||||
DA:31,1
|
||||
DA:33,2
|
||||
DA:39,1
|
||||
DA:41,3
|
||||
DA:42,1
|
||||
DA:44,1
|
||||
DA:45,1
|
||||
DA:48,1
|
||||
DA:50,1
|
||||
DA:52,1
|
||||
DA:53,2
|
||||
DA:54,5
|
||||
DA:55,5
|
||||
DA:57,2
|
||||
DA:58,2
|
||||
DA:62,1
|
||||
DA:64,1
|
||||
DA:65,4
|
||||
DA:68,1
|
||||
DA:70,1
|
||||
DA:71,2
|
||||
DA:72,3
|
||||
DA:73,3
|
||||
DA:74,3
|
||||
DA:75,3
|
||||
DA:76,3
|
||||
DA:77,3
|
||||
DA:78,3
|
||||
DA:79,3
|
||||
DA:80,3
|
||||
DA:81,3
|
||||
DA:82,1
|
||||
DA:84,2
|
||||
DA:85,1
|
||||
DA:86,1
|
||||
DA:88,2
|
||||
DA:89,1
|
||||
DA:90,1
|
||||
DA:92,2
|
||||
DA:93,1
|
||||
DA:94,1
|
||||
DA:96,2
|
||||
DA:97,1
|
||||
DA:98,1
|
||||
DA:100,2
|
||||
DA:101,1
|
||||
DA:104,1
|
||||
DA:105,1
|
||||
DA:106,3
|
||||
DA:108,2
|
||||
DA:109,2
|
||||
DA:110,1
|
||||
DA:111,2
|
||||
DA:112,1
|
||||
DA:113,1
|
||||
DA:114,5
|
||||
DA:115,5
|
||||
DA:116,5
|
||||
DA:119,3
|
||||
DA:120,2
|
||||
DA:121,1
|
||||
LF:69
|
||||
LH:68
|
||||
end_of_record
|
||||
@@ -0,0 +1,20 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="94" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<clipPath id="a">
|
||||
<rect width="94" height="20" rx="3" fill="#fff"/>
|
||||
</clipPath>
|
||||
<g clip-path="url(#a)">
|
||||
<path fill="#555" d="M0 0h59v20H0z"/>
|
||||
<path fill="#4ecb0e" d="M59 0h35v20H59z"/>
|
||||
<path fill="url(#b)" d="M0 0h94v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110">
|
||||
<text x="305" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="490">coverage</text>
|
||||
<text x="305" y="140" transform="scale(.1)" textLength="490">coverage</text>
|
||||
<text x="755" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="250">98%</text>
|
||||
<text x="755" y="140" transform="scale(.1)" textLength="250">98%</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,123 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:system_info/system_info.dart';
|
||||
import 'package:flutter_ume/flutter_ume.dart';
|
||||
import 'icon.dart' as icon;
|
||||
import 'package:platform/platform.dart';
|
||||
|
||||
class CpuInfoPage extends StatefulWidget implements Pluggable {
|
||||
CpuInfoPage({Key? key, this.child, this.platform = const LocalPlatform()})
|
||||
: super(key: key);
|
||||
|
||||
final Platform platform;
|
||||
|
||||
final Widget? child;
|
||||
|
||||
@override
|
||||
_CpuInfoPageState createState() => _CpuInfoPageState();
|
||||
|
||||
@override
|
||||
Widget buildWidget(BuildContext? context) => this;
|
||||
|
||||
@override
|
||||
String get name => 'CPUInfo';
|
||||
|
||||
@override
|
||||
String get displayName => 'CPUInfo';
|
||||
|
||||
@override
|
||||
void onTrigger() {}
|
||||
|
||||
@override
|
||||
ImageProvider<Object> get iconImageProvider => MemoryImage(icon.iconBytes);
|
||||
}
|
||||
|
||||
class _CpuInfoPageState extends State<CpuInfoPage> {
|
||||
var _deviceInfo = <Map<String, String>>[];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!widget.platform.isAndroid)
|
||||
return Container(
|
||||
color: Colors.white,
|
||||
child: Center(
|
||||
child: Text('Only available on Android device'),
|
||||
),
|
||||
);
|
||||
return Container(
|
||||
color: Colors.white,
|
||||
child: SafeArea(
|
||||
bottom: false,
|
||||
child: ListView.separated(
|
||||
itemBuilder: (ctx, index) => ListTile(
|
||||
title: Text(_deviceInfo[index].keys.first),
|
||||
trailing: Text(_deviceInfo[index].values.first),
|
||||
),
|
||||
separatorBuilder: (ctx, index) => Divider(),
|
||||
itemCount: _deviceInfo.length)),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.platform.isAndroid) _setupData();
|
||||
}
|
||||
|
||||
_setupData() {
|
||||
const int MEGABYTE = 1024 * 1024;
|
||||
final deviceInfo = <Map<String, String>>[];
|
||||
deviceInfo.addAll([
|
||||
{'Kernel architecture': '${SysInfo.kernelArchitecture}'},
|
||||
{'Kernel bitness': '${SysInfo.kernelBitness}'},
|
||||
{'Kernel name': '${SysInfo.kernelName}'},
|
||||
{'Kernel version': '${SysInfo.kernelVersion}'},
|
||||
{'Operating system name': '${SysInfo.operatingSystemName}'},
|
||||
{'Operating system ': '${SysInfo.operatingSystemVersion}'},
|
||||
{'User directory': '${SysInfo.userDirectory}'},
|
||||
{'User id': '${SysInfo.userId}'},
|
||||
{'User name': '${SysInfo.userName}'},
|
||||
{'User space bitness': '${SysInfo.userSpaceBitness}'},
|
||||
{
|
||||
'Total physical memory':
|
||||
'${SysInfo.getTotalPhysicalMemory() ~/ MEGABYTE} MB'
|
||||
},
|
||||
{
|
||||
'Free physical memory':
|
||||
'${SysInfo.getFreePhysicalMemory() ~/ MEGABYTE} MB'
|
||||
},
|
||||
{
|
||||
'Total virtual memory':
|
||||
'${SysInfo.getTotalVirtualMemory() ~/ MEGABYTE} MB'
|
||||
},
|
||||
{
|
||||
'Free virtual memory':
|
||||
'${SysInfo.getFreeVirtualMemory() ~/ MEGABYTE} MB'
|
||||
},
|
||||
{
|
||||
'Virtual memory size':
|
||||
'${SysInfo.getVirtualMemorySize() ~/ MEGABYTE} MB'
|
||||
},
|
||||
]);
|
||||
|
||||
final processors = SysInfo.processors;
|
||||
deviceInfo.add(
|
||||
{'Number of processors': '${processors.length}'},
|
||||
);
|
||||
for (var processor in processors) {
|
||||
deviceInfo.addAll([
|
||||
{
|
||||
'[${processors.indexOf(processor)}] Architecture':
|
||||
'${processor.architecture}'
|
||||
},
|
||||
{'[${processors.indexOf(processor)}] Name': '${processor.name}'},
|
||||
{'[${processors.indexOf(processor)}] Socket': '${processor.socket}'},
|
||||
{'[${processors.indexOf(processor)}] Vendor': '${processor.vendor}'},
|
||||
]);
|
||||
}
|
||||
Future.delayed(Duration(seconds: 1), () {
|
||||
setState(() {
|
||||
_deviceInfo = deviceInfo;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:platform/platform.dart';
|
||||
import 'package:flutter_ume/flutter_ume.dart';
|
||||
import 'icon.dart' as icon;
|
||||
|
||||
class DeviceInfoPanel extends StatefulWidget implements Pluggable {
|
||||
final Platform platform;
|
||||
|
||||
const DeviceInfoPanel({this.platform = const LocalPlatform()});
|
||||
|
||||
@override
|
||||
_DeviceInfoPanelState createState() => _DeviceInfoPanelState();
|
||||
|
||||
@override
|
||||
Widget buildWidget(BuildContext? context) => this;
|
||||
|
||||
@override
|
||||
ImageProvider<Object> get iconImageProvider => MemoryImage(icon.iconBytes);
|
||||
|
||||
@override
|
||||
String get name => 'DeviceInfo';
|
||||
|
||||
@override
|
||||
String get displayName => 'DeviceInfo';
|
||||
|
||||
@override
|
||||
void onTrigger() {}
|
||||
}
|
||||
|
||||
class _DeviceInfoPanelState extends State<DeviceInfoPanel> {
|
||||
String _content = '';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_getDeviceInfo();
|
||||
}
|
||||
|
||||
void _getDeviceInfo() async {
|
||||
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
||||
Map dataMap = Map();
|
||||
if (widget.platform.isAndroid) {
|
||||
AndroidDeviceInfo androidDeviceInfo = await deviceInfo.androidInfo;
|
||||
dataMap = _readAndroidBuildData(androidDeviceInfo);
|
||||
} else if (widget.platform.isIOS) {
|
||||
IosDeviceInfo iosDeviceInfo = await deviceInfo.iosInfo;
|
||||
dataMap = _readIosDeviceInfo(iosDeviceInfo);
|
||||
}
|
||||
StringBuffer buffer = StringBuffer();
|
||||
dataMap.forEach((k, v) {
|
||||
buffer.write('$k: $v\n');
|
||||
});
|
||||
_content = buffer.toString();
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
Map<String, dynamic> _readAndroidBuildData(AndroidDeviceInfo build) {
|
||||
return <String, dynamic>{
|
||||
'version.securityPatch': build.version.securityPatch,
|
||||
'version.sdkInt': build.version.sdkInt,
|
||||
'version.release': build.version.release,
|
||||
'version.previewSdkInt': build.version.previewSdkInt,
|
||||
'version.incremental': build.version.incremental,
|
||||
'version.codename': build.version.codename,
|
||||
'version.baseOS': build.version.baseOS,
|
||||
'board': build.board,
|
||||
'bootloader': build.bootloader,
|
||||
'brand': build.brand,
|
||||
'device': build.device,
|
||||
'display': build.display,
|
||||
'fingerprint': build.fingerprint,
|
||||
'hardware': build.hardware,
|
||||
'host': build.host,
|
||||
'id': build.id,
|
||||
'manufacturer': build.manufacturer,
|
||||
'model': build.model,
|
||||
'product': build.product,
|
||||
'supported32BitAbis': build.supported32BitAbis,
|
||||
'supported64BitAbis': build.supported64BitAbis,
|
||||
'supportedAbis': build.supportedAbis,
|
||||
'tags': build.tags,
|
||||
'type': build.type,
|
||||
'isPhysicalDevice': build.isPhysicalDevice,
|
||||
'androidId': build.id
|
||||
};
|
||||
}
|
||||
|
||||
Map<String, dynamic> _readIosDeviceInfo(IosDeviceInfo data) {
|
||||
return <String, dynamic>{
|
||||
'name': data.name,
|
||||
'systemName': data.systemName,
|
||||
'systemVersion': data.systemVersion,
|
||||
'model': data.model,
|
||||
'localizedModel': data.localizedModel,
|
||||
'identifierForVendor': data.identifierForVendor,
|
||||
'isPhysicalDevice': data.isPhysicalDevice,
|
||||
'utsname.sysname': data.utsname.sysname,
|
||||
'utsname.nodename': data.utsname.nodename,
|
||||
'utsname.release': data.utsname.release,
|
||||
'utsname.version': data.utsname.version,
|
||||
'utsname.machine': data.utsname.machine,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(left: 12, right: 12, top: 32, bottom: 32),
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12.0),
|
||||
color: Colors.black.withOpacity(0.85),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Text(
|
||||
'Device Info',
|
||||
textScaleFactor: 1.15,
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.red),
|
||||
)),
|
||||
Container(
|
||||
constraints: BoxConstraints(
|
||||
maxHeight: MediaQuery.of(context).size.height - 150),
|
||||
child: SingleChildScrollView(
|
||||
physics: BouncingScrollPhysics(),
|
||||
child: Text(_content,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
strutStyle:
|
||||
const StrutStyle(forceStrutHeight: true, height: 2)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import 'dart:convert';
|
||||
|
||||
const iconData =
|
||||
r'iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAANBElEQVR4Xu2dfYxcVRnG33Nndrd8tqFfhAJqNSSasmTvne1iJLpWQ/wANRj5o4qKgKTFhChRY4zYTRtDiUBjQQxpMFL/0DQpRoISASWSWrfZe7ct0ABagy2mEaoCC5TuzsxrBpco2N2Ze84zM+fcefbfnve55/ye++vdnW5njPCLBEhgTgKGbEiABOYmQEF4d5DAPAQoCG8PEqAgvAdIwI4AnyB23DjVIwQoSI8UzWPaEaAgdtw41SMEKEiPFM1j2hGgIHbcONUjBChIjxTNY9oRoCB23DjVIwQoSI8UzWPaEaAgdtw41SMECi1IkiR91Wp1WalUOt8Yc54xpt4jvfbcMVU1UtV/lkqlx/v7+w/u2rVrCgGhsIIkSbJERH4gIh8TkYUIWMwIhsBBVf1ylmW/ExF12XUhBZmVY6+IrHCBw9mgCTTE2Coi30rT9FXbkxROkJGRkdNnZmYeMsYM20LhXGEIVFX18izL7rU9UeEESZLkehHZYguEc4Uj8Fi1Wv3ovn37/mZzskIJMjg4eEq5XL7FGHOtDQzOFJLAERH5Ypqmv7E5XaEEGR4ePqdWq91tjPmwDQzOFJbA+jRN77Q5XaEEGRoaek8URb8VkeU2MDhTWAI3pmm60eZ0hRKkUqmsUtVHRGSxDQzOFJOAqm7IsmzM5nQUxIYaZ4IiQEFm6+ITJKj7tmObpSAUpGM3W4gXoiAUJMT7tmN7piBugjxijNlijHmhVqud1LHWeCEbAhpF0WmquiPPMAVxEERVb86y7Jt5gHNt9wisXr16ca1WO5pnBxTEQRBjzNjExMSGPMC5tnsERkZGller1cavjZRa3QUFoSCt3ivBr6MgDhXavMzLJ4gD8C6MUhAH6BTEAV4goxTEoSgK4gAvkFEK4lAUBXGAF8goBXEoioI4wAtklII4FEVBHOAFMkpBHIqiIA7wAhmlIA5FURAHeIGMUhCHoiiIA7xARimIQ1EUxAFeIKMUxKEoCuIAL5BRCuJQFAVxgBfIKAVxKIqCOMALZJSCOBRFQRzgBTJKQRyKoiAO8AIZpSAORVEQB3iBjFIQh6IoiAO8QEYpiENRFMQBXiCjFMShKAriAC+QUQriUBQFcYAXyCgFcSiKgjjAC2SUgjgURUEc4AUySkEciqIgDvACGaUgDkVREAd4gYxSEIeiKIgDvEBGgxUkjuMLRWSNMaaiqmd0g3cURaeo6gUi0pfj+n9V1WdyrEcvPRhF0Z5SqXT/+Pj4s+jwouUFJ8jw8PCZqnqHql5WtDI6fJ5XRGTDypUrb9uxY0etw9cO5nJBCdL42OW+vr4DInJuMIT93+idaZqu93+b3dlhUIIkSbJNRK7qDqriXlVVP5RlWePTevn1FgLBCJIkycki0vi2gF9gAqr6yyzLPgmOLURcSIJcJCKPFoK6f4c4lKbp2/zbVvd3FIwgcRxfY4y5q/vIirmDY8eOnXbgwIGXi3k6+1PNfsLU373/AJ04jq8zxtxuf1ROzkegXq8vm5ycfJ6U3kxgaGhoaRRFRyhIj98ZFOTEN0BI32LxCdJGiSkIBWnj7RV+NAWhIOHfxW08AQUJXJAkSdaJyA/z3COvvH2VaKmcZ6QQa/teOioDz+f+NaulaZrm+jzwQsBqcohC/wzyxNh9Uj11US/0+KYzLtm1U1bsvC3XufkECfwJYvMyLwVp3REKQkFav1sCX8knCK5AfouFY+lNEgXBVUFBcCy9SaIguCooCI6lN0kUBFcFBcGx9CaJguCqoCA4lt4kURBcFRQEx9KbJAqCq4KC4Fh6k0RBcFVQEBxLb5IoCK4KCoJj6U0SBcFVQUFwLL1JoiC4KigIjqU3SRQEVwUFwbH0JomC4KqgIDiW3iRREFwVFATH0pskCoKrgoLgWHqTREFwVVAQHEtvkigIrgoKgmPpTRIFwVVBQXAsvUmiILgqKAiOpTdJFARXBQXBsfQmiYLgqqAgOJbeJFEQXBUUBMfSmyQKgquCguBYepNEQXBVUBAcS2+SKAiuCgqCY+lNEgXBVUFBcCy9SaIguCooCI6lN0kUBFcFBcGx9CaJguCqoCA4lt4kURBcFRQEx9KbJAqCq4KC4Fh6k0RBcFVQEBxLb5IoCK4KCoJj6U0SBcFVQUFwLL1JoiC4KigIjqU3SRQEVwUFwbH0JomC4KqgIDiW3iRREFwVFATH0pskCoKrgoLgWHqTREFwVVAQHEtvkigIrgoKgmPpTRIFwVVBQXAsvUmiILgqKAiOpTdJFARXBQXBsfQmiYLgqqAgOJbeJFEQXBUUBMfSmyQKgquCguBYepNEQXBVUBAcS2+SKAiuCgqCY+lNEgXBVUFBcCy9SaIguCooCI6lN0kUBFcFBcGx9CaJguCqoCA4lt4kURBcFRQEx9KbJAqCq4KC4Fh6k0RBcFVQEBxLb5IoCK4KCoJj6U0SBcFVQUFwLL1JoiC4KigIjqU3SRQEVwUFwbH0JomC4KqgIDiW3iRREFwVFATH0pskCoKrgoLgWHqTREFwVVAQHEtvkigIrgoKgmPpTRIFwVVBQXAsvUmiILgqKAiOpTdJFARXBQXBsfQmiYLgqqAgOJbeJFEQXBUUBMfSmyQKgquCguBYepNEQXBVUBAcS2+SKAiuCgqCY+lNEgXBVUFBcCy9SaIguCooCI6lN0kUBFcFBUGwPPaymOOvNU3ShWeImKjpOtcFnRQkSZIl9XrdzLdnVZ3Zu3fvC67n6sY8BXGgXt6+WUoP/VzMoadaSzFG6kMfkNqlX5La6Kdbm7FY1W5B4jheY4z5ioh8REROanGLh0Vkx8zMzOb9+/c/1+JM15dREIsKzOE/Sf/YFWIOPmYx/Z+R2iVXyswNt1vPzzfYTkGSJLlRRMYcNv5cvV5fOzk5+bBDRsdGKUhu1CoDV7/XSY43Llm9/Hqprvte7h00G2iXIEmSrBeRO5pdv4U/f1FEkjRND7awtqtLKEhO/OXtN0n57o05p+ZefnzbH0XfeT4srxHUDkFGR0cXTE1NHRGRRYjNqur2LMs+j8hqZwYFyUl34AtDYg49nXNq7uXVz35dqldvgOW1S5BKpXKFqt6D3Gh/f//Ju3fvPobMRGdRkDxEX52SBR8/M89E07X1kYtl+qZ7m67Ls6AdT5AkSb4vIjfk2UeztcaYkYmJiT3N1nXzzylIDvrmxX/IwKfOzTHRfGk9HpXpW+5vvjDHinYIEsfx1tlXrnLsZP6lqvr+LMsehQW2IYiC5IQ68ImzxUz9K+fU3Mvb8WpWOwSpVCpfVdVbYQcXkXK5fM74+PizyEx0FgXJSbRv87VSeuCnOafmXj6z8WdSu+hSWF67fgaJ4/jdxpgDqI2q6h+yLHsfKq9dORQkJ1nzl8dl4KqRnFMnXt74R8PpW38FyfrfkHY8QRr5SZLcJSLXIDasqpdlWYb94QuxsbdkUBALqKVf3yN9N6+zmPzviJ79Lpne/AvRs97hlHOi4XYJIiJRkiQPisgal00bY8YmJiawL925bGieWQpiCTba86CUf7xJoicncifULl77+j8Q6qKluWdbGWijIK9ffvYVra+JyLy/g3WCvf5ZRDalafqTVs7hwxoK4thC9FQmjW+7pIVfVpSFi6W+6kLRpSscrzr/eLsFmZVkoYh8UETOUtVmosyo6pOTk5O/b+vB2xBOQdoAtduRnRCk22fs1PUpSKdId/A6FAQHm4LgWHqTREFwVVAQHEtvkigIrgoKgmPpTRIFwVVBQXAsvUmiILgqKAiOpTdJFARXBQXBsfQmiYLgqqAgOJbeJFEQXBUUBMfSmyQKgquCguBYepNEQXBVDA4OLuvr62v8X/yW39BMVTdkWWb1zi/NfmdnzpPFcXydMSbX++Q8MXafVE+FvMcAjngHkmwEqdVq59VqtaMd2F5QlyiXy6dHUfRMnk0HI0ieQ3GtVMng/wg0nhotPznemKYgvJNIYB4CFIS3BwlQEN4DJGBHgE8QO26c6hECFKRHiuYxrQl8J03TTTbTLi/zfs4Ys93mopwhgQ4TWJem6Y9srukiyKAxZp/NRTlDAh0k8Fq9Xr/E9uMdrAWZfcuZxsc49XXwsLwUCeQiYIx5eHp6eq3thwS5CCKVSuUbqro51465mAQ6R+AlVb0yy7Kdtpd0EqRx0TiONxljvm27Ac6RQJsINN6w+btpmm51yXcWpHHxoaGhC6Io2iIioy6b4SwJOBI4LiKHVbXxs/G2LMsecMzL/U58816v8QmrIvIZY8xy1431wryqRsaYBaraeEd1yF9WvcDtRGc0xtRE5KiqPp2maYbiwFJQJJlTSAIUpJC18lAoAhQERZI5hSRAQQpZKw+FIkBBUCSZU0gCFKSQtfJQKAIUBEWSOYUkQEEKWSsPhSJAQVAkmVNIAhSkkLXyUCgCFARFkjmFJEBBClkrD4UiQEFQJJlTSAIUpJC18lAoAv8GH84Vqjytw1cAAAAASUVORK5CYII=';
|
||||
|
||||
final iconBytes = base64Decode(iconData);
|
||||
@@ -0,0 +1,4 @@
|
||||
library flutter_ume_kit_device;
|
||||
|
||||
export 'components/cpu_info/cpu_info_page.dart';
|
||||
export 'components/device_info/device_info_panel.dart';
|
||||
@@ -0,0 +1,29 @@
|
||||
name: flutter_ume_kit_device
|
||||
description: Device info kit for flutter_ume.
|
||||
version: 1.0.0
|
||||
homepage: https://github.com/bytedance/flutter_ume
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ">=2.12.0 <4.0.0"
|
||||
flutter: ">=2.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_ume:
|
||||
path: ../../../flutter_ume
|
||||
system_info: ^1.0.1
|
||||
device_info_plus: ^12.1.0
|
||||
platform: ^3.0.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
mockito: ^5.0.12
|
||||
flutter_coverage_badge:
|
||||
git:
|
||||
url: https://github.com/smileShirely/flutter_coverage_badge.git
|
||||
ref: 59b7580f406bb712e9d9049c8c99212946e34f65
|
||||
|
||||
flutter:
|
||||
@@ -0,0 +1,4 @@
|
||||
# melos_managed_dependency_overrides: flutter_ume
|
||||
dependency_overrides:
|
||||
flutter_ume:
|
||||
path: ../..
|
||||
@@ -0,0 +1,75 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.packages
|
||||
.pub-cache/
|
||||
.pub/
|
||||
build/
|
||||
|
||||
# Android related
|
||||
**/android/**/gradle-wrapper.jar
|
||||
**/android/.gradle
|
||||
**/android/captures/
|
||||
**/android/gradlew
|
||||
**/android/gradlew.bat
|
||||
**/android/local.properties
|
||||
**/android/**/GeneratedPluginRegistrant.java
|
||||
|
||||
# iOS/XCode related
|
||||
**/ios/**/*.mode1v3
|
||||
**/ios/**/*.mode2v3
|
||||
**/ios/**/*.moved-aside
|
||||
**/ios/**/*.pbxuser
|
||||
**/ios/**/*.perspectivev3
|
||||
**/ios/**/*sync/
|
||||
**/ios/**/.sconsign.dblite
|
||||
**/ios/**/.tags*
|
||||
**/ios/**/.vagrant/
|
||||
**/ios/**/DerivedData/
|
||||
**/ios/**/Icon?
|
||||
**/ios/**/Pods/
|
||||
**/ios/**/.symlinks/
|
||||
**/ios/**/profile
|
||||
**/ios/**/xcuserdata
|
||||
**/ios/.generated/
|
||||
**/ios/Flutter/App.framework
|
||||
**/ios/Flutter/Flutter.framework
|
||||
**/ios/Flutter/Flutter.podspec
|
||||
**/ios/Flutter/Generated.xcconfig
|
||||
**/ios/Flutter/ephemeral
|
||||
**/ios/Flutter/app.flx
|
||||
**/ios/Flutter/app.zip
|
||||
**/ios/Flutter/flutter_assets/
|
||||
**/ios/Flutter/flutter_export_environment.sh
|
||||
**/ios/ServiceDefinitions.json
|
||||
**/ios/Runner/GeneratedPluginRegistrant.*
|
||||
|
||||
# Exceptions to above rules.
|
||||
!**/ios/**/default.mode1v3
|
||||
!**/ios/**/default.mode2v3
|
||||
!**/ios/**/default.pbxuser
|
||||
!**/ios/**/default.perspectivev3
|
||||
@@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: d79295af24c3ed621c33713ecda14ad196fd9c31
|
||||
channel: stable
|
||||
|
||||
project_type: package
|
||||
@@ -0,0 +1,23 @@
|
||||
# Changelog
|
||||
|
||||
## 1.0.0
|
||||
|
||||
* 正式版
|
||||
|
||||
* Normal version.
|
||||
|
||||
## 1.0.0-dev.0
|
||||
|
||||
* 适配 Flutter 3
|
||||
|
||||
* Adapt Flutter 3
|
||||
|
||||
## 0.3.0
|
||||
|
||||
* 更新版本号
|
||||
|
||||
* Update version
|
||||
|
||||
## 0.2.0-dev.0
|
||||
|
||||
* Initial release.
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 ByteDance Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,3 @@
|
||||
# flutter_ume_kit_dio
|
||||
|
||||
Dio kit for flutter_ume.
|
||||
@@ -0,0 +1,229 @@
|
||||
SF:lib/src/models/http_interceptor.dart
|
||||
DA:10,0
|
||||
DA:20,0
|
||||
DA:22,0
|
||||
DA:23,0
|
||||
DA:26,0
|
||||
DA:31,0
|
||||
DA:32,0
|
||||
DA:33,0
|
||||
DA:36,0
|
||||
DA:39,0
|
||||
DA:40,0
|
||||
DA:41,0
|
||||
DA:42,0
|
||||
LF:13
|
||||
LH:0
|
||||
end_of_record
|
||||
SF:lib/src/pluggable.dart
|
||||
DA:18,2
|
||||
DA:19,4
|
||||
DA:24,1
|
||||
DA:25,1
|
||||
DA:27,1
|
||||
DA:29,2
|
||||
DA:31,1
|
||||
DA:34,0
|
||||
DA:37,1
|
||||
DA:40,1
|
||||
LF:10
|
||||
LH:9
|
||||
end_of_record
|
||||
SF:lib/src/constants/extensions.dart
|
||||
DA:10,0
|
||||
DA:11,0
|
||||
DA:13,0
|
||||
DA:14,0
|
||||
DA:16,0
|
||||
DA:17,0
|
||||
DA:19,0
|
||||
DA:20,0
|
||||
LF:8
|
||||
LH:0
|
||||
end_of_record
|
||||
SF:lib/src/containers/http_container.dart
|
||||
DA:14,0
|
||||
DA:18,2
|
||||
DA:23,1
|
||||
DA:24,7
|
||||
DA:27,0
|
||||
DA:29,0
|
||||
DA:30,0
|
||||
DA:31,0
|
||||
DA:34,0
|
||||
DA:35,0
|
||||
DA:38,0
|
||||
DA:39,0
|
||||
DA:42,1
|
||||
DA:43,1
|
||||
DA:44,1
|
||||
DA:47,0
|
||||
DA:48,0
|
||||
DA:49,0
|
||||
DA:50,0
|
||||
DA:53,0
|
||||
DA:55,0
|
||||
DA:56,0
|
||||
LF:22
|
||||
LH:6
|
||||
end_of_record
|
||||
SF:lib/src/instances.dart
|
||||
DA:11,0
|
||||
DA:13,3
|
||||
LF:2
|
||||
LH:1
|
||||
end_of_record
|
||||
SF:lib/src/widgets/pluggable_state.dart
|
||||
DA:16,1
|
||||
DA:20,1
|
||||
DA:23,1
|
||||
DA:24,1
|
||||
DA:26,2
|
||||
DA:33,1
|
||||
DA:35,1
|
||||
DA:37,3
|
||||
DA:40,1
|
||||
DA:42,1
|
||||
DA:43,2
|
||||
DA:44,1
|
||||
DA:45,1
|
||||
DA:50,0
|
||||
DA:51,0
|
||||
DA:52,0
|
||||
DA:53,0
|
||||
DA:54,0
|
||||
DA:58,1
|
||||
DA:59,1
|
||||
DA:60,2
|
||||
DA:61,1
|
||||
DA:68,1
|
||||
DA:78,1
|
||||
DA:80,2
|
||||
DA:81,1
|
||||
DA:82,1
|
||||
DA:83,0
|
||||
DA:84,0
|
||||
DA:85,0
|
||||
DA:86,0
|
||||
DA:87,0
|
||||
DA:88,0
|
||||
DA:89,0
|
||||
DA:90,0
|
||||
DA:92,0
|
||||
DA:93,0
|
||||
DA:99,0
|
||||
DA:100,0
|
||||
DA:101,0
|
||||
DA:117,1
|
||||
DA:119,1
|
||||
DA:121,1
|
||||
DA:122,3
|
||||
DA:123,1
|
||||
DA:125,1
|
||||
DA:126,1
|
||||
DA:128,4
|
||||
DA:130,1
|
||||
DA:134,2
|
||||
DA:136,1
|
||||
DA:137,1
|
||||
DA:138,1
|
||||
DA:140,1
|
||||
DA:141,1
|
||||
DA:143,1
|
||||
DA:145,3
|
||||
DA:147,1
|
||||
DA:148,1
|
||||
DA:150,1
|
||||
DA:156,1
|
||||
DA:157,1
|
||||
DA:158,2
|
||||
DA:159,1
|
||||
DA:172,0
|
||||
DA:175,0
|
||||
DA:179,0
|
||||
DA:180,0
|
||||
DA:186,0
|
||||
DA:188,0
|
||||
DA:189,0
|
||||
DA:192,0
|
||||
DA:193,0
|
||||
DA:196,0
|
||||
DA:198,0
|
||||
DA:201,0
|
||||
DA:204,0
|
||||
DA:207,0
|
||||
DA:210,0
|
||||
DA:213,0
|
||||
DA:214,0
|
||||
DA:217,0
|
||||
DA:220,0
|
||||
DA:223,0
|
||||
DA:230,0
|
||||
DA:233,0
|
||||
DA:236,0
|
||||
DA:237,0
|
||||
DA:238,0
|
||||
DA:240,0
|
||||
DA:244,0
|
||||
DA:245,0
|
||||
DA:246,0
|
||||
DA:248,0
|
||||
DA:251,0
|
||||
DA:252,0
|
||||
DA:253,0
|
||||
DA:254,0
|
||||
DA:262,0
|
||||
DA:263,0
|
||||
DA:264,0
|
||||
DA:265,0
|
||||
DA:267,0
|
||||
DA:272,0
|
||||
DA:273,0
|
||||
DA:274,0
|
||||
DA:276,0
|
||||
DA:277,0
|
||||
DA:282,0
|
||||
DA:283,0
|
||||
DA:287,0
|
||||
DA:289,0
|
||||
DA:294,0
|
||||
DA:295,0
|
||||
DA:296,0
|
||||
DA:297,0
|
||||
DA:301,0
|
||||
DA:303,0
|
||||
DA:305,0
|
||||
DA:306,0
|
||||
DA:307,0
|
||||
DA:308,0
|
||||
DA:309,0
|
||||
DA:311,0
|
||||
DA:320,0
|
||||
DA:322,0
|
||||
DA:324,0
|
||||
DA:326,0
|
||||
DA:328,0
|
||||
DA:330,0
|
||||
DA:331,0
|
||||
DA:333,0
|
||||
DA:334,0
|
||||
DA:343,0
|
||||
DA:348,0
|
||||
DA:354,0
|
||||
DA:355,0
|
||||
DA:356,0
|
||||
DA:357,0
|
||||
DA:358,0
|
||||
DA:361,0
|
||||
DA:366,0
|
||||
DA:369,0
|
||||
DA:370,0
|
||||
DA:372,0
|
||||
DA:374,0
|
||||
DA:382,0
|
||||
DA:386,0
|
||||
DA:387,0
|
||||
DA:388,0
|
||||
LF:150
|
||||
LH:46
|
||||
end_of_record
|
||||
@@ -0,0 +1,20 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="94" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<clipPath id="a">
|
||||
<rect width="94" height="20" rx="3" fill="#fff"/>
|
||||
</clipPath>
|
||||
<g clip-path="url(#a)">
|
||||
<path fill="#555" d="M0 0h59v20H0z"/>
|
||||
<path fill="#e05d44" d="M59 0h35v20H59z"/>
|
||||
<path fill="url(#b)" d="M0 0h94v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110">
|
||||
<text x="305" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="490">coverage</text>
|
||||
<text x="305" y="140" transform="scale(.1)" textLength="490">coverage</text>
|
||||
<text x="755" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="250">30%</text>
|
||||
<text x="755" y="140" transform="scale(.1)" textLength="250">30%</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,4 @@
|
||||
library flutter_ume_kit_dio;
|
||||
|
||||
export 'src/models/http_interceptor.dart';
|
||||
export 'src/pluggable.dart';
|
||||
@@ -0,0 +1,6 @@
|
||||
///
|
||||
/// [Author] Alex (https://github.com/AlexV525)
|
||||
/// [Date] 2021/8/6 13:25
|
||||
///
|
||||
const String DIO_EXTRA_START_TIME = 'ume_start_time';
|
||||
const String DIO_EXTRA_END_TIME = 'ume_end_time';
|
||||
@@ -0,0 +1,21 @@
|
||||
///
|
||||
/// [Author] Alex (https://github.com/AlexV525)
|
||||
/// [Date] 2021/8/6 13:58
|
||||
///
|
||||
import 'package:dio/dio.dart' show Response;
|
||||
|
||||
import 'constants.dart';
|
||||
|
||||
extension ResponseExtension on Response<dynamic> {
|
||||
int get startTimeMilliseconds =>
|
||||
requestOptions.extra[DIO_EXTRA_START_TIME] as int;
|
||||
|
||||
int get endTimeMilliseconds =>
|
||||
requestOptions.extra[DIO_EXTRA_END_TIME] as int;
|
||||
|
||||
DateTime get startTime =>
|
||||
DateTime.fromMillisecondsSinceEpoch(startTimeMilliseconds);
|
||||
|
||||
DateTime get endTime =>
|
||||
DateTime.fromMillisecondsSinceEpoch(endTimeMilliseconds);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
///
|
||||
/// [Author] Alex (https://github.com/AlexV525)
|
||||
/// [Date] 4/13/21 2:49 PM
|
||||
///
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:dio/dio.dart' show Response;
|
||||
|
||||
/// Implements a [ChangeNotifier] to notify listeners when new responses
|
||||
/// were recorded. Use [page] to support paging.
|
||||
class HttpContainer extends ChangeNotifier {
|
||||
/// Store all responses.
|
||||
List<Response<dynamic>> get requests => _requests;
|
||||
final List<Response<dynamic>> _requests = <Response<dynamic>>[];
|
||||
|
||||
/// Paging fields.
|
||||
int get page => _page;
|
||||
int _page = 1;
|
||||
final int _perPage = 10;
|
||||
|
||||
/// Return requests according to the paging.
|
||||
List<Response<dynamic>> get pagedRequests {
|
||||
return _requests.sublist(0, math.min(page * _perPage, _requests.length));
|
||||
}
|
||||
|
||||
bool get _hasNextPage => _page * _perPage < _requests.length;
|
||||
|
||||
void addRequest(Response<dynamic> response) {
|
||||
_requests.insert(0, response);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void loadNextPage() {
|
||||
if (!_hasNextPage) {
|
||||
return;
|
||||
}
|
||||
_page++;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void resetPaging() {
|
||||
_page = 1;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void clearRequests() {
|
||||
_requests.clear();
|
||||
_page = 1;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_requests.clear();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
///
|
||||
/// [Author] Alex (https://github.com/AlexV525)
|
||||
/// [Date] 4/13/21 2:54 PM
|
||||
///
|
||||
import 'containers/http_container.dart';
|
||||
|
||||
/// The inner singleton instance to keep containers.
|
||||
///
|
||||
/// Currently we only have a http container here.
|
||||
class InspectorInstance {
|
||||
const InspectorInstance._();
|
||||
|
||||
static final HttpContainer httpContainer = HttpContainer();
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
///
|
||||
/// [Author] Alex (https://github.com/AlexV525)
|
||||
/// [Date] 4/13/21 1:58 PM
|
||||
///
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
import '../constants/constants.dart';
|
||||
import '../instances.dart';
|
||||
|
||||
int get _timestamp => DateTime.now().millisecondsSinceEpoch;
|
||||
|
||||
/// Implement a [Interceptor] to handle dio methods.
|
||||
///
|
||||
/// Main idea about this interceptor:
|
||||
/// - Use [RequestOptions.extra] to store our timestamps.
|
||||
/// - Add [DIO_EXTRA_START_TIME] when a request was requested.
|
||||
/// - Add [DIO_EXTRA_END_TIME] when a response is respond or thrown an error.
|
||||
/// - Deliver the [Response] to the container.
|
||||
class UMEDioInterceptor extends Interceptor {
|
||||
@override
|
||||
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
|
||||
options.extra[DIO_EXTRA_START_TIME] = _timestamp;
|
||||
handler.next(options);
|
||||
}
|
||||
|
||||
@override
|
||||
void onResponse(
|
||||
Response<dynamic> response,
|
||||
ResponseInterceptorHandler handler,
|
||||
) {
|
||||
response.requestOptions.extra[DIO_EXTRA_END_TIME] = _timestamp;
|
||||
InspectorInstance.httpContainer.addRequest(response);
|
||||
handler.next(response);
|
||||
}
|
||||
|
||||
@override
|
||||
void onError(DioException err, ErrorInterceptorHandler handler) {
|
||||
// Create an empty response with the [RequestOptions] for delivery.
|
||||
// err.response ??= Response<dynamic>(requestOptions: err.requestOptions);
|
||||
err.response!.requestOptions.extra[DIO_EXTRA_END_TIME] = _timestamp;
|
||||
InspectorInstance.httpContainer.addRequest(err.response!);
|
||||
handler.next(err);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
///
|
||||
/// [Author] Alex (https://github.com/AlexV525)
|
||||
/// [Date] 2021/8/6 11:24
|
||||
///
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:dio/dio.dart' show Dio;
|
||||
import 'package:flutter_ume/core/pluggable.dart';
|
||||
|
||||
import 'models/http_interceptor.dart';
|
||||
import 'widgets/icon.dart' as icon;
|
||||
import 'widgets/pluggable_state.dart';
|
||||
|
||||
// TODO(Alex): Implement [PluggableStream] for dot features.
|
||||
/// Implement a [Pluggable] to integrate with UME.
|
||||
class DioInspector extends StatefulWidget implements Pluggable {
|
||||
DioInspector({Key? key, required this.dio}) : super(key: key) {
|
||||
dio.interceptors.add(UMEDioInterceptor());
|
||||
}
|
||||
|
||||
final Dio dio;
|
||||
|
||||
@override
|
||||
DioPluggableState createState() => DioPluggableState();
|
||||
|
||||
@override
|
||||
ImageProvider<Object> get iconImageProvider => MemoryImage(icon.iconBytes);
|
||||
|
||||
@override
|
||||
String get name => 'DioInspector';
|
||||
|
||||
@override
|
||||
String get displayName => 'DioInspector';
|
||||
|
||||
@override
|
||||
void onTrigger() {}
|
||||
|
||||
@override
|
||||
Widget buildWidget(BuildContext? context) => this;
|
||||
}
|
||||
@@ -0,0 +1,418 @@
|
||||
///
|
||||
/// [Author] Alex (https://github.com/AlexV525)
|
||||
/// [Date] 2021/8/6 11:25
|
||||
///
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../constants/extensions.dart';
|
||||
import '../instances.dart';
|
||||
import '../pluggable.dart';
|
||||
|
||||
const JsonEncoder _encoder = JsonEncoder.withIndent(' ');
|
||||
|
||||
ButtonStyle _buttonStyle(
|
||||
BuildContext context, {
|
||||
EdgeInsetsGeometry? padding,
|
||||
}) {
|
||||
return TextButton.styleFrom(
|
||||
padding: padding ?? const EdgeInsets.symmetric(horizontal: 5, vertical: 3),
|
||||
minimumSize: Size.zero,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(999999),
|
||||
),
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
// primary: Colors.white,
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
);
|
||||
}
|
||||
|
||||
class DioPluggableState extends State<DioInspector> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Bind listener to refresh requests.
|
||||
InspectorInstance.httpContainer.addListener(_listener);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
InspectorInstance.httpContainer
|
||||
..removeListener(_listener) // First, remove refresh listener.
|
||||
..resetPaging(); // Then reset the paging field.
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/// Using [setState] won't cause too much performance regression,
|
||||
/// since we've implemented the list with `findChildIndexCallback`.
|
||||
void _listener() {
|
||||
Future.microtask(() {
|
||||
if (mounted &&
|
||||
!context.debugDoingBuild &&
|
||||
context.owner?.debugBuilding != true) {
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Widget _clearAllButton(BuildContext context) {
|
||||
return TextButton(
|
||||
onPressed: InspectorInstance.httpContainer.clearRequests,
|
||||
style: _buttonStyle(
|
||||
context,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 3,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: const <Widget>[
|
||||
Text('Clear'),
|
||||
Icon(Icons.cleaning_services, size: 14),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _itemList(BuildContext context) {
|
||||
final List<Response<dynamic>> requests =
|
||||
InspectorInstance.httpContainer.pagedRequests;
|
||||
final int length = requests.length;
|
||||
if (length > 0) {
|
||||
return CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(_, int index) {
|
||||
final Response<dynamic> r = requests[index];
|
||||
if (index == length - 2) {
|
||||
InspectorInstance.httpContainer.loadNextPage();
|
||||
}
|
||||
return _ResponseCard(
|
||||
key: ValueKey<int>(r.startTimeMilliseconds),
|
||||
response: r,
|
||||
);
|
||||
},
|
||||
childCount: length,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
return const Center(
|
||||
child: Text(
|
||||
'Come back later...\n🧐',
|
||||
style: TextStyle(fontSize: 28),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
color: Colors.black26,
|
||||
child: DefaultTextStyle.merge(
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Container(
|
||||
constraints: BoxConstraints.tightFor(
|
||||
width: double.maxFinite,
|
||||
height: MediaQuery.of(context).size.height / 1.25,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.vertical(
|
||||
top: Radius.circular(20),
|
||||
),
|
||||
color: Theme.of(context).cardColor,
|
||||
),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
const Spacer(),
|
||||
Text(
|
||||
'Dio Requests',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: AlignmentDirectional.centerEnd,
|
||||
child: _clearAllButton(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ColoredBox(
|
||||
color: Theme.of(context).canvasColor,
|
||||
child: _itemList(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ResponseCard extends StatefulWidget {
|
||||
const _ResponseCard({
|
||||
required Key? key,
|
||||
required this.response,
|
||||
}) : super(key: key);
|
||||
|
||||
final Response<dynamic> response;
|
||||
|
||||
@override
|
||||
_ResponseCardState createState() => _ResponseCardState();
|
||||
}
|
||||
|
||||
class _ResponseCardState extends State<_ResponseCard> {
|
||||
final ValueNotifier<bool> _isExpanded = ValueNotifier<bool>(false);
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_isExpanded.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _switchExpand() {
|
||||
_isExpanded.value = !_isExpanded.value;
|
||||
}
|
||||
|
||||
Response<dynamic> get _response => widget.response;
|
||||
|
||||
RequestOptions get _request => _response.requestOptions;
|
||||
|
||||
/// The start time for the [_request].
|
||||
DateTime get _startTime => _response.startTime;
|
||||
|
||||
/// The end time for the [_response].
|
||||
DateTime get _endTime => _response.endTime;
|
||||
|
||||
/// The duration between the request and the response.
|
||||
Duration get _duration => _endTime.difference(_startTime);
|
||||
|
||||
/// Status code for the [_response].
|
||||
int get _statusCode => _response.statusCode ?? 0;
|
||||
|
||||
/// Colors matching status.
|
||||
Color get _statusColor {
|
||||
if (_statusCode >= 200 && _statusCode < 300) {
|
||||
return Colors.lightGreen;
|
||||
}
|
||||
if (_statusCode >= 300 && _statusCode < 400) {
|
||||
return Colors.orangeAccent;
|
||||
}
|
||||
if (_statusCode >= 400 && _statusCode < 500) {
|
||||
return Colors.purple;
|
||||
}
|
||||
if (_statusCode >= 500 && _statusCode < 600) {
|
||||
return Colors.red;
|
||||
}
|
||||
return Colors.blueAccent;
|
||||
}
|
||||
|
||||
/// The method that the [_request] used.
|
||||
String get _method => _request.method;
|
||||
|
||||
/// The [Uri] that the [_request] requested.
|
||||
Uri get _requestUri => _request.uri;
|
||||
|
||||
String? get _requestHeadersBuilder {
|
||||
final Map<String, List<String>> map = _request.headers.map(
|
||||
(key, value) => MapEntry(
|
||||
key,
|
||||
value is Iterable ? value.map((v) => v.toString()).toList() : [value],
|
||||
),
|
||||
);
|
||||
final Headers headers = Headers.fromMap(map);
|
||||
if (headers.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return '$headers';
|
||||
}
|
||||
|
||||
/// Data for the [_request].
|
||||
String? get _requestDataBuilder {
|
||||
if (_request.data is Map) {
|
||||
return _encoder.convert(_request.data);
|
||||
}
|
||||
return _request.data?.toString();
|
||||
}
|
||||
|
||||
/// Data for the [_response].
|
||||
String? get _responseDataBuilder {
|
||||
final data = _response.data;
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
if (_response.data is Map) {
|
||||
return _encoder.convert(_response.data);
|
||||
}
|
||||
return _response.data.toString();
|
||||
}
|
||||
|
||||
String? get _responseHeadersBuilder {
|
||||
if (_response.headers.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return '${_response.headers}';
|
||||
}
|
||||
|
||||
Widget _detailButton(BuildContext context) {
|
||||
return TextButton(
|
||||
onPressed: _switchExpand,
|
||||
style: _buttonStyle(context),
|
||||
child: const Text(
|
||||
'Detail🔍',
|
||||
style: TextStyle(fontSize: 12, height: 1.2),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _infoContent(BuildContext context) {
|
||||
return Row(
|
||||
children: <Widget>[
|
||||
Text(_startTime.hms()),
|
||||
const SizedBox(width: 6),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 5,
|
||||
vertical: 1,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(3),
|
||||
color: _statusColor,
|
||||
),
|
||||
child: Text(
|
||||
_statusCode.toString(),
|
||||
style: const TextStyle(color: Colors.white, fontSize: 12),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
_method,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Text('${_duration.inMilliseconds}ms'),
|
||||
const Spacer(),
|
||||
_detailButton(context),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _detailedContent(BuildContext context) {
|
||||
return ValueListenableBuilder<bool>(
|
||||
valueListenable: _isExpanded,
|
||||
builder: (_, bool value, __) {
|
||||
if (!value) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
_TagText(
|
||||
tag: 'Request headers',
|
||||
content: _requestHeadersBuilder,
|
||||
),
|
||||
_TagText(
|
||||
tag: 'Request data',
|
||||
content: _requestDataBuilder,
|
||||
),
|
||||
_TagText(
|
||||
tag: 'Response body',
|
||||
content: _responseDataBuilder,
|
||||
),
|
||||
_TagText(
|
||||
tag: 'Response headers',
|
||||
content: _responseHeadersBuilder,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
margin: const EdgeInsets.all(8.0),
|
||||
shadowColor: Theme.of(context).canvasColor,
|
||||
elevation: 5,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
_infoContent(context),
|
||||
const SizedBox(height: 10),
|
||||
_TagText(
|
||||
tag: 'Uri',
|
||||
content: '$_requestUri',
|
||||
shouldStartFromNewLine: false,
|
||||
),
|
||||
_detailedContent(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TagText extends StatelessWidget {
|
||||
const _TagText({
|
||||
Key? key,
|
||||
required this.tag,
|
||||
this.content,
|
||||
this.shouldStartFromNewLine = true,
|
||||
}) : super(key: key);
|
||||
|
||||
final String tag;
|
||||
final String? content;
|
||||
final bool shouldStartFromNewLine;
|
||||
|
||||
TextSpan get span {
|
||||
return TextSpan(
|
||||
children: <TextSpan>[
|
||||
TextSpan(
|
||||
text: '$tag: ',
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
if (shouldStartFromNewLine) TextSpan(text: '\n'),
|
||||
TextSpan(text: content!),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (content == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: SelectableText.rich(span),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension _DateTimeExtension on DateTime {
|
||||
String hms([String separator = ':']) => '$hour$separator'
|
||||
'${'$minute'.padLeft(2, '0')}$separator'
|
||||
'${'$second'.padLeft(2, '0')}';
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
name: flutter_ume_kit_dio
|
||||
description: Dio kit for flutter_ume.
|
||||
version: 1.0.0
|
||||
homepage: https://github.com/bytedance/flutter_ume
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ">=2.12.0 <4.0.0"
|
||||
flutter: ">=2.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_ume:
|
||||
path: ../../../flutter_ume
|
||||
|
||||
dio: ^5.4.3+1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
mockito: ^5.0.12
|
||||
flutter_coverage_badge:
|
||||
git:
|
||||
url: https://github.com/smileShirely/flutter_coverage_badge.git
|
||||
ref: 59b7580f406bb712e9d9049c8c99212946e34f65
|
||||
@@ -0,0 +1,4 @@
|
||||
# melos_managed_dependency_overrides: flutter_ume
|
||||
dependency_overrides:
|
||||
flutter_ume:
|
||||
path: ../..
|
||||
@@ -0,0 +1,75 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.packages
|
||||
.pub-cache/
|
||||
.pub/
|
||||
build/
|
||||
|
||||
# Android related
|
||||
**/android/**/gradle-wrapper.jar
|
||||
**/android/.gradle
|
||||
**/android/captures/
|
||||
**/android/gradlew
|
||||
**/android/gradlew.bat
|
||||
**/android/local.properties
|
||||
**/android/**/GeneratedPluginRegistrant.java
|
||||
|
||||
# iOS/XCode related
|
||||
**/ios/**/*.mode1v3
|
||||
**/ios/**/*.mode2v3
|
||||
**/ios/**/*.moved-aside
|
||||
**/ios/**/*.pbxuser
|
||||
**/ios/**/*.perspectivev3
|
||||
**/ios/**/*sync/
|
||||
**/ios/**/.sconsign.dblite
|
||||
**/ios/**/.tags*
|
||||
**/ios/**/.vagrant/
|
||||
**/ios/**/DerivedData/
|
||||
**/ios/**/Icon?
|
||||
**/ios/**/Pods/
|
||||
**/ios/**/.symlinks/
|
||||
**/ios/**/profile
|
||||
**/ios/**/xcuserdata
|
||||
**/ios/.generated/
|
||||
**/ios/Flutter/App.framework
|
||||
**/ios/Flutter/Flutter.framework
|
||||
**/ios/Flutter/Flutter.podspec
|
||||
**/ios/Flutter/Generated.xcconfig
|
||||
**/ios/Flutter/ephemeral
|
||||
**/ios/Flutter/app.flx
|
||||
**/ios/Flutter/app.zip
|
||||
**/ios/Flutter/flutter_assets/
|
||||
**/ios/Flutter/flutter_export_environment.sh
|
||||
**/ios/ServiceDefinitions.json
|
||||
**/ios/Runner/GeneratedPluginRegistrant.*
|
||||
|
||||
# Exceptions to above rules.
|
||||
!**/ios/**/default.mode1v3
|
||||
!**/ios/**/default.mode2v3
|
||||
!**/ios/**/default.pbxuser
|
||||
!**/ios/**/default.perspectivev3
|
||||
@@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: 02c026b03cd31dd3f867e5faeb7e104cce174c5f
|
||||
channel: unknown
|
||||
|
||||
project_type: package
|
||||
@@ -0,0 +1,37 @@
|
||||
# Changelog
|
||||
|
||||
## 1.0.0
|
||||
|
||||
* 正式版
|
||||
|
||||
* Normal version.
|
||||
|
||||
## 1.0.0-dev.0
|
||||
|
||||
* 适配 Flutter 3
|
||||
|
||||
* Adapt Flutter 3
|
||||
|
||||
## 0.3.0
|
||||
|
||||
* 修复分页错误 #28
|
||||
|
||||
* Fix DioInspector paging issue #28
|
||||
|
||||
## 0.2.1
|
||||
|
||||
* null-safety 正式版本
|
||||
|
||||
* Null-Safety formal version.
|
||||
|
||||
## 0.2.0-dev.0
|
||||
|
||||
* 适配 null-safety
|
||||
|
||||
* Adapted Null-Safety.
|
||||
|
||||
## 0.1.0
|
||||
|
||||
* 发布开源版本。
|
||||
|
||||
* Release opensource version.
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 ByteDance Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,11 @@
|
||||
# flutter_ume_kit_perf
|
||||
|
||||
[flutter_ume](https://pub.dev/packages/flutter_ume) 是由字节跳动 Flutter Infra 团队出品的应用内调试工具平台。
|
||||
|
||||
flutter_ume_kit_perf 是 flutter_ume 的性能插件包。接入方式请见 [flutter_ume](https://pub.dev/packages/flutter_ume)。
|
||||
|
||||
----
|
||||
|
||||
[flutter_ume](https://pub.dev/packages/flutter_ume) is an in-app debug kits platform produced for Flutter apps by ByteDance Flutter Infra team.
|
||||
|
||||
flutter_ume_kit_perf is the Performance kits package of flutter_ume. Please visit [flutter_ume](https://pub.dev/packages/flutter_ume) for details.
|
||||
@@ -0,0 +1,279 @@
|
||||
SF:lib/components/memory_info/memory_service.dart
|
||||
DA:11,1
|
||||
DA:12,1
|
||||
DA:13,1
|
||||
DA:14,1
|
||||
DA:15,1
|
||||
DA:16,1
|
||||
DA:17,1
|
||||
DA:18,1
|
||||
DA:20,1
|
||||
DA:21,1
|
||||
DA:23,4
|
||||
DA:26,1
|
||||
DA:33,1
|
||||
DA:45,1
|
||||
DA:46,3
|
||||
DA:47,2
|
||||
DA:48,2
|
||||
DA:49,2
|
||||
DA:51,0
|
||||
DA:52,0
|
||||
DA:53,0
|
||||
DA:55,0
|
||||
DA:59,0
|
||||
DA:61,0
|
||||
DA:63,0
|
||||
DA:64,0
|
||||
DA:67,0
|
||||
DA:69,0
|
||||
DA:71,0
|
||||
DA:72,0
|
||||
DA:73,0
|
||||
DA:74,0
|
||||
DA:75,0
|
||||
DA:76,0
|
||||
DA:77,0
|
||||
DA:79,0
|
||||
DA:81,0
|
||||
DA:82,0
|
||||
DA:83,0
|
||||
DA:84,0
|
||||
DA:87,0
|
||||
DA:88,0
|
||||
DA:89,0
|
||||
DA:92,0
|
||||
DA:94,0
|
||||
DA:97,0
|
||||
DA:98,0
|
||||
DA:99,0
|
||||
DA:100,0
|
||||
DA:101,0
|
||||
DA:102,0
|
||||
DA:105,0
|
||||
DA:106,0
|
||||
DA:107,0
|
||||
DA:108,0
|
||||
DA:109,0
|
||||
DA:110,0
|
||||
DA:113,0
|
||||
DA:114,0
|
||||
DA:115,0
|
||||
DA:116,0
|
||||
DA:117,0
|
||||
DA:118,0
|
||||
DA:121,1
|
||||
DA:123,0
|
||||
DA:124,0
|
||||
DA:125,0
|
||||
DA:127,2
|
||||
DA:131,1
|
||||
DA:133,1
|
||||
DA:134,1
|
||||
DA:135,0
|
||||
DA:136,0
|
||||
DA:138,0
|
||||
DA:139,0
|
||||
DA:143,2
|
||||
DA:144,2
|
||||
DA:146,1
|
||||
DA:150,0
|
||||
DA:154,0
|
||||
DA:155,0
|
||||
DA:156,0
|
||||
DA:157,0
|
||||
DA:159,0
|
||||
LF:84
|
||||
LH:26
|
||||
end_of_record
|
||||
SF:lib/components/performance/performance.dart
|
||||
DA:8,1
|
||||
DA:10,1
|
||||
DA:13,1
|
||||
DA:14,1
|
||||
DA:15,3
|
||||
DA:18,1
|
||||
DA:21,1
|
||||
DA:23,2
|
||||
DA:25,1
|
||||
DA:28,0
|
||||
DA:31,1
|
||||
LF:11
|
||||
LH:10
|
||||
end_of_record
|
||||
SF:lib/components/memory_info/memory_info_page.dart
|
||||
DA:9,2
|
||||
DA:11,1
|
||||
DA:13,1
|
||||
DA:14,1
|
||||
DA:15,1
|
||||
DA:19,1
|
||||
DA:22,1
|
||||
DA:24,2
|
||||
DA:26,1
|
||||
DA:29,0
|
||||
DA:32,1
|
||||
DA:41,0
|
||||
DA:45,2
|
||||
DA:47,1
|
||||
DA:48,1
|
||||
DA:58,1
|
||||
DA:60,1
|
||||
DA:61,2
|
||||
DA:62,0
|
||||
DA:66,1
|
||||
DA:67,1
|
||||
DA:68,2
|
||||
DA:69,2
|
||||
DA:72,0
|
||||
DA:73,0
|
||||
DA:74,0
|
||||
DA:75,0
|
||||
DA:76,0
|
||||
DA:77,0
|
||||
DA:78,0
|
||||
DA:82,1
|
||||
DA:83,2
|
||||
DA:84,1
|
||||
DA:87,1
|
||||
DA:90,1
|
||||
DA:93,3
|
||||
DA:94,1
|
||||
DA:97,1
|
||||
DA:100,1
|
||||
DA:103,3
|
||||
DA:104,1
|
||||
DA:106,2
|
||||
DA:107,1
|
||||
DA:110,1
|
||||
DA:112,1
|
||||
DA:113,1
|
||||
DA:114,1
|
||||
DA:115,1
|
||||
DA:116,1
|
||||
DA:119,2
|
||||
DA:123,1
|
||||
DA:125,1
|
||||
DA:127,1
|
||||
DA:129,1
|
||||
DA:130,1
|
||||
DA:132,1
|
||||
DA:133,1
|
||||
DA:134,1
|
||||
DA:135,1
|
||||
DA:136,3
|
||||
DA:137,1
|
||||
DA:140,2
|
||||
DA:141,1
|
||||
DA:142,1
|
||||
DA:143,2
|
||||
DA:144,1
|
||||
DA:147,2
|
||||
DA:148,1
|
||||
DA:151,1
|
||||
DA:152,2
|
||||
DA:153,0
|
||||
DA:154,1
|
||||
DA:155,2
|
||||
DA:156,1
|
||||
DA:159,2
|
||||
DA:160,1
|
||||
DA:162,1
|
||||
DA:166,2
|
||||
DA:170,1
|
||||
DA:171,1
|
||||
DA:172,1
|
||||
DA:173,0
|
||||
DA:174,0
|
||||
DA:175,0
|
||||
DA:177,0
|
||||
DA:178,0
|
||||
DA:179,0
|
||||
DA:180,0
|
||||
DA:181,0
|
||||
DA:182,0
|
||||
DA:184,0
|
||||
DA:185,0
|
||||
DA:186,0
|
||||
DA:187,0
|
||||
DA:188,0
|
||||
DA:189,0
|
||||
DA:190,0
|
||||
DA:191,0
|
||||
DA:192,0
|
||||
DA:193,0
|
||||
DA:198,3
|
||||
DA:210,1
|
||||
DA:217,1
|
||||
DA:225,1
|
||||
DA:226,1
|
||||
DA:232,1
|
||||
DA:234,1
|
||||
DA:235,3
|
||||
DA:238,1
|
||||
DA:240,1
|
||||
DA:241,1
|
||||
DA:242,2
|
||||
DA:243,2
|
||||
DA:244,2
|
||||
DA:246,2
|
||||
DA:247,6
|
||||
DA:251,1
|
||||
DA:252,2
|
||||
DA:253,3
|
||||
DA:255,2
|
||||
DA:256,2
|
||||
DA:257,1
|
||||
DA:264,1
|
||||
DA:266,1
|
||||
DA:272,1
|
||||
DA:274,1
|
||||
DA:276,1
|
||||
DA:277,0
|
||||
DA:278,0
|
||||
DA:279,0
|
||||
DA:280,1
|
||||
DA:282,1
|
||||
DA:283,3
|
||||
DA:284,1
|
||||
DA:285,1
|
||||
DA:289,1
|
||||
DA:295,0
|
||||
DA:296,0
|
||||
DA:297,0
|
||||
DA:298,0
|
||||
DA:304,0
|
||||
DA:305,0
|
||||
DA:312,0
|
||||
DA:314,0
|
||||
DA:316,0
|
||||
DA:317,0
|
||||
DA:318,0
|
||||
DA:319,0
|
||||
DA:321,0
|
||||
DA:322,0
|
||||
DA:323,0
|
||||
DA:324,0
|
||||
DA:326,0
|
||||
DA:327,0
|
||||
DA:331,0
|
||||
DA:333,0
|
||||
DA:334,0
|
||||
DA:335,0
|
||||
DA:336,0
|
||||
DA:337,0
|
||||
DA:338,0
|
||||
DA:339,0
|
||||
DA:340,0
|
||||
DA:342,0
|
||||
DA:343,0
|
||||
DA:346,0
|
||||
DA:350,0
|
||||
DA:351,0
|
||||
DA:352,0
|
||||
DA:355,0
|
||||
DA:359,0
|
||||
DA:360,0
|
||||
LF:172
|
||||
LH:103
|
||||
end_of_record
|
||||
@@ -0,0 +1,20 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="94" height="20">
|
||||
<linearGradient id="b" x2="0" y2="100%">
|
||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
||||
<stop offset="1" stop-opacity=".1"/>
|
||||
</linearGradient>
|
||||
<clipPath id="a">
|
||||
<rect width="94" height="20" rx="3" fill="#fff"/>
|
||||
</clipPath>
|
||||
<g clip-path="url(#a)">
|
||||
<path fill="#555" d="M0 0h59v20H0z"/>
|
||||
<path fill="#df6e3a" d="M59 0h35v20H59z"/>
|
||||
<path fill="url(#b)" d="M0 0h94v20H0z"/>
|
||||
</g>
|
||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110">
|
||||
<text x="305" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="490">coverage</text>
|
||||
<text x="305" y="140" transform="scale(.1)" textLength="490">coverage</text>
|
||||
<text x="755" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="250">52%</text>
|
||||
<text x="755" y="140" transform="scale(.1)" textLength="250">52%</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,362 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_ume/flutter_ume.dart';
|
||||
import 'memory_service.dart';
|
||||
import 'icon.dart' as icon;
|
||||
|
||||
class MemoryInfoPage extends StatelessWidget implements Pluggable {
|
||||
const MemoryInfoPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
theme: ThemeData(primaryColor: Colors.white),
|
||||
home: _MemoryWidget(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildWidget(BuildContext? context) => this;
|
||||
|
||||
@override
|
||||
ImageProvider<Object> get iconImageProvider => MemoryImage(icon.iconBytes);
|
||||
|
||||
@override
|
||||
String get name => 'MemoryInfo';
|
||||
|
||||
@override
|
||||
String get displayName => 'MemoryInfo';
|
||||
|
||||
@override
|
||||
void onTrigger() {}
|
||||
}
|
||||
|
||||
class _DetailModel {
|
||||
final int? count;
|
||||
final String? classId;
|
||||
final String? className;
|
||||
|
||||
_DetailModel(this.count, this.classId, this.className);
|
||||
}
|
||||
|
||||
class _MemoryWidget extends StatefulWidget {
|
||||
_MemoryWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_MemoryWidgetState createState() => _MemoryWidgetState();
|
||||
}
|
||||
|
||||
class _MemoryWidgetState extends State<_MemoryWidget> {
|
||||
MemoryService _memoryservice = MemoryService();
|
||||
|
||||
int _sortColumnIndex = 0;
|
||||
|
||||
bool? _checked = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_memoryservice.getInfos(() {
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
void _hidePrivateClass(bool? check) {
|
||||
_checked = check;
|
||||
_memoryservice.hidePrivateClasses(check!);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void _enterDetailPage(_DetailModel detail) {
|
||||
Navigator.of(context).push(MaterialPageRoute(builder: (ctx) {
|
||||
return Scaffold(
|
||||
body: _MemoryDetail(detail: detail, service: _memoryservice),
|
||||
appBar: PreferredSize(
|
||||
child: AppBar(elevation: 0.0, title: Text(detail.className!)),
|
||||
preferredSize: Size.fromHeight(44)));
|
||||
}));
|
||||
}
|
||||
|
||||
Widget _header() {
|
||||
return Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
padding: const EdgeInsets.only(left: 15, top: 10, bottom: 10),
|
||||
child: Text("VM Info: ",
|
||||
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
|
||||
textAlign: TextAlign.left)),
|
||||
Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
padding: const EdgeInsets.only(left: 15, right: 5),
|
||||
child: Text(_memoryservice.vmInfo)),
|
||||
Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
padding: const EdgeInsets.only(left: 15, bottom: 10),
|
||||
child: Text("Memory Info: ",
|
||||
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.w500),
|
||||
textAlign: TextAlign.left)),
|
||||
Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
padding: const EdgeInsets.only(left: 15, right: 5),
|
||||
child: Text(_memoryservice.memoryUseage)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 12.0),
|
||||
child: Row(children: [
|
||||
SizedBox(
|
||||
height: 24,
|
||||
width: 24,
|
||||
child: Checkbox(
|
||||
materialTapTargetSize: MaterialTapTargetSize.padded,
|
||||
value: _checked,
|
||||
onChanged: _hidePrivateClass)),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 5),
|
||||
child: Text("Hide private class"))
|
||||
]),
|
||||
),
|
||||
Padding(padding: EdgeInsets.only(top: 10))
|
||||
]);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
color: Colors.white,
|
||||
child: SafeArea(
|
||||
bottom: false,
|
||||
child: Scaffold(
|
||||
body: NestedScrollView(
|
||||
headerSliverBuilder:
|
||||
(BuildContext context, bool innerBoxIsScrolled) {
|
||||
return <Widget>[
|
||||
SliverAppBar(
|
||||
bottom: PreferredSize(
|
||||
child: _PerRow(customColor: Color(0xFFF4F4F4), widgets: [
|
||||
_DropButton(
|
||||
title: "Size",
|
||||
index: 0,
|
||||
stateChanged: (index, descending) => _memoryservice
|
||||
.sort((d) => d.accumulatedSize, descending,
|
||||
() {
|
||||
setState(() {
|
||||
_sortColumnIndex = index;
|
||||
});
|
||||
}),
|
||||
showArrow: _sortColumnIndex == 0),
|
||||
_DropButton(
|
||||
title: "Count",
|
||||
index: 1,
|
||||
stateChanged: (index, descending) =>
|
||||
_memoryservice.sort(
|
||||
(d) => d.instancesAccumulated, descending,
|
||||
() {
|
||||
setState(() {
|
||||
_sortColumnIndex = index;
|
||||
});
|
||||
}),
|
||||
showArrow: _sortColumnIndex == 1),
|
||||
_DropButton(title: "ClassName")
|
||||
]),
|
||||
preferredSize: Size.fromHeight(44)),
|
||||
expandedHeight: 310.0,
|
||||
floating: true,
|
||||
pinned: true,
|
||||
flexibleSpace: FlexibleSpaceBar(background: _header()),
|
||||
)
|
||||
];
|
||||
},
|
||||
body: Scrollbar(
|
||||
child: ListView.builder(
|
||||
physics: BouncingScrollPhysics(),
|
||||
itemBuilder: (_, index) {
|
||||
var stats = _memoryservice.infoList[index];
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
_DetailModel detail = _DetailModel(
|
||||
stats.instancesAccumulated,
|
||||
stats.classRef!.id,
|
||||
stats.classRef!.name);
|
||||
_enterDetailPage(detail);
|
||||
},
|
||||
child: _PerRow(
|
||||
darkColor: index % 2 == 0,
|
||||
widgets: [
|
||||
Text(
|
||||
"${_memoryservice.byteToString(stats.accumulatedSize!)}",
|
||||
style: TextStyle(color: Colors.black87)),
|
||||
Text("${stats.instancesAccumulated}",
|
||||
style: TextStyle(color: Colors.black87)),
|
||||
Text("${stats.classRef!.name}",
|
||||
style: TextStyle(color: Colors.black87)),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: _memoryservice.infoList.length),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
typedef _DropState = void Function(int, bool);
|
||||
|
||||
class _DropButton extends StatefulWidget {
|
||||
_DropButton(
|
||||
{Key? key,
|
||||
this.showArrow = false,
|
||||
this.descending = true,
|
||||
required this.title,
|
||||
this.index = 0,
|
||||
this.stateChanged})
|
||||
: super(key: key);
|
||||
|
||||
final bool showArrow;
|
||||
final bool descending;
|
||||
final int index;
|
||||
final String title;
|
||||
final _DropState? stateChanged;
|
||||
|
||||
@override
|
||||
__DropButtonState createState() => __DropButtonState();
|
||||
}
|
||||
|
||||
class __DropButtonState extends State<_DropButton> {
|
||||
bool _descending = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_descending = widget.descending;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
if (widget.showArrow) {
|
||||
_descending = !_descending;
|
||||
}
|
||||
if (widget.stateChanged != null) {
|
||||
widget.stateChanged!(widget.index, _descending);
|
||||
}
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
child: Row(children: [
|
||||
Text(widget.title,
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
|
||||
widget.showArrow
|
||||
? Icon(_descending ? Icons.arrow_drop_down : Icons.arrow_drop_up)
|
||||
: Container()
|
||||
], mainAxisSize: MainAxisSize.min)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PerRow extends StatelessWidget {
|
||||
const _PerRow(
|
||||
{Key? key, this.widgets, this.customColor, this.darkColor = false})
|
||||
: super(key: key);
|
||||
|
||||
final List<Widget>? widgets;
|
||||
final bool darkColor;
|
||||
final Color? customColor;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(left: 15, right: 15),
|
||||
color: this.customColor ??
|
||||
(this.darkColor
|
||||
? Colors.grey.withOpacity(0.2)
|
||||
: Colors.grey.withOpacity(0.03)),
|
||||
child: Row(
|
||||
children: this
|
||||
.widgets!
|
||||
.map((e) => Expanded(
|
||||
child: Align(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 10, bottom: 10),
|
||||
child: e),
|
||||
alignment: Alignment.centerLeft)))
|
||||
.toList()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MemoryDetail extends StatefulWidget {
|
||||
_MemoryDetail({Key? key, required this.detail, required this.service})
|
||||
: assert(service != null),
|
||||
assert(detail != null),
|
||||
super(key: key);
|
||||
|
||||
final _DetailModel detail;
|
||||
|
||||
final MemoryService service;
|
||||
|
||||
@override
|
||||
__MemoryDetailState createState() => __MemoryDetailState();
|
||||
}
|
||||
|
||||
class __MemoryDetailState extends State<_MemoryDetail> {
|
||||
String _textInfoO = "";
|
||||
String _textInfoT = "";
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
widget.service.getClassDetailInfo(widget.detail.classId!, (info) {
|
||||
StringBuffer buffer = StringBuffer();
|
||||
info?.propeties?.forEach((element) {
|
||||
buffer.writeln(element.propertyStr);
|
||||
});
|
||||
_textInfoO = buffer.toString();
|
||||
StringBuffer bf = StringBuffer();
|
||||
info?.functions?.forEach((element) {
|
||||
bf.writeln(element);
|
||||
});
|
||||
_textInfoT = bf.toString();
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 15, left: 15, right: 15),
|
||||
child: _textInfoO.isEmpty && _textInfoT.isEmpty
|
||||
? Center(
|
||||
child: Text('The Object is Sentinel',
|
||||
style: TextStyle(fontSize: 20)))
|
||||
: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Text("Property: ",
|
||||
style: const TextStyle(
|
||||
fontSize: 20, fontWeight: FontWeight.w500),
|
||||
textAlign: TextAlign.left)),
|
||||
Text(_textInfoO,
|
||||
textAlign: TextAlign.left, style: TextStyle(fontSize: 16)),
|
||||
Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
padding: const EdgeInsets.only(bottom: 10),
|
||||
child: Text("Function: ",
|
||||
style: const TextStyle(
|
||||
fontSize: 20, fontWeight: FontWeight.w500),
|
||||
textAlign: TextAlign.left)),
|
||||
Text(_textInfoT,
|
||||
textAlign: TextAlign.left, style: TextStyle(fontSize: 16)),
|
||||
],
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
import 'package:vm_service/vm_service.dart';
|
||||
import 'package:flutter_ume/flutter_ume.dart';
|
||||
|
||||
class Property {
|
||||
final bool? isConst;
|
||||
final bool? isStatic;
|
||||
final bool? isFinal;
|
||||
final String? type;
|
||||
final String? name;
|
||||
|
||||
String get propertyStr {
|
||||
StringBuffer val = StringBuffer();
|
||||
if (this.isStatic!) {
|
||||
val.write("static");
|
||||
val.write(' ');
|
||||
} else if (this.isConst!) {
|
||||
val.write("const");
|
||||
val.write(' ');
|
||||
} else {
|
||||
val.write('final');
|
||||
val.write(' ');
|
||||
}
|
||||
return "${val.toString()} ${this.type} ${this.name}";
|
||||
}
|
||||
|
||||
Property(this.isConst, this.isStatic, this.isFinal, this.type, this.name);
|
||||
}
|
||||
|
||||
class ClsModel {
|
||||
final List<Property>? propeties;
|
||||
final List<String>? functions;
|
||||
|
||||
ClsModel({this.propeties, this.functions});
|
||||
}
|
||||
|
||||
class MemoryService with VMServiceWrapper {
|
||||
List<ClassHeapStats> infoList = [];
|
||||
|
||||
List<ClassHeapStats> allClasses = [];
|
||||
|
||||
String vmInfo = "";
|
||||
|
||||
String memoryUseage = "";
|
||||
|
||||
void getInfos(Function completion) async {
|
||||
List results = await Future.wait([
|
||||
serviceWrapper.getClassHeapStats(),
|
||||
serviceWrapper.getMemoryUsage(),
|
||||
serviceWrapper.getVM()
|
||||
]);
|
||||
_heapInfoList(results[0]);
|
||||
_memoryUsed(results[1]);
|
||||
_vmToInfo(results[2]);
|
||||
completion();
|
||||
}
|
||||
|
||||
void getInstanceIds(
|
||||
String classId, int limit, Function(List<String?>) completion) async {
|
||||
InstanceSet instanceSet = await serviceWrapper.getInstances(classId, limit);
|
||||
List<String?> instanceIds =
|
||||
instanceSet.instances!.map((e) => e.id).toList();
|
||||
completion(instanceIds);
|
||||
}
|
||||
|
||||
void getClassDetailInfo(
|
||||
String classId, Function(ClsModel?) completion) async {
|
||||
Class cls = await serviceWrapper.getObject(classId) as Class;
|
||||
ClsModel? _clsModel;
|
||||
if (cls.fields != null && cls.fields!.isNotEmpty) {
|
||||
List<Property> properties = [];
|
||||
List<String> functions = [];
|
||||
cls.fields?.forEach((fieldRef) {
|
||||
Property _property = Property(fieldRef.isConst, fieldRef.isStatic,
|
||||
fieldRef.isFinal, fieldRef.declaredType!.name, fieldRef.name);
|
||||
properties.add(_property);
|
||||
});
|
||||
for (var fucRef in cls.functions!) {
|
||||
String? code;
|
||||
Obj func = await serviceWrapper.getObject(fucRef.id!);
|
||||
if (func is Func) {
|
||||
code = func.code!.name;
|
||||
if (func.code!.name!.contains("[Stub]")) {
|
||||
continue;
|
||||
}
|
||||
code = code!.replaceAll('[Unoptimized] ', '');
|
||||
code = code.replaceAll('[Optimized] ', '');
|
||||
functions.add(code);
|
||||
}
|
||||
}
|
||||
_clsModel = ClsModel(propeties: properties, functions: functions);
|
||||
}
|
||||
completion(_clsModel);
|
||||
}
|
||||
|
||||
void _heapInfoList(List<ClassHeapStats> list) {
|
||||
allClasses = list;
|
||||
allClasses.sort((a, b) => b.accumulatedSize!.compareTo(a.accumulatedSize!));
|
||||
infoList = allClasses
|
||||
.where((element) => !element.classRef!.name!.startsWith("_"))
|
||||
.toList();
|
||||
}
|
||||
|
||||
void _vmToInfo(VM vm) {
|
||||
StringBuffer buffer = StringBuffer();
|
||||
buffer.writeln("Pid: ${vm.pid}");
|
||||
buffer.writeln("CPU: ${vm.hostCPU}");
|
||||
buffer.writeln("Version: ${vm.version}");
|
||||
vmInfo = buffer.toString();
|
||||
}
|
||||
|
||||
void _memoryUsed(MemoryUsage usage) {
|
||||
StringBuffer buffer = StringBuffer();
|
||||
buffer.writeln("ExternalUsage: ${byteToString(usage.externalUsage!)}");
|
||||
buffer.writeln("HeapCapacity: ${byteToString(usage.heapCapacity!)}");
|
||||
buffer.writeln("HeapUsage: ${byteToString(usage.heapUsage!)}");
|
||||
memoryUseage = buffer.toString();
|
||||
}
|
||||
|
||||
void hidePrivateClasses(bool hide) {
|
||||
if (hide) {
|
||||
infoList = allClasses
|
||||
.where((element) => !element.classRef!.name!.startsWith("_"))
|
||||
.toList();
|
||||
} else {
|
||||
infoList = allClasses;
|
||||
}
|
||||
}
|
||||
|
||||
void sort<T>(Comparable<T>? Function(ClassHeapStats d) getField,
|
||||
bool descending, void Function() completion) {
|
||||
s(List list) {
|
||||
list.sort((a, b) {
|
||||
final aValue = getField(a);
|
||||
final bValue = getField(b);
|
||||
return descending
|
||||
? Comparable.compare(bValue!, aValue!)
|
||||
: Comparable.compare(aValue!, bValue!);
|
||||
});
|
||||
}
|
||||
|
||||
s(infoList);
|
||||
s(allClasses);
|
||||
completion();
|
||||
}
|
||||
|
||||
String byteToString(int size) {
|
||||
const int m = 1024 * 1024;
|
||||
const int k = 1024;
|
||||
String resultSize = "";
|
||||
if (size / m >= 1) {
|
||||
resultSize = "${(size / m).toStringAsFixed(1)} M";
|
||||
} else if (size / k >= 1) {
|
||||
resultSize = "${(size / k).toStringAsFixed(1)} K";
|
||||
} else {
|
||||
resultSize = "$size B";
|
||||
}
|
||||
return resultSize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_ume/flutter_ume.dart';
|
||||
import 'icon.dart' as icon;
|
||||
|
||||
class Performance extends StatelessWidget implements Pluggable {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
alignment: Alignment.topCenter,
|
||||
margin: const EdgeInsets.only(top: 20),
|
||||
child: SizedBox(
|
||||
child: PerformanceOverlay.allEnabled(),
|
||||
width: MediaQuery.of(context).size.width));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildWidget(BuildContext? context) => this;
|
||||
|
||||
@override
|
||||
ImageProvider<Object> get iconImageProvider => MemoryImage(icon.iconBytes);
|
||||
|
||||
@override
|
||||
String get name => 'PerfOverlay';
|
||||
|
||||
@override
|
||||
String get displayName => 'PerfOverlay';
|
||||
|
||||
@override
|
||||
void onTrigger() {}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
library flutter_ume_kit_perf;
|
||||
|
||||
export 'components/performance/performance.dart';
|
||||
export 'components/memory_info/memory_info_page.dart';
|
||||
@@ -0,0 +1,25 @@
|
||||
name: flutter_ume_kit_perf
|
||||
description: Performance kits for flutter_ume.
|
||||
version: 1.0.0
|
||||
homepage: https://github.com/bytedance/flutter_ume
|
||||
|
||||
environment:
|
||||
sdk: ">=2.12.0 <4.0.0"
|
||||
flutter: ">=2.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
vm_service: 14.2.1
|
||||
flutter_ume: ">=1.0.0 <2.0.0"
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
mockito: ^5.0.12
|
||||
flutter_coverage_badge:
|
||||
git:
|
||||
url: https://github.com/smileShirely/flutter_coverage_badge.git
|
||||
ref: 59b7580f406bb712e9d9049c8c99212946e34f65
|
||||
|
||||
flutter:
|
||||
@@ -0,0 +1,4 @@
|
||||
# melos_managed_dependency_overrides: flutter_ume
|
||||
dependency_overrides:
|
||||
flutter_ume:
|
||||
path: ../..
|
||||
@@ -0,0 +1,75 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.packages
|
||||
.pub-cache/
|
||||
.pub/
|
||||
build/
|
||||
|
||||
# Android related
|
||||
**/android/**/gradle-wrapper.jar
|
||||
**/android/.gradle
|
||||
**/android/captures/
|
||||
**/android/gradlew
|
||||
**/android/gradlew.bat
|
||||
**/android/local.properties
|
||||
**/android/**/GeneratedPluginRegistrant.java
|
||||
|
||||
# iOS/XCode related
|
||||
**/ios/**/*.mode1v3
|
||||
**/ios/**/*.mode2v3
|
||||
**/ios/**/*.moved-aside
|
||||
**/ios/**/*.pbxuser
|
||||
**/ios/**/*.perspectivev3
|
||||
**/ios/**/*sync/
|
||||
**/ios/**/.sconsign.dblite
|
||||
**/ios/**/.tags*
|
||||
**/ios/**/.vagrant/
|
||||
**/ios/**/DerivedData/
|
||||
**/ios/**/Icon?
|
||||
**/ios/**/Pods/
|
||||
**/ios/**/.symlinks/
|
||||
**/ios/**/profile
|
||||
**/ios/**/xcuserdata
|
||||
**/ios/.generated/
|
||||
**/ios/Flutter/App.framework
|
||||
**/ios/Flutter/Flutter.framework
|
||||
**/ios/Flutter/Flutter.podspec
|
||||
**/ios/Flutter/Generated.xcconfig
|
||||
**/ios/Flutter/ephemeral
|
||||
**/ios/Flutter/app.flx
|
||||
**/ios/Flutter/app.zip
|
||||
**/ios/Flutter/flutter_assets/
|
||||
**/ios/Flutter/flutter_export_environment.sh
|
||||
**/ios/ServiceDefinitions.json
|
||||
**/ios/Runner/GeneratedPluginRegistrant.*
|
||||
|
||||
# Exceptions to above rules.
|
||||
!**/ios/**/default.mode1v3
|
||||
!**/ios/**/default.mode2v3
|
||||
!**/ios/**/default.pbxuser
|
||||
!**/ios/**/default.perspectivev3
|
||||
@@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: 02c026b03cd31dd3f867e5faeb7e104cce174c5f
|
||||
channel: unknown
|
||||
|
||||
project_type: package
|
||||
@@ -0,0 +1,39 @@
|
||||
# Changelog
|
||||
|
||||
## 1.0.0
|
||||
|
||||
* 正式版
|
||||
|
||||
* Normal version.
|
||||
|
||||
## 1.0.0-dev.0
|
||||
|
||||
* 适配 Flutter 3
|
||||
|
||||
* Adapt Flutter 3
|
||||
|
||||
## 0.3.0
|
||||
|
||||
* 更新版本号
|
||||
|
||||
* Update version
|
||||
|
||||
## 0.2.1
|
||||
|
||||
* null-safety 正式版本
|
||||
* 代码优化,修复 typo #11
|
||||
|
||||
* Null-Safety formal version.
|
||||
* Optimization and fix typo #11
|
||||
|
||||
## 0.2.0-dev.0
|
||||
|
||||
* 适配 null-safety
|
||||
|
||||
* Adapted Null-Safety.
|
||||
|
||||
## 0.1.0
|
||||
|
||||
* 发布开源版本。
|
||||
|
||||
* Release opensource version.
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 ByteDance Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||