Repository: CircuitVerse/mobile-app
Pull request #319: Fallback email/URL mechanism and sms intent
Author: login: NitinSohal
Labels: name: released
Created: 2025-04-01T19:48:35Z | Merged: 2025-04-03T19:33:26Z

Fixes #

Describe the changes you have made in this PR -

Introduced a fallback method for email links: If the email link (mailto) is not working on Android, it opens Gmail in Chrome so that users can send emails to the organization. Integrated a copy feature for the email: When the email link can’t be accessed, the address gets copied, and the user is notified.

**Also added the SMS Intent** in the intent so that it can come in handy in the future if someone wants to use the SMS directly in the application

**Also added the retry mechanism** that if the URL fails to open then it should be copied into the clipboard and also a popup should open for retrying to open that url


NOTE:-

Upgraded the url_launcher to the latest version and the positions of the intents (not causing any error)

Screenshots of the changes (If any) -

Note: Please check Allow edits from maintainers. if you would like us to assist in the PR.


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

## Summary by CodeRabbit

- **New Features**
	- Improved external link handling across the app for more context-aware navigation.
	- Enhanced support for phone links on Android, added SMS handling, and refined email link behavior with user notifications and fallback options.
- **Chores**
	- Upgraded a core URL-launch dependency to version 6.3.0 for improved performance and reliability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 294b05cc..1e6608a5 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -7,31 +7,31 @@
          FlutterApplication and put your custom class here. -->
 
     <!-- Queries section needed for Android 11+ to handle mailto and other URLs -->
-    <queries>
-        <!-- For mailto: links -->
-        <intent>
-            <action android:name="android.intent.action.VIEW" />
-            <data android:scheme="mailto" />
-        </intent>
-        
-        <!-- For https: links -->
-        <intent>
-            <action android:name="android.intent.action.VIEW" />
-            <data android:scheme="https" />
-        </intent>
-        
-        <!-- For tel: links -->
-        <intent>
-            <action android:name="android.intent.action.DIAL" />
-            <data android:scheme="tel" />
-        </intent>
-    </queries>
+
 
     <!-- Permissions -->
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-
+    <queries>
+            <!-- To support mailto URLs -->
+            <intent>
+                <action android:name="android.intent.action.VIEW" />
+                <data android:scheme="mailto" />
+            </intent>
+            <intent>
+                <action android:name="android.intent.action.VIEW" />
+                <data android:scheme="tel" />
+            </intent>
+            <intent>
+                <action android:name="android.intent.action.VIEW" />
+                <data android:scheme="sms" />
+            </intent>
+            <intent>
+                <action android:name="android.intent.action.VIEW" />
+                <data android:scheme="https" />
+            </intent>
+        </queries>
     <application
         android:name="${applicationName}"
         android:label="CircuitVerse"
