3

I currently have a widget that I use to open a specific webpage using the LaunchUrl feature from the URL_Launcher package.

Throughout my app there are several instances where I'd like to open a webpage using this exact method, but I suppose it's not very efficient to copy-paste the widget I created for this over and over again, and simply replacing the URL.

This is the widget I have written, which is working. But this one is opening a specific URL.

Future<void> openHCReservationPortal(String url) async {
  final url = Uri.parse('https://portal.mijnhandicart.nl/site/login');
  if (!await launchUrl(url, mode: LaunchMode.platformDefault)) {
    throw Exception('Kon $url niet openen');
  }
}

I'd like to have the url be a parameter, so I can reuse the widget.

I have tried to create a statefull widget with a required parameter but got all sorts of errors, so I must be missing something or doing something wrong.

This is what I wrote:

class OpenWebPageWidget extends StatefulWidget {
  final String url;
  const OpenWebPageWidget({super.key, required this.url});

  @override
  State<OpenWebPageWidget> createState() => _OpenWebPageWidgetState();
}

class _OpenWebPageWidgetState extends State<OpenWebPageWidget> {
  @override
  Widget build(BuildContext context) {
    Future<void> openWebPageWidget(String url) async {
      final url = Uri.parse(url);
      if (!await launchUrl(url, mode: LaunchMode.platformDefault)) {
        throw Exception('Kon $url niet openen');
      }
    }
  }
}

Flutter is complaining that:

  • The body might complete normally, causing 'null' to be returned, but the return type, 'Widget', is a potentially non-nullable type. Try adding either a return or a throw statement at the end.
  • The declaration 'openWebPageWidget' isn't referenced.
1
  • To launch a URL a reusable method may be a better option. You method only needs slight modification. Please check out my answer below. Commented Feb 16 at 2:26

3 Answers 3

3

You can easily pass the URL as a string to the simple function as below:

Future<void> launchMyURL(String url) async {
  if (url.isEmpty) {
    throw 'Given URL is empty';
  }

  final Uri uri = Uri.parse(url);

  if (await canLaunchUrl(uri)) {
    await launchUrl(uri);
  } else {
    throw 'Could not launch $url';
  }
}

Then you can call the function to launch the URL from anywhere you like, e.g. from an elevated button:

ElevatedButton(
  onPressed: () async {
    try {
      await launchMyURL("https://www.google.com/");
    } catch (e) {
      print("#### Caught exception: ${e.toString()}");
    }
  },
  style: ElevatedButton.styleFrom(
    backgroundColor: Colors.blue,
    textStyle: const TextStyle(fontSize: 16.0),
  ),
  child: const Text(
    "Click to launch URL",
    style: TextStyle(
      color: Colors.white,
      fontWeight: FontWeight.normal,
      fontSize: 16.0,
    ),
  ),
),

Currently in your code, your build method has a Widget return type, but you are not returning any widget from the method, and only calling a function of Future<void> type, so the warning.

And, your method openHCReservationPortal can be slightly modified to achieve what you need. Here is the modified version:

Future<void> openHCReservationPortal(String url) async {
  final urlLaunch = Uri.parse('https://portal.mijnhandicart.nl/site/login');
  if (!await launchUrl(urlLaunch, mode: LaunchMode.platformDefault)) {
    throw Exception('Kon $url niet openen');
  }
}

You can modify your method with the above changes and also include the check with canLaunchUrl instead of directly calling launchUrl.

Sign up to request clarification or add additional context in comments.

3 Comments

Hi Rusty, this is indeed exactly what I was looking for! I only had to remove the '_' from the function name in order to use it, as I try to define reusable functions in a separate dart file to keep my code clean. Thank you very much, I learned something today!
I learned that canLaunchUrl() method can be unreliable at times, so it is best to use launchUrl() method directly and check its return for any error.
2

The errors you're encountering stem from two primary issues in your OpenWebPageWidget implementation:

Non-Exhaustive Return in build Method: In Dart, every code path in a function with a non-nullable return type must return a value. Your build method currently lacks a return statement.

try this :

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

class OpenWebPageWidget extends StatelessWidget {
  final String url;

  const OpenWebPageWidget({Key? key, required this.url}) : super(key: key);

  Future<void> _openWebPage() async {
    final uri = Uri.parse(url);
    if (!await launchUrl(uri, mode: LaunchMode.platformDefault)) {
      throw Exception('Could not open $url');
    }
  }

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: _openWebPage,
      child: const Text('Open Web Page'),
    );
  }
}

and use it like this:

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('URL Launcher Example'),
      ),
      body: Center(
        child: OpenWebPageWidget(url: 'https://example.com'),
      ),
    );
  }
}

Comments

2

The code you have Provided is not a Widget it is a Function of type Future<void>

Future<void> openHCReservationPortal(String url) async {
  final url = Uri.parse('https://portal.mijnhandicart.nl/site/login');
  if (!await launchUrl(url, mode: LaunchMode.platformDefault)) {
    throw Exception('Kon $url niet openen');
  }
}

The body might complete normally, causing 'null' to be returned, but the return type, 'Widget', is a potentially non-nullable type. Try adding either a return or a throw statement at the end.

This error is because the build method return type is a Widget but you did not return anything and it is need a return value of a Widget too , So try to return a Widget like a Scaffold or whatever you want to return then you can use your method when an event happened like click on a button or any change in the UI or you can call it on another method

Example

class OpenWebPageWidget extends StatelessWidget {
  const OpenWebPageWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: MaterialButton(
          onPressed: () {
            LaunchURL.openWebPageWidget(
                url: "https://portal.mijnhandicart.nl/site/login");
          },
          color: Colors.cyan,
        ),
      ),
    );
  }
}

Function: It's better to create it inside the class

class LaunchURL {
  static Future<void> openWebPageWidget({
    required String url,
  }) async {
    final Uri uri = Uri.parse(url);
    if (!await canLaunchUrl(uri)) {
      log("Error while luanch URL");
      return;
    }
    await launchUrl(uri, mode: LaunchMode.externalApplication);
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.