fix: Support URLs with hyphenated domain names - Updated URL regex pattern to include hyphens in domain names - Added test cases for hyphenated domains - Updated example app with hyphenated domain examples - Fixes #22
This commit is contained in:
parent
ea9faf279b
commit
43cb1b79cd
|
@ -3,24 +3,28 @@ import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:linkfy_text/linkfy_text.dart';
|
import 'package:linkfy_text/linkfy_text.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
runApp(MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
final k = GlobalKey<ScaffoldMessengerState>();
|
final k = GlobalKey<ScaffoldMessengerState>();
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
|
const MyApp({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: 'linkfy_text Demo',
|
title: 'linkfy_text Demo',
|
||||||
scaffoldMessengerKey: k,
|
scaffoldMessengerKey: k,
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
home: App(),
|
home: const App(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class App extends StatefulWidget {
|
class App extends StatefulWidget {
|
||||||
|
const App({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_AppState createState() => _AppState();
|
_AppState createState() => _AppState();
|
||||||
}
|
}
|
||||||
|
@ -31,7 +35,8 @@ class _AppState extends State<App> {
|
||||||
|
|
||||||
final List<Map<String, dynamic>> texts = [
|
final List<Map<String, dynamic>> texts = [
|
||||||
{
|
{
|
||||||
"text": "O1. This text contains a url: https://flutter.dev",
|
"text":
|
||||||
|
"O1. Testing hyphenated domains: https://my-website.com and http://sub-domain.example-site.com",
|
||||||
"types": null
|
"types": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -52,7 +57,7 @@ class _AppState extends State<App> {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text":
|
"text":
|
||||||
"O6. My website url: https://hello.com/GOOGLE search using: www.google.com, social media is facebook.com, additional link https://example.com/method?param=fullstackoverflow.dev, hashtag #trending & mention @dev.user +18009999999",
|
"O6. Testing complex URLs: https://my-complex-domain.com/path?param=value and https://sub-domain.my-site.com/test",
|
||||||
"types": [
|
"types": [
|
||||||
LinkType.phone,
|
LinkType.phone,
|
||||||
LinkType.email,
|
LinkType.email,
|
||||||
|
@ -66,7 +71,7 @@ class _AppState extends State<App> {
|
||||||
void showSnackbar(String msg) {
|
void showSnackbar(String msg) {
|
||||||
k.currentState!.removeCurrentSnackBar();
|
k.currentState!.removeCurrentSnackBar();
|
||||||
k.currentState!.showSnackBar(SnackBar(
|
k.currentState!.showSnackBar(SnackBar(
|
||||||
content: Text("$msg", style: textStyle),
|
content: Text(msg, style: textStyle),
|
||||||
behavior: SnackBarBehavior.floating,
|
behavior: SnackBarBehavior.floating,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -90,13 +95,13 @@ class _AppState extends State<App> {
|
||||||
),
|
),
|
||||||
for (var i = 0; i < texts.length; i++)
|
for (var i = 0; i < texts.length; i++)
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 14),
|
padding: const EdgeInsets.only(top: 14),
|
||||||
child: LinkifyText(
|
child: LinkifyText(
|
||||||
texts[i]['text'],
|
texts[i]['text'],
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
linkTypes: texts[i]['types'],
|
linkTypes: texts[i]['types'],
|
||||||
textStyle: textStyle,
|
textStyle: textStyle,
|
||||||
customLinkStyles: {
|
customLinkStyles: const {
|
||||||
LinkType.email: TextStyle(color: Colors.blue),
|
LinkType.email: TextStyle(color: Colors.blue),
|
||||||
LinkType.hashTag: TextStyle(color: Colors.green),
|
LinkType.hashTag: TextStyle(color: Colors.green),
|
||||||
LinkType.userTag: TextStyle(color: Colors.deepPurple),
|
LinkType.userTag: TextStyle(color: Colors.deepPurple),
|
||||||
|
|
|
@ -4,11 +4,13 @@ import 'package:linkfy_text/src/enum.dart';
|
||||||
|
|
||||||
// url regex that accept https, http, www
|
// url regex that accept https, http, www
|
||||||
String urlRegExp =
|
String urlRegExp =
|
||||||
r'[(http(s)?):\/\/(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)';
|
r'((https?://)?(www\.)?[a-zA-Z0-9@:%._\+~#=-]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*))';
|
||||||
|
|
||||||
String hashtagRegExp = r'#[a-zA-Z\u00C0-\u01B4\w_\u1EA0-\u1EF9!$%^&]{1,}(?=\s|$)';
|
String hashtagRegExp =
|
||||||
|
r'#[a-zA-Z\u00C0-\u01B4\w_\u1EA0-\u1EF9!$%^&]{1,}(?=\s|$)';
|
||||||
|
|
||||||
String userTagRegExp = r'@[a-zA-Z\u00C0-\u01B4\w_\u1EA0-\u1EF9!$%^&]{1,}(?=\s|$)';
|
String userTagRegExp =
|
||||||
|
r'@[a-zA-Z\u00C0-\u01B4\w_\u1EA0-\u1EF9!$%^&]{1,}(?=\s|$)';
|
||||||
String phoneRegExp =
|
String phoneRegExp =
|
||||||
r'\s*(?:\+?(\d{1,3}))?[-. (]*(\d{3})[-. )]*(\d{3})[-. ]*(\d{4})(?: *x(\d+))?\s*';
|
r'\s*(?:\+?(\d{1,3}))?[-. (]*(\d{3})[-. )]*(\d{3})[-. ]*(\d{4})(?: *x(\d+))?\s*';
|
||||||
String emailRegExp =
|
String emailRegExp =
|
||||||
|
@ -49,7 +51,6 @@ RegExp constructRegExpFromLinkType(List<LinkType> types) {
|
||||||
? buffer.write("($phoneRegExp)")
|
? buffer.write("($phoneRegExp)")
|
||||||
: buffer.write("($phoneRegExp)|");
|
: buffer.write("($phoneRegExp)|");
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return RegExp(buffer.toString());
|
return RegExp(buffer.toString());
|
||||||
|
@ -61,11 +62,11 @@ LinkType getMatchedType(String match) {
|
||||||
type = LinkType.email;
|
type = LinkType.email;
|
||||||
} else if (RegExp(urlRegExp).hasMatch(match)) {
|
} else if (RegExp(urlRegExp).hasMatch(match)) {
|
||||||
type = LinkType.url;
|
type = LinkType.url;
|
||||||
}else if (RegExp(phoneRegExp).hasMatch(match)) {
|
} else if (RegExp(phoneRegExp).hasMatch(match)) {
|
||||||
type = LinkType.phone;
|
type = LinkType.phone;
|
||||||
} else if (RegExp(userTagRegExp).hasMatch(match)) {
|
} else if (RegExp(userTagRegExp).hasMatch(match)) {
|
||||||
type = LinkType.userTag;
|
type = LinkType.userTag;
|
||||||
} else if (RegExp(hashtagRegExp).hasMatch(match)) {
|
} else if (RegExp(hashtagRegExp).hasMatch(match)) {
|
||||||
type = LinkType.hashTag;
|
type = LinkType.hashTag;
|
||||||
}
|
}
|
||||||
return type;
|
return type;
|
||||||
|
|
|
@ -29,6 +29,10 @@ void main() {
|
||||||
"https://domain.com/Google?param=",
|
"https://domain.com/Google?param=",
|
||||||
"https://domain.com/Google?param=helloworld",
|
"https://domain.com/Google?param=helloworld",
|
||||||
"https://sub.domain.com/Google?param=helloworld#hash",
|
"https://sub.domain.com/Google?param=helloworld#hash",
|
||||||
|
"https://my-domain.com",
|
||||||
|
"http://my-awesome-website.com",
|
||||||
|
"www.my-site-with-hyphens.com",
|
||||||
|
"https://sub-domain.example.com"
|
||||||
];
|
];
|
||||||
|
|
||||||
const hashtags = [
|
const hashtags = [
|
||||||
|
@ -67,7 +71,8 @@ void main() {
|
||||||
|
|
||||||
test("Should match all urls", () {
|
test("Should match all urls", () {
|
||||||
for (final url in urls) {
|
for (final url in urls) {
|
||||||
expect(RegExp(urlRegExp).hasMatch(url), isTrue);
|
expect(RegExp(urlRegExp).hasMatch(url), isTrue,
|
||||||
|
reason: "Failed to match URL: $url");
|
||||||
expect(getMatchedType(url), equals(LinkType.url));
|
expect(getMatchedType(url), equals(LinkType.url));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue