Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

url_launcher opens mailto link with plus(+) character in subject and body #75552

Closed
kashishkhullar opened this issue Feb 6, 2021 · 10 comments · Fixed by flutter/plugins#3817
Closed
Labels
found in release: 1.22 Found to occur in 1.22 has reproducible steps The issue has been confirmed reproducible and is ready to work on p: url_launcher Plugin to launch external applications P3 Issues that are less important to the Flutter project package flutter/packages repository. See also p: labels. r: timeout Issue is closed due to author not providing the requested details in time

Comments

@kashishkhullar
Copy link

Steps to Reproduce

  1. Create a new flutter app
  2. Add url_launcher package
  3. Create URI
  4. Create a button and call lauch( uri_instance.toString())
  5. Clicking the button would opens Gmail in my case.

Code

// To create email with params
final Uri _emailLaunchUri = Uri(
    scheme: 'mailto',
    path: GlobalStrings.email,
    queryParameters: {'subject': "This is my subject", 'body': "Hey Kashish here goes the body"},
  );

// To launch the link
launch(_emailLaunchUri.toString());

Expected results:
Gmail app compose page opens with required body and subject having space separated words

Actual results:
Gmail app compose page opens with required body and subject having "+" separated words

Screenshot
image

@darshankawar
Copy link
Member

@kashishkhullar
Can you provide flutter doctor -v ? Also, what are the steps to create URI, as you mentioned in step 3 ?

@darshankawar darshankawar added in triage Presently being triaged by the triage team waiting for customer response The Flutter team cannot make further progress on this issue until the original reporter responds labels Feb 8, 2021
@kashishkhullar
Copy link
Author

Flutter Doctor output:

flutter doctor -v
[√] Flutter (Channel stable, 1.22.6, on Microsoft Windows [Version 10.0.19041.746], locale en-IN)
    • Flutter version 1.22.6 at G:\flutter_windows_v1.12.13+hotfix.5-stable\flutter
    • Framework revision 9b2d32b605 (2 weeks ago), 2021-01-22 14:36:39 -0800
    • Engine revision 2f0af37152
    • Dart version 2.10.5

 
[√] Android toolchain - develop for Android devices (Android SDK version 29.0.2) 
    • Android SDK at G:\Android\sdk
    • Platform android-29, build-tools 29.0.2
    • ANDROID_SDK_ROOT = G:\Android\sdk
    • Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java       
    • Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b03)
    • All Android licenses accepted.

[!] Android Studio (version 3.5)
    • Android Studio at C:\Program Files\Android\Android Studio
    X Flutter plugin not installed; this adds Flutter specific functionality.
    X Dart plugin not installed; this adds Dart specific functionality.
    • Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b03)

[√] VS Code, 64-bit edition (version 1.52.1)
    • VS Code at C:\Program Files\Microsoft VS Code
    • Flutter extension version 3.19.0

[√] Connected device (1 available)
    • Nokia 5 1 Plus (mobile) • PDAID18092305525 • android-arm64 • Android 10 (API 29)

! Doctor found issues in 1 category.

Source code of a sample app to replicate this issue:

source code
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
        // This makes the visual density adapt to the platform that you run
        // the app on. For desktop platforms, the controls will be smaller and
        // closer together (more dense) than on mobile platforms.
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final Uri _emailLaunchUri = Uri(scheme: 'mailto', path: 'smith@example.com', queryParameters: {'subject': 'Example Subject & Symbols are allowed!'});

  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: Center(
          // Center is a layout widget. It takes a single child and positions it
          // in the middle of the parent.
          child: RaisedButton(
        onPressed: () {
          launch(_emailLaunchUri.toString());
        },
        child: Text("Send email"),
      )),
    );
  }
}

URI used for this issue

I have used the same code to create the URI as given in the documentation.

import 'dart:core';
import 'package:url_launcher/url_launcher.dart';

final Uri _emailLaunchUri = Uri(
  scheme: 'mailto',
  path: 'smith@example.com',
  queryParameters: {
    'subject': 'Example Subject & Symbols are allowed!'
  }
);

// ...

// mailto:smith@example.com?subject=Example+Subject+%26+Symbols+are+allowed%21
launch(_emailLaunchUri.toString());

Screenshots using the URI in the documentation

image
image

@no-response no-response bot removed the waiting for customer response The Flutter team cannot make further progress on this issue until the original reporter responds label Feb 8, 2021
@parthdave93
Copy link

