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.