Here is how I implemented it - use the buildTextWithLinks
function to get a Text
component with links.
It uses url_launcher and currnetly supports URL, mail and phone links, but can easily be expanded by adding more RegExp
s and handlers.
import 'package:url_launcher/url_launcher.dart';
Text buildTextWithLinks(String textToLink) => Text.rich(TextSpan(children: linkify(textToLink)));
Future<void> openUrl(String url) async {
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
const String urlPattern = r'https?:/\/\\S+';
const String emailPattern = r'\S+@\S+';
const String phonePattern = r'[\d-]{9,}';
final RegExp linkRegExp = RegExp('($urlPattern)|($emailPattern)|($phonePattern)', caseSensitive: false);
WidgetSpan buildLinkComponent(String text, String linkToOpen) => WidgetSpan(
child: InkWell(
child: Text(
text,
style: TextStyle(
color: Colors.blueAccent,
decoration: TextDecoration.underline,
),
),
onTap: () => openUrl(linkToOpen),
)
);
List<InlineSpan> linkify(String text) {
final List<InlineSpan> list = <InlineSpan>[];
final RegExpMatch match = linkRegExp.firstMatch(text);
if (match == null) {
list.add(TextSpan(text: text));
return list;
}
if (match.start > 0) {
list.add(TextSpan(text: text.substring(0, match.start)));
}
final String linkText = match.group(0);
if (linkText.contains(RegExp(urlPattern, caseSensitive: false))) {
list.add(buildLinkComponent(linkText, linkText));
}
else if (linkText.contains(RegExp(emailPattern, caseSensitive: false))) {
list.add(buildLinkComponent(linkText, 'mailto:$linkText'));
}
else if (linkText.contains(RegExp(phonePattern, caseSensitive: false))) {
list.add(buildLinkComponent(linkText, 'tel:$linkText'));
} else {
throw 'Unexpected match: $linkText';
}
list.addAll(linkify(text.substring(match.start + linkText.length)));
return list;
}
RegExp
to get matches for phone numbers, emails and URLs, and useTextSpan
to render them properly. Just wanted to know if there was some library I was missing before re-inventing the wheel. – Mcferren