Open chan150 opened 2 months ago
It should be related with #25
What's the point of removing WidgetSpan
during layout? In this scenario, the package won't function as expected:
trimMode
is set to TrimMode.Line
and trimLines
is set to 3, if there are 3 lines of text followed by a WidgetSpan
on the 4th line, the measurement will not detect any text to trim.trimMode
is set to TrimMode.Line
and trimLines
is set to 3, if there are 3 lines of WidgetSpan
followed by 2 lines of text, the measurement will not detect any text to trim.What's the point of removing
WidgetSpan
during layout? In this scenario, the package won't function as expected:
- When
trimMode
is set toTrimMode.Line
andtrimLines
is set to 3, if there are 3 lines of text followed by aWidgetSpan
on the 4th line, the measurement will not detect any text to trim.- When
trimMode
is set toTrimMode.Line
andtrimLines
is set to 3, if there are 3 lines ofWidgetSpan
followed by 2 lines of text, the measurement will not detect any text to trim.
Hi @maRci002,
It is a scenario to be useful of the added feature.
We can attach overlayable widget inside readmore
widget.
I understand that removing just WidgetSpan is too naive and there is no way to estimate the size of widget before rendering.
ReadMoreText(
'@[{"key": {"inner":"test"}}] and @[{"key":"value"}]',
annotations: [
Annotation(
regExp: RegExp('@\\[(.+?)\\]'),
spanBuilder: ({required text, required textStyle}) {
return WidgetSpan(
child: Tooltip(
message: 'It is just tooltip',
child: Text(
'It is a widget',
style: TextStyle(height: 0),
),
),
);
},
),
],
style: TextStyle(color: Colors.red),
)
I understand that removing just WidgetSpan is too naive and there is no way to estimate the size of widget before rendering.
I think instead of removing WidgetSpan
during the layout phase, we should provide or calculate the WidgetSpan
size.
Note: The effectiveTextStyle
is already provided to Annotation.spanBuilder
, and we could offer a simple function that takes a TextSpan
(without any WidgetSpan
children) and returns its size. For example, this works when WidgetSpan
includes a ToolTip
widget, but users can also return a hardcoded size. Therefore, the spanBuilder
could return both InlineSpan
and possible PlaceholderDimensions
.
Here is an example of how PlaceholderDimensions
can be used in the case of WidgetSpan
. However, the trimming logic should also be updated, which results in a very difficult API.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('WidgetSpan Test'),
),
body: const Center(
child: TextPainterButton(),
),
),
);
}
}
class TextPainterButton extends StatelessWidget {
const TextPainterButton({super.key});
void _printTextLayout(BuildContext context) {
final defaultTextStyle = DefaultTextStyle.of(context);
var effectiveTextStyle = defaultTextStyle.style;
if (MediaQuery.boldTextOf(context)) {
effectiveTextStyle = effectiveTextStyle.merge(const TextStyle(fontWeight: FontWeight.bold));
}
final textScaler = MediaQuery.textScalerOf(context);
final textAlign = defaultTextStyle.textAlign ?? TextAlign.start;
final textDirection = Directionality.of(context);
final locale = Localizations.maybeLocaleOf(context);
final textWidthBasis = defaultTextStyle.textWidthBasis;
final textHeightBehavior = defaultTextStyle.textHeightBehavior ?? DefaultTextHeightBehavior.maybeOf(context);
final textPainter = TextPainter(
textAlign: textAlign,
textDirection: textDirection,
locale: locale,
textScaler: textScaler,
maxLines: 1,
textWidthBasis: textWidthBasis,
textHeightBehavior: textHeightBehavior,
);
const txt = 'It is a widget';
const widgetSpan = WidgetSpan(
child: Tooltip(
message: 'It is just tooltip',
child: Text(txt),
),
);
final widgetSpanText = TextSpan(
text: txt,
style: effectiveTextStyle,
);
textPainter.text = widgetSpanText;
textPainter.layout();
final widgetSpanSize = textPainter.size;
final widgetSpanPlaceHolderDimension = PlaceholderDimensions(
size: widgetSpanSize,
alignment: widgetSpan.alignment,
baseline: widgetSpan.baseline,
);
final span = TextSpan(
style: effectiveTextStyle.merge(const TextStyle(color: Colors.black, fontSize: 18)),
text: 'Hello, TextPainter!',
children: const [widgetSpan],
);
textPainter.text = span;
textPainter.setPlaceholderDimensions([widgetSpanPlaceHolderDimension]);
textPainter.layout();
print('Text size: ${textPainter.size}');
}
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () => _printTextLayout(context),
child: const Text('Layout Text'),
);
}
}
I think instead of removing
WidgetSpan
during the layout phase, we should provide or calculate theWidgetSpan
size.
I provide a widget size estimator. It will be helpful for above idea.
Size? _calculateWidgetSize(Widget widget) {
final child = InheritedTheme.captureAll(
context,
MediaQuery(
data: MediaQuery.of(context),
child: Material(
color: Colors.transparent,
child: widget,
),
),
);
final RenderRepaintBoundary repaintBoundary = RenderRepaintBoundary();
final platformDispatcher = WidgetsBinding.instance.platformDispatcher;
final fallBackView = platformDispatcher.views.first;
final view = View.maybeOf(context) ?? fallBackView;
Size logicalSize = view.physicalSize / view.devicePixelRatio; // Adapted
final RenderView renderView = RenderView(
view: view,
child: RenderPositionedBox(
alignment: Alignment.center, child: repaintBoundary),
configuration: ViewConfiguration(
// size: logicalSize,
logicalConstraints: BoxConstraints(
maxWidth: logicalSize.width,
maxHeight: logicalSize.height,
),
devicePixelRatio: 1.0,
),
);
final PipelineOwner pipelineOwner = PipelineOwner();
final BuildOwner buildOwner =
BuildOwner(focusManager: FocusManager(), onBuildScheduled: () {});
pipelineOwner.rootNode = renderView;
renderView.prepareInitialFrame();
final RenderObjectToWidgetElement<RenderBox> rootElement =
RenderObjectToWidgetAdapter<RenderBox>(
container: repaintBoundary,
child: Directionality(
textDirection: TextDirection.ltr,
child: child,
)).attachToRenderTree(
buildOwner,
);
buildOwner.buildScope(
rootElement,
);
buildOwner.finalizeTree();
pipelineOwner.flushLayout();
pipelineOwner.flushCompositingBits();
pipelineOwner.flushPaint();
return rootElement.size;
}
_isTextSpan
.