Flutter控制台调试插件fconsole_winggo的使用

Flutter控制台调试插件fconsole_winggo的使用

简介

fconsole 是一个用于调试的面板组件,类似于微信小程序中的 v-console。它在页面上创建一个可拖拽的悬浮窗,点击悬浮窗可启用日志列表面板。

主要功能

  • 显示悬浮窗,随时打开 Log 页面。
  • 可自定义页面插入,项目专属调试页不用藏。
  • 使用 FlowLog 可记录流程事件,网络日志清晰可见(支持 JSON 树状展示)。
  • 分享完整的 FlowLog 网络请求,一键反馈(甩锅)后端报错。

使用步骤

首先确保初始化并使用 runAppWithFConsole 方法:

void main() => runAppWithFConsole(
      MyApp(),
      delegate: MyCardDelegate(),
      beforeRun: () async {
        WidgetsFlutterBinding.ensureInitialized();
        // 在这里进行一些初始化操作
      }
    );

启动悬浮窗

只需调用顶层方法即可打开悬浮窗:

// 启动悬浮窗
showConsole();

// 隐藏悬浮窗
hideConsole();

自定义页面

很多时候我们需要插入自定义页面,可以这样做:

class MyCardDelegate extends FConsoleCardDelegate {
  [@override](/user/override)
  List<FConsoleCard> cardsBuilder(DefaultCards defaultCards) {
    return [
      defaultCards.logCard,
      defaultCards.flowCard,
      FConsoleCard(
        name: "my",
        builder: (ctx) => CustomLogPage(),
      ),
      defaultCards.sysInfoCard,
    ];
  }
}

class CustomLogPage extends StatelessWidget {
  const CustomLogPage({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      child: Text('custom page content'),
    );
  }
}

如果卡片内无法使用 OKToast,若要显示 toast 信息,FConsole 提供了一个 showMessage 方法:

FConsole.showMessage("Copy Success");

拦截原生 print 函数和未捕获的异常

fconsole 可以拦截原生的 print 函数,包括其他库中的 print 语句和未捕获的 throw 同样可以被拦截。

要使用此功能,请将 runApp 替换为 runFConsoleApp:

void main() => runFConsoleApp(MyApp());

然后,原生 printthrow 将被拦截:

SettingRow(
  icon: Icons.warning,
  text: '原生Print',
  right: Container(),
  onTap: () {
    print('${DateTime.now().toIso8601String()}');
  },
),
SettingRow(
  icon: Icons.warning,
  text: '原生Throw',
  right: Container(),
  onTap: () {
    throw '${DateTime.now().toIso8601String()}';
  },
),

添加日志

使用 FConsole 添加日志非常简单:

// 添加日志
FConsole.log("打印了一行log");
FConsole.log("打印了一行log");
FConsole.log("打印了一行log");
FConsole.log("打印了一行log");

// 添加错误
FConsole.error("打印了一行error");
FConsole.error("打印了一行error");
FConsole.error("打印了一行error");
FConsole.error("打印了一行error");

然后就可以在 FConsole 内查看日志记录。

创建 FlowLog(支持 JSON)

可以使用 FlowLog 的形式记录日志:

FlowLog.of('分享启动').log('用户进入页面 $id');
FlowLog.of('分享启动').log('获取到分享值1 $shareId');
FlowLog.of('分享启动').log('查询分享信息1 成功');
FlowLog.of('分享启动').log('获取到分享值2 $shareId');
FlowLog.of('分享启动').log('查询分享信息2 成功');
FlowLog.of('分享启动').log('获取到分享值3 $shareId');
FlowLog.of('分享启动').log('查询分享信息3 成功');
FlowLog.of('分享启动').log('获取到分享值4 $shareId');
FlowLog.of('分享启动').log(map); // 传入 Map/List 将会以 JSON 形式展示
FlowLog.of('分享启动').error('查询分享信息4错误: $map');
FlowLog.of('分享启动').end();

也可以使用变量来记录:

var logger = FlowLog.of('分享启动');
logger.log('用户进入页面 $id');
logger.log('获取到分享值 $shareId');
logger.error('查询分享信息错误: $map');
logger.end();

FlowLog 可以记录用户的一系列行为,在用户出现问题时,通过控制台信息即可快速定位问题。

FlowLog with Dio

一个很好的实践:使用 FlowLog 记录每一次网络请求

extension _GetLogID on RequestOptions {
  String get logId {
    return '[$method]${uri.path}';
  }
}

