I have problems using the Navigation in Flutter with the MVVM pattern.

I have two Views:

  • Login
  • Dashboard

I Navigate with Navigator.pushReplacementNamed(context, Dashboard) from Login to Dashboard. When the user loggs out I naviagte from Dashboard to Login with Navigator.pushReplacementNamed(context, Login)

But the i get the following error:

A Login ViewModel was used after being disposed. Once you have called dispose() on a LoginViewModel, it can no longer be used.

I thought that a View\Widget is created when i call it again after it was disposed? What should i do? It makes no sense to Navigate with

pushNamed

and leave the widget in the widget tree.

My LoginView:

import 'package:app/common/routing_contstants.dart';
import 'package:app/core/viewmodels/views/login_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_signin_button/button_view.dart';
import 'package:flutter_signin_button/flutter_signin_button.dart';
import 'package:app/common/colors.dart';

import 'base_view.dart';

class LoginView extends StatefulWidget {
  @override
  _LoginView createState() => _LoginView();
}

class _LoginView extends State<LoginView> {
  final TextEditingController _mailCtr = TextEditingController();
  final TextEditingController _passCtr = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return BaseView<LoginViewModel>(
      builder: (context, model, child) => Scaffold(
        body: Container(
          width: double.infinity,
          height: double.infinity,
          // Add box decoration
          decoration: BoxDecoration(
            // Box decoration takes a gradient
            gradient: LinearGradient(
              // Where the linear gradient begins and ends
              begin: Alignment.topRight,
              end: Alignment.bottomCenter,
              // Add one stop for each color. Stops should increase from 0 to 1
              stops: [0.2, 0.4, 0.6, 1.0],
              colors: [
                Colors.deepPurple[800],
                Colors.deepPurple[700],
                Colors.deepPurple[500],
                Theme.of(context).colorScheme.pink,
              ],
            ),
          ),
          child: Center(
              child: SingleChildScrollView(
            child: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 20),
              child: Column(
                children: <Widget>[
                  SizedBox(
                    height: 120,
                  ),
                  Image.asset(
                    "assets/images/icon.png",
                    color: Colors.white,
                    width: 200,
                    height: 100,
                  ),
                  SizedBox(
                    height: 20,
                  ),
                  Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: TextField(
                      controller: _mailCtr,
                      decoration: InputDecoration(
                        hintText: "Email",
                        hintStyle: TextStyle(color: Colors.grey[500]),
                        icon: Icon(
                          Icons.email,
                          color: Colors.white,
                        ),
                        focusedBorder: UnderlineInputBorder(
                          borderSide: BorderSide(color: Colors.white),
                        ),
                      ),
                      cursorColor: Colors.white,
                      style: TextStyle(color: Colors.white),
                    ),
                  ),
                  Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: TextField(
                      controller: _passCtr,
                      decoration: InputDecoration(
                        hintText: "Password",
                        hintStyle: TextStyle(color: Colors.grey[500]),
                        icon: Icon(
                          Icons.lock,
                          color: Colors.white,
                        ),
                        focusedBorder: UnderlineInputBorder(
                          borderSide: BorderSide(color: Colors.white),
                        ),
                      ),
                      cursorColor: Colors.white,
                      style: TextStyle(color: Colors.white),
                      obscureText: true,
                    ),
                  ),
                  GestureDetector(
                    onTap: () {},
                    child: Container(
                      margin: EdgeInsets.symmetric(vertical: 10),
                      child: new Text("Forget Password?",
                          style: Theme.of(context)
                              .textTheme
                              .body1
                              .copyWith(color: Colors.white)),
                    ),
                  ),
                  Padding(
                    padding: const EdgeInsets.all(16.0),
                    child: SizedBox(
                        width: double.infinity,
                        child: OutlineButton(
                          child: Container(
                              margin: EdgeInsets.symmetric(vertical: 10),
                              child: new Text("Login",
                                  style: Theme.of(context)
                                      .textTheme
                                      .title
                                      .copyWith(color: Colors.white))),
                          onPressed: () async {
                            var loginSuccess = await model.signInWithEmailAndPassword(context, _mailCtr.text, _passCtr.text);
                            if (loginSuccess != null) {
                              Navigator.pushReplacementNamed(context, DashboardRoute);
                            }
                          },
                          // color: Colors.blue,
                          textColor: Colors.white,
                          borderSide: BorderSide(color: Colors.white),
                        )),
                  ),
                  Padding(
                    padding: const EdgeInsets.all(0.8),
                    child: SignInButton(
                      Buttons.Google,
                      onPressed: () {},
                    ),
                  ),
                  Padding(
                    padding: const EdgeInsets.all(0.8),
                    child: SignInButton(
                      Buttons.Facebook,
                      onPressed: () {},
                    ),
                  ),
                  SizedBox(
                    height: 100,
                  ),
                ],
              ),
            ),
          )),
        ),
      ),
    );
  }
}

