Riverpod con anotaciones – utilizando code generation

Riverpod

Inicialmente es importante conocer un poco sobre este gestor de estados y sus características más importantes.

Riverpod es una biblioteca de administración de estado para el framework de desarrollo de aplicaciones Flutter. Fue diseñada para facilitar la gestión del estado en las aplicaciones Flutter de una manera más intuitiva y eficiente. Riverpod es una alternativa a otras soluciones de administración de estado en Flutter, como Provider.

La administración de estado es un aspecto fundamental en el desarrollo de aplicaciones, ya que implica manejar y compartir datos entre diferentes partes de la aplicación, como widgets, pantallas y componentes. Riverpod aborda este desafío proporcionando una sintaxis más clara y flexible para definir, consumir y actualizar el estado en Flutter.

Las características clave de Riverpod incluyen:

  1. Proveedores y Consumidores: En Riverpod, los datos se exponen a través de «proveedores» y se consumen mediante «consumidores». Los proveedores son fuentes de datos que pueden ser accedidas desde cualquier parte de la aplicación, y los consumidores son widgets que se suscriben a estos proveedores para recibir actualizaciones cuando los datos cambian.
  2. Ámbito y Ciclo de Vida: Riverpod ofrece un sistema de ámbitos que permite definir proveedores con diferentes ámbitos de visibilidad y ciclos de vida. Esto significa que puedes controlar cómo se crea y se destruye el estado en función del ciclo de vida de los widgets.
  3. Inyección de Dependencias: Riverpod facilita la inyección de dependencias al proporcionar una manera sencilla de compartir instancias de clases en toda la aplicación. Esto es útil para mantener una única instancia de un objeto en lugar de crear múltiples copias.
  4. Optimización y Rendimiento: Riverpod tiene estrategias incorporadas para optimizar el rendimiento, como la reutilización inteligente de instancias de objetos y la actualización eficiente de widgets solo cuando sea necesario.
  5. Pruebas Unitarias: Riverpod simplifica las pruebas unitarias al permitirte controlar fácilmente los datos proporcionados por los proveedores durante las pruebas.

Instalación y configuración

Empezaremos con la instalación de los paquetes recomendados en la documentación de Riverpod.

  • Debemos copiar el siguiente código, pegarlo en una terminal y dar enter.
flutter pub add \
  flutter_riverpod \
  riverpod_annotation \
  dev:riverpod_generator \
  dev:build_runner \
  dev:custom_lint \
  dev:riverpod_lint
  • Como vamos a usar la opción de code generation debemos ejecutar comando build runner para que tome los cambios de riverpod.
dart run build_runner watch
  • Para habilitar el custom_lint y poder usarlo es necesario agregar la siguiente configuración en el archivo analysis_options.yaml.
analyzer:
  plugins:
    - custom_lint

Se puede ejecutar con el comando: dart run custom_lint.

  • Para usar Riverpod en nuestro proyecto se debe hacer algunos cambios en el archivo main.dart.

Se debe envolver el widget principal en un ProviderScope.

void main() {
    runApp(const ProviderScope(
        child: MyApp(),
        ),
    );
}

Además debemos convertir el StatelessWidget o StatefulWidget en un ConsumerWidget, lo cual requiere agregar un nuevo parámetro en el build un WidgetRef. Con el cual podemos acceder a todos los provider creados con Riverpod.

class MyApp extends ConsumerWidget {
    @override
    Widget build(BuildContext context, WidgetRef ref) {

        return MaterialApp(
            home: Scaffold(
                appBar: AppBar(title: const Text('Provider')),
                body: Center(
                    child: Text('Hello World'),
                ),
            ),
        );
    }
}

Provider de sólo lectura

Los provider de sólo lectura devuelven un valor. Hay que tener varias cosas en cuenta para que se cree correctamente y el generador de código lo tenga en cuenta.

  • Hay que tener el generador de código ejecutándose con el comando: dart run build_runner watch

Podemos crear una función en el main.dart para reemplazar el nombre de «Hello World!». El nombre del parámetro ref que recibe la función debe ser el mismo del nombre de la función con camelcase y acompañado de la palabra Ref al final. Es importante para que el generador de código lo cree correctamente.

String helloWorld(HelloWorldRef ref) {
   return 'Hello World!';
}

Además hay que agregar la anotación @riverpod sobre la función y agregar el código part 'hello_world_provider.g.dart';, para que el generador de código cree el nuevo archivo main.g.dart.

part 'main.g.dart';

@riverpod
String helloWorld(HelloWorldRef ref) {
   return 'Hello World';
}

Al guardar los cambios se genera el nuevo archivo main.g.dart con toda la configuración del nuevo provider helloWorldProvider.

Para acceder a este nuevo Provider se puede hacer mediante el método watch que contiene el parámetro WidgetRef y así obtener su valor.

    class MyApp extends ConsumerWidget {
        @override
        Widget build(BuildContext context, WidgetRef ref) {
            final String helloWorld = ref.watch(helloWorldProvider);

            return MaterialApp(
                home: Scaffold(
                    appBar: AppBar(title: Text('Provider')),
                    body: Center(
                        child: Text(value),
                    ),
                ),
            );
        }
    }

Para acceder al valor de un Provider dentro de una función se debe usar el read y no el watch.

ref.read(appRouterProvider).pop();

State Provider

Creamos un nuevo archivo state_providers.dart para tener allí los providers de estados. Estos providers tienen la particularidad de que son una clase y pueden tener métodos o funciones que cambien el estado del provider. Aquí tenemos una que incrementa en 1 el estado.

import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'state_providers.g.dart';

@riverpod
class Counter extends _$Counter {
  @override
  int build() => 5;

  void increaseByOne() {
    state++;
  }
}

Los pasos para su uso son iguales que para los Providers de sólo lectura.

Algo que si es diferente respecto a los providers de sólo lectura, es la forma en que se usa dentro de las funciones, para acceder a la función increaseByOne del Counter Provider por ejemplo, se hace utilizando un notificador notifier el cual nos da alcance a las funciones de la clase.

ref.read(counterProvider.notifier).increaseByOne();

Los State Provider mantienen el estado sobre el Widget que se esté utilizando, una vez que se sale de éste se reinicia el estado. Una opción para mantenerlo siempre es usar la anotación @Riverpod(keepAlive: true) reemplazando la anotación @riverpod de las clases.

import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'state_providers.g.dart';

@Riverpod(keepAlive: true)
class Counter extends _$Counter {
  @override
  int build() => 5;

  void increaseByOne() {
    state++;
  }
}

Fuentes

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *