繁体   English   中英

如何在 Flutter 小部件中创建超链接?

[英]How to create a hyperlink in Flutter widget?

我想创建一个超链接以显示在我的 Flutter 应用程序中。

超链接应嵌入Text或类似的文本视图中,例如:

The last book bought is <a href='#'>this</a>

有什么提示吗?

只需在 Text 小部件周围包裹一个 InkWell,并为 onTap 属性提供一个 UrlLauncher(来自服务库)。 在下面使用之前,将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('UrlLauchner'),
        ),
        body: new Center(
          child: new InkWell(
              child: new Text('Open Browser'),
              onTap: () => launch('https://docs.flutter.io/flutter/services/UrlLauncher-class.html')
          ),
        ),
      ),
    );
  }
}

您可以为 Text 小部件提供样式,使其看起来像一个链接。

更新

在稍微调查了这个问题后,我找到了一个不同的解决方案来实现您要求的“内嵌”超链接。 您可以将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');
                  },
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

通过这种方式,您实际上可以突出显示一个单词并从中创建一个超链接;)

Flutter 没有内置的超链接支持,但你可以自己伪造它。 Gallery 的 drawer.dart 中有一个例子。 他们使用包含彩色TextSpanRichText小部件,该小部件具有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,
              ),
            ],
          ),

超链接 浏览器

您可以将您的Text包装在GestureDetector并处理onTap()点击。

GestureDetector(
  child: Text("Click here", style: TextStyle(decoration: TextDecoration.underline, color: Colors.blue)),
  onTap: () {
    // do what you need to do when "Click here" gets clicked
  }
)

在此处输入图片说明

您可以使用包 flutter_linkify
https://pub.dev/packages/flutter_linkify
只是想提供另一种选择。
包将分割您的文本并自动突出显示 http/https
结合插件 url_launcher 你可以启动 url
您可以查看以下示例:

在此处输入图片说明

完整代码如下

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

如果你想让它看起来更像一个链接,你可以添加下划线:

new Text("Hello Flutter!", style: new TextStyle(color: Colors.blue, decoration: TextDecoration.underline),)

结果:

在此处输入图片说明

您可以使用链接文本https://pub.dev/packages/link_text并像这样使用它

 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 2.0 中,引入了 Link 小部件。 使用此小部件可启动网页并导航到应用程序中的新屏幕。 在使用之前,您需要使用 url_launcher 包。

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

如果您想拥有更高级的文本功能,可以使用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 中。

在您的应用程序中放置可点击链接的另一种(或不)方法(对我来说它就是这样工作的):

1 - 在 pubspec.yaml 文件中添加 url_launcher 包

(包版本 5.0 对我来说效果不佳,所以我使用的是 4.2.0+3)。

dependencies:
  flutter:
    sdk: flutter
  url_launcher: ^4.2.0+3

2 - 导入并使用如下。

import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';

void main() {
  runApp(MaterialApp(
    title: 'Navigation Basics',
    home: MyUrl(),
  ));
}

class MyUrl extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Url Launcher'),
      ),
      body: Center(
        child: FlatButton(
          onPressed: _launchURL,
          child: Text('Launch Google!',
              style: TextStyle(fontSize: 17.0)),
        ),
      ),
    );
  }

  _launchURL() async {
    const url = 'https://google.com.br';
    if (await canLaunch(url)) {
      await launch(url);
    } else {
      throw 'Could not launch $url';
    }
  }
}

这是一个交钥匙 package https://pub.dev/packages/link_text

child: LinkText(
  'Hello check http://google.com',
  textStyle: ...,
),

为这种复杂性使用库更安全

优点:

  • 将整个字符串作为单个参数
  • 解析链接并将它们呈现到 UI 中作为可点击的,与不可点击的文本内联
  • 不需要复杂的 RichText 结构

为此,您可以使用Link,Link_Text,linkify等软件包。

只需在pub.dev软件包中搜索链接 ,您将获得优质的软件包,可以轻松添加链接。

这是来自link_text包的图片

建议将RichText<\/code>与TextSpan<\/code> + GestureRecognizer<\/code>一起使用的问题的答案在功能上都是正确的,但从 UX 的角度来看,它们不会向响应触摸的用户提供反馈。 为了保持与其他 Material 小部件的一致性,您可以使用类似的方法,但改用WidgetSpan<\/code> + InkWell<\/code> 。

此示例使用url_launcher<\/a>包和无样式的 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';
    }
  }
}

添加一个更简单整洁的技巧,因为上述技巧对于某些用例来说过于复杂。 我使用 RichText - WidgetSpan、TextButton 和 URL 启动器 package 完成了它。 只需根据您的需要修改以下示例块。

结果: 结果

代码:

class UserAgreementText extends StatelessWidget {
  const UserAgreementText({Key? key}) : super(key: key);

  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 Row(
      children: [
        Expanded(
          child: RichText(
            textAlign: TextAlign.center,
            text: TextSpan(
              text: 'By logging in, you accept our ',
              style: Theme.of(context).textTheme.bodySmall,
              children: <InlineSpan>[
                WidgetSpan(
                  alignment: PlaceholderAlignment.baseline,
                  baseline: TextBaseline.alphabetic,
                  child: 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(
                          "https://example.com/terms-and-conditions");
                    },
                    child: const Text("Terms and Conditions"),
                  ),
                ),
                const TextSpan(
                  text: ' and ',
                ),
                WidgetSpan(
                  alignment: PlaceholderAlignment.baseline,
                  baseline: TextBaseline.alphabetic,
                  child: 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(
                          "https://example.com/privacy-policy");
                    },
                    child: const Text("Privacy Policy"),
                  ),
                ),
              ],
            ),
          ),
        ),
      ],
    );
  }
}

这可能会晚了; 但是我发现了这个包裹

https://github.com/Sub6Resources/flutter_html

结合网址启动器https://pub.dev/packages/url_launcher

可以在水龙头上为我们提供很棒的html链接,例如widget(包括html href)

Html(
  data: """
    <!--For a much more extensive example, look at example/main.dart-->
    <div>
      <h1>Demo Page</h1>
      <p>This is a fantastic nonexistent product that you should buy!</p>
      <h2>Pricing</h2>
      <p>Lorem ipsum <b>dolor</b> sit amet.</p>
      <h2>The Team</h2>
      <p>There isn't <i>really</i> a team...</p>
      <h2>Installation</h2>
      <p>You <u>cannot</u> install a nonexistent product!</p>
      <!--You can pretty much put any html in here!-->
    </div>
  """,
  //Optional parameters:
  padding: EdgeInsets.all(8.0),
  backgroundColor: Colors.white70,
  defaultTextStyle: TextStyle(fontFamily: 'serif'),
  linkStyle: const TextStyle(
    color: Colors.redAccent,
  ),
  onLinkTap: (url) {
    // open url in a webview
  },
  onImageTap: (src) {
    // Display the image in large form.
  },
  //Must have useRichText set to false for this to work.
  customRender: (node, children) {
    if(node is dom.Element) {
      switch(node.localName) {
        case "video": return Chewie(...);
        case "custom_tag": return CustomWidget(...);
      }
    }
  },
  customTextAlign: (dom.Node node) {
    if (node is dom.Element) {
      switch (node.localName) {
        case "p":
          return TextAlign.justify;
      }
    }
  },
  customTextStyle: (dom.Node node, TextStyle baseStyle) {
    if (node is dom.Element) {
      switch (node.localName) {
        case "p":
          return baseStyle.merge(TextStyle(height: 2, fontSize: 20));
      }
    }
    return baseStyle;
  },
)

所有学分都归授给导师。

干杯。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM