Hello ,
I am new to the BloC model and am getting a problem. I looked at the example on the todos flutter and when I add a new "FicheMvt". The table with the BlocConsumer does not change. Maybe I forgot something. Can someone help me please.
Here is my code:
FicheScreen is responsible for loading the "Fiche" (fiche.dart)
class FicheScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final FicheRepository ficheRepository = FicheRepository();
return BlocProvider(
create: (context) {
return FicheBloc(
ficheRepository: ficheRepository,
)..add(FicheLoaded());
},
child: FicheBody(),
);
}
}
FicheBody is the content of "Fiche" (fiche_body.dart)
class FicheBody extends StatefulWidget {
@override
_FicheBodyState createState() => _FicheBodyState();
}
class _FicheBodyState extends State<FicheBody> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Liste des fiches"),
),
body: Center(
child: BlocConsumer<FicheBloc, FicheState>(
listener: (context, state) {
if (state is FicheLoadFailure) {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text('Une erreur est survenue'),
backgroundColor: Colors.red,
),
);
}
},
builder: (context, state) {
if (state is FicheLoadSuccess) {
return Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
verticalDirection: VerticalDirection.down,
children: <Widget>[
Expanded(
child: Container(
padding: EdgeInsets.all(5),
child: FicheDataTable(
listFiches: state.listFiches,
)),
),
],
);
}
if (state is FicheLoadInProgress) {
return LoadingIndicator();
}
return Center(child: Text('Aucun enregistrement'));
},
),
),
);
}
}
And the datatable of "Fiche" (fiche_datatable.dart)
class FicheDataTable extends StatelessWidget {
final List<FicheModel> listFiches;
FicheDataTable({Key key, @required this.listFiches})
: assert(listFiches != null),
super(key: key);
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: DataTable(
sortColumnIndex: 0,
showCheckboxColumn: false,
columns: [
DataColumn(
label: Text("Description"),
numeric: false,
tooltip: "Description"
)
],
rows: listFiches
.map(
(fiche) => DataRow(
onSelectChanged: (b) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EtatCollecteScreen(),
settings: RouteSettings(
arguments: fiche,
),
),
);
},
cells: [
DataCell(
Text(fiche.description)
),
]),
)
.toList(),
),
),
);
}
}
And when I select the row on the datatable. I access "EtatCollecteScreen" (etat_collecte.dart) which contains the 5 tabs as on the Flutter todos example
class EtatCollecteScreen extends StatelessWidget {
final FicheMvtDAO ficheMvtDAO = FicheMvtDAO();
final FicheMvtReferenceDAO ficheMvtReferenceDAO = FicheMvtReferenceDAO();
@override
Widget build(BuildContext context) {
final FicheModel fiche = ModalRoute.of(context).settings.arguments;
return MultiBlocProvider(
providers: [
BlocProvider<TabEtatCollecteBloc>(
create: (context) => TabEtatCollecteBloc(),
),
BlocProvider<FicheMvtBloc>(
create: (context) => FicheMvtBloc(
ficheMvtDAO: ficheMvtDAO,
)..add(FicheMvtRequested(idFiche: fiche.id)),
),
BlocProvider<FicheMvtReferenceBloc>(
create: (context) => FicheMvtReferenceBloc(
ficheMvtReferenceDAO: ficheMvtReferenceDAO,
)..add(FicheMvtReferenceRequested(idFiche: fiche.id)),
),
],
child: EtatCollecteContent(
ficheModel: fiche,
),
);
}
}
And when I click on the floating action button. It allows me to add a "FicheMvt" attached to "Fiche". (fiche_mvt_add.dart)
But when I add something, the datatable doesn't change. And I don't know where the problem is. I need to refresh.
class FicheMvtAddScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final FicheModel fiche = ModalRoute.of(context).settings.arguments;
final FicheMvtRepository ficheMvtRepository = FicheMvtRepository();
final FicheMvtDAO ficheMvtDAO = FicheMvtDAO();
return Scaffold(
key: _scaffoldKey,
appBar: new AppBar(
title: new Text("${fiche.id} - ${fiche.description}"),
),
backgroundColor: Colors.white,
body: MultiBlocProvider(
providers: [
BlocProvider<FicheMvtBloc>(
create: (context) => FicheMvtBloc(
ficheMvtDAO: ficheMvtDAO,
)..add(FicheMvtRequested(idFiche: fiche.id)),
),
BlocProvider<FicheMvtAddBloc>(
create: (context) => FicheMvtAddBloc(
ficheMvtRepository: ficheMvtRepository,
idFiche: fiche.id,
)..add(NewFicheMvtFormLoaded(idFiche: fiche.id)),
),
],
child: FicheMvtAddBody(
ficheModel: fiche,
),
),
);
}
}
In addition, the form "FicheMvtAddBody " is dynamic and depends on the "FicheMvtAddBloc" block to load dropdown form field.
And finally here is "FicheMvtScreen " (fiche_mvt.dart) which must rebuild its interface of the change (create new "FicheMvt")
class FicheMvtScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: BlocConsumer<FicheMvtBloc, FicheMvtState>(
listener: (context, state) {
if (state is FicheMvtRefreshSuccess) {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text('Mis 脿 jour r茅ussi'),
backgroundColor: Colors.green,
),
);
}
if (state is FicheMvtLoadFailure) {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text('Une erreur est survenue'),
backgroundColor: Colors.red,
),
);
}
},
builder: (context, state) {
if (state is FicheMvtLoadSuccess) {
final list = state.listFicheMvt;
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
verticalDirection: VerticalDirection.down,
children: <Widget>[
Expanded(
child: Container(
padding: EdgeInsets.all(5),
child: FicheMvtDataTable(
listFicheMvt: list,
)),
),
],
);
}
if (state is FicheMvtLoadInProgress) {
return CircularProgressIndicator();
}
return Center(child: Text('Aucun enregistrement'));
},
),
);
}
}
The files are available on this link
Thanks a lot for your help.
Hi @RLNat 馃憢
Thanks for opening an issue!
Have you taken a look at the FAQs? I'm guessing you're mutating state or yielding the same state.
Closing for now but if the documentation didn't help please provide the FicheBloc implementation. Thanks! 馃憤
Thank you very much for your answer. Yes, I have already followed what is mentioned in the FAQ.
Here is the code of 'FicheMvtBoc'
class FicheMvtBloc extends Bloc<FicheMvtEvent, FicheMvtState> {
final FicheMvtDAO ficheMvtDAO;
FicheMvtBloc({@required this.ficheMvtDAO})
: assert(ficheMvtDAO != null),
super(FicheMvtInitial());
@override
Stream<FicheMvtState> mapEventToState(FicheMvtEvent event) async* {
if (event is FicheMvtRequested) {
yield* _mapFicheMvtRequestedToState(event);
} else if (event is FicheMvtRefreshRequested) {
yield* _mapFicheMvtRefreshRequestedToState(event);
} else if (event is SendFicheMvtInServerRequested) {
yield* _mapSendFicheMvtInServerRequestedToState(event);
} else if (event is FicheMvtAdded) {
yield* _mapFicheMvtAddedToState(event);
}
}
Stream<FicheMvtState> _mapFicheMvtAddedToState(FicheMvtAdded event) async* {
if (state is FicheMvtLoadSuccess) {
num valueReelle;
final identifier = await TomDeviceInfo.tomDeviceInfo.deviceId;
if (event.indicateurModel.typeIndicateur != describeEnum(TypeIndicateur.Pourcentage) &&
event.indicateurModel.modeSaisie != describeEnum(ModeSaisiePourcentageIndicateur.SaisieNumerateurDenominateur)) {
if (event.indicateurModel.typeIndicateur == describeEnum(TypeIndicateur.Booleen) ||
event.indicateurModel.typeIndicateur == describeEnum(TypeIndicateur.Qualitatif)) {
valueReelle = num.parse(event.valeurReelleObj) ?? null;
} else {
valueReelle = event.valeurReelle ?? null;
}
}
FicheMvtModel ficheMvtModel = FicheMvtModel(
idFiche: event.idFiche,
codeIndicateur: event.indicateurModel.code,
codeSite: event.codeSite,
codeActi: event.codeActi ?? null,
codeGeo: event.codeGeo ?? null,
codeLogique: event.codeLogique ?? null,
codeVentilation: event.codeVentilation ?? null,
valeurPrevue: event.valeurPrevue ?? null,
valeurReelle: valueReelle ?? null,
valeurPrevueNumerateur: event.valeurPrevueNumerateur ?? null,
valeurPrevueDenominateur: event.valeurPrevueDenominateur ?? null,
valeurReelleNumerateur: event.valeurReelleNumerateur ?? null,
valeurReelleDenominateur: event.valeurReelleDenominateur ?? null,
mois: event.mois,
deviceId: identifier);
final List<FicheMvtModel> updatedFicheMvt = List.from((state as FicheMvtLoadSuccess).listFicheMvt)..add(ficheMvtModel);
yield FicheMvtLoadSuccess(updatedFicheMvt);
final FicheMvtRepository _ficheMvtRepository = FicheMvtRepository();
await _ficheMvtRepository.add(ficheMvtModel: ficheMvtModel);
}
}
And here the model "FicheMvt" extends Equatable
class FicheMvtModel extends Equatable {
@JsonKey(name: 'ID')
final int id;
@JsonKey(name: 'IDFICHE')
final int idFiche;
@JsonKey(name: 'CODEINDICATEUR')
final String codeIndicateur;
@JsonKey(name: 'CODESITE')
final String codeSite;
@JsonKey(name: 'CODEACTI')
final String codeActi;
@JsonKey(name: 'CODEGEO')
final String codeGeo;
@JsonKey(name: 'CODELOGIQUE')
final String codeLogique;
@JsonKey(name: 'CODEVENTILATION')
final String codeVentilation;
@JsonKey(name: 'VALEURPREVUE')
final num valeurPrevue;
@JsonKey(name: 'VALEURREELLE')
final num valeurReelle;
@JsonKey(name: 'VALEURPREVUENUMERATEUR')
final num valeurPrevueNumerateur;
@JsonKey(name: 'VALEURPREVUEDENOMINATEUR')
final num valeurPrevueDenominateur;
@JsonKey(name: 'VALEURREELLENUMERATEUR')
final num valeurReelleNumerateur;
@JsonKey(name: 'VALEURREELLEDENOMINATEUR')
final num valeurReelleDenominateur;
@JsonKey(name: 'MOIS')
final DateTime mois;
@JsonKey(name: 'COMMENTAIRE')
final String commentaire;
@JsonKey(name: 'IMPORTID')
final int importId;
@JsonKey(name: 'DEVICEID')
final String deviceId;
@JsonKey(name: 'ISALREADYCOMMITTED')
final int isAlreadyCommitted;
FicheMvtModel({
this.id,
this.idFiche,
this.codeIndicateur,
this.codeSite,
this.codeActi,
this.codeGeo,
this.codeLogique,
this.codeVentilation,
this.valeurPrevue,
this.valeurReelle,
this.valeurPrevueNumerateur,
this.valeurPrevueDenominateur,
this.valeurReelleNumerateur,
this.valeurReelleDenominateur,
this.mois,
this.commentaire,
this.importId,
this.deviceId,
this.isAlreadyCommitted,
});
factory FicheMvtModel.fromJson(Map<String, dynamic> json) => _$FicheMvtModelFromJson(json);
/// `toJson` is the convention for a class to declare support for serialization
/// to JSON. The implementation simply calls the private, generated
/// helper method `_$UserToJson`.
Map<String, dynamic> toJson() => _$FicheMvtModelToJson(this);
@override
List<Object> get props => [
id,
idFiche,
codeIndicateur,
codeSite,
codeActi,
codeGeo,
codeLogique,
codeVentilation,
valeurPrevue,
valeurReelle,
valeurPrevueNumerateur,
valeurPrevueDenominateur,
valeurReelleNumerateur,
valeurReelleDenominateur,
mois,
commentaire,
importId,
];
And here the state "FicheMvtEvent"
abstract class FicheMvtEvent extends Equatable {
const FicheMvtEvent();
@override
List<Object> get props => [];
}
class FicheMvtRequested extends FicheMvtEvent {
final int idFiche;
const FicheMvtRequested({@required this.idFiche});
@override
List<Object> get props => [idFiche];
}
class FicheMvtRefreshRequested extends FicheMvtEvent {
final int idFiche;
const FicheMvtRefreshRequested({@required this.idFiche});
@override
List<Object> get props => [idFiche];
}
class SendFicheMvtInServerRequested extends FicheMvtEvent {
final int idFiche;
const SendFicheMvtInServerRequested({@required this.idFiche});
@override
List<Object> get props => [idFiche];
}
class FicheMvtAdded extends FicheMvtEvent {
final int idFiche;
final IndicateurModel indicateurModel;
final String codeSite;
final String codeActi;
final String codeGeo;
final String codeLogique;
final String codeVentilation;
final num valeurPrevue;
final num valeurReelle;
final String valeurReelleObj;
final num valeurPrevueNumerateur;
final num valeurPrevueDenominateur;
final num valeurReelleNumerateur;
final num valeurReelleDenominateur;
final DateTime mois;
const FicheMvtAdded({
this.idFiche,
this.indicateurModel,
this.codeSite,
this.codeActi,
this.codeGeo,
this.codeLogique,
this.codeVentilation,
this.valeurPrevue,
this.valeurReelle,
this.valeurReelleObj,
this.valeurPrevueNumerateur,
this.valeurPrevueDenominateur,
this.valeurReelleNumerateur,
this.valeurReelleDenominateur,
this.mois,
});
@override
List<Object> get props => [
idFiche,
indicateurModel,
codeSite,
codeActi,
codeGeo,
codeLogique,
codeVentilation,
valeurPrevue,
valeurReelle,
valeurReelleObj,
valeurPrevueNumerateur,
valeurPrevueDenominateur,
valeurReelleNumerateur,
valeurReelleDenominateur,
mois,
];
}
And here is the state
abstract class FicheMvtState extends Equatable {
const FicheMvtState();
@override
List<Object> get props => [];
}
class FicheMvtInitial extends FicheMvtState {}
class FicheMvtLoadInProgress extends FicheMvtState {}
class FicheMvtLoadSuccess extends FicheMvtState {
final List<FicheMvtModel> listFicheMvt;
const FicheMvtLoadSuccess([this.listFicheMvt = const []]) : assert(listFicheMvt != null);
@override
List<Object> get props => [listFicheMvt];
}
class FicheMvtLoadFailure extends FicheMvtState {}
/*Refresh Fiche Mvt*/
class FicheMvtRefreshSuccess extends FicheMvtState {
final List<FicheMvtModel> listFicheMvt;
const FicheMvtRefreshSuccess({@required this.listFicheMvt}) : assert(listFicheMvt != null);
@override
List<Object> get props => [listFicheMvt];
}
/*Send Fiche Mvt*/
class FicheMvtSendInProgress extends FicheMvtState {}
class FicheMvtSendSuccess extends FicheMvtState {}
class FicheMvtSendFailure extends FicheMvtState {}
@RLNat I noticed that deviceId and isAlreadyCommitted are missing from the props of FicheMvtModel. Can you please try adding them and see if that resolves the issue?
@felangel Unfortunately that did not solve the problem. :(
Are you able to share a link to a sample app which illustrates the issue so I can debug it locally?
I have a question. There is no effect for another block which manages the dynamic form 'FicheMvtAddBloc' different from which manages the list of the Fiche 'FicheMvtBloc'
Yes this is the link
@RLNat can you also include a pubspec.yaml so that I can run it along with steps to reproduce the issue?
There is no effect for another block which manages the dynamic form 'FicheMvtAddBloc' different from which manages the list of the Fiche 'FicheMvtBloc'
I'm not sure what you mean
The state which manages the dynamic form is "FicheMvtAddState" different from the state of "FicheMvtState" which manages the create new one FicheMvt.
I just uploaded the Yaml pubspec file. :)
Hi @felangel !
I've found the problem. I exposed another FicheMvtBloc when I opened the form to create new FicheMvt to have access to this FicheMvtBloc to add an event (FicheMvtCreateRequested).
So, I just removed this FicheMvtBloc.
class FicheMvtAddScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final FicheModel fiche = ModalRoute.of(context).settings.arguments;
final FicheMvtRepository ficheMvtRepository = FicheMvtRepository();
final FicheMvtDAO ficheMvtDAO = FicheMvtDAO();
return Scaffold(
key: _scaffoldKey,
appBar: new AppBar(
title: new Text("${fiche.id} - ${fiche.description}"),
),
backgroundColor: Colors.white,
body: MultiBlocProvider(
providers: [
BlocProvider<FicheMvtBloc>(
create: (context) => FicheMvtBloc(
ficheMvtDAO: ficheMvtDAO,
)..add(FicheMvtRequested(idFiche: fiche.id)),
),
BlocProvider<FicheMvtAddBloc>(
create: (context) => FicheMvtAddBloc(
ficheMvtRepository: ficheMvtRepository,
idFiche: fiche.id,
)..add(NewFicheMvtFormLoaded(idFiche: fiche.id)),
),
],
child: FicheMvtAddBody(
ficheModel: fiche,
),
),
);
}
}
Sorry but i'm new to Flutter :)
Thanks for you help
Most helpful comment
Hi @felangel !
I've found the problem. I exposed another FicheMvtBloc when I opened the form to create new FicheMvt to have access to this FicheMvtBloc to add an event (FicheMvtCreateRequested).
So, I just removed this FicheMvtBloc.
Sorry but i'm new to Flutter :)
Thanks for you help