Флаттер: поиск предка деактивированного виджета небезопасен - PullRequest
0 голосов
/ 25 апреля 2020

Я знаю, что уже есть два сообщения по этому вопросу, но я не мог решить свою проблему, глядя на них. Возможно, потому что в моем случае проблема в другом.

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

class _InitDBDataState extends State<_InitDBData> {
  @override
  Widget build(BuildContext context) {
    _fetchData(context);
    return const Center(child: const CircularProgressIndicator());
  }

  Future<void> _fetchData(BuildContext context) async {
    print('fetching data...');
    print('context: $context');
    final initData = await DBService.service.getInitialData();
    print('Data fetched');
    print('context: $context');
    Provider.of<DataProvider>(context, listen: false).init(initData);
    Navigator.of(context).pushReplacementNamed(MainScreen.routeName);
  }
}

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

I/flutter ( 9596): fetching data...
I/flutter ( 9596): context: _InitDBData(dirty, state: _InitDBDataState#46860)
I/flutter ( 9596): fetching data...
I/flutter ( 9596): context: _InitDBData(dirty, state: _InitDBDataState#55124)
I/flutter ( 9596): Data fetched
I/flutter ( 9596): context: _InitDBData
E/flutter ( 9596): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe.
E/flutter ( 9596): At this point the state of the widget's element tree is no longer stable.
E/flutter ( 9596): To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.
E/flutter ( 9596): #0      Element._debugCheckStateIsActiveForAncestorLookup.<anonymous closure> 
package:flutter/…/widgets/framework.dart:3508
E/flutter ( 9596): #1      Element._debugCheckStateIsActiveForAncestorLookup 
package:flutter/…/widgets/framework.dart:3522
E/flutter ( 9596): #2      Element.getElementForInheritedWidgetOfExactType 
package:flutter/…/widgets/framework.dart:3588
E/flutter ( 9596): #3      Provider.of 
package:provider/src/provider.dart:221
E/flutter ( 9596): #4      _InitDBDataState._fetchData 
package:productive_diary/initScreen.dart:46
E/flutter ( 9596): <asynchronous suspension>
E/flutter ( 9596): #5      _InitDBDataState.build 

Я не Я не знаю, почему «выборка данных ...» печатается дважды, и я понятия не имею, как решить проблему.


Я думал, что проблема была решена с помощью решения Саман Салехи , но работая в режиме отладки, у меня было то же исключение в функции _fetchData, которое теперь вызывается в функции initState ()

Exception has occurred.
FlutterError (Looking up a deactivated widget's ancestor is unsafe.
At this point the state of the widget's element tree is no longer stable.
To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.)

Я получил другую ошибку после применения изменений, предложенных Stew ie Griffin .

The ошибка на линии Provider.of<DataProvider>(context, listen: false).init(initData);

Я получил это во время горячей перезагрузки. Кажется, он встречается реже, чем другая ошибка, поэтому ответ Stew ie Griffin определенно улучшил стабильность моего Stew ie Griffin

E/flutter (23815): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: NoSuchMethodError: The getter 'owner' was called on null.
E/flutter (23815): Receiver: null
E/flutter (23815): Tried calling: owner
E/flutter (23815): #0      Object.noSuchMethod  (dart:core-patch/object_patch.dart:53:5)
E/flutter (23815): #1      Provider.of 
package:provider/src/provider.dart:193
E/flutter (23815): #2      _InitDBDataState._fetchData 
package:productive_diary/initScreen.dart:49
E/flutter (23815): <asynchronous suspension>
E/flutter (23815): #3      _InitDBDataState.initState 

Не могли бы вы мне помочь?

Ответы [ 2 ]

1 голос
/ 01 мая 2020

Прежде всего, никогда не вызывайте asyn c методы внутри сборки, как уже упоминалось. Метод сборки постоянно перестраивается, и он будет повторять ваш метод извлечения как бесконечный l oop. После исправления вы по-прежнему получаете ту же ошибку из-за этой части:

Navigator.of(context).pushReplacementNamed(MainScreen.routeName);

Вы не должны вызывать Navigator во время сборки. Вот что вам нужно сделать:

Добавьте эту строку в начало вашего файла, чтобы использовать SchedulerBinding:

import 'package:flutter/scheduler.dart';

Оберните Навигатор в SchedulerBinding, чтобы дождаться завершения состояния перед навигацией на другой экран. Затем вызовите свой метод asyn c внутри initState.

class _InitDBDataState extends State<_InitDBData> {
@override
  void initState() {
    // Call your async method here
    _fetchData();
    super.initState();
  }

  Future<void> _fetchData() async {
    print('fetching data...');
    print('context: $context');
    final initData = await DBService.service.getInitialData();
    print('Data fetched');
    print('context: $context');
    Provider.of<DataProvider>(context, listen: false).init(initData);

    // Wrap Navigator with SchedulerBinding to wait for rendering state before navigating
    SchedulerBinding.instance.addPostFrameCallback((_) {
      Navigator.of(context).pushReplacementNamed(MainScreen.routeName);
    });
  }
  @override
  Widget build(BuildContext context) {
    return Center(child: CircularProgressIndicator());
  }
}

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

1 голос
/ 25 апреля 2020

Вы не должны использовать метод build для чего-либо, кроме построения пользовательского интерфейса. build можно вызвать в любое время, даже если его нет на экране.

Я бы переместил _fetchData на initState, чтобы он не вызвал никакого конфликта при методе build.

class _InitDBDataState extends State<_InitDBData> {

  @override
  void initState() {
    super.initState();
    _fetchData(context);
  }

  @override
  Widget build(BuildContext context) {
    return const Center(child: const CircularProgressIndicator());
  }

  Future<void> _fetchData(BuildContext context) async {
    print('fetching data...');
    print('context: $context');
    final initData = await DBService.service.getInitialData();
    print('Data fetched');
    print('context: $context');
    Provider.of<DataProvider>(context, listen: false).init(initData);
    Navigator.of(context).pushReplacementNamed(MainScreen.routeName);
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...