Поставщик: setState() или markNeedsBuild (), вызываемый во время сборки

#flutter #flutter-provider #flutter-hive

Вопрос:

Я использую Hive для хранения всего списка Card элементов, связанных с an Extension . На экране выбранного расширения я показываю карточки этого расширения с кучей фильтров/сортировки.

Для этого я использую ValueListenableBuilder , чтобы получить текущее расширение и их карты.

И когда я фильтрую/сортирую этот список, я хочу сохранить их в своем Provider классе, потому что я хотел бы повторно использовать этот список на другом экране.

Но мы это делаем context.read<ShowingCardsProvider>().setAll(sortedList) , я получил эту ошибку:

 setState() or markNeedsBuild() called during build.
 

Я ShowingCardsProvider пока ничего не слушаю.

Не могли бы вы объяснить мне, что здесь не так?

Спасибо!

главная.дротик

   runApp(MultiProvider(
    providers: [
      ChangeNotifierProvider.value(value: AuthProvider()),
      ChangeNotifierProvider.value(value: UserProvider()),
      ChangeNotifierProvider.value(value: ShowingCardsProvider()),
    ],
    child: MyApp(),
  ));
 

showing_cards_provider.dart

 import 'dart:collection';

import 'package:flutter/material.dart';
import 'package:pokecollect/card/models/card.dart';

class CardsProvider extends ChangeNotifier {
  final List<CardModel> _items = [];

  UnmodifiableListView<CardModel> get items => UnmodifiableListView(_items);

  void setAll(List<CardModel>? items) {
    _items.clear();
    if (items != null || items!.isNotEmpty) {
      _items.addAll(items);
    }
    notifyListeners();
  }

  void addAll(List<CardModel> items) {
    _items.addAll(items);
    notifyListeners();
  }

  void removeAll() {
    _items.clear();
    notifyListeners();
  }
}
 

extension_page.dart

   @override
  Widget build(BuildContext context) {
    return Scaffold(
      extendBodyBehindAppBar: true,
      body: SafeArea(
        child: LayoutBuilder(builder: (context, constraints) {
          return CustomScrollView(
            slivers: [
              SliverAppBar(
                ...
              ),
              ...
              SliverPadding(
                padding: EdgeInsets.symmetric(horizontal: 16.0),
                sliver: ValueListenableBuilder(
                  valueListenable: ExtensionBox.box.listenable(keys: [_extensionUuid]),
                  builder: (ctx, Box<Extension> box, child) {
                    List<CardModel>? cardsList = box
                        .get(_uuid)!
                        .cards
                        ?.where((card) => _isCardPassFilters(card))
                        .cast<CardModel>()
                        .toList();

                    var sortedList = _simpleSortCards(cardsList);

                    context.read<ShowingCardsProvider>().setAll(sortedList);

                    return _buildCardListGrid(sortedList);
                  },
                ),
              ),
            ]
          );
        }),
      ),
    );
  }
 

Ответ №1:

Это действительно потому, что я использую ValueListenableBuilder и, пока виджет создается, notifyListeners вызывается (в моего провайдера).

Мое исправление состоит в том, чтобы сделать это:

 return Future.delayed(
  Duration(milliseconds: 1),
  () => context.read<ShowingCardsProvider>().setAll(list),
);
 

Не очень красиво, но это работает 😅

Если у вас есть более элегантный способ, не стесняйтесь комментировать его!