如何在 Flutter 中链接文本

Posted

技术标签:

【中文标题】如何在 Flutter 中链接文本【英文标题】:How to linkify text in 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 来正确呈现它们。只是想知道在重新发明***之前是否缺少一些库。 Flutter Hyperlink example 【参考方案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)),
              )

【讨论】:

以上是关于如何在 Flutter 中链接文本的主要内容,如果未能解决你的问题,请参考以下文章

Flutter——如何使用 html 链接渲染 Flutter 文本 [重复]

当用户使用flutter键入一些文本时,如何解析和替换单词作为主题标签链接?

如何在颤动的降价中添加超链接到文本

如何在 Flutter 中清除输入文本

Flutter - 如何在一行中显示文本和图标?

如何在 Flutter 的 TextField 中禁用预测文本?