Riverpod, как перестроить виджет, если поставщик косвенно влияет на пользовательский интерфейс?

Проблема в том, почему Riverpod не восстанавливается, хотя _isHintBarStack обновляется?

Здесь я слушаю hintBarStack (вы можете просто перейти в начало и конец виджета, средняя часть предназначена для дизайна.)

class AnswerBarBubble extends HookWidget {
  AnswerBarBubble({Key? key, required this.index}) : super(key: key);
  final int index;
  @override
  Widget build(BuildContext context) {
    final usableWidth = MediaQuery.of(context).size.width -
        (MediaQuery.of(context).padding.left +
            MediaQuery.of(context).padding.right);
    final usableHeight = MediaQuery.of(context).size.height -
        (MediaQuery.of(context).padding.bottom +
            MediaQuery.of(context).padding.top);
    final _answerBar = useProvider(IndexStackProvider);
    final _randomBar = useProvider(randomBarProvider);
    final _isHintBarStack = useProvider(hintBarStack).state.contains(index);
    final _questionIndex = useProvider(questionIndexProvider).state;
    final _question = useProvider(questionsProvider);

    var _boxDecor = useState(BoxDecoration(boxShadow: [
      BoxShadow(
        offset: Offset(usableWidth * 0.005, usableWidth * 0.005),
        blurRadius: usableWidth * 0.02,
        color: Color.fromRGBO(179, 118, 84, 1),
      ),
    ], shape: BoxShape.circle, color: Color.fromRGBO(255, 237, 227, 0.85)));
    String char;
    try {
      var temp = _answerBar[index];
      char = _randomBar[temp];
      _boxDecor.value = BoxDecoration(boxShadow: [
        BoxShadow(
          offset: Offset(usableWidth * 0.001, usableWidth * 0.001),
          blurRadius: usableWidth * 0.005,
          color: Colors.grey.shade300,
        ),
        BoxShadow(
          offset: Offset(usableWidth * 0.0008, usableWidth * 0.0008),
          blurRadius: usableWidth * 0.004,
          color: Colors.grey.shade800,
        ),
      ], shape: BoxShape.circle, color: Color.fromRGBO(255, 237, 227, 0.6));
    } catch (e) {
      char = '';
      _boxDecor.value = BoxDecoration(boxShadow: [
        BoxShadow(
          offset: Offset(usableWidth * 0.005, usableWidth * 0.005),
          blurRadius: usableWidth * 0.02,
          color: Color.fromRGBO(217, 196, 184, 1),
        ),
      ], shape: BoxShape.circle, color: Color.fromRGBO(255, 237, 227, 0.85));
    }
    return Badge(
        showBadge: !_isHintBarStack,
        elevation: 10,
        badgeContent: Text('${_question[_questionIndex].answer[index]}',
            style:
                TextStyle(fontSize: usableHeight * 0.020, color: Colors.white)),
        badgeColor: Colors.blue,
        padding: EdgeInsets.all(usableWidth * 0.01),
        alignment: Alignment.center,
        position: BadgePosition.topEnd(),
        child: AnimatedContainer(
            duration: Duration(milliseconds: 200),
            child: Center(
                child: Text('$char',
                    style: TextStyle(
                        fontSize: usableHeight * 0.03, color: Colors.black))),
            height: usableWidth * 0.1,
            width: usableWidth * 0.1,
            decoration: _boxDecor.value));
  }
}



StateProvider:

final hintBarStack = StateProvider((ref) {
  final questionIndex = ref.watch(questionIndexProvider).state;
  final question = ref.read(questionsProvider);
  final length = question[questionIndex].answer.length;
  final stack = <int>[];
  for (var i = 0; i < length; i++) {
    stack.add(i);
  }
  return stack;
});

HintButton: (Эта кнопка обновляет hintBarStack) (обратный вызов onTap)




