skip to Main Content

Tested in debug and in production on Chrome and Edge.

My flutter web project throws an Exception after I click the code (< >) IconButton in the upper-right corner. You may be able to confirm in your devtools console. The Exception should only be thrown if url_launcher fails to launch the url… but the new tab opens just fine.

This is how I’m calling url_launcher#launchUrl()

Future<void> _launchUrl(String urlString) async {
  if (!await launchUrl(Uri.parse(urlString))) {
    throw Exception("Could not launch $urlString");
  }
}

This method is called from the AppBar actions

appBar: AppBar(
  backgroundColor: Theme.of(context).colorScheme.primaryContainer,
  title: Text(widget.title),
  actions: [
    IconButton(onPressed: () => setState(() {
      _launchUrl("https://gitlab.com/friend.code");
    }), icon: const Icon(Icons.code)),
    IconButton(onPressed: () {}, icon: const Icon(Icons.person)),
    IconButton(onPressed: () {}, icon: const Icon(Icons.music_note))
  ],
),

This is ~exactly as written in the url_launcher example.

I was able to debug the url_launcher#launchUrl() execution up to url_launcher_web#openNewWindow(), which calls _window.open(url, target, 'noopener, noreferrer');. I am unable to step into that method, so not sure what is happening there, but this method call returns null.

Why am I getting a seemingly false-negative result here?

Full source code

webOnlyWindowName

If I set webOnlyWindowName: "_self", then the link opens in the current tab and I do not see any console error. If I set webOnlyWindowName: "_blank", then I still get the console error even though the new tab opens as intended. The docs say that launchUrl "Returns true if the URL was launched successful, otherwise either returns false or throws a PlatformException depending on the failure." I just want to know why it’s returning false.

2

Answers


  1. Chosen as BEST ANSWER

    The issue was that one should not pass an async callback function to setState!

    Broken code:

    IconButton(
      onPressed: () => setState(() {
        _launchUrl("https://gitlab.com/friend.code");
      }), icon: const Icon(Icons.code)),
    

    Fixed code:

    IconButton(
      onPressed: () async {
        await _launchUrl("https://gitlab.com/friend.code");
      }, icon: const Icon(Icons.code)),
    

    Reasoning

    I discovered my error when I noticed that my call to _launchUrl was returning a Future<void>, so I should await the result when I call it. Then IntelliJ helpfully informed me that if my function is going to await a result, then it should be declared an async function. It looked like this:

    IconButton(
      onPressed: () => setState(() async {
        await _launchUrl("https://gitlab.com/friend.code");
      }),icon: const Icon(Icons.code)),
    

    When I ran that method, I got a helpful error message, along with some sage advice:

    The following assertion was thrown while handling a gesture:
    setState() callback argument returned a Future.
    
    ...
    
    Instead of performing asynchronous work inside a call to setState(), first execute the work (without updating the widget state), and then synchronously update the state inside a call to setState().
    

    (If anyone knows why the example depicts the function call occurring within a setState callback, please leave a comment)


  2. In my case i have used

    kIsWeb?html.window.open(
      "https://gitlab.com/friend.code",
      "GitLab",
    ): _launchUrl("https://gitlab.com/friend.code");
    

    or

     await launchUrl(
          Uri.parse(url),
          webOnlyWindowName: isNewTab ? '_blank' : '_self',
        );
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search