Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
3.4k views
in Technique[技术] by (71.8m points)

android - Flutter: Looking up a deactivated widget's ancestor is unsafe


I know that there are already two posts regarding this question, but I could not solve my issue looking at them. Probably because in my case the issue is different.

The code is the following. I want to load some data from the database while showing a loading page. After the data is loaded, I initialize the provider with the loaded data and then I move into a different page. This code does not need to be in a StatefulWidget, but I try to put it in a StatefulWidget to solve the issue, but without success.

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 do not have any error if the application runs from scratch, but when I am doing a "Hot Reload" I get the following error pretty often, and it is annoying since I need to restart the application for each small change in the code.

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 

I don't know why "fetching data..." is printed twice, and I have no clue on how to solve the issue.


I thought the issue was solved with the solution of Saman Salehi, but working in debug mode I had the same exception in the _fetchData function, that now is called in the function 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.)

I got another error after applying the edits suggested by Stewie Griffin .

The error is on the line Provider.of<DataProvider>(context, listen: false).init(initData);

I got it during a hot reload. It seems less common than the other error, so the answer of Stewie Griffin surely improved the stability of my Stewie 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 

Could you please help me?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

First of all, never call async methods inside of build as mentioned. Build method is constantly rebuilt and it will cause your fetch method to be repeated like an infinite loop. After you fix that, you still get the same error because of this part:

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

You shouldn't call Navigator during build. Here is what you need to do:

Add this line to the top of your file to use SchedulerBinding:

import 'package:flutter/scheduler.dart';

Wrap Navigator with SchedulerBinding to wait for completing the state before navigating to another screen. Then, call your async method inside of 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());
  }
}

Tip: You don't need to pass the context in a Stateful Widget because you can access it from everywhere.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...