relatica/lib/screens/logviewer_screen.dart

166 wiersze
4.8 KiB
Dart
Czysty Zwykły widok Historia

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:provider/provider.dart';
import '../controls/padding.dart';
import '../controls/responsive_max_width.dart';
import '../controls/standard_appbar.dart';
import '../services/log_service.dart';
import '../services/setting_service.dart';
import '../utils/clipboard_utils.dart';
import '../utils/logrecord_extensions.dart';
class LogViewerScreen extends StatefulWidget {
const LogViewerScreen({super.key});
@override
State<LogViewerScreen> createState() => _LogViewerScreenState();
}
class _LogViewerScreenState extends State<LogViewerScreen> {
var filterText = '';
var filterByText = false;
var filterByModule = false;
var filterModuleName = '';
@override
Widget build(BuildContext context) {
final settings = context.watch<SettingsService>();
final logService = context.watch<LogService>();
final events = logService.events;
return Scaffold(
appBar: StandardAppBar.build(context, 'Log Viewer'),
body: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ResponsiveMaxWidth(
child: Column(
children: [
buildLogPanel(settings),
buildModuleFilter(settings, events),
buildTextSearchPanel(),
const VerticalPadding(),
buildLogList(settings.logLevel, events),
],
),
),
),
));
}
Widget buildLogPanel(SettingsService settings) {
return ListTile(
title: const Text('Log Level'),
trailing: DropdownButton<Level>(
value: settings.logLevel,
items: Level.LEVELS
.map((c) => DropdownMenuItem(value: c, child: Text(c.name)))
.toList(),
onChanged: (value) {
settings.logLevel = value ?? Level.OFF;
}),
);
}
Widget buildModuleFilter(SettingsService settings, List<LogRecord> events) {
final modules = events.map((e) => e.loggerName).toSet().toList();
modules.sort();
modules.add('');
return ListTile(
leading: Checkbox(
value: filterByModule,
onChanged: (bool? value) {
setState(() {
filterByModule = value ?? false;
});
},
),
title: const Text('Filter by module:'),
trailing: !filterByModule
? null
: DropdownButton<String>(
value: filterModuleName,
items: modules
.map((c) => DropdownMenuItem(value: c, child: Text(c)))
.toList(),
onChanged: (value) {
setState(() {
filterModuleName = value ?? '';
});
}),
);
}
Widget buildTextSearchPanel() {
return ListTile(
leading: Checkbox(
value: filterByText,
onChanged: (value) {
setState(() {
filterByText = value ?? false;
});
},
),
title: TextField(
onChanged: (value) {
setState(() {
filterText = value.toLowerCase();
});
},
decoration: InputDecoration(
labelText: 'Filter by text',
alignLabelWithHint: true,
border: OutlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).highlightColor,
),
borderRadius: BorderRadius.circular(5.0),
),
),
),
);
}
Widget buildLogList(Level logLevel, List<LogRecord> allEvents) {
final events = allEvents.where((e) {
final levelFilterPasses = e.level >= logLevel;
final passesTextFilter = filterByText
? e.message.toLowerCase().contains(filterText.toLowerCase())
: true;
final passesModuleFilter = filterByModule
? filterModuleName.isEmpty || e.loggerName == filterModuleName
: true;
return levelFilterPasses && passesTextFilter && passesModuleFilter;
}).toList();
return Expanded(
child: ListView.separated(
itemBuilder: (context, index) {
final event = events[index];
return ListTile(
onLongPress: () {
copyToClipboard(
context: context,
text: jsonEncode(event.toJson()),
);
},
titleAlignment: ListTileTitleAlignment.titleHeight,
leading: Text(event.level.toString()),
title:
Text('${event.loggerName} at ${event.time.toIso8601String()}'),
subtitle: Text(
event.message,
softWrap: true,
),
);
},
separatorBuilder: (_, __) => const Divider(),
itemCount: events.length,
),
);
}
}