패키지, 저장소 및 앱 카테고리 검색

패키지 상세v6.0.0

listen_it

Flutter용 반응형 기본 요소 - 관찰 가능한 컬렉션과 강력한 연산자입니다. Streams처럼 ValueNotifiers를 사용하세요. ListNotifier, MapNotifier, SetNotifier 포함. 이전에 functional_listener로 출판되었습니다.

AndroidiOSWebmacOSWindowsLinux
30일 다운로드
1.1만
좋아요
16
Pub 점수
160

패키지 설치

flutter pub add listen_it

패키지 개요

공개 정보를 바탕으로 생성한 선택 가이드입니다.

ListenIt는 Flutter용 반응형 상태 관리 패키지로, ListNotifier, MapNotifier, SetNotifier와 같은 관찰 가능한 컬렉션을 제공하여 ValueNotifiers를 Streams처럼 사용할 수 있게 해줍니다. 데이터 스트림을 변환하고 결합하기 위한 강력한 연산자를 제공하며, 함수형 반응형 프로그래밍 스타일을 촉진합니다. 원래 functional_listener로 출시되었으며, 최소한의 부가 코드로 상태 업데이트와 구독을 간소화합니다. 반응형 UI에서 동적 데이터를 관리하는 데 이상적입니다.

사용 사례

  • 실시간 데이터 동기화
  • 동적 목록 또는 맵 상태 관리
  • 최소한의 코드로 반응형 UI 업데이트
  • ValueNotifier에 대한 스트림과 유사한 동작
  • 복잡한 상태 전환

주요 기능

  • 관찰 가능한 컬렉션(ListNotifier, MapNotifier, SetNotifier)
  • ValueNotifiers용 스트림 유사 연산자
  • 함수형 반응형 프로그래밍 모델
  • 최소한의 보일러플레이트
  • 크로스 플랫폼 지원

적합한 대상

  • 경량 반응형 상태 관리를 찾는 개발자
  • 반응형 컬렉션이 필요한 프로젝트
  • 함수형 프로그래밍 패턴에 익숙한 팀
  • 자주 동적 데이터가 업데이트되는 앱
  • 다중 플랫폼을 대상으로 하는 Flutter 앱

고려 사항

  • 반응형 프로그래밍 개념을 배워야 할 수 있습니다
  • 중간에서 복잡한 상태 로직에 가장 적합
  • 간단한 정적 상태에 적합하지 않음
  • 매우 큰 데이터 세트 사용 시 성능 영향
  • 성숙한 대안에 비해 문서화가 제한적

원본 README

연결된 소스 저장소의 README 원문 스냅샷입니다. 최신 내용은 저장소에서 확인하세요.

listen_it logo
Buy Me A Coffee
Sponsor

listen_it codecov

📚 Complete documentation available at flutter-it.dev Check out the comprehensive docs with detailed guides, examples, and best practices!

Reactive primitives for Flutter - observable collections and powerful operators for ValueListenable.

Managing reactive state in Flutter can be complex. You need collections that notify listeners when they change, operators to transform and combine observables, and patterns that don't cause memory leaks. listen_it provides two powerful primitives: reactive collections (ListNotifier, MapNotifier, SetNotifier) that automatically notify on mutations, and extension operators on ValueListenable (map, select, where, debounce, combineLatest) that let you build reactive data pipelines.

Previously published as functional_listener. Now includes reactive collections from listenable_collections.

flutter_it is a construction set — listen_it works perfectly standalone or combine it with other packages like watch_it (which provides automatic selector caching for safe inline chain creation!), get_it (dependency injection), or command_it (which uses listen_it internally). Use what you need, when you need it.

Why listen_it?

  • 🔔 Reactive Collections — ListNotifier, MapNotifier, SetNotifier that automatically notify listeners on mutations. No manual notifyListeners() calls needed.
  • 🔗 Chainable Operators — Transform, filter, combine ValueListenables with map(), select(), where(), debounce(), combineLatest(), mergeWith().
  • 🎯 Selective Updates — React only to specific property changes with select(). Avoid unnecessary rebuilds.
  • ⚡ Transaction Support — Batch multiple operations into a single notification for optimal performance.
  • 🔒 Type Safe — Full compile-time type checking. No runtime surprises.
  • 📦 Pure Dart Core — Operators work in pure Dart (collections require Flutter for ChangeNotifier).

