Extension functions on ValueListenable that allows you to work with them almost as if it was a synchronous stream.

Overview

functional_listener

Extension functions on ValueListenable that allows you to work with them almost as if it was a synchronous stream. Each extension function returns a new ValueNotifier that updates its value when the value of this changes. You can chain these functions to build complex processing pipelines from a simple ValueListenable.

Here are some examples how to use it:

listen()

Lets you work with a ValueListenable as it should be by installing a handler function that is called on any value change of this and gets the new value passed as an argument.

If we want to print every new value of a ValueListenable we can do:

final listenable = ValueNotifier<int>(0);
final subscription = listenable.listen((x, _) => print(x));

The returned subscription can be used to deactivate the passed handler function. As you might need to uninstall the handler function from inside the handler you get the subscription object passed to the handler function as second parameter like:

listenable.listen((x, subscription) {
  print(x);
  if (x == 42){
     subscription.cancel();
  }
}

map()

Lets you convert the value of one ValueListenable to anything you want. Imagine we have a ValueNotifier in an Model object that we can't change but we need it's value all UPPER CASE in our UI:

  ValueNotifier<String> source;  //this is the one from the model object

  final upperCaseSource = source.map( (s)=>s.toUpperCase() );

or you can change the type:

  ValueNotifier<int> intNotifier;  

  final stringNotifier = intNotifier.map<String>( (s)=>s.toString() );

where()

Lets you filter the values that an ValueListenable can have:

  ValueNotifier<int> intNotifier;  
  bool onlyEven = false; // depending on this variavble we want only even values or all

  final filteredNotifier = intNotifier.where( (i)=> onlyEven ? i.isEven : i );

The selector function that you pass to where is called on every new value which means your filter criteria has not to be static but can be changed as you need like in the example where it will always use the latest value of onlyEven and not the one it had when where was called.

chaining functions

As all the extension function (with the exception of listen) return a new ValueNotifier we can chain these extension functions as we need them like:

  ValueNotifier<int> intNotifier;  

  intNotifier.where((x)=>x.isEven).map<String>( (s)=>s.toString() ).listen(print);

debounce()

If you don't want or can't handle too rapid value changes debounce is your friend. It only propagate values if there is a pause after a value changes. Most typical example is you have a search function that polls a REST API and in every change of the search term you execute a http request. To avoid overloading your REST server you probably want to avoid that a new request is made on every keypress. I makes much more sense to wait till the user stops modifying the search term for a moment.

  ValueNotifier<String> searchTerm;  //this is the one from the model object

  searchTerm.debounce(const Duration(milliseconds: 500)).listen((s)  => callRestApi(s) );

  // We ignore for this example that calling a REST API probably involves some asynv magic

combineLatest()

Combines two source ValueListenables to one that gets updated with the combined source values when any of the sources values changed. This comes in handy if you want to use one ValueListenableBuilder with two ValueNotifiers.

class StringIntWrapper {
  final String s;
  final int i;

  StringIntWrapper(this.s, this.i);

  @override
  String toString() {
    return '$s:$i';
  }
}


ValueNotifier<int> intNotifier;  
ValueNotifier<String> stringNotifier;  

intNotifier.combineLastest<String,StringIntWrapper>(stringNotifier, (i,s)
   => StringIntWrapper(s,i)).listen(print);

mergeWith

Merges value changes of one ValueListenable together with value changes of a List of ValueListenables so that when ever any of them changes the result of mergeWith() will change too.

final listenable1 = ValueNotifier<int>(0);
final listenable2 = ValueNotifier<int>(0);
final listenable3 = ValueNotifier<int>(0);
final listenable4 = ValueNotifier<int>(0);

listenable1.mergeWith([listenable2, listenable3, listenable4]).listen(
    (x, _) => print(x));

listenable2.value = 42;
listenable1.value = 43;
listenable4.value = 44;
listenable3.value = 45;
listenable1.value = 46;

Will print 42,43,44,45,46

For details on the functions check the source documentation, the tests and the example.

If you miss a function, open an issue on GitHub or even better make an PR :-)

