Flutter NullSafety cannot push Page after showing and change modalProgressHUD

Viewed 222

I'm using Provider package to expose a simple boolean variabile that allow to change the status of variable "inAsyncCall" of the ModalProgressHUD widget.

When i try to do somethings before navigate to another page, and i want to display the circlular progress indicator during that computation, when the Future terminated, the current widget has been disposed and i cannot use Navigator.push():

Unhandled Exception: This widget has been unmounted, so the State no longer has a context (and should be considered defunct).
Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active.

this is my Provider with ChangeNotifier class:

class CartProvider with ChangeNotifier {
  bool _inAsync = false;

  bool get inAsync => _inAsync;

  void setInAsync(bool flag) {
    this._inAsync = flag;
    notifyListeners();
  }
}

I inject the provider before the MaterialApp widget like this:

 void main() async {
   runApp(App());
 }

class App extends StatefulWidget {
  @override
  _AppState createState() => _AppState();
}

class _AppState extends State<App> {
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(
          create: (context) => CartProvider(),
        )
      ],
      child: MaterialApp(
        home: HomePage(),
      ),
    );
  }
}

And this is the simple home page where i access via context the provider injected:

class HomePage extends StatefulWidget {
  HomePage({Key? key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    final cartProvider = context.watch<CartProvider>();
    return ModalProgressHUD(
      inAsyncCall: cartProvider.inAsync,
      child: Scaffold(
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text('HOME'),
              FirstStatefulWidget(),
            ],
          ),
        ),
        bottomNavigationBar: SecondStatefulWidget(),
      ),
    );
  }
}

class FirstStatefulWidget extends StatefulWidget {
  FirstStatefulWidget({Key? key}) : super(key: key);

  @override
  _FirstStatefulWidgetState createState() => _FirstStatefulWidgetState();
}

class _FirstStatefulWidgetState extends State<FirstStatefulWidget> {
  late CartProvider cartProvider = context.read<CartProvider>();

  Future doSomething() async {
    cartProvider.setInAsync(true);
    await Future.delayed(
      Duration(seconds: 2),
      () => {
        cartProvider.setInAsync(false),
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => SecondPage(),
          ),
        )
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: ElevatedButton(
        child: Text('do call'),
        onPressed: doSomething,
      ),
    );
  }
}

class SecondStatefulWidget extends StatefulWidget {
  SecondStatefulWidget({Key? key}) : super(key: key);

  @override
  _SecondStatefulWidgetState createState() => _SecondStatefulWidgetState();
}

class _SecondStatefulWidgetState extends State<SecondStatefulWidget> {
  late CartProvider cartProvider = context.read<CartProvider>();

  void goToAnotherPageAfterCall() async {
    try {
      cartProvider.setInAsync(true);
      Future.delayed(
        Duration(seconds: 2),
        () => {
          cartProvider.setInAsync(false),
          Navigator.push(
            context,
            new MaterialPageRoute(
              builder: (context) => SecondPage(),
            ),
          )
        },
      );
    } on Exception catch (e) {
      cartProvider.setInAsync(false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: ElevatedButton(
        child: Text('goToAnotherPage'),
        onPressed: goToAnotherPageAfterCall,
      ),
    );
  }
}
0 Answers
Related