From edce21cbff294b57a5f085fbcd431b293313f1ab Mon Sep 17 00:00:00 2001 From: Keith Black Date: Fri, 17 Sep 2021 13:06:11 -0500 Subject: [PATCH 1/9] Added the ability to pass in a regEx string to override the built in regEx string used to detect emails and urls. --- lib/linkwell.dart | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/linkwell.dart b/lib/linkwell.dart index 6418122..3387580 100644 --- a/lib/linkwell.dart +++ b/lib/linkwell.dart @@ -19,7 +19,7 @@ import 'package:url_launcher/url_launcher.dart'; /// LinkWell class created class LinkWell extends StatelessWidget { /// The RegEx pattern is created - final RegExp exp = Helper.regex; + final String? regEx; /// This is holds all links when detected final List links = []; @@ -104,6 +104,7 @@ class LinkWell extends StatelessWidget { /// LinkWell class is constructed here LinkWell( this.text, { + this.regEx, this.key, this.style, this.linkStyle, @@ -117,22 +118,19 @@ class LinkWell extends StatelessWidget { this.strutStyle, this.listOfNames, this.textWidthBasis = TextWidthBasis.parent, - }) : assert(text != null), - assert(textAlign != null), - assert(softWrap != null), - assert(overflow != null), - assert(textScaleFactor != null), - assert(maxLines == null || maxLines > 0), - assert(textWidthBasis != null) { + }) : assert(maxLines == null || maxLines > 0) { /// At construction _initialize function is called _initialize(); } /// _initialize function _initialize() { + // Use passed in regular expression string if provided, otherwise default + var exp = regEx != null ? RegExp(regEx!) : Helper.regex; + /// An Iterable with variable name matches /// Is assigned to our regular expression with - /// allMatched method call + /// allMatched method cal Iterable matches = exp.allMatches(this.text); /// We now run a forEach Loop to add our matche to @@ -229,7 +227,9 @@ class LinkWell extends StatelessWidget { var l = value.toString().contains('https://') ? value - : value.toString().contains('http://') ? value : 'http://' + value; + : value.toString().contains('http://') + ? value + : 'http://' + value; var name = l; if (this.listOfNames != null) { From 3d1066b620510bea51b2c3197734f4b3ef0f47e7 Mon Sep 17 00:00:00 2001 From: Keith Black Date: Mon, 11 Oct 2021 19:31:42 -0500 Subject: [PATCH 2/9] Updated regex string to better detect urls and email addresses. The previous regex string would flag numbers like 60.99 as a url, and it would flag any word with a dot like good.bye or anything with two dots like hello..there as urls. This new version does not flag these false positives, however, it will only catch emails and urls with the following extensions. .com .ru .org .gov .net .ir .in .uk .au .de .ua .edu .io .co --- lib/src/source.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/source.dart b/lib/src/source.dart index 38f8668..9b16b7f 100644 --- a/lib/src/source.dart +++ b/lib/src/source.dart @@ -5,7 +5,7 @@ class Helper { const Helper(this.value); static var regex = new RegExp( - r"((https?:www\.)|(https?:\/\/)|(www\.))?[\w/\-?=%.][-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9]{1,6}(\/[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)?"); + r"((https?:www\.)|(https?:\/\/)|(www\.))?[\w\-?=%.][-a-zA-Z@:%._\+~#=]{1,256}\.+((com|ru|org|gov|net|ir|in|uk|au|de|ua|edu|io|co))(\/[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)?"); static var phoneRegex = new RegExp(r"(^(?:[+0]9)?[0-9]{10,12}$)"); From 0c5fb3932dfd87f6e38bf85470bf363e59d9374a Mon Sep 17 00:00:00 2001 From: Keith Black Date: Wed, 15 Dec 2021 18:06:23 -0600 Subject: [PATCH 3/9] Added new parameter 'clickHandler' that gets called before the standard launch processing. If the method exists and returns true then LinkWell will not call the standard launch. --- lib/linkwell.dart | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/linkwell.dart b/lib/linkwell.dart index 3387580..879ddcc 100644 --- a/lib/linkwell.dart +++ b/lib/linkwell.dart @@ -16,6 +16,8 @@ import 'package:linkwell/src/source.dart'; /// it help lauches the links and emails when user taps import 'package:url_launcher/url_launcher.dart'; +typedef LinkWellClickHandler = bool Function(String url); + /// LinkWell class created class LinkWell extends StatelessWidget { /// The RegEx pattern is created @@ -97,6 +99,13 @@ class LinkWell extends StatelessWidget { /// by default can also be null final TextDirection? textDirection; + /// This optional parameter gives the caller the opportunity to evaluate + /// the link prior to LinkWell calling the default click handling + /// clickHandler should return 'true' if it processes the click, + /// otherwise, if it returns 'false' then LinkWell will call the + /// built-in click handler. + final LinkWellClickHandler? clickHandler; + /// This hold user defined Widget key /// by default can also be null final Key? key; @@ -117,6 +126,7 @@ class LinkWell extends StatelessWidget { this.locale, this.strutStyle, this.listOfNames, + this.clickHandler, this.textWidthBasis = TextWidthBasis.parent, }) : assert(maxLines == null || maxLines > 0) { /// At construction _initialize function is called @@ -218,7 +228,7 @@ class LinkWell extends StatelessWidget { var link = TextSpan( text: name, style: linkStyle == null ? Helper.linkDefaultTextStyle : linkStyle, - recognizer: new TapGestureRecognizer()..onTap = () => launch(url)); + recognizer: new TapGestureRecognizer()..onTap = () => _launch(url)); /// added textSpanWidget.add(link); @@ -244,7 +254,7 @@ class LinkWell extends StatelessWidget { var link = TextSpan( text: name, style: linkStyle == null ? Helper.linkDefaultTextStyle : linkStyle, - recognizer: new TapGestureRecognizer()..onTap = () => launch(l)); + recognizer: new TapGestureRecognizer()..onTap = () => _launch(l)); /// added textSpanWidget.add(link); @@ -266,6 +276,14 @@ class LinkWell extends StatelessWidget { }); } + void _launch(url) { + var handled = false; + if (clickHandler != null) { + handled = clickHandler!(url); + } + if (!handled) launch(url); + } + @override Widget build(BuildContext context) { return Container( From edfbbb8ff99be885ab6ab9aeb2a1a8659a668b34 Mon Sep 17 00:00:00 2001 From: Keith Black Date: Tue, 29 Aug 2023 14:07:18 -0500 Subject: [PATCH 4/9] Added support to handle multiple regex strings. This was necessary because a single regex string could not effectively detect every type of link (i.e. email, url, phone) --- example/ios/Flutter/Debug.xcconfig | 1 + example/ios/Flutter/Release.xcconfig | 1 + example/lib/main.dart | 32 +++---- example/pubspec.lock | 137 +++++++++++++++------------ lib/linkwell.dart | 83 ++++++++++++---- lib/src/source.dart | 13 ++- pubspec.lock | 132 +++++++++++++++----------- pubspec.yaml | 2 - 8 files changed, 239 insertions(+), 162 deletions(-) diff --git a/example/ios/Flutter/Debug.xcconfig b/example/ios/Flutter/Debug.xcconfig index 592ceee..ec97fc6 100644 --- a/example/ios/Flutter/Debug.xcconfig +++ b/example/ios/Flutter/Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/example/ios/Flutter/Release.xcconfig b/example/ios/Flutter/Release.xcconfig index 592ceee..c4855bf 100644 --- a/example/ios/Flutter/Release.xcconfig +++ b/example/ios/Flutter/Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/example/lib/main.dart b/example/lib/main.dart index c5eebb3..38da973 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,12 +1,9 @@ - -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:linkwell/linkwell.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { - @override Widget build(BuildContext context) { return MaterialApp( @@ -28,34 +25,27 @@ class MyHomePage extends StatefulWidget { } class _MyHomePageState extends State { - - @override Widget build(BuildContext context) { - return Scaffold( appBar: AppBar( title: Text(widget.title), ), - body: Container( - child: Center( + child: Center( child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - - SizedBox( - height: 10, - ), - LinkWell( - "Hi here's my email: samuelezedi@gmail.com and website: https://pronoun.com.ng", - ) - ], + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + height: 10, ), + LinkWell( + "Hi here's my email: samuelezedi@gmail.com and website: https://pronoun.com.ng", + ) + ], + ), ), - - ), ); } diff --git a/example/pubspec.lock b/example/pubspec.lock index 85ae66a..43c94ac 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,58 +5,58 @@ packages: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.10.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" characters: dependency: transitive description: name: characters - url: "https://pub.dartlang.org" + sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + url: "https://pub.dev" source: hosted - version: "1.1.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" + version: "1.2.1" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.17.0" cupertino_icons: dependency: "direct main" description: name: cupertino_icons - url: "https://pub.dartlang.org" + sha256: a937da4c006989739ceb4d10e3bd6cce64ca85d0fe287fc5b2b9f6ee757dcee6 + url: "https://pub.dev" source: hosted version: "0.1.3" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.1" flutter: dependency: "direct main" description: flutter @@ -76,44 +76,57 @@ packages: dependency: transitive description: name: js - url: "https://pub.dartlang.org" + sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + url: "https://pub.dev" source: hosted - version: "0.6.3" + version: "0.6.5" linkwell: dependency: "direct main" description: path: ".." relative: true source: path - version: "0.1.9" + version: "2.0.6" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + url: "https://pub.dev" source: hosted - version: "0.12.10" + version: "0.12.13" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" + source: hosted + version: "0.2.0" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.8.0" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.8.2" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - url: "https://pub.dartlang.org" + sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd" + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.1.5" sky_engine: dependency: transitive description: flutter @@ -123,100 +136,106 @@ packages: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.19" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.dartlang.org" + sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "0.4.16" url_launcher: dependency: transitive description: name: url_launcher - url: "https://pub.dartlang.org" + sha256: "704b2bd47392940d1ab71aa507b72f71989a255b2cb188cf3fff141b5921f364" + url: "https://pub.dev" source: hosted version: "6.0.3" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - url: "https://pub.dartlang.org" + sha256: "86f3f393cde6bed2a05bfc7f05e52aeaf4f9911a3ad9ff78a42e89e57e5a264a" + url: "https://pub.dev" source: hosted version: "2.0.0" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - url: "https://pub.dartlang.org" + sha256: f72b523da791d519aed53c12fd99c7dc50fdd1e4913da904081f3666d06334b5 + url: "https://pub.dev" source: hosted version: "2.0.0" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - url: "https://pub.dartlang.org" + sha256: "9589b5fea61ae62177d687098edde3adcc808ea976b2b96a8267563d681d75f1" + url: "https://pub.dev" source: hosted version: "2.0.2" url_launcher_web: dependency: transitive description: name: url_launcher_web - url: "https://pub.dartlang.org" + sha256: "057e3458dfcc4276d171ae70cd98efc6d2485bf39b93a015349754e5dec0f657" + url: "https://pub.dev" source: hosted version: "2.0.0" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - url: "https://pub.dartlang.org" + sha256: f98b970a12236957881fa28df0c6700f03d9dc5f471cf5ca6d205b77e7ad92d2 + url: "https://pub.dev" source: hosted version: "2.0.0" vector_math: dependency: transitive description: name: vector_math - url: "https://pub.dartlang.org" + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.4" sdks: - dart: ">=2.12.0 <3.0.0" + dart: ">=2.18.0 <3.0.0" flutter: ">=1.22.0" diff --git a/lib/linkwell.dart b/lib/linkwell.dart index 879ddcc..a2c5858 100644 --- a/lib/linkwell.dart +++ b/lib/linkwell.dart @@ -11,6 +11,7 @@ library linkwell; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:linkwell/src/source.dart'; +// import 'package:cm_utils/string_utils.dart'; /// LinkWell depends on url_launcher plugin /// it help lauches the links and emails when user taps @@ -23,8 +24,18 @@ class LinkWell extends StatelessWidget { /// The RegEx pattern is created final String? regEx; - /// This is holds all links when detected - final List links = []; + // An array of RegEx strings matching different patterms that should + // be considered as links. + final List? regExLinkPatterns; + + /// This is holds all links found in the text. + /// The format is jason: + /// {link: 'examplelink.com', location: 20 }; + /// + /// 'link' is the detected link which could be email, url, etc. + /// 'location' is the location in the string where the link begins. + /// + final List> links = []; /// This hold all Names of links provided by the User /// this is set to null by default @@ -114,6 +125,7 @@ class LinkWell extends StatelessWidget { LinkWell( this.text, { this.regEx, + this.regExLinkPatterns, this.key, this.style, this.linkStyle, @@ -135,18 +147,30 @@ class LinkWell extends StatelessWidget { /// _initialize function _initialize() { - // Use passed in regular expression string if provided, otherwise default - var exp = regEx != null ? RegExp(regEx!) : Helper.regex; - - /// An Iterable with variable name matches - /// Is assigned to our regular expression with - /// allMatched method cal - Iterable matches = exp.allMatches(this.text); - - /// We now run a forEach Loop to add our matche to - /// the links List - matches.forEach((match) { - this.links.add(text.substring(match.start, match.end)); + // Use passed in regular expression strings if provided, otherwise use default + var regexStrings = regExLinkPatterns ?? Helper.defaultLinkRegexStr; + + regexStrings.forEach((regexStr) { + /// An Iterable with variable name matches + /// Is assigned to our regular expression with + /// allMatched method cal + var exp = RegExp(regexStr); + Iterable matches = exp.allMatches(this.text); + + /// We now run a forEach Loop to add our match to + /// the links List + matches.forEach((match) { + this.links.add({ + 'link': text.substring(match.start, match.end), + 'location': match.start + }); + }); + }); + + // sort links by order found in the text string. Necessary so substitution + // works correctly. + links.sort((item1, item2) { + return item1['location'] - item2['location']; }); /// We run a check to know if urls and Emails are found @@ -184,9 +208,10 @@ class LinkWell extends StatelessWidget { var t = this.text; /// a foreach is run on all the links found - this.links.forEach((value) async { - /// var wid which represents widget + this.links.forEach((linkMap) async { + var value = linkMap['link'] as String? ?? ''; + /// var wid which represents widget var wid = t.split(value.trim()); /// if not value is found after splitting @@ -214,7 +239,7 @@ class LinkWell extends StatelessWidget { String url = params.toString(); - var name = value; + String? name = value; if (this.listOfNames != null) { if (this.listOfNames!.containsKey(value)) { @@ -232,6 +257,18 @@ class LinkWell extends StatelessWidget { /// added textSpanWidget.add(link); + + // TODO: Uncomment here to allow clicking on and calling a phone number. + // Must be implemented in custom clickHandler. + // + // } else if (value.isValidPhoneNumber()) { + // var link = TextSpan( + // text: value, + // style: linkStyle == null ? Helper.linkDefaultTextStyle : linkStyle, + // recognizer: new TapGestureRecognizer() + // ..onTap = () => _launch('tel:$value')); + + // textSpanWidget.add(link); } else { /// else we let url_laucher know that this is url and not an email @@ -240,7 +277,7 @@ class LinkWell extends StatelessWidget { : value.toString().contains('http://') ? value : 'http://' + value; - var name = l; + String? name = l; if (this.listOfNames != null) { if (this.listOfNames!.containsKey(value)) { @@ -261,7 +298,7 @@ class LinkWell extends StatelessWidget { } if (wid[1] != '') { - if (value == links.last) { + if (value == links.last['link']) { var text = TextSpan( text: wid[1], style: style == null ? Helper.defaultTextStyle : style, @@ -281,7 +318,13 @@ class LinkWell extends StatelessWidget { if (clickHandler != null) { handled = clickHandler!(url); } - if (!handled) launch(url); + if (!handled) { + try { + launch(url); + } catch (err) { + print('Error launching URL: $url. Error = $err'); + } + } } @override diff --git a/lib/src/source.dart b/lib/src/source.dart index 9b16b7f..ca05ff9 100644 --- a/lib/src/source.dart +++ b/lib/src/source.dart @@ -1,11 +1,18 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; class Helper { const Helper(this.value); - static var regex = new RegExp( - r"((https?:www\.)|(https?:\/\/)|(www\.))?[\w\-?=%.][-a-zA-Z@:%._\+~#=]{1,256}\.+((com|ru|org|gov|net|ir|in|uk|au|de|ua|edu|io|co))(\/[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)?"); + static List defaultLinkRegexStr = [ + // This string will detect url's + r'((http|ftp|https):\/\/|www\.)([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])', + + // This string will detect email addresses + r'([a-zA-Z0-9+._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)' + + // TODO: Uncomment here to allow clicking on and calling a phone number. + // '(?:\\+?(\\d{1,3}))?[-.(]*(\\d{3})[-. )]*(\\d{3})[-. ]*(\\d{4})(?: *x(\\d+))?' + ]; static var phoneRegex = new RegExp(r"(^(?:[+0]9)?[0-9]{10,12}$)"); diff --git a/pubspec.lock b/pubspec.lock index e5dfe2c..253d2cb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,51 +5,50 @@ packages: dependency: transitive description: name: async - url: "https://pub.dartlang.org" + sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.10.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://pub.dartlang.org" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" characters: dependency: transitive description: name: characters - url: "https://pub.dartlang.org" + sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + url: "https://pub.dev" source: hosted - version: "1.1.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" + version: "1.2.1" clock: dependency: transitive description: name: clock - url: "https://pub.dartlang.org" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" collection: dependency: transitive description: name: collection - url: "https://pub.dartlang.org" + sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.17.0" fake_async: dependency: transitive description: name: fake_async - url: "https://pub.dartlang.org" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.1" flutter: dependency: "direct main" description: flutter @@ -69,37 +68,50 @@ packages: dependency: transitive description: name: js - url: "https://pub.dartlang.org" + sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + url: "https://pub.dev" source: hosted - version: "0.6.3" + version: "0.6.5" matcher: dependency: transitive description: name: matcher - url: "https://pub.dartlang.org" + sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + url: "https://pub.dev" source: hosted - version: "0.12.10" + version: "0.12.13" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" + source: hosted + version: "0.2.0" meta: dependency: transitive description: name: meta - url: "https://pub.dartlang.org" + sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.8.0" path: dependency: transitive description: name: path - url: "https://pub.dartlang.org" + sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.8.2" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - url: "https://pub.dartlang.org" + sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd" + url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.1.5" sky_engine: dependency: transitive description: flutter @@ -109,100 +121,106 @@ packages: dependency: transitive description: name: source_span - url: "https://pub.dartlang.org" + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" stack_trace: dependency: transitive description: name: stack_trace - url: "https://pub.dartlang.org" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - url: "https://pub.dartlang.org" + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner - url: "https://pub.dartlang.org" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - url: "https://pub.dartlang.org" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.19" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.dartlang.org" + sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "0.4.16" url_launcher: dependency: "direct main" description: name: url_launcher - url: "https://pub.dartlang.org" + sha256: "704b2bd47392940d1ab71aa507b72f71989a255b2cb188cf3fff141b5921f364" + url: "https://pub.dev" source: hosted version: "6.0.3" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - url: "https://pub.dartlang.org" + sha256: "86f3f393cde6bed2a05bfc7f05e52aeaf4f9911a3ad9ff78a42e89e57e5a264a" + url: "https://pub.dev" source: hosted version: "2.0.0" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - url: "https://pub.dartlang.org" + sha256: f72b523da791d519aed53c12fd99c7dc50fdd1e4913da904081f3666d06334b5 + url: "https://pub.dev" source: hosted version: "2.0.0" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - url: "https://pub.dartlang.org" + sha256: "9589b5fea61ae62177d687098edde3adcc808ea976b2b96a8267563d681d75f1" + url: "https://pub.dev" source: hosted version: "2.0.2" url_launcher_web: dependency: transitive description: name: url_launcher_web - url: "https://pub.dartlang.org" + sha256: "057e3458dfcc4276d171ae70cd98efc6d2485bf39b93a015349754e5dec0f657" + url: "https://pub.dev" source: hosted version: "2.0.0" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - url: "https://pub.dartlang.org" + sha256: f98b970a12236957881fa28df0c6700f03d9dc5f471cf5ca6d205b77e7ad92d2 + url: "https://pub.dev" source: hosted version: "2.0.0" vector_math: dependency: transitive description: name: vector_math - url: "https://pub.dartlang.org" + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.1.4" sdks: - dart: ">=2.12.0 <3.0.0" + dart: ">=2.18.0 <3.0.0" flutter: ">=1.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index 3e0d640..664e65a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,8 +1,6 @@ name: linkwell description: LinkWell is Text Plugin that detects URLs and Emails in a String and when tapped opens in users browsers, version: 2.0.6 -authors: - - samuelezedi@gmail.com homepage: https://github.com/samuelezedi/linkwell environment: From d2dd4b6bf6ebb953c2f9c38397c287a50d424115 Mon Sep 17 00:00:00 2001 From: Keith Black Date: Wed, 30 Aug 2023 12:57:47 -0500 Subject: [PATCH 5/9] Removing old custom regEx field and replacing with input of array of regex patterns. --- lib/linkwell.dart | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/linkwell.dart b/lib/linkwell.dart index a2c5858..ea3688c 100644 --- a/lib/linkwell.dart +++ b/lib/linkwell.dart @@ -21,11 +21,8 @@ typedef LinkWellClickHandler = bool Function(String url); /// LinkWell class created class LinkWell extends StatelessWidget { - /// The RegEx pattern is created - final String? regEx; - - // An array of RegEx strings matching different patterms that should - // be considered as links. + /// (Optional) Custom regex strings can be passed in [regExLinkPatterns]. + /// This can be used to replace the built in pattern matching. final List? regExLinkPatterns; /// This is holds all links found in the text. @@ -124,7 +121,6 @@ class LinkWell extends StatelessWidget { /// LinkWell class is constructed here LinkWell( this.text, { - this.regEx, this.regExLinkPatterns, this.key, this.style, From 43f900d746d804729c31fae4f642b59cdb53e1bb Mon Sep 17 00:00:00 2001 From: Keith Black Date: Sun, 3 Sep 2023 16:15:33 -0500 Subject: [PATCH 6/9] Added ability to include phone numbers in links. --- example/pubspec.lock | 186 ++++++++++++++++++++++++++++++++++- lib/linkwell.dart | 74 ++++++++------ lib/src/linkwell_helper.dart | 31 ++++++ lib/src/source.dart | 27 ----- pubspec.lock | 186 ++++++++++++++++++++++++++++++++++- pubspec.yaml | 4 +- 6 files changed, 444 insertions(+), 64 deletions(-) create mode 100644 lib/src/linkwell_helper.dart delete mode 100644 lib/src/source.dart diff --git a/example/pubspec.lock b/example/pubspec.lock index 43c94ac..6fad645 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -33,6 +33,20 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + cm_logger: + dependency: transitive + description: + path: "/Users/keithblack/GitHub/cm_logger" + relative: false + source: path + version: "0.0.1" + cm_utils: + dependency: transitive + description: + path: "/Users/keithblack/GitHub/cm_utils" + relative: false + source: path + version: "0.0.1" collection: dependency: transitive description: @@ -57,11 +71,75 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99 + url: "https://pub.dev" + source: hosted + version: "2.0.2" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_keychainaccess: + dependency: transitive + description: + name: flutter_keychainaccess + sha256: a292127104c411e4afc15d29d9f105fff828302a1516698c273c19ecc90c8fab + url: "https://pub.dev" + source: hosted + version: "0.2.1" + flutter_secure_storage: + dependency: transitive + description: + name: flutter_secure_storage + sha256: "5abe3d5c25ab435e48c47fb61bac25606062a305fac637c2f020e25abd30014a" + url: "https://pub.dev" + source: hosted + version: "5.1.2" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: "3d5032e314774ee0e1a7d0a9f5e2793486f0dff2dd9ef5a23f4e3fb2a0ae6a9e" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "388f76fd0f093e7415a39ec4c169ae7cceeee6d9f9ba529d788a13f2be4de7bd" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: "0d4d3a5dd4db28c96ae414d7ba3b8422fd735a8255642774803b2532c9a61d7e" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: "30f84f102df9dcdaa2241866a958c2ec976902ebdaa8883fbfe525f1f2f3cf20" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: ca89c8059cf439985aa83c59619b3674c7ef6cc2e86943d169a7369d6a69cab5 + url: "https://pub.dev" + source: hosted + version: "1.1.3" flutter_test: dependency: "direct dev" description: flutter @@ -72,6 +150,14 @@ packages: description: flutter source: sdk version: "0.0.0" + intl: + dependency: transitive + description: + name: intl + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + url: "https://pub.dev" + source: hosted + version: "0.18.1" js: dependency: transitive description: @@ -87,6 +173,14 @@ packages: relative: true source: path version: "2.0.6" + logging: + dependency: transitive + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" matcher: dependency: transitive description: @@ -119,6 +213,62 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.2" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa + url: "https://pub.dev" + source: hosted + version: "2.1.1" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + platform: + dependency: transitive + description: + name: platform + sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 + url: "https://pub.dev" + source: hosted + version: "3.1.2" plugin_platform_interface: dependency: transitive description: @@ -140,6 +290,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" stack_trace: dependency: transitive description: @@ -164,6 +322,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" + url: "https://pub.dev" + source: hosted + version: "3.1.0" term_glyph: dependency: transitive description: @@ -236,6 +402,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + win32: + dependency: transitive + description: + name: win32 + sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c" + url: "https://pub.dev" + source: hosted + version: "4.1.4" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" + url: "https://pub.dev" + source: hosted + version: "1.0.3" sdks: - dart: ">=2.18.0 <3.0.0" - flutter: ">=1.22.0" + dart: ">=2.19.6 <3.0.0" + flutter: ">=3.7.0" diff --git a/lib/linkwell.dart b/lib/linkwell.dart index ea3688c..3f056e8 100644 --- a/lib/linkwell.dart +++ b/lib/linkwell.dart @@ -10,8 +10,8 @@ library linkwell; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:linkwell/src/source.dart'; -// import 'package:cm_utils/string_utils.dart'; +import 'src/linkwell_helper.dart'; +import 'package:cm_utils/string_utils.dart'; /// LinkWell depends on url_launcher plugin /// it help lauches the links and emails when user taps @@ -38,13 +38,12 @@ class LinkWell extends StatelessWidget { /// this is set to null by default final Map? listOfNames; - /// This hold TextSpan Widgets List - /// which is then placed as child in the RichText Widget - final List textSpanWidget = []; - /// This hold the text the user provides final String text; + /// This hold the text the user provides + final bool includePhoneLinks; + /// This hold user defined styling /// It's an instanciation of Flutter Widget TextStyle final TextStyle? style; @@ -108,8 +107,8 @@ class LinkWell extends StatelessWidget { final TextDirection? textDirection; /// This optional parameter gives the caller the opportunity to evaluate - /// the link prior to LinkWell calling the default click handling - /// clickHandler should return 'true' if it processes the click, + /// the link prior to LinkWell calling the default click handling. + /// 'clickHandler' should return 'true' if it processes the click, /// otherwise, if it returns 'false' then LinkWell will call the /// built-in click handler. final LinkWellClickHandler? clickHandler; @@ -118,9 +117,14 @@ class LinkWell extends StatelessWidget { /// by default can also be null final Key? key; + /// This hold TextSpan Widgets List + /// which is then placed as child in the RichText Widget + final List _textSpanWidget = []; + /// LinkWell class is constructed here LinkWell( this.text, { + this.includePhoneLinks = false, this.regExLinkPatterns, this.key, this.style, @@ -143,8 +147,12 @@ class LinkWell extends StatelessWidget { /// _initialize function _initialize() { + var regexStrings = []; + // Use passed in regular expression strings if provided, otherwise use default - var regexStrings = regExLinkPatterns ?? Helper.defaultLinkRegexStr; + regexStrings + .addAll(regExLinkPatterns ?? LinkWellHelper.defaultLinkRegexStr); + if (includePhoneLinks) regexStrings.add(LinkWellHelper.phoneRegexStr); regexStrings.forEach((regexStr) { /// An Iterable with variable name matches @@ -189,9 +197,9 @@ class LinkWell extends StatelessWidget { /// Adds TextSpan to textSpanWidget /// checks if style is null and set deafult style /// otherwise set style to user defined - textSpanWidget.add(TextSpan( + _textSpanWidget.add(TextSpan( text: this.text, - style: style == null ? Helper.defaultTextStyle : style)); + style: style == null ? LinkWellHelper.defaultTextStyle : style)); } /// _buildBody() @@ -216,11 +224,11 @@ class LinkWell extends StatelessWidget { if (wid[0] != '') { var text = TextSpan( text: wid[0], - style: style == null ? Helper.defaultTextStyle : style, + style: style == null ? LinkWellHelper.defaultTextStyle : style, ); /// added - textSpanWidget.add(text); + _textSpanWidget.add(text); } /// when a link is found @@ -248,23 +256,23 @@ class LinkWell extends StatelessWidget { var link = TextSpan( text: name, - style: linkStyle == null ? Helper.linkDefaultTextStyle : linkStyle, + style: linkStyle == null + ? LinkWellHelper.linkDefaultTextStyle + : linkStyle, recognizer: new TapGestureRecognizer()..onTap = () => _launch(url)); /// added - textSpanWidget.add(link); - - // TODO: Uncomment here to allow clicking on and calling a phone number. - // Must be implemented in custom clickHandler. - // - // } else if (value.isValidPhoneNumber()) { - // var link = TextSpan( - // text: value, - // style: linkStyle == null ? Helper.linkDefaultTextStyle : linkStyle, - // recognizer: new TapGestureRecognizer() - // ..onTap = () => _launch('tel:$value')); - - // textSpanWidget.add(link); + _textSpanWidget.add(link); + } else if (value.isValidPhoneNumber()) { + var link = TextSpan( + text: value, + style: linkStyle == null + ? LinkWellHelper.linkDefaultTextStyle + : linkStyle, + recognizer: new TapGestureRecognizer() + ..onTap = () => _launch('tel:$value')); + + _textSpanWidget.add(link); } else { /// else we let url_laucher know that this is url and not an email @@ -286,22 +294,24 @@ class LinkWell extends StatelessWidget { var link = TextSpan( text: name, - style: linkStyle == null ? Helper.linkDefaultTextStyle : linkStyle, + style: linkStyle == null + ? LinkWellHelper.linkDefaultTextStyle + : linkStyle, recognizer: new TapGestureRecognizer()..onTap = () => _launch(l)); /// added - textSpanWidget.add(link); + _textSpanWidget.add(link); } if (wid[1] != '') { if (value == links.last['link']) { var text = TextSpan( text: wid[1], - style: style == null ? Helper.defaultTextStyle : style, + style: style == null ? LinkWellHelper.defaultTextStyle : style, ); /// added - textSpanWidget.add(text); + _textSpanWidget.add(text); } else { t = wid[1]; } @@ -336,7 +346,7 @@ class LinkWell extends StatelessWidget { textScaleFactor: textScaleFactor, textWidthBasis: textWidthBasis, textDirection: textDirection, - text: TextSpan(children: textSpanWidget), + text: TextSpan(children: _textSpanWidget), ), ); } diff --git a/lib/src/linkwell_helper.dart b/lib/src/linkwell_helper.dart new file mode 100644 index 0000000..6e4e3dc --- /dev/null +++ b/lib/src/linkwell_helper.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +class LinkWellHelper { + const LinkWellHelper(this.value); + + static List defaultLinkRegexStr = [ + urlRegexStr, + emailRegexStr, + ]; + + // This string will detect email addresses + static var emailRegexStr = + r'([a-zA-Z0-9+._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)'; + + // This string will detect url's + static var urlRegexStr = + r'((http|ftp|https):\/\/|www\.)([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])'; + + // This string will detect phone numbers. + static var phoneRegexStr = + r'''(?<=[\s|'"]|^)((?:\+?1?)?[-.(]*(\d{3})[-. )]*(\d{3})[-. ]*(\d{4}))(?=[\s.;?'"!]|$)'''; + + static var defaultTextStyle = TextStyle( + fontSize: 17, + color: Colors.black, + ); + + static var linkDefaultTextStyle = TextStyle(fontSize: 17, color: Colors.blue); + + final int value; +} diff --git a/lib/src/source.dart b/lib/src/source.dart deleted file mode 100644 index ca05ff9..0000000 --- a/lib/src/source.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/material.dart'; - -class Helper { - const Helper(this.value); - - static List defaultLinkRegexStr = [ - // This string will detect url's - r'((http|ftp|https):\/\/|www\.)([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])', - - // This string will detect email addresses - r'([a-zA-Z0-9+._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)' - - // TODO: Uncomment here to allow clicking on and calling a phone number. - // '(?:\\+?(\\d{1,3}))?[-.(]*(\\d{3})[-. )]*(\\d{3})[-. ]*(\\d{4})(?: *x(\\d+))?' - ]; - - static var phoneRegex = new RegExp(r"(^(?:[+0]9)?[0-9]{10,12}$)"); - - static var defaultTextStyle = TextStyle( - fontSize: 17, - color: Colors.black, - ); - - static var linkDefaultTextStyle = TextStyle(fontSize: 17, color: Colors.blue); - - final int value; -} diff --git a/pubspec.lock b/pubspec.lock index 253d2cb..9c25f4a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -33,6 +33,20 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + cm_logger: + dependency: transitive + description: + path: "/Users/keithblack/GitHub/cm_logger" + relative: false + source: path + version: "0.0.1" + cm_utils: + dependency: "direct main" + description: + path: "/Users/keithblack/GitHub/cm_utils" + relative: false + source: path + version: "0.0.1" collection: dependency: transitive description: @@ -49,11 +63,75 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99 + url: "https://pub.dev" + source: hosted + version: "2.0.2" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_keychainaccess: + dependency: transitive + description: + name: flutter_keychainaccess + sha256: a292127104c411e4afc15d29d9f105fff828302a1516698c273c19ecc90c8fab + url: "https://pub.dev" + source: hosted + version: "0.2.1" + flutter_secure_storage: + dependency: transitive + description: + name: flutter_secure_storage + sha256: "5abe3d5c25ab435e48c47fb61bac25606062a305fac637c2f020e25abd30014a" + url: "https://pub.dev" + source: hosted + version: "5.1.2" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: "3d5032e314774ee0e1a7d0a9f5e2793486f0dff2dd9ef5a23f4e3fb2a0ae6a9e" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "388f76fd0f093e7415a39ec4c169ae7cceeee6d9f9ba529d788a13f2be4de7bd" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: "0d4d3a5dd4db28c96ae414d7ba3b8422fd735a8255642774803b2532c9a61d7e" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: "30f84f102df9dcdaa2241866a958c2ec976902ebdaa8883fbfe525f1f2f3cf20" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: ca89c8059cf439985aa83c59619b3674c7ef6cc2e86943d169a7369d6a69cab5 + url: "https://pub.dev" + source: hosted + version: "1.1.3" flutter_test: dependency: "direct dev" description: flutter @@ -64,6 +142,14 @@ packages: description: flutter source: sdk version: "0.0.0" + intl: + dependency: transitive + description: + name: intl + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + url: "https://pub.dev" + source: hosted + version: "0.18.1" js: dependency: transitive description: @@ -72,6 +158,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.5" + logging: + dependency: transitive + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" matcher: dependency: transitive description: @@ -104,6 +198,62 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.2" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa + url: "https://pub.dev" + source: hosted + version: "2.1.1" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + platform: + dependency: transitive + description: + name: platform + sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 + url: "https://pub.dev" + source: hosted + version: "3.1.2" plugin_platform_interface: dependency: transitive description: @@ -125,6 +275,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" stack_trace: dependency: transitive description: @@ -149,6 +307,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" + url: "https://pub.dev" + source: hosted + version: "3.1.0" term_glyph: dependency: transitive description: @@ -221,6 +387,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + win32: + dependency: transitive + description: + name: win32 + sha256: "5a751eddf9db89b3e5f9d50c20ab8612296e4e8db69009788d6c8b060a84191c" + url: "https://pub.dev" + source: hosted + version: "4.1.4" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" + url: "https://pub.dev" + source: hosted + version: "1.0.3" sdks: - dart: ">=2.18.0 <3.0.0" - flutter: ">=1.22.0" + dart: ">=2.19.6 <3.0.0" + flutter: ">=3.7.0" diff --git a/pubspec.yaml b/pubspec.yaml index 664e65a..391d565 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,6 @@ name: linkwell description: LinkWell is Text Plugin that detects URLs and Emails in a String and when tapped opens in users browsers, +publish_to: 'none' version: 2.0.6 homepage: https://github.com/samuelezedi/linkwell @@ -10,7 +11,8 @@ dependencies: flutter: sdk: flutter url_launcher: ^6.0.3 - + cm_utils: + path: /Users/keithblack/GitHub/cm_utils dev_dependencies: flutter_test: From 2ce45a1428d3051d42d047050c3fd38300c69926 Mon Sep 17 00:00:00 2001 From: Keith Black Date: Thu, 7 Sep 2023 17:17:16 -0500 Subject: [PATCH 7/9] Callback parameter change and moved regex strings to cm_utils. --- lib/linkwell.dart | 8 ++++---- lib/src/linkwell_helper.dart | 10 ++++------ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/linkwell.dart b/lib/linkwell.dart index 3f056e8..75fdba3 100644 --- a/lib/linkwell.dart +++ b/lib/linkwell.dart @@ -17,7 +17,7 @@ import 'package:cm_utils/string_utils.dart'; /// it help lauches the links and emails when user taps import 'package:url_launcher/url_launcher.dart'; -typedef LinkWellClickHandler = bool Function(String url); +typedef LinkWellClickHandler = Future Function(String url); /// LinkWell class created class LinkWell extends StatelessWidget { @@ -281,7 +281,7 @@ class LinkWell extends StatelessWidget { : value.toString().contains('http://') ? value : 'http://' + value; - String? name = l; + String? name = value; if (this.listOfNames != null) { if (this.listOfNames!.containsKey(value)) { @@ -319,10 +319,10 @@ class LinkWell extends StatelessWidget { }); } - void _launch(url) { + void _launch(url) async { var handled = false; if (clickHandler != null) { - handled = clickHandler!(url); + handled = await clickHandler!(url); } if (!handled) { try { diff --git a/lib/src/linkwell_helper.dart b/lib/src/linkwell_helper.dart index 6e4e3dc..eb4edc9 100644 --- a/lib/src/linkwell_helper.dart +++ b/lib/src/linkwell_helper.dart @@ -1,3 +1,4 @@ +import 'package:cm_utils/string_utils.dart'; import 'package:flutter/material.dart'; class LinkWellHelper { @@ -9,16 +10,13 @@ class LinkWellHelper { ]; // This string will detect email addresses - static var emailRegexStr = - r'([a-zA-Z0-9+._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)'; + static var emailRegexStr = CmRegexPatterns.embeddedEmailRegexStr; // This string will detect url's - static var urlRegexStr = - r'((http|ftp|https):\/\/|www\.)([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])'; + static var urlRegexStr = CmRegexPatterns.embeddedUrlRegexStr; // This string will detect phone numbers. - static var phoneRegexStr = - r'''(?<=[\s|'"]|^)((?:\+?1?)?[-.(]*(\d{3})[-. )]*(\d{3})[-. ]*(\d{4}))(?=[\s.;?'"!]|$)'''; + static var phoneRegexStr = CmRegexPatterns.embeddedUSPhoneNum; static var defaultTextStyle = TextStyle( fontSize: 17, From 5e960b364da2a36414b8667dae3ce8b4ad4a62f7 Mon Sep 17 00:00:00 2001 From: Keith Black Date: Fri, 8 Sep 2023 14:58:08 -0500 Subject: [PATCH 8/9] Fixed comment. --- lib/linkwell.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/linkwell.dart b/lib/linkwell.dart index 75fdba3..4d09034 100644 --- a/lib/linkwell.dart +++ b/lib/linkwell.dart @@ -41,7 +41,10 @@ class LinkWell extends StatelessWidget { /// This hold the text the user provides final String text; - /// This hold the text the user provides + /// If true embedded phone numbers will have hyperlinks. + /// By default clicking on the number will make a phone call + /// through the mobile device's phone dialer. To override the default + /// behavior implement [clickHandler]. final bool includePhoneLinks; /// This hold user defined styling From fc091402332e7d14cc1b9b6c056910bbd9b9ea04 Mon Sep 17 00:00:00 2001 From: Keith Black Date: Wed, 4 Sep 2024 14:54:32 -0500 Subject: [PATCH 9/9] Update linkwell.dart --- lib/linkwell.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/linkwell.dart b/lib/linkwell.dart index 4d09034..4f19201 100644 --- a/lib/linkwell.dart +++ b/lib/linkwell.dart @@ -221,7 +221,7 @@ class LinkWell extends StatelessWidget { /// var wid which represents widget var wid = t.split(value.trim()); - /// if not value is found after splitting + /// if no value is found after splitting /// we simple place text inside TextSpan /// and add to textSpanWidget if (wid[0] != '') {