I'm developing a web application using flutter_bloc
library for state management. In my app I have to keep track of the state of a form (AreaForm
widget). In this form I have to manage a list of objects: I must be able to add or remove an object to the list.
This is the code for the AreaFormState:
part of 'area_form_bloc.dart';
class AreaFormState extends Equatable {
final Provincia provincia;
final Comune comune;
final String denominazione;
final List<TipoUdo> listaTipoUdo;
final List<Specialita> listaSpecialita;
final bool isDisciplineChecked;
final bool isBrancheChecked;
final String indirizzo;
const AreaFormState({
@required this.provincia,
@required this.comune,
@required this.denominazione,
@required this.listaTipoUdo,
@required this.listaSpecialita,
@required this.isDisciplineChecked,
@required this.isBrancheChecked,
@required this.indirizzo,
});
factory AreaFormState.empty() {
return AreaFormState(
provincia: null,
comune: null,
denominazione: null,
listaTipoUdo: <TipoUdo>[],
listaSpecialita: <Specialita>[],
isDisciplineChecked: false,
isBrancheChecked: false,
indirizzo: null,
);
}
AreaFormState update({
Provincia provincia,
Comune comune,
String denominazione,
List<TipoUdo> listaTipoUdo,
List<Specialita> listaSpecialita,
bool isDisciplineChecked,
bool isBrancheChecked,
String indirizzo,
}) {
return copyWith(
provincia: provincia,
comune: comune,
denominazione: denominazione,
listaTipoUdo: listaTipoUdo,
listaSpecialita: listaSpecialita,
isDisciplineChecked: isDisciplineChecked,
isBrancheChecked: isBrancheChecked,
indirizzo: indirizzo,
);
}
AreaFormState copyWith({
Provincia provincia,
Comune comune,
String denominazione,
List<TipoUdo> listaTipoUdo,
List<Specialita> listaSpecialita,
bool isDisciplineChecked,
bool isBrancheChecked,
String indirizzo,
}) {
return AreaFormState(
provincia: provincia ?? this.provincia,
comune: comune ?? this.comune,
denominazione: denominazione ?? this.denominazione,
listaTipoUdo: listaTipoUdo ?? this.listaTipoUdo,
listaSpecialita: listaSpecialita ?? this.listaSpecialita,
isDisciplineChecked: isDisciplineChecked ?? this.isDisciplineChecked,
isBrancheChecked: isBrancheChecked ?? this.isBrancheChecked,
indirizzo: indirizzo ?? this.indirizzo,
);
}
@override
List<Object> get props => [
provincia,
comune,
denominazione,
listaTipoUdo,
listaSpecialita,
isDisciplineChecked,
isBrancheChecked,
indirizzo,
];
@override
String toString() {
return '''
AreaFormState {
provincia: $provincia,
comune: $comune,
denominazione: $denominazione,
listaTipoUdo: $listaTipoUdo,
listaSpecialita: $listaSpecialita,
isDisciplineChecked: $isDisciplineChecked,
isBrancheChecked: $isBrancheChecked,
indirizzo: $indirizzo,
}''';
}
}
This is the code for the AreaFormEvent (I have reported only the two events of interest):
part of 'area_form_bloc.dart';
abstract class AreaFormEvent extends Equatable {
const AreaFormEvent();
@override
List<Object> get props => [];
}
...
class SpecialitaAdded extends AreaFormEvent {
final Specialita specialita;
const SpecialitaAdded({@required this.specialita});
@override
List<Object> get props => [specialita];
@override
String toString() => 'SpecialitaAdded { specialita: $specialita }';
}
class SpecialitaRemoved extends AreaFormEvent {
final Specialita specialita;
const SpecialitaRemoved({@required this.specialita});
@override
List<Object> get props => [specialita];
@override
String toString() => 'SpecialitaRemoved { specialita: $specialita }';
}
...
And finally this is the code for AreaFormBloc:
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
import '../../../data/comune.dart';
import '../../../data/provincia.dart';
import '../../../data/specialita.dart';
import '../../../data/tipo_udo.dart';
import '../../repositories/area/area_repository.dart';
part 'area_form_event.dart';
part 'area_form_state.dart';
class AreaFormBloc extends Bloc<AreaFormEvent, AreaFormState> {
final AreaRepository areaRepository;
AreaFormBloc({@required this.areaRepository})
: assert(areaRepository != null);
@override
AreaFormState get initialState => AreaFormState.empty();
@override
Stream<AreaFormState> mapEventToState(
AreaFormEvent event,
) async* {
if (event is ProvinciaSelected) {
yield* _mapProvinciaSelectedToState(event.provincia);
} else if (event is ComuneSelected) {
yield* _mapComuneSelectedToState(event.comune);
} else if (event is DenominazioneChanged) {
yield* _mapDenominazioneChangedToState(event.denominazione);
} else if (event is TipoUdoAdded) {
yield* _mapTipoUdoAddedToState(event.tipoUdo);
} else if (event is TipoUdoRemoved) {
yield* _mapTipoUdoRemovedToState(event.tipoUdo);
} else if (event is SpecialitaAdded) {
yield* _mapSpecialitaAddedToState(event.specialita);
} else if (event is SpecialitaRemoved) {
yield* _mapSpecialitaRemovedToState(event.specialita);
} else if (event is DisciplineChanged) {
yield* _mapDisciplineChangedToState();
} else if (event is BrancheChanged) {
yield* _mapBrancheChangedToState();
} else if (event is IndirizzoChanged) {
yield* _mapIndirizzoChangedToState(event.indirizzo);
} else if (event is NearestUdoIconPressed) {
yield* _mapNearestUdoIconPressedToState();
} else if (event is PulisciPressed) {
yield* _mapPulisciPressedToState();
} else if (event is CercaPressed) {
yield* _mapCercaPressedToState();
}
}
...
Stream<AreaFormState> _mapSpecialitaAddedToState(
Specialita specialita,
) async* {
yield state.update(listaSpecialita: state.listaSpecialita..add(specialita));
}
Stream<AreaFormState> _mapSpecialitaRemovedToState(
Specialita specialita,
) async* {
yield state.update(
listaSpecialita: state.listaSpecialita..remove(specialita));
}
...
}
In this way, when the event SpecialitaAdded
is added to the AreaFormBloc
, the bloc should produce a Transition to a new AreaFormState
which should have a listaSpecialita
equal to the one before with the addition of the new Specialita
object just added.
Unfortunately no transition at all is triggered! But the really strange thing is that if I add the element like this instead:
Stream<AreaFormState> _mapSpecialitaAddedToState(
Specialita specialita,
) async* {
yield state.update(listaSpecialita: state.listaSpecialita + [specialita]);
}
then the transition is triggered.
Unfortunately I can't keep this solution because I don't know how to manage the removal of an element.
I think the problem is that I use the Equatable
package for equality comparison but I really don't understand where I'm wrong.