Giter Site home page Giter Site logo

functional_listener's Introduction

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<int> 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<String> 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 a ValueListenable can have:

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

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

select()

Lets you react only on changes to selected properties of a ValueListenable value.

This is usefully when you have a complex state model, and only want to react when a specific property change.

  ValueNotifier<User> notifier = ValueNotifier(User(age: 18, property2: "John"));

  final birthdayNotifier = notifier.select<int>((model)=> model.age); //selectedNotifier will ignore changes that does not affect age

The selector function that you pass to select is called on every new value, but only propagate it when the returned value distinct.

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 propagates 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 async 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.

CustomValueNotifier

enum CustomNotifierMode { normal, manual, always }

/// Sometimes you want a ValueNotifier where you can control when its
/// listeners are notified. With the `CustomValueNotifier` you can do this:
/// If you pass [CustomNotifierMode.always] for the [mode] parameter,
/// `notifierListeners` will be called everytime you assign a value to the
/// [value] property independent of if the value is different from the
/// previous one.
/// If you pass [CustomNotifierMode.manual] for the [mode] parameter,
/// `notifierListeners` will not be called when you assign a value to the
/// [value] property. You have to call it manually to notify the Listeners.
/// Aditionally it has a [listenerCount] property that tells you how many
/// listeners are currently listening to the notifier.
class CustomValueNotifier<T> extends ChangeNotifier

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

functional_listener's People

Contributors

escamoteur avatar ffgiraldez avatar sagarsuri avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

functional_listener's Issues

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?

`mergeWith` issue when the listenable has no listeners

See the gist in https://gist.github.com/kekland/31ba4585446e586db7a8aec2ce2ca185

The idea is that the MyFunctionalListenerClass and MyNormalListenerClass should behave the same. The first one uses the .mergeWith() from functional_listener to merge two ValueNotifiers, and the second one uses two listeners.

However, when running the code, pressing "Next functional listener" will sometimes not update the value of the listener. Pressing "Next normal listener" behaves as expected.

License clarification

The license file attached to the project simply states MIT.

Can we assume that you are referring to the last version of the MIT license reported here?
For better clarity, it would be great to add the full license to the project.

Thanks in advance.

syntax for maintaining property in service2 that always reflects value of a ValueNotifier from service1

this relates to escamoteur/watch_it#20 (comment) , where i have a service2, which is needing to know about the current dark mode value. The dark mode ValueNotifier is in service1.

below is what i'm trying. could you help me with a short syntax to achieve it?
if i do this:
final _isDarkMode = GetIt<ThemeState>().isDarkMode.map((x) => x);
then, i get a ValueListenable.

i'm wanting to have some Color get property that will use this _isDarkMode.value to return the Color. but, it appears such a getter property is not reflecting the latest value.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.