95

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?

Charles Jr
  • 8,333
  • 15
  • 53
  • 74
  • 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 Feb 17 '18 at 11:13
  • Is this what you're looking for? https://stackoverflow.com/questions/12951989/how-to-reference-another-file-in-dart – RedBrogdon Feb 18 '18 at 03:50
  • 1
    @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 Feb 18 '18 at 06:49
  • _ in front of function name will make it private _launchwebview, – Praveen Soni Sep 29 '21 at 14:15

5 Answers5

150

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();
Antonino
  • 3,775
  • 1
  • 19
  • 16
  • 1
    I successfully printed a statement, but I'm having a little trouble executing the launchWebView function in the class. Any ideas? – Charles Jr Feb 27 '18 at 00:48
  • 3
    I've been thinking btw these 2 options for quite some time. Could you please elaborate on pros/cons btw the two options. The first option doesn't sound like a rule-of-thumb option, right? Although, I don't know why. Could you explain why? On the other hand, I've read that it's best to avoid static methods/variables since they take up memory. But isn't the first option the same story in terms of memory though? In other words if I don't create a class just functions in a file isn't automatically each function static? – Tomas Baran Jun 20 '20 at 23:33
  • 2
    @Antonino Cacace you say top-level functions are not really clean in flutter. but why? Here is what they say in official docs: "Note: Consider using top-level functions, instead of static methods, for common or widely used utilities and functionality." https://dart.dev/guides/language/language-tour#adding-features-to-a-class-mixins#static-methods – cofirazak Jun 09 '21 at 22:16
  • This was a great thank you! Helped me on to resolve my blockage! – Anthony Peña Jul 19 '22 at 17:44
16

Use mixin instead of global functions!

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, the method will only be accessible within that file. I explored how to expose private methods across files, but found the solution messy. I thus 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. I have detailed the drawbacks of the existing options 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 clearly a code smell because we are copy-pasting the same code.
  2. Using global functions doesn't follow Object Oriented programming. Global state breaks the Encapsulation principle. OOP is the best paradigm to use within Dart apps because of the way the language is designed (classes). The top answer recommends global functions, however, this should be a last resort given the powerful language capabilities of Dart.
  3. Static functions break encapsulation and the Open-Closed principle. They are hard to integrate with state management solutions because we track the state within instance method contexts. For example, integrating provider and other packages like GetX.
  4. Defining a Function as a property of a class can lead to subtle bugs further down the line. For example, we could assign any function to this property because there is no function signature type checking. Furthermore, this is not a scalable solution with code duplications.

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. We can restrict the mixin to a specific subclass with on if needed.

Code example

LaunchWebView.dart

mixin LaunchWebView on StatelessWidget { // 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.
  }
}

Usage

class ExampleClass extends StatelessWidget with LaunchWebView {
  Widget build(BuildContext context) {
    ....
  }

  void testFunction() {
    // We now have access to launchWebView().
    launchWebView();
  }
}
Pranav Kasetti
  • 8,770
  • 2
  • 50
  • 71
  • 1
    The Encapsulation principle is a core principle for good Object-Oriented design (OOP). The idea says that methods should be defined on object instances. Public methods are still called on objects, so they don't break the principle. I hope that helps. – Pranav Kasetti Jun 10 '21 at 09:52
  • 1
    It seems you don’t know what you’re talking about. I’m not talking about inheritance. Mixins are an example of composition. – Pranav Kasetti Jun 11 '21 at 13:43
  • 1
    How about you read what OOP is first? Then you wouldn't wonder why methods are better than top-level functions. – Pranav Kasetti Jun 12 '21 at 12:02
  • 1
    That's comparing static methods to top-level functions. I am talking about how instance methods are better than top-level functions. Let me know if you still have questions. – Pranav Kasetti Jun 17 '21 at 14:23
11

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();
   }
}  
Jake
  • 1,906
  • 9
  • 32
  • 60
Mahamat
  • 321
  • 4
  • 14
6

You can do that in different ways:

1. Global function:

Define your function in a file, say global.dart:

void func() => print('Hello');

To use it in any file, just call:

func();

2. Static function in a class:

Create a class, say Foo and define your function in it:

class Foo {
  static void func() => print('Hello');
}

To use it in any file, just call

Foo.func();

3. Use mixin:

  • If you want to use the function in any class:

    Create a mixin, say Bar:

    mixin Bar {
      void func() => print('Hello');
    }
    

    To use it in a class, just use with keyword followed by mixin.

    class Baz with Bar {
      void main() => func();
    }
    
  • If you want to restrict the mixin from being used with any class:

    class Foo {}
    

    Create a mixin, say Bar which is on Foo.

    mixin Bar on Foo {
      void func() => print('Hello');
    }
    

    To use Bar mixin we need to extend Foo class because this is what it is on.

    class Baz extends Foo with Bar {
      void main() => func();
    }
    
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
  • In global functions part, you can also use `import 'global.dart' as global` in other file and then invoke the function using `global.func();` – CopsOnRoad Dec 23 '21 at 12:26
-10

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


}
Filippos Zofakis
  • 561
  • 6
  • 12
Tree
  • 29,135
  • 24
  • 78
  • 98