Autoverpod

A package providing annotations for creating Riverpod-powered widgets with automated code generation.

Features

  • Simplifies the creation of Riverpod widgets through code generation
  • Provides type-safe annotations that integrate with Riverpod's ecosystem
  • Supports form widgets via the @formWidget annotation for both create and update forms
  • Supports state management widgets via the @stateWidget annotation
  • Reduces boilerplate code and enforces best practices

Installation

To use autoverpod in your project, you need both the annotation package and the generator:

dependencies:
  autoverpod: ^0.0.3
  # riverpod generation dependancies

dev_dependencies:
  autoverpod_generator: ^0.0.1
  build_runner: ^2.4.0  # Required for code generation

Configuration

To prevent lint errors related to Riverpod's notifier_extends rule, add the following to your analysis_options.yaml file:

analyzer:
  plugins:
    - custom_lint
  errors:
    invalid_annotation_target: ignore

custom_lint:
  rules:
    - notifier_extends: false  # Allows providers to extend custom classes instead of generated ones

This configuration disables the lint rule that requires providers to extend the generated Riverpod provider classes, which is necessary when using Autoverpod's custom widget extensions.

Usage

Autoverpod works together with Riverpod annotations. You must apply both a Riverpod annotation (like @riverpod) and an autoverpod annotation (like @formWidget or @stateWidget) to the same class or function.

Form Widget Example

The @formWidget annotation is used to create form widgets with built-in state management:

import 'dart:typed_data';

import 'package:autoverpod/autoverpod.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'example.freezed.dart';
part 'example.g.dart';

// Define your form model (using Freezed is recommended)
@freezed
class UpdateUserModel with _$UpdateUserModel {
  const UpdateUserModel._();

  const factory UpdateUserModel({
    @Default('') String name,
    int? age,
    String? email,
  }) = _UpdateUserModel;

  factory UpdateUserModel.fromJson(Map<String, dynamic> json) => 
      _$UpdateUserModelFromJson(json);
}

// Create your form widget with @formWidget and @riverpod annotations
@formWidget
@riverpod
class UpdateUser extends _$UpdateUserWidget {
  @override
  Future<UpdateUserModel> build(int id) async {
    // For editing existing users: fetch and return initial form data
    // For new users: return empty model
    return const UpdateUserModel(
      name: 'John Doe',
      age: 30,
    );
  }

  @override
  Future<bool> submit(
    UpdateUserModel state, {
    Uint8List? photoBytes,
  }) async {
    // Validate and submit form data
    if (state.name.isEmpty) throw 'Name is required';
    
    // Call your repository/API
    return await ref.read(userRepositoryProvider).updateUser(id, state);
  }

  @override
  void onSuccess(bool result) {
    // Handle successful submission
    invalidateSelf();
  }
}

// Usage in UI
void main() {
  runApp(
    ProviderScope(
      child: MaterialApp(
        home: Scaffold(
          body: UpdateUserFormScope(
            id: 1,
            builder: (context, ref, _) {
              return Column(
                children: [
                  // Field widgets are auto-generated
                  UpdateUserNameField(builder: (context, ref) {
                    return TextFormField(controller: ref.textController);
                  }),
                  
                  UpdateUserAgeField(builder: (context, ref) {
                    return TextFormField(
                      onChanged: (value) {
                        ref.updateAge(int.tryParse(value));
                      },
                    );
                  }),
                  
                  // Form submission status and button
                  UpdateUserFormStatus(
                    builder: (context, ref, status) {
                      return TextButton(
                        onPressed: () async {
                          final result = await ref.submit();
                          // Handle result
                        },
                        child: Text(status!.isLoading ? 'Saving...' : 'Save'),
                      );
                    },
                  )
                ],
              );
            },
          ),
        ),
      ),
    ),
  );
}

State Widget Examples

The @stateWidget annotation is used to create widgets that consume Riverpod state:

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:autoverpod/autoverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'example.g.dart';

// Class-based provider with parameters
@stateWidget
@riverpod
class ProductDetail extends _$ProductDetail {
  @override
  FutureOr<String> build(int productId) {
    return 'Product Detail $productId';
  }
}

// Function-based provider
@riverpod
@stateWidget
int counter(Ref ref) {
  return 1;
}

// Class-based provider with multiple parameters
@riverpod
@stateWidget
class StringFuture extends _$StringFuture {
  @override
  Future<String> build({
    required String family,
    required String second,
  }) async {
    return 'string';
  }
}

// Provider with optional parameters
@riverpod
@stateWidget
class StringFutureOptional extends _$StringFutureOptional {
  @override
  Future<String> build(
    int a, {
    required String family,
    String? second,
  }) async {
    return 'string';
  }
}

// Async provider with Stream
@riverpod
@stateWidget
Stream<int> counterStream(Ref ref, {required int initialValue}) async* {
  yield initialValue;
}

Code Generation

After annotating your classes or functions, run the generator:

flutter pub run build_runner build

The generator will create:

  • Provider implementations
  • Widget components for form fields
  • Helper methods for state management
  • Type-safe interfaces for your widgets

Error Handling

Autoverpod enforces that widget annotations must be used together with Riverpod annotations. If you use an autoverpod annotation without a corresponding Riverpod annotation, you'll receive a compile-time error.

Additional Information

This package is designed to work seamlessly with the Riverpod ecosystem. It's part of the kimapp project, which aims to simplify Flutter development with Riverpod.

License

This package is licensed under the MIT License - see the LICENSE file for details.

Libraries

autoverpod
Autoverpod library