Я пытаюсь изучить Flutter и лучше познакомиться с передачей данных. Итак, у меня есть очень простое приложение, которое представляет собой своего рода сложную версию этого: Версия поставщика стартовая демонстрация флаттера

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

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

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

Приложение работает нормально, как и ожидалось, без сбоев и, насколько мои зеленые травяные глаза могут сказать, мой код структурирован точно так же, как в примере Flutter (в отношении Provider StateManagement). Однако я получаю эту ошибку в консоли:

======== Exception caught by foundation library ====================================================
The following assertion was thrown while dispatching notifications for Keeper:
setState() or markNeedsBuild() called during build.

This _InheritedProviderScope<Keeper> widget cannot be marked as needing to build because the framework is already in the process of building widgets.  A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: _InheritedProviderScope<Keeper>
  value: Instance of 'Keeper'
  listening to value
The widget which was currently being built when the offending call was made was: Consumer<Keeper>
  dirty
  dependencies: [_InheritedProviderScope<Keeper>]

Страница 1

class ScreenOne extends StatelessWidget {
  static const String id = 'screen_one';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(
          'Page One',
        ),
      ),
      backgroundColor: Colors.grey.shade200,
      body: Container(
        child: SafeArea(
          child: Padding(
            padding: EdgeInsets.all(20.0),
            child: Center(
              child: Column(
                children: [
                  CustomTextBoxes(title: 'Screen One'),
                  Consumer<Keeper>(
                    builder: (_, keeper, child) => Text(
                      '${keeper.pageOneValue}',
                      style: TextStyle(color: Colors.grey, fontSize: 20.0),
                    ),
                  ),
                  CustomTextBoxes(title: 'Screen Two'),
                  Consumer<Keeper>(
                    builder: (_, keeper, child) => Text(
                      '${keeper.pageTwoValue}',
                      style: TextStyle(color: Colors.grey, fontSize: 20.0),
                    ),
                  ),
                  CustomTextBoxes(title: 'Total'),
                  Consumer<Keeper>(
                    builder: (_, keeper, child) => Text(
                      '${keeper.addCounters()}',
                      style: TextStyle(color: Colors.grey, fontSize: 20.0),
                    ),
                  ),
                  SizedBox(
                    height: 20.0,
                  ),
                  CustomButton(
                    text: 'Screen 2',
                    function: () {
                      Navigator.pushNamed(context, ScreenTwo.id);
                    },
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
      floatingActionButton: CustomFloatingButton(
        function: () {
          var counter = context.read<Keeper>();
          counter.incrementCounterOne();
        },
      ),
    );
  }
}

"Хранитель"

class Keeper with ChangeNotifier {
  int pageOneValue = 0;
  int pageTwoValue = 0;
  int totalValue = 0;

  void incrementCounterOne() {
    pageOneValue += 1;
    notifyListeners();
  }

  void incrementCounterTwo() {
    pageTwoValue += 1;
    notifyListeners();
  }

  int addCounters() {
    totalValue = pageOneValue + pageTwoValue;
    notifyListeners();
    return totalValue;
  }
}

Основной

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => Keeper(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(primarySwatch: Colors.blue),
      initialRoute: ScreenOne.id,
      routes: {
        ScreenOne.id: (context) => ScreenOne(),
        ScreenTwo.id: (context) => ScreenTwo()
      },
    );
  }
}
0
RobbB 17 Фев 2021 в 07:52

1 ответ

Лучший ответ

Ваша проблема в вызове addCounters() внутри вашего метода build. addCounters() вызывает notifyListeners(), который запускает setState().

Это невозможно выполнить в вашей функции build. Это может быть выполнено позже только по запросу пользователя. Например, внутри onPressed кнопки, как и для incrementCounterOne().

Вместо вычисления и сохранения общего значения ваших двух счетчиков вы можете использовать геттер:

Хранитель:

class Keeper with ChangeNotifier {
  int pageOneValue = 0;
  int pageTwoValue = 0;

  int get totalValue => pageOneValue + pageTwoValue;

  void incrementCounterOne() {
    pageOneValue += 1;
    notifyListeners();
  }

  void incrementCounterTwo() {
    pageTwoValue += 1;
    notifyListeners();
  }
}

ScreenOne:

Consumer<Keeper>(
  builder: (_, keeper, child) => Text(
    '${keeper.totalValue}', // addCounters()}',
    style: TextStyle(color: Colors.grey, fontSize: 20.0),
  ),
),
1
Thierry 17 Фев 2021 в 06:25