El_Stevo
El_Stevo

Reputation: 63

Flutter: require URL parameter in LaunchURL widget to make it reusable

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:

Upvotes: 3

Views: 60

Answers (3)

rusty
rusty

Reputation: 402

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.

Upvotes: 3

Mahmoud Al-shehyby
Mahmoud Al-shehyby

Reputation: 355

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);
  }
}

Upvotes: 2

hab
hab

Reputation: 72

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'),
      ),
    );
  }
}

Upvotes: 2

Related Questions