_http = Dio();
_http!.interceptors.add(InterceptorsWrapper(
  onRequest: (RequestOptions options, handler) {
    var _logger = FlowLog.ofNameAndId(
      options.logId,
      id: '${options.hashCode}',
    );
    _logger.log('开始请求 ${options.method}');
    _logger.log('data:${options.data}');
    _logger.log('params:${options.queryParameters}');
    _logger.log('header:${options.headers.toString()}');
    handler.next(options);
  },
  onResponse: (e, handler) {
    var _logger = FlowLog.ofNameAndId(
      e.requestOptions.logId,
      id: '${e.requestOptions.hashCode}',
    );
    _logger.log('请求结束');
    _logger.log('Http Status Code:${e.statusCode}');
    _logger.log('Response Data:\n${e.data}');
    _logger.end();
    handler.next(e);
  },
  onError: (e, handler) {
    var _logger = FlowLog.ofNameAndId(
      e.requestOptions.logId,
      id: '${e.requestOptions.hashCode}',
    );
    _logger.error('请求错误:$e');
    _logger.end('请求错误结束');
    handler.next(e);
  },
));

完整示例代码

以下是一个完整的示例代码,展示了如何使用 fconsole 插件:

import 'package:flutter/material.dart';
import 'package:fconsole_winggo/fconsole.dart';
import './style/color.dart';
import './style/text.dart';
import 'package:tapped/tapped.dart';

void main() => runAppWithFConsole(
      MyApp(),
      delegate: MyCardDelegate(),
    );

class MyCardDelegate extends FConsoleCardDelegate {
  [@override](/user/override)
  List<FConsoleCard> cardsBuilder(DefaultCards defaultCards) {
    return [
      defaultCards.logCard,
      defaultCards.flowCard,
      FConsoleCard(
        name: "my",
        builder: (ctx) => CustomLogPage(),
      ),
      defaultCards.sysInfoCard,
    ];
  }
}

class CustomLogPage extends StatelessWidget {
  const CustomLogPage({Key? key}) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      child: Text('custom page content'),
    );
  }
}

class MyApp extends StatefulWidget {
  [@override](/user/override)
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  [@override](/user/override)
  void initState() {
    super.initState();
    FConsole.instance.status.addListener(() {
      setState(() {});
    });
  }

  bool get consoleHasShow => FConsole.instance.status.value != FConsoleStatus.hide;

