Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:talker_flutter/src/utils/download_logs/donwload_logs.dart';
import 'package:talker_flutter/talker_flutter.dart';
Expand All @@ -8,9 +10,12 @@ class TalkerViewController extends ChangeNotifier {
required Talker talker,
bool expandedLogs = true,
isLogOrderReversed = true,
}) : _expandedLogs = expandedLogs,
}) : _talker = talker,
_expandedLogs = expandedLogs,
_isLogOrderReversed = isLogOrderReversed;

final Talker _talker;

/// Filter for selecting specific logs and errors on [TalkerScreen] and [TalkerView]
/// by their keys [TalkerData.key] and by string query [TalkerFilter.searchQuery]
/// Works only on screen (don't affect [Talker.filter])
Expand All @@ -19,6 +24,18 @@ class TalkerViewController extends ChangeNotifier {
bool _expandedLogs;
bool _isLogOrderReversed;

/// Whether log streaming is paused.
/// When paused, the view shows a frozen snapshot of logs.
bool _isPaused = false;

/// Frozen snapshot of logs taken when pause was activated.
List<TalkerData> _frozenLogs = [];

Timer? _searchDebounceTimer;

/// Duration for debouncing search query updates.
static const _searchDebounceDuration = Duration(milliseconds: 300);

/// Filter for selecting specific logs and errors
TalkerFilter get filter => _uiFilter;
set filter(TalkerFilter val) {
Expand All @@ -41,12 +58,35 @@ class TalkerViewController extends ChangeNotifier {
notifyListeners();
}

/// Method for updating a search query based on errors and logs
void updateFilterSearchQuery(String query) {
_uiFilter = _uiFilter.copyWith(searchQuery: query);
/// Whether log streaming is paused
bool get isPaused => _isPaused;

/// Frozen snapshot of logs (empty when not paused)
List<TalkerData> get frozenLogs => _frozenLogs;

/// Toggle pause/resume. When pausing, freezes the current log history.
/// When resuming, clears the frozen snapshot and returns to live logs.
void togglePause() {
if (_isPaused) {
_frozenLogs = [];
_isPaused = false;
} else {
_frozenLogs = List.from(_talker.history);
_isPaused = true;
}
notifyListeners();
}

/// Method for updating a search query based on errors and logs.
/// Uses debouncing to avoid excessive rebuilds during rapid typing.
void updateFilterSearchQuery(String query) {
_searchDebounceTimer?.cancel();
_searchDebounceTimer = Timer(_searchDebounceDuration, () {
_uiFilter = _uiFilter.copyWith(searchQuery: query);
notifyListeners();
});
}

/// Method adds an key to the filter
void addFilterKey(String key) {
_uiFilter = _uiFilter.copyWith(
Expand All @@ -67,4 +107,10 @@ class TalkerViewController extends ChangeNotifier {

/// Redefinition [notifyListeners]
void update() => notifyListeners();

@override
void dispose() {
_searchDebounceTimer?.cancel();
super.dispose();
}
}
64 changes: 57 additions & 7 deletions packages/talker_flutter/lib/src/ui/talker_builder.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:talker_flutter/talker_flutter.dart';

Expand All @@ -6,7 +8,9 @@ typedef TalkerWidgetBuilder = Widget Function(
List<TalkerData> data,
);

class TalkerBuilder extends StatelessWidget {
/// Listens to [Talker.stream] and rebuilds with throttling to prevent
/// excessive widget rebuilds during high-frequency logging.
class TalkerBuilder extends StatefulWidget {
const TalkerBuilder({
Key? key,
required this.talker,
Expand All @@ -16,13 +20,59 @@ class TalkerBuilder extends StatelessWidget {
final Talker talker;
final TalkerWidgetBuilder builder;

@override
State<TalkerBuilder> createState() => _TalkerBuilderState();
}

class _TalkerBuilderState extends State<TalkerBuilder> {
StreamSubscription<TalkerData>? _subscription;
Timer? _throttleTimer;

/// Trailing-edge throttle window. During high-frequency logging,
/// at most one rebuild fires per window (after the burst settles).
static const _throttleDuration = Duration(milliseconds: 500);

@override
void initState() {
super.initState();
_subscribe();
}

@override
void didUpdateWidget(covariant TalkerBuilder oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.talker != widget.talker) {
_unsubscribe();
_subscribe();
}
}

@override
void dispose() {
_unsubscribe();
super.dispose();
}

void _subscribe() {
_subscription = widget.talker.stream.listen((_) {
// Trailing-edge throttle: reset the timer on every event,
// so setState fires only once after the burst settles.
_throttleTimer?.cancel();
_throttleTimer = Timer(_throttleDuration, () {
if (mounted) setState(() {});
});
});
}

void _unsubscribe() {
_throttleTimer?.cancel();
_throttleTimer = null;
_subscription?.cancel();
_subscription = null;
}

@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: talker.stream,
builder: (BuildContext context, _) {
return builder(context, talker.history);
},
);
return widget.builder(context, widget.talker.history);
}
}
7 changes: 7 additions & 0 deletions packages/talker_flutter/lib/src/ui/talker_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class TalkerScreen extends StatelessWidget {
this.customSettings = const [],
this.isLogsExpanded = true,
this.isLogOrderReversed = true,
this.maxDisplayedLogs = 500,
}) : super(key: key);

/// Talker implementation
Expand Down Expand Up @@ -40,6 +41,11 @@ class TalkerScreen extends StatelessWidget {
///{@macro talker_flutter_is_log_order_reversed}
final bool isLogOrderReversed;

/// Maximum number of log entries to display.
/// Set to 0 to display all logs.
/// Defaults to 500.
final int maxDisplayedLogs;

@override
Widget build(BuildContext context) {
return Scaffold(
Expand All @@ -53,6 +59,7 @@ class TalkerScreen extends StatelessWidget {
customSettings: customSettings,
isLogsExpanded: isLogsExpanded,
isLogOrderReversed: isLogOrderReversed,
maxDisplayedLogs: maxDisplayedLogs,
),
);
}
Expand Down
Loading