Flutter Bloc Equatable state with one mutual property across all states classes - flutter

I am trying to figure out the best way to define my Search Bloc's State to preserve text property (search key) across all state classes.
Currently, it looks like this:
import 'package:equatable/equatable.dart';
import 'package:project/models/searchResults.dart';
class SearchState extends Equatable {
SearchState([List props = const []]) : super(props);
}
class SearchStateEmpty extends SearchState {
final String text;
SearchStateEmpty({this.text});
#override
String toString() => 'SearchStateEmpty';
}
class SearchStateLoading extends SearchState {
final String text;
SearchStateLoading({this.text});
#override
String toString() => 'SearchStateLoading';
}
class SearchStateSuccess extends SearchState {
final String text;
final List<RestaurantSearchItem> items;
SearchStateSuccess({this.text, this.items}) : super([text, items]);
#override
String toString() => 'SearchStateSuccess { items: ${items.length} }';
}
class SearchStateError extends SearchState {
final String text;
final String error;
SearchStateError({this.text, this.error}) : super([text, error]);
#override
String toString() => 'SearchStateError';
}
Is there a better way of using text property than defining it throughout all state classes?
This would not be as bad as it is now if I wouldn't have to use currentState property every time an event does not have it. For example:
SearchStateEmpty(text: currentState.text);
...
SearchStateLoading(text: event.text);
...
SearchStateSuccess(text: currentState.text, items: results.items);
I was looking for examples in Flutter docs but all I was able to find out was that I should either use different blocs for it or ditch equatable (which I want to keep since it's pretty nice to have).
Any suggestions with examples would be highly appreciated. Thanks.

The way I was doing it was sort of antipattern. With some help, I was able to find a more clean way to use the current state in my state classes.
Code now looks like this:
class SearchState extends Equatable {
final String text;
SearchState(this.text,
[List<RestaurantSearchItem> items = const [], String error = ''])
: super([text, items, error]);
}
class SearchStateEmpty extends SearchState {
SearchStateEmpty({String text})
: super(text); // Here I want to use text from SearchState
SearchStateEmpty.fromState(SearchState state) : super(state.text);
#override
String toString() => 'SearchStateEmpty';
}
class SearchStateLoading extends SearchState {
final String text;
SearchStateLoading({this.text})
: super(text); // Here I want to set text that comes from event
#override
String toString() => 'SearchStateLoading';
}
class SearchStateError extends SearchState {
final String error;
SearchStateError({this.error, String text}) : super(text, [], error);
// Text comes from SearchState, error comes from event
SearchStateError.fromState(SearchState state, {this.error})
: super(state.text, [], error);
#override
String toString() => 'SearchStateError';
}
class SearchStateSuccess extends SearchState {
final List<RestaurantSearchItem> items;
SearchStateSuccess({this.items, String text}) : super(text, items, null);
SearchStateSuccess.fromState(SearchState state, {this.items})
: super(state.text, items, null);
#override
String toString() => 'SearchStateSuccess { items: ${items.length} }';
}

Related

Accessing state in widget and making class immutable

I need to expose a couple of functions of a Stateful Widget. Since these functions depend on the state of the widget, I created a variable to store the state.
However, I am getting a compile time warning:
This class (or a class that this class inherits from) is marked as '#immutable', but one or more of its instance fields aren't final.
My Code:
class ItemWidget extends StatefulWidget {
final Record record;
final Function additem;
final Function removeItem;
var state;
ItemWidget(this.record, this.additem, this.removeItem);
#override
_ItemWidgetState createState() {
return this.state = new _ItemWidgetState();
}
// These are public functions which I need to expose.
bool isValid() => state.validate();
void validate() => state.validate();
}
Is there a better way /correct way of achieving this?
Thanks.
You should write the function on state, and access it via GlobalKey.
import 'package:flutter/material.dart';
class ItemWidget extends StatefulWidget {
final Record record;
final Function additem;
final Function removeItem;
const ItemWidget(
Key? key,
this.record,
this.additem,
this.removeItem,
) : super(key: key);
#override
ItemWidgetState createState() => ItemWidgetState();
}
class ItemWidgetState extends State<ItemWidget> {
bool isValid() {
return true;
}
void validate() {}
#override
Widget build(BuildContext context) {
// TODO: implement build
throw UnimplementedError();
}
}
https://api.flutter.dev/flutter/widgets/GlobalKey-class.html

How do I update a Map with BLoC and equatable?

I am still a beginner with BLoC architecture. So far the UI updates when using int, bool, and other basic data types. But when it comes to Maps it really confuses me. My code basically looks like this:
my state
enum TalentStatus { initial, loading, loaded, error }
class TalentState extends Equatable {
const TalentState({
required this.talentStatus,
this.selectedService = const {},
required this.talents,
this.test = 0,
});
final TalentStatus talentStatus;
final Talents talents;
final Map<String, Service> selectedService;
final int test;
TalentState copyWith({
TalentStatus? talentStatus,
Talents? talents,
Map<String, Service>? selectedService,
int? test,
}) =>
TalentState(
selectedService: selectedService ?? this.selectedService,
talentStatus: talentStatus ?? this.talentStatus,
talents: talents ?? this.talents,
test: test ?? this.test,
);
#override
List<Object> get props => [talentStatus, talents, selectedService, test];
}
my event
abstract class TalentEvent extends Equatable {
const TalentEvent();
#override
List<Object> get props => [];
}
class TalentStarted extends TalentEvent {}
class TalentSelectService extends TalentEvent {
const TalentSelectService(
this.service,
this.talentName,
);
final Service service;
final String talentName;
}
and my bloc
class TalentBloc extends Bloc<TalentEvent, TalentState> {
TalentBloc(this._talentRepository)
: super(TalentState(
talentStatus: TalentStatus.initial, talents: Talents())) {
on<TalentSelectService>(_selectService);
}
final TalentRepository _talentRepository;
Future<void> _selectService(
TalentSelectService event,
Emitter<TalentState> emit,
) async {
state.selectedService[event.talentName] = event.service;
final selectedService = Map<String, Service>.of(state.selectedService);
emit(
state.copyWith(
selectedService: selectedService,
),
);
}
}
whenever an event TalentSelectService is called BlocBuilder doesn't trigger, what's wrong with my code?
Your Service object must be comparable. One suggestion is that it extends Equatable. Either way it have to implement (override) the == operator and hashCode
The reason your BlocBuilder doesn't trigger is (probably) that it doesn't recognize that there has been a change in the Map.

undefined_named_parameter in super constructor of abstract class

I am very new to Dart and Flutter, and I've been trying to make two classes (SuccessState and ErrorState) that implement an abstract class (DataState) with optional named parameters. For some reason, whenever I call the super constructor in SuccessState and ErrorState, I get an undefined_named_parameter error on the "data" parameter in the SuccessState constructor and "status" and "msg" parameters in the ErrorState constructor. Any input is greatly appreciated, thanks.
abstract class DataState<T> {
final T? data;
final int? status;
final String? msg;
const DataState({this.data, this.status, this.msg});
}
class SuccessState<T> implements DataState<T> {
const SuccessState(T data) : super(data: data);
#override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
class ErrorState<T> implements DataState<T> {
const ErrorState(int status, String msg) : super(status: status, msg: msg);
#override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
You want to extend SuccessState and ErrorState to DataState.
abstract class DataState<T> {
final T? data;
final int? status;
final String? msg;
const DataState({this.data, this.status, this.msg});
}
class SuccessState<T> extends DataState<T> {
SuccessState(T data) : super(data: data);
#override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
class ErrorState<T> implements DataState<T> {
const ErrorState(int status, String msg) : super();
#override
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
The problem is you are using implements rather than extends. You don't inherit the super constructor when using implements.
The thing you are doing wrong is your are implementing as you have to extend DataState class then issue will resolved
'class ErrorState implements DataState'
Change to class ErrorState extends DataState

get data from constructor and pass it to variable

I want to get the data from the constructor and passes it to a variable.
I dont want it to be listed...
Class:
class ListaProjetos extends StatefulWidget {
ListaProjetos({Key key, this.title, this.jsonData}) : super(key: key);
static const String routeName = "/ListaProjetos";
final String title;
final List jsonData;
#override
_ListaProjetosState createState() => _ListaProjetosState();
}
I want to:
class _ListaProjetosState extends State<ListaProjetos> {
var message = widget.jsonData;
print(message);
//HERE I WANT TO PRINT ALL JSON DATA
}
Inside the class you need create a method and place the variables initialization, for example:
#override
void initState() {
var message = widget.jsonData;
print(message);
}
Or you can declare the var outside of the method to use it in others methods.
var message;
#override
void initState() {
message = widget.jsonData;
print(message);
}

How can I use variable of CubitState inside Cubit? Flutter/Bloc

so I don't have any idea how to take argument from mine Cubit state which is AnswerPicked in this case, there is a code from states file.
part of 'answer_cubit.dart';
abstract class AnswerState extends Equatable {
const AnswerState();
#override
List<Object> get props => [];
}
class AnswerInitial extends AnswerState {}
class AnswerPicked extends AnswerState {
final String answer;
AnswerPicked({
this.answer,
});
String toString() => '{AnswerPicked: $answer}';
}
I want to use it in Cubit function right there:
part 'answer_state.dart';
class AnswerCubit extends Cubit<AnswerState> {
final ExamScoreCubit scoreCubit;
AnswerCubit({
#required this.scoreCubit,
}) : super(AnswerInitial());
List<String> userAnswersList = [];
void pickAnswer(String answer) {
emit(AnswerInitial());
emit(AnswerPicked(answer: answer));
}
void takeAnswer(String questionAnswer, int type) {
if(state is AnswerPicked){
userAnswersList.add(state.answer); // state.answer don't work
scoreCubit.checkAnswer(AnswerPicked().answer, questionAnswer, type); // AnswerPicked().answer don't work
}
emit(AnswerInitial());
}
}
In void takeAnswer() I don't want to pass it throw argument inside the widget tree using context. Any ideas how to do it?
userAnswersList.add((state as AnswerPicked) .answer);