Hi there, I'm trying to implement Bloc in a large application and I'm having some issues right now, I have successfully added a Bloc using BlocProvider, BlocListener and BlocBuilder elements, so, the thing is that I see no state change after yielding on bloc event handling, apparently the only way I have found so far to trigger a UI Build is to use setState manually. I also notice that the listener inside my widget is not receiving any state change no matter if I yield the state from my bloc, I'm having issues with RegistrationBloc and LikesSelected state, I also have this widget as a child of a BlocProvider declaration.
The hierarchy of elements is
RegistrationView which has a Provider in top of Scaffold
Inside I have several BlocBuilders and BlocListeners which are working properly.
And as child of my main RegistrationView i have a function that returns Widgets based on a List of widgets
This is part of the bloc, i excluded not relevant functions and variables:
````
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:spmobile/src/models/Channel.dart';
import 'registration_state.dart';
import 'registration_event.dart';
import 'package:spmobile/src/models/Likes.dart';
import 'package:spmobile/src/app_services/registrationService.dart';
class RegistrationBloc extends Bloc
int _activeView = 0;
List
final _registrationService = RegistrationService();
List
void addLikes(Likes like){
_selectedLikes.add(like);
}
void removeLikes(Likes key) {
_selectedLikes.removeWhere((item) => item.key == key.key);
}
@override
RegistrationState get initialState => RegistrationFormDisplayed();
@override
Stream
if (event is SelectLikes) {
addLikes(event.like);
yield LikesSelected(_selectedLikes);
}
if (event is DeSelectLikes) {
removeLikes(event.like);
yield LikesSelected(_selectedLikes);
}
}
void next() {
_activeView++;
}
}
And my implementation is basically this:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:spmobile/src/bloc/registration/registration.dart';
import 'package:spmobile/src/models/Likes.dart';
class LikesView extends StatefulWidget {
@override
_LikesViewState createState() => _LikesViewState();
}
class _LikesViewState extends State
final List
'restaurants', 'travel', 'beauty', 'health', 'movies', 'music', 'culture', 'home', 'vehicles', 'events', 'pets', 'fitness'
];
final List
@override
Widget build(BuildContext context) {
RegistrationBloc _registrationBloc = BlocProvider.of<RegistrationBloc>(context);
Widget likesList() {
return BlocListener(
bloc: _registrationBloc,
listener: (context, state){
if (state is LikesSelected) {
print(state.likesList);
}
},
child: BlocBuilder(
bloc: _registrationBloc,
builder: (context, state) {
print("REBUILDE");
return ListView.separated(
itemCount: items.length,
itemBuilder: (context, int index) {
return SwitchListTile(
activeColor: Color(0xFFC62D29),
contentPadding: EdgeInsets.only(left:25.0, right: 20.0),
title: Text(items[index], style: TextStyle(fontWeight: FontWeight.bold),),
value: _registrationBloc.selectedLikes.indexWhere((item) => item.key == _keys[index]) > -1,
onChanged: (bool value) {
setState(() {
Likes like = Likes(items[index],_keys[index]);
if(_registrationBloc.selectedLikes.length == 0) {
_registrationBloc.add(SelectLikes(like));
} else {
if (_registrationBloc.selectedLikes.indexWhere((element) => element.key == like.key)>-1) {
_registrationBloc.add(DeSelectLikes(like));
} else {
_registrationBloc.add(SelectLikes(like));
}
}
});
});
},
separatorBuilder: (BuildContext context, int index) => const Divider());
}
),
);
}
return Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(top:10.0, bottom:10.0),
child: Text('Queremos saber tus gustos', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),),
),
Padding(
padding: const EdgeInsets.only(left: 30.0, right: 30.0),
child: Container(
height: 320,
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.grey,
offset: Offset(2,2)
),
],
color: Colors.white, borderRadius: BorderRadius.circular(10)),
child: likesList(),
),
),
],
);
}
@override
void dispose() {
super.dispose();
}
}
So that is the widget I'm connecting the bloc with, so, I'm pretty sure I have some errors going on like disposing and bad declarations but not sure if those are actually blocking my UI rebuild each time I send an event to the bloc.
If someone needs access to this, I can grant it with the email, is just that the application is private.
thanks in advance.
This is how I handle events:
import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:spmobile/src/models/Likes.dart';
abstract class RegistrationEvent extends Equatable {
const RegistrationEvent();
@override
List
class RegistrationFormEvent extends RegistrationEvent {}
class EmailChanged extends RegistrationEvent {
final String email;
const EmailChanged({@required this.email});
@override
List
class PhoneChanged extends RegistrationEvent {
final String phone;
const PhoneChanged({@required this.phone});
@override
List
class SmsSelected extends RegistrationEvent {
final bool sms;
SmsSelected(this.sms);
@override
List
class EmailSelected extends RegistrationEvent {
final bool emailOpt;
EmailSelected(this.emailOpt);
@override
List
class WhatsAppSelected extends RegistrationEvent {
final bool whatsapp;
WhatsAppSelected(this.whatsapp);
@override
List
class FormSubmitted extends RegistrationEvent {
final bool isFormValid;
FormSubmitted(this.isFormValid) : super();
}
class SelectLikes extends RegistrationEvent {
final Likes like;
SelectLikes(this.like);
}
class DeSelectLikes extends RegistrationEvent {
final Likes like;
DeSelectLikes(this.like);
}
class FormReset extends RegistrationEvent {}
and states:
import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';
import 'package:spmobile/src/models/Likes.dart';
class RegistrationState extends Equatable {
final String email;
final bool isEmailValid;
final String phone;
final bool isPhoneValid;
final bool formSubmittedSuccessfully;
bool get isFormValid {
if (isPhoneValid != null && isEmailValid != null) {
return isPhoneValid && isEmailValid;
} else {
return false;
}
}
RegistrationState({@required this.email, @required this.isEmailValid, @required this.phone,
@required this.isPhoneValid, @required this.formSubmittedSuccessfully});
@override
List
RegistrationState copyWith({ email, isEmailValid, phone, isPhoneValid, formSubmittedSuccessfully}) {
return RegistrationState(
email: email ?? this.email,
isEmailValid: isEmailValid ?? this.isEmailValid,
phone: phone ?? this.phone,
isPhoneValid: isPhoneValid ?? this.isPhoneValid,
formSubmittedSuccessfully: formSubmittedSuccessfully ?? this.formSubmittedSuccessfully,
);
}
}
class RegistrationFormDisplayed extends RegistrationState {}
class EmailChannelSelected extends RegistrationState {
final bool emailChannel;
EmailChannelSelected(this.emailChannel);
}
class SmsChannelSelected extends RegistrationState {
final bool sms;
SmsChannelSelected(this.sms);
}
class WhatsAppChannelSelected extends RegistrationState {
final bool whatsapp;
WhatsAppChannelSelected(this.whatsapp);
}
class RegistrationFormValid extends RegistrationState {
final bool valid;
RegistrationFormValid(this.valid);
}
class SendingRegistrationForm extends RegistrationState {
SendingRegistrationForm();
}
class RegistrationCompleted extends RegistrationState {
final String record;
RegistrationCompleted({
String phone,
String email,
bool isEmailValid,
bool isPhoneValid,
bool formSubmittedSuccessfully,
@required this.record
}) :
super(
phone: phone,
email : email,
formSubmittedSuccessfully: formSubmittedSuccessfully,
isEmailValid: isEmailValid,
isPhoneValid: isPhoneValid
);
}
class RegistrationIncomplete extends RegistrationState {
final String error;
RegistrationIncomplete(this.error);
@override
List
@override
String toString() => 'Registration Failure { error: $error }';
}
class LikesSelected extends RegistrationState {
final List
LikesSelected(this.likesList);
}
class RegistrationFormInvalid extends RegistrationState {}
````
Hi @hackerunet , it would be easier for you to get help if you could share a repo. Thanks 馃憤
I can share it. => git clone https://[email protected]/superpesos/superpesos-mobile.git
I've taken a quick look at your code and you seem to have a lot of state inside the bloc. You should move all those fields in the state class/es; you should also be able to rebuild your UI using BlocBuilders only without needing setState.
If you're still having issues after that, feel free to comment 馃憤
Thanks @RollyPeres for taking the time to answer!
@hackerunet closing for now but feel free to comment with additional questions/issues are applying the suggested changes and I'm happy to continue the conversation 馃憤