Comments
  • add select operator

    add select operator

    :tophat: What is the goal?

    • ✨ Add select operator that allows only be aware of changes based on the selector output
    • 📝 Updated readme and fixed some typos

    :boom: How can it be tested?

    • 🏗️ Reworked the example model to use the select internally and listen to the properties changes independently.
    • 🤖 Added a unit test to cover the new functionality
    opened by ffgiraldez 5
  • migrated to sound null-safety

    migrated to sound null-safety

    Hey @escamoteur

    This library provides good handy extensions for ValueNotifier. Hence I thought to get it migrated to sound null-safety. If you feel this is needed please feel free to merge it.

    Thanks

    opened by SAGARSURI 3
  • EventNotifier

    EventNotifier

    I create my own subclass of ValueListenable which I called EventNotifier because ValueNotifier only updates the state if the new value is not equal to the old one (which doesn't work for events which might fire with the same value over time).

    Any idea of how such a thing could work with these extensions?

    opened by Norbert515 3
  • Fix double chain subscriptions.

    Fix double chain subscriptions.

    Currently if you use map() (and probably other functions) there are 2 listeners added to the parent listenable One in constructor, and another one as soon as mapped listener gets it's own subscription in addListener (the check is for hasListeners which is false at the time, but the chain is already set up). After mapped subscription is removed, the second chain listener is properly removed but the first one remains. This PR fixes the issue described above.

    opened by ctrl-shift 1
  • Fixed failing debounce test and changed its example in the documentation

    Fixed failing debounce test and changed its example in the documentation

    Example code from the doc and test

    final listenable = ValueNotifier<int>(0);
    listenable
        .debounce(const Duration(milliseconds: 500))
        .listen((x, _) => print(x));
    
    listenable.value = 42;
    await Future.delayed(const Duration(milliseconds: 100));
    listenable.value = 43;
    await Future.delayed(const Duration(milliseconds: 100));
    listenable.value = 44;
    await Future.delayed(const Duration(milliseconds: 350));
    listenable.value = 45;
    await Future.delayed(const Duration(milliseconds: 550));
    listenable.value = 46;
    // will print out 42 ,45,46
    

    But after the execution of the above code, only 45 should be printed out and it does too, because whenever we assign a new value to listenable.value it will be overriding the old value after the debounce time is ended. If we want to print all values, in this example 42, 43, 44, 45, and 46, we need to await for >= debounce time which is (500 in this case) after each assignment. So the code will be like

    final listenable = ValueNotifier<int>(0);
    listenable
        .debounce(const Duration(milliseconds: 500))
        .listen((x, _) => print(x));
    
    listenable.value = 42;
    await Future.delayed(const Duration(milliseconds: 500));
    listenable.value = 43;
    await Future.delayed(const Duration(milliseconds: 500));
    listenable.value = 44;
    await Future.delayed(const Duration(milliseconds: 500));
    listenable.value = 45;
    await Future.delayed(const Duration(milliseconds: 500));
    listenable.value = 46;
    // will print out 42, 43, 44, 45,46
    
    opened by elias8 1
  • How to use combineLatest for more than 2 valuenotifier?

    How to use combineLatest for more than 2 valuenotifier?

    Hey @escamoteur I have a use case where I need to combine 4 valuenotifier. But with the current implementation of combineLatest it's not possible. Any plans for having something like these versions of combineLatest? combineLatest3 combineLatest4

    opened by SAGARSURI 3
Owner
null
Provides null-safety implementation to simplify JSON data handling by adding extension method to JSON object

Lazy JSON Provides null-safety implementation to simplify JSON data handling by adding extension method to JSON object and JSON array. Getting started

Kinnara Digital Studio 0 Oct 27, 2021
Parse and compose Magnet URI extension (BEP53) ranges.

bep53-range Parse and compose Magnet URI extension (BEP53) ranges. Dart port of https://github.com/webtorrent/bep53-range Usage parse Parse Magnet URI

Chiziaruhoma Ogbonda 2 Feb 7, 2022
An extension to the Flutter SDK for building Flutter applications for Tizen devices.

Flutter for Tizen An extension to the Flutter SDK for building Flutter applications for Tizen devices. Flutter and the related logo are trademarks of

null 356 Dec 16, 2022
A pure dart package to apply useful rate limiting strategies on regular functions.

Rate limiting is a strategy for limiting an action. It puts a cap on how often someone can repeat an action within a certain timeframe. Using rate_limiter we made it easier than ever to apply these strategies on regular dart functions.

Stream 24 Dec 14, 2022
FaaS (Function as a service) framework for writing portable Dart functions

Functions Framework for Dart This is a community-supported project, meaning there is no official level of support. The code is not covered by any SLA

Google Cloud Platform 485 Dec 26, 2022
Contains utility functions and classes in the style of dart:collection to make working with collections easier

The collection package for Dart contains a number of separate libraries with utility functions and classes that makes working with collections easier.

Dart 273 Dec 27, 2022
A few handy Flutter tools, dead simple `UriRouter` for `Uri`-based navigator or `BuildTracker` to track widget rebuilds and what caused them to rebuild.

noob A few handy tools for Flutter apps. UriRouter Hooks Providers PointerIndicator BuildTracker PeriodicListenable UriRouter Dead simple Uri-based pa

null 6 Jan 18, 2022
This package binds to Cronet's native API to expose them in Dart.

Experimental Cronet Dart bindings This package binds to Cronet's native API to expose them in Dart. This is an HTTP Client Package with almost the sam

Google 103 Dec 9, 2022
Context is a HashMap with shadowing (inherits parent values) and helpers to work with Types as keys

contextualized Context is a HashMap with shadowing (inherits parent values) and helpers to work with Types as keys Installation pubspec.yaml: dependen

Andrei Lesnitsky 4 Nov 24, 2022
Allows you to cancel compute operation.

cancelable-compute.dart Spawn an isolate, run callback on that isolate, passing it message, and return the value returned by callback or canceled by u

Olzhas Suleimen 8 Oct 7, 2022
A flutter package allows you to search all universities in Brazil

Universities This package allows you to search all universities in Brazil. Usage To use this plugin, add universities as a dependency in your pubspec.

Gabriel Patrick Souza 2 Oct 24, 2021
A flutter package that allows you to transform your excel to json

excel_to_json A package that allows you to transform your excel to the following format: Excel To JSON Getting Started At current the package allows y

Vitor Amaral de Melo 0 Nov 7, 2022
Chuanying - what you see is what you get. 传影--所见即所得

传影--所见即所得 简介 从前 想要制作证件照,需要先把图片用QQ传到电脑,再用PS手动抠图; 看到一句喜欢的话,想要记到电脑里,需要一个字一个字用键盘打出来; 看到黑板上老师写的公式,想要记到Word里,需要用MathType一点点打出来; 但是有没有可能,只用手机拍照,就能搞定上面所有的事,一步

null 16 Apr 8, 2022
Official Git of flutter code-push made by Chimera inc. If you want to get more info or seek for biz corporation, you can contact [email protected].

中文版 Chimera Flutter Code Push Chimera is a Dart compiler developed by ourselves, which generates interpretable and executable bytecode to implement co

Waytoon 21 Oct 6, 2022
An auto mapper for Dart. It allows mapping objects of different classes automatically and manually using JSON serialization.

AutoMapper for Dart An auto mapper for Dart. It allows mapping objects of different classes automatically and manually using JSON serialization. Examp

Leynier Gutiérrez González 7 Aug 24, 2022
Dependency Injection is a great design pattern that allows us to eliminate rigid dependencies between elements and it makes the application more flexible

GetX lib DI pattern Dependency Injection is a great design pattern that allows us to eliminate rigid dependencies between elements and it makes the ap

Trương Việt Hoàng 4 Feb 1, 2022
The `TypedEventNotifier` library allows notifying listeners with an object.

The TypedEventNotifier library allows notifying listeners with an object. listeners can be subscribed to only a special type or group of objects.

Evgeniy Ilyin 0 Nov 13, 2021
Reflectable is a Dart library that allows programmers to eliminate certain usages of dynamic reflection by specialization of reflective code to an equivalent implementation using only static techniques

Reflectable is a Dart library that allows programmers to eliminate certain usages of dynamic reflection by specialization of reflective code to an equivalent implementation using only static techniques. The use of dynamic reflection is constrained in order to ensure that the specialized code can be generated and will have a reasonable size.

Google 318 Dec 31, 2022