Как использовать провайдера Riverpod в initState

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

Для этой цели я создал ScrollController в качестве провайдера.

      final scrollControllerProvider = StateProvider<ScrollController?>((ref) => ScrollController());

Для прокрутки я использую.animateToиз действий AppBar.

      ref.read(scrollControllerProvider)!.animateTo(
  0,
  duration: const Duration(milliseconds: 500),
  curve: Curves.easeInOut,
);

Прокрутка работает, но выдает исключение

      The provided ScrollController is currently attached to more than one ScrollPosition.

Я читал, что должен использовать StatefulWidget. Однако, используя ConsumerStatefulWidget, я не могу создать ScrollController с помощью Riverpod, потому что мне нужно инициировать его вinitState()и я не могу получить доступ к провайдеру с него. Возможно ли совместить эти два элемента?

3 ответа

Я думаю, что решение состоит в том, чтобы использовать ref.read(...) в методе инициализации. Это не обновит представление и не примет правильное значение.

ScrollControllerэтоChangeNotifierкласс, вместоStateProvider, использоватьChangeNotifierProvider, имейте в виду, если вы не можете прикрепить его к нескольким прокручиваемым.

      final myScrollControllerProvider =
    ChangeNotifierProvider((ref) => ScrollController());

может быть, мы можем сделать это в новом классе, создать лайкforceDispose()метод:

      class MyScrollController extends ScrollController {
  void forceDispose(){
    this.dispose();
  }
}

но поскольку я не уверен, как вы это используете и где вы будете располагать его в каком-то состоянии, либо мы не можем инициализировать его в каком-то initState(), давайте просто оставим провайдеру autoDispose самого себя:

       final myScrollControllerProvider =
    ChangeNotifierProvider.autoDispose((ref) => ScrollController());

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

      class MyWidget extends ConsumerWidget {
  const MyWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, ref) {
    final sc = ref.watch(myScrollControllerProvider);
    return Scaffold(
      body: Column(
        children: [
          Row(
            children: [
              Consumer(
                builder: (BuildContext context, WidgetRef ref, Widget? _) {
                  final scFromAnotherWidget = ref.watch(myScrollControllerProvider);
                  return TextButton(
                      onPressed: () {
                        scFromAnotherWidget.jumpTo(sc.offset + 30);
                      },
                      child: const Text("Animate to"));
                },
              ),
              TextButton(
                  onPressed: () {
                    Navigator.pushReplacement(
                        context,
                        MaterialPageRoute(
                            builder: (context) => const NewScreen()));
                  },
                  child: const Text("Navigate to brand new Screen"))
            ],
          ),
          Expanded(
            child: ListView.builder(
              controller: sc,
              itemCount: 100,
              itemBuilder: (context, index) => ListTile(
                title: Text('$index'),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class NewScreen extends StatelessWidget {
  const NewScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: TextButton(
            onPressed: () {
              Navigator.pushReplacement(context,
                  MaterialPageRoute(builder: (context) => const MyWidget()));
            },
            child: const Text('Back to My Widget')),
      ),
    );
  }
}

У вас есть доступ кrefвнутри любой функции, которая находится вConsumerStatefulWidget. Таким образом, вы можете просто позвонить своему провайдеру вinitState()работать, и он должен вести себя нормально

      ...
  class _MyClassState extends ConsumerState<MyClass> {

  late ScrollController _scrollController;
  
  @override
    void initState() {
      super.initState();
      _scrollController = ref.watch(scrollControllerProvider); // Doesn't throw error
    }
...
Другие вопросы по тегам