Related
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,
),)
)
),
],
),
)
),
),
);
}
}
You should try like this
pushNamedAndRemoveUntil('/login', (Route route) => false);
I am working on a flutter app and I wanted to change the color property(for each widget) to red when the user clicks on red and to green when the user clicks on the green. So any help?
this is the code of the widgets of the first page
cardItem(BuildContext context, int index){
return Padding(
padding: const EdgeInsets.all(5.0),
child: GestureDetector(
onTap: ()
{
SecondPage.name = "Question ${index +1}";
SecondPage.index = index;
Navigator.push(context, MaterialPageRoute(builder:
(context) => SecondPage()));
},
child: Card(
color: Colors.white,
child: ListTile(
title: Text('Question ${index +1 }'),
subtitle: Text('Yes or No'),
)
)
)
);
}
}
this is the code of the yes or no page
class _SecondPageState extends State<SecondPage> {
#override
Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(
title: Text(SecondPage.name),
),
body: Center(
child:
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(right: 3),
child: FlatButton(
padding: EdgeInsets.all(15),
color: Colors.red,
child: Text('No', style: TextStyle(color: Colors.black),),
onPressed: () {
},
),
),
Padding(
padding: EdgeInsets.only(left: 3),
child: FlatButton(
padding: EdgeInsets.all(15),
color: Colors.green,
child: Text('Yes', style: TextStyle(color: Colors.black),),
onPressed: () {
},
),
),
],
),
)
);
}
Define a color variable Color color; inside your state and then give this color to all of your widget like button or whatever you have, And when user clicks on red inside the onPressed of your red button.
color =Colors.red;
setState(() { });
similarly, you can do this inside your green color button.
1). First declare a global variable like this,
var btnColor = Colors.green;
2). Now use this variable inside your widget like this,
color: btnColor,
3). Now use setState on button pressed to change the value of the Color
onPressed: () {
setState(() {
btnColor = Colors.red;
});
},
The value of color will change. This is called Stage-management you can
learn more about that
My Scrollview not Responding, can someone tell what I am missing in code:
Please Suggest me how to add scroll view listener I'm beginner.
import 'package:flutter/material.dart';
import 'package:share/share.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:url_launcher/url_launcher.dart';
import '../main.dart';
import 'favourite_articles.dart';
import 'coin_system.dart';
class Settings extends StatefulWidget {
#override
_SettingsState createState() => _SettingsState();
}
class _SettingsState extends State<Settings> {
bool _notification = false;
ScrollController _controller;
#override
void initState() {
super.initState();
checkNotificationSetting();
}
checkNotificationSetting() async {
final prefs = await SharedPreferences.getInstance();
final key = 'notification';
final value = prefs.getInt(key) ?? 0;
if (value == 0) {
setState(() {
_notification = false;
});
} else {
setState(() {
_notification = true;
});
}
}
saveNotificationSetting(bool val) async {
final prefs = await SharedPreferences.getInstance();
final key = 'notification';
final value = val ? 1 : 0;
prefs.setInt(key, value);
if (value == 1) {
setState(() {
_notification = true;
});
} else {
setState(() {
_notification = false;
});
}
Future.delayed(const Duration(milliseconds: 500), () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (BuildContext context) => MyHomePage()));
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
centerTitle: true,
title: Text(
'More',
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 20,
fontFamily: 'Poppins'),
),
elevation: 5,
backgroundColor: Colors.white,
actions: <Widget>[
IconButton(
icon: Icon(Icons.mail),
color: Colors.black,
tooltip: 'Song Request',
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CoinSystem(),
),
);
})
],
),
body: Container(
decoration: BoxDecoration(color: Colors.white),
child: SingleChildScrollView(
controller: _controller,
scrollDirection: Axis.vertical,
child: Column(
children: <Widget>[
Container(
alignment: Alignment.center,
padding: EdgeInsets.fromLTRB(0, 20, 0, 10),
child: Image(
image: AssetImage('assets/icon.png'),
height: 50,
),
),
Container(
alignment: Alignment.center,
padding: EdgeInsets.fromLTRB(0, 10, 0, 20),
child: Text(
"Version 2.1.0 \n ",
textAlign: TextAlign.center,
style: TextStyle(height: 1.6, color: Colors.black87),
),
),
Divider(
height: 10,
thickness: 2,
),
//ListWheelScrollView(
ListView(
//itemExtent: 75,
shrinkWrap: true,
children: <Widget>[
InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FavouriteArticles(),
),
);
},
child: ListTile(
leading: Image.asset(
"assets/more/favourite.png",
width: 30,
),
title: Text('Favourite'),
subtitle: Text("See the saved songs"),
),
),
//Song Request Code
InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CoinSystem(),
),
);
},
child: ListTile(
leading: Image.asset(
"assets/more/songrequest.png",
width: 30,
),
title: Text('Songs Request'),
subtitle: Text("Request your favourite songs"),
),
),
//Song Request Code End
ListTile(
leading: Image.asset(
"assets/more/notification.png",
width: 30,
),
isThreeLine: true,
title: Text('Notification'),
subtitle: Text("Change notification preference"),
trailing: Switch(
onChanged: (val) async {
await saveNotificationSetting(val);
},
value: _notification),
),
],
)
],
),
),
),
//),
);
//);
}
}
So, I have tried SingleChildScrollView, in that I have Container and Listview. But Listview doesn't response on scrolling action in landscape mode.
I have added ScrollController _controller; Do i have to create _controller class that listern the scrolling action?
In my understanding, you want to be able to get 2 scrolling. 1. using SingleChildScrollView and inside that Widget, you want to be able to scroll the bottom layer, thus you use ListView. To make it work, you have to place your ListView inside widget that has certain height. Example this implementation is:
SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: <Widget>[
SizedBox(child: Text('Upper scrollable'), height: 450),
Divider(
height: 10,
thickness: 2,
),
Container(
height: 350,
child: ListView(
shrinkWrap: true,
children: <Widget>[
Container(
color:Colors.red,
child: SizedBox(child: Text('Bottom scrollable'), height: 450),
),
],
),
)
],
),
),
If you don't want to use 2 scroll, don't use ListView inside SingleChildScrollView.
ListView cannot be wrapped with SingleChildScrollView remove it
surround ListView with Expanded Widget
try one of the two alternatives.
void showSimpleCustomDialog(BuildContext context, String listName) async{
if(randList.isEmpty){
randList = await theDb.queryWhere("$listName");
}else{
randList.clear();
randList = await theDb.queryWhere("$listName");
}
setState((){
});
String randValue = "Click generate new to get a random value";
Dialog simpleDialog = Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
),
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
child: Text(
"$randValue"
),
),
Padding(
child: Row(
children: <Widget>[
RaisedButton(
color: Colors.blue,
onPressed: ()
String lol = randList[0 + rng.nextInt(randList.length - 0)].getItem();
print(randValue);
setState(() {
randValue = lol;
});
},
child: Text(
'generate new',
style: TextStyle(fontSize: 18.0, color: Colors.white),
),
)
],
),
),
],
),
),
);
showDialog(
context: context, builder: (BuildContext context) => simpleDialog);}
When I am clicking on "generate new" button I want to update my randomValue and to display that randomValue as Text Widget. To do that dynamically Im using setState, but it is not working, and i dont understand why. Please help me.
You should use StatefulBuilder with dialogue.
showDialog(
context: context,
builder: (context) {
String contentText = "Content of Dialog";
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
title: Text("Title of Dialog"),
content: Text(contentText),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.pop(context),
child: Text("Cancel"),
),
FlatButton(
onPressed: () {
setState(() {
contentText = "Changed Content of Dialog";
});
},
child: Text("Change"),
),
],
);
},
);
},
How did i setup popup AlertDialog when int is equal or greater than 100?
showDialog(
context: _context,
builder: (BuildContext context) => AlertDialog(
title: Text("$_winner Won"),
)
);
void scoreTeamA() {
setState(() {
outputTeamA += _choiceA;
});
}
// I would like to show outputTeamA on the AlertDialog
Thank you
Mohammad
One way is to create a Widget for the AlertDialog and pass the String/Widget you want to show on that dialog.
class LoadingDialog{
static Future<void> showLoadingDialog(BuildContext context,String text) async {
return showDialog(
context: context,
builder: (BuildContext context){
return SimpleDialog(
children: <Widget>[
Center(
child: Container(
child: Column(
children: <Widget>[
SizedBox(
height:10,
),
Text(text),
]
),
),
),
],
),
}
);
}
}
then you can call on an Event like:
void scoreTeamA(){
outputTeamA += _choiceA;
LoadingDialog.showLoadingDialog(context, outputTeamA);
}
Note: this code might have some errors. i haven't tested this.
This is how I display an AlertDialog
showAlertDialog() {
return showDialog(
context: context,
builder: (context) => AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
side: BorderSide(color: Colors.redAccent, width: 2.0),
),
elevation: 6,
titlePadding: EdgeInsets.all(8.0),
titleTextStyle: TextStyle(
fontSize: 18, color: Colors.red, fontWeight: FontWeight.bold),
title: Text(
'This is the alert title',
textAlign: TextAlign.center,
),
contentPadding: EdgeInsets.all(8.0),
contentTextStyle: TextStyle(fontSize: 14, color: Colors.black),
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Content...')
//content goes here
],
),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.of(context).pop(), child: Text('Okay'))
],
),
);
}
And then you would do your if check to check
if( outputTeamA >= 100){
showAlertDialog();
}
Of course you could use outputTeamA in AlertDialog