Reputation: 2568
Is there an easy way in Flutter to 'linkify' a text that might contain a mix of plain text, emails and web URLs? E.g. if my text is My phone number is 099 123 45 67 and my email is [email protected]
the phone number and the email would be rendered as clickable links.
In Android it would be a one liner:
textView.setAutoLinkMask(Linkify.ALL);
I've seen that a similar question has been asked here. That solution would work fine for static texts, but for dynamic texts it would be a lot more complicated to parse the text, detect all URLs, phone numbers, emails etc. and use TextSpan
s to render them accordingly.
Upvotes: 23
Views: 20782
Reputation: 1043
This function can auto detect what is hyper link in textview. You can modify Regx pattern depend on your requirements.
List<TextSpan> extractText(String rawString) {
List<TextSpan> textSpan = [];
final urlRegExp = new RegExp(
r"((https?:www\.)|(https?:\/\/)|(www\.))[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9]{1,6}(\/[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)?");
getLink(String linkString) {
textSpan.add(
TextSpan(
text: linkString,
style: new TextStyle(color: Colors.blue),
recognizer: new TapGestureRecognizer()
..onTap = () {
Fluttertoast.showToast(msg: linkString);
},
),
);
return linkString;
}
getNormalText(String normalText) {
textSpan.add(
TextSpan(
text: normalText,
style: new TextStyle(color: Colors.black),
),
);
return normalText;
}
rawString.splitMapJoin(
urlRegExp,
onMatch: (m) => getLink("${m.group(0)}"),
onNonMatch: (n) => getNormalText("${n.substring(0)}"),
);
return textSpan;}
Usages
child: SelectableText.rich(
TextSpan(
children: extractText(dummyText),
style: TextStyle(fontSize: _fontSize)),
)
Here is result
Upvotes: 6
Reputation: 3312
Check this library flutter_autolink_text. Is very similar to flutter_linkify but it adds support to phone number as well.
This is how to use it.
import 'package:flutter_autolink_text/flutter_autolink_text.dart';
AutolinkText(
text: ...,
textStyle: TextStyle(color: Colors.black),
linkStyle: TextStyle(color: Colors.blue),
onWebLinkTap: (link) => print('Clicked: ${link}'),
onEmailTap: (link) => print('Clicked: ${link}'),
onPhoneTap: (link) => print('Clicked: ${link}')
);
Upvotes: 2
Reputation: 1811
This plugin named linker (https://github.com/best-flutter/linker/blob/master/example/lib/main.dart)
is going to help you.
You can open phone number to call
you can open other system apps or system settings
You can open any 3rd party apps using this package..
you can open email
This package is available in pub.dev ...Search Linker to get it
Upvotes: 0
Reputation: 10154
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;
}
Upvotes: 9
Reputation: 8083
Thank you @Charles Crete for creating that library.
I just want to add one more solution here by combining RichText, TextSpan and TapGestureRecognizer (all are in Flutter framework)
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
void main() {
runApp(HyperLinkDemo());
}
class HyperLinkDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: RichText(
text: TextSpan(children: [
TextSpan(
text: 'This is a very long text, but you can not click on it. ',
style: TextStyle(fontSize: 20, color: Colors.black)),
TextSpan(
text: 'And this is a clickable text',
style: TextStyle(
fontSize: 20,
color: Colors.blue,
decoration: TextDecoration.underline),
recognizer: TapGestureRecognizer()
..onTap = () {
print('You clicked on me!');
})
])),
),
),
),
);
}
}
Here is the result
Upvotes: 9
Reputation: 964
I created a new package for this: flutter_linkify. It currently only supports URLS, but you can always submit an issue on GitHub with feature requests.
Baisc usage:
import 'package:flutter_linkify/flutter_linkify.dart';
Linkify(
onOpen: (url) => print("Clicked $url!"),
text: "Made by https://cretezy.com",
);
Upvotes: 20