refactor: optimize renderer
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gpt_markdown/gpt_markdown.dart';
|
||||
|
||||
@@ -5,7 +7,7 @@ import 'markdown_config.dart';
|
||||
|
||||
typedef MarkdownLinkTapCallback = void Function(String url, String title);
|
||||
|
||||
class StreamingMarkdownWidget extends StatelessWidget {
|
||||
class StreamingMarkdownWidget extends StatefulWidget {
|
||||
const StreamingMarkdownWidget({
|
||||
super.key,
|
||||
required this.content,
|
||||
@@ -17,6 +19,78 @@ class StreamingMarkdownWidget extends StatelessWidget {
|
||||
final bool isStreaming;
|
||||
final MarkdownLinkTapCallback? onTapLink;
|
||||
|
||||
@override
|
||||
State<StreamingMarkdownWidget> createState() =>
|
||||
_StreamingMarkdownWidgetState();
|
||||
}
|
||||
|
||||
class _StreamingMarkdownWidgetState extends State<StreamingMarkdownWidget> {
|
||||
late final ValueNotifier<String> _contentNotifier;
|
||||
late String _currentContent;
|
||||
Timer? _debounce;
|
||||
String? _pendingContent;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_currentContent = widget.content;
|
||||
_contentNotifier = ValueNotifier(widget.content);
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant StreamingMarkdownWidget oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (widget.content == _currentContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Coalesce rapid streaming updates so we only rebuild markdown a few times.
|
||||
_pendingContent = widget.content;
|
||||
_debounce ??= Timer(const Duration(milliseconds: 45), () {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
final next = _pendingContent ?? widget.content;
|
||||
_currentContent = next;
|
||||
_contentNotifier.value = next;
|
||||
_pendingContent = null;
|
||||
_debounce = null;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_debounce?.cancel();
|
||||
_contentNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ValueListenableBuilder<String>(
|
||||
valueListenable: _contentNotifier,
|
||||
builder: (context, value, _) {
|
||||
return _StreamingMarkdownContent(
|
||||
content: value,
|
||||
isStreaming: widget.isStreaming,
|
||||
onTapLink: widget.onTapLink,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _StreamingMarkdownContent extends StatelessWidget {
|
||||
const _StreamingMarkdownContent({
|
||||
required this.content,
|
||||
required this.isStreaming,
|
||||
required this.onTapLink,
|
||||
});
|
||||
|
||||
final String content;
|
||||
final bool isStreaming;
|
||||
final MarkdownLinkTapCallback? onTapLink;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final markdownTheme = ConduitMarkdownConfig.resolve(context);
|
||||
@@ -38,6 +112,8 @@ class StreamingMarkdownWidget extends StatelessWidget {
|
||||
onLinkTap: onTapLink,
|
||||
codeBuilder: markdownTheme.codeBuilder,
|
||||
imageBuilder: markdownTheme.imageBuilder,
|
||||
components: markdownTheme.blockComponents,
|
||||
inlineComponents: markdownTheme.inlineComponents,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user