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

插件详情v0.2.1

syncx

syncx 是一个轻量级、灵活的 Flutter 状态管理库,具有通知器和响应式 UI 更新功能,灵感来自 provider、bloc 和 riverpod。

AndroidiOSWebmacOSWindowsLinux
近 30 天下载
154
喜欢数
3
Pub 分数
160

安装插件

flutter pub add syncx

插件介绍

基于插件公开信息、社区数据与项目使用情况整理的选型参考。

SyncX 是一个轻量级、灵活的状态管理库,专为 Flutter 设计,通过通知器实现响应式 UI 更新。受 Provider、Bloc 和 Riverpod 的启发,它提供了一种简单而强大的方式来管理应用状态,且代码冗余极少。SyncX 支持依赖注入,并可无缝集成到所有主要平台,非常适合构建可扩展且易于维护的 Flutter 应用程序。其设计强调清晰性、性能和易用性。

适合场景

  • 管理全局应用状态
  • 基于状态更改的响应式 UI 更新
  • 大型应用程序中的依赖注入
  • 简化复杂控件中的状态逻辑
  • 在多个屏幕之间共享数据

主要特点

  • 轻量且快速
  • 支持通知器和响应式更新
  • 跨平台兼容性
  • 最小化样板代码
  • 受流行状态管理模式的启发
  • 与现有 Flutter 项目轻松集成

适合对象

  • 寻求 Provider 或 Bloc 简单替代方案的开发者
  • 需要高效状态共享的项目
  • 希望拥有简洁、可扩展架构的团队
  • Flutter 应用针对多个平台
  • 需要响应式 UI 但无复杂性的应用

使用建议

  • 对于不熟悉通知器模式的初学者,可能需要一定的学习曲线
  • 与成熟库相比,文档有限
  • 更适合中大型应用,而非非常小型的应用
  • 不推荐在没有自定义的情况下用于具有极其复杂状态逻辑的应用程序

原始 README

以下为关联源码仓库的 README 原文快照,最新内容请访问源码仓库。

SyncX

Flutter state management made easy: SyncX is a lightweight, flexible state management solution for Flutter. It provides notifiers, builders, and consumers for reactive UI updates with minimal boilerplate. SyncX is designed for developers who want a simple, composable, and powerful way to manage state in their Flutter apps.

Note: The implementation and API of SyncX are inspired by popular state management solutions such as bloc, provider, and riverpod. Naming conventions and patterns are chosen to be developer-friendly, especially for those migrating from or familiar with these libraries.


Table of Contents


Why SyncX?

  • Simple and familiar API for Flutter state management.
  • Reactive UI updates with rebuild control: Widgets rebuild on state changes, with control over when they update.
  • Notifier pattern: Cleanly separates business logic from UI code.
  • Effortless async state handling: Easily manage loading, data, and error states.
  • Minimal boilerplate: Quick to set up and easy to integrate into any Flutter project.

Features

  • 🔄 Notifier-based state management: Simple, extendable notifiers for your app's state.
  • 🏗️ Builder widgets: Easily rebuild UI in response to state changes.
  • 👂 Listener widgets: React to state changes with side effects.
  • 🪶 Minimal boilerplate: Focus on your app logic, not on wiring up state.
  • ⚡ Async state support: Built-in support for loading, data, and error states in async flows.

Core Concepts & Usage

1. Create a Notifier

A Notifier holds and updates your state:


/// Extend [Notifier] for basic state management
/// update state and notify listeners using [setState].
class CounterNotifier extends Notifier<int> {
  /// Creates a [CounterNotifier] with an initial state of 0.
  CounterNotifier() : super(0);

  /// Optionally override [onInit] to perform initialization logic when the notifier is first created.
  ///
  /// This method is called automatically once the instance is created.
  /// Use it to initialize resources or start listeners if needed.
  @override
  void onInit(){
    // doSomething...
  }

  /// Increments the counter by 1 and notifies listeners.
  ///
  /// Use [setState] to update the state and notify any listening widgets.
  ///
  /// [setState] also accepts two optional parameters:
  ///   - [forced] (default: false): Forces the state update even if the value hasn't changed, useful for mutating iterable state.
  ///   - [notify] (default: true): Controls whether listeners are notified and the UI is rebuilt.
  void increment() => setState(state + 1);
}

For async state (loading/data/error):

/// Extend [AsyncNotifier] to handle async operations such as network calls 
/// to handle loading, data, and error states using [AsyncState].
class GreetingAsyncNotifier extends AsyncNotifier<String> {
  /// Creates a [GreetingAsyncNotifier] with an initial state.
  GreetingAsyncNotifier() : super();

  /// Called when the notifier is first initialized.
  ///
  /// This method is called automatically once the instance is created.
  ///
  /// It simulates a network call by delaying for 2 seconds.
  /// If the operation is successful, it returns an [AsyncState.data] with a greeting message.
  /// Otherwise, it returns an [AsyncState.error] with an error message.
  @override
  Future<AsyncState<String>> onInit() async {
    // Consider this as a Network call
    await Future.delayed(const Duration(seconds: 2));
    final bool isSuccess = true; // Set to false to simulate error
    if (isSuccess) {
      return const AsyncState.data('Hello from AsyncNotifier!');
    }
    return const AsyncState.error('Failed to load greeting');
  }

