move dropdown up; remove unused view

This commit is contained in:
hardliner66 2024-10-06 03:21:25 +02:00
parent 8f95b644d7
commit bba42586f3
5 changed files with 155 additions and 249 deletions

View File

@ -1,10 +1,12 @@
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'src/app.dart'; import 'src/app.dart';
import 'src/settings/settings_controller.dart'; import 'src/settings/settings_controller.dart';
import 'src/settings/settings_service.dart'; import 'src/settings/settings_service.dart';
void main() async { void main(List<String> args) async {
// Set up the SettingsController, which will glue user settings to multiple // Set up the SettingsController, which will glue user settings to multiple
// Flutter Widgets. // Flutter Widgets.
final settingsController = SettingsController(SettingsService()); final settingsController = SettingsController(SettingsService());
@ -13,8 +15,13 @@ void main() async {
// This prevents a sudden theme change when the app is first displayed. // This prevents a sudden theme change when the app is first displayed.
await settingsController.loadSettings(); await settingsController.loadSettings();
File? file;
if (args.isNotEmpty) {
file = File(args[0]);
}
// Run the app and pass in the SettingsController. The app listens to the // Run the app and pass in the SettingsController. The app listens to the
// SettingsController for changes, then passes it further down to the // SettingsController for changes, then passes it further down to the
// SettingsView. // SettingsView.
runApp(LogViewerApp(settingsController: settingsController)); runApp(LogViewerApp(settingsController: settingsController, file: file));
} }

View File

@ -6,16 +6,24 @@ import 'package:desktop_drop/desktop_drop.dart';
class LogViewerApp extends StatefulWidget { class LogViewerApp extends StatefulWidget {
final SettingsController settingsController; final SettingsController settingsController;
const LogViewerApp({super.key, required this.settingsController}); final File? file;
const LogViewerApp(
{super.key, required this.settingsController, required this.file});
@override @override
LogViewerAppState createState() => LogViewerAppState createState() => LogViewerAppState();
LogViewerAppState(settingsController: settingsController);
} }
class LogViewerAppState extends State<LogViewerApp> { class LogViewerAppState extends State<LogViewerApp> {
final SettingsController settingsController; late SettingsController settingsController;
LogViewerAppState({required this.settingsController}); late File? file;
@override
void initState() {
super.initState();
settingsController = widget.settingsController;
file = widget.file;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -23,6 +31,7 @@ class LogViewerAppState extends State<LogViewerApp> {
listenable: settingsController, listenable: settingsController,
builder: (BuildContext context, Widget? child) { builder: (BuildContext context, Widget? child) {
return MaterialApp( return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Fast Log Viewer', title: 'Fast Log Viewer',
theme: ThemeData( theme: ThemeData(
primarySwatch: Colors.blue, primarySwatch: Colors.blue,
@ -34,6 +43,7 @@ class LogViewerAppState extends State<LogViewerApp> {
), ),
themeMode: settingsController.themeMode, themeMode: settingsController.themeMode,
home: LogViewerScreen( home: LogViewerScreen(
file: file,
onThemeChanged: (ThemeMode mode) async { onThemeChanged: (ThemeMode mode) async {
await settingsController.updateThemeMode(mode); await settingsController.updateThemeMode(mode);
}, },
@ -47,13 +57,16 @@ class LogViewerAppState extends State<LogViewerApp> {
class LogViewerScreen extends StatefulWidget { class LogViewerScreen extends StatefulWidget {
final Function(ThemeMode) onThemeChanged; final Function(ThemeMode) onThemeChanged;
final SettingsController settingsController; final SettingsController settingsController;
final File? file;
const LogViewerScreen( const LogViewerScreen(
{required this.onThemeChanged, required this.settingsController}); {super.key,
required this.onThemeChanged,
required this.settingsController,
required this.file});
@override @override
LogViewerScreenState createState() => LogViewerScreenState createState() => LogViewerScreenState();
LogViewerScreenState(settingsController: settingsController);
} }
class LogViewerScreenState extends State<LogViewerScreen> { class LogViewerScreenState extends State<LogViewerScreen> {
@ -62,24 +75,51 @@ class LogViewerScreenState extends State<LogViewerScreen> {
String search = ''; String search = '';
LogLevel selectedLogLevel = LogLevel.info; LogLevel selectedLogLevel = LogLevel.info;
bool _isDragging = false; bool _isDragging = false;
int? selectedLogIndex;
final SettingsController settingsController; late SettingsController settingsController;
LogViewerScreenState({required this.settingsController}); @override
void initState() {
super.initState();
if (widget.file != null) {
widget.file?.readAsString().then((String fileContent) {
logs = _parseLogFile(fileContent);
_filterLogs();
});
}
settingsController = widget.settingsController;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( return Scaffold(
onTap: () {
setState(() {
selectedLogIndex = null;
FocusScope.of(context).unfocus();
});
},
child: Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Log Viewer'), title: const Text('Fast Log Viewer'),
actions: [ actions: [
DropdownButton<ThemeMode>(
value: settingsController.themeMode,
onChanged: (ThemeMode? newValue) async {
if (newValue != null) {
await settingsController.updateThemeMode(newValue);
setState(() {
widget.onThemeChanged(newValue);
});
}
},
items: const [
DropdownMenuItem(
value: ThemeMode.light,
child: Text('Light'),
),
DropdownMenuItem(
value: ThemeMode.dark,
child: Text('Dark'),
),
DropdownMenuItem(
value: ThemeMode.system,
child: Text('System'),
),
],
),
IconButton( IconButton(
icon: const Icon(Icons.folder_open), icon: const Icon(Icons.folder_open),
onPressed: _openFile, onPressed: _openFile,
@ -151,58 +191,26 @@ class LogViewerScreenState extends State<LogViewerScreen> {
); );
}).toList(), }).toList(),
), ),
const SizedBox(width: 8.0),
DropdownButton<ThemeMode>(
value: settingsController.themeMode,
onChanged: (ThemeMode? newValue) async {
if (newValue != null) {
await settingsController.updateThemeMode(newValue);
setState(() {
widget.onThemeChanged(newValue);
});
}
},
items: const [
DropdownMenuItem(
value: ThemeMode.light,
child: Text('Light'),
),
DropdownMenuItem(
value: ThemeMode.dark,
child: Text('Dark'),
),
DropdownMenuItem(
value: ThemeMode.system,
child: Text('System'),
),
],
),
], ],
), ),
), ),
Expanded( Expanded(
child: SelectionArea(
child: ListView.builder( child: ListView.builder(
itemCount: visibleLogs.length, itemCount: visibleLogs.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return GestureDetector( return GestureDetector(
onTap: () {
setState(() {
selectedLogIndex = index;
});
},
child: LogEntryWidget( child: LogEntryWidget(
log: visibleLogs[index], log: visibleLogs[index],
isSelected: selectedLogIndex == index,
), ),
); );
}, },
), )),
), ),
], ],
), ),
), ),
), ),
),
); );
} }
@ -334,45 +342,33 @@ class LogEntry {
class LogEntryWidget extends StatelessWidget { class LogEntryWidget extends StatelessWidget {
final LogEntry log; final LogEntry log;
final bool isSelected;
const LogEntryWidget( const LogEntryWidget({super.key, required this.log});
{super.key, required this.log, required this.isSelected});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Card( return Card(
color: isSelected ? Colors.blue[100] : null,
margin: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 8.0), margin: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 8.0),
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SelectableText( Text(
'${log.timestamp} - [${log.level.name}] - ${log.category} - ${log.threadId}', '${log.timestamp} - [${log.level.name}] - ${log.category} - ${log.threadId}',
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: _getLevelColor(log.level), color: _getLevelColor(log.level),
), ),
onTap: () {
FocusScope.of(context).unfocus();
},
), ),
SelectableText( Text(
'Component: ${log.component.name}@${log.component.address}', 'Component: ${log.component.name}@${log.component.address}',
style: const TextStyle( style: const TextStyle(
fontStyle: FontStyle.italic, fontStyle: FontStyle.italic,
), ),
onTap: () {
FocusScope.of(context).unfocus();
},
), ),
SelectableText( Text(
log.message.join('\n'), log.message.join('\n'),
onTap: () {
FocusScope.of(context).unfocus();
},
), ),
], ],
), ),

