我想在我的Flutter应用程序中创建一个超链接。
超链接应该嵌入到Text
或类似的文本视图中:
最后一本购买的书是<a href='#'>这本</a>
如何实现?
我想在我的Flutter应用程序中创建一个超链接。
超链接应该嵌入到Text
或类似的文本视图中:
最后一本购买的书是<a href='#'>这本</a>
如何实现?
只需在文本小部件周围包装一个InkWell,并将UrlLauncher(来自服务库)提供给onTap属性。在下面使用它之前,将UrlLauncher作为Flutter软件包安装。
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:url_launcher/url_launcher.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text('UrlLauncher'),
),
body: new Center(
child: new InkWell(
child: new Text('Open Browser'),
onTap: () => launch('https://docs.flutter.io/flutter/services/UrlLauncher-class.html')
),
),
),
);
}
}
您可以为 Text widget 提供样式以使其看起来像一个链接。
在稍微研究了一下这个问题之后,我找到了一个不同的解决方案,可以实现您要求的“内联”超链接。您可以使用 RichText Widget 与封闭的 TextSpans。
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:url_launcher/url_launcher.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text('UrlLauchner'),
),
body: new Center(
child: new RichText(
text: new TextSpan(
children: [
new TextSpan(
text: 'This is no Link, ',
style: new TextStyle(color: Colors.black),
),
new TextSpan(
text: 'but this is',
style: new TextStyle(color: Colors.blue),
recognizer: new TapGestureRecognizer()
..onTap = () { launch('https://docs.flutter.io/flutter/services/UrlLauncher-class.html');
},
),
],
),
),
),
),
);
}
}
通过这种方式,您实际上可以突出显示一个单词并将其变成超链接 ;)
TapGestureRecognizer
的生命周期。当 RichText
不再使用时,你需要调用 dispose()
方法。请参考这里:https://api.flutter.dev/flutter/painting/TextSpan/recognizer.html - Alex SemeniukStatelessWidget
不能自动为您处理TapGestureRecognizer
的释放。实际上,在这种情况下使用StatelessWidget
是不正确的,因为您无法以这种方式释放资源。而且,您绝对需要调用TapGestureRecognizer
的dispose()
方法,因为它会运行内部计时器,需要停止它。 - Alex SemeniukFlutter 没有内置的超链接支持,但你可以自己进行模拟。在 Gallery 的 drawer.dart 中有一个例子。他们使用了一个包含有色 TextSpan
的 RichText
小部件,该小部件具有一个 recognizer
属性来处理触摸事件:
RichText(
text: TextSpan(
children: [
TextSpan(
style: bodyTextStyle,
text: seeSourceFirst,
),
TextSpan(
style: bodyTextStyle.copyWith(
color: colorScheme.primary,
),
text: repoText,
recognizer: TapGestureRecognizer()
..onTap = () async {
final url = 'https://github.com/flutter/gallery/';
if (await canLaunch(url)) {
await launch(
url,
forceSafariVC: false,
);
}
},
),
TextSpan(
style: bodyTextStyle,
text: seeSourceSecond,
),
],
),
打开info.plist
文件并添加:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>https</string>
</array>
打开位于app/src/main
的AndroidManifest.xml
文件,并在根目录下添加以下内容:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Add this query -->
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
</queries>
<application ... />
</manifest>
只需将您的 Text
包装在 GestureDetector
或 InkWell
中,并使用url_launcher
包中的 onTap()
处理点击即可。
InkWell(
onTap: () => launchUrl(Uri.parse('https://www.google.com')),
child: Text(
'Click here',
style: TextStyle(decoration: TextDecoration.underline, color: Colors.blue),
),
)
InkWell
代替GestureDetector
。 - CopsOnRoad你可以使用flutter_linkify这个包
https://pub.dev/packages/flutter_linkify
它会自动将你的文本分割并突出显示http/https链接。
结合url_launcher插件,您可以启动链接。
您可以查看下面的示例:
完整代码如下:
import 'package:flutter/material.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'dart:async';
import 'package:url_launcher/url_launcher.dart';
void main() => runApp(new LinkifyExample());
class LinkifyExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'flutter_linkify example',
home: Scaffold(
appBar: AppBar(
title: Text('flutter_linkify example'),
),
body: Center(
child: Linkify(
onOpen: _onOpen,
text: "Made by https://cretezy.com \n\nMail: example@gmail.com \n\n this is test http://pub.dev/ ",
),
),
),
);
}
Future<void> _onOpen(LinkableElement link) async {
if (await canLaunch(link.url)) {
await launch(link.url);
} else {
throw 'Could not launch $link';
}
}
}
url_launcher: ^6.0.8
Link(
uri: Uri.parse('https://androidride.com'),
//target: LinkTarget.self,
builder: (context, followLink) {
return RichText(
text: TextSpan(children: [
TextSpan(
text: 'Click here: ',
style: TextStyle(
fontSize: 20,
color: Colors.black,
),
),
TextSpan(
text: 'AndroidRide',
style: TextStyle(
color: Colors.blue,
decoration: TextDecoration.underline,
fontWeight: FontWeight.bold,
fontSize: 21,
),
recognizer: TapGestureRecognizer()
..onTap = followLink,
),
]),
);
}),
),
SizedBox(
height: 20,
),
Link(
uri: Uri.parse('/second'),
builder: (context, followLink) {
return InkWell(
onTap: followLink,
child: Text(
'Go to Second Screen',
style: TextStyle(
fontSize: 20,
color: Colors.blue,
decoration: TextDecoration.underline,
),
),
);
},
),
Link
小部件是url_launcher
包的一部分,而不是Flutter本身。 - Chuck Batson因为一些用例过于复杂,所以我再提供一个更简单明了的小技巧。我使用了RichText - WidgetSpan、TextButton和URL launcher package来实现。只需根据您的需要修改下面的示例代码块。
代码:
class UserAgreementText extends StatelessWidget {
const UserAgreementText({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
text: 'By logging in, you accept our ',
style: Theme.of(context).textTheme.bodySmall,
children: const <InlineSpan>[
WidgetSpan(
alignment: PlaceholderAlignment.baseline,
baseline: TextBaseline.alphabetic,
child: LinkButton(
urlLabel: "Terms and Conditions",
url: "https://example.com/terms-and-conditions"),
),
TextSpan(
text: ' and ',
),
WidgetSpan(
alignment: PlaceholderAlignment.baseline,
baseline: TextBaseline.alphabetic,
child: LinkButton(
urlLabel: "Privacy Policy",
url: "https://example.com/privacy-policy"),
),
],
),
),
),
],
);
}
}
link_button.dart
class LinkButton extends StatelessWidget {
const LinkButton({Key? key, required this.urlLabel, required this.url})
: super(key: key);
final String urlLabel;
final String url;
Future<void> _launchUrl(String url) async {
final Uri uri = Uri.parse(url);
if (!await launchUrl(uri)) {
throw 'Could not launch $uri';
}
}
@override
Widget build(BuildContext context) {
return TextButton(
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(0),
),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: VisualDensity.compact,
minimumSize: const Size(0, 0),
textStyle: Theme.of(context).textTheme.bodySmall,
),
onPressed: () {
_launchUrl(url);
},
child: Text(urlLabel),
);
}
}
注意:如果出现与调用launchUrl
相关的错误,请确保安装了URL launcher包,然后重新构建您的应用程序。
flutter Intrinsics are not available for PlaceholderAlignment.baseline
,那么请从 WidgetSpan
中移除 alignment
和 baseline
。 - Chuck Batson final String _text = 'Lorem ipsum https://flutter.dev\nhttps://pub.dev';
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: LinkText(
text: _text,
textAlign: TextAlign.center,
),
),
);
}
flutter pub get
时,出现了“无法找到插件“url_launcher_windows”的vcxproj”错误。 - Eugene Gr. Philippov如果您想具备更高级的文本功能,可以使用flutter_html插件:
Html(
data: 'The last <i><u><b>book</b></u></i> bought is <a href="#">this</a>',
onLinkTap: (url, context, attrs, element) {
// Handle link tapped...
},
)
这只是插件功能的冰山一角。
仅供参考: 你甚至可以将你的Flutter应用程序的部分内容作为HTML托管到在线上,并使用此插件将其作为小部件呈现在Flutter中。
RichText
与 TextSpan
+ GestureRecognizer
,它们在功能上都是正确的,但从用户体验的角度来看,它们没有为响应触摸的用户提供反馈。为了保持与其他 Material 组件的一致性,您可以采用类似的方法,但改用 WidgetSpan
+ InkWell
。import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
class TextWithLink extends StatelessWidget {
const TextWithLink({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
const textStyle = TextStyle(color: Colors.black); // default style for all text
return RichText(
text: TextSpan(
style: textStyle,
children: [
const TextSpan(
text: 'The last book bought is ',
),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: InkWell(
onTap: () => _launchUrl('https://url-to-launch.com'),
child: Text('this',
style: textStyle.merge(const TextStyle(
color: Colors.blue, fontWeight: FontWeight.bold))))), // override default text styles with link-specific styles
],
),
);
}
_launchUrl(String url) async {
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
}