Currently I have the problem, that the screens are not being properly disposed by the TopNavigationBar.
How can I properly dispose them?
I tried to add navigatorKey.currentState.pop;
before I navigate to a specific screen, which get selected at the TopNavigationBar. So the current screen will get disposed and after that I navigate to the selected screen from the TopNavigationBar.
But unfortunatly I still get a Duplicate GlobalKey detected in widget tree.
error.
I think I am getting this error in the login screen, because the screens are not properly disposed (when I click 2 times on the TopNavigationBarItem "Login", then the error occurs). So I try to figure out, how I can properly dispose them.
What I am doing wrong? How can I properly dispose them?
I think, when I dispose the screens correctly, then my final GlobalKey<FormState> _formKeys
will work as expected and the error will disappear.
Edit:
"Properly disposing screens" solved with the comment of Anees, but the problem is still not fixed.
I have 2 options:
-using the answer from Duplicate GlobalKey detected in widget tree and change my GlobalKey to final GlobalKey<FormState> _formKeys = GlobalKey<FormState>();
But then my keyboard on the smartphone is not working anymore (instantly disappearing after clicking on a Textfield).
or
-trying to find another solution for this problem without getting another problem.
navigation_service.dart code:
import 'package:flutter/material.dart';
class NavigationService {
final GlobalKey<NavigatorState> navigatorKey =
GlobalKey<NavigatorState>();
Future<dynamic> navigateTo(String routeName) {
// dispose of the current screen, then navigate to the new screen
navigatorKey.currentState.pop;
return navigatorKey.currentState.pushNamed(routeName);
}
void goBack() {
return navigatorKey.currentState.pop();
}
}
top_nav_item.dart code: (onTap function is doing the navigation in the TopNavigationbar)
import 'package:bestfitnesstrackereu/widgets/top_navbar_item/top_navbar_item_desktop.dart';
import 'package:bestfitnesstrackereu/widgets/top_navbar_item/top_navbar_item_mobile.dart';
import 'package:bestfitnesstrackereu/widgets/top_navbar_item/top_navbar_item_tablet.dart';
import 'package:flutter/material.dart';
import 'package:responsive_builder/responsive_builder.dart';
import '../../datamodels/navbar_item_model.dart';
import '../../locator.dart';
import '../../services/navigation_service.dart';
class TopNavBarItem extends StatelessWidget {
final String title;
final String navigationPath;
final IconData icon;
const TopNavBarItem(this.title, this.navigationPath, {this.icon});
@override
Widget build(BuildContext context) {
// every NavBarItem need to have this model
var model = NavBarItemModel(
title: title,
navigationPath: navigationPath,
iconData: icon,
);
return GestureDetector(
onTap: () {
locator<NavigationService>().navigateTo(navigationPath);
},
child: ScreenTypeLayout(
desktop: TopNavBarItemTabletDesktop(
model: model,
),
tablet: TopNavBarItemTablet(
model: model,
),
mobile: TopNavBarItemMobile(
model: model,
),
),
);
}
}
top_navigation_bar_user.dart code: (it's the TopNavigationBar)
class TopNavigationBarUserDesktop extends StatelessWidget {
const TopNavigationBarUserDesktop({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
height: 100,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(width: 30,),
TopNavBarLogo(),
SizedBox(width: 30,),
Visibility(child: Text( "TheBestFitnessTracker", style: TextStyle(color: Colors.black, fontSize: 14, fontWeight: FontWeight.normal,))),
Spacer(), //Space between logo+text and widgets in the center of the row
TopNavBarItem('Informationen', InformationRoute, ),
SizedBox(width: 30,),
TopNavBarItem('Neuigkeiten', NeuigkeitenRoute),
SizedBox(width: 30,),
Spacer(), //Space between widgets in the center of the row and end of row
SizedBox(width: 30,),
TopNavBarItem('Login', AuthenticationPageRoute),
SizedBox(width: 30,),
TopNavBarItem('Teilehmer \n werden', RegristrationUserRoute),
SizedBox(width: 30,),
],
),
);
}
}
authentification.dart (login) code: (problems with the GlobalKey _formKeys, so form widget and TextFormFields)
import 'package:bestfitnesstrackereu/routing/route_names.dart';
import 'package:email_validator/email_validator.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../provider/auth.dart';
import '../../widgets/loading_circle/loading_circle.dart';
//AuthenticationPage (Login page)
class AuthenticationPage extends StatefulWidget {
@override
State<AuthenticationPage> createState() => _AuthenticationPageState();
}
class _AuthenticationPageState extends State<AuthenticationPage> {
static final GlobalKey<FormState> _formKeys = GlobalKey<FormState>();
var userData;
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
final authProvider = Provider.of<AuthProvider>(context);
return Scaffold(
body: SingleChildScrollView(
child: Center(
// checks the authentication status, when it is Authenticating, then return loading, else show the page
child: authProvider.status == Status.Authenticating ? Loading() : Container(
constraints: BoxConstraints(maxWidth: 440),
padding: EdgeInsets.all(24),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
children: [
Padding(
padding: EdgeInsets.only(right: 12),
child: Image.asset("assets/logo.png", width: 300,),
),
Expanded(child: Container()),
],
),
SizedBox(
height: 30,
),
Row(
children: [
Text("Login",
style: TextStyle(
fontSize: 30, fontWeight: FontWeight.bold
)),
],
),
SizedBox(height: 10,),
Row(
children: const [
Text(
"Wilkommen zurück zum Login",
style: TextStyle(
color: Colors.grey,))
],
),
SizedBox(height: 15,),
Form(
key: _formKeys,
//autovalidateMode: AutovalidateMode.always,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
//validator: (email) => EmailValidator.validate(email) ? null : "Bitte gib eine gültige E-Mail an.",
controller: authProvider.emailController,
decoration: InputDecoration(
labelText: "E-Mail",
hintText: "abc@domain.com",
suffixIcon: Icon(Icons.mail_outline,),
//isDense: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(20)
)
),
),
SizedBox(height: 15,),
TextFormField(
validator: (password) {
print(authProvider.validatePassword(password));
return authProvider.validatePassword(password);
},
controller: authProvider.passwordController,
obscureText: true,
decoration: InputDecoration(
labelText: "Passwort",
hintText: "******",
suffixIcon: Icon(Icons.lock_outline, color: Colors.grey,),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(20)
)
),
),
],
),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
GestureDetector(
onTap: () {
authProvider.clearController();
Navigator.of(context).pushNamed(ForgotPasswordRoute); // navigate to the forgot password page
},
child: Text(
'Passwort vergessen',
style: TextStyle(
color: Colors.blue[700],
fontWeight: FontWeight.bold,
),
),
)
],
),
),
SizedBox(height: 15,),
InkWell(
onTap: () async {
//check if email and password field is not empty
if(authProvider.emailController.text.trim().isEmpty || authProvider.passwordController.text.trim().isEmpty){
showDialog(context: context, builder: (BuildContext context){
return AlertDialog(
title: Text("Error: Bitte fülle das E-Mail- und Passwort-Feld aus."),
actions: [
TextButton(
child: Text("Ok"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
} else {
//checking if the email and password is valid
if(_formKeys.currentState.validate()){
print('validate email okok');
// input is the authProvider.emailController, which provides the written email from the user
// output are all the user informations in a Map<String, dynamic>
// used to check the status and role of the user
Map<String, dynamic> mapUserinformations = {};
mapUserinformations = await authProvider.getUserByEmail();
// checking if the admin/scientist exist
if (mapUserinformations != null){
//status from user = locked
if(mapUserinformations['status'] == 'gesperrt'){
showDialog(context: context, builder: (BuildContext context){
return AlertDialog(
title: Text("Error: Dein Account ist gesperrt"),
actions: [
TextButton(
child: Text("Ok"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
}
//status from user = deleted
if(mapUserinformations['status'] == 'gelöscht'){
showDialog(context: context, builder: (BuildContext context){
return AlertDialog(
title: Text("Error: Dein Account wurde gelöscht. Er existiert nicht mehr."),
actions: [
TextButton(
child: Text("Ok"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
}
//status from user = active
if(mapUserinformations['status'] == 'aktiv') {
//role from user = admin
if (mapUserinformations['role'] == 'Admin') {
print('admin - am einloggen');
if(!await authProvider.signIn()){ //signIn failed, then return "Login failed"
showDialog(context: context, builder: (BuildContext context){
return AlertDialog(
title: Text("Error: Login fehlgeschlagen. Falsche Kombination aus E-Mail und Passwort."),
actions: [
TextButton(
child: Text("Ok"),
onPressed: () {
authProvider.clearController();
Navigator.of(context).pop();
},
)
],
);
});
}
else {
authProvider.clearController();
Navigator.of(context).pushNamed(UsersAdministrationRoute);
}
}
//role from user = scientist
if (mapUserinformations['role'] == 'Wissenschaftler') {
print('scientist - am einloggen');
if(!await authProvider.signIn()){ //signIn failed, then return "Login failed"
showDialog(context: context, builder: (BuildContext context){
return AlertDialog(
title: Text("Error: Error: Login fehlgeschlagen. Falsche Kombination aus E-Mail und Passwort."),
actions: [
TextButton(
child: Text("Ok"),
onPressed: () {
authProvider.clearController();
Navigator.of(context).pop();
},
)
],
);
});
}
else { //if signIn is success, then clear controller and navigate to User Scientist page
authProvider.clearController();
Navigator.of(context).pushNamed(UsersAdministrationRoute);
}
}
//role from user = user
if (mapUserinformations['role'] == 'User') {
print('user - kein zugriff');
showDialog(context: context, builder: (BuildContext context){
return AlertDialog(
title: Text("Error: Du hast keine Zugriffsberichtigung auf diesen Login."),
actions: [
TextButton(
child: Text("Ok"),
onPressed: () {
authProvider.clearController();
Navigator.of(context).pop();
},
)
],
);
});
}
}
}else {
showDialog(context: context, builder: (BuildContext context){
return AlertDialog(
title: Text("Error: Ein Benutzer mit dieser E-Mail existiert nicht."),
actions: [
TextButton(
child: Text("Ok"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
}
}else{
print('validate email notgoodatall');
showDialog(context: context, builder: (BuildContext context){
return AlertDialog(
title: Text("Error: Bitte gebe eine gültige E-Mail an."),
actions: [
TextButton(
child: Text("Ok"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
}}},
child: Container(
decoration: BoxDecoration(color: Colors.deepPurple,
borderRadius: BorderRadius.circular(20)),
alignment: Alignment.center,
width: double.maxFinite,
padding: EdgeInsets.symmetric(vertical: 16),
child: Text(
"Login",
style: TextStyle(
color: Colors.white,
),)
)
),
SizedBox(height: 15,),
Row(
children: [
Expanded(
child: Divider(
height: 50,
color: Colors.grey[500],
)
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 20),
child: Text('Du bist noch nicht registriert?'),
),
Expanded(
child: Divider(
height: 50,
color: Colors.grey[500],
)
),
],
),
SizedBox(height: 15,),
InkWell(
onTap: (){
authProvider.clearController();
Navigator.of(context).pushNamed(RegristrationUserRoute); // navigation to the Registration page
},
child: Container(
decoration: BoxDecoration(color: Colors.deepPurple,
borderRadius: BorderRadius.circular(20)),
alignment: Alignment.center,
width: double.maxFinite,
padding: EdgeInsets.symmetric(vertical: 16),
child: Text(
"Teilnehmer werden",
style: TextStyle(
color: Colors.white,
),)
)
),
],
),
)
),
),
);
}
}