Flutter BloC не обновляет мой вид (приложение счетчика)

Я пытаюсь использовать библиотеку BloC с тестовым приложением Counter, но у меня возникает небольшая проблема, когда я использую объект для увеличения / уменьшения счетчика. MyObject.MyProperty увеличивается или уменьшается, но мое представление не обновляется. Почему?

Образец приложения с простой переменной int.

Мой код с объектом:

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;
    }
  }
}

Как видите, я немного отредактировал пример приложения, просто чтобы добавить свой класс TestTest со свойством int myProperty. Когда я нажимаю кнопку, значение в моем объекте изменяется, но мое представление не обновляется.

Как я могу это исправить?

Я нашел решение воссоздать свой TestTest в mapEventToState, но странно воссоздавать весь объект только для обновления свойства ...

Спасибо


person Naografix    schedule 27.08.2019    source источник
comment
вы можете дать свое точное решение для этого? Я бы хотел посмотреть, как это решить, спасибо.   -  person Axil    schedule 02.06.2020


Ответы (3)


Я вижу, что вы используете какую-то реализацию редукции, и я не совсем уверен, как она обрабатывает перестройку при отправке действий.

Насколько мне известно, вы отправляете действие, обновляющее какое-то свойство виджета, но никогда больше не вызываете setState, поэтому виджет не перестраивается.

Попробуйте вызвать действия увеличения или уменьшения внутри функции setState.

Что-то вроде этого:

onPressed: () {
 setState(() => counterBloc.dispatch(CounterEvent.decrement));
},
person Rodrigo Bastos    schedule 27.08.2019
comment
У меня работает переключение в Stateful и использование setState. Мы не можем обновить пользовательский интерфейс, потому что мой объект неизменен: github.com/felangel/bloc/issues/ 103 - person Naografix; 28.08.2019
comment
это решение может иногда работать, но это не очень хорошая идея. блок должен автоматически менять состояние без setState - person Ali; 14.11.2019

Просто опубликуйте еще одно возможное решение для будущих посетителей.

Если вы сделаете yield для одного и того же состояния дважды. Ваш пользовательский интерфейс не обновляется.

Например, если вы уже запустили ShowDataToUser(data) из своего блока, а теперь, после другой операции, вы снова получаете ShowDataToUser(data), ваш пользовательский интерфейс не будет обновляться.

Быстрый способ обойти это - просто создать DummyState() в своем блоке и давать DummyState() каждый раз, прежде чем давать ShowDataToUser(data)

Другое решение, помимо быстрого взлома, - определить способ для дротика сравнивать и приравнивать класс, потому что в настоящее время по умолчанию ShowDataToUser это тот же класс, но его contents разные, но Дарт не знает, как оценивать контент и Сравните их.

Входит Equatable

Для этого вам необходимо использовать пакет: https://pub.dev/packages/equatable

Теперь вам нужно расширить свои состояния Bloc с помощью Equtable, например:

class DummyState extends Equatable {}

Теперь, если ваши состояния блока расширяют Equatable, вы должны переопределить функцию:

@override
  List<Object> get props => [name];

Здесь name - это имя свойства / поля, которое вы хотите сравнить при сравнении двух одинаковых классов.

Эта функция сообщает вашему блоку, как различать два одинаковых состояния (которые могут иметь разные значения).

person devDeejay    schedule 26.03.2020
comment
Для меня это было идеальным решением. Интересно, почему мы должны уступить 2 состояния, чтобы это работало. - person calistus; 09.05.2020
comment
Я не понимаю, как вы это решили, у меня такая же проблема. Вы можете уточнить. Это моя проблема. stackoverflow.com/questions/62132532/ @devDeejay - person Axil; 02.06.2020
comment
Надеюсь, это поможет @Axil - person devDeejay; 02.06.2020

У меня была такая же проблема, и после нескольких часов поиска в Google я нашел решение в https://github.com/felangel/bloc/issues/203#issuecomment-480619554

Как показано в приведенном ниже коде, основная проблема заключается в том, что, когда вы передаете состояние, предыдущее состояние и текущее состояние равны, поэтому blocBuilder не сработал.

case CounterEvent.decrement:        
  currentState.myProperty -= 1;
  yield currentState; 

Вы должны скопировать состояние, затем изменить и уступить его.

final TestTest newState = TestTest();
newState.myProperty = currentState.myProperty;

...

case CounterEvent.decrement:        
  newState.myProperty -= 1;
  yield newState; 
person Ali    schedule 14.11.2019