class HintButton extends HookWidget {
  const HintButton({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final usableWidth = MediaQuery.of(context).size.width -
        (MediaQuery.of(context).padding.left +
            MediaQuery.of(context).padding.right);
    final usableHeight = MediaQuery.of(context).size.height -
        (MediaQuery.of(context).padding.bottom +
            MediaQuery.of(context).padding.top);
    return ElevatedButton(
        onLongPress: () {},
        onPressed: () {
          if (context.read(hintBarStack).state.length > 0) {
            final randInt =
                Random().nextInt(context.read(hintBarStack).state.length);
            context.read(hintBarStack).state.removeAt(randInt);
          }
        },
        child: Center(
          child: Icon(Icons.contact_support_sharp,
              color: Colors.black, size: usableHeight * 0.045),
        ),
        style: ElevatedButton.styleFrom(
            animationDuration: Duration(milliseconds: 300),
            shadowColor: Colors.black,
            onSurface: Colors.blueAccent,
            onPrimary: Colors.blueAccent.withOpacity(0.8),
            shape:
                StadiumBorder(side: BorderSide(width: 4, color: Colors.black)),
            primary: Colors.blueAccent.withOpacity(0.8),
            fixedSize: Size(usableWidth * 0.6, usableHeight * 0.06)));
  }
}

Главное, что сбивает меня с толку в отношении riverpod, я всегда наблюдаю, что riverpod только успешно перестраивает виджеты, когда я использую слушателей внутри текстовых виджетов, я имею в виду, что они напрямую видны на экране, в этих сценариях прямого использования пользовательского интерфейса нет проблема. Например, внутри AnswerBarBubble (первый сегмент кода):

Text('$char) - ›Riverpod перестраивает виджет при обновлении символов (прямое использование пользовательского интерфейса)

Badge(showBadge : _isHintBarStack, - ›Riverpod не восстанавливает виджет при обновлении _isHintBarStack. (Параметр виджета)


person sweCB    schedule 08.06.2021    source источник
comment
Это должно сработать. Вы можете поделиться своим кодом?   -  person Alex Hartford    schedule 08.06.2021
comment
@AlexHartford Можешь взглянуть? Я его обновил.   -  person sweCB    schedule 08.06.2021
comment
Ваш StateProvider делает слишком много. StateProvider предназначен для внешнего изменения простого состояния. Попробуйте преобразовать это в StateNotifierProvider, это должно решить вашу проблему.   -  person Alex Hartford    schedule 08.06.2021
comment
ура!! это сработало, большое спасибо за ваше решение. Пожалуйста, продолжайте помогать сообществу Riverpod, я прочитал все ваши ответы на stackoverflow riverpod и многому у них научился.   -  person sweCB    schedule 08.06.2021
comment
Я ценю это, добро пожаловать. Вы должны опубликовать ответ на этот вопрос, включая код, который решил вашу проблему, я думаю, это может помочь другим!   -  person Alex Hartford    schedule 08.06.2021
comment
@AlexHartford Привет, Алекс, можешь также взглянуть на мой последний пост, я не могу найти никого, кто мог бы мне помочь. Ссылка: stackoverflow.com/questions/67918044/   -  person sweCB    schedule 10.06.2021


Ответы (1)


Благодаря Алексу Хартфорду проблема заключалась в перегрузке StateProvider, я преобразовал StateProvider в StateNotifierProvider и решил свою проблему.

Вот виджет AnswerBubble:


class AnswerBarBubble extends HookWidget {
  AnswerBarBubble({Key? key, required this.index}) : super(key: key);
  final int index;
  @override
  Widget build(BuildContext context) {
    final usableWidth = MediaQuery.of(context).size.width -
        (MediaQuery.of(context).padding.left +
            MediaQuery.of(context).padding.right);
    final usableHeight = MediaQuery.of(context).size.height -
        (MediaQuery.of(context).padding.bottom +
            MediaQuery.of(context).padding.top);
    final _answerBar = useProvider(IndexStackProvider);
    final _randomBar = useProvider(randomBarProvider);
    final _isHintBarStack = useProvider(hintBarStack).contains(index);
    final _questionIndex = useProvider(questionIndexProvider).state;
    final _question = useProvider(questionsProvider);

    var _boxDecor = useState(BoxDecoration(boxShadow: [
      BoxShadow(
        offset: Offset(usableWidth * 0.005, usableWidth * 0.005),
        blurRadius: usableWidth * 0.02,
        color: Color.fromRGBO(179, 118, 84, 1),
      ),
    ], shape: BoxShape.circle, color: Color.fromRGBO(255, 237, 227, 0.85)));
    String char;
    try {
      var temp = _answerBar[index];
      char = _randomBar[temp];
      _boxDecor.value = BoxDecoration(boxShadow: [
        BoxShadow(
          offset: Offset(usableWidth * 0.001, usableWidth * 0.001),
          blurRadius: usableWidth * 0.005,
          color: Colors.grey.shade300,
        ),
        BoxShadow(
          offset: Offset(usableWidth * 0.0008, usableWidth * 0.0008),
          blurRadius: usableWidth * 0.004,
          color: Colors.grey.shade800,
        ),
      ], shape: BoxShape.circle, color: Color.fromRGBO(255, 237, 227, 0.6));
    } catch (e) {
      char = '';
      _boxDecor.value = BoxDecoration(boxShadow: [
        BoxShadow(
          offset: Offset(usableWidth * 0.005, usableWidth * 0.005),
          blurRadius: usableWidth * 0.02,
          color: Color.fromRGBO(217, 196, 184, 1),
        ),
      ], shape: BoxShape.circle, color: Color.fromRGBO(255, 237, 227, 0.85));
    }
    return Stack(
      alignment: Alignment.center,
      clipBehavior: Clip.hardEdge,
      children: [
        Badge(
            showBadge: !_isHintBarStack,
            elevation: 10,
            badgeContent: Text('${_question[_questionIndex].answer[index]}',
                style: TextStyle(
                    fontSize: usableHeight * 0.020, color: Colors.white)),
            badgeColor: Colors.blue,
            padding: EdgeInsets.all(usableWidth * 0.01),
            alignment: Alignment.center,
            position: BadgePosition.topEnd(),
            child: AnimatedContainer(
                duration: Duration(milliseconds: 200),
                child: Center(
                    child: Text('$char',
                        style: TextStyle(
                            fontSize: usableHeight * 0.03,
                            color: Colors.black))),
                height: usableWidth * 0.1,
                width: usableWidth * 0.1,
                decoration: _boxDecor.value))
      ],
    );
  }
}


StateNotifierProvider:

final hintBarStack = StateNotifierProvider<HintBarStack, List<int>>((ref) {
  final _question = ref.read(questionsProvider);
  final _index = ref.watch(questionIndexProvider).state;
  final _length = _question[_index].answer.length;
  final _state = <int>[];
  for (var i = 0; i < _length; i++) {
    _state.add(i);
  }
  return HintBarStack(_state);
});

HintButton:

class HintButton extends HookWidget {
  const HintButton({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final usableWidth = MediaQuery.of(context).size.width -
        (MediaQuery.of(context).padding.left +
            MediaQuery.of(context).padding.right);
    final usableHeight = MediaQuery.of(context).size.height -
        (MediaQuery.of(context).padding.bottom +
            MediaQuery.of(context).padding.top);
    return ElevatedButton(
        onLongPress: () {},
        onPressed: () {
          if (context.read(hintBarStack).length > 0) {
            final randInt = Random().nextInt(context.read(hintBarStack).length);
            context.read(hintBarStack.notifier).pop(randInt);
          }
        },
        child: Center(
          child: Icon(Icons.contact_support_sharp,
              color: Colors.black, size: usableHeight * 0.045),
        ),
        style: ElevatedButton.styleFrom(
            animationDuration: Duration(milliseconds: 300),
            shadowColor: Colors.black,
            onSurface: Colors.blueAccent,
            onPrimary: Colors.blueAccent.withOpacity(0.8),
            shape:
                StadiumBorder(side: BorderSide(width: 4, color: Colors.black)),
            primary: Colors.blueAccent.withOpacity(0.8),
            fixedSize: Size(usableWidth * 0.6, usableHeight * 0.06)));
  }
}

StateNotifier:

class HintBarStack extends StateNotifier<List<int>> {
  HintBarStack(List<int> state) : super(state);
  void pop(int index) {
    var temp = state;
    temp.removeAt(index);
    state = [...temp];
  }
}
person sweCB    schedule 08.06.2021