My DashboardView:

import 'package:app/common/routing_contstants.dart';
import 'package:app/core/viewmodels/views/dashboard_view_model.dart';
import 'package:flutter/material.dart';
import 'package:app/common/colors.dart';

import 'base_view.dart';

class DashboardView extends StatefulWidget {
  @override
  _DashboardView createState() => _DashboardView();
}

class _DashboardView extends State<DashboardView> {
  @override
  Widget build(BuildContext context) {
    return BaseView<DashboardViewModel>(
      builder: (context, model, child) => Scaffold(
        body: Container(
          width: double.infinity,
          height: double.infinity,
          // Add box decoration
          decoration: BoxDecoration(
            // Box decoration takes a gradient
            gradient: LinearGradient(
              // Where the linear gradient begins and ends
              begin: Alignment.topRight,
              end: Alignment.bottomCenter,
              // Add one stop for each color. Stops should increase from 0 to 1
              stops: [0.3, 0.7, 0.4, 0.3],
              colors: [
                Theme.of(context).colorScheme.blue,
                Theme.of(context).colorScheme.darkBlue,
                Theme.of(context).colorScheme.pink,
                Colors.pink[600],
              ],
            ),
          ),
          child: Center(
            child: SingleChildScrollView(
              child: Padding(
                padding: const EdgeInsets.symmetric(horizontal: 20),
                child: Column(
                  children: <Widget>[
                    SizedBox(height: 32.0),
                    SizedBox(
                      height: 50.0,
                      child: _signOutButton(context, model),
                    ),
                    SizedBox(height: 30.0),
                    SizedBox(
                      height: 100,
                    ),
                  ],
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }

  Widget _signOutButton(context, DashboardViewModel model) {
    return OutlineButton(
      child: Container(
          margin: EdgeInsets.symmetric(vertical: 10),
          child: new Text("Logout",
              style: Theme.of(context)
                  .textTheme
                  .title
                  .copyWith(color: Colors.white))),
      onPressed: () async {
         await model.signOut(context).then(
              (_) => {
                Navigator.pushReplacementNamed(context, LoginRoute)
              },
            );
      },
      // color: Colors.blue,
      textColor: Colors.white,
      borderSide: BorderSide(color: Colors.white),
    );
  }
}

My BaseView:

import 'package:app/common/locator.dart';
import 'package:app/core/viewmodels/base_model.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class BaseView<T extends BaseModel> extends StatefulWidget {
  final Widget Function(BuildContext context, T model, Widget child) builder;
  final Function(T) onModelReady;

  BaseView({this.builder, this.onModelReady});

  @override
  _BaseViewState<T> createState() => _BaseViewState<T>();
}

class _BaseViewState<T extends BaseModel> extends State<BaseView<T>> {
  T model = getIt<T>();

  @override
  void initState() {
    if (widget.onModelReady != null) {
      widget.onModelReady(model);
    }
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<T>(
        create: (context) => model,
        child: Consumer<T>(builder: widget.builder));
  }  
}


Solution 1: Nab

User Factory instead of RegisterLazySingleton.