I am implementing something like a breadcrumbs trail of hot (tap-able) text separated by separator characters and I want the whole thing to wrap as needed in the middle of the hot text if the need to wrap falls there. Because the crumbs are links, they have to be a collection of widgets, but displaying them in a Wrap object causes them to wrap on a per-widget basis and therefore never in the middle of the crumb links.
I tried to do this using Row, rows of Rows with the last one expanded, trying properties like softWrap on Text objects, "rich text," and more. I looked around for this use case, and posted to another forum. Seems like this should be possible in a mature app development framework.
After a quick research, it turns out that TextSpan
has a property that provides gesture recognizer in recognizer
. This recognizer can be filled with many gesture recognizer such as TapGestureRecognizer
that has similar ability with onTap
in GestureDetector
.
I provide example code of how this works:
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
class InlineLinkPage extends StatelessWidget {
const InlineLinkPage({super.key});
@override
Widget build(BuildContext context) {
// Crumb separator.
const TextSpan separator = TextSpan(text: ' | ');
// Build crumb with [TextSpan] with the parameter [text] as [String].
//
// Usage Example:
// ```
// crumb('First Breadcrumb');
// ```
TextSpan crumb(String text) {
// A function that triggered when the text is tapped.
void handleTap() {
// Write your logical [crumb] behavior when user tap the [crumb] here...
print('Clicked: $text');
}
// Initialize [GestureRecognizer] for TextSpan. This is for detect tap
// from user.
//
// In this case [recognizer] use [TapGestureRecognizer], The way it work
// is the same as [onTap] in [GestureDetector].
//
// There's other option for [recognizer] like [LongPressGestureRecognizer()..onLongPress],
// for detect long press on the widget.
final recognizer = TapGestureRecognizer()..onTap = handleTap;
return TextSpan(
text: text,
recognizer: recognizer,
);
}
return Scaffold(
appBar: AppBar(title: const Text('Inline Link Page')),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(16),
child: RichText(
text: TextSpan(
style: const TextStyle(color: Colors.black87),
children: [
crumb('This is first crumb'),
separator,
crumb('This is the second crumb'),
separator,
crumb('This is the third crumb'),
separator,
crumb('This is the fourth crumb'),
separator,
crumb('etc'),
separator,
],
),
),
),
),
);
}
}
Possible issue
I don't see that
TapGestureRecognizer
hastapTargetSize
to manage the tap area of the link, so the user needs to tap precisely insideTextSpan
.Recommendation
Adding line height to
TextSpan
will minimalize miss click case. Check this question for the insight.
Reference: