61
votes

I have a Flutter app where I'm using the flutter_web_view package. I'm using it over several different files and would love to create its own file and simply reference with the _launchwebview function anywhere in my app because there are several lines of code needed in order to make it work. I know how to reference files and pass information but not methods/functions. Here is the class code...

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

class ShopClass extends StatefulWidget {
  @override
  ShopClassState createState() => new ShopClassState();
}

class ShopClassState extends State<ShopClass> {
  String _redirectedToUrl;
  FlutterWebView flutterWebView = new FlutterWebView();
  bool _isLoading = false;

  @override
  initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    Widget leading;
    if (_isLoading) {
      leading = new CircularProgressIndicator();
    }
    var columnItems = <Widget>[
      new MaterialButton(
          onPressed: launchWebViewExample, child: new Text("Launch"))
    ];
    if (_redirectedToUrl != null) {
      columnItems.add(new Text("Redirected to $_redirectedToUrl"));
    }
    var app = new MaterialApp(
      home: new Scaffold(
        appBar: new AppBar(
          leading: leading,
        ),
        body: new Column(
          children: columnItems,
        ),
      ),
    );
    return app;
  }


  void launchWebViewExample() {
    if (flutterWebView.isLaunched) {
      return;
    }

    flutterWebView.launch("https://apptreesoftware.com",
        headers: {
          "X-SOME-HEADER": "MyCustomHeader",
        },
        javaScriptEnabled: false,
        toolbarActions: [
          new ToolbarAction("Dismiss", 1),
          new ToolbarAction("Reload", 2)
        ],
        barColor: Colors.green,
        tintColor: Colors.white);
    flutterWebView.onToolbarAction.listen((identifier) {
      switch (identifier) {
        case 1:
          flutterWebView.dismiss();
          break;
        case 2:
          reload();
          break;
      }
    });
    flutterWebView.listenForRedirect("mobile://test.com", true);

    flutterWebView.onWebViewDidStartLoading.listen((url) {
      setState(() => _isLoading = true);
    });
    flutterWebView.onWebViewDidLoad.listen((url) {
      setState(() => _isLoading = false);
    });
    flutterWebView.onRedirect.listen((url) {
      flutterWebView.dismiss();
      setState(() => _redirectedToUrl = url);
    });
  }



  void reload() {
    flutterWebView.load(
      "https://google.com",
      headers: {
        "X-SOME-HEADER": "MyCustomHeader",
      },
    );
  }
}

How can I use launchWebViewExample in another class?

4
Can you please share some code. It's unclear what the problem is. Importing the file with the function should do. If you want to pass the function around there isn't really a difference to fields or other variables. - Günter Zöchbauer
Is this what you're looking for? stackoverflow.com/questions/12951989/… - RedBrogdon
@GünterZöchbauer I've added the code from the file I created. I'd like to use it to launch the WebView in another file without adding all these lines to each file. - Charles Jr

4 Answers

104
votes

You can write a file with just that function, like:

test.dart

void launchWebView () {
  print("1234");
}

and then import that file like this:

main.dart

import "test.dart";

class _MyHomePageState extends State<MyHomePage> {
   @override
   Widget build(BuildContext context) {
       launchWebView();

It is not really clean, but you can do that. Alternatively you can use a class with a static method like:

class test {
    static void foo() {
        print("1234");
    }
}

and then in your code invoke it like that (after the import):

test.foo();
10
votes

Or you can just declare all your functions (helpers) inside a class and pass them as an argument to other class.

//The class which contains your functions
class HelperFunction{

  //Define your method
  void launchWebView () {
    print("1234");
  }

  //Pass that function to a class
  MyHomePage(launchWebView);

}

//The class which receives the function.
class MyHomePage extends StatefulWidget{
  //Retrieve the function and store it to a variable of type Function.
  final Function launchWebView;
  MyHomePage(this.launchWebView);
}

class _MyHomePageState extends State<MyHomePage> {
   @override
   Widget build(BuildContext context) {
     //Access that function in State class using widget keyword.
     widget.launchWebView();
   }
}  
2
votes

Why does this happen?

I'm using it over several different files and would love to create its own file and reference with the _launchwebview function anywhere in my app because there are several lines of code needed in order to make it work.

Underscored methods are private to a given library. Thus, if we define _launchwebview in one file, that function is in a mini-library (Source), so it will only be accessible within that file. While exploring how to expose private methods within different files, I think the question would be better answered using public functions. The problem is implementing a shared function within different classes rather than simply providing access.

I've chosen to add this solution for this particular question since the method (launching a web view) would be good to have implemented within every appropriate Widget class. Note that extension methods could also work in this case, but Flutter prefers composition over inheritance.

If we want to move a public, i.e. non-underscored method, to a different class without copy-pasting, we could try several outlined approaches (see below).

Hacky solutions

  1. Using the part directive enables us to copy the source of one file with a private implementation and use it in another file. But this is discouraged because the technique increases the binary size, goes against the Effective Dart usage guide and is not an elegant solution.
  2. Using global functions isn't good Object Oriented programming because they break the Encapsulation principle. The top answer recommends this approach, and I think the answer could be improved further. I wrote this answer to offer an alternative solution.
  3. Static functions also break encapsulation and the Open-Closed principle. They also make things hard for us to integrate with state management solutions because we should track the state within instance method contexts (like provider and other well-known packages).

Solution: Mixins

Dart has inbuilt support for optionally adding functions to a class when we want to reduce duplicated code but avoid extending the whole class (Source). The mixin keyword enables this by mixing a class with some specific logic and restricting the mixin to a specific subclass with on.

So we can first add the shared code in a new file:

mixin LaunchWebView { // you can also constrain the mixin to specific classes using on in this line.
  void launchWebView() {
    // Add web view logic here. We can add variables to the mixin itself as well.
  }
}

And then use it wherever we like in another file as follows:

class ExampleClass extends Musician with LaunchWebView {
  void testFunction() {
    // We now have access to launchWebView().
    launchWebView();
  }
}
-10
votes

You want to declare function on a class level

file foo.dart

class Foo {

  static void launchWebView () {};

}

file bar.dart

import 'foo.dart'
class Bar {

  void someFunction (){
    Foo.launchWebView();
  }


}