import 'package:flutter/material.dart'; class ConsoleEntry { final String text; final bool generatedByRuntime; final DateTime timeGenerated; ConsoleEntry({ required this.text, required this.generatedByRuntime, required this.timeGenerated, }); } class _ConsoleFilter extends StatelessWidget { const _ConsoleFilter(this.filter, {required this.onChanged}); final String filter; final Function(String filter) onChanged; @override Widget build(BuildContext context) { return Row( children: [ Expanded( child: Padding( padding: const EdgeInsets.only(bottom: 3), child: TextFormField( autocorrect: false, enableSuggestions: false, onChanged: onChanged, style: const TextStyle(fontSize: 12), decoration: const InputDecoration( contentPadding: EdgeInsets.all(6), labelText: "Filter: ", ), ), ), ), const SizedBox(width: 16), const Text("Console entries are only kept for several seconds"), ], ); } } class _ConsoleLine extends StatelessWidget { const _ConsoleLine( this.text, { required this.runtime, required this.time, }); final String text; final bool runtime; final DateTime time; @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric(vertical: 3), child: DecoratedBox( decoration: BoxDecoration( color: this.runtime ? Colors.blue.shade900.withAlpha(128) : Colors.purple.shade900.withAlpha(128), borderRadius: const BorderRadius.all(Radius.circular(4)), border: Border.all( color: this.runtime ? Colors.blue.shade700 : Colors.purple.shade700, width: 3, )), child: Padding( padding: const EdgeInsets.all(6), child: Row( children: [ Text(this.runtime ? "Runtime" : "Editor"), Expanded( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Text( this.text, style: const TextStyle(fontFamily: 'Consolas'), ), ), ), Text( "${this.time.hour.toString().padLeft(2, '0')}:${this.time.minute.toString().padLeft(2, '0')}") ], ), ), ), ); } } class ConsolePage extends StatefulWidget { final List entries; final void Function(String) messageFn; const ConsolePage(this.entries, this.messageFn, {super.key}); @override State createState() => _ConsolePageState(); } class _ConsolePageState extends State { String filter = ""; @override Widget build(BuildContext context) { final controller = TextEditingController(); final filteredMessages = this.widget.entries.where((e) => e.text.contains(this.filter)).toList(); return Center( child: Padding( padding: const EdgeInsets.all(20), child: Column(children: [ _ConsoleFilter( filter, onChanged: (newFilter) => this.setState(() => this.filter = newFilter), ), Expanded( child: ListView.builder( reverse: true, itemCount: filteredMessages.length, prototypeItem: _ConsoleLine( "Loading...", runtime: false, time: DateTime(1970), ), itemBuilder: (context, index) { final entry = filteredMessages[filteredMessages.length - index - 1]; return _ConsoleLine( entry.text, runtime: entry.generatedByRuntime, time: entry.timeGenerated, ); }, ), ), TextField( autocorrect: false, enableSuggestions: false, style: const TextStyle(fontFamily: 'Consolas'), controller: controller, decoration: const InputDecoration(hintText: "Send a console message to the runtime"), onSubmitted: (message) { this.widget.messageFn(message); controller.clear(); }, ), ]), ), ); } }