【问题标题】:How to linkify text in Flutter如何在 Flutter 中链接文本
【发布时间】:2019-01-29 19:01:14
【问题描述】:

在 Flutter 中是否有一种简单的方法来“链接”可能包含纯文本、电子邮件和 Web URL 的文本?例如。如果我的文字是My phone number is 099 123 45 67 and my email is test@test.com 电话号码和电子邮件将呈现为可点击的链接。

在 Android 中,这将是一个单行:

textView.setAutoLinkMask(Linkify.ALL);

我看到有人问过类似的问题here。该解决方案适用于静态文本,但对于动态文本,解析文本、检测所有 URL、电话号码、电子邮件等并使用 TextSpans 相应地呈现它们会复杂得多。

【问题讨论】:

  • 我不这么认为。也许您可以从android源代码中复制链接解析器?
  • 我也找不到。我可能会使用RegExp 来获取电话号码、电子邮件和URL 的匹配项,并使用TextSpan 来正确呈现它们。只是想知道在重新发明轮子之前是否缺少一些库。

标签: dart flutter


【解决方案1】:

我为此创建了一个新包:flutter_linkify。它目前仅支持 URL,但您可以随时在 GitHub 上提交问题并提出功能请求。

基本用法:

import 'package:flutter_linkify/flutter_linkify.dart';

Linkify(
  onOpen: (url) => print("Clicked $url!"),
  text: "Made by https://cretezy.com",
);

【讨论】:

  • 添加功能以打开系统应用、电子邮件、第三方应用、要拨打的电话号码
  • 我们怎样才能只在 Dart 中做到这一点,而不是在 Flutter 中呢?预期输出:Made by <a href="https://cretezy.com">https://cretezy.com</a>。非常感谢。
  • @Kamlesh 你可以使用 Dart-only 库 pub.dev/packages/linkify,但是它的级别有点低。它应该提供解析文本的所有实用程序,但是您需要为它编写自己的 HTML 生成器。
【解决方案2】:

感谢@Charles Crete 创建该库。

我只是想在这里再添加一个解决方案,结合 RichText、TextSpan 和 TapGestureRecognizer(都在 Flutter 框架中)

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!');
                    })
            ])),
          ),
        ),
      ),
    );
  }
}

这是结果

【讨论】:

  • 我相信 OP 是在询问自动检测电话号码、地址或链接的代码,而不仅仅是将某些文本硬编码为“可点击”。
  • 你可能是对的。我只是想提供一种额外的方法,以便有人可以使用它。无论如何,为什么要投反对票? :)
  • 我的反对意见是因为没有正确处理TapGestureRecognizer 的生命周期(你并不孤单)。您必须按照文档中的说明显式调用 dispose() 方法:api.flutter.dev/flutter/painting/TextSpan/recognizer.html
  • 我们怎样才能只在 Dart 中做到这一点,而不是在 Flutter 中呢?预期输出:Made by <a href="https://cretezy.com">https://cretezy.com</a>。非常感谢。
【解决方案3】:

这是我的实现方式 - 使用 buildTextWithLinks 函数获取带有链接的 Text 组件。

它使用 url_launcher 并且当前支持 URL、邮件和电话链接,但可以通过添加更多 RegExps 和处理程序轻松扩展。

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

【讨论】:

  • 太棒了!谢谢你的回答。您节省了我们的另一个依赖项。这里只有一件事是 urlPattern 无法通过复制此代码来工作,尽管当我将其更改为 const String urlPattern = 'https?:/\/\\S+'; 时它工作了
  • @Smit 不确定您更改了什么。删除了原始前缀 (r)?
  • 是的。这是正确的。你能解释一下r的意义吗?
  • r 前缀表示raw string。在定义 RegExp 时,通常最好使用原始字符串,因此特殊的 RegExp 字符会被正确处理,而不是作为字符串转义序列。
【解决方案4】:

这个插件名为链接器 (https://github.com/best-flutter/linker/blob/master/example/lib/main.dart)

会帮助你。

您可以打开电话号码拨打电话

您可以打开其他系统应用或系统设置

您可以使用此软件包打开任何 3rd 方应用程序..

您可以打开电子邮件

此包在 pub.dev 中可用 ...搜索 Linker 以获取它

【讨论】:

    【解决方案5】:

    检查这个库flutter_autolink_text。与flutter_linkify 非常相似,但它也增加了对电话号码的支持。

    这是如何使用它。

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

    【讨论】:

    • 不适用于welinq.io 之类的链接 仅适用于像www.welinq.io 之类的链接
    【解决方案6】:

    此功能可以自动检测文本视图中的超链接。你可以修改 Regx 模式取决于您的要求。

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

    用法

    child: SelectableText.rich(
                    TextSpan(
                        children: extractText(dummyText),
                        style: TextStyle(fontSize: _fontSize)),
                  )
    

    【讨论】:

      猜你喜欢
      • 2020-12-10
      • 1970-01-01
      • 2021-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-11
      • 2020-09-26
      相关资源
      最近更新 更多