Provider vs BLoC vs GetX
Features | Provider | BLoC | GetX |
Type of solution | Dependency Injection | Reactive Programming | Dependency Injection |
Ease of use | Easy | Moderate | Easy |
Boilerplate code | Low | High | Low |
Flutter version | Supports all versions of Flutter | Supports all versions of Flutter | Supports all versions of Flutter |
Integration | Integrates with other packages and libraries easily | Requires specific packages and libraries | Integrates with other packages and libraries easily |
Performance | Fast | Fast, but can suffer from large bloc trees | Fast |
State management | Simple state management | Complex state management with many levels of depth | Simple state management |
Reusability | Reusable across different widgets | Reusable across different widgets | Reusable across different widgets |
Scalability | Good | Good | Good |
Code Size | Small | Large | Small |
Dependency Injection | No | No | Yes |
Reactive programming | No | Yes | Yes |
Testing | Easy to test with Flutter’s widget tests | Easy to test with unit tests and widget tests | Easy to test with Flutter’s widget tests |
Flutter Tutorial:
Flutter Widgets:
Flutter Advance
Flutter REST API
Advanced Concepts
Wrap vs Builder vs OverBarFlow
Circular progress conatin Icon
Flutter State management Comparison
Flutter Database
Flutter Token Expired Handling
Flutter Provider
Flutter GetX
Flutter with Native
Flutter Tips:
Interview Questions
Flutter 100 Interview Questions
Provider
Provider is a simple and lightweight solution for state management in Flutter that uses the concept of dependency injection.
Here’s an example of how to use Provider to manage the state of a counter app:
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // like setState
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Counter App')),
body: Center(
child: Consumer<Counter>(
builder: (context, counter, child) => Text(
'Count: ${counter.count}',
style: TextStyle(fontSize: 24.0),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => Provider.of<Counter>(context, listen: false).increment(),
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
In this example, we define a Counter class that extends ChangeNotifier and contains the state of the app, which is the count. We use notifyListeners() to notify the listeners whenever the state changes. In the MyHomePage widget, we use the Consumer widget to listen for changes in the state and update the UI. We use Provider.of to access the Counter instance and increment the count.
BLoC
BLoC stands for Business Logic Component and is a more complex solution for state management in Flutter that uses reactive programming.
Here’s an example of how to use BLoC to manage the state of a weather app:
class WeatherBloc {
final _weatherController = StreamController<WeatherState>();
Stream<WeatherState> get weatherStream => _weatherController.stream;
void fetchWeather(String city) async {
_weatherController.add(WeatherLoading());
try {
final weather = await fetchWeatherFromApi(city);
_weatherController.add(WeatherLoaded(weather));
} catch (error) {
_weatherController.add(WeatherError(error));
}
}
void dispose() {
_weatherController.close();
}
}
abstract class WeatherState {}
class WeatherLoading extends WeatherState {}
class WeatherLoaded extends WeatherState {
final Weather weather;
WeatherLoaded(this.weather);
}
class WeatherError extends WeatherState {
final String error;
WeatherError(this.error);
}
class Weather {
final String city;
final double temperature;
Weather({required this.city, required this.temperature});
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _weatherBloc = WeatherBloc();
@override
void dispose() {
_weatherBloc.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Weather App')),
body: StreamBuilder<WeatherState>(
stream: _weatherBloc.weatherStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
final weatherState = snapshot.data;
if (weatherState is WeatherLoading) {
return Center(child: CircularProgressIndicator());
} else if (weatherState is WeatherLoaded) {
final weather = weatherState.weather;
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
weather.city,
style: TextStyle(fontSize: 24.0),
),
SizedBox(height: 16.0),
Text(
'${weather.temperature.toStringAsFixed(1)}°C',
style: TextStyle(fontSize: 48.0),
),
],
),
);
} else if (weatherState is WeatherError) {
return Center(child: Text(weatherState.error));
}
}
return Container();
},
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
final city = await showDialog<String>(
context: context,
builder: (context) => SimpleDialog(
title: Text('Enter a city name'),
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: TextField(
autofocus: true,
decoration: InputDecoration(hintText: 'City name'),
onSubmitted: (value) => Navigator.pop(context, value),
),
),
],
),
);
if (city != null && city.isNotEmpty) {
_weatherBloc.fetchWeather(city);
}
},
tooltip: 'Fetch weather',
child: Icon(Icons.refresh),
),
);
}
}
In this example, we define a WeatherBloc class that manages the state of the weather app. We use a StreamController to emit different states of the app, such as WeatherLoading, WeatherLoaded, and WeatherError. We also define a Weather class that represents the weather data.
In the MyHomePage widget, we use a StreamBuilder to listen for changes in the state and update the UI accordingly. We also use a FloatingActionButton to fetch the weather data by calling the fetchWeather method on the WeatherBloc instance.
GetX
GetX is a simple and powerful solution for state management in Flutter that uses reactive programming and dependency injection.
Here’s an example of how to use GetX to manage the state of a cart app:
class Product {
final int id;
final String name;
final double price;
Product({required this.id, required this.name, required this.price});
}
class CartController extends GetxController {
final _cartItems = <Product>[].obs;
List<Product> get cartItems => _cartItems.toList();
void addToCart(Product product) {
_cartItems.add(product);
}
double get total => _cartItems.fold(0.0, (sum, product) => sum + product.price);
void clear() {
_cartItems.clear();
}
}
class MyHomePage extends StatelessWidget {
final _cartController = Get.put(CartController());// read more
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Cart App')),
body: Obx(() => ListView.builder(
itemCount: _cartController.cartItems.length,
itemBuilder: (context, index) {
final product = _cartController.cartItems[index];
return ListTile(
title: Text(product.name),
subtitle: Text(product.price.toString()),
);
},
)),
floatingActionButton: FloatingActionButton(
onPressed: () {
_cartController.addToCart(Product(id: 1, name: 'Product 1', price: 9.99));
},
tooltip: 'Add to cart',
child: Icon(Icons.add_shopping_cart),
),
bottomNavigationBar: Container(
height: 56.0,
color: Colors.grey[200],
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('Total: ${_cartController.total.toStringAsFixed(2)}'),
TextButton(
onPressed
() => _cartController.clear(),
child: Text('Clear'),
),
],
),
),
);
}
}
In this example, we define a Product class that represents a product with an ID, name, and price. We also define a CartController class that manages the state of the cart app. We use the obs property to make the _cartItems list observable and use the Get.put method to create a global instance of the CartController.
In the MyHomePage widget, we use the Obx widget to listen for changes in the state of the cart and update the UI accordingly. We also use a FloatingActionButton to add a product to the cart by calling the addToCart method on the CartController instance, and use a bottomNavigationBar to display the total price of the cart and a button to clear the cart by calling the clear method on the CartController instance.
Note:- For beginners Provider is best option as compare to BLoC and Getx
In conclusion, Provider, BLoC, and GetX are three popular state management solutions for Flutter. Provider is a simple and flexible solution that uses the InheritedWidget to manage the state of an app. BLoC is a more complex but powerful solution that uses reactive programming and streams to manage the state of an app. GetX is a simple and powerful solution that uses reactive programming and dependency injection to manage the state of an app. The choice of which solution to use depends on the complexity of the app and the preferences of the developer.
Other Articles:-