搜索插件、代码仓与 APP 分类

插件详情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 等可观察集合,使开发者能够像使用 Streams 一样操作 ValueNotifiers。它提供了强大的操作符来转换和组合数据流,促进函数式响应式编程风格。最初发布为 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 项

源码仓库

使用此插件的项目