parthdave93 commented Feb 9, 2021

Need to understand the difference between query and QueryParamters difference in uri_launcher package

as

    final Uri _emailLaunchUri = Uri(
      scheme: 'mailto',
      path: "GlobalStrings.email",
      query: 'subject=$subject&body=$body',
    );

this code returns mailto:GlobalStrings.email?subject=Hey%20Kashish&body=body when we do _emailLaunchUri.toString()
while
for this

_emailLaunchUriIssue = Uri(
      scheme: 'mailto',
      path: "GlobalStrings.email",
      queryParameters: {'subject': "This is my subject", 'body': "Hey Kashish here goes the body"},
    );

toString returns this: mailto:GlobalStrings.email?subject=This+is+my+subject&body=Hey+Kashish+here+goes+the+body
which reason we are encoding queryParamerts as application/x-www-form-urlencoded ?

code which replaces space with +

 static String encodeQueryComponent(String component,
      {Encoding encoding = utf8}) {
    return _Uri._uriEncode(_Uri._unreservedTable, component, encoding, true);
  }

exactly what's the difference between query and queryParameters ?
and for mobile users mailto:GlobalStrings.email?subject=Hey%20Kashish&body=body this is the perfect intent which is missing from documentation https://pub.dev/packages/url_launcher

I think in documentation there should be clear red line to what differences are if any. else there's a bug in the encodeQueryComponent for queryParameters @darshankawar

@darshankawar
Copy link
Member

Thanks @kashishkhullar for code sample. Seems to be happening on Android 10 devices, since it doesn't occur on 8.1.0 device I tried.

Screenshot 2021-02-09 at 11 38 03 AM

flutter doctor -v
[✓] Flutter (Channel stable, 1.22.6, on Mac OS X 10.15.4 19E2269 darwin-x64,
    locale en-IN)
    • Flutter version 1.22.6 at /Users/dhs/documents/Fluttersdk/flutter
    • Framework revision 9b2d32b605 (2 weeks ago), 2021-01-22 14:36:39 -0800
    • Engine revision 2f0af37152
    • Dart version 2.10.5

 
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.0)
    • Android SDK at /Users/dhs/Library/Android/sdk
    • Platform android-30, build-tools 30.0.0
    • Java binary at: /Applications/Android
      Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build
      1.8.0_242-release-1644-b3-6915495)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 12.3)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 12.3, Build version 12C33
    • CocoaPods version 1.9.3

[!] Android Studio (version 4.1)
    • Android Studio at /Applications/Android Studio.app/Contents
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
    • Java version OpenJDK Runtime Environment (build
      1.8.0_242-release-1644-b3-6915495)

