span_builder 0.0.1 span_builder: ^0.0.1 copied to clipboard
Facilitates creation of spans from plain text and provides automated disposal of GestureRecognizers
Facilitates creation of spans from plain text and provides an automated disposal of GestureRecognizers
.
Description #
Given some plain text, e.g.: "The quick brown fox" allows you to apply
multiple spans at multiple positions. Positions can be specified by a matching word (whereText
), e.g. "brown" and/or range from=10, to=15
. Spans are subclasses of InlineSpan
. If the span you are trying to apply is TextSpan
you don't need to pass whereText
as it will be inferred from text within the provided span.
Once you are done using SpanBuilder
, use build()
to return calculated list of spans. Spans can't overlap.
Usage: #
final spans = SpanBuilder("The quick brown fox")
.apply(TextSpan(text: "brown", style: TextStyle(fontWeight: FontWeight.bold)))
.apply(TextSpan(text: "🦊"), whereText: "fox")
.build()
From there you can use these spans in your RichText, e.g.:
RichText(
text: TextSpan(children: spans)
)
If you plan to make your text "tappable" read on.
Handling GestrueRecognizer (text taps)
If you try passing GestrueRecognizer
as a field in TextSpan
it will get stripped away - HERE IS WHY.
TL;DR: We don't want to leak GestureRecognizer
but TextSpan
has no idea about the lifecycle of Widget
so you need a stateful widget to keep a reference to the recognizer untill you're done with it. Sounds like a mess, right?
The workaround is to provide a builder for the recognizer like this:
apply(TextSpan(text: "jumps"),
recognizerBuilder: () => TapGestureRecognizer()..onTap = () {
// your code here
})
Then you can use this SpanBuilder
together with SpanBuilderWidget
which will manage creating and disposing of TapGestrueRecognizer
at the right time:
SpanBuilderWidget(
text: SpanBuilder("fox jumps")
..apply(TextSpan(text: "jumps"),
recognizerBuilder: () => TapGestureRecognizer()..onTap = () {
// your code here
})
)
If you care only about onTap
interaction you can use this API shortcut:
apply(TextSpan(text: "jumps"),
onTap: () {
// your code here
}
)
Testing: #
There are little resources on how to test RichText in general. For this reason there is helper testing library span_builder_test
that can help you verify state of your spans in your UI tests.
void main() {
testWidgets('MyApp test', (WidgetTester tester) async {
await tester.pumpWidget(MyApp());
final spanFinder = find.byKey(span_key);
expect(spanFinder, findsOneWidget);
final allSpans = tester.findSpans(spanFinder).length;
expect(allSpans, 8);
final foxSpans = tester.findSpans(spanFinder, predicate: (span) {
return span is TextSpan && span.text == "🦊";
});
expect(foxSpans.length, 1);
});
}