Reputation: 23
So, I tried to learn flutter especially in BLoC method and I made a simple ToggleButtons with BLoC. Here it looks like
ToggleUI.dart
class Flutter501 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter 50 With Bloc Package',
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
BlocProvider<ToggleBloc>(
builder: (context) => ToggleBloc(maxToggles: 4),
child: MyToggle(),
)
],
),
),
),
);
}
}
class MyToggle extends StatelessWidget {
const MyToggle({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
ToggleBloc bloc = BlocProvider.of<ToggleBloc>(context);
return BlocBuilder<ToggleBloc, List<bool>>(
bloc: bloc,
builder: (context, state) {
return ToggleButtons(
children: [
Icon(Icons.arrow_back),
Icon(Icons.arrow_upward),
Icon(Icons.arrow_forward),
Icon(Icons.arrow_downward),
],
onPressed: (idx) {
bloc.dispatch(ToggleTap(index: idx));
},
isSelected: state,
);
},
);
}
}
ToogleBloc.dart
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';
abstract class ToggleEvent extends Equatable {
const ToggleEvent();
}
class ToggleTap extends ToggleEvent {
final int index;
ToggleTap({this.index});
@override
// TODO: implement props
List<Object> get props => [];
}
class ToggleBloc extends Bloc<ToggleEvent, List<bool>> {
final List<bool> toggles = [];
ToggleBloc({
@required int maxToggles,
}) {
for (int i = 0; i < maxToggles; i++) {
this.toggles.add(false);
}
}
@override
// TODO: implement initialState
List<bool> get initialState => this.toggles;
@override
Stream<List<bool>> mapEventToState(ToggleEvent event) async* {
// TODO: implement mapEventToState
if (event is ToggleTap) {
this.toggles[event.index] = !this.toggles[event.index];
}
yield this.toggles;
}
}
The problem came when I tried to Tap/Press one of the buttons, but it doesn't want to change into the active button. But it works whenever I tried to press the "Hot Reload". It likes I have to make a setState whenever the button pressed.
Upvotes: 2
Views: 3878
Reputation: 18690
BlocBuilder will ignore the update if a new state was equal to the old state. When comparing two lists in Dart language, if they are the same instance, they are equal, otherwise, they are not equal.
So, in your case, you would have to create a new instance of list for every state change, or define a state object and send your list as property of it.
Here is how you would create new list instance for every state:
if (event is ToggleTap) {
this.toggles[event.index] = !this.toggles[event.index];
}
yield List.from(this.toggles);
You can read more about bloc library and equality here: https://bloclibrary.dev/#/faqs?id=when-to-use-equatable
Upvotes: 2
Reputation: 3034
The BlocBuilder.builder
method is only executed if the State
changes. So in your case the State
is a List<bool>
of which you only change a specific index and yield the same object. Because of this, BlocBuilder can't determine if the List changed and therefore doesn't trigger a rebuild of the UI.
See https://github.com/felangel/bloc/blob/master/docs/faqs.md for the explanation in the flutter_bloc
docs:
Equatable properties should always be copied rather than modified. If an Equatable class contains a List or Map as properties, be sure to use List.from or Map.from respectively to ensure that equality is evaluated based on the values of the properties rather than the reference.
In your ToggleBloc
, change the List like this, so it creates a completely new List object:
@override
Stream<List<bool>> mapEventToState(ToggleEvent event) async* {
// TODO: implement mapEventToState
if (event is ToggleTap) {
this.toggles[event.index] = !this.toggles[event.index];
this.toggles = List.from(this.toggles);
}
yield this.toggles;
}
Also, make sure to set the props
for your event, although it won't really matter for this specific question.
Upvotes: 2