diff --git a/README.md b/README.md index ba4d6b5..da69ee3 100644 --- a/README.md +++ b/README.md @@ -27,41 +27,39 @@ Container( ) ``` -Be default, The above snippet would linkify all urls in the string, you can choose what type of link to linkify by passing the `linkTypes` parameter +Be default, The above snippet would linkify all urls in the string, you can choose what type of link to linkify by passing the `Links` parameter ```dart Container( child: LinkifyText( "This text contains a url: https://flutter.dev and #flutter", linkStyle: TextStyle(color: Colors.blue, fontSize: 16), - linkTypes: [LinkType.url, LinkType.hashtag] + Links: [Link.url, Link.hashtag] onTap: (link) { /// do stuff with `link` like - /// if(link.type == Linktype.url) launchUrl(link.value); + /// if(link.type == Link.url) launchUrl(link.value); }, ); ) ``` - ## API Reference #### LinkfyText -| Parameter | Type | Description | -| :-------- | :------- | :-------------------------------- | -| `textStyle` | `TextStyle` | style applied to the text | -| `linkStyle` | `TextStyle` | style applied to the linkified text. **defaults** to `textStyle` | -| `linkTypes` | `List` | a list of `LinkType` used to override the links to be linkified in a text either a url, hashtag or email. **defaults** to `[LinkType.url]`| -| `onTap` | `Function(Link)` | function called when a link is pressed | - +| Parameter | Type | Description | +| :---------- | :--------------- | :--------------------------------------------------------------------------------------------------------------------------------- | +| `textStyle` | `TextStyle` | style applied to the text | +| `linkStyle` | `TextStyle` | style applied to the linkified text. **defaults** to `textStyle` | +| `Links` | `List` | a list of `Link` used to override the links to be linkified in a text either a url, hashtag or email. **defaults** to `[Link.url]` | +| `onTap` | `Function(Link)` | function called when a link is pressed | #### Link -| Parameter | Type | Description | -| :-------- | :------- | :-------------------------------- | -| `type` | `LinkType` | the link type either url, email or hashtag | -| `value` | `String` | value this link holds | +| Parameter | Type | Description | +| :-------- | :------- | :----------------------------------------- | +| `type` | `Link` | the link type either url, email or hashtag | +| `value` | `String` | value this link holds | # Contributions @@ -72,5 +70,4 @@ If you fixed a bug or implemented a feature, please send a [pull request](https: # TODO -- linkify "@" mention and "$" sign - LinkifyTextField widget diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..8acb3fd --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,11 @@ +include: package:lint/analysis_options.yaml +analyzer: + errors: + missing_required_param: error + todo: info +linter: + rules: + always-specify-types: true + prefer-relative-imports: true + sort_pub_dependencies: false + unnecessary_raw_strings: false diff --git a/example/lib/main.dart b/example/lib/main.dart index 7d1fe9b..7aaa15b 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -12,7 +12,7 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - title: 'linkify_text Demo', + title: 'linkfy_text Demo', scaffoldMessengerKey: _msgKey, debugShowCheckedModeBanner: false, home: App(), diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 05a20bd..ee8e195 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -18,7 +18,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: '>=2.12.0 <3.0.0' + sdk: ">=2.12.0 <3.0.0" dependencies: flutter: @@ -26,7 +26,6 @@ dependencies: google_fonts: ^2.1.0 linkfy_text: path: ../ - # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. diff --git a/lib/linkfy_text.dart b/lib/linkfy_text.dart index 590b9f6..fc1251b 100644 --- a/lib/linkfy_text.dart +++ b/lib/linkfy_text.dart @@ -1,5 +1,5 @@ /// A lightweight flutter package to linkify texts containing urls, emails and hashtags. library linkfy_text; -export 'src/linkify.dart'; export 'src/enum.dart'; +export 'src/linkify.dart'; diff --git a/lib/src/linkify.dart b/lib/src/linkify.dart index 90bcd95..7668295 100644 --- a/lib/src/linkify.dart +++ b/lib/src/linkify.dart @@ -157,22 +157,22 @@ TextSpan _linkify({ List? linkTypes, Function(Link)? onTap, }) { - RegExp _regExp = constructRegExpFromLinkType(linkTypes ?? [LinkType.url]); + final _regExp = constructRegExpFromLinkType(linkTypes ?? [LinkType.url]); // return the full text if there's no match or if empty if (!_regExp.hasMatch(text) || text.isEmpty) return TextSpan(text: text); - List texts = text.split(_regExp); - List spans = []; - List links = _regExp.allMatches(text).toList(); + final texts = text.split(_regExp); + final List spans = []; + final links = _regExp.allMatches(text).toList(); - for (String text in texts) { + for (final text in texts) { spans.add(TextSpan( text: text, )); - if (links.length > 0) { - RegExpMatch match = links.removeAt(0); - Link link = Link.fromMatch(match); + if (links.isNotEmpty) { + final match = links.removeAt(0); + final link = Link.fromMatch(match); // add the link spans.add( TextSpan( diff --git a/lib/src/model/link.dart b/lib/src/model/link.dart index 4ecec54..8136401 100644 --- a/lib/src/model/link.dart +++ b/lib/src/model/link.dart @@ -2,13 +2,13 @@ import 'package:linkfy_text/src/enum.dart'; import 'package:linkfy_text/src/utils/regex.dart'; class Link { - String? _value; - LinkType? _type; + final String? _value; + final LinkType? _type; String? get value => _value; LinkType? get type => _type; - /// construct link from matched regExp + /// construct link from matched regExp Link.fromMatch(RegExpMatch match) - : this._type = getMatchedType(match), - this._value = match.input.substring(match.start, match.end); + : _type = getMatchedType(match), + _value = match.input.substring(match.start, match.end); } diff --git a/lib/src/utils/regex.dart b/lib/src/utils/regex.dart index b29550e..0640fd2 100644 --- a/lib/src/utils/regex.dart +++ b/lib/src/utils/regex.dart @@ -6,46 +6,55 @@ String hashtagRegExp = r'(#+[a-zA-Z0-9(_)]{1,})'; String userTagRegExp = r'(? linkTypes) { +/// construct regexp. pattern from provided link types +RegExp constructRegExpFromLinkType(List types) { // default case where we always want to match url strings - if (linkTypes.length == 1 && linkTypes.first == LinkType.url) return RegExp(urlRegExp); - - StringBuffer _regexBuffer = StringBuffer(); - for (var i = 0; i < linkTypes.length; i++) { - final _type = linkTypes[i]; - final _isLast = i == linkTypes.length - 1; - switch (_type) { + final len = types.length; + if (len == 1 && types.first == LinkType.url) { + return RegExp(urlRegExp); + } + final buffer = StringBuffer(); + for (var i = 0; i < len; i++) { + final type = types[i]; + final isLast = i == len - 1; + switch (type) { case LinkType.url: - _isLast ? _regexBuffer.write("($urlRegExp)") : _regexBuffer.write("($urlRegExp)|"); + isLast ? buffer.write("($urlRegExp)") : buffer.write("($urlRegExp)|"); break; case LinkType.hashTag: - _isLast ? _regexBuffer.write("($hashtagRegExp)") : _regexBuffer.write("($hashtagRegExp)|"); + isLast + ? buffer.write("($hashtagRegExp)") + : buffer.write("($hashtagRegExp)|"); break; case LinkType.userTag: - _isLast ? _regexBuffer.write("($userTagRegExp)") : _regexBuffer.write("($userTagRegExp)|"); + isLast + ? buffer.write("($userTagRegExp)") + : buffer.write("($userTagRegExp)|"); break; case LinkType.email: - _isLast ? _regexBuffer.write("($emailRegExp)") : _regexBuffer.write("($emailRegExp)|"); + isLast + ? buffer.write("($emailRegExp)") + : buffer.write("($emailRegExp)|"); break; default: } } - return RegExp(_regexBuffer.toString()); + return RegExp(buffer.toString()); } LinkType getMatchedType(RegExpMatch match) { - late LinkType _type; + late LinkType type; if (RegExp(urlRegExp).hasMatch(match.input)) { - _type = LinkType.url; + type = LinkType.url; } else if (RegExp(hashtagRegExp).hasMatch(match.input)) { - _type = LinkType.hashTag; + type = LinkType.hashTag; } else if (RegExp(emailRegExp).hasMatch(match.input)) { - _type = LinkType.email; + type = LinkType.email; } else if (RegExp(userTagRegExp).hasMatch(match.input)) { - _type = LinkType.userTag; + type = LinkType.userTag; } - return _type; + return type; } diff --git a/pubspec.lock b/pubspec.lock index 852caa8..cc9d950 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -151,6 +151,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.6.3" + lint: + dependency: "direct dev" + description: + name: lint + url: "https://pub.dartlang.org" + source: hosted + version: "1.5.3" logging: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 3d9adfd..9989fdc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,6 +15,7 @@ dev_dependencies: flutter_test: sdk: flutter test: ^1.16.5 + lint: ^1.5.3 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/test/utils/regex_test.dart b/test/utils/regex_test.dart index b44b0b4..55d8b89 100644 --- a/test/utils/regex_test.dart +++ b/test/utils/regex_test.dart @@ -4,17 +4,17 @@ import 'package:test/test.dart'; void main() { group('Regular Expression', () { - /// test values - final _urlText = + // test values + const urlText = "My website url: https://hello.com/GOOGLE search using: www.google.com, social media is facebook.com, http://example.com/method?param=fullstackoverflow.dev"; - final _hashtagText = "#helloWorld and #dev are trending"; - final _emailText = + const hashtagText = "#helloWorld and #dev are trending"; + const emailText = "My email address is hey@stanleee.me and dev@gmail.com, yah!"; - final _text = _urlText + _hashtagText + _emailText; + const text = urlText + hashtagText + emailText; - final _emails = ["hello@world.com", "foo.bar@js.com"]; + const emails = ["hello@world.com", "foo.bar@js.com"]; - final List _urls = [ + const urls = [ "http://domain.com", "http://domain.com/", "https://domain.com", @@ -28,7 +28,7 @@ void main() { "https://sub.domain.com/Google?param=helloworld#hash", ]; - final List _hashtags = [ + const hashtags = [ "#123", "#H", "#0", @@ -38,46 +38,51 @@ void main() { "#trending_topic", ]; + const userTags = [ + '@helloWorld', + '@helloWorld123', + '@hello_world', + '@hello_world123', + '@hello_world_123' + ]; + /// test("Should match all emails", () { - _emails.forEach((e) { - bool hasMatch = RegExp(emailRegExp).hasMatch(e); - expect(hasMatch, isTrue); - }); - }); - - test("Should not match all emails", () { - List _emails = ["hello@world", "@js.com"]; - _emails.forEach((e) { - bool hasMatch = RegExp(emailRegExp).hasMatch(e); - expect(hasMatch, isFalse); - }); + for (final email in emails) { + expect(RegExp(emailRegExp).hasMatch(email), isTrue); + } }); test("Should match all urls", () { - _urls.forEach((u) { - bool hasMatch = RegExp(urlRegExp).hasMatch(u); - expect(hasMatch, isTrue); - }); + for (final url in urls) { + expect(RegExp(urlRegExp).hasMatch(url), isTrue); + } }); test("Should match all hashtags", () { - _hashtags.forEach((h) { - bool hasMatch = RegExp(hashtagRegExp).hasMatch(h); - expect(hasMatch, isTrue); - }); + for (final tag in hashtags) { + expect(RegExp(hashtagRegExp).hasMatch(tag), isTrue); + } + }); + test("Should match all usertags", () { + for (final tag in userTags) { + expect(RegExp(userTagRegExp).hasMatch(tag), isTrue); + } }); - test("Should construct regex pattern from LinkTypes and match length", () { - RegExp _urlRegExp = constructRegExpFromLinkType([LinkType.url]); - RegExp _hashtagRegExp = constructRegExpFromLinkType([LinkType.hashTag]); - RegExp _emailRegExp = constructRegExpFromLinkType([LinkType.email]); - RegExp _textRegExp = constructRegExpFromLinkType( + test( + "Should construct regex pattern from LinkTypes and match required output", + () { + final urlRegExp = constructRegExpFromLinkType([LinkType.url]); + final hashtagRegExp = constructRegExpFromLinkType([LinkType.hashTag]); + final emailRegExp = constructRegExpFromLinkType([LinkType.email]); + final textRegExp = constructRegExpFromLinkType( [LinkType.url, LinkType.hashTag, LinkType.email]); - expect(_urlRegExp.allMatches(_urlText).length, 4); - expect(_hashtagRegExp.allMatches(_hashtagText).length, 2); - expect(_emailRegExp.allMatches(_emailText).length, 2); - expect(_textRegExp.allMatches(_text).length, 8); + + expect(urlRegExp.allMatches(urlText).length, 4); + expect(hashtagRegExp.allMatches(hashtagText).length, 2); + expect(emailRegExp.allMatches(emailText).length, 2); + expect(textRegExp.allMatches(text).length, 8); }); }); }