如何在 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、电话号码、电子邮件等并使用 TextSpan
s 相应地呈现它们会复杂得多。
【问题讨论】:
我不这么认为。也许您可以从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、邮件和电话链接,但可以通过添加更多 RegExp
s 和处理程序轻松扩展。
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 文本 [重复]