Tiendita
A Flutter package for interacting with the Fake Store API. Provides a clean, type-safe interface for fetching products and managing shopping carts with built-in caching and comprehensive error handling.
Features
- Products API: Fetch all products, single product, categories, and filter by category
- Cart API: Full CRUD operations for shopping carts
- Type-safe error handling: Uses sealed
Result<T>type with pattern matching - Built-in caching: In-memory caching with manual refresh support
- Custom exceptions: Rich exception hierarchy for different error scenarios
- Testable architecture: Dependency injection throughout for easy mocking
Getting Started
Prerequisites
- Flutter SDK >= 3.18.0
- Dart SDK >= 3.11.0
Installation
Add tiendita to your pubspec.yaml:
dependencies:
tiendita:
path: ../tiendita # For local development
# Or from pub.dev when published:
# tiendita: ^0.0.1
Then run:
flutter pub get
Usage
Import the package
import 'package:tiendita/tiendita.dart';
Fetching Products
// Create a repository instance
final productRepository = ProductRepository();
// Fetch all products
final result = await productRepository.getProducts();
switch (result) {
case Ok(:final value):
print('Got ${value.length} products');
for (final product in value) {
print('${product.title}: \$${product.price}');
}
case Error(:final error):
print('Error: $error');
}
Fetching a Single Product
final result = await productRepository.getProduct(1);
switch (result) {
case Ok(:final value):
print('Product: ${value.title}');
print('Price: \$${value.price}');
print('Rating: ${value.rating.rate}/5 (${value.rating.count} reviews)');
case Error(:final error):
print('Error: $error');
}
Working with Categories
// Get all categories
final categoriesResult = await productRepository.getCategories();
switch (categoriesResult) {
case Ok(:final value):
print('Categories: ${value.join(", ")}');
case Error(:final error):
print('Error: $error');
}
// Filter products by category
final electronicsResult = await productRepository.getProductsByCategory('electronics');
Managing Carts
final cartRepository = CartRepository();
// Get a user's carts
final cartsResult = await cartRepository.getUserCarts(1);
// Add a new cart
final newCartResult = await cartRepository.addCart(
userId: 1,
products: [
CartProduct(productId: 1, quantity: 2),
CartProduct(productId: 5, quantity: 1),
],
);
// Update a cart
final updateResult = await cartRepository.updateCart(
cartId: 1,
userId: 1,
products: [
CartProduct(productId: 1, quantity: 3),
],
);
// Delete a cart
final deleteResult = await cartRepository.deleteCart(1);
Using Pagination and Sorting
// Get first 5 products, sorted descending
final result = await productRepository.getProducts(
limit: 5,
sort: 'desc',
);
// Get carts within a date range
final cartsResult = await cartRepository.getCarts(
startDate: DateTime(2024, 1, 1),
endDate: DateTime(2024, 12, 31),
limit: 10,
);
Cache Management
// Force refresh to bypass cache
final freshResult = await productRepository.getProducts(forceRefresh: true);
// Clear all cached data
productRepository.clearCache();
cartRepository.clearCache();
Error Handling
The package uses a sealed Result<T> type for type-safe error handling:
final result = await productRepository.getProduct(999);
switch (result) {
case Ok(:final value):
// Handle success
displayProduct(value);
case Error(:final error):
// Handle specific error types
if (error is NotFoundException) {
print('Product not found');
} else if (error is NetworkException) {
print('Network error: ${error.message}');
} else if (error is ServerException) {
print('Server error: ${error.message}');
} else {
print('Unknown error: $error');
}
}
Available Exception Types
| Exception | Description |
|---|---|
TienditaException |
Base exception class |
NetworkException |
Connection timeouts, no internet |
ServerException |
HTTP error responses (5xx) |
ParseException |
JSON parsing failures |
NotFoundException |
Resource not found (404) |
AuthException |
Authentication failures (401) |
API Reference
ProductRepository
| Method | Description | Returns |
|---|---|---|
getProducts({limit, sort, forceRefresh}) |
Get all products | Result<List<Product>> |
getProduct(id, {forceRefresh}) |
Get single product | Result<Product> |
getCategories({forceRefresh}) |
Get all categories | Result<List<String>> |
getProductsByCategory(category) |
Get products by category | Result<List<Product>> |
clearCache() |
Clear cached data | void |
CartRepository
| Method | Description | Returns |
|---|---|---|
getCarts({limit, sort, startDate, endDate}) |
Get all carts | Result<List<Cart>> |
getCart(id, {forceRefresh}) |
Get single cart | Result<Cart> |
getUserCarts(userId, {forceRefresh}) |
Get user's carts | Result<List<Cart>> |
addCart({userId, products}) |
Create new cart | Result<Cart> |
updateCart({cartId, userId, products}) |
Update cart | Result<Cart> |
deleteCart(cartId) |
Delete cart | Result<void> |
clearCache() |
Clear cached data | void |
Models
Product
class Product {
final int id;
final String title;
final double price;
final String description;
final String category;
final String image;
final Rating rating;
}
class Rating {
final double rate;
final int count;
}
Cart
class Cart {
final int id;
final int userId;
final DateTime date;
final List<CartProduct> products;
}
class CartProduct {
final int productId;
final int quantity;
}
Running the Example App
The package includes a complete example app demonstrating all features:
cd example
flutter pub get
flutter run
The example app showcases:
- Product listing with grid view
- Category filtering with chips
- Pull-to-refresh functionality
- Product detail view with full information
- MVVM architecture with
ChangeNotifier - Error handling with retry functionality
Example App Screenshots
The example app displays:
- Product List: Grid of products with images, titles, prices, and ratings
- Category Filter: Horizontal scrollable chips to filter by category
- Product Detail: Full product information with image, description, and add-to-cart button
Running Tests
# Run all tests
flutter test
# Run tests with coverage
flutter test --coverage
Architecture
lib/
├── tiendita.dart # Public API exports
└── src/
├── core/
│ ├── result.dart # Result<T> sealed class
│ └── exceptions.dart # Custom exception hierarchy
└── data/
├── models/
│ ├── product.dart # Product & Rating models
│ └── cart.dart # Cart & CartProduct models
├── services/
│ └── fake_store_api_service.dart # HTTP client
└── repositories/
├── product_repository.dart # Product data layer
└── cart_repository.dart # Cart data layer
Dependencies
- dio ^5.4.0 - HTTP client
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Libraries
- tiendita
- A Flutter package for interacting with the Fake Store API.