structured_log_material
A ready-to-use Material 3 in-app log viewer for
structured_log, built on
structured_log_flutter’s
LogViewerController: a live, newest-first list with search, category, and
level filtering, a bottom-sheet detail view with full context and copy, and
an empty state that distinguishes “no logs yet” from “no logs match the
current filter”. Available both as a full screen (MaterialLogViewerPage)
and as a plain embeddable widget (MaterialLogViewer) for dropping into
existing page chrome — a tab, a side panel, a dialog, …
Status: published on pub.dev (
0.1.0). Design reference: the Log Viewer UI Concepts canvas (Material section —structured_log_fluentandstructured_log_cupertinoare implemented too, from the Fluent/Cupertino sections of the same canvas).
Features
Section titled “Features”MaterialLogViewer— the log viewer as a plain embeddable widget: a toolbar (search field, pause/resume, clear-all) above category- and level-filter chips and a live newest-first list — no page chrome of its own, so it can be dropped anywhere in an existing layoutMaterialLogViewerPage— a thinScaffold/AppBarwrapper aroundMaterialLogViewerfor the full-screen case: adds a title (“Logs”) and, when pushed viaNavigator, a back button (AppBar’s own)- Adaptive — how the selected entry’s context is shown reacts to the
width this widget is actually given (its own constraints, not the
window’s): below the master-detail breakpoint (the mobile-style default)
tapping a row opens
LogEntryDetailSheetas a modal bottom sheet; at or above it, the list and a non-modalLogEntryDetailPanelshow side by side instead — Material’s own list-detail layout guidance for tablet and desktop LogCategoryChips— acategory-filter chip row whose options are derived from the distinctcategoryvalues currently in the buffer; hidden automatically when fewer than two are presentLogEntryTile— one row: level-colored dot,event, formatted time, and a category tag if the entry has one; tinted when it’s the entry shown in an adjacentLogEntryDetailPanelLogEntryDetailSheet— tap a row (on narrow screens) to see its full context as formatted key/value pairs in a modal bottom sheet, with a “Copy context” actionLogEntryDetailPanel— the same context/copy content asLogEntryDetailSheet, but as a non-modal panel for the wide-screen master-detail split — no drag handle or “Close” buttonLogViewerEmptyState— “No logs yet” (nothing captured at all) vs. “No logs match the current filter” (with a “Clear filters” action)- Theme-aware — colors and typography come from
Theme.of(context)(light/dark both supported); only the level-indicator palette is fixed (logLevelColor), kept in one place so it can’t drift between widgets
Installation
Section titled “Installation”dependencies: structured_log_material: ^0.1.0Within this monorepo, melos bootstrap resolves both to path dependencies
instead:
dependencies: structured_log_flutter: path: ../structured_log_flutter structured_log_material: path: ../structured_log_materialQuick Start
Section titled “Quick Start”import 'package:flutter/material.dart';import 'package:structured_log/structured_log.dart';import 'package:structured_log_flutter/structured_log_flutter.dart';import 'package:structured_log_material/structured_log_material.dart';
void main() { final buffer = LogBuffer(); final controller = LogViewerController(buffer);
StructlogConfiguration.configure(sinks: [ LogSink(name: 'viewer', output: buffer.capture), ]);
runApp(MaterialApp( home: Builder( builder: (context) => Scaffold( body: Center( child: OutlinedButton( onPressed: () => Navigator.of(context).push( MaterialPageRoute( builder: (_) => MaterialLogViewerPage(controller: controller), ), ), child: const Text('Open log viewer'), ), ), ), ), ));}See example/ for a full runnable app (including web) — run it
with flutter run -d chrome from that directory.
To embed the viewer inside existing page chrome instead of giving it the
whole screen, use MaterialLogViewer directly:
Row( children: [ Expanded(child: MyAppContent()), SizedBox( width: 420, child: MaterialLogViewer(controller: controller), ), ],)Screenshots
Section titled “Screenshots”MaterialLogViewerPage as the full screen — master-detail split at this
width, list on the left and the selected entry’s full context on the
right:

MaterialLogViewer embedded in a side panel next to other app content —
the same widget, no page chrome of its own:

On a phone-width screen, the same page collapses to a list, and tapping
an entry opens LogEntryDetailSheet as a modal bottom sheet instead of
a side panel:

API Reference
Section titled “API Reference”| Widget | Description |
|---|---|
MaterialLogViewer({required LogViewerController controller}) |
The log viewer as an embeddable widget (no page chrome) |
MaterialLogViewerPage({required LogViewerController controller}) |
The full screen |
LogCategoryChips({required LogViewerController controller, String allLabel = 'All'}) |
The category-filter chip row |
LogEntryTile({required entry, required onTap, bool selected = false}) |
One list row |
LogEntryDetailSheet({required entry}) |
The expanded-entry bottom sheet content (narrow screens) |
LogEntryDetailPanel({required entry}) |
The expanded-entry non-modal panel content (wide screens) |
LogViewerEmptyState({required hasLogs, required onClearFilters}) |
The two-variant empty state |
logLevelColor(LogLevel level, Brightness brightness) |
The canonical level-indicator color — defined once in structured_log_flutter, shared by every skin |
entry throughout is the Map<String, dynamic> shape structured_log
produces directly — no separate typed model.
License
Section titled “License”MIT