Egemen
Egemen

Reputation: 2568

How to linkify text in Flutter

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.

Upvotes: 23

Views: 20782

Answers (6)

Wai Han Ko
Wai Han Ko

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

velval
velval

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

raman raman
raman raman

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

splintor
splintor

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 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;
}

Upvotes: 9

Phuc Tran
Phuc Tran

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

enter image description here

Upvotes: 9

Charles Crete
Charles Crete

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

Related Questions