GetX Listener and Consumer Library

This library extends the functionality of the GetX package in Flutter, allowing for efficient state management with GetListener and GetConsumer. These widgets make it easy to reactively update the UI based on changes in state.

Features

  • GetListener: A widget that listens to a specific observable variable and triggers a callback when the state changes.
  • GetConsumer: A widget that listens to state changes and also provides a builder function to rebuild part of the UI.

Acknowledgments

This implementation was inspired by Jean Roldan, whose contributions on Stack Overflow provided valuable insights into integrating reactive programming principles in Flutter applications.

Installation

Add the following dependency to your pubspec.yaml file:

dependencies:
  get: ^4.0.0  # or the latest version

Usage

GetListener

GetListener is useful when you need to react to changes in a specific variable from a controller without rebuilding the entire widget tree.

Example

import 'package:get/get.dart';
import 'package:flutter/material.dart';

class MyController extends GetxController {
  final RxInt counter = 0.obs;
  final Rx<RequestState<List<String>>> feedsRequestState = RequestState<List<String>>.initial().obs;

  void increment() => counter++;
  void decrement() => counter--;

  /// Fetch data and update the state
  Future<void> fetchFeeds() async {
    try {
      feedsRequestState.value = RequestState.loading();
      await Future.delayed(const Duration(seconds: 2));
      feedsRequestState.value = RequestState.success(['Feed 1', 'Feed 2', 'Feed 3']);
    } catch (error) {
      feedsRequestState.value = RequestState.error('Failed to fetch feeds');
    }
  }
}

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

  @override
  Widget build(BuildContext context) {
    final MyController controller = Get.put(MyController());
    
    return Scaffold(
      appBar: AppBar(title: Text(title)),
      body: Center(
        child: GetListener<RequestState>(
          valueRx: controller.feedsRequestState,
          listener: (context, state) {
            if (state is Error) {
              ScaffoldMessenger.of(context).showSnackBar(
                SnackBar(
                  content: Text(state.errorMessage),
                  duration: const Duration(seconds: 3),
                  backgroundColor: Colors.red,
                ),
              );
            }
            if (state is Success<List<String>>) {
              ScaffoldMessenger.of(context).showSnackBar(
                SnackBar(
                  content: Text('Fetched ${state.data.length} items successfully!'),
                  duration: const Duration(seconds: 2),
                  backgroundColor: Colors.green,
                ),
              );
            }
          },
          child: GetConsumer(
            controller: controller,
            valueRx: controller.counter,
            listener: (context, state) {
              if (state % 2 == 0) {
                ScaffoldMessenger.of(context).showSnackBar(
                  const SnackBar(
                    content: Text('Hello! This is a SnackBar message.'),
                    duration: Duration(seconds: 2),
                    backgroundColor: Colors.blue,
                  ),
                );
              }
            },
            builder: (context, state) {
              return Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  const Text('You have pushed the button this many times:'),
                  Text(
                    state.toString(),
                    style: Theme.of(context).textTheme.headlineMedium,
                  ),
                ],
              );
            },
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: controller.increment,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

RequestState Class

This class represents different states of a request:

abstract class RequestState<T> {
  const RequestState();

  factory RequestState.initial() = Initial<T>;
  factory RequestState.loading() = Loading<T>;
  factory RequestState.success(T data) = Success<T>;
  factory RequestState.error(String errorMessage) = Error<T>;
}

class Initial<T> extends RequestState<T> {}
class Loading<T> extends RequestState<T> {}

class Success<T> extends RequestState<T> {
  final T data;
  Success(this.data);
}

class Error<T> extends RequestState<T> {
  final String errorMessage;
  Error(this.errorMessage);
}

License

This project is licensed under the MIT License. See the LICENSE file for more details.

Libraries

get_consumer/get_consumer
get_listener/get_listener
getx_exten
Support for doing something awesome.