  /// Updates the state by simulating an asynchronous operation.
  ///
  /// This method demonstrates how to manually set the state to loading,
  /// perform an async task, and then update the state to either data or error.
  ///
  /// - Sets the state to loading before starting the async operation.
  /// - If successful, sets the state to data with a success message.
  /// - If failed, sets the state to error with an error object and message.
  Future<void> updateState() async {
    setState(state.toLoading());
    // Consider this as a Network call
    await Future.delayed(const Duration(seconds: 2));
    final bool isSuccess = true; // Set to false to simulate error
    if (isSuccess) {
      return setState(state.toData('Success'));
    }

    // Replace 'errorObject' with your actual error object as needed
    return setState(
      state.toError('..errorObject', message: 'Network call failed'),
    );
  }
}
2. Register the Notifier

Register your notifier at the root of your widget tree:

NotifierRegister(
  create: (context) => CounterNotifier(),
  child: MyApp(),
)

You can nest registers for multiple notifiers:

NotifierRegister(
  create: (context) => CounterNotifier(),
  child: NotifierRegister(
    create: (context) => GreetingAsyncNotifier(),
    child: MyApp(),
  ),
)
3. Consume State with Builders & Listeners

Rebuild UI on State Change:

NotifierBuilder<CounterNotifier, int>(
  builder: (context, count) => Text('Count: $count'),
)

Control rebuilds:

NotifierBuilder<CounterNotifier, int>(
  buildWhen: (prev, curr) => prev != curr,
  builder: (context, count) => Text('Count: $count'),
)

Listen for Side Effects:

NotifierListener<CounterNotifier, int>(
  listenWhen: (prev, curr) => curr % 2 == 0,
  listener: (count) => print('Count is even: $count'),
  child: MyWidget(),
)

Combine Build and Listen:

NotifierConsumer<CounterNotifier, int>(
  builder: (context, count) => Text('Count: $count'),
  listener: (count) => print('Count changed: $count'),
)

Async State Handling:

SyncX provides specialized widgets for handling asynchronous state:

  • AsyncNotifierBuilder – a wrapper over NotifierBuilder for async state, providing a convenient API for loading, data, and error UI.
  • AsyncNotifierConsumer – combines building and listening for side effects in async flows.
  • AsyncNotifierListener – listens for async state changes and triggers side effects without rebuilding the UI.

Direct usage with builder:

AsyncNotifierBuilder<GreetingAsyncNotifier, String>(
  builder: (context, state) {
    return state.when(
      loading: () => const CircularProgressIndicator(),
      data: (greeting) => Text(greeting),
      error: (error) => Text('Error: \\${error.message}'),
    );
  },
)

Convenient usage with .withData:

AsyncNotifierBuilder<GreetingAsyncNotifier, String>.withData(
  loadingBuilder: () => CircularProgressIndicator(),
  dataBuilder: (greeting) => Text(greeting),
  errorBuilder: (error) => Text('Error: \\${error.message}'),
)

You can use AsyncNotifierConsumer and AsyncNotifierListener similarly to handle both UI and side effects for async state.

4. Invoking Notifier Methods

To invoke a method inside your notifier (for example, to increment the counter), use the context extension or provider pattern in your widget. Here’s how you can call a notifier method from a button press:

ElevatedButton(
  onPressed: () => context.read<CounterNotifier>().increment(),
  child: const Text('Increment'),
)

This will call the increment method on your CounterNotifier and update the state accordingly.


Bad Practices / Don’ts
  • Don’t: Do not call the notifier’s onInit manually from builder, consumer, or listener onInit callbacks. This will result in duplicate initialization and unexpected behavior. The onInit method inside your notifier will be called automatically once the notifier instance is created. You do not need to call it yourself.

    Incorrect usage example:

    NotifierBuilder<Notifier, State>(
      // Do NOT do this:
      onInit: (notifier) => notifier.onInit(),
      // Do this if you need to call your own custom initialization logic when the widget is created:
      onInit: (notifier) => notifier.doSomethingElse(),
      builder: (context, state) => Text('State: $state'),
    )
    

Usage Patterns

  • Rebuild UI on State Change: Use NotifierBuilder with buildWhen for fine-grained rebuild control.
  • Listen for Side Effects: Use NotifierListener with listenWhen for side-effect logic.
  • Combine Build and Listen: Use NotifierConsumer for both UI and side effects.
  • Async State Handling: Use AsyncNotifier and AsyncState for loading, data, and error flows.

Future Plans

  • Built-in stream support in notifiers for easy loading and error state management.
  • Top-level observer support to monitor state changes across the app.
  • Migrate dependency injection (DI) from provider to a custom DI implementation for more flexibility and control.

Contributing

Contributions are welcome! Please open issues or pull requests on GitHub.


License

This project is licensed under the MIT License.


If you have questions or need help, please open an issue or start a discussion on GitHub.

所属分类

依赖

依赖清单 1 项

源码仓库

使用此插件的项目

暂无相关数据。