asp
ASP (Atomic State Pattern) offers a simplified, Predictable and Powerful State Management for Flutter.
2.0.3
2.3K downloads/mo
#5088 most downloaded on pub.dev
Flutterando/asp
What this package is like to depend on
Last release 2 years ago
no release in 18 months
Ships unpredictably
gaps range from 8 days to 11 months
Most releases are documented
notes for 10 of 14 stable releases
Nothing withdrawn
no release was ever pulled
3 years old
14 releases · first in 2023
0 releases in the last 12 months
see the full history below
Release timeline
14 releases · May 2023 to Oct 2024Releases
latest 14-
2.0.322 Oct 2024 -
2.0.206 Aug 2024Release notes
Open source →- Fix: atomAction now returning FutureOr.
- Fix: propertie when in useAtomState hook;
-
2.0.101 Aug 2024 -
2.0.025 Jun 2024Release notes
Open source →After extensive feedback, we are changing the API to make the standard more intuitive and user-friendly. We also added stricter limits for more complex projects and introduced a “predictability” system in state changes to improve tracking and debugging while reducing unwanted side effects. Some changes may seem drastic, but we believe everything will make more sense once you give this new API a chance.
Let’s start with what has been removed, followed by the new features:
- [BREAKING CHANGE]: Removed all collections such as RxList, RxSet, and RxMap. Asynchronous converters like RxFuture and RxStream have also been removed. Initially, these helpers seemed beneficial, but they soon complicated things, prompting us to remove them. Consequently, all extensions have been removed as well. Now there is only one way to create an Atom:
final productsState = atom<List<Product>>([]);-
[BREAKING CHANGE]: Due to the new API capabilities, some widgets have changed. ASP now adopts hook_state for state distribution, retiring RxRoot, which has been removed. We will discuss the new state distribution method based on hooks later. RxCallback has also been removed for the same reason. The RxBuilder has been renamed to AtomBuilder for semantic clarity.
-
[BREAKING CHANGE]: The Reducer API in ASP was a major source of confusion and complaints. While it was magical and capable of deriving values from multiple Atoms, the reactive actions system needed a rethink to make the process more predictable and easier to learn. The steep learning curve of RxReducer led us to segregate it into two new APIs: AtomAction and AtomSelector. As a result, RxReducer no longer exists. However, don’t worry—something much better is coming.
-
[BREAKING CHANGE]: ASP is moving towards a more functional approach, emphasizing functions over classes. Some components, such as Atom, have been converted to functions. The main difference you’ll notice is the change in the capitalization of the component name:
//v1 final counter = Atom(0); //v2 final counter = atom(0);- [BREAKING CHANGE]: The biggest challenge in reactivity is observability. We have taken this to the next level with ASP. Now, you can track which action modified the state of an Atom, and when someone starts or stops listening to an Atom. We needed to tweak our observability API slightly, but it is definitely worth it:
void main() { AtomObserver.changes((status) { print(status.atom); // counterState print(status.action); // incrementAction print(status.event.name) // change }); runApp(MyApp()); }New Features and Future Direction of Atomic State Management
- [NEW]: Atom with more rigid architectural limits.
The Atom is the heart of atomic state. It allows creating an object that stores an autonomous micro-state, observable by anything—be it a widget or a function. However, an Atom could also be modified by anything, anywhere, leading to classic side-effect problems. To address this, we removed the state setter and added a new component called AtomAction. This way, while an Atom can still be listened to by anything, it can only be modified in a specific place, enhancing predictability and reducing uncontrolled side effects.
Example:
// atom creation final counterState = atom(0); // listen changes counterState.addListener((){ print(counterState.state); // 1 }); // action creation final increment = atomAction((set){ return set(counterState, 1); }); // execute action increment()Actions can also accept up to 3 arguments and be used as a pub-sub mechanism:
final counterAction = atomAction1<String>((set, action){ final state = counterState.state; if(action == 'INCREMENT'){ set(counterState, state + 1); } else if(action == 'DECREMENT'){ set(counterState, state - 1); } });The atomAction feature opens up numerous possibilities, functioning as both a simple action and a complex reducer. We replaced RxReducer with AtomAction for action calls. To fully replace RxReducer, we also needed a way to handle the derivation of multiple Atoms, which we achieve using the AtomSelector API.
[NEW]: Data derivation with AtomSelector.<br> Managing state distribution and converging multiple states into a new one was identified as the major challenge in the case study that led to the creation of ASP. We introduced a very simple way to combine multiple Atoms and compute a new value whenever any of them changes. This is very straightforward with AtomSelector:
// atoms final nameState = atom(''); final lastNameState = atom(''); // selectors final fullName = selector((get){ final name = get(nameState); final lastName = get(lastNameState); return '$name $lastName'; }); // actions final changeName = atomAction1<String>((set, name){ set(nameState, name); }); changeName('Matias');Within the selector’s internal scope, the get property is used to subscribe to an Atom. This visual indicator shows what is being observed. The example above demonstrates the three pillars of ASP: Atoms, Selectors, and Actions.
There is also a way to derive states asynchronously with AsyncSelector:
final userIdState = atom(1); final userState = asyncSelector<User>( User.empty(), (get) async { final id = get(userIdState); final response = await dio.get('/user/$id'); return User.fromJson(response.data); } );This example shows a more complex derivation where modifying an Atom triggers an external API call. Despite this complexity, RxReducer users should find it familiar. It’s worth noting that any Selector is also an Atom and shares the same internal APIs. Now that we’ve shown how to use Atom with Selector and modify it only in Actions, let’s talk about how we’ve made it easier to connect all this with Widgets.
- [NEW]: Replacing rxObserver with atomEffect.<br> Before diving into Widgets, let’s discuss a small change in the default listener for an Atom, formerly known as rxObserver. With the transparency of reactivity abandoned, we need to register each reactivity automatically using the get property seen in Selectors. This makes it more descriptive and easier to understand what is being subscribed. Additionally, it allows the use of other Atoms within the scope without needing additional subscriptions. The syntax hasn’t changed much, but it’s important to highlight since it’s also a [BREAKING CHANGE].
final disposer = atomEffect( (get) => get(counterState), (state) => print('Number is $state'), ); // release disposer();The atomEffect might not be used as often, as there is a hook that does the same thing and automatically manages the lifecycle, including automatic disposal.
- [NEW]: Automatic subscription with HOOKS. We are adding a hooks system to subscribe Atoms to a Widget. Unlike flutter_hook, which is excellent but invasive, we use hook_state that doesn’t require changing widgets. Just add a mixin and you’re ready to go.
We have two hooks: 1. useAtomState: Subscribes to the Atom and rebuilds the Widget when the state changes. 2. useAtomEffect: Subscribes to one or more Atoms and triggers a callback when they change. This does not affect the Widget’s state. It is great for executing code like Snackbar or Navigator.
With useAtomEffect, RxCallback is no longer necessary and has been removed. Pay attention to the mixin that enables Hooks:<br>
For StatelessWidget, add the HookMixin mixin.<br> For StatefulWidget, add the HookStateMixin mixin to the State.<br>
Hooks will only be available after adding the mixin correctly.
Example:
class CounterPage extends StatelessWidget with HookMixin { const CounterPage({super.key}); void callSnackBar(int state){ if(state > 5){ final snackBar = SnackBar(content: Text('Max $state')); Asuka.showSnackBar(snackBar); } } @override Widget build(BuildContext context) { //listen atom final state = useAtomState(counterState); // listen and call function useAtomEffect( (get) => get(counterState), effect: callSnackBar, ); ...Hooks manage the lifecycle automatically, so there is no need to worry about memory "releases" like "dispose". A dream come true!
OTHER CHANGES
-
[BREAKING CHANGE]: Atom.value is now Atom.state.
-
[NEW]: The distinct property has been added to Atom to specify if the user wants to trigger reactivity even if the same state is received.
-
[NEW]: A new documentation available at https://asp.flutterando.com.br.
-
[FIX]: Corrected all usage examples.
All support is free in Brazil's largest Flutter community, Flutterando. Join us on Discord.
Let's set the pattern together!
-
1.3.020 Jul 2023Nothing published for this version
-
1.2.016 Jul 2023Release notes
Open source →What's Changed
- docs update example readme by @dtodt in #3
- update public code documentation by @dtodt in #4
- Update README.md by @Kulitech in #5
- Bump semver from 5.7.1 to 5.7.2 in /doc by @dependabot in #8
New Contributors
- @dtodt made their first contribution in #3
- @Kulitech made their first contribution in #5
- @dependabot made their first contribution in #8
Full Changelog: https://github.com/Flutterando/asp/commits/v1.2.0
Release notes
Open source →- Added:
Atom.nextandAtom.buffer; - Added: Doc FAQ.
- @Deprecated: RxList, RxMap, RxSet, RxFuture and RxStream.
-
1.1.114 Jul 2023 -
1.1.012 Jul 2023 -
1.0.408 Jun 2023 -
1.0.329 May 2023 -
1.0.227 May 2023Nothing published for this version
-
1.0.127 May 2023Nothing published for this version
-
1.0.1+127 May 2023Nothing published for this version
-
1.0.026 May 2023