Can't send an email through url_launcher - flutter

I try to send an email whenever a user clicks on an individual's email address. However, I ran into an error. I am testing through the Android emulator. Doesn't it work since there is no mail app on the emulator?
This is the error I get:
PlatformException (PlatformException(ACTIVITY_NOT_FOUND, No Activity found to handle intent { mailto:example#gmail.com?subject=Default%20subject&body=Default%20body }, null, null))
Here is the code:
sendEmail(String email) async {
String? encodeQueryParameters(Map<String, String> params) {
return params.entries
.map((e) =>
'${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}')
.join('&');
}
final Uri emailLaunchUri = Uri(
scheme: 'mailto',
path: '$email',
query: encodeQueryParameters(<String, String>{
'subject': 'Default subject',
'body': 'Default body',
}));
await launch(emailLaunchUri.toString());
}
I call it like this:
onTap: () {
setState(() {
sendEmail('example#gmail.com');
});
},
I have configured Android and IOS.

For API >=30, you need to add in AndroidManifest.xml.
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
<intent>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="*/*" />
</intent>
</queries>
You can find more info from Configuration section.

dependencies:
flutter_email_sender: ^5.0.2
Example:
final Email email = Email(
body: 'Email body',
subject: 'Email subject',
recipients: ['example#example.com'],
cc: ['cc#example.com'],
bcc: ['bcc#example.com'],
attachmentPaths: ['/path/to/attachment.zip'],
isHTML: false,
);
await FlutterEmailSender.send(email);
Setup:
With Android 11, package visibility is introduced that alters the ability to query installed applications and packages on a user’s device. To enable your application to get visibility into the packages you will need to add a list of queries into your AndroidManifest.xml.
<manifest package="com.mycompany.myapp">
<queries>
<intent>
<action android:name="android.intent.action.SENDTO" />
<data android:scheme="mailto" />
</intent>
</queries>
</manifest>

I use below function for handling email try using it, for sending email through android/ ios using flutter and launch function
launchMail(String toMailId, String subject, String body) async {
final Uri _emailLaunchUri = Uri(
scheme: 'mailto', path: toMailId, query: "subject=$subject&body=$body");
String a = _emailLaunchUri
.toString()
.replaceAll("+", "%20")
.replaceAll("%2520", "%20");
if (await canLaunch(a)) {
await launch(a);
} else {
throw 'Could not launch $a';
}
}

Related

How to connect Phantom wallet to a Flutter app using deep links?

I'm making a flutter mobile app where I want to connect the user to the Phantom wallet using the connect deep link and then set the redirect_link as a Firebase dynamic link for the app, however I am not getting a response from the Phantom wallet as a query parameters. Any help will be highly appreciated! Thanks.
Install uni_links and url_luncher pakage
add this intent to androidManifest
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- Accepts URIs that begin with https://YOUR_HOST -->
<data
android:scheme="https"
android:host="[YOUR_HOST]" />
</intent-filter>
then create queryParameter like
Map<String, dynamic> queryParameters = {
"dapp_encryption_public_key":
base58.encode(Uint8List.fromList(keypair.publicKey)),
"cluster": "devnet",
"app_url": "https://google.com",
"redirect_link":
"app://flutterdapp?handleQuery=onConnect}",
};
then lunchUrl
final url =Uri(
scheme: "https",
host: "phantom.app",
path: "/ul/v1/onConnect",
queryParameters: queryParameters,
);
launchUrl(
url,
mode: LaunchMode.externalNonBrowserApplication,
);
and recive data from phantom like
StreamSubscription _sub;
Future<void> initUniLinks() async {
// ... check initialLink
// Attach a listener to the stream
_sub = linkStream.listen((String? link) {
// Parse the link and warn the user, if it is not correct
}, onError: (err) {
// Handle exception by warning the user their action did not succeed
});
// NOTE: Don't forget to call _sub.cancel() in dispose()
}
// ...
hope help you

Flutter oauth2 login with discord. redirect doesn't work

I'm looking to make a button login with discord. For that I use flutter_web_auth but discord shows me an error with the redirect URI and appear black screenshot on device
<activity android:name="com.linusu.flutter_web_auth.CallbackActivity" >
<intent-filter android:label="flutter_web_auth">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" android:host="com.example.hola" />
</intent-filter>
</activity>
void loginWithDiscord() async {
// App specific variables
const clientId = 'myClientId' ;
const callbackUrlScheme = 'com.area';
const redirectUri = 'com.area://home'; // OR 'com.area:/';
// Construct the url
final url = Uri.https('discord.com', '/api/oauth2/authorize', {
'response_type': 'code',
'client_id': clientId,
'redirect_uri': redirectUri,
'scope': 'identify',
});
// Present the dialog to the user
final result = await FlutterWebAuth.authenticate(
url: url.toString(), callbackUrlScheme: callbackUrlScheme);
// Extract code from resulting url
final code = Uri.parse(result).queryParameters['code'];
// Use this code to get an access token
final response = await http
.post(Uri.parse('https://discord.com/api/oauth2/authorize'), body: {
'client_id': clientId,
'redirect_uri': redirectUri,
'grant_type': 'authorization_code',
'code': code,
});
// Get the access token from the response
final accessToken = jsonDecode(response.body)['access_token'] as String;
print(accessToken);
}
Thanks
redirect_uri is whatever URL you registered when creating your application, url-encoded
See Authorization Code Grant for details

I/UrlLauncher(17669): component name for (url) is null

Why does it throw an error and give me the link is empty even though the link exists?
And when I use launch (url) alone, the link opens without any problems
String StateUrl = 'View App' ;
var url = 'https://www.youtube.com/watch?v=-k0IXjCHObw' ;
body: Column(
children: [
Text(StateUrl),
Center(
child: ElevatedButton.icon(
onPressed: () async{
try {
await canLaunch(url) ?
await launch(url):
throw 'Error';
} catch(e){
setState(() {
StateUrl = e.toString() ;
});
}
},
icon: const Icon(FontAwesomeIcons.link),
label: const Text('View Url')
),
),
],
),
Performing hot reload
D/EGL_emulation(17669): app_time_stats: avg=17852.65ms min=658.78ms
max=35046.52ms count=2 I/UrlLauncher(17669): component name for
https://www.youtube.com/watch?v=-k0IXjCHObw is null
D/EGL_emulation(17669): app_time_stats: avg=8279.72ms min=8279.72ms
max=8279.72ms count=1
You have to add <queries> elements to you AndroidManifest.xml file.
more info
try using
await launch(url);
instead of
if (await canLaunch(url)) { print("launching $url"); await launch(url); } else { throw 'Could not launch maps'; }
it seems theres a problem with canLaunch(url) function
With link can handle via other app like youtube, spreadsheets, document...
from android 11 (API 30) and above you must add this permission to AndroidManifest.xml
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
please refer:
https://developer.android.com/training/package-visibility/declaring
don't use canLaunch with videos URL just use try/catch
If you come here looking for why your email link (mailto:email#example.com) doesn't work, then try this out.
Don't call canLaunch for mailto links - use it only for http and https!
Since I have both http(s) and mailto links in my app, I use the try-catch block.
Here is the full function:
class UrlHandler {
/// Attempts to open the given [url] in in-app browser. Returns `true` after successful opening, `false` otherwise.
static Future<bool> open(String url) async {
try {
await launch(
url,
enableJavaScript: true,
);
return true;
} catch (e) {
log(e.toString());
return false;
}
}
}
You can use this code, it works for me. Check it out:
_launchURL() async {
const url = 'https://en.wikipedia.org/wiki/Body_mass_index';
if (await launch(url)) {
await canLaunch(url);
} else {
throw 'Could not launch $url';
}
}
and use this _launchURL() function in onPressed();
Try is like this:
try {
if(await canLaunch(url)) await launch(url):
} catch(e){
setState(() {
StateUrl = e.toString() ;
});
throw e;}
},
maybe a little late, but i also had the same problem. The solution was so set an intent in the android manifest file. If this is done, the canLaunch() call will not fail, cause you allow the android system to query this url.
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" android:host="youtube.com" />
</intent>
</queries>
For comparison, the url launcher now prints following text to the console:
I/UrlLauncher( 2628): component name for <your youtube link> is {com.google.android.youtube/com.google.android.youtube.UrlActivity}
Further if you set the launchMode to LaunchMode.externalApplication the youtube app will launch, if installed.
Hope this helps.
Also Google updates his PolicyBytes and I think using
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
or
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
might lead to app rejects, if you can not explain in detail why you need to use those permissions.
Regards Max.
The url_launcher requires a Uri object to be passed instead of a string. Add a Uri.parse
String StateUrl = 'View App' ;
var url = 'https://www.youtube.com/watch?v=-k0IXjCHObw' ;
body: Column(
children: [
Text(StateUrl),
Center(
child: ElevatedButton.icon(
onPressed: () async{
try {
Uri uri = Uri.parse(url);
await canLaunch(uri) ?
await launch(uri):
throw 'Error';
} catch(e){
setState(() {
StateUrl = e.toString() ;
});
}
},
icon: const Icon(FontAwesomeIcons.link),
label: const Text('View Url')
),
),
],
),
for me the solution is
to copy and paste
<!-- Provide required visibility configuration for API level 30 and above -->
<queries>
<!-- If your app checks for SMS support -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="sms" />
</intent>
<!-- If your app checks for call support -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="tel" />
</intent>
</queries>
from official packages docs
but the problem is that the package removed the next lines from code snippet
<intent>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
</intent>
so add them first to the
I just used this and it worked...(nb:Dec 2022)
if (!await launchUrl(url)) {
throw 'Could not launch $url';
}
Try using await launch(url); instead of
if (await canLaunch(url)) {
print("launching $url");
await launch(url);
}
else {
throw 'Could not launch maps';
}
It seems theres a problem with canLaunch(url) function
Thank you for this Solution :)

flutter + firebase passwordless email login - dynamic link data returns null

I am trying to implement passwordless email login on my app.
After the link has been clicked on, the app returns from the background but the dynamic link is null.
This is how I send the mail (with the right values for my app of course):
user.sendSignInWithEmailLink(
email: _email,
androidInstallIfNotAvailable: true,
iOSBundleID: "com.company.appname",
androidMinimumVersion: "16",
androidPackageName: "com.company.appname",
url: "https://appname.page.link/email",
handleCodeInApp: true);
I also added the intent as follows:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:host="appname.page.link" android:scheme="http"/>
<data android:host="appname.page.link" android:scheme="https"/>
</intent-filter>
Tried different variations with the intent, and non helped, including writing the firebase
project url as the host like: project-name.firebaseapp.com
The data still comes back empty.
Any thoughts? am I missing something?
I've updated the firebase_dynamic_links package to the latest version (0.5.0+9),
while using the configuration below and it starts working.
Sending sign-in link configuration:
firebaseAuth.sendSignInWithEmailLink(
email: email,
url: "https://mydemoapp.page.link/email",
androidInstallIfNotAvailable: true,
androidMinimumVersion: '21',
androidPackageName: 'com.example.mydemoapp'
handleCodeInApp: true,
iOSBundleID: 'com.example.mydemoapp');
AndroidManifest.xml intent-filter configuration:
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<data android:host="mydemoapp.page.link" android:scheme="https"/>
<data android:host="mydemoapp.page.link" android:scheme="http"/>
</intent-filter>
Getting a dynamic link in Flutter.
The example comes form from https://pub.dev/packages/firebase_dynamic_links
If your app did not open from a dynamic link, getInitialLink() will return null. That's the reason why you have to FirebaseDynamicLinks.instance.onLink implemented in case the app is already opened.
void main() {
runApp(MaterialApp(
title: 'Dynamic Links Example',
routes: <String, WidgetBuilder>{
'/': (BuildContext context) => MyHomeWidget(), // Default home route
'/helloworld': (BuildContext context) => MyHelloWorldWidget(),
},
));
}
class MyHomeWidgetState extends State<MyHomeWidget> {
.
.
.
#override
void initState() {
super.initState();
this.initDynamicLinks();
}
void initDynamicLinks() async {
final PendingDynamicLinkData data = await FirebaseDynamicLinks.instance.getInitialLink();
final Uri deepLink = data?.link;
if (deepLink != null) {
Navigator.pushNamed(context, deepLink.path);
}
FirebaseDynamicLinks.instance.onLink(
onSuccess: (PendingDynamicLinkData dynamicLink) async {
final Uri deepLink = dynamicLink?.link;
if (deepLink != null) {
Navigator.pushNamed(context, deepLink.path);
}
},
onError: (OnLinkErrorException e) async {
print('onLinkError');
print(e.message);
}
);
}
.
.
.
}
The mydemoapp.page.link must be added to Authorized domains in Firebase console
Is it appname.page.link or appname.page.link/email you have to specify only one email in order to achieve that
If coming to this answer in March 2022, this error is happening again and it's a bug on Flutterfire.
Here is the issue with a Pull Request that hasn't been merged yet.

Flutter email sender

I have this error when I send an email from form in flutter.
Unhandled Exception: PlatformException(UNAVAILABLE, defualt mail app not available, null)
class _MyAppState extends State<MyApp> {
List<String> attachment = <String>[];
TextEditingController _subjectController =
TextEditingController(text: 'ct');
TextEditingController _bodyController = TextEditingController(
text: ''' a
''');
final GlobalKey<ScaffoldState> _scafoldKey = GlobalKey<ScaffoldState>();
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> send() async {
// Platform messages may fail, so we use a try/catch PlatformException.
final MailOptions mailOptions = MailOptions(
body: 'Ro',
subject: 'the Email Subject',
recipients: ['rodrigo#houlak.com'],
isHTML: true,
attachments: [ 'path/to/image.png', ],
);
await FlutterMailer.send(mailOptions);
String platformResponse;
try {
await FlutterMailer.send(mailOptions);
platformResponse = 'success';
} catch (error) {
platformResponse = error.toString();
}
if (!mounted) return;
_scafoldKey.currentState.showSnackBar(SnackBar(
content: Text(platformResponse),
));
}
I had the same issue on iPhone, it was caused because I hadn't set up the default iOS default Mail App.
Adding in AndroidManifest.xml this for me on Android solve the issue:
<application .... />
// add queries tag for mailto intent out side of application tag
<queries>
<intent>
<action android:name="android.intent.action.SENDTO" />
<data android:scheme="mailto" />
</intent>
</queries>
make sure the Android Gradle Plugin is higher then 4.1.0 you will find it inside android\build.gradle file some thing like this
dependencies { classpath 'com.android.tools.build:gradle:4.1.1' "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.google.gms:google-services:4.3.4' }
2 . add this to your android/app/src/main/AndroidManifest.xml file just after where you add the <uses-permission>
<queries> <intent> <action android:name="android.intent.action.SENDTO" /> <data android:scheme="mailto" /> </intent> </queries>
run flutter clean
run flutter pub get or you can run your code it will do this for you after that Flutter email sender package should works for you
this solve the issue for me