Flutter新手求助:如何用ListView展示控制台日志?已用Fimber库
在Flutter中用ListView展示Fimber日志
要实现设备上用ListView展示Fimber日志,核心是自定义Fimber的日志收集树,把日志存储后绑定到ListView上,具体实现步骤如下:
1. 自定义日志收集树
创建继承自LogTree的类,重写log方法收集日志内容,用ValueNotifier维护日志列表,确保日志更新时能自动触发UI刷新:
import 'package:fimber/fimber.dart'; import 'package:synchronized/synchronized.dart'; import 'package:flutter/foundation.dart'; class CollectingLogTree extends LogTree { final ValueNotifier<List<String>> logs = ValueNotifier([]); final _lock = Lock(); // 保证多线程环境下日志添加的线程安全 @override void log(String level, String msg, {String? tag, dynamic ex, StackTrace? stacktrace}) { // 格式化日志内容,添加时间戳、级别等信息 final logEntry = '[${DateTime.now().toString().substring(0, 19)}] [$level] ${tag ?? ""}: $msg'; _lock.synchronized(() { // 复制原列表并添加新日志,避免直接修改原列表导致UI不刷新 logs.value = List.from(logs.value)..add(logEntry); // 可选:限制日志最大条数,避免内存占用过高 if (logs.value.length > 1000) { logs.value = logs.value.sublist(logs.value.length - 1000); } }); } }
2. 全局初始化自定义日志树
在main函数中替换原有的DebugTree(保留控制台输出),同时添加自定义收集树:
final collectingLogTree = CollectingLogTree(); void main() { Fimber.plantTree(DebugTree()); // 保留控制台日志输出 Fimber.plantTree(collectingLogTree); // 启用日志收集 runApp(const MyApp()); }
3. 创建日志展示页面
新建LogDisplayScreen,通过ValueListenableBuilder监听日志列表变化,渲染ListView展示日志:
import 'package:flutter/material.dart'; class LogDisplayScreen extends StatelessWidget { const LogDisplayScreen({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('日志展示'), actions: [ // 添加清空日志按钮 IconButton( icon: const Icon(Icons.delete), onPressed: () => collectingLogTree.logs.value = [], ) ], ), body: ValueListenableBuilder<List<String>>( valueListenable: collectingLogTree.logs, builder: (context, logs, child) { return ListView.builder( reverse: true, // 最新日志显示在底部 padding: const EdgeInsets.all(8), itemCount: logs.length, itemBuilder: (context, index) { final log = logs[index]; // 根据日志级别设置文本颜色 Color textColor = Colors.black87; if (log.contains('[D]')) textColor = Colors.blue.shade700; else if (log.contains('[E]')) textColor = Colors.red.shade700; else if (log.contains('[I]')) textColor = Colors.green.shade700; return Text(log, style: TextStyle(color: textColor, fontSize: 12)); }, ); }, ), ); } }
4. 集成到现有页面
在BluetoothOffScreen中添加入口按钮,方便跳转到日志页面:
@override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.lightBlue, appBar: AppBar( actions: [ IconButton( icon: const Icon(Icons.list_alt), onPressed: () => Navigator.push( context, MaterialPageRoute(builder: (_) => const LogDisplayScreen()) ), ), ], ), body: Center( child: Column( mainAxisSize: MainAxisSize.min, children: <Widget>[ const Icon( Icons.bluetooth_disabled, size: 200.0, color: Colors.white54, ), Text( 'Bluetooth Adapter is ${widget.state.toString().substring(15)}.', style: Theme.of(context) .primaryTextTheme .caption! .copyWith(color: Colors.white), ), ], ), ), ); }
额外提示
原代码中requestLocationPermission的权限判断逻辑有误,Map<Permission, PermissionStatus>不能直接与PermissionStatus.granted比较,需单独判断每个权限状态:
final bluetoothStatus = status[Permission.bluetooth]; final scanStatus = status[Permission.bluetoothScan]; if (bluetoothStatus == PermissionStatus.granted && scanStatus == PermissionStatus.granted) { Fimber.d("Permission Granted"); }
内容的提问来源于stack exchange,提问作者Nns_ninteyFIve
相关产品推荐
相关产品推荐