Learn more about listen_it →

💡 Chain lifecycle (v6.0.0+): Operator chains subscribe to their source eagerly on creation (or on the first listener with lazy: true), detach from the source when their last listener is removed and re-attach on the next one. While a chain has no listeners its .value is derived from the current source value on read, so it is never stale, and a chain that nobody references any more can be garbage collected. For best practices, see the complete documentation.

Quick Start

Installation

Add to your pubspec.yaml:

dependencies:
  listen_it: ^5.1.0
Reactive Collections

Simply wrap your collection type with a notifier:

// Instead of:
final items = <String>[];

// Use:
final items = ListNotifier<String>();

// With initial data:
final items = ListNotifier<String>(data: ['item1', 'item2']);

All standard collection methods work as expected - the difference is they now notify listeners!

Integration with Flutter
class TodoListWidget extends StatelessWidget {
  final todos = ListNotifier<String>();

  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder<List<String>>(
      valueListenable: todos,
      builder: (context, items, _) {
        return ListView.builder(
          itemCount: items.length,
          itemBuilder: (context, index) => Text(items[index]),
        );
      },
    );
  }
}
ValueListenable Operators
listen()

Lets you work with a ValueListenable (and Listenable) as it should be by installing a handler function that is called on any value change and gets the new value passed as an argument. This gives you the same pattern as with Streams, making it natural and consistent.

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

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

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

This is particularly useful when you want a handler to run only once or a certain number of times:

// Run only once
listenable.listen((x, subscription) {
  print('First value: $x');
  subscription.cancel();
});

// Run exactly 3 times
var count = 0;
listenable.listen((x, subscription) {
  print('Value: $x');
  if (++count >= 3) subscription.cancel();
});

For regular Listenable (not ValueListenable), the handler only receives the subscription parameter since there's no value to access:

final listenable = ChangeNotifier();
listenable.listen((subscription) => print('Changed!'));
Chaining Operators

Chain operators to build reactive data pipelines:

final searchTerm = ValueNotifier<String>('');

searchTerm
    .debounce(const Duration(milliseconds: 300))
    .where((term) => term.length >= 3)
    .listen((term, _) => callSearchApi(term));

That's it! Collections notify automatically, operators let you transform data reactively.

Key Features

Reactive Collections

Choose the collection that fits your needs:

  • ListNotifier — Order matters, duplicates allowed. Perfect for: todo lists, chat messages, search history. Read more →

  • MapNotifier<K,V> — Key-value lookups. Perfect for: user preferences, caches, form data. Read more →

  • SetNotifier — Unique items only, fast membership tests. Perfect for: selected item IDs, active filters, tags. Read more →

Notification Modes:

  • always (default) — Notify on every operation
  • normal — Only notify on actual changes
  • manual — You control when to notify

Read more about notification modes →

Transactions — Batch operations into single notification:

products.startTransAction();
products.add(item1);
products.add(item2);
products.add(item3);
products.endTransAction(); // Single notification

Read more →

ValueListenable Operators

Transform and combine observables:

  • listen() — Install handlers that react to value changes. The foundation for reactive programming with ValueListenables.

    listenable.listen((value, subscription) => print(value));
    
  • map() — Transform values to different types

  • select() — React only when specific properties change

  • where() — Filter which values propagate (now with optional fallbackValue for initial value handling!)

  • debounce() — Control rapid value changes (great for search!)

  • async() — Defer updates to next frame to avoid setState-during-build

  • combineLatest() — Merge multiple ValueListenables (supports 2-6 sources)

  • mergeWith() — Combine value changes from multiple sources

Read operator documentation →

Chain Lifecycle: Attach, Detach and Derived Values (v6.0.0+)

An operator chain (source.map(...), a.combineLatest(b, ...), ...) is a ValueListenable that follows this lifecycle:

  1. Attach - the chain subscribes to its source(s) when it is created (default) or, with lazy: true, when it gets its first listener.
  2. Detach - when the last listener is removed, the chain unsubscribes from its source(s). In a longer chain this cascades down to the original source.
  3. Re-attach - the next addListener subscribes again and refreshes the stored value before the new listener is registered, so nothing is missed and no spurious notification is sent.

