3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Flutter 正規表現を利用してリンクテキストを作る

Posted at

APIから文字列を取得してウィジェットへ表示したときにその文字列の中に**https://**が含まれていた場合、マッチしたテキスト部分のみタップイベント発火させてブラウザアプリケーションを起動してリンクさせたい:frowning2:

そこで!その処理をどこでも利用できるようなStatefulWidgetを作成してみました。

スクリーンショット 2020-09-15 20.09.59.png

利用したパッケージ

  • url_launcher
link_text_atoms.dart
import 'package:flutter/gestures.dart';
import 'package:url_launcher/url_launcher.dart';

class LinkTextAtoms extends StatefulWidget {
  LinkTextAtoms(this.text, {this.linkStyle, this.textStyle});

  final String text;
  final TextStyle linkStyle;
  final TextStyle textStyle;

  @override
  createState() => LinkTextAtomsState();
}

class LinkTextAtomsState extends State<LinkTextAtoms> {
  List<GestureRecognizer> recList = List<GestureRecognizer>();

  @override
  Widget build(BuildContext context) {
    // 通常のテキストデフォルトスタイル
    var text = Theme.of(context).textTheme.bodyText1.merge(widget.textStyle);
    // リンク時のテキストデフォルトスタイル
    var link = Theme.of(context)
        .textTheme
        .bodyText1
        .merge(TextStyle(
            inherit: true,
            color: Colors.blue,
            decoration: TextDecoration.underline))
        .merge(widget.linkStyle);

    List children = List<TextSpan>();
    List matches = RegExp(r'https?://[a-zA-Z0-9\-%_/=&?.]+')
        .allMatches(widget.text)
        .toList();
    var links = matches.map<TextSpan>((m) {
      var rec = TapGestureRecognizer()
        ..onTap = () async {
          // URL を処理できるアプリケーションが存在しない可能性があるためcanLaunchメソッドで処理可能かどうかチェックし、OK の場合のみlaunch()メソッドを呼ぶようにする
          if (await canLaunch(m.group(0))) {
            await launch(m.group(0), forceSafariVC: false);
          }
        };
      recList.add(rec);
      return TextSpan(text: m.group(0), style: link, recognizer: rec);
    }).toList();
    if (matches.length > 0) {
      for (var i = 0; i < matches.length; i++) {
        if (i == 0) {
          if (matches[i].start != 0) {
            children.add(TextSpan(
                text: widget.text.substring(0, matches[i].start), style: text));
          }
          children.add(links[i]);
          continue;
        }
        children.add(TextSpan(
            text: widget.text.substring(matches[i - 1].end, matches[i].start),
            style: text));
        children.add(links[i]);
      }
      if (matches.last.end != widget.text.length) {
        children.add(TextSpan(
            text: widget.text.substring(matches.last.end), style: text));
      }
    } else {
      children.add(TextSpan(text: widget.text, style: text));
    }
    return RichText(text: TextSpan(children: children));
  }

  @override
  dispose() {
    for (var i = recList.length - 1; i >= 0; i--) {
      recList.removeAt(i);
    }
    super.dispose();
  }
}

テキストの一部の**色を変えたい、リンクにしたい、イベント発火させたい、**ので、TextSpanウィジェットを利用し、通常テキストのデフォルトスタイルとリンクテキストだった場合のテキストスタイルを振り分けています。
リンクテキストだった場合はテキストの文字を青くし、アンダーラインを引いています。ここのスタイルはお好みに合わせて変更してしまってよいです。

TextSpanのrecognizerプロパティでタップを検出できるよう設定し、url_launcherパッケージで外部ブラウザを起動します。

...コード量長いし力技感いなめない。:frowning2:

利用方法

// ↑上で作成したファイルをimportする
import 'package:<プロジェクト名>/widgets/components/atoms/link_text_atoms.dart';

class SampleClass extends StatelessWidget {
  SampleClass({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
        child: Center(
            child: LinkTextAtoms(
                'このテキストはリンクですhttps://www.google.com/このテキストはリンクです')));
  }
}

関係ないけどproviderの作者がhooksパッケージ出してたの最近気が付きました。。
https://github.com/rrousselGit/flutter_hooks

新しい状態管理パッケージのRiverpodと併用してうまく使えていければと思います:v:
早くこのあたりのベストプラクティスが決まってくれると嬉しいなと思う今日この頃でございます:kissing_closed_eyes:
たまにFlutter(Dart)関係のことつぶやいていたりするので、@amitatantanをフォローしてくれると嬉しいです。

参考にしたdocument

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?