Skip to content

A graph based state management library in flutter with support for MVC & MVVM patterns

License

Notifications You must be signed in to change notification settings

frozen-beak/ecko

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

2 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Ecko ๐Ÿ•ธ๏ธ

pub package CI Last Commits Pull Requests Code size License

Ecko is a graph-based state management library for Flutter applications.

โš ๏ธ Important Notice: Ecko remains in active development and hasn't yet hit v0.1.0. Due to frequent updates and enhancements, breaking changes might occur in the public API. Stay tuned for progress updates, and consider contributing ideas or reporting any issues you encounter via GitHub Issues. Thank you for joining me on this exciting journey towards building a ๐Ÿ†’ state management library in Flutter!

Table Of Content

Why Ecko?

๐Ÿ“š Learning Project:

First and foremost, Ecko was born as a personal learning project. I wanted to dive deep into understanding how state management systems function internally while also improving on my skills along the way.

โšก๏ธ Inspiration:

I've settled onto GetX for the majority of my time in past, because how easy it is to get started. However, I came to realise that, GetX tries to do many things at the same time, which leads architectural demise in larger projects.

However, sometimes less can be more โ€” I aim to maintain the simplicity of GetX with a different approach to manage state.

โš™๏ธ Decoupled Business Logic:

One major issue observed with existing libraries is their tendency to encourage tight coupling between business logic and user interface components. This makes code harder to test, refactor, and reuse across different parts of the application.

That's why I designed ecko with principle of separation of concerns in mind. Maintaining loose coupling between various layers helps ensure cleaner, easier-to-maintain codebases.

๐Ÿ•ธ๏ธ Managing Complexity with Directed Graphs::

Ecko allows representation of interdependent states effectively through a directed graph structure. It simplifies navigating complex scenarios in contemporary applications while preserving clear separation and convenience in usage.

๐Ÿš€ (Planned) Out-of-the-box Service Management:

As experienced developers know, handling external dependencies such as APIs, databases, or other services often requires extra care. Although still under development, one of Ecko's planned features will include an integrated service management system.

The idea behind this feature is to offer a unified methodology for controlling services within your applications similar to GetX Controllers & Services, reducing boilerplate code and ensuring consistent design patterns throughout your projects.

Getting Started

You can directly install Ecko by adding ecko: ^0.0.1 to your pubspec.yaml dependencies section as shown below,

dependencies:
  ecko: ^0.0.1

You can also add Ecko in your project by executing fallowing,

flutter pub add ecko

To start using Ecko in your application, make sure to initialise the singleton Ecko class as fallowing,

void main() {
  // Initialize Ecko at the start of your application.
  Ecko.init(printLogs: true);
  // Access the Ecko instance.
  final ecko = Ecko();
  // Use ecko to manage controllers, stores, etc.
  // ...
}

Now you are good to go ๐Ÿ”ฅ!

Core

At the heart of Ecko are stores, which are classes that encapsulate and manage state. You can create stores wherever it makes sense for your app, but be aware that you will need to manually call their dispose() method when they are no longer needed to avoid memory leaks.

Stores can depend on one another through a directed acyclic graph (DAG) maintained internally by Ecko. This means that if you change the value of a store, any other stores that depend on it will automatically update as well.

Under the hood, Ecko uses ValueNotifier and ChangeNotifier to enable reactivity and allow components to efficiently rebuild only when necessary. To listen to updates from a specific store, you can use the StoreBuilder widget. Alternatively, you can extend the StoreStateWidget abstract base class to easily build widgets that subscribe to a single store without having to worry about manual subscription or disposal.

Usage

Creating Store

The building blocks of Ecko are stores. At its simplest form, a store is just an instance of the Store class that wraps around your application state. For example, let's say you want to maintain a counter variable:

// A store to hold the counter state
final counter = Store(0);

Feel free to create stores where it makes sense for your app architecture. However, remember to explicitly call their dispose() method when the associated state becomes obsolete to prevent potential memory leaks:

@override
void dispose() {
  // Dispose the `counter` store
  counter.dispose();

  // Perform any additional cleanup required before removing this StatefulWidget
  // ...
}

Listening to Store Updates with StoreBuilder

Use the StoreBuilder widget to listen to updates for a specific store to update applications UI.

It accepts two named parameters โ€“ store specifies the target store, while widget defines the callback responsible for rendering the current store value. In the following example, we display the updated value of the counter store inside a Text widget:

