publisher_subscriber 0.0.2 copy "publisher_subscriber: ^0.0.2" to clipboard
publisher_subscriber: ^0.0.2 copied to clipboard

A simple, minimal, and opinionated state management library for Flutter.

Publisher/Subscriber #

A simple, minimal, and opinionated state management library for Flutter.

[Publisher/Subscriber Usage Example]

What is it? #

Publisher/Subscriber is a tool for managing your app's state. Think of it as a central place to hold information (like a user's name or a shopping cart's contents) that your app's widgets can use and automatically react to when it changes.

It's designed to be straightforward, with no complex setup, so you can focus on building your app.

Core Concepts #

There are just three key ideas to understand:

  1. Publisher<T>: This is your state holder. It's a class that holds a value (like a number or a string) and tells other parts of your app when that value has been updated.

    // A simple publisher that holds a number.
    class Counter extends Publisher<int> {
      Counter() : super(0); // It starts with the value 0.
      void increment() => setState(state + 1);
    }
    
  2. PublisherRecipe: This is a "blueprint" that tells the system how to create your Publisher. You don't use this directly, but create one using Publisher.global() or Publisher.scoped().

  3. Subscriber: This is a widget that listens to one or more Publishers. When a Publisher it's listening to changes, the Subscriber automatically rebuilds its part of the UI to show the new information.


How to Use #

Step 1: Define a Recipe #

First, create a recipe for your Publisher. This recipe is like a blueprint: it's cheap to create and doesn't do anything until it's used. You can define all your recipes in one place.

// A recipe for a counter that lives as long as the app does.
final globalCounterRecipe = Publisher.global(() => Counter());

// A recipe for a counter that's created and destroyed automatically.
final scopedCounterRecipe = Publisher.scoped(() => Counter());

Step 2: Read State in the UI #

To use your state in a widget, wrap the part of the UI that needs the data in a Subscriber. Inside its builder, use context.read(recipe) to get your Publisher instance.

When you access .state, the Subscriber knows to listen for changes.

class CounterDisplay extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Subscriber(
      builder: (context) {
        // Get the publisher instance using its recipe.
        final counter = context.read(scopedCounterRecipe);

        // Display the state. This widget will now rebuild
        // automatically whenever the counter's state changes.
        return Text('Count: ${counter.state}');
      },
    );
  }
}

Global vs. Scoped: Which Should I Use? #

  • Use Publisher.global() for app-wide state. This is for data that should never be lost as you navigate through the app.

    • Good Examples: User authentication status, theme settings, a shopping cart.
  • Use Publisher.scoped() for temporary state. This is for data that's only relevant to a specific page or feature. It's automatically created when needed and cleaned up when no longer used, which is great for saving memory.

    • Good Examples: The text in a search bar, filters on a results page, the state of a single form.

A Note on Memory: Recipes vs. Instances #

It's safe to create all your recipes globally, even the "scoped" ones. Here's why:

  1. The Recipe (Blueprint): The recipe itself uses almost no memory. It's just a plan.
  2. The Instance (The House): The actual stateful Publisher object is only built and allocated in memory the first time it's requested by a Subscriber.
  3. The Cleanup: When the last Subscriber using a scoped instance disappears, the instance is automatically destroyed and its memory is reclaimed.

This design ensures you only use memory for the state you are actively showing on screen.


Pro Tip: Keep Subscribers Small #

For the best performance, wrap only the specific widget that needs to rebuild, not your entire screen.

GOOD:

// Only the Text widget rebuilds when the counter changes.
Scaffold(
  appBar: AppBar(
    title: Subscriber(
      builder: (context) {
        final counter = context.read(globalCounterRecipe);
        return Text('Count: ${counter.state}');
      },
    ),
  ),
  body: MyComplexWidgetTree(),
)

NOT-SO-GOOD:

// The entire Scaffold and all its children would rebuild.
Subscriber(
  builder: (context) {
    final counter = context.read(globalCounterRecipe);
    return Scaffold(
      appBar: AppBar(title: Text('Count: ${counter.state}')),
      body: MyComplexWidgetTree(),
    );
  }
)

Advanced: Global Observer #

For debugging or logging, you can listen to all state changes for a certain type of Publisher from anywhere in your code.

void main() {
  // This will print a message every time ANY Counter changes state.
  GlobalPublisherObserver.on<Counter>((state) {
    print('A Counter somewhere changed its state to: $state');
  });

  runApp(const MainApp());
}

Author #

This library is developed and maintained by p4-k4.

0
likes
140
points
3
downloads

Publisher

unverified uploader

Weekly Downloads

A simple, minimal, and opinionated state management library for Flutter.

Repository (GitHub)
View/report issues

Topics

#riverpod #provider #bloc #getx #publisher-subscriber

Documentation

API reference

License

unknown (license)

Dependencies

flutter

More

Packages that depend on publisher_subscriber