使用flutter_bloc和Equatable

时间:2020-07-01 12:23:30

标签: flutter dart flutter-web bloc

我正在使用flutter_bloc库开发用于状态管理的Web应用程序。在我的应用程序中,我必须跟踪表单(AreaForm小部件)的状态。在这种形式下,我必须管理对象列表:我必须能够在列表中添加或删除对象。

这是 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,
    }''';
  }
}

这是 AreaFormEvent 的代码(我只报告了两个感兴趣的事件):

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 }';
}

...

最后这是 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));
  }

  ...

}

通过这种方式,当将事件SpecialitaAdded添加到AreaFormBloc时,该集团应产生到新AreaFormState的过渡,该listaSpecialita应该等于之前添加了新的Specialita对象。

不幸的是,根本没有触发转换!但是真正奇怪的是,如果我改为添加这样的元素:

  Stream<AreaFormState> _mapSpecialitaAddedToState(
    Specialita specialita,
  ) async* {
    yield state.update(listaSpecialita: state.listaSpecialita + [specialita]);
  }

然后触发转换。

不幸的是,我无法保留此解决方案,因为我不知道如何管理元素的移除。

我认为问题在于我使用Equatable软件包进行相等性比较,但是我真的不明白我哪里错了。

2 个答案:

答案 0 :(得分:0)

使用Equatable,您必须具有不可变的字段(您的列表是可修改的)。

类似的事情我想:

final someList = <int>[1, 2, 3]; // this is done when creating state
final anotherList = someList; // this is done with update, you just pass the reference
someList.add(4);
print(anotherList == someList); // true

所以等价,它检查通过的props是否相等(在这种情况下相等)。

解决方案:

Stream<AreaFormState> _mapSpecialitaAddedToState(
  Specialita specialita,
) async* {
  // creating a new copy of the list
  yield state.update(
    listaSpecialita: List<Specialita>.of(
      state.listaSpecialita..add(specialita),
    ),
  );
}

这为列表提供了具有相同值的新引用。 这将使内部==检查为false。

除非它们不相等,否则Bloc不会发出新状态。

建议您改用freezed软件包,这样可以节省您 copyWith的麻烦。

答案 1 :(得分:0)

我使用Dart的 spread运算符解决了该问题:

let tags = ["12345"]