[✓] VS Code (version 1.52.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.18.1

[✓] Connected device (1 available)
    • SM G975F (mobile) • RZ8M802WY0X • android-arm64 • Android 10 (API 29)

! Doctor found issues in 1 category.

@darshankawar darshankawar added found in release: 1.22 Found to occur in 1.22 has reproducible steps The issue has been confirmed reproducible and is ready to work on p: first party p: url_launcher Plugin to launch external applications and removed in triage Presently being triaged by the triage team labels Feb 9, 2021
@darshankawar
Copy link
Member

Similar to #73717

@stuartmorgan
Copy link
Contributor

stuartmorgan commented Feb 22, 2021

Need to understand the difference between query and QueryParamters difference in uri_launcher package

They aren't part of url_launcher, they are part of Uri, which is from the Dart SDK.

this code returns mailto:GlobalStrings.email?subject=Hey%20Kashish&body=body when we do _emailLaunchUri.toString()

Which is an invalid URI, because it contains spaces.

toString returns this: mailto:GlobalStrings.email?subject=This+is+my+subject&body=Hey+Kashish+here+goes+the+body

Which is a correctly encoded URI.

exactly what's the difference between query and queryParameters ?

The Uri constructor documentation explicitly addresses this. In the former, it is your responsibility to provide a valid, URI-escaped query string (which your code above does not, this the invalid resulting URI.)

and for mobile users mailto:GlobalStrings.email?subject=Hey%20Kashish&body=body this is the perfect intent which is missing from documentation https://pub.dev/packages/url_launcher

No, that's an invalid URI unless body is already properly escaped; the example in the url_launcher docs is there specifically because many people did what you are suggesting and got bad outcomes because theh were passing invalid URIs. (Subjects or bodies with "&"s being one common source of bugs, for instance.)

As for the specific issue you are having, does this happen with a different mailto handler? This looks like a bug in the handler to me, since + is a standard encoding for spaces in query parameters. This turns out not to be true; it's standard only for HTML form submissions, not URI queries in general.

@stuartmorgan stuartmorgan added the waiting for customer response The Flutter team cannot make further progress on this issue until the original reporter responds label Feb 26, 2021
@cyanglaz cyanglaz added the P3 Issues that are less important to the Flutter project label Mar 11, 2021
@pedromassangocode
Copy link

Without additional information, we are unfortunately not sure how to resolve this issue.
We are therefore reluctantly going to close this bug for now.
Please don't hesitate to comment on the bug if you have any more information for us; we will reopen it right away!
Thanks for your contribution.

Could everyone who still has this problem please file a new issue with the exact description of what happens, logs, and the output of flutter doctor -v.
All system setups can be slightly different, so it's always better to open new issues and reference related issues.

@pedromassangocode pedromassangocode added r: timeout Issue is closed due to author not providing the requested details in time and removed waiting for customer response The Flutter team cannot make further progress on this issue until the original reporter responds labels Apr 7, 2021
@d9media
Copy link

d9media commented Apr 13, 2021

Confirming this issue

This worked for us:

final String _emailUriString = _emailLaunchUri
                                .toString()
                                .replaceAll('+', '\%20');

@stuartmorgan
Copy link
Contributor

See #73717 (comment) for more details; this general issue is now tracked there.

stuartmorgan added a commit to stuartmorgan/plugins that referenced this issue Apr 21, 2021
`Uri`'s constructor doesn't handle query parameters correctly for
non-http(s) schemes, so the `mailto` example in the README is
misleading. This adds a new utility method to do query string
construction correctly, and updates the README to show using it and
warning about the need to use it in general.

If/when `Uri` is fixed to handle generic URI query parameters correctly,
this utility method can be deprecated.

Fixes flutter/flutter#75552
Fixes flutter/flutter#73717
stuartmorgan added a commit to stuartmorgan/plugins that referenced this issue Apr 21, 2021
`Uri`'s constructor doesn't handle query parameters correctly for
non-http(s) schemes, so the `mailto` example in the README is
misleading. This adds a new utility method to do query string
construction correctly, and updates the README to show using it and
warning about the need to use it in general.

If/when `Uri` is fixed to handle generic URI query parameters correctly,
this utility method can be deprecated.

Fixes flutter/flutter#75552
Fixes flutter/flutter#73717
stuartmorgan added a commit to flutter/plugins that referenced this issue Jun 16, 2021
`Uri`'s constructor doesn't handle query parameters correctly for
non-http(s) schemes, so the `mailto` example in the README is
misleading. This updates the README to show using a simple method
to work around that bug, and a warning about the need to use it.

Fixes flutter/flutter#75552
Fixes flutter/flutter#73717
@github-actions
Copy link

github-actions bot commented Aug 2, 2021

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 2, 2021
fotiDim pushed a commit to fotiDim/plugins that referenced this issue Sep 13, 2021
`Uri`'s constructor doesn't handle query parameters correctly for
non-http(s) schemes, so the `mailto` example in the README is
misleading. This updates the README to show using a simple method
to work around that bug, and a warning about the need to use it.

Fixes flutter/flutter#75552
Fixes flutter/flutter#73717
henkibro pushed a commit to henkibro/url_launcher that referenced this issue Mar 1, 2022
`Uri`'s constructor doesn't handle query parameters correctly for
non-http(s) schemes, so the `mailto` example in the README is
misleading. This updates the README to show using a simple method
to work around that bug, and a warning about the need to use it.

Fixes flutter/flutter#75552
Fixes flutter/flutter#73717
@flutter-triage-bot flutter-triage-bot bot added the package flutter/packages repository. See also p: labels. label Jul 5, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
found in release: 1.22 Found to occur in 1.22 has reproducible steps The issue has been confirmed reproducible and is ready to work on p: url_launcher Plugin to launch external applications P3 Issues that are less important to the Flutter project package flutter/packages repository. See also p: labels. r: timeout Issue is closed due to author not providing the requested details in time
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants