qm 0.1.0 copy "qm: ^0.1.0" to clipboard
qm: ^0.1.0 copied to clipboard

Locked Async Notifiers for Queries and Mutations

QM - Query & Mutation State Management #

A lightweight Flutter package for managing async operations with Queries and Mutations. Built on top of ValueNotifier and locked_async, QM provides a simple, type-safe way to handle loading states, errors, and data in your Flutter applications.

Why QM? #

QM uses locked_async under the hood to ensure only one operation runs at a time. When a new operation starts, the previous one is automatically cancelled. No more race conditions, no manual state management.

Checkout the locked_async package for more details why we need this.

Installation #

Add to your pubspec.yaml:

dependencies:
  qm: ^0.1.0

Usage #

Query - Automatic Data Fetching #

A Query runs automatically when created and manages loading, success, and error states.

import 'package:qm/qm.dart';

// Create a query
final userQuery = Query<User>((state) async {
  final response = await http.get('/api/user');
  return User.fromJson(response.body);
});

// Use in a widget
class UserProfile extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder(
      valueListenable: userQuery,
      builder: (context, state, child) {
        return state.when(
          loading: () => CircularProgressIndicator(),
          data: (user) => Text('Hello, ${user.name}!'),
          failed: (error, _) => Column(
            children: [
              Text('Error: $error'),
              ElevatedButton(
                onPressed: userQuery.restart,
                child: Text('Retry'),
              ),
            ],
          ),
        );
      },
    );
  }
}

Mutation - User-Triggered Actions #

A Mutation starts idle and only runs when explicitly called with arguments.

import 'package:qm/qm.dart';

// Create a mutation
final updateUser = Mutation<User, String>((name, state) async {
  final response = await http.post('/api/user', body: {'name': name});
  return User.fromJson(response.body);
});

// Use in a widget
class UpdateNameForm extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ValueListenableBuilder(
      valueListenable: updateUser,
      builder: (context, state, child) {
        return Column(
          children: [
            ElevatedButton(
              onPressed: () => updateUser.run('John'),
              child: state.isLoading
                ? CircularProgressIndicator()
                : Text('Update Name'),
            ),
            state.map(
              idle: () => SizedBox(),
              running: () => Text('Updating...'),
              data: (user) => Text('Updated: ${user.name}'),
              failed: (error, _) => Column(
                children: [
                  Text('Error: $error'),
                  TextButton(
                    onPressed: updateUser.retry,
                    child: Text('Retry'),
                  ),
                ],
              ),
            ),
          ],
        );
      },
    );
  }
}

Multiple Arguments in Mutations

Use classes or records for complex arguments:

final updateProfile = Mutation<User, ({String name, int age})>(
  (args, state) async {
    final response = await http.post('/api/user', body: {
      'name': args.name,
      'age': args.age,
    });
    return User.fromJson(response.body);
  },
);

// Call it
updateProfile.run((name: 'John', age: 30));
5
likes
140
points
37
downloads

Publisher

verified publisherdickersystems.com

Weekly Downloads

Locked Async Notifiers for Queries and Mutations

Homepage
Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

equatable, flutter, locked_async

More

Packages that depend on qm