Skip to content

Instantly share code, notes, and snippets.

@munificent
Last active February 21, 2023 11:16
Show Gist options
  • Save munificent/e894b14d3e5a26352f30af4b960e0a96 to your computer and use it in GitHub Desktop.
Save munificent/e894b14d3e5a26352f30af4b960e0a96 to your computer and use it in GitHub Desktop.
The "hidden forwarders" pattern in Dart

Say you have a package my_stuff. It contains two classes, A and B. You want to store them in separate files, but A needs access to some private functionality of B (in this case, the method _secret() and the constructor B._magic()). You don't want to expose that functionality outside of the package. You can express that like this:

The app using your package does:

some_app.dart:

import 'my_stuff.dart';

main() {
  A().doStuff();
}

That looks like:

my_stuff.dart:

export 'a.dart';
export 'b.dart' hide callSecret, constructMagic;

The two implementation libraries are:

a.dart:

import 'b.dart';

class A {
  // Make a B using its hidden constructor.
  B b = constructMagic();

  doStuff() {
    // Can even call instance methods.
    callSecret(b);
  }
}

b.dart:

B constructMagic() => B._magic();
callSecret(B b) => b._secret();

class B {
  B._magic();

  _secret() {
    print("I'm a private method on B.");
  }
}

The relevant pieces are:

  1. The implementation library "a.dart" makes the members it wants to hide private.
  2. Then it provides public top level functions that forward to those. Those functions can access the private members since they're in the same library.
  3. The other implementation library "a.dart" imports "b.dart" directly so it can call those top-level functions to reach the private members.
  4. A public wrapper library exports "b.dart" but hides the top-level forwarding functions.
  5. All outside code only imports the public wrapper. Thus, it has no way to reach the private members.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment