簡體   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