diff --git a/lib/ui/components/cv_social_card.dart b/lib/ui/components/cv_social_card.dart
index f261d7db..21f8295e 100644
--- a/lib/ui/components/cv_social_card.dart
+++ b/lib/ui/components/cv_social_card.dart
@@ -20,7 +20,7 @@ class CircuitVerseSocialCard extends StatelessWidget {
   Widget build(BuildContext context) {
     return GestureDetector(
       onTap: () async {
-        launchURL(url);
+        launchURL(context, url);
       },
       child: Card(
         color:
diff --git a/lib/ui/views/about/components/contributor_avatar.dart b/lib/ui/views/about/components/contributor_avatar.dart
index 704a33fa..c2f6684c 100644
--- a/lib/ui/views/about/components/contributor_avatar.dart
+++ b/lib/ui/views/about/components/contributor_avatar.dart
@@ -12,7 +12,7 @@ class ContributorAvatar extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     return GestureDetector(
-      onTap: () => launchURL(contributor.htmlUrl),
+      onTap: () => launchURL(context, contributor.htmlUrl),
       child: Tooltip(
         message: contributor.login,
         child: Container(
diff --git a/lib/ui/views/contributors/components/contributors_donate_card.dart b/lib/ui/views/contributors/components/contributors_donate_card.dart
index 5d943172..e1c5a3f8 100644
--- a/lib/ui/views/contributors/components/contributors_donate_card.dart
+++ b/lib/ui/views/contributors/components/contributors_donate_card.dart
@@ -21,7 +21,7 @@ class ContributeDonateCard extends StatelessWidget {
         const SizedBox(height: 10),
         GestureDetector(
           onTap: () async {
-            launchURL(url);
+            launchURL(context, url);
           },
           child: Container(
             decoration: BoxDecoration(
diff --git a/lib/ui/views/ib/ib_page_view.dart b/lib/ui/views/ib/ib_page_view.dart
index ef2c3a0a..176e1437 100644
--- a/lib/ui/views/ib/ib_page_view.dart
+++ b/lib/ui/views/ib/ib_page_view.dart
@@ -128,7 +128,7 @@ class _IbPageViewState extends State<IbPageView> {
       } else {
         // Try to navigate to another page using url
         // (TODO) We need [IbLandingViewModel] to be able to get Chapter using [httpUrl]
-        launchURL(href);
+        launchURL(context, href);
       }
     }
 
diff --git a/lib/utils/url_launcher.dart b/lib/utils/url_launcher.dart
index 1284793e..5c6643e0 100644
--- a/lib/utils/url_launcher.dart
+++ b/lib/utils/url_launcher.dart
@@ -1,9 +1,124 @@
 import 'package:url_launcher/url_launcher_string.dart';
+import 'package:url_launcher/url_launcher.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter/material.dart';
 
-void launchURL(String url) async {
-  if (await canLaunchUrlString(url)) {
-    await launchUrlString(url);
+Future<void> launchURL(
+  BuildContext context,
+  String url, {
+  int retryCount = 0,
+}) async {
+  const int maxRetryAttempts = 2;
+
+  void showCustomSnackBar({
+    required String message,
+    Color backgroundColor = Colors.grey,
+    Duration duration = const Duration(seconds: 2),
+    SnackBarAction? action,
+  }) {
+    ScaffoldMessenger.of(context).showSnackBar(
+      SnackBar(
+        content: Text(
+          message,
+          style: const TextStyle(
+            color: Colors.white,
+            fontWeight: FontWeight.bold,
+          ),
+        ),
+        backgroundColor: backgroundColor,
+        behavior: SnackBarBehavior.floating,
+        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
+        duration: duration,
+        margin: const EdgeInsets.all(16),
+        action: action,
+      ),
+    );
+  }
+
+  void showFallbackDialog(BuildContext context, String message) {
+    showDialog(
+      context: context,
+      builder: (BuildContext context) {
+        return AlertDialog(
+          title: const Text("Notification"),
+          content: Text(message),
+          actions: [
+            TextButton(
+              onPressed: () => Navigator.of(context).pop(),
+              child: const Text("OK"),
+            ),
+          ],
+        );
+      },
+    );
+  }
+
+  if (url.startsWith('mailto:')) {
+    final Uri uri = Uri.parse(url);
+    if (await canLaunchUrl(uri)) {
+      await launchUrl(uri, mode: LaunchMode.externalApplication);
+    } else {
+      final email = url.replaceFirst('mailto:', '');
+      Clipboard.setData(ClipboardData(text: email));
+      showCustomSnackBar(
+        message: "Email copied to clipboard: $email",
+        backgroundColor: Colors.grey.shade800,
+        duration: const Duration(seconds: 2),
+      );
+      final emailurl = 'https://mail.google.com/mail/u/0/?fs=1&to=$email&tf=cm';
+      if (await canLaunchUrlString(emailurl)) {
+        await launchUrlString(emailurl, mode: LaunchMode.externalApplication);
+      } else {
+        showFallbackDialog(context, "Failed to open the email client");
+      }
+    }
+  } else if (url.startsWith('sms:')) {
+    final Uri smsUri = Uri.parse(url);
+    if (await canLaunchUrl(smsUri)) {
+      await launchUrl(smsUri, mode: LaunchMode.externalApplication);
+    } else {
+      final phone = url.replaceFirst('sms:', '');
+      Clipboard.setData(ClipboardData(text: phone));
+      showCustomSnackBar(
+        message: "Phone number copied to clipboard: $phone",
+        backgroundColor: Colors.grey.shade800,
+        duration: const Duration(seconds: 2),
+      );
+    }
   } else {
-    throw 'Could not launch $url';
+    if (await canLaunchUrlString(url)) {
+      await launchUrlString(url, mode: LaunchMode.externalApplication);
+    } else if (retryCount < maxRetryAttempts) {
+      Clipboard.setData(ClipboardData(text: url));
+      if (ScaffoldMessenger.of(context).mounted) {
+        showCustomSnackBar(
+          message: "Couldn't open $url. Copied to clipboard.",
+          backgroundColor: Colors.redAccent,
+          duration: const Duration(seconds: 2),
+          action: SnackBarAction(
+            label: "Retry",
+            textColor: Colors.white,
+            onPressed: () async {
+              await launchURL(context, url, retryCount: retryCount + 1);
+            },
+          ),
+        );
+      } else {
+        showFallbackDialog(context, "Failed to open the URL");
+      }
+    } else {
+      if (ScaffoldMessenger.of(context).mounted) {
+        showCustomSnackBar(
+          message: "Failed to open the URL after several attempts.",
+          backgroundColor: Colors.redAccent,
+          duration: const Duration(seconds: 2),
+        );
+      } else {
+        showFallbackDialog(
+          context,
+          "Failed to open the URL after several attempts.",
+        );
+      }
+    }
   }
 }
diff --git a/pubspec.yaml b/pubspec.yaml
index 9e9df06b..c7b0fdb3 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -59,7 +59,7 @@ dependencies:
   theme_provider: ^0.6.0
   timeago: ^3.1.0
   transparent_image: ^2.0.0
-  url_launcher: ^6.1.11
+  url_launcher: ^6.3.0
   flutter_facebook_auth: ^6.0.4
 
   http: any


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (3)</summary><blockquote>

<details>
<summary>lib/ui/components/cv_social_card.dart (1)</summary><blockquote>

`23-23`: **Correctly updated launchURL call to include context.**

The addition of the context parameter to the launchURL function call aligns with the PR objectives to implement the fallback email mechanism. This change allows the URL launcher to interact with the Flutter widget tree for providing user feedback when handling URLs.


Consider adding a space after the comma for better readability:
```diff
-        launchURL(context,url);
+        launchURL(context, url);
```

</blockquote></details>
<details>
<summary>lib/ui/views/about/components/contributor_avatar.dart (1)</summary><blockquote>

`15-15`: **Correctly updated launchURL call to include context.**

The addition of the context parameter to the launchURL function call is consistent with the changes across the codebase and supports the fallback email mechanism implementation.


Consider adding a space after the comma for better readability:
```diff
-      onTap: () => launchURL(context,contributor.htmlUrl),
+      onTap: () => launchURL(context, contributor.htmlUrl),
```

</blockquote></details>
<details>
<summary>lib/ui/views/contributors/components/contributors_donate_card.dart (1)</summary><blockquote>

`23-23`: **Correctly updated launchURL call to include context.**

The addition of the context parameter to the launchURL function call maintains consistency with the other files in this PR and supports the fallback email mechanism implementation.


Consider adding a space after the comma for better readability:
```diff
-            launchURL(context,url);
+            launchURL(context, url);
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 488b53866148885a63a15e52230aa83ef9166cb8 and d078056007b1e185b9c357ccc85607058ff20cec.

</details>

<details>
<summary>📒 Files selected for processing (7)</summary>

* `android/app/src/main/AndroidManifest.xml` (1 hunks)
* `lib/ui/components/cv_social_card.dart` (1 hunks)
* `lib/ui/views/about/components/contributor_avatar.dart` (1 hunks)
* `lib/ui/views/contributors/components/contributors_donate_card.dart` (1 hunks)
* `lib/ui/views/ib/ib_page_view.dart` (1 hunks)
* `lib/utils/url_launcher.dart` (1 hunks)
* `pubspec.yaml` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (6)</summary><blockquote>

<details>
<summary>pubspec.yaml (1)</summary>

`62-62`: **Good url_launcher package upgrade.**

Upgrading the url_launcher package to version ^6.3.0 is appropriate for implementing the fallback email mechanism and SMS intent functionality mentioned in the PR objectives. This newer version provides better support for handling various URL schemes.

</details>
<details>
<summary>lib/ui/views/ib/ib_page_view.dart (1)</summary>

`131-131`: **Updated function call to include context parameter.**

The function call has been correctly updated to pass the `context` parameter to the `launchURL` function, which is now required for the new fallback email mechanism.

</details>
<details>
<summary>android/app/src/main/AndroidManifest.xml (1)</summary>

`16-34`: **Enhanced URL handling with updated intent filters.**

The `<queries>` section has been properly updated to include necessary intent filters for:
1. `mailto` URLs (for email handling)
2. `tel` URLs (for phone calls) - now using `VIEW` action 
3. New `sms` URL scheme support (for text messaging)
4. `https` URLs (for web links)

These changes are well-aligned with the PR objectives to support fallback email mechanisms and add SMS intent functionality.

</details>
<details>
<summary>lib/utils/url_launcher.dart (3)</summary>

`2-4`: **Added required imports for enhanced URL handling.**

The additional imports are necessary for:
- `url_launcher.dart`: For launching URLs with the Uri class
- `services.dart`: For clipboard functionality
- `material.dart`: For showing SnackBar notifications

---

`6-29`: **Implemented fallback mechanism for email links.**

This implementation correctly addresses the PR objective of adding a fallback email mechanism by:
1. First attempting to launch the native email client
2. If that fails (especially on Android), it:
   - Copies the email address to the clipboard
   - Shows a user notification via SnackBar
   - Opens Gmail in the browser with the email pre-filled

This provides a significantly improved user experience for email interaction.

---

`30-33`: **Simplified non-email URL handling.**

The non-email URL handling has been simplified and now correctly handles URLs without throwing exceptions when the URL can't be launched.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-01T19:52:43Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>lib/utils/url_launcher.dart (2)</summary><blockquote>

`7-25`: **Robust fallback mechanism for email handling.**

The email handling logic with fallback mechanism is well-implemented. Some thoughts:

1. The 1-second delay before launching Gmail may be arbitrary
2. There's no error handling if the Gmail fallback fails to launch


Consider these improvements:

```diff
void launchURL(BuildContext context, String url) async {
  if (url.startsWith('mailto:')) {
    final Uri uri = Uri.parse(url);
    if (await canLaunchUrl(uri)) {
      await launchUrl(uri, mode: LaunchMode.externalApplication);
    } else {
      final email = url.replaceFirst('mailto:', '');
      Clipboard.setData(ClipboardData(text: email));
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text("Email copied to clipboard: $email"),
          duration: Duration(seconds: 1),
        ),
      );
-     await Future.delayed(Duration(seconds: 1));
      await launchUrlString(
        'https://mail.google.com/mail/u/0/?fs=1&to=$email&tf=cm',
        mode: LaunchMode.externalApplication,
+     ).catchError((error) {
+       ScaffoldMessenger.of(context).showSnackBar(
+         SnackBar(
+           content: Text("Could not open Gmail. Please use the copied email address."),
+           duration: Duration(seconds: 2),
+         ),
+       );
+     });
    }
  } else {
```

---

`26-30`: **Improved non-email URL handling.**

The error handling for non-mailto URLs is more user-friendly by removing exceptions, but there's no feedback to the user if a URL can't be launched.


Consider adding user feedback when a URL can't be launched:

```diff
  } else {
    if (await canLaunchUrlString(url)) {
      await launchUrlString(url);
+   } else {
+     ScaffoldMessenger.of(context).showSnackBar(
+       SnackBar(
+         content: Text("Cannot open the link: $url"),
+         duration: Duration(seconds: 2),
+       ),
+     );
    }
  }
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between d078056007b1e185b9c357ccc85607058ff20cec and 832834e97c94710e85acb32f5297d2c3ca459455.

</details>

<details>
<summary>📒 Files selected for processing (5)</summary>

* `lib/ui/components/cv_social_card.dart` (1 hunks)
* `lib/ui/views/about/components/contributor_avatar.dart` (1 hunks)
* `lib/ui/views/contributors/components/contributors_donate_card.dart` (1 hunks)
* `lib/ui/views/ib/ib_page_view.dart` (1 hunks)
* `lib/utils/url_launcher.dart` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (4)</summary>

* lib/ui/views/contributors/components/contributors_donate_card.dart
* lib/ui/components/cv_social_card.dart
* lib/ui/views/ib/ib_page_view.dart
* lib/ui/views/about/components/contributor_avatar.dart

</details>

<details>
<summary>🔇 Additional comments (3)</summary><blockquote>

<details>
<summary>lib/utils/url_launcher.dart (3)</summary>

`1-4`: **Appropriate package imports for enhanced functionality.**

The new imports support the required functionality - `url_launcher.dart` for URL handling capabilities, `services.dart` for clipboard functionality, and `material.dart` for UI components like SnackBar.

---

`6-6`: **Method signature appropriately updated to include BuildContext.**

Adding the BuildContext parameter is necessary for showing UI feedback when copying email addresses to the clipboard.

---

`6-31`: <details>
<summary>❓ Verification inconclusive</summary>

**SMS handling is missing despite being mentioned in PR objectives.**

The PR mentions adding an SMS intent, but there's no specific handling for SMS URLs in this function.





Consider adding SMS URL handling similar to email:

```diff
Future<void> launchURL(BuildContext context, String url) async {
  if (url.startsWith('mailto:')) {
    // ...existing email handling code
+  } else if (url.startsWith('sms:')) {
+    final Uri uri = Uri.parse(url);
+    if (await canLaunchUrl(uri)) {
+      await launchUrl(uri, mode: LaunchMode.externalApplication);
+    } else {
+      // Extract phone number
+      final phone = url.replaceFirst('sms:', '');
+      Clipboard.setData(ClipboardData(text: phone));
+      ScaffoldMessenger.of(context).showSnackBar(
+        SnackBar(
+          content: Text("Phone number copied to clipboard: $phone"),
+          duration: Duration(seconds: 2),
+        ),
+      );
+    }
  } else {
    // ...existing non-email URL handling
  }
}
```

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if SMS intent is implemented elsewhere in the code
grep -r "sms:" --include="*.dart" --include="*.xml" .
```

Length of output: 53

---



</details>

**Attention: SMS Intent Handling Still Missing**

The PR objectives mention adding SMS intent support, but our verification shows no implementation for SMS handling anywhere in the code base. Please confirm whether the absence of SMS intent handling is intentional. If SMS support is indeed required, consider adding an SMS-specific block similar to the email handling logic. For example:

```diff
Future<void> launchURL(BuildContext context, String url) async {
  if (url.startsWith('mailto:')) {
    // ...existing email handling code
+  } else if (url.startsWith('sms:')) {
+    final Uri uri = Uri.parse(url);
+    if (await canLaunchUrl(uri)) {
+      await launchUrl(uri, mode: LaunchMode.externalApplication);
+    } else {
+      // Extract phone number, similar to email handling
+      final phone = url.replaceFirst('sms:', '');
+      Clipboard.setData(ClipboardData(text: phone));
+      ScaffoldMessenger.of(context).showSnackBar(
+        SnackBar(
+          content: Text("Phone number copied to clipboard: $phone"),
+          duration: Duration(seconds: 2),
+        ),
+      );
+    }
  } else {
    // ...existing non-email URL handling
  }
}
```

Please re-verify if SMS handling might be implemented elsewhere in the repository or if this missing block is the intended oversight.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-01T20:03:50Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>lib/utils/url_launcher.dart (2)</summary><blockquote>

`7-25`: **Robust fallback mechanism for email handling.**

The implementation provides a good fallback mechanism for email links that can't be opened directly. However, there are a few aspects to consider:

1. The 1-second delay before opening Gmail might cause confusion as there's no loading indicator.
2. This implementation assumes Gmail is available, which may not be true for all users.
3. Consider platform-specific checks since this fallback is primarily for Android.


```diff
if (url.startsWith('mailto:')) {
  final Uri uri = Uri.parse(url);
  if (await canLaunchUrl(uri)) {
    await launchUrl(uri, mode: LaunchMode.externalApplication);
  } else {
    final email = url.replaceFirst('mailto:', '');
    Clipboard.setData(ClipboardData(text: email));
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text("Email copied to clipboard: $email"),
-       duration: Duration(seconds: 1),
+       duration: Duration(seconds: 2),
      ),
    );
-   await Future.delayed(Duration(seconds: 1));
+   // Show a loading indicator
+   final bool userWantsToOpenGmail = await showDialog(
+     context: context,
+     builder: (context) => AlertDialog(
+       title: Text('Open Gmail?'),
+       content: Text('Would you like to open Gmail in browser?'),
+       actions: [
+         TextButton(
+           onPressed: () => Navigator.pop(context, false),
+           child: Text('No'),
+         ),
+         TextButton(
+           onPressed: () => Navigator.pop(context, true),
+           child: Text('Yes'),
+         ),
+       ],
+     ),
+   ) ?? false;
+   
+   if (userWantsToOpenGmail) {
      await launchUrlString(
        'https://mail.google.com/mail/u/0/?fs=1&to=$email&tf=cm',
        mode: LaunchMode.externalApplication,
      );
+   }
  }
```

---

`27-29`: **Add error handling for non-mailto URLs.**

The current implementation silently fails if a URL can't be launched. Consider adding error handling to provide user feedback in that case.


```diff
if (await canLaunchUrlString(url)) {
  await launchUrlString(url);
+} else {
+  ScaffoldMessenger.of(context).showSnackBar(
+    SnackBar(
+      content: Text("Could not open the link: $url"),
+      duration: Duration(seconds: 2),
+    ),
+  );
}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 832834e97c94710e85acb32f5297d2c3ca459455 and a5f810347af7cfc913226dec9cf1a2fd6db63183.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `lib/utils/url_launcher.dart` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (2)</summary><blockquote>

<details>
<summary>lib/utils/url_launcher.dart (2)</summary>

`2-4`: **Good addition of necessary imports.**

These new imports correctly support the enhanced functionality in the launchURL method, providing access to Clipboard, BuildContext, and URI handling capabilities.

---

`6-6`: **Function signature updated correctly.**

The function signature change from `void launchURL(String url)` to `Future<void> launchURL(BuildContext context, String url)` properly addresses the previous review comment and adds the necessary BuildContext parameter for UI interactions.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-01T20:10:54Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>lib/utils/url_launcher.dart (2)</summary><blockquote>

`7-24`: **Well-implemented email fallback mechanism with user feedback.**

The fallback logic for email links is robust:
1. First attempts to launch the native email client
2. If that fails, copies the email to clipboard with user notification
3. Then attempts to open Gmail in Chrome as a last resort

However, there are two potential improvements:


1. The 1-second SnackBar duration (line 17) may be too short for users to read the message. Consider increasing it to at least 2-3 seconds.
2. There's no error handling for the Gmail fallback. Consider adding error handling in case the Gmail URL also fails to launch.

```diff
-          duration: Duration(seconds: 1),
+          duration: Duration(seconds: 3),
```

---

`26-28`: **Consider consistent launch modes across URL types.**

For email URLs, you're using `LaunchMode.externalApplication` (lines 10 and 22), but for regular URLs, no mode is specified (line 27). This inconsistency might lead to different user experiences.


Consider using a consistent launch mode for all URL types:

```diff
-      await launchUrlString(url);
+      await launchUrlString(url, mode: LaunchMode.platformDefault);
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between a5f810347af7cfc913226dec9cf1a2fd6db63183 and c58d3605926cc7994b238bbae2f7b6407803c105.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `lib/utils/url_launcher.dart` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (2)</summary><blockquote>

<details>
<summary>lib/utils/url_launcher.dart (2)</summary>

`2-4`: **Added necessary imports for enhanced functionality.**

The additional imports support the new functionality well:
- `url_launcher.dart` provides URI handling
- `services.dart` enables clipboard operations
- `material.dart` allows for UI feedback components

---

`6-6`: **Function signature improved for async operations.**

Changed return type to `Future<void>` which properly reflects the async nature of this function. The addition of the BuildContext parameter enables UI feedback.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-02T19:59:45Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>lib/utils/url_launcher.dart (2)</summary><blockquote>

`7-24`: **Good implementation of email fallback mechanism, but consider UX improvements.**

The implementation properly handles email links with a fallback to Gmail in browser and clipboard functionality, which aligns with the PR objectives. However, there are a few areas for improvement:

1. The SnackBar duration is only 1 second, which might be too short for users to notice.
2. There's no error handling for the Gmail fallback if `launchUrlString` fails.


Consider these improvements:

```diff
  Clipboard.setData(ClipboardData(text: email));
  ScaffoldMessenger.of(context).showSnackBar(
    SnackBar(
      content: Text("Email copied to clipboard: $email"),
-     duration: Duration(seconds: 1),
+     duration: Duration(seconds: 2),
    ),
  );
- await launchUrlString(
-   'https://mail.google.com/mail/u/0/?fs=1&to=$email&tf=cm',
-   mode: LaunchMode.externalApplication,
- );
+ try {
+   await launchUrlString(
+     'https://mail.google.com/mail/u/0/?fs=1&to=$email&tf=cm',
+     mode: LaunchMode.externalApplication,
+   );
+ } catch (e) {
+   ScaffoldMessenger.of(context).showSnackBar(
+     SnackBar(
+       content: Text("Failed to open Gmail. Please manually compose an email to $email"),
+       duration: Duration(seconds: 3),
+     ),
+   );
+ }
```

---

`1-56`: **Consider adding SMS URL handling.**

The PR objectives mention adding SMS intent functionality, but there's no specific handling for SMS URLs (e.g., `sms:` scheme) in the implementation. Consider adding special handling for SMS URLs similar to the email handling.


```diff
Future<void> launchURL(BuildContext context, String url) async {
  if (url.startsWith('mailto:')) {
    // Existing mailto handling
+  } else if (url.startsWith('sms:')) {
+    final Uri uri = Uri.parse(url);
+    if (await canLaunchUrl(uri)) {
+      await launchUrl(uri, mode: LaunchMode.externalApplication);
+    } else {
+      // Extract phone number from sms URL
+      final phoneNumber = url.replaceFirst('sms:', '').split('?')[0];
+      Clipboard.setData(ClipboardData(text: phoneNumber));
+      ScaffoldMessenger.of(context).showSnackBar(
+        SnackBar(
+          content: Text("Phone number copied to clipboard: $phoneNumber"),
+          duration: Duration(seconds: 2),
+        ),
+      );
+    }
  } else {
    // Existing handling for other URLs
  }
}
```

This would complete the SMS intent functionality mentioned in the PR objectives and provide a consistent user experience for both email and SMS URLs.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between c58d3605926cc7994b238bbae2f7b6407803c105 and dbb9767a7d4ca0b8aad41022ead30bf6b043562c.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `lib/utils/url_launcher.dart` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (3)</summary><blockquote>

<details>
<summary>lib/utils/url_launcher.dart (3)</summary>

`2-4`: **Appropriate imports added for new functionality.**

The added imports support the enhanced URL launching functionality with clipboard operations and UI feedback through Material components.

---

`6-6`: **Function signature properly updated.**

The function now returns `Future<void>` and accepts a `BuildContext` parameter, addressing the previous review comment and enabling context-dependent UI operations like showing SnackBars.

---

`26-54`: **Improve error handling to prevent potential infinite retry loop.**

The current implementation has a good error handling mechanism with a retry option, but it calls `launchURL` recursively without a retry limit, which could lead to stacked SnackBars and potential infinite retries if the URL remains inaccessible.


**Action Required: Limit Recursive Retry in URL Launching**

The current implementation uses a recursive retry mechanism via the SnackBar's "Retry" action. If the URL remains inaccessible, this could potentially lead to multiple stacked error messages. Please consider adding a retry counter or an alternative fallback strategy after one or two failed attempts.

```dart
Future<void> launchURL(BuildContext context, String url, {int retryCount = 0}) async {
  const int maxRetryAttempts = 2;

  if (url.startsWith('mailto:')) {
    // Existing mailto handling
  } else {
    if (await canLaunchUrlString(url)) {
      await launchUrlString(url);
    } else if (retryCount < maxRetryAttempts) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text(
            "Failed to open the URL: $url (Attempt ${retryCount + 1} of $maxRetryAttempts)",
            style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
          ),
          backgroundColor: Colors.redAccent,
          behavior: SnackBarBehavior.floating,
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
          duration: const Duration(seconds: 2),
          margin: const EdgeInsets.all(16),
          action: SnackBarAction(
            label: "Retry",
            textColor: Colors.white,
            onPressed: () async {
              await launchURL(context, url, retryCount: retryCount + 1);
            },
          ),
        ),
      );
    } else {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text(
            "Failed to open the URL after several attempts.",
            style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
          ),
          backgroundColor: Colors.redAccent,
          behavior: SnackBarBehavior.floating,
          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
          duration: const Duration(seconds: 2),
          margin: const EdgeInsets.all(16),
        ),
      );
    }
  }
}
```

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-02T20:01:13Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (3)</summary><blockquote>

<details>
<summary>lib/utils/url_launcher.dart (3)</summary><blockquote>

`11-29`: **Good implementation of email fallback mechanism.**

The mailto handling provides an excellent user experience with graceful degradation:
1. First attempts to launch the native email client
2. If that fails, copies the email to clipboard and notifies the user
3. Then opens Gmail in the browser as a fallback

However, consider extending the SnackBar duration for better readability.

```diff
-          duration: Duration(seconds: 1),
+          duration: Duration(seconds: 2),
```

---

`60-75`: **Use const constructors consistently.**

For consistency with the retry case SnackBar, consider using const constructors for the TextStyle, Duration, and EdgeInsets in the final error notification.

```diff
-            style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
+            style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
-          duration: Duration(seconds: 2),
+          duration: const Duration(seconds: 2),
-          margin: EdgeInsets.all(16),
+          margin: const EdgeInsets.all(16),
```

---

`25-28`: **Consider adding error handling for the Gmail fallback.**

The Gmail fallback doesn't have error handling. If this fails too, the user won't receive any feedback.

```diff
    await launchUrlString(
      'https://mail.google.com/mail/u/0/?fs=1&to=$email&tf=cm',
      mode: LaunchMode.externalApplication,
+   ).catchError((error) {
+     ScaffoldMessenger.of(context).showSnackBar(
+       const SnackBar(
+         content: Text(
+           "Could not open Gmail in browser. Please manually send an email.",
+           style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
+         ),
+         backgroundColor: Colors.redAccent,
+         behavior: SnackBarBehavior.floating,
+         shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
+         duration: Duration(seconds: 2),
+         margin: EdgeInsets.all(16),
+       ),
+     );
+   });
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between dbb9767a7d4ca0b8aad41022ead30bf6b043562c and 427636a1cc317fbb2cb1e8c036d9023dbc2f3f06.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `lib/utils/url_launcher.dart` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (3)</summary><blockquote>

<details>
<summary>lib/utils/url_launcher.dart (3)</summary>

`2-4`: **Good addition of necessary imports.**

These imports properly support the new functionality, including URL handling, clipboard operations, and UI notifications.

---

`6-10`: **Well implemented function signature improvements.**

The updated signature now properly:
1. Returns a Future for better async handling
2. Accepts a BuildContext for UI notifications
3. Includes a retry counter parameter with default value

This addresses previous feedback while enabling the new functionality.

---

`31-59`: **Well-implemented URL handling with retry mechanism.**

The implementation:
1. Attempts to launch the URL
2. Provides a well-styled error notification if it fails
3. Includes a retry button with attempt tracking
4. Uses well-defined constants for maximum attempts

This is a significant improvement that addresses previous feedback.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-02T20:15:53Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (3)</summary><blockquote>

<details>
<summary>lib/utils/url_launcher.dart (3)</summary><blockquote>

`11-29`: **Good implementation of email fallback mechanism, but consider UI consistency.**

The email fallback logic is well implemented, providing three layers of handling:
1. Try to launch the email client directly
2. Copy email to clipboard if direct launch fails
3. Open Gmail in browser as final fallback

However, there are a couple of minor issues:


1. Line 22 has a commented-out duration. Either remove it or use it.
2. The SnackBar for email copying lacks the styling applied to other SnackBars in this file.

```diff
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
-          content: Text("Email copied to clipboard: $email"),
-          // duration: Duration(seconds: 1),
+          content: Text(
+            "Email copied to clipboard: $email",
+            style: const TextStyle(
+              color: Colors.white,
+              fontWeight: FontWeight.bold,
+            ),
+          ),
+          backgroundColor: Colors.blue,
+          behavior: SnackBarBehavior.floating,
+          shape: RoundedRectangleBorder(
+            borderRadius: BorderRadius.circular(10),
+          ),
+          duration: const Duration(seconds: 2),
+          margin: const EdgeInsets.all(16),
        ),
      );
```

---

`60-76`: **Missing const keywords in final error SnackBar.**

The final error SnackBar is missing const keywords for TextStyle and other widget properties, unlike the retry SnackBar above it.


```diff
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text(
            "Failed to open the URL after several attempts.",
-            style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
+            style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
          ),
          backgroundColor: Colors.redAccent,
          behavior: SnackBarBehavior.floating,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(10),
          ),
-          duration: Duration(seconds: 2),
-          margin: EdgeInsets.all(16),
+          duration: const Duration(seconds: 2),
+          margin: const EdgeInsets.all(16),
        ),
      );
```

---

`6-76`: **Consider extracting SnackBar creation to a reusable method.**

There's duplication in the SnackBar creation code across different scenarios. Consider extracting this to a helper method for better maintainability.


You could create a method like:

```dart
SnackBar _createSnackBar({
  required String message,
  Color backgroundColor = Colors.redAccent,
  Duration duration = const Duration(seconds: 2),
  SnackBarAction? action,
}) {
  return SnackBar(
    content: Text(
      message,
      style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
    ),
    backgroundColor: backgroundColor,
    behavior: SnackBarBehavior.floating,
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(10),
    ),
    duration: duration,
    margin: const EdgeInsets.all(16),
    action: action,
  );
}
```

This would simplify the code and ensure consistency across all SnackBar instances.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 427636a1cc317fbb2cb1e8c036d9023dbc2f3f06 and 78514d14191392d1e5c1fddbd327d798dccec21d.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `lib/utils/url_launcher.dart` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (3)</summary><blockquote>

<details>
<summary>lib/utils/url_launcher.dart (3)</summary>

`2-4`: **Good addition of necessary imports for enhanced functionality.**

The new imports support the expanded functionality:
- `url_launcher.dart` for URI-based launching
- `services.dart` for clipboard operations
- `material.dart` for UI components like SnackBar

These additions align well with the PR's objective to implement fallback mechanisms for URL handling.

---

`6-10`: **Well-structured function signature with proper async return type.**

The updated function signature correctly:
- Returns `Future<void>` for async operations
- Accepts a `BuildContext` for UI interactions
- Includes a retry counter parameter with default value

This addresses the previous review comment about returning a Future for better async handling.

---

`31-59`: **Effective error handling with retry functionality for non-email URLs.**

The implementation provides:
- Proper checking if URL can be launched
- Clear user feedback when launching fails
- Clipboard copying as a fallback
- Retry option with attempt limiting
- Well-designed SnackBar with appropriate styling

This implementation addresses the feedback from PR comments about handling URL launch failures.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-02T20:39:09Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>🧹 Nitpick comments (4)</summary><blockquote>

<details>
<summary>lib/utils/url_launcher.dart (4)</summary><blockquote>

`11-29`: **Email fallback mechanism looks good, but consider increasing SnackBar duration.**

The email fallback mechanism is well implemented, copying the email address to clipboard and redirecting to Gmail when the native mail client fails. However, the SnackBar notification appears for only 1 second, which might be too brief for users to notice.


```diff
-          duration: Duration(seconds: 1),
+          duration: const Duration(seconds: 2),
```

Also consider making the SnackBar dismissible and maintaining consistency with the `const` keyword:
```diff
-          duration: Duration(seconds: 1),
+          duration: const Duration(seconds: 2),
+          behavior: SnackBarBehavior.floating,
+          action: SnackBarAction(
+            label: "Dismiss",
+            onPressed: () {
+              ScaffoldMessenger.of(context).hideCurrentSnackBar();
+            },
+          ),
```

---

`60-76`: **Good final error handling, but maintain consistency in style properties.**

The final error handling for exceeded retry attempts is well implemented. However, some style properties are missing the `const` keyword that's present in the earlier SnackBar.


```diff
-            style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
+            style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
-          duration: Duration(seconds: 2),
+          duration: const Duration(seconds: 2),
-          margin: EdgeInsets.all(16),
+          margin: const EdgeInsets.all(16),
```

---

`6-78`: **Consider refactoring for code reuse and adding documentation.**

The function handles multiple responsibilities and contains duplicated SnackBar styles. Consider extracting common elements into helper functions and adding documentation.


You could refactor the code to reduce duplication:

1. Add function documentation:
```dart
/// Launches a URL with appropriate fallback mechanisms.
/// 
/// For email URLs (mailto:), attempts to open the native mail client,
/// falling back to copying the address and opening Gmail in browser.
/// 
/// For other URLs, attempts to open them with retry mechanism,
/// falling back to copying to clipboard after max retries.
/// 
/// Parameters:
/// - context: BuildContext for showing UI feedback
/// - url: The URL to launch
/// - retryCount: Current retry attempt count (internal use)
Future<void> launchURL(...)
```

2. Extract common SnackBar styles to reduce duplication:
```dart
SnackBar _createErrorSnackBar(BuildContext context, String message, {SnackBarAction? action}) {
  return SnackBar(
    content: Text(
      message,
      style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
    ),
    backgroundColor: Colors.redAccent,
    behavior: SnackBarBehavior.floating,
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
    duration: const Duration(seconds: 2),
    margin: const EdgeInsets.all(16),
    action: action,
  );
}
```

---

`6-78`: **Consider adding a similar retry mechanism for email URLs as implemented for other URLs.**

Currently, there's a retry mechanism for non-email URLs but not for email URLs. For consistency in user experience, consider implementing a similar approach.


You could modify the email handling section to include retries:

```diff
  if (url.startsWith('mailto:')) {
    final Uri uri = Uri.parse(url);
    if (await canLaunchUrl(uri)) {
      await launchUrl(uri, mode: LaunchMode.externalApplication);
-    } else {
+    } else if (retryCount < maxRetryAttempts) {
      final email = url.replaceFirst('mailto:', '');
      Clipboard.setData(ClipboardData(text: email));
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text("Email copied to clipboard: $email"),
-          duration: Duration(seconds: 1),
+          duration: const Duration(seconds: 2),
+          behavior: SnackBarBehavior.floating,
+          action: SnackBarAction(
+            label: "Retry",
+            onPressed: () async {
+              await launchURL(context, url, retryCount: retryCount + 1);
+            },
+          ),
        ),
      );
      await launchUrlString(
        'https://mail.google.com/mail/u/0/?fs=1&to=$email&tf=cm',
        mode: LaunchMode.externalApplication,
      );
+    } else {
+      final email = url.replaceFirst('mailto:', '');
+      ScaffoldMessenger.of(context).showSnackBar(
+        SnackBar(
+          content: Text(
+            "Failed to open email client after several attempts. Email copied: $email",
+            style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
+          ),
+          backgroundColor: Colors.redAccent,
+          behavior: SnackBarBehavior.floating,
+          shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
+          duration: const Duration(seconds: 2),
+          margin: const EdgeInsets.all(16),
+        ),
+      );
    }
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between f31677b6f283df88d67eca62433bf3ace23524ed and b8547bf57766faf3a77901feb9eda431622fd35c.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `lib/utils/url_launcher.dart` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (4)</summary><blockquote>

<details>
<summary>lib/utils/url_launcher.dart (4)</summary>

`2-4`: **Good job adding necessary imports for the new functionality.**

The added imports support the new features appropriately:
- `url_launcher.dart` - For `canLaunchUrl` and `launchUrl` methods
- `flutter/services.dart` - For clipboard functionality
- `flutter/material.dart` - For UI components (SnackBar)

---

`6-10`: **Good improvements to function signature.**

The updated signature properly returns `Future<void>` for async operations and adds the necessary `BuildContext` parameter for UI interactions. The retry counter parameter with a default value is also well implemented.

---

`31-33`: **Good error handling for non-email URLs.**

The code correctly checks if the URL can be launched before attempting to do so, preventing exceptions.

---

`33-60`: **Well-implemented retry mechanism with clipboard fallback.**

The retry mechanism with limited attempts is properly implemented. Good job copying the URL to clipboard as a fallback and providing clear visual feedback to the user with a styled SnackBar.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-02T21:04:54Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (5)</summary><blockquote>

<details>
<summary>lib/utils/url_launcher.dart (5)</summary><blockquote>

`33-34`: **Consider longer SnackBar duration for copied content.**

According to the PR comments, hardik17771 suggested that a one-second duration might be too short for users to notice the clipboard message. Consider increasing this to 2-3 seconds for better visibility.

```diff
-          duration: const Duration(seconds: 1),
+          duration: const Duration(seconds: 2),
```

---

`52-52`: **Improve error message clarity for SMS.**

The current message is awkwardly phrased and could be clearer.

```diff
-            "Error, Phone no copied to clipboard: $phone",
+            "Phone number copied to clipboard: $phone",
```

---

`12-114`: **Consider extracting the SnackBar creation to reduce duplication.**

There's significant duplication in the SnackBar creation logic across different URL types. Consider extracting this into a helper function for better maintainability.

```diff
 Future<void> launchURL(
   BuildContext context,
   String url, {
   int retryCount = 0,
 }) async {
   const int maxRetryAttempts = 2;
+
+  // Helper function to show a snackbar
+  void showCustomSnackBar({
+    required String message,
+    Color backgroundColor = Colors.grey,
+    Duration duration = const Duration(seconds: 2),
+    SnackBarAction? action,
+  }) {
+    ScaffoldMessenger.of(context).showSnackBar(
+      SnackBar(
+        content: Text(
+          message,
+          style: const TextStyle(
+            color: Colors.white,
+            fontWeight: FontWeight.bold,
+          ),
+        ),
+        backgroundColor: backgroundColor,
+        behavior: SnackBarBehavior.floating,
+        shape: RoundedRectangleBorder(
+          borderRadius: BorderRadius.circular(10),
+        ),
+        duration: duration,
+        margin: const EdgeInsets.all(16),
+        action: action,
+      ),
+    );
+  }

   if (url.startsWith('mailto:')) {
     // ... existing code
     if (await canLaunchUrl(uri)) {
       await launchUrl(uri, mode: LaunchMode.externalApplication);
     } else {
       final email = url.replaceFirst('mailto:', '');
       Clipboard.setData(ClipboardData(text: email));
-      ScaffoldMessenger.of(context).showSnackBar(
-        SnackBar(
-          content: Text(
-            "Email copied to clipboard: $email",
-            style: const TextStyle(
-              color: Colors.white,
-              fontWeight: FontWeight.bold,
-            ),
-          ),
-          backgroundColor: Colors.grey[800],
-          behavior: SnackBarBehavior.floating,
-          shape: RoundedRectangleBorder(
-            borderRadius: BorderRadius.circular(10),
-          ),
-          duration: const Duration(seconds: 1),
-          margin: const EdgeInsets.all(16),
-        ),
-      );
+      showCustomSnackBar(
+        message: "Email copied to clipboard: $email",
+        backgroundColor: Colors.grey.shade800,
+        duration: const Duration(seconds: 2),
+      );
       // ... rest of the code
```

You would use similar calls to the helper function for the other SnackBar instances.

---

`69-70`: **Consider using consistent launch mode.**

You're using `LaunchMode.externalApplication` for email and SMS URLs but not for general URLs. Consider using the same mode consistently or documenting why the modes differ.

```diff
   } else {
     if (await canLaunchUrlString(url)) {
-      await launchUrlString(url);
+      await launchUrlString(url, mode: LaunchMode.externalApplication);
     } else if (retryCount < maxRetryAttempts) {
```

---

`100-112`: **Add `const` keywords for consistent style.**

The final SnackBar is missing some `const` keywords that are present in other similar widgets.

```diff
         SnackBar(
           content: Text(
             "Failed to open the URL after several attempts.",
-            style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
+            style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
           ),
           backgroundColor: Colors.redAccent,
           behavior: SnackBarBehavior.floating,
           shape: RoundedRectangleBorder(
             borderRadius: BorderRadius.circular(10),
           ),
-          duration: Duration(seconds: 2),
-          margin: EdgeInsets.all(16),
+          duration: const Duration(seconds: 2),
+          margin: const EdgeInsets.all(16),
         ),
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between ced98b188372638d26eb3d85a539d2c0ee4f4031 and 149bbf78506d044b71fd3c02d6938604ff5d3bf8.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `lib/utils/url_launcher.dart` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (5)</summary><blockquote>

<details>
<summary>lib/utils/url_launcher.dart (5)</summary>

`2-4`: **LGTM: Appropriate imports added.**

The added imports support the new functionality: `url_launcher.dart` for URI-based launching, `flutter/services.dart` for clipboard operations, and `flutter/material.dart` for UI components like SnackBar.

---

`6-11`: **Function signature properly enhanced.**

The function now correctly returns `Future<void>`, accepts a `BuildContext` parameter for UI interactions, and includes a retry mechanism with proper constraints.

---

`12-41`: **Good implementation of mailto fallback.**

The email fallback logic now handles the case when a native email client can't be launched by:
1. Copying the email to clipboard
2. Providing user feedback via SnackBar
3. Attempting to open Gmail in browser as a secondary option

This addresses the PR objective to improve the email functionality with a fallback mechanism.

---

`42-67`: **SMS intent implementation properly added.**

The SMS URL handling is correctly implemented, with fallback to clipboard copying when the SMS app cannot be launched. This fulfills the PR objective to add SMS intent functionality.

---

`69-114`: **Retry mechanism implemented properly for general URLs.**

The implementation now:
1. Limits retry attempts to prevent infinite recursion
2. Copies the URL to clipboard on failure
3. Provides clear user feedback with a retry option
4. Shows a final message after exceeding retry attempts

This addresses the feedback about implementing an error message mechanism when URL launching fails.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-02T22:27:30Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>lib/utils/url_launcher.dart (2)</summary><blockquote>

`48-48`: **Consider adjusting SnackBar duration based on PR feedback.**

The PR comments mentioned that "the one-second delay after the email address is copied may not be necessary". Consider making this consistent with the SMS handler which uses a 1-second duration instead of 2 seconds.

```diff
-        duration: const Duration(seconds: 2),
+        duration: const Duration(seconds: 1),
```

---

`69-91`: **Consider adding try-catch blocks for exception handling.**

While the code handles cases where `canLaunchUrlString` returns false, it doesn't catch potential exceptions that might be thrown during the launch process. Adding try-catch blocks would make the function more robust.

```diff
 } else {
   if (await canLaunchUrlString(url)) {
-      await launchUrlString(url, mode: LaunchMode.externalApplication);
+      try {
+        await launchUrlString(url, mode: LaunchMode.externalApplication);
+      } catch (e) {
+        Clipboard.setData(ClipboardData(text: url));
+        showCustomSnackBar(
+          message: "Error opening $url: ${e.toString()}. Copied to clipboard.",
+          backgroundColor: Colors.redAccent,
+          duration: const Duration(seconds: 2),
+        );
+      }
   } else if (retryCount < maxRetryAttempts) {
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 149bbf78506d044b71fd3c02d6938604ff5d3bf8 and 9a09f4380487a6c4db939245d236c37da133f089.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `lib/utils/url_launcher.dart` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (8)</summary><blockquote>

<details>
<summary>lib/utils/url_launcher.dart (8)</summary>

`2-4`: **LGTM: Added necessary imports for new functionality.**

These imports provide the required components for the URL launching, clipboard manipulation, and UI feedback mechanisms used throughout the function.

---

`6-10`: **Function signature updated correctly to match requirements.**

The function signature has been properly modified to:
1. Return `Future<void>` instead of `void` (as suggested in previous review)
2. Accept a `BuildContext` parameter for showing feedback to the user
3. Include a `retryCount` parameter with default value to manage recursion depth

These changes align with both the PR objectives and previous review comments.

---

`11-11`: **Good addition of retry limit to prevent infinite recursion.**

Setting a maximum retry attempts constant prevents potential stack overflow issues from unlimited recursive calls, addressing a previous review comment.

---

`13-36`: **Well-structured helper function for SnackBar creation.**

The `showCustomSnackBar` helper function effectively encapsulates SnackBar creation logic, promoting code reuse and improving readability. The function has appropriate defaults and customization options.

---

`38-54`: **Email fallback mechanism works as intended.**

The implementation successfully addresses the PR objective by:
1. Attempting to launch the email client directly first
2. Copying the email to clipboard if direct launch fails
3. Opening Gmail in Chrome browser as a fallback

This ensures users can always contact the organization even if the mailto: link doesn't work.

---

`55-67`: **SMS functionality correctly implemented.**

The implementation for SMS URLs matches the PR objective of adding SMS Intent functionality. The code properly handles fallback by copying the phone number to clipboard if the SMS app cannot be launched.

---

`69-91`: **Improved error handling for URL launching.**

The code now properly handles URL launch failures by:
1. Copying the URL to clipboard
2. Showing a descriptive error message
3. Providing a retry option with a finite number of attempts
4. Showing a final message after maximum retry attempts

This addresses the feedback from hardik17771 regarding error handling mechanisms for URL launch failures.

---

`80-82`: **Retry mechanism implemented correctly.**

The retry mechanism properly increments the retry counter, avoiding potential infinite recursion, which addresses a previous review comment.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-03T09:27:53Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>🧹 Nitpick comments (5)</summary><blockquote>

<details>
<summary>lib/utils/url_launcher.dart (5)</summary><blockquote>

`13-36`: **Consider extracting the helper function**

The `showCustomSnackBar` function is defined inside `launchURL`, which means it's recreated every time `launchURL` is called. For better performance, consider moving it outside the function as a private utility function in the same file.

```diff
+void _showCustomSnackBar({
+  required BuildContext context,
+  required String message,
+  Color backgroundColor = Colors.grey,
+  Duration duration = const Duration(seconds: 2),
+  SnackBarAction? action,
+}) {
+  ScaffoldMessenger.of(context).showSnackBar(
+    SnackBar(
+      content: Text(
+        message,
+        style: const TextStyle(
+          color: Colors.white,
+          fontWeight: FontWeight.bold,
+        ),
+      ),
+      backgroundColor: backgroundColor,
+      behavior: SnackBarBehavior.floating,
+      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
+      duration: duration,
+      margin: const EdgeInsets.all(16),
+      action: action,
+    ),
+  );
+}
+
 Future<void> launchURL(
   BuildContext context,
   String url, {
   int retryCount = 0,
 }) async {
   const int maxRetryAttempts = 2;

-  void showCustomSnackBar({
-    required String message,
-    Color backgroundColor = Colors.grey,
-    Duration duration = const Duration(seconds: 2),
-    SnackBarAction? action,
-  }) {
-    ScaffoldMessenger.of(context).showSnackBar(
-      SnackBar(
-        content: Text(
-          message,
-          style: const TextStyle(
-            color: Colors.white,
-            fontWeight: FontWeight.bold,
-          ),
-        ),
-        backgroundColor: backgroundColor,
-        behavior: SnackBarBehavior.floating,
-        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
-        duration: duration,
-        margin: const EdgeInsets.all(16),
-        action: action,
-      ),
-    );
-  }
```

Then update all calls to include the context parameter:
```diff
-showCustomSnackBar(
+_showCustomSnackBar(
+  context: context,
   message: "Email copied to clipboard: $email",
   // ...
```

---

`38-54`: **Add documentation for the mailto fallback mechanism**

The fallback to Gmail in Chrome browser when the email app can't be launched is a good feature. Consider adding a comment to explain this fallback mechanism for better code maintainability.

```diff
 if (url.startsWith('mailto:')) {
   final Uri uri = Uri.parse(url);
   if (await canLaunchUrl(uri)) {
     await launchUrl(uri, mode: LaunchMode.externalApplication);
   } else {
+    // Fallback for devices where mailto links don't work:
+    // 1. Copy the email to clipboard
+    // 2. Open Gmail in Chrome browser with the email pre-filled
     final email = url.replaceFirst('mailto:', '');
     Clipboard.setData(ClipboardData(text: email));
     showCustomSnackBar(
       message: "Email copied to clipboard: $email",
       backgroundColor: Colors.grey.shade800,
       duration: const Duration(seconds: 2),
     );
     await launchUrlString(
       'https://mail.google.com/mail/u/0/?fs=1&to=$email&tf=cm',
       mode: LaunchMode.externalApplication,
     );
   }
```

---

`55-67`: **Consider adding a fallback mechanism for SMS**

For mailto URLs, you've added a fallback to open Gmail in the browser. Consider implementing a similar fallback for SMS URLs, perhaps using a web-based SMS service or providing instructions to the user.

```diff
 } else if (url.startsWith('sms:')) {
   final Uri smsUri = Uri.parse(url);
   if (await canLaunchUrl(smsUri)) {
     await launchUrl(smsUri, mode: LaunchMode.externalApplication);
   } else {
     final phone = url.replaceFirst('sms:', '');
     Clipboard.setData(ClipboardData(text: phone));
     showCustomSnackBar(
       message: "Phone number copied to clipboard: $phone",
       backgroundColor: Colors.grey.shade800,
       duration: const Duration(seconds: 2),
     );
+    // Consider adding a fallback similar to the mailto handling
+    // For example, opening a web-based SMS service if available
   }
```

---

`45-48`: **Consider increasing SnackBar duration**

The current duration of 2 seconds for the clipboard notification may be too short for users to read comfortably. Consider increasing it to 3-4 seconds for a better user experience, especially since the PR comment mentioned UX concerns about the delay.

```diff
     showCustomSnackBar(
       message: "Email copied to clipboard: $email",
       backgroundColor: Colors.grey.shade800,
-      duration: const Duration(seconds: 2),
+      duration: const Duration(seconds: 3),
     );
```

---

`6-10`: **Consider adding an option to skip clipboard copying**

For security-sensitive URLs or email addresses, you might want to add an option to skip copying to clipboard while still showing the error message and fallback mechanism.

```diff
 Future<void> launchURL(
   BuildContext context,
   String url, {
   int retryCount = 0,
+  bool skipClipboardCopy = false,
 }) async {
```

Then modify the clipboard operations:
```diff
     final email = url.replaceFirst('mailto:', '');
-    Clipboard.setData(ClipboardData(text: email));
+    if (!skipClipboardCopy) {
+      Clipboard.setData(ClipboardData(text: email));
+    }
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 9a09f4380487a6c4db939245d236c37da133f089 and 456ea410fecf145205215f389cddeed2d55c0d78.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `lib/utils/url_launcher.dart` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (1)</summary><blockquote>

<details>
<summary>lib/utils/url_launcher.dart (1)</summary>

`6-10`: **Good enhancement to the function signature**

The changes to return `Future<void>` instead of `void` and adding the `BuildContext` parameter improve the function's usability. The retry mechanism with a default count is also a good addition for error handling.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-03T09:34:52Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>🧹 Nitpick comments (3)</summary><blockquote>

<details>
<summary>lib/utils/url_launcher.dart (3)</summary><blockquote>

`38-54`: **Consider simplifying the fallback dialog.**

The fallback dialog implementation is good, but could be simplified by using the simpler `showDialog` API or a constant dialog configuration.

```diff
void showFallbackDialog(BuildContext context, String message) {
- showDialog(
-   context: context,
-   builder: (BuildContext context) {
-     return AlertDialog(
-       title: const Text("Notification"),
-       content: Text(message),
-       actions: [
-         TextButton(
-           onPressed: () => Navigator.of(context).pop(),
-           child: const Text("OK"),
-         ),
-       ],
-     );
-   },
- );
+ showDialog(
+   context: context,
+   builder: (_) => AlertDialog(
+     title: const Text("Notification"),
+     content: Text(message),
+     actions: [
+       TextButton(
+         onPressed: () => Navigator.of(context).pop(),
+         child: const Text("OK"),
+       ),
+     ],
+   ),
+ );
}
```

---

`56-72`: **Robust email URL handling with fallback mechanism.**

The implementation provides a good fallback mechanism for mailto links by:
1. Attempting to launch the email client directly
2. Copying the email to clipboard if that fails
3. Providing a Gmail fallback link

However, consider enhancing the email extraction to handle complex mailto URLs with query parameters.

```diff
- final email = url.replaceFirst('mailto:', '');
+ // Extract email from mailto URL, handling potential query parameters
+ final Uri uri = Uri.parse(url);
+ final email = uri.path;
```

---

`6-122`: **Consider extracting helper functions to file level.**

The nested helper functions `showCustomSnackBar` and `showFallbackDialog` are recreated each time `launchURL` is called. For better code organization and reusability, consider moving them outside the `launchURL` function.

```diff
import 'package:url_launcher/url_launcher_string.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';

+void showCustomSnackBar({
+  required BuildContext context,
+  required String message,
+  Color backgroundColor = Colors.grey,
+  Duration duration = const Duration(seconds: 2),
+  SnackBarAction? action,
+}) {
+  ScaffoldMessenger.of(context).showSnackBar(
+    SnackBar(
+      content: Text(
+        message,
+        style: const TextStyle(
+          color: Colors.white,
+          fontWeight: FontWeight.bold,
+        ),
+      ),
+      backgroundColor: backgroundColor,
+      behavior: SnackBarBehavior.floating,
+      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
+      duration: duration,
+      margin: const EdgeInsets.all(16),
+      action: action,
+    ),
+  );
+}
+
+void showFallbackDialog(BuildContext context, String message) {
+  showDialog(
+    context: context,
+    builder: (_) => AlertDialog(
+      title: const Text("Notification"),
+      content: Text(message),
+      actions: [
+        TextButton(
+          onPressed: () => Navigator.of(context).pop(),
+          child: const Text("OK"),
+        ),
+      ],
+    ),
+  );
+}

Future<void> launchURL(
  BuildContext context,
  String url, {
  int retryCount = 0,
}) async {
  const int maxRetryAttempts = 2;

-  void showCustomSnackBar({
-    required String message,
-    Color backgroundColor = Colors.grey,
-    Duration duration = const Duration(seconds: 2),
-    SnackBarAction? action,
-  }) {
-    ScaffoldMessenger.of(context).showSnackBar(
-      SnackBar(
-        content: Text(
-          message,
-          style: const TextStyle(
-            color: Colors.white,
-            fontWeight: FontWeight.bold,
-          ),
-        ),
-        backgroundColor: backgroundColor,
-        behavior: SnackBarBehavior.floating,
-        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
-        duration: duration,
-        margin: const EdgeInsets.all(16),
-        action: action,
-      ),
-    );
-  }
-
-  void showFallbackDialog(BuildContext context, String message) {
-    showDialog(
-      context: context,
-      builder: (BuildContext context) {
-        return AlertDialog(
-          title: const Text("Notification"),
-          content: Text(message),
-          actions: [
-            TextButton(
-              onPressed: () => Navigator.of(context).pop(),
-              child: const Text("OK"),
-            ),
-          ],
-        );
-      },
-    );
-  }

  if (url.startsWith('mailto:')) {
    final Uri uri = Uri.parse(url);
    if (await canLaunchUrl(uri)) {
      await launchUrl(uri, mode: LaunchMode.externalApplication);
    } else {
      final email = url.replaceFirst('mailto:', '');
      Clipboard.setData(ClipboardData(text: email));
-      showCustomSnackBar(
+      showCustomSnackBar(
+        context: context,
        message: "Email copied to clipboard: $email",
        backgroundColor: Colors.grey.shade800,
        duration: const Duration(seconds: 2),
      );
      await launchUrlString(
        'https://mail.google.com/mail/u/0/?fs=1&to=$email&tf=cm',
        mode: LaunchMode.externalApplication,
      );
    }
  } else if (url.startsWith('sms:')) {
    final Uri smsUri = Uri.parse(url);
    if (await canLaunchUrl(smsUri)) {
      await launchUrl(smsUri, mode: LaunchMode.externalApplication);
    } else {
      final phone = url.replaceFirst('sms:', '');
      Clipboard.setData(ClipboardData(text: phone));
-      showCustomSnackBar(
+      showCustomSnackBar(
+        context: context,
        message: "Phone number copied to clipboard: $phone",
        backgroundColor: Colors.grey.shade800,
        duration: const Duration(seconds: 2),
      );
    }
  } else {
    if (await canLaunchUrlString(url)) {
      await launchUrlString(url, mode: LaunchMode.externalApplication);
    } else if (retryCount < maxRetryAttempts) {
      Clipboard.setData(ClipboardData(text: url));
      if (ScaffoldMessenger.of(context).mounted) {
-        showCustomSnackBar(
+        showCustomSnackBar(
+          context: context,
          message: "Couldn't open $url. Copied to clipboard.",
          backgroundColor: Colors.redAccent,
          duration: const Duration(seconds: 2),
          action: SnackBarAction(
            label: "Retry",
            textColor: Colors.white,
            onPressed: () async {
              await launchURL(context, url, retryCount: retryCount + 1);
            },
          ),
        );
      } else {
        showFallbackDialog(context, "Failed to open the URL");
      }
    } else {
      if (ScaffoldMessenger.of(context).mounted) {
-        showCustomSnackBar(
+        showCustomSnackBar(
+          context: context,
          message: "Failed to open the URL after several attempts.",
          backgroundColor: Colors.redAccent,
          duration: const Duration(seconds: 2),
        );
      } else {
        showFallbackDialog(
          context,
          "Failed to open the URL after several attempts.",
        );
      }
    }
  }
}
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 456ea410fecf145205215f389cddeed2d55c0d78 and d46537aafe9c8a70f871b4b790857f3642a918d2.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `lib/utils/url_launcher.dart` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (6)</summary><blockquote>

<details>
<summary>lib/utils/url_launcher.dart (6)</summary>

`2-4`: **Good addition of necessary imports.**

The additional imports for Flutter services, Material components, and the URL launcher API are appropriate for the enhanced functionality being implemented.

---

`6-11`: **Improved function signature and retry mechanism.**

The function now properly returns `Future<void>` and includes a BuildContext parameter for UI interactions, as well as a retry mechanism with a maximum attempt limit. These changes align with Flutter best practices for asynchronous operations.

---

`13-36`: **Well-structured helper function for SnackBar notifications.**

The `showCustomSnackBar` helper function is well-designed with appropriate parameters for customization. The floating behavior and styling improve user experience.

---

`73-85`: **Good SMS URL handling implementation.**

The SMS URL handling follows the same pattern as email handling, providing a clipboard fallback when the SMS app can't be launched. This implementation addresses the previous review comment requesting SMS URL support.

---

`87-106`: **Well-implemented error handling with retry mechanism.**

The error handling for regular URLs is robust, with:
1. Clipboard fallback
2. A retry mechanism with a maximum attempt count
3. Proper checks for ScaffoldMessenger availability
4. Fallback to dialog if SnackBar can't be shown

This implementation addresses previous review comments about limiting recursive retries and handling potential context-related errors.

---

`107-119`: **Good final fallback for maximum retry attempts.**

The code properly handles the scenario when maximum retry attempts are reached, showing either a SnackBar or dialog depending on context availability.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-03T10:18:17Z

Review comments:
diff_hunk: @@ -1,9 +1,31 @@
 import 'package:url_launcher/url_launcher_string.dart';
+import 'package:url_launcher/url_launcher.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter/material.dart';
 
-void launchURL(String url) async {
-  if (await canLaunchUrlString(url)) {
-    await launchUrlString(url);
+void launchURL(BuildContext context, String url) async {
+  if (url.startsWith('mailto:')) {
+    final Uri uri = Uri.parse(url);
+    if (await canLaunchUrl(uri)) {
+      await launchUrl(uri, mode: LaunchMode.externalApplication);
+    } else {
+      final email = url.replaceFirst('mailto:', '');
+      Clipboard.setData(ClipboardData(text: email));
+      ScaffoldMessenger.of(context).showSnackBar(
+        SnackBar(
+          content: Text("Email copied to clipboard: $email"),
+          duration: Duration(seconds: 1),
+        ),
+      );
+      await Future.delayed(Duration(seconds: 1));
+      await launchUrlString(
+        'https://mail.google.com/mail/u/0/?fs=1&to=$email&tf=cm',
+        mode: LaunchMode.externalApplication,
+      );
+    }
   } else {
-    throw 'Could not launch $url';
+    if (await canLaunchUrlString(url)) {
+      await launchUrlString(url);
+    }
   }
 }
path: lib/utils/url_launcher.dart
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Make the function return a Future for better async handling.**

The function is declared as `void` but contains async operations. It would be better to declare it as returning a Future.


```diff
-void launchURL(BuildContext context, String url) async {
+Future<void> launchURL(BuildContext context, String url) async {
  if (url.startsWith('mailto:')) {
    // ...existing code
  } else {
    // ...existing code
  }
}
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
Future<void> launchURL(BuildContext context, String url) async {
  if (url.startsWith('mailto:')) {
    final Uri uri = Uri.parse(url);
    if (await canLaunchUrl(uri)) {
      await launchUrl(uri, mode: LaunchMode.externalApplication);
    } else {
      final email = url.replaceFirst('mailto:', '');
      Clipboard.setData(ClipboardData(text: email));
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text("Email copied to clipboard: $email"),
          duration: Duration(seconds: 1),
        ),
      );
      await Future.delayed(Duration(seconds: 1));
      await launchUrlString(
        'https://mail.google.com/mail/u/0/?fs=1&to=$email&tf=cm',
        mode: LaunchMode.externalApplication,
      );
    }
  } else {
    if (await canLaunchUrlString(url)) {
      await launchUrlString(url);
    }
  }
}
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit a5f8103
created_at: 2025-04-01T20:03:50Z
author_association: NONE
diff_hunk: @@ -1,9 +1,56 @@
 import 'package:url_launcher/url_launcher_string.dart';
+import 'package:url_launcher/url_launcher.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter/material.dart';
 
-void launchURL(String url) async {
-  if (await canLaunchUrlString(url)) {
-    await launchUrlString(url);
+Future<void> launchURL(BuildContext context, String url) async {
+  if (url.startsWith('mailto:')) {
+    final Uri uri = Uri.parse(url);
+    if (await canLaunchUrl(uri)) {
+      await launchUrl(uri, mode: LaunchMode.externalApplication);
+    } else {
+      final email = url.replaceFirst('mailto:', '');
+      Clipboard.setData(ClipboardData(text: email));
+      ScaffoldMessenger.of(context).showSnackBar(
+        SnackBar(
+          content: Text("Email copied to clipboard: $email"),
+          duration: Duration(seconds: 1),
+        ),
+      );
+      await launchUrlString(
+        'https://mail.google.com/mail/u/0/?fs=1&to=$email&tf=cm',
+        mode: LaunchMode.externalApplication,
+      );
+    }
   } else {
-    throw 'Could not launch $url';
+    if (await canLaunchUrlString(url)) {
+      await launchUrlString(url);
+    } else {
+      ScaffoldMessenger.of(context).showSnackBar(
+        SnackBar(
+          content: Text(
+            "Failed to open the URL: $url",
+            style: const TextStyle(
+              color: Colors.white,
+              fontWeight: FontWeight.bold,
+            ),
+          ),
+          backgroundColor: Colors.redAccent,
+          behavior: SnackBarBehavior.floating,
+          shape: RoundedRectangleBorder(
+            borderRadius: BorderRadius.circular(10),
+          ),
+          duration: const Duration(seconds: 2),
+          margin: const EdgeInsets.all(16),
+          action: SnackBarAction(
+            label: "Retry",
+            textColor: Colors.white,
+            onPressed: () async{
+              await launchURL(context, url);
+            },
+          ),
+        ),
+      );
+    }
path: lib/utils/url_launcher.dart
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Improved error handling with user feedback and retry option.**

The error handling has been significantly enhanced:
1. Shows a well-styled SnackBar notification if URL launching fails
2. Includes a retry button that allows users to attempt launching again
3. Provides clear feedback with the problematic URL displayed

However, there's a potential issue with the retry mechanism:


The retry action calls the same function recursively with identical parameters. If the URL cannot be launched, this could lead to multiple stacked error messages. Consider adding a retry counter or alternative action after one or two failed attempts.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if there are any failure handling patterns elsewhere in the codebase
rg -A 5 "Retry|retry|failed to|couldn't|cannot" --type dart
```

Length of output: 3344

---



</details>

**Action Required: Limit Recursive Retry in URL Launching**

The current implementation in `lib/utils/url_launcher.dart` (lines 26-54) uses a recursive retry mechanism via the SnackBar’s "Retry" action. If the URL remains inaccessible, this could potentially lead to multiple stacked error messages. Please consider adding a retry counter or an alternative fallback strategy after one or two failed attempts.

- **Issue Location:** `lib/utils/url_launcher.dart` (lines 26-54)
- **Suggestion:** Modify the `launchURL` function to include a retry counter so that after a predefined number of retries the user is either informed or an alternative action is taken.

For example, you might adjust your function as follows:

```dart
Future<void> launchURL(BuildContext context, String url, {int retryCount = 0}) async {
  const int maxRetryAttempts = 2;

  if (await canLaunchUrlString(url)) {
    await launchUrlString(url);
  } else if (retryCount < maxRetryAttempts) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text(
          "Failed to open the URL: $url (Attempt ${retryCount + 1} of $maxRetryAttempts)",
          style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
        ),
        backgroundColor: Colors.redAccent,
        behavior: SnackBarBehavior.floating,
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
        duration: const Duration(seconds: 2),
        margin: const EdgeInsets.all(16),
        action: SnackBarAction(
          label: "Retry",
          textColor: Colors.white,
          onPressed: () async {
            await launchURL(context, url, retryCount: retryCount + 1);
          },
        ),
      ),
    );
  } else {
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(
        content: Text(
          "Failed to open the URL after several attempts.",
          style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
        ),
        backgroundColor: Colors.redAccent,
        behavior: SnackBarBehavior.floating,
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
        duration: Duration(seconds: 2),
        margin: EdgeInsets.all(16),
      ),
    );
  }
}
```

This adjustment prevents unbounded recursion and avoids overlapping SnackBars on repeated failures.

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 427636a
created_at: 2025-04-02T19:59:44Z
author_association: NONE
diff_hunk: @@ -1,9 +1,78 @@
 import 'package:url_launcher/url_launcher_string.dart';
+import 'package:url_launcher/url_launcher.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter/material.dart';
 
-void launchURL(String url) async {
-  if (await canLaunchUrlString(url)) {
-    await launchUrlString(url);
+Future<void> launchURL(
+  BuildContext context,
+  String url, {
+  int retryCount = 0,
+}) async {
+  const int maxRetryAttempts = 2;
+  if (url.startsWith('mailto:')) {
+    final Uri uri = Uri.parse(url);
+    if (await canLaunchUrl(uri)) {
+      await launchUrl(uri, mode: LaunchMode.externalApplication);
+    } else {
+      final email = url.replaceFirst('mailto:', '');
+      Clipboard.setData(ClipboardData(text: email));
+      ScaffoldMessenger.of(context).showSnackBar(
+        SnackBar(
+          content: Text("Email copied to clipboard: $email"),
+          duration: Duration(seconds: 1),
+        ),
+      );
+      await launchUrlString(
+        'https://mail.google.com/mail/u/0/?fs=1&to=$email&tf=cm',
+        mode: LaunchMode.externalApplication,
+      );
+    }
   } else {
-    throw 'Could not launch $url';
+    if (await canLaunchUrlString(url)) {
+      await launchUrlString(url);
+    } else if (retryCount < maxRetryAttempts) {
+      Clipboard.setData(ClipboardData(text: url));
+      ScaffoldMessenger.of(context).showSnackBar(
+        SnackBar(
+          content: Text(
+            "Couldn't open $url. Copied to clipboard.",
+            style: const TextStyle(
+              color: Colors.white,
+              fontWeight: FontWeight.bold,
+            ),
+          ),
+          backgroundColor: Colors.redAccent,
+          behavior: SnackBarBehavior.floating,
+          shape: RoundedRectangleBorder(
+            borderRadius: BorderRadius.circular(10),
+          ),
+          duration: const Duration(seconds: 2),
+          margin: const EdgeInsets.all(16),
+          action: SnackBarAction(
+            label: "Retry",
+            textColor: Colors.white,
+            onPressed: () async {
+              await launchURL(context, url, retryCount: retryCount + 1);
+            },
+          ),
+        ),
+      );
+    } else {
+      ScaffoldMessenger.of(context).showSnackBar(
+        SnackBar(
+          content: Text(
+            "Failed to open the URL after several attempts.",
+            style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
+          ),
+          backgroundColor: Colors.redAccent,
+          behavior: SnackBarBehavior.floating,
+          shape: RoundedRectangleBorder(
+            borderRadius: BorderRadius.circular(10),
+          ),
+          duration: Duration(seconds: 2),
+          margin: EdgeInsets.all(16),
+        ),
+      );
+    }
   }
 }
path: lib/utils/url_launcher.dart
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**Note from PR objectives: SMS Intent functionality not visible in this file.**

The PR objectives mentioned adding SMS Intent functionality, but I don't see handling for SMS URLs (e.g., `sms:`) in this file. This might be implemented in the AndroidManifest.xml file mentioned in the AI summary, but consider adding SMS URL handling here for completeness.

---

🌐 Web query:

```web
How to handle SMS URLs in Flutter url_launcher?
```

💡 Result:

To handle SMS URLs in Flutter using the `url_launcher` package, follow these steps:

---

## **SMS URL Implementation**
1. **Add Dependencies**  
   Include `url_launcher` in your `pubspec.yaml`:
   ```yaml
   dependencies:
     url_launcher: ^6.3.1
   ```
   Run `flutter pub get`[1][2][6].

2. **Platform-Specific Configuration**  
   - **Android**: Add SMS intent queries to `AndroidManifest.xml`:
     ```xml
     <queries>
       <intent>
         <action android:name="android.intent.action.VIEW" />
         <data android:scheme="sms" />
       </intent>
     </queries>
     ```
   - **iOS**: Add `LSApplicationQueriesSchemes` to `Info.plist`:
     ```xml
     <key>LSApplicationQueriesSchemes</key>
     <array>
       <string>sms</string>
     </array>
     ```
   [2][6][8]

3. **Construct SMS URI**  
   Use `Uri` to encode phone numbers and message bodies:
   ```dart
   final Uri smsUri = Uri(
     scheme: 'sms',
     path: '+919952313535',
     queryParameters: {'body': 'Hello, this is a test message!'},
   );
   ```
   For special characters (e.g., `&`, `?`), use `Uri.encodeQueryComponent`[6][9]:
   ```dart
   String message = 'Message with & symbol';
   String encodedMessage = Uri.encodeQueryComponent(message);
   ```

4. **Launch SMS App**  
   Check support and launch the URL:
   ```dart
   ElevatedButton(
     onPressed: () async {
       if (await canLaunchUrl(smsUri)) {
         await launchUrl(smsUri);
       } else {
         print('SMS not supported');
       }
     },
     child: Text('Send SMS'),
   )
   ```
   Use `try`/`catch` for error handling[4][8].

---

## **Key Considerations**
- **Android 11+ and iOS 9+**: Ensure manifest/plist configurations to avoid `canLaunchUrl` returning `false`[2][6][8].
- **Encoding**: Always encode dynamic content in message bodies to prevent URI parsing errors (e.g., spaces, `&`, `#`)[9].
- **Deprecated Methods**: Avoid `launch()` and use `launchUrl(Uri)` for modern implementations[6][8].

---

This approach ensures cross-platform compatibility and proper handling of SMS URLs with message bodies, while adhering to platform-specific requirements.

Citations:

- 1: https://codesundar.com/flutter-url-launcher-example/
- 2: https://www.blog.finotes.com/post/integrating-url_launcher-in-flutter-apps
- 3: https://www.cybrosys.com/blog/how-to-install-and-configure-flutter-url-launcher
- 4: https://ravipatel.hashnode.dev/seamlessly-open-urls-in-flutter-with-the-urllauncher-package
- 5: https://github.com/flutter/flutter/issues/18823
- 6: https://pub.dev/packages/url_launcher
- 7: https://www.youtube.com/watch?v=6eF6d8zKGqk
- 8: https://www.youtube.com/watch?v=B-7nXlGD8xk
- 9: https://github.com/flutter/flutter/issues/156281
- 10: https://pub.dev/packages/url_launcher/example

---



</details>

**Action Required: Add SMS URL Scheme Handling**

The current implementation of `launchURL` covers `mailto:` and general URLs but omits specific handling for URL schemes starting with `sms:`. According to best practices for the `url_launcher` package, adding a branch similar to the `mailto:` handling will ensure completeness. Although platform-specific configurations (AndroidManifest.xml and Info.plist) may have been updated per the PR objectives, consider extending the function as follows:

- **Add an `else if` branch for SMS URLs:**  
  ```dart
  else if (url.startsWith('sms:')) {
    final Uri smsUri = Uri.parse(url);
    if (await canLaunchUrl(smsUri)) {
      await launchUrl(smsUri, mode: LaunchMode.externalApplication);
    } else {
      final phone = url.replaceFirst('sms:', '');
      Clipboard.setData(ClipboardData(text: phone));
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text("SMS number copied to clipboard: $phone"),
          duration: Duration(seconds: 1),
        ),
      );
    }
  }
  ```
- **Ensure platform configurations are in place:**  
  Verify that the AndroidManifest.xml and Info.plist include the necessary intent queries and URL schemes for SMS handling.

Please update the function accordingly and verify cross-platform behavior.

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 149bbf7
created_at: 2025-04-02T21:04:54Z
author_association: NONE
diff_hunk: @@ -1,9 +1,93 @@
 import 'package:url_launcher/url_launcher_string.dart';
+import 'package:url_launcher/url_launcher.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter/material.dart';
 
-void launchURL(String url) async {
-  if (await canLaunchUrlString(url)) {
-    await launchUrlString(url);
+Future<void> launchURL(
+  BuildContext context,
+  String url, {
+  int retryCount = 0,
+}) async {
+  const int maxRetryAttempts = 2;
+
+  void showCustomSnackBar({
+    required String message,
+    Color backgroundColor = Colors.grey,
+    Duration duration = const Duration(seconds: 2),
+    SnackBarAction? action,
+  }) {
+    ScaffoldMessenger.of(context).showSnackBar(
+      SnackBar(
+        content: Text(
+          message,
+          style: const TextStyle(
+            color: Colors.white,
+            fontWeight: FontWeight.bold,
+          ),
+        ),
+        backgroundColor: backgroundColor,
+        behavior: SnackBarBehavior.floating,
+        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
+        duration: duration,
+        margin: const EdgeInsets.all(16),
+        action: action,
+      ),
+    );
+  }
+
+  if (url.startsWith('mailto:')) {
+    final Uri uri = Uri.parse(url);
+    if (await canLaunchUrl(uri)) {
+      await launchUrl(uri, mode: LaunchMode.externalApplication);
+    } else {
+      final email = url.replaceFirst('mailto:', '');
+      Clipboard.setData(ClipboardData(text: email));
+      showCustomSnackBar(
+        message: "Email copied to clipboard: $email",
+        backgroundColor: Colors.grey.shade800,
+        duration: const Duration(seconds: 2),
+      );
+      await launchUrlString(
+        'https://mail.google.com/mail/u/0/?fs=1&to=$email&tf=cm',
+        mode: LaunchMode.externalApplication,
+      );
+    }
+  } else if (url.startsWith('sms:')) {
+    final Uri smsUri = Uri.parse(url);
+    if (await canLaunchUrl(smsUri)) {
+      await launchUrl(smsUri, mode: LaunchMode.externalApplication);
+    } else {
+      final phone = url.replaceFirst('sms:', '');
+      Clipboard.setData(ClipboardData(text: phone));
+      showCustomSnackBar(
+        message: "Phone number copied to clipboard: $phone",
+        backgroundColor: Colors.grey.shade800,
+        duration: const Duration(seconds: 2),
+      );
+    }
   } else {
-    throw 'Could not launch $url';
+    if (await canLaunchUrlString(url)) {
+      await launchUrlString(url, mode: LaunchMode.externalApplication);
+    } else if (retryCount < maxRetryAttempts) {
+      Clipboard.setData(ClipboardData(text: url));
+      showCustomSnackBar(
+        message: "Couldn't open $url. Copied to clipboard.",
+        backgroundColor: Colors.redAccent,
+        duration: const Duration(seconds: 2),
+        action: SnackBarAction(
+          label: "Retry",
+          textColor: Colors.white,
+          onPressed: () async {
+            await launchURL(context, url, retryCount: retryCount + 1);
+          },
+        ),
+      );
+    } else {
+      showCustomSnackBar(
+        message: "Failed to open the URL after several attempts.",
+        backgroundColor: Colors.redAccent,
+        duration: const Duration(seconds: 2),
+      );
+    }
path: lib/utils/url_launcher.dart
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Handle potential context-related errors**

The current implementation assumes that the provided context always has a Scaffold ancestor. Consider adding a check to handle cases where it doesn't, to prevent potential runtime errors.

```diff
 } else {
   if (await canLaunchUrlString(url)) {
     await launchUrlString(url, mode: LaunchMode.externalApplication);
   } else if (retryCount < maxRetryAttempts) {
     Clipboard.setData(ClipboardData(text: url));
-    showCustomSnackBar(
-      message: "Couldn't open $url. Copied to clipboard.",
-      backgroundColor: Colors.redAccent,
-      duration: const Duration(seconds: 2),
-      action: SnackBarAction(
-        label: "Retry",
-        textColor: Colors.white,
-        onPressed: () async {
-          await launchURL(context, url, retryCount: retryCount + 1);
-        },
-      ),
-    );
+    // Check if context has a Scaffold ancestor before showing SnackBar
+    if (ScaffoldMessenger.of(context).mounted) {
+      showCustomSnackBar(
+        message: "Couldn't open $url. Copied to clipboard.",
+        backgroundColor: Colors.redAccent,
+        duration: const Duration(seconds: 2),
+        action: SnackBarAction(
+          label: "Retry",
+          textColor: Colors.white,
+          onPressed: () async {
+            await launchURL(context, url, retryCount: retryCount + 1);
+          },
+        ),
+      );
+    } else {
+      print("Could not show SnackBar: No Scaffold ancestor found");
+      // Consider alternative feedback mechanism here
+    }
   } else {
-    showCustomSnackBar(
-      message: "Failed to open the URL after several attempts.",
-      backgroundColor: Colors.redAccent,
-      duration: const Duration(seconds: 2),
-    );
+    if (ScaffoldMessenger.of(context).mounted) {
+      showCustomSnackBar(
+        message: "Failed to open the URL after several attempts.",
+        backgroundColor: Colors.redAccent,
+        duration: const Duration(seconds: 2),
+      );
+    } else {
+      print("Could not show SnackBar: No Scaffold ancestor found");
+      // Consider alternative feedback mechanism here
+    }
   }
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    if (await canLaunchUrlString(url)) {
      await launchUrlString(url, mode: LaunchMode.externalApplication);
    } else if (retryCount < maxRetryAttempts) {
      Clipboard.setData(ClipboardData(text: url));
      // Check if context has a Scaffold ancestor before showing SnackBar
      if (ScaffoldMessenger.of(context).mounted) {
        showCustomSnackBar(
          message: "Couldn't open $url. Copied to clipboard.",
          backgroundColor: Colors.redAccent,
          duration: const Duration(seconds: 2),
          action: SnackBarAction(
            label: "Retry",
            textColor: Colors.white,
            onPressed: () async {
              await launchURL(context, url, retryCount: retryCount + 1);
            },
          ),
        );
      } else {
        print("Could not show SnackBar: No Scaffold ancestor found");
        // Consider alternative feedback mechanism here
      }
    } else {
      if (ScaffoldMessenger.of(context).mounted) {
        showCustomSnackBar(
          message: "Failed to open the URL after several attempts.",
          backgroundColor: Colors.redAccent,
          duration: const Duration(seconds: 2),
        );
      } else {
        print("Could not show SnackBar: No Scaffold ancestor found");
        // Consider alternative feedback mechanism here
      }
    }
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit d46537a
created_at: 2025-04-03T09:34:51Z
author_association: NONE
diff_hunk: @@ -1,9 +1,122 @@
 import 'package:url_launcher/url_launcher_string.dart';
+import 'package:url_launcher/url_launcher.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter/material.dart';
 
-void launchURL(String url) async {
-  if (await canLaunchUrlString(url)) {
-    await launchUrlString(url);
+Future<void> launchURL(
+  BuildContext context,
+  String url, {
+  int retryCount = 0,
+}) async {
+  const int maxRetryAttempts = 2;
+
+  void showCustomSnackBar({
+    required String message,
+    Color backgroundColor = Colors.grey,
+    Duration duration = const Duration(seconds: 2),
+    SnackBarAction? action,
+  }) {
+    ScaffoldMessenger.of(context).showSnackBar(
+      SnackBar(
+        content: Text(
+          message,
+          style: const TextStyle(
+            color: Colors.white,
+            fontWeight: FontWeight.bold,
+          ),
+        ),
+        backgroundColor: backgroundColor,
+        behavior: SnackBarBehavior.floating,
+        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
+        duration: duration,
+        margin: const EdgeInsets.all(16),
+        action: action,
+      ),
+    );
+  }
+
+  void showFallbackDialog(BuildContext context, String message) {
+    showDialog(
+      context: context,
+      builder: (BuildContext context) {
+        return AlertDialog(
+          title: const Text("Notification"),
+          content: Text(message),
+          actions: [
+            TextButton(
+              onPressed: () => Navigator.of(context).pop(),
+              child: const Text("OK"),
+            ),
+          ],
+        );
+      },
+    );
+  }
+
+  if (url.startsWith('mailto:')) {
+    final Uri uri = Uri.parse(url);
+    if (await canLaunchUrl(uri)) {
+      await launchUrl(uri, mode: LaunchMode.externalApplication);
+    } else {
+      final email = url.replaceFirst('mailto:', '');
+      Clipboard.setData(ClipboardData(text: email));
+      showCustomSnackBar(
+        message: "Email copied to clipboard: $email",
+        backgroundColor: Colors.grey.shade800,
+        duration: const Duration(seconds: 2),
+      );
+      await launchUrlString(
+        'https://mail.google.com/mail/u/0/?fs=1&to=$email&tf=cm',
+        mode: LaunchMode.externalApplication,
+      );
path: lib/utils/url_launcher.dart
user: login: coderabbitai[bot]
body: _💡 Verification agent_

<details>
<summary>❓ Verification inconclusive</summary>

**Consider handling potential Gmail fallback failure.**

The Gmail fallback URL is used without checking if it can be launched, which might result in another failure if the device doesn't have a compatible browser.

---

🏁 Script executed:

```shell
#!/bin/bash
# Check if there's any error handling for URL launching failures elsewhere in the codebase
rg "launchUrlString.*catch" --type dart
```

Length of output: 41

---



</details>

**Action Required: Add Error Handling for Gmail Fallback URL**

In `lib/utils/url_launcher.dart` (lines 67–71), the Gmail fallback URL is launched via `launchUrlString` without any check to confirm it can actually be handled by the device. Our code search for error handling (using patterns like `launchUrlString` with a `catch` block) returned no results, suggesting that no protective mechanism is in place.  
 
**Recommendation:**
- Verify that the URL can be launched (e.g., with a `canLaunchUrlString` check) before calling `launchUrlString`.
- Alternatively, wrap the `launchUrlString` call in a try-catch block to gracefully handle any failures if the device does not have a compatible browser.

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit d9aa80f
created_at: 2025-04-03T10:18:16Z
author_association: NONE

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-04-01T19:48:42Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
This pull request makes several updates to URL handling within the project. The AndroidManifest’s `<queries>` section is modified to adjust intent filters for URL schemes, including changing the `tel` intent action, adding an intent for `sms`, and preserving `mailto` and `https` configurations. In addition, multiple UI components and the utility function now pass a `BuildContext` to the `launchURL` function, enabling context-aware operations. Finally, the dependency for the `url_launcher` package is upgraded in the configuration file.

## Changes

| File(s) | Change Summary |
|---------|----------------|
| `android/.../AndroidManifest.xml` | Modified `<queries>` section: updated intent filters for `tel` (changed to `VIEW`), added new intent for `sms`, and maintained intents for `mailto` and `https`. |
| `lib/ui/components/cv_social_card.dart`, `lib/ui/views/about/components/contributor_avatar.dart`, `lib/ui/views/contributors/components/contributors_donate_card.dart`, `lib/ui/views/ib/ib_page_view.dart` | Updated `launchURL` calls to include a `context` parameter, modifying the method invocations across UI components. |
| `lib/utils/url_launcher.dart` | Updated `launchURL` function signature to accept `BuildContext`, enhanced email (`mailto:`) handling, and added snackbar notification fallback. |
| `pubspec.yaml` | Upgraded `url_launcher` dependency version from `^6.1.11` to `^6.3.0`. |

## Possibly related PRs
- **CircuitVerse/mobile-app#313**: Involved similar modifications to the `<queries>` section in the AndroidManifest, addressing URL scheme handling for intents.

<!-- walkthrough_end -->


---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between d46537aafe9c8a70f871b4b790857f3642a918d2 and d9aa80f5ee199d5c6b43ae05a29c338c152320da.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `lib/utils/url_launcher.dart` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* lib/utils/url_launcher.dart

</details>

</details>
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=CircuitVerse/mobile-app&utm_content=319):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai plan` to trigger planning for file edits and PR creation.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-01T20:57:47Z
body: ## Pull Request Test Coverage Report for [Build 14205044429](https://coveralls.io/builds/73028978)

### Warning: This coverage report may be inaccurate.

This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.

- For more information on this, see <a target="_blank" href="https://docs.coveralls.io/build-types#tracking-coverage-changes-with-pull_request-builds">Tracking coverage changes with pull request builds</a>.
- To avoid this issue with future PRs, see these <a target="_blank" href="https://docs.coveralls.io/build-types#recommended-ci-configurations">Recommended CI Configurations</a>.
- For a quick fix, <a target="_blank" href="https://github.blog/changelog/2022-02-03-more-ways-to-keep-your-pull-request-branch-up-to-date/#update-your-pull-request-branch-by-rebasing">rebase this PR at GitHub</a>. Your next report should be accurate.

### Details

* **0** of **20**   **(0.0%)**  changed or added relevant lines in **5** files are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage decreased (**-0.1%**) to **64.596%**

---

|  Changes Missing Coverage | Covered Lines | Changed/Added Lines | % |
| :-----|--------------|--------|---: |
| [lib/ui/components/cv_social_card.dart](https://coveralls.io/builds/73028978/source?filename=lib%2Fui%2Fcomponents%2Fcv_social_card.dart#L23) | 0 | 1 | 0.0%
| [lib/ui/views/about/components/contributor_avatar.dart](https://coveralls.io/builds/73028978/source?filename=lib%2Fui%2Fviews%2Fabout%2Fcomponents%2Fcontributor_avatar.dart#L15) | 0 | 1 | 0.0%
| [lib/ui/views/contributors/components/contributors_donate_card.dart](https://coveralls.io/builds/73028978/source?filename=lib%2Fui%2Fviews%2Fcontributors%2Fcomponents%2Fcontributors_donate_card.dart#L24) | 0 | 1 | 0.0%
| [lib/ui/views/ib/ib_page_view.dart](https://coveralls.io/builds/73028978/source?filename=lib%2Fui%2Fviews%2Fib%2Fib_page_view.dart#L131) | 0 | 1 | 0.0%
| [lib/utils/url_launcher.dart](https://coveralls.io/builds/73028978/source?filename=lib%2Futils%2Furl_launcher.dart#L6) | 0 | 16 | 0.0%
<!-- | **Total:** | **0** | **20** | **0.0%** | -->


|  Totals | [![Coverage Status](https://coveralls.io/builds/73028978/badge)](https://coveralls.io/builds/73028978) |
| :-- | --: |
| Change from base [Build 14203894044](https://coveralls.io/builds/73027494): |  -0.1% |
| Covered Lines: | 4003 |
| Relevant Lines: | 6197 |

---
##### 💛  - [Coveralls](https://coveralls.io)

author_association: CONTRIBUTOR
user: login: hardik17771
created_at: 2025-04-02T19:05:25Z
body: Hey @NitinSohal ,thanks for the PR. Just one thing, I guess the 1 sec delay is not necessary after address is copied, it may affect the UX. Also, add a error message or mechanism if the url fails to launch as well.
author_association: MEMBER
user: login: NitinSohal
created_at: 2025-04-02T22:25:50Z
body: Hey @hardik17771  I have included all the necessary functionalities in this commit  
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-06-13T12:09:58Z
body: :tada: This PR is included in version 1.1.0 :tada:

The release is available on:
- `v1.1.0`
- [GitHub release](https://github.com/CircuitVerse/mobile-app/releases/tag/v1.1.0)

Your **[semantic-release](https://github.com/semantic-release/semantic-release)** bot :package::rocket:
author_association: CONTRIBUTOR