While a chain is detached its .value is derived from the current source value on read, so it is never stale:

final source = ValueNotifier<int>(1);
final doubled = source.map((x) => x * 2);

void listener() {}
doubled.addListener(listener);
doubled.removeListener(listener); // last listener gone -> detached

source.value = 5;
print(doubled.value); // 10 ✓ derived on read, no subscription needed

doubled.addListener(listener); // re-attached, value already fresh
Operator .value while detached
map, select transform / selector applied to the current source value
where current source value if it passes the filter, otherwise the last passing value
debounce, async current source value
combineLatest combiner applied to the current source values
mergeWith last received value (it can't know which source changed last)

Because the transformation may run on read while detached, keep transformation functions pure.

lazy: true only changes step 1: the first subscription happens on the first listener instead of on creation. Before that, .value is derived on read exactly as for a detached chain, so lazy: true is a pure memory optimisation with no stale-value trade-off.

Chain Lifecycle & Memory Management

Because a chain releases its source as soon as nobody listens to it any more, a chain that is created for a widget and discarded with it no longer leaves a dangling listener on the source. With watch_it, creating chains inline in a selector is therefore safe:

class MyWidget extends WatchingWidget {
  @override
  Widget build(BuildContext context) {
    // selector is cached (called once per widget instance); when the widget
    // is disposed the chain loses its listener and detaches from m.source
    final value = watchValue((Model m) => m.source.map((x) => x * 2));
    return Text('$value');
  }
}

Two things still matter:

❌ Don't create chains on every rebuild - each one is a new object doing work while it is attached:

Widget build(BuildContext context) {
  return ValueListenableBuilder(
    valueListenable: source.map((x) => x * 2), // NEW CHAIN EVERY REBUILD!
    builder: (context, value, _) => Text('$value'),
  );
}

Create the chain once instead (a field, late final, or createOnce with watch_it).

✅ Chains you keep alive yourself (e.g. as a field of a long-lived manager) stay attached while they have listeners and re-attach when needed - just like any other ValueListenable.

Disposal & Garbage Collection

Chains don't require manual disposal in most cases. A chain without listeners holds no subscription on its source, so it is garbage collected as soon as nothing references it any more; and when the whole graph (source + chain) becomes unreachable, Dart's GC collects it regardless.

Call dispose() on a chain only when you want to end it explicitly while it still has listeners, or to be sure a pending debounce timer / async update is cancelled.

class MyService {
  final counter = ValueNotifier<int>(0);
  late final doubled = counter.map((x) => x * 2);

  void dispose() {
    counter.dispose(); // stops notifications; the chain is GC'd with the service
  }
}

Read complete disposal guide →

Read complete best practices guide →

Ecosystem Integration

listen_it works independently — Use it standalone for reactive collections and operators in any Dart or Flutter project.

Want more? Combine with other packages from the flutter_it ecosystem:

  • Optional: watch_it — Reactive state management with automatic selector caching. Makes inline chain creation safe! Highly recommended for listen_it operator chains.

  • Optional: get_it — Dependency injection. Register your ListNotifiers, ValueNotifiers, and chains in get_it for global access.

  • Optional: command_it — Command pattern with automatic state tracking. Uses listen_it operators internally.

Remember: flutter_it is a construction set. Each package works independently. Pick what you need, combine as you grow.

Learn about the ecosystem →

AI-Assisted Development

This package ships an Agent Skill for AI coding assistants (Claude Code, Cursor, GitHub Copilot, Codex, Gemini CLI and others) in skills/listen-it-expert/. It teaches them the critical rules, common patterns and anti-patterns of listen_it. Install it into your project with the official Dart skills tool:

dart run skills@ get

For the ecosystem-wide skills (architecture guidance, feed/data-source patterns, overview) and the skills of the other flutter_it packages run:

dart run skills@ add flutter-it/flutter_it

Learn more about AI skills →

Learn More

Documentation
Community & Support

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT License - see LICENSE file for details.


Part of the flutter_it ecosystem — Build reactive Flutter apps the easy way. No codegen, no boilerplate, just code.

카테고리

의존성

의존성 목록 1 개

소스 저장소

이 패키지를 사용하는 프로젝트