  double slideValue = 0;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return ConsoleWidget(
      options: ConsoleOptions(),
      child: MaterialApp(
        home: Scaffold(
          backgroundColor: ColorPlate.lightGray,
          appBar: AppBar(
            title: const Text('FConsole example app'),
          ),
          body: ListView(
            children: [
              AspectRatio(
                aspectRatio: 24 / 9,
                child: Center(
                  child: StText.big(
                    'FConsole Example',
                    style: TextStyle(color: ColorPlate.blue),
                  ),
                ),
              ),
              SettingRow(
                icon: consoleHasShow ? Icons.tab : Icons.tab_unselected,
                text: consoleHasShow ? 'Console打开' : 'Console关闭',
                right: Container(
                  height: 36,
                  padding: EdgeInsets.only(right: 12),
                  child: Switch(
                    value: consoleHasShow,
                    onChanged: (v) {
                      if (v) {
                        showConsole();
                      } else {
                        hideConsole();
                      }
                      setState(() {});
                    },
                  ),
                ),
              ),
              Container(height: 12),
              SettingRow(
                icon: Icons.info_outline,
                text: '打印log',
                right: Container(),
                onTap: () {
                  FConsole.log('打印信息:', DateTime.now());
                },
              ),
              SettingRow(
                icon: Icons.warning,
                text: '打印error',
                right: Container(),
                onTap: () {
                  FConsole.error('打印Error:', DateTime.now());
                },
              ),
              Container(height: 12),
              SettingRow(
                icon: Icons.edit,
                text: '原生Print',
                right: Container(),
                onTap: () {
                  print('${DateTime.now().toIso8601String()}');
                },
              ),
              SettingRow(
                icon: Icons.edit,
                text: '原生Throw',
                right: Container(),
                onTap: () {
                  var _ = [][123];
                  throw '${DateTime.now().toIso8601String()}';
                },
              ),
              Container(height: 12),
              SettingRow(
                icon: Icons.info_outline,
                text: '滑动事件Flow',
                right: Slider(
                  value: slideValue,
                  onChanged: (v) {
                    FlowLog.of(
                      '滑动Slider',
                      Duration(seconds: 2),
                    ).log({
                      'type': 'slide',
                      "value": [
                        for (var i = 0; i < 100; i++)
                          {
                            "value": {
                              "value": {
                                "value": {
                                  "value": "$v",
                                },
                              },
                            },
                          },
                      ],
                    });
                    setState(() {
                      slideValue = v;
                    });
                  },
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class SettingRow extends StatelessWidget {
  final double? padding;
  final IconData? icon;
  final Widget? right;
  final Widget? beforeRight;
  final String? text;
  final Color? textColor;
  final Function? onTap;
  const SettingRow({
    Key? key,
    this.padding = 14,
    this.icon,
    this.text,
    this.textColor,
    this.right,
    this.onTap,
    this.beforeRight,
  }) : super(key: key);

  [@override](/user/override)
  Widget build(BuildContext context) {
    var child = Container(
      color: ColorPlate.white,
      padding: EdgeInsets.only(left: 8),
      child: Row(
        children: <Widget>[
          icon == null
              ? Container()
              : Container(
                  margin: EdgeInsets.symmetric(horizontal: 16),
                  child: Icon(
                    icon,
                    size: 20,
                    color: textColor,
                  ),
                ),
          Expanded(
            child: Container(
              padding: EdgeInsets.symmetric(vertical: padding ?? 0),
              decoration: BoxDecoration(
                border: Border(
                  bottom: BorderSide(color: ColorPlate.lightGray),
                ),
              ),
              child: Row(
                children: <Widget>[
                  Expanded(
                    child: Container(
                      child: StText.normal(
                        text ?? '--',
                        style: TextStyle(
                          color: textColor,
                        ),
                      ),
                    ),
                  ),
                  Row(
                    children: <Widget>[
                      beforeRight ?? Container(),
                      Container(
                        margin: EdgeInsets.only(left: 8),
                        child: right ??
                            Container(
                              margin: EdgeInsets.only(right: 12),
                              child: Icon(
                                Icons.arrow_forward_ios,
                                color: ColorPlate.gray,
                                size: 12,
                              ),
                            ),
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
    if (onTap == null) {
      return child;
    }
    return Tapped(
      onTap: onTap,
      child: child,
    );
  }
}

更多关于Flutter控制台调试插件fconsole_winggo的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter控制台调试插件fconsole_winggo的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


fconsole_winggo 是一个 Flutter 调试工具插件,它提供了一个控制台界面,方便开发者在应用运行时查看日志、执行命令和调试代码。以下是如何使用 fconsole_winggo 的基本步骤:

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 fconsole_winggo 依赖:

dependencies:
  flutter:
    sdk: flutter
  fconsole_winggo: ^1.0.0 # 请使用最新版本

然后运行 flutter pub get 来获取依赖。

2. 初始化 FConsole

在你的应用的 main.dart 文件中,初始化 FConsole

import 'package:flutter/material.dart';
import 'package:fconsole_winggo/fconsole.dart';

void main() {
  FConsole.init(); // 初始化 FConsole
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

3. 打开控制台

在应用运行时,你可以通过以下方式打开 FConsole

  • 摇一摇设备:默认情况下,摇动设备可以打开或关闭控制台。
  • 代码调用:你也可以通过代码手动打开控制台:
FConsole.show();

4. 使用控制台

FConsole 提供了以下功能:

  • 查看日志:你可以通过 FConsole.log() 方法打印日志到控制台:
FConsole.log('This is a log message');
  • 执行命令:你可以在控制台中输入命令并执行。例如,你可以执行一些简单的 Dart 代码。

  • 调试信息FConsole 还会显示应用的性能信息、网络请求等。

5. 自定义配置

你可以通过 FConsole.init() 方法进行一些自定义配置,例如:

FConsole.init(
  enableShake: true, // 是否启用摇一摇打开控制台
  enableNotification: true, // 是否在通知栏显示控制台开关
  enableOverlay: true, // 是否在应用界面上显示悬浮按钮
);

6. 其他功能

FConsole 还支持一些高级功能,例如:

  • 网络请求监控:可以查看应用的所有网络请求。
  • 性能监控:可以查看应用的帧率、内存使用情况等。
  • 自定义命令:你可以注册自定义命令,方便在控制台中执行特定的操作。

7. 示例代码

以下是一个简单的示例,展示了如何使用 FConsole 打印日志和打开控制台:

import 'package:flutter/material.dart';
import 'package:fconsole_winggo/fconsole.dart';

void main() {
  FConsole.init();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('FConsole Demo'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            FConsole.log('Button pressed');
            FConsole.show();
          },
          child: Text('Open FConsole'),
        ),
      ),
    );
  }
}
回到顶部