View File

@ -1,6 +0,0 @@
/// A placeholder class that represents an entity or model.
class SampleItem {
const SampleItem(this.id);
final int id;
}

View File

@ -1,20 +0,0 @@
import 'package:flutter/material.dart';
/// Displays detailed information about a SampleItem.
class SampleItemDetailsView extends StatelessWidget {
const SampleItemDetailsView({super.key});
static const routeName = '/sample_item';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Item Details'),
),
body: const Center(
child: Text('More Information Here'),
),
);
}
}

View File

@ -1,71 +0,0 @@
import 'package:flutter/material.dart';
import '../settings/settings_view.dart';
import 'sample_item.dart';
import 'sample_item_details_view.dart';
/// Displays a list of SampleItems.
class SampleItemListView extends StatelessWidget {
const SampleItemListView({
super.key,
this.items = const [SampleItem(1), SampleItem(2), SampleItem(3)],
});
static const routeName = '/';
final List<SampleItem> items;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Sample Items'),
actions: [
IconButton(
icon: const Icon(Icons.settings),
onPressed: () {
// Navigate to the settings page. If the user leaves and returns
// to the app after it has been killed while running in the
// background, the navigation stack is restored.
Navigator.restorablePushNamed(context, SettingsView.routeName);
},
),
],
),
// To work with lists that may contain a large number of items, its best
// to use the ListView.builder constructor.
//
// In contrast to the default ListView constructor, which requires
// building all Widgets up front, the ListView.builder constructor lazily
// builds Widgets as theyre scrolled into view.
body: ListView.builder(
// Providing a restorationId allows the ListView to restore the
// scroll position when a user leaves and returns to the app after it
// has been killed while running in the background.
restorationId: 'sampleItemListView',
itemCount: items.length,
itemBuilder: (BuildContext context, int index) {
final item = items[index];
return ListTile(
title: Text('SampleItem ${item.id}'),
leading: const CircleAvatar(
// Display the Flutter Logo image asset.
foregroundImage: AssetImage('assets/images/flutter_logo.png'),
),
onTap: () {
// Navigate to the details page. If the user leaves and returns to
// the app after it has been killed while running in the
// background, the navigation stack is restored.
Navigator.restorablePushNamed(
context,
SampleItemDetailsView.routeName,
);
}
);
},
),
);
}
}