问题
I'm trying BloC library with Counter test app but I have a little problem on my view when I use object to increment/decrement my counter. MyObject.MyProperty increment or decrement but my view not refreshed. Why?
Sample app with simple int variable.
My code with object:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:bloc/bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
void main() {
runApp(App());
}
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: BlocProvider(
builder: (context) => CounterBloc(),
child: CounterPage(),
),
);
}
}
class TestTest{
int myProperty;
TestTest(){
myProperty = 0;
}
}
class CounterPage extends StatefulWidget {
@override
_CounterPageState createState() => _CounterPageState();
}
class _CounterPageState extends State<CounterPage> {
@override
Widget build(BuildContext context) {
final counterBloc = BlocProvider.of<CounterBloc>(context);
return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: BlocBuilder<CounterBloc, TestTest>(
builder: (context, myClassTestTest) {
return Center(
child: Text(
'${myClassTestTest.myProperty}',
style: TextStyle(fontSize: 24.0),
),
);
},
),
floatingActionButton: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(vertical: 5.0),
child: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
counterBloc.dispatch(CounterEvent.increment);
},
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 5.0),
child: FloatingActionButton(
child: Icon(Icons.remove),
onPressed: () {
counterBloc.dispatch(CounterEvent.decrement);
},
),
),
],
),
);
}
}
enum CounterEvent { increment, decrement }
class CounterBloc extends Bloc<CounterEvent, TestTest> {
@override
TestTest get initialState => TestTest();
@override
Stream<TestTest> mapEventToState(CounterEvent event) async* {
switch (event) {
case CounterEvent.decrement:
currentState.myProperty -= 1;
yield currentState;
break;
case CounterEvent.increment:
currentState.myProperty += 1;
yield currentState;
break;
}
}
}
As you can see I edited the sample app a little just to add my class TestTest
with a int property myProperty
. When I clic on my button the value in my object change but my view is not refreshed.
How can I fix that?
I found a solution to recreate my TestTest in mapEventToState
but it's weird to recreate an entire object just to update a property...
Thank you
回答1:
I see you are using some kind of redux implementation and i'm not quite sure how this handles the rebuild when actions are dispatched.
As my knowledge you are dispatching an action that update some property on the widget but you are never calling setState again, so the widget is not being rebuild.
Try to call the increment or decrement actions inside a setState function.
Something like this:
onPressed: () {
setState(() => counterBloc.dispatch(CounterEvent.decrement));
},
回答2:
Just posting another possible solution for future visitors.
If you do yield
for the same state twice. Your UI won't update.
For example if you have already triggered ShowDataToUser(data)
from your Bloc and now after another operation you are yielding ShowDataToUser(data)
again, your UI won't update.
A quick hack around this would be to just create a DummyState()
in your Bloc and yield DummyState()
everytime before you yield ShowDataToUser(data)
Another solution apart from the quick hack around is to define a way for dart to compare and equate a class, cos currently by default ShowDataToUser
is the same class, but its contents
are different, but Dart doesn't know how to evaluate the content and compare them.
Enters Equatable
For this, you need to use the package : https://pub.dev/packages/equatable
Now you have to extend your Bloc states with Equtable like:
class DummyState extends Equatable {}
Now, if your Bloc States are extending Equatable then you must override the function:
@override
List<Object> get props => [name];
Here name
is the property/field name you want to compare when comparing two same classes.
This function tells your Bloc, how to differentiate between two same states (which might have different values in them).
回答3:
I have had same problem, and after googling couple of hour, I have found the solution in https://github.com/felangel/bloc/issues/203#issuecomment-480619554
As shown in the below code, The main problem is that when you yield the state, the previous state and the current state are equal, so blocBuilder have hadn't triggered.
case CounterEvent.decrement:
currentState.myProperty -= 1;
yield currentState;
You should copy the state, then change and yield it.
final TestTest newState = TestTest();
newState.myProperty = currentState.myProperty;
...
case CounterEvent.decrement:
newState.myProperty -= 1;
yield newState;
来源:https://stackoverflow.com/questions/57676874/flutter-bloc-not-refresh-my-view-counter-app