I use bloc but I want to remake this code into cubit because I don't have to use event, I can remove them. I tried to do this, but I got an error and the code did not work, but I don’t understand what I could be wrong about. I will be grateful if you help me correctly conveyed Tell me, how can I convert bloc into cubit correctly (I don’t have to use the event)?
bloc
class ConnectedBloc extends Bloc<ConnectedEvent, ConnectedState> {
StreamSubscription? subscription;
ConnectedBloc() : super(ConnectedInitial()) {
on<OnConnectedEvent>((event, emit) => emit(ConnectedSucess()));
on<OnNotConnectedEvent>((event, emit) => emit(ConnectedFailure()));
var resultCon = Connectivity().checkConnectivity();
if (resultCon == ConnectivityResult.wifi ||
resultCon == ConnectivityResult.mobile) {
add(OnConnectedEvent());
} else {
add(OnNotConnectedEvent());
}
Connectivity().onConnectivityChanged.listen((ConnectivityResult result) {
if (result == ConnectivityResult.wifi ||
result == ConnectivityResult.mobile) {
add(OnConnectedEvent());
} else {
add(OnNotConnectedEvent());
}
});
#override
Future<void> close() {
subscription?.cancel();
return super.close();
}
}
}
event
abstract class ConnectedEvent {}
class OnConnectedEvent extends ConnectedEvent {}
class OnNotConnectedEvent extends ConnectedEvent {}
state
abstract class ConnectedState {}
class ConnectedInitial extends ConnectedState {}
class ConnectedSucess extends ConnectedState {}
class ConnectedFailure extends ConnectedState {}
home
home: BlocConsumer<ConnectedBloc, ConnectedState>(
listener: ((context, state) {
if (state is ConnectedFailure) {
Remove your event classes and extend ConnectivityCubit class from Cubit instead of Bloc.
abstract class ConnectedState {}
class ConnectedInitial extends ConnectedState {}
class ConnectedSuccess extends ConnectedState {}
class ConnectedFailure extends ConnectedState {}
class ConnectivityCubit extends Cubit<ConnectedState> {
late final StreamSubscription connectivityStreamSubscription;
final Connectivity connectivity = Connectivity();
ConnectivityCubit() : super(ConnectedInitial()) {
connectivityStreamSubscription =
connectivity.onConnectivityChanged.listen((result) {
if (result == ConnectivityResult.wifi ||
result == ConnectivityResult.mobile) {
emit(ConnectedSuccess());
} else {
emit(ConnectedFailure());
}
});
}
#override
Future<void> close() {
connectivityStreamSubscription.cancel();
return super.close();
}
}
Related
I have an abstract class A:
abstract class A {
Future<String> firstMethod();
}
and I implemented this abstract class:
class Aimp implements A {
#override
Future<String> firstMethod() async {
return "test";
}
}
I have created another abstract class:
abstract class B extends A {
Future<String> secondMethod();
}
and I implemented this abstract class:
class Bweb extends B {
#override
Future<Object> secondMethod() async {
final t = //I want to call firstMethod()
if(t.isNotEmpty()) // do sth
}
}
In the implementation of secondMethod(), how can I call the implementation of firstMethod()?
I don't want to use mixin.
I try to use with instead:
abstract class A {
Future<String> firstMethod();
}
class Aimp implements A {
#override
Future<String> firstMethod() async {
return "test";
}
}
abstract class B with Aimp {
Future<String> secondMethod();
}
class Bweb extends B {
#override
Future<String> secondMethod() async {
final String t = await firstMethod(); //Your firstMethod function
if(t.isNotEmpty) {
return t;
}
return '';
}
}
Then you need an object of Aimp class item as a field of Bweb class. Or place A class as a field for the B one.
I have an abstract class written in Dart, that not only contains abstract method but also contains concrete methods like below:
abstract class Person {
void speak();
Foot getFoot();
void walk() {
getFoot().move();
}
}
class Foot {
void move() {
}
}
Test:
class MockPerson extends Person {
#override
Foot getFoot() {
return Foot();
}
#override
void speak() {
}
}
void main() {
late MockPerson sut;
setUp(() {
sut = MockPerson();
});
test('test walk', () {
/// ??
});
}
I tried to created mock of Person to be able to test against it. But I'm not sure how can I write unit test for walk() method?
For filter my list I use a state FilterState.
I have in this state my list filter but my widget for build list is not rebuild.
My print shows that the list is correct according to the state.
But GameList keeps its initial state which is not filtered : it's not rebuild.
Thanks,
my page for state :
BlocBuilder<GameBloc, GameState>(
builder: (context, state) {
if (state is LoadingState) {
return buildLoading();
} else if (state is FailState) {
return ErrorUI(message: state.message);
} else if (state is ListLoadedState) {
_list = state.list;
return GameList(list: state.list!);
} else if (state is FilterGamesState) {
print(state.list);
return GameList(list: state.list!);
}
return Container();
},
),
Bloc Page :
class GameBloc extends Bloc<GameEvent, GameState> {
GameBloc({required this.repository}) : super(LoadingState()) {
on<BackEvent>(_onBackEvent);
on<FetchGamesEvent>(_onFetchList);
on<FetchGameEvent>(_onFetchItem);
on<SavingEvent>(_onSavingEvent);
on<UpdateGameEvent>(_onUpdate);
on<CreateGameEvent>(_onCreate);
on<FilterGamesEvent>(_onFilter);
}
GameRepository repository;
final currentFilter = BehaviorSubject<Map<String, dynamic>>();
Future<void> _onFilter(
FilterGamesEvent event,
Emitter<GameState> emit,
) async {
try {
emit(LoadingState());
final list = event.list?.where((Game item) {
if (currentFilter.value.containsKey('player')) {
int players = currentFilter.value['player'].nb;
return players.isBetween(from: item.nopMin, to: item.nopMax);
}
return true;
}).where((Game item) {
if (currentFilter.value.containsKey('age')) {
return item.age!.isBetween(
from: currentFilter.value['age'].min,
to: currentFilter.value['age'].max);
}
return true;
}).where((Game item) {
if (currentFilter.value.containsKey('duration')) {
return compareToDuration(
item.durMin!,
item.durMax!,
currentFilter.value['duration'].min,
currentFilter.value['duration'].max);
}
return true;
}).where((Game item) {
if (currentFilter.value.containsKey('tags')) {
return item.tags!
.compareToList(currentFilter.value['tags'] as List<String>);
}
return true;
}).where((Game item) {
if (currentFilter.value.containsKey('collection')) {
return item.collection!
.compareToList(currentFilter.value['collection'] as List<String>);
}
return true;
}).toList();
emit(FilterGamesState(listGame: list!));
} catch (e) {
emit(const FailState(message: 'Failed to fetch all games data.'));
}
}
Event Page :
abstract class GameEvent extends Equatable {
final Game? item;
const GameEvent({this.item});
#override
List<Object> get props => [];
}
class InitialEvent extends GameEvent {
const InitialEvent({required Game item}) : super(item: item);
}
class BackEvent extends GameEvent {}
class SavingEvent extends GameEvent {}
class FetchGameEvent extends GameEvent {
const FetchGameEvent({required Game item}) : super(item: item);
}
class FetchGamesEvent extends GameEvent {}
class FilterGamesEvent extends GameEvent {
const FilterGamesEvent({required this.list});
final List<Game>? list;
}
State Page :
abstract class GameState extends Equatable {
final Game? item;
final List<Game>? list;
const GameState({this.item, this.list});
#override
List<Object> get props => [];
}
class GameInitial extends GameState {}
class FailState extends GameState {
const FailState({required this.message});
final String message;
}
class LoadingState extends GameState {}
class ListLoadedState extends GameState {
const ListLoadedState({required this.listGame}) : super(list: listGame);
final List<Game> listGame;
}
class ItemLoadedState extends GameState {
const ItemLoadedState({required this.game}) : super(item: game);
final Game game;
}
class FilterGamesState extends GameState {
const FilterGamesState({required this.listGame}) : super(list: listGame);
final List<Game> listGame;
}
I resolved this,
I send the key to GameList in FilterGameState.
else if (state is FilterGamesState) {
print(state.list);
return GameList(key: GlobalKey<GameFilterState>(), list: state.list!);
}
You are using Equatable but your props are empty. If you're using Equatable make sure to pass all properties to the props getter. (In both your state and event!)
Source: https://bloclibrary.dev/#/faqs?id=state-not-updating
The Flutter Todos Tutorial might also be helpful because it uses a filter too.
I am trying to find solution to manage async queries. For example, i have internet shop and i want to update all products and categories when city is changed. And i don't want to keep all async logic on ui. In order to achieve this result, i've created this bloc:
class AppEvent {
String message;
AppEvent({this.message = ''});
}
class EventsBlock extends Bloc<AppEvent, AppEvent> {
EventsBlock() : super(AppEvent());
#override
Stream<AppEvent> mapEventToState(AppEvent event) async* {
yield event;
}
}
final events = EventsBlock();
Then, i can use it like this:
class CityCubit() {
CityCubit() : super(CityState());
Future<void> changeCity() async {
await api.changeCity();
events.add(AppEvent(message: 'cityChanged'));
}
}
class CategoryCubit extends Cubit<CategoryState> {
CategoryCubit() : super(CategoryEmptyState()) {
events.stream.listen((e) {
if(e.message == 'cityChanged') {
fetchCategories();
}
});
};
Future<void> fetchCategories() async {
//fetch categories
}
}
class ProductCubit extends Cubit<ProductState> {
ProductCubit() : super(ProductEmptyState()) {
events.stream.listen((e) {
if(e.message == 'cityChanged') {
fetchProducts();
}
});
};
Future<void> fetchProducts() async {
//fetch products
}
}
It's something like eventBus pattern. But i am not sure that it's a correct way to use bloc. I've tried to use redux + redux saga, but it has a lot of boilerplate and i believe that flutter has better solution to manage things like that.
Your general idea is ok, but I can't see a real need for the EventsBloc class. In fact, it is kinda strange that you use the same class for the events and for the states of this bloc, and simply yield the event you receive. It's like EventsBloc could be a simple stream.
Here's a way to go, turning CityCubit into an actual bloc (and also with some error handling, which is something you can do gracefully with bloc):
abstract class CityState {}
class CityInitial extends CityState {}
class CityLoadingCities extends CityState {}
class CityCitiesLoaded extends CityState {}
class CityLoadError extends CityState {}
abstract class CityEvent {}
class CityLoadCities extends CityEvent {}
class CityBloc<CityEvent, CityState> {
CityBloc() : super(CityInitial());
#override
Stream<AppEvent> mapEventToState(CityEvent event) async* {
if(event is CityLoadCities) {
yield CityLoadingCities();
try {
await api.changeCity();
yield CityCitiesLoaded();
} catch (error) {
yield CityLoadError();
}
}
}
void changeCity() {
add(CityLoadCities());
}
}
Now you can do this inside any other bloc:
instanceOfCityBloc.listen((cityState) {
if(cityState is CityCitiesLoaded){
// do stuff
}
});
I ended up with this code:
class CityChangedEvent {
int cityId;
CityChangedEvent(this.cityId);
}
EventBus eventBus = EventBus();
mixin EventBusMixin {
StreamSubscription<T> listenEvent<T>(void Function(T) subscription) =>
eventBus.on<T>().listen(subscription);
void shareEvent<S>(S event) => eventBus.fire(event);
}
class CityCubit extends CityCubit<CityState> with EventBusMixin {
CityCubit() : super(CityInitialState());
Future<void> changeCity(cityId) async {
try {
emit(ChangeCityLoadingState());
final result = await api.changeCity(cityId);
if(result.success) {
emit(ChangeCitySuccessState());
shareEvent(CityChangedEvent(cityId));
}
} catch (_) {
emit(ChangeCityErrorState());
}
}
}
class CategoryCubit extends Cubit<CategoryState> with EventBusMixin {
CategoryCubit() : super(CategoryEmptyState()) {
listenEvent<CityChangedEvent>((e) {
fetchCategories(e.cityId);
);
}
Future<void> fetchCategories(cityId) async {
try {
emit(CategoryLoadingState());
final categoriesList = await fetchCategoriesApi();
emit(CategoryLoadedState(categories: categoriesList));
} catch (_) {
emit(CategoryErrorState());
}
}
}
Now, i can communicate between blocs without the need to instantiate or inject their instances. Thanks to this library https://pub.dev/packages/event_bus
I'm trying to figure out the BLoC library, but it gives me headaches.
I'm trying to fetch hotel names from an API. I have a model and a service responsible for contacting the API and fetching the data. However, I don't know how to connect it to the BLoC library.
Once my app starts, I want BLoC to fetch the data from the API and then show it in the app.
Here's my code:
hotel_model.dart
class Hotels {
final List<Hotel> hotels;
Hotels({this.hotels});
factory Hotels.fromJson(Map<String, dynamic> json) {
return Hotels(
hotels: List<Hotel>.from(
json['hotels'].map(
(x) => Hotel.fromJson(x),
),
),
);
}
}
class Hotel {
final String hotelName;
Hotel({this.hotelName});
factory Hotel.fromJson(Map<String, dynamic> json) {
return Hotel(
hotelName: json['name'],
);
}
}
hotel_service.dart
import 'package:http/http.dart' as http;
abstract class DownloadService {
Future<http.Response> fetchHotels();
}
class HotelService extends DownloadService {
#override
Future<http.Response> fetchHotels() {
final Uri uri = Uri.https('services.lastminute.com', 'mobile/stubs/hotels');
return http.get(uri);
}
}
And here's what I did wit the BLoC lib.
hotel_event.dart
part of 'hotel_bloc.dart';
#immutable
abstract class HotelEvent {}
class OnAppStartEvent extends HotelEvent {}
hotel_bloc.dart
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:hotels/models/hotel/hotel_model.dart';
import 'package:hotels/services/hotel/hotel_service.dart';
import 'package:meta/meta.dart';
part 'hotel_event.dart';
part 'hotel_state.dart';
class HotelBloc extends Bloc<HotelEvent, HotelState> {
HotelBloc() : super(HotelFinal());
final HotelService hotelService = HotelService();
#override
Stream<HotelState> mapEventToState(
HotelEvent event,
) async* {
if (event is FetchEvent) {
final response = hotelService.fetchHotels();
yield
}
}
}
hotel_state.dart
part of 'hotel_bloc.dart';
#immutable
abstract class HotelState {
HotelState();
}
class HotelFinal extends HotelState {
final Hotel hotel;
HotelFinal(this.hotel);
Hotel getHotel() {
return hotel;
}
}
First of all add await to this line in your bloc
final response = await hotelService.fetchHotels();
return List<Hotel> from your fetchHotels function
you must have stateful class for your screen and in the initState
you can create your bloc object and call .add method on it
in your build method wrap your widget with BlocBuilder and on builder callback check your bloc state, if the state is HotelFinal return your ui with list of hotels in your state object.
It'll be useful to add another state for your HotelState for when your bloc is fetching the data, and even for when there's an error. e.g;
part of 'hotel_bloc.dart';
#immutable
abstract class HotelState {
HotelState();
}
class HotelFinal extends HotelState {
final Hotel hotel;
HotelFinal(this.hotel);
Hotel getHotel() {
return hotel;
}
}
class HotelLoading extends HotelState {
HotelLoading();
}
class HotelError extends HotelState {
final String error;
HotelError(this.error);
}
You would want to change your mapEventToState to something like this:
#override
Stream<HotelState> mapEventToState(
HotelEvent event,
) async* {
if (event is FetchEvent) {
yield HotelLoading();
try {
final response = await hotelService.fetchHotels();
// It seems like your service doesn't return an hotel directly, so you'll have to deal with this as it is not part of the question.
final hotel = getYourHotelHereWithTheResponse;
yield HotelFinal(hotel);
} catch (e) {
yield HotelError('something went wrong getting the hotel info');
}
}
}
Lastly, add a widget to your widget tree that adds FetchEvent to your bloc and add a BlocBuilder to react to the change of states. Note that this is very flexible and can be done in many ways, but it is out of the scope of your very broad question, I'm just showing you how to use the library at a minimal:
class MyStatefulWidget extends StatefulWidget {
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
HotelBloc hotelBloc;
#override
void initState() {
hotelBloc = HotelBloc..add(FetchEvent());
super.initState();
}
#override
void dispose() {
hotelBloc.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
return BlocBuilder(builder: (context, state) {
if(state is HotelLoading) {
// return a widget to deal with loading
}
if(state is HotelFinal) {
// return a widget to deal with success
}
if(state is HotelError) {
// return a widget to deal with error
}
});
}
}