👻

【Flutter】文字列内のURLをタップできるようにする

2022/11/30に公開

みなさんこんにちは。

Flutterで文字列内のURLをタップできるようにする際、RichTextを使用するかと思います。
コード内で文字列を定義できる場合は直にURLを記載すれば良いですが、DBから取得した(動的な)文字列内にあるURLをタップできるようにするには、別途実装が必要な場合もあります。

今回は、私が開発の中で自前で作成したものをコードベースで共有しています。
「一つの文字列内に同じURLが複数含まれている」場合にも、それぞれがタップできるようになる実装となっております。

text_with_url.dart
class TextWithUrl extends StatelessWidget {
  const TextWithUrl({
    required this.text,
    Key? key,
  }) : super(key: key);

  final String text;

  @override
  Widget build(BuildContext context) {
    /// URL検知の正規表現で、テキストがURLを含むか確認
    final urlRegExp = RegExp(
        r'((https?:www\.)|(https?:\/\/)|(www\.))[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9]{1,6}(\/[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)?');
    final urlMatches = urlRegExp.allMatches(text);

    /// URLが含まれていない場合はそのままのText, 含まれている場合はRichTextを返す
    if (urlMatches.isEmpty) {
      return SelectableText(
        text,
        style: const TextStyle(
          fontSize: 16,
        ),
      );
    } else {
      /// 返り値としてのTextSpanのリスト
      final textSpanList = <TextSpan>[];
      var remainingText = text;

      for (final regExpMatch in urlMatches) {
        final url = text.substring(regExpMatch.start, regExpMatch.end);
        final index = remainingText.indexOf(url);

        /// 文字列の初めがURLでない場合は通常のテキストとして生成し、文字列を分割
        if (index != 0) {
          textSpanList.add(
              CustomTextSpan.normalTextSpan(remainingText.substring(0, index)));
          remainingText = remainingText.substring(index);
        }

        /// URL文字列をハイパーリンクとして生成、文字列を分割して次のループに入る
        textSpanList.add(
            CustomTextSpan.urlTextSpan(remainingText.substring(0, url.length)));
        remainingText = remainingText.substring(url.length);
      }

      /// 文字列の最後がURLでない場合はremainingTextに残るので、通常のテキストとして生成
      if (remainingText.isNotEmpty) {
        textSpanList.add(CustomTextSpan.normalTextSpan(remainingText));
      }

      return SelectableText.rich(
        TextSpan(
          children: textSpanList,
        ),
      );
    }
  }
}
上記コード内で出てくるTextSpanです。(ここら辺はお好みで)
custom_text_span.dart
class CustomTextSpan {
  const CustomTextSpan({
    required this.text,
  });

  final String text;
  static double fontSize = 14;
  static Color color = const Color(0xff111111);

  static TextSpan urlTextSpan(String text) {
    return TextSpan(
      text: text,
      style: TextStyle(
        fontSize: fontSize,
        color: color,
        decoration: TextDecoration.underline,
      ),
      recognizer: TapGestureRecognizer()
        ..onTap = () {
          launchUrl(Uri.parse(text));
        },
    );
  }

  static TextSpan normalTextSpan(String text) {
    return TextSpan(
      text: text,
      style: TextStyle(
        fontSize: fontSize,
        color: color,
      ),
    );
  }
}

使用例

  TextWithUrl(text: text), // text:取得した文字列

必要に応じて使ってもらえればと思います!

Discussion