pottery 0.5.0 copy "pottery: ^0.5.0" to clipboard
pottery: ^0.5.0 copied to clipboard

Utility widgets for Pot in Flutter, automatically managing the lifecycle of pots and the objects they hold based on the widget lifecycle.

Pub Version pottery CI codecov

Utility widgets for Pot (a simple and type-safe DI package) in Flutter. Pottery and LocalPottery automatically manage the lifecycle of pots and the objects they hold based on the widget lifecycle.

Examples #

Usage #

Pottery #

Use Pottery to set or replace the factories of pots for the duration that the widget exists in the tree. It is useful when a pot is not immediately necessary at app startup.

Create Pots as "pending" first in such cases. The pots should usually be assigned to global variables.

final counterNotifierPot = Pot.pending<CounterNotifier>();

Note

If a target pot is not pending and an object already exists in it when Pottery is created, Pottery immediately replaces the object as well as the factory.

Use the overrides parameter of Pottery to specify pots and their factories using set(). Each of the factories becomes available thereafter for the pot it is called on.

Widget build(BuildContext context) {
  // counterNotifierPot does not have a factory yet.
  // Calling `counterNotifierPot()` here throws a PotNotReadyException.

  ...

  return Scaffold(
    body: Pottery(
      overrides: [
        counterNotifierPot.set(CounterNotifier.new),
      ],
      // The new factory specified in `overrides` above is ready
      // before this builder is called for the first time.
      builder: (context) {
        // Methods and getters of counterNotifierPot are now available.
        final count = counterNotifierPot();
        ...
      },
    ),
  ),
);

Removing Pottery from the tree (e.g. navigating back from the page where Pottery is used) resets all pots in the overrides list and replaces their factories to throw an PotNotReadyException.

Note

It is easier to understand how to use Pottery by imagining it as something similar to MultiProvider of the provider package, although they internally work quite differently:

  • MultiProvider: Creates objects and provides them so that they are available in the subtree.
  • Pottery: Replaces factories to make pots ready so that they are available after that point. The widget tree is only used to manage the lifetime of factories and objects in pots, so pots are still available outside the tree.

LocalPottery #

LocalPottery defines new factories for existing pots to create objects that are available only in the subtree.

An important fact is that the existing pots remain unchanged. The factories and objects are associated with those pots and stored in LocalPottery for local use. Therefore, calling yourPot() still returns the globally accessible object stored in the pot itself.

To obtain the local object, use of() or maybeOf() instead. These methods look up the widget tree for the nearest LocalPottery ancestor that has the pot in its overrides list.

When no relevant LocalPottery ancestor is found:

Warning

When the object type is nullable, maybeOf() cannot distinguish between "the relevant LocalPottery was not found" and "it was found but the provided object is null." Use of() if the pot is expected to be provided by a LocalPottery ancestor.

final fooPot = Pot(() => Foo(111));
class ParentWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return LocalPottery(
      overrides: [
        fooPot.set(() => Foo(222)),
      ],
      builder: (context) {
        print(fooPot()); // 111
        print(fooPot.of(context)); // 222

        return ChildWidget();
      },
    );
  }
}

class ChildWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print(fooPot()); // 111
    print(fooPot.of(context)); // 222
    ...
  }
}

For more practical use cases, see the example in main2.dart and the documentation for LocalPottery, of, and maybeOf.

Important differences in LocalPottery compared to Pottery:

  • Objects are created immediately when LocalPottery is created, not when objects in pots are accessed for the first time.
  • Objects created with LocalPottery are only accessible with of() or maybeOf() in the subtree.
  • Objects created within LocalPottery are not automatically disposed when the LocalPottery is removed from the tree. Use the disposer argument of LocalPottery (instead of the disposer in each pot) to define a custom clean-up function.

Below is an example of a disposer function that disposes of all ChangeNotifiers and subtypes:

LocalPottery(
  overrides: [
    myChangeNotifier.set(() => MyChangeNotifier()),
    intValueNotifier.set(() => ValueNotifier(111)),
  ],
  disposer: (pots) {
    pots.values.whereType<ChangeNotifier>().forEach((v) => v.dispose());
  },
  builder: (context) { ... },
)

FAQ #

Why is this better than the scoping feature of Pot? #

Pot itself has the feature of scoping, but it is a package for Dart, not specific to Flutter.

Pottery is a utility that makes up for it. It makes use of the widget lifecycle to limit the scope of pots. It is more natural in Flutter and less error-prone.

How will this make things better? #

While it is convenient that you can access a pot stored in a global variable from anywhere, it gives you too much freedom, making you wonder how pots should be managed in a Flutter app. For example, you may easily lose track of from where in your app code a particular pot is used.

Pottery makes it possible to manage pots in a similar manner to using package:provider.

DevTools extension #

This package includes the DevTools extension.

To use it, run your app in debug mode with Flutter 3.16 or newer and open the DevTools.

The extension starts when either Pottery or LocalPottery is first used. It is also possible to start it earlier by calling Pottery.startExtension().

Note

Updates of the object in a pot caused by external factors (e.g. the object is a ValueNotifier and its value is reassigned) are not automatically detected by the DevTools Extension. However, because the Extension refreshes the entire view whenever any event from Pot, Pottery, or LocalPottery occurs, such changes may be reflected "coincidentally" as a result of an unrelated event.

To ensure the changes are reflected immediately, press the refresh icon button, or use notifyObjectUpdate() on a pot to emit an event to cause a refresh.

3
likes
160
points
218
downloads
screenshot

Publisher

verified publisherkaboc.cc

Weekly Downloads

Utility widgets for Pot in Flutter, automatically managing the lifecycle of pots and the objects they hold based on the widget lifecycle.

Repository (GitHub)
View/report issues

Topics

#dependency-injection

Documentation

API reference

License

MIT (license)

Dependencies

flutter, pot

More

Packages that depend on pottery