How to linkify text in Flutter
Asked Answered
M

6

21

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 TextSpans to render them accordingly.

Mcferren answered 23/8, 2018 at 11:46 Comment(3)
I don't think so. Maybe you can copy the link parser from the android source code?Jorge
I also couldn't find any. I will probably use RegExp to get matches for phone numbers, emails and URLs, and use TextSpan to render them properly. Just wanted to know if there was some library I was missing before re-inventing the wheel.Mcferren
Flutter Hyperlink exampleStarr
M
20

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",
);
Moon answered 18/9, 2018 at 23:4 Comment(4)
add features to open system apps,emails,third party apps ,phone number to callBoogeyman
how can we do it in Dart only, not in Flutter? Expected output: Made by <a href="https://cretezy.com">https://cretezy.com</a>. Thanks a lot.Briar
@Briar You can use the Dart-only library pub.dev/packages/linkify, however it's a bit lower level. It should provide all the utilities to parse the text, however you will need to write your own HTML generator for it.Moon
Can u show how youve set $url then i restore your pointBetancourt
I
9

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

enter image description here

Indo answered 4/12, 2018 at 13:21 Comment(4)
I believe the OP is asking about code to automatically detect phone numbers, addresses, or links, not just hardcoding some text as 'clickable.'Miru
You may be correct. I just wanted to provide an additional method, so someone could use it. Anyway, why vote down? :)Indo
My vote down is for not handling life cycle of TapGestureRecognizer correctly (you are not alone in this). You have to explicitly call dispose() method as is stated in the docs: api.flutter.dev/flutter/painting/TextSpan/recognizer.htmlCentury
how can we do it in Dart only, not in Flutter? Expected output: Made by <a href="https://cretezy.com">https://cretezy.com</a>. Thanks a lot.Briar
B
9

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 RegExps 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;
}
Bombardon answered 13/10, 2019 at 8:21 Comment(4)
Great! thanks for this answer. You save our one more dependency. Just one thing here is that urlPattern wasn't working by copying this code, though it worked when I changed it to const String urlPattern = 'https?:/\/\\S+';Moly
@Smit Not sure what you changed. Removed the raw prefix (r)?Bombardon
Yes. that's correct. Can you please explain what is the significance of r ?Moly
The r prefix indicates a raw string. When defining a RegExp it is usually a good idea to use raw strings, so special RegExp charachters are treated properly, and not as a string escape sequnces.Bombardon
E
6

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

 

Evangelinaevangeline answered 8/11, 2021 at 18:59 Comment(0)
B
2

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}')
);
Blush answered 12/11, 2020 at 23:0 Comment(2)
Doesn't work with the link like welinq.io It works only when it is like www.welinq.ioUnstriped
Not compatible with Dart 3, bummer :(Woodyard
B
0

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

Boogeyman answered 14/11, 2019 at 13:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.