twonly-app/lib/src/visual/elements/better_text.element.dart
otsmr 8505ed865a
Some checks are pending
Flutter analyze & test / flutter_analyze_and_test (push) Waiting to run
fix url parsing
2026-08-05 21:47:26 +02:00

78 lines
2.1 KiB
Dart

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:url_launcher/url_launcher.dart';
class BetterText extends StatelessWidget {
const BetterText({required this.text, required this.textColor, super.key});
final String text;
final Color textColor;
@override
Widget build(BuildContext context) {
// Regular expression to find URLs and domains
final urlRegExp = RegExp(
r'''(?:(?:https?://|www\.)(?:[^\s()<>]+|\([^\s()<>]+\))+(?:\([^\s()<>]+\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))|(?:(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,})''',
caseSensitive: false,
);
final spans = <TextSpan>[];
final matches = urlRegExp.allMatches(text);
var lastMatchEnd = 0;
for (final match in matches) {
if (match.start > lastMatchEnd) {
spans.add(TextSpan(text: text.substring(lastMatchEnd, match.start)));
}
final url = match.group(0);
spans.add(
TextSpan(
text: url,
style: const TextStyle(
decoration: TextDecoration.underline,
decorationColor: Colors.white,
),
recognizer: TapGestureRecognizer()
..onTap = () async {
final lUrl = Uri.parse(
url!.startsWith('http') ? url : 'http://$url',
);
try {
await launchUrl(lUrl, mode: LaunchMode.externalApplication);
} catch (e) {
Log.error('Could not launch $e');
}
},
),
);
lastMatchEnd = match.end;
}
if (lastMatchEnd < text.length) {
spans.add(
TextSpan(
text: text.substring(lastMatchEnd),
),
);
}
return Text.rich(
TextSpan(
children: spans,
),
softWrap: true,
textAlign: TextAlign.start,
overflow: TextOverflow.visible,
style: TextStyle(
color: textColor,
fontSize: 17,
decoration: TextDecoration.none,
fontWeight: FontWeight.normal,
),
);
}
}