smart_response_builder 1.0.1 copy "smart_response_builder: ^1.0.1" to clipboard
smart_response_builder: ^1.0.1 copied to clipboard

A smart widget for handling API responses, offline states, and pagination in Flutter.

📦 smart_response_builder #

Banner

pub package popularity likes pub points building style: effective dart Flutter License

A Flutter widget to handle API states with ease — loading, error, empty, offline, and pagination in one unified solution.


✨ Features #

  • ✅ Unified handling of Loading, Error, Empty, and Data states
  • ✅ Built-in support for Offline state with customizable retry
  • ✅ Seamless Pagination support (isLoadingMore, hasMore, paginationError)
  • ✅ Works with any state management (GetX, Provider, Riverpod, BLoC, etc.)
  • ✅ Highly customizable builders for full UI control
  • ✅ Lightweight, no heavy dependencies

📥 Installation #

Add the dependency to your pubspec.yaml:

dependencies:
  smart_response_builder:
    git:
      url: https://github.com/mirzamahmud/smart_response_builder.git

or

dependencies:
  smart_response_builder: ^1.0.0

or

flutter pub add smart_response_builder

Then run

flutter pub get

🚀 Usage #

🔹 Basic Example #

ResponseBuilder<List<String>>(
  isLoading: false,
  data: ["Apple", "Banana", "Orange"],
  dataBuilder: (context, data) => ListView(
    children: data.map((e) => ListTile(title: Text(e))).toList(),
  ),
);

🧩 Using with State Management #

🔹 Provider #

class ProductProvider extends ChangeNotifier {
  List<String>? products;
  bool isLoading = false;
  String? error;

  Future<void> fetchProducts() async {
    isLoading = true;
    notifyListeners();
    try {
      // API call
      products = ["Apple", "Banana"];
    } catch (e) {
      error = e.toString();
    } finally {
      isLoading = false;
      notifyListeners();
    }
  }
}

Consumer<ProductProvider>(
  builder: (context, provider, _) {
    return ResponseBuilder<List<String>>(
      data: provider.products,
      isLoading: provider.isLoading,
      error: provider.error,
      onRetry: provider.fetchProducts,
      dataBuilder: (context, data) => ListView(
        children: data.map((e) => ListTile(title: Text(e))).toList(),
      ),
    );
  },
);

🔹 GetX #

class ProductController extends GetxController {
  final products = <String>[].obs;
  final isLoading = false.obs;
  final error = RxnString();

  Future<void> fetchProducts() async {
    try {
      isLoading.value = true;
      // fetch API
      products.value = ["Apple", "Banana"];
    } catch (e) {
      error.value = e.toString();
    } finally {
      isLoading.value = false;
    }
  }
}

Obx(() => ResponseBuilder<List<String>>(
  data: controller.products,
  isLoading: controller.isLoading.value,
  error: controller.error.value,
  onRetry: controller.fetchProducts,
  dataBuilder: (context, data) => ListView(
    children: data.map((e) => ListTile(title: Text(e))).toList(),
  ),
));

🔹 BLoC / Cubit #

class ProductCubit extends Cubit<AsyncValue<List<String>>> {
  ProductCubit() : super(const AsyncValue.loading());

  Future<void> fetchProducts() async {
    try {
      emit(AsyncValue.loading());
      // API call
      emit(AsyncValue.data(["Apple", "Banana"]));
    } catch (e) {
      emit(AsyncValue.error(e, StackTrace.current));
    }
  }
}

BlocBuilder<ProductCubit, AsyncValue<List<String>>>(
  builder: (context, state) {
    return ResponseBuilder<List<String>>(
      isLoading: state.isLoading,
      error: state.hasError ? state.error.toString() : null,
      data: state.value,
      onRetry: () => context.read<ProductCubit>().fetchProducts(),
      dataBuilder: (context, data) => ListView(
        children: data.map((e) => ListTile(title: Text(e))).toList(),
      ),
    );
  },
);

🔹 Riverpod #

final productsProvider = StateNotifierProvider<ProductNotifier, AsyncValue<List<String>>>(
  (ref) => ProductNotifier(),
);

class ProductNotifier extends StateNotifier<AsyncValue<List<String>>> {
  ProductNotifier() : super(const AsyncValue.loading());

  Future<void> fetchProducts() async {
    try {
      // API call
      state = AsyncValue.data(["Apple", "Banana"]);
    } catch (e) {
      state = AsyncValue.error(e, StackTrace.current);
    }
  }
}

Consumer(builder: (context, ref, _) {
  final state = ref.watch(productsProvider);

  return ResponseBuilder<List<String>>(
    isLoading: state.isLoading,
    error: state.hasError ? state.error.toString() : null,
    data: state.value,
    onRetry: () => ref.read(productsProvider.notifier).fetchProducts(),
    dataBuilder: (context, data) => ListView(
      children: data.map((e) => ListTile(title: Text(e))).toList(),
    ),
  );
});

🔄 Pagination + Offline Example #

See the full Example App ⇗⇗ for infinite scroll and offline retry support:

ResponseBuilder<List<String>>(
  data: controller.products,
  isLoading: controller.isLoading.value,
  error: controller.error.value,
  isLoadingMore: controller.isLoadingMore.value,
  hasMore: controller.hasMore.value,
  paginationError: controller.paginationError.value,
  isOffline: controller.isOffline.value,
  onRetry: () => controller.fetchProducts(refresh: true),
  dataBuilder: (context, data) {
    return NotificationListener<ScrollNotification>(
      onNotification: (scroll) {
        if (scroll.metrics.pixels >= scroll.metrics.maxScrollExtent - 200) {
          controller.loadMore();
        }
        return false;
      },
      child: ListView.builder(
        itemCount: data.length,
        itemBuilder: (_, i) => ListTile(title: Text(data[i])),
      ),
    );
  },
);

🧩 Properties #

Property Type Description
data T? Data object (e.g., list, model, map)
isLoading bool Whether request is loading
errorMsg String? Error message
isLoadingMore bool True if fetching more items
hasMore bool Whether more items exist
paginationError String? Error during pagination
isOffline bool True if offline
onRetry void Function()? Retry callback
loadingWidgetBuilder WidgetBuilder? Custom loader
dataWidgetBuilder Widget Function(BuildContext, T)? Builder for data UI
errorWidgetBuilder Widget Function(BuildContext, String)? Custom error widget
emptyWidgetBuilder WidgetBuilder? Empty state widget
loadingMoreWidgetBuilder WidgetBuilder? Loader for pagination
paginationErrorWidgetBuilder Widget Function(BuildContext, String)? Pagination error UI
noMoreDataWidgetBuilder WidgetBuilder? "No more data" UI
offlineWidgetBuilder WidgetBuilder? Offline state UI

📂 Example Project #

Run the demo:

cd example
flutter run

📜 License #

MIT License. See LICENSE ⇗⇗.


❤️ Support #

If you like this package, ⭐ it on GitHub ⇗⇗


❤️ Contribution #

Contributions, bug reports, and feature requests are welcome!

6
likes
160
points
3
downloads

Publisher

unverified uploader

Weekly Downloads

A smart widget for handling API responses, offline states, and pagination in Flutter.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on smart_response_builder