Column(
  children: [
    // A widget listening to the `counter` store
    StoreBuilder<int>(
      store: counter,
      builder: (context, value) {
        return Text(value.toString());
      },
    ),

    // More widgets here...
  ],
)

โ„น๏ธ Important: Make sure to clean up the Store listener along with the corresponding UI element that depends on it. Neglecting this task can cause undesirable side effects due to lingering listeners.

For convenience, consider implementing the StoreStateWidget interface instead, which handles automatic subscription behind the scenes. Check out the API reference below for more details.

Dependency Between Stores

Ecko stores can be dependent on other stores with a directed graph which is used internally.

Creating Dependencies

Introducing a dependency node between two stores helps automatically update the state of the dependent stores when the stores it depends upon are updated.

Let's take an example, we have a StoreA which holds current temperature in Celsius. Similarly we have a StoreB which holds the current temperature in Fahrenheit.

Now if we add StoreB as a dependency to StoreA, whenever now StoreA is updated, StoreB will update automatically

The new state for the StoreB is calculated by executing its updateCallback. You'll be learning more about it ahead in the docs. Also can refer here

Creating dependency is very easy,

// store to hold temperature value in `celsius`
final celsiusStore = Store<double>(0);

// store to hold temperature value in `fahrenheit`
final fahrenheitStore = Store<double>(0);

Now, if we want to create dependency between celsiusStore and fahrenheitStore, meaning that fahrenheitStore should automatically update when the state of celsiusStore is updated, we can added dependency to the celsiusStore as fallows,

// [fahrenheitStore] is now dependent on [celsiusStore]
celsiusStore.addDependency(fahrenheitStore);

๐Ÿ’€ Warning: If a storeA is dependent on storeB then storeB can not be dependent on storeA. This will result in infinite callback of updating states and will throw Stack Overflow error in the end.

Creating An updateCallback

Every store has a updateCallback property, which is a function which is called when the store is automatically updated. For ex, when celsius store is updated, updateCallback for the fahrenheit store is called to update its state, because of its dependency with celsius store.

You can set updateCallback while creating the store,

// store to hold temperature value in `celsius`
// when this store is automatically updated, the state will increment by `1`
final fahrenheitStore = Store<double>(0, updateCallback: (val) => val + 1);

For every store, updateCallback is mutable, hence you can also change it whenever & wherever you like,

// this sets a new `updateCallback` for the store, it'll then be called
// when the store is automatically updated
fahrenheitStore.setUpdateCallback((value) {
  return (celsiusStore.state * 9 / 5) + 32;
});

Using autoUpdate

Every store provides autoUpdate function, this function is called internally when updating the states of the store when its dependencies are updated.

You can call autoUpdate to update the state of the store according to the updateCallback,

// a store to hold the counter state with an update callback to increment the state by `1`
final counter = Store<int>(0, updateCallback: (val) => val++);

// update the state of the store according to the updateCallback
counter.autoUpdate();

Using StoreStateWidget

Alternatively, you can extend the StoreStateWidget abstract base class to easily build stateless widgets that subscribe to a single store without having to worry about manual subscription or disposal.

///
/// This is a Slider Widget which has its own store,
///
/// State of this slider is managed internally by this class,
/// you don't need to worry about creating and disposing the store
///
/// This widget offers an alternative way to work with stores,
/// can only be used when a store is completely isolated from other stores
///
class SliderWidget extends StoreStateWidget<double> {
  // init the store the default value 0, by passing it through super
  SliderWidget({Key? key}) : super(state: 0, key: key);

  @override
  Widget build(BuildContext context, double state) {
    return Slider(
      min: 0,
      max: 100,
      value: state,
      onChanged: (val) => store.set(val),
    );
  }
}

This widget offers an alternative way to work with stores, but it can only be used when a store is completely isolated from other stores.

Ecko

Ecko class is used to group everything together in Ecko library. Currently it serves no purpose more then initiating underlying services for Ecko.

As stated above, in future updates, Ecko will be used to manage services and controllers offering an out-of-the-box service locator.

Contributions

I welcome contributions! If you'd like to improve Ecko, please open an issue or an PR with your suggested changes on this repo. Happy Coding ๐Ÿ•ธ๏ธ!

About

A graph based state management library in flutter with support for MVC & MVVM patterns

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published