I have a navigation app screen, and if you tap one of the buttons in the bottom navigation, it checks if the user is logged in. And if not, it pushes the login screen. The problem is that if you tap on the textfield, the LoginScreen UI is rebuilding. I don't know why. I have tried many options, but they have had no effect. Please help me!
NavigationalAppScreen:
class NavigationalAppScreen extends StatefulWidget {
const NavigationalAppScreen({Key? key}) : super(key: key);
static String routeName = "/navigational-app";
@override
_NavigationalAppScreenState createState() => _NavigationalAppScreenState();
}
class _NavigationalAppScreenState extends State<NavigationalAppScreen> {
final _screens = [
HomePageScreen(),
SearchPageScreen(),
EstateCreationPageScreen(),
ChatPageScreen(),
UserPageScreen()
];
final List<String> appBarTitles = [
"homescreen_appbar_text",
"searchpagescreen_appbar_text",
"estatecreationscreen_appbar_text",
"chatpagescreen_appbar_text",
"userpagescreen_appbar_text",
];
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Consumer<NavigationScreenProvider>(
builder: (context, navigator, _) {
return LocaleText(appBarTitles[navigator.currentIndex]);
}),
actions: [
IconButton(
onPressed: () {},
icon: Icon(
Icons.favorite_border_outlined,
size: 30,
color: Colors.redAccent,
),
)
],
),
body: Consumer<NavigationScreenProvider>(
builder: (context, navigator, _) {
return IndexedStack(
index: navigator.currentIndex,
children: _screens,
);
},
),
bottomNavigationBar: _buildBottomNavigation(),
),
);
}
Widget _buildBottomNavigation() {
return BottomNavigationBar(
showSelectedLabels: false,
showUnselectedLabels: false,
type: BottomNavigationBarType.fixed,
currentIndex: Provider.of<NavigationScreenProvider>(context).currentIndex,
onTap: (index) {
if (index != 1) {
Provider.of<NavigationScreenProvider>(context, listen: false)
.clearData();
}
Provider.of<NavigationScreenProvider>(context, listen: false)
.changePageIndex(index, () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => LoginScreen()),
);
});
},
items: [
BottomNavigationBarItem(
icon: Icon(
Provider.of<NavigationScreenProvider>(context).currentIndex == 0
? Icons.home_rounded
: Icons.home_outlined,
color: darkPurple,
),
label: "Home",
),
BottomNavigationBarItem(
icon: Icon(
Provider.of<NavigationScreenProvider>(context).currentIndex == 1
? Icons.search_outlined
: Icons.search,
color: darkPurple,
),
label: "Search",
),
BottomNavigationBarItem(
icon: Icon(
Provider.of<NavigationScreenProvider>(context).currentIndex == 2
? Icons.add_circle_rounded
: Icons.add_circle_outline_rounded,
color: darkPurple,
),
label: "Add",
),
BottomNavigationBarItem(
icon: Icon(
Provider.of<NavigationScreenProvider>(context).currentIndex == 3
? Icons.chat_rounded
: Icons.chat_outlined,
color: darkPurple,
),
label: "Chat",
),
BottomNavigationBarItem(
icon: Icon(
Provider.of<NavigationScreenProvider>(context).currentIndex == 4
? Icons.person_rounded
: Icons.person_outline_rounded,
color: darkPurple,
),
label: "User",
),
],
);
}
}
NavigationScreenProvider:
class NavigationScreenProvider extends ChangeNotifier {
final AuthProvider auth;
NavigationScreenProvider({required this.auth});
int _currentIndex = 0;
List<int> _authRequiredScreens = [2, 3, 4];
Map<String, dynamic> _data = {};
int get currentIndex {
final currentIndex = _currentIndex;
return currentIndex;
}
Map<String, dynamic> get data {
return {..._data};
}
changePageIndex(int index, [Function? callback]) {
if (!_authRequiredScreens.contains(index) || auth.isAuthenticated) {
_currentIndex = index;
notifyListeners();
} else {
callback != null ? callback() : null;
}
if (index == 0) clearData();
}
visitSearchPage(String term) {
_data = {
"search_term": term,
};
changePageIndex(1);
}
clearData() {
_data = {};
}
}
LoginScreen:
class LoginScreen extends StatefulWidget {
const LoginScreen({Key? key}) : super(key: key);
static String routeName = "/login";
@override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
bool _hidePassword = true;
FocusNode _phoneFocusNode = FocusNode();
FocusNode _passwordFocusNode = FocusNode();
final _phoneController = TextEditingController();
final _passwordController = TextEditingController();
@override
void didChangeDependencies() {
// _phoneFocusNode.addListener(() {
// setState(() {});
// });
// _passwordFocusNode.addListener(() {
// setState(() {});
// });
super.didChangeDependencies();
}
@override
void dispose() {
_phoneFocusNode.dispose();
_passwordFocusNode.dispose();
_phoneController.dispose();
_passwordController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Padding(
padding: const EdgeInsets.all(defaultPadding),
child: Form(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
LocaleText(
"log_in",
style: TextStyle(
fontSize: 25,
color: normalOrange,
fontWeight: FontWeight.w700,
),
),
SizedBox(
height: 60,
),
TextFormField(
focusNode: _phoneFocusNode,
controller: _phoneController,
inputFormatters: [
MaskTextInputFormatter(mask: "+998 ## ### ## ##")
],
decoration: InputDecoration(
border: InputStyles.inputBorder(),
focusedBorder: InputStyles.focusBorder(),
prefixIcon: Icon(
Icons.phone_outlined,
),
hintText: Locales.string(context, "phone_number_hint"),
),
keyboardType: TextInputType.number,
onFieldSubmitted: (value) {
FocusScope.of(context).requestFocus(_passwordFocusNode);
},
),
SizedBox(
height: 20,
),
TextField(
focusNode: _passwordFocusNode,
controller: _passwordController,
decoration: InputDecoration(
border: InputStyles.inputBorder(),
focusedBorder: InputStyles.focusBorder(),
prefixIcon: Icon(
Icons.lock,
),
hintText: Locales.string(context, "password_hint"),
suffixIcon: IconButton(
icon: Icon(
Icons.remove_red_eye,
),
onPressed: () {
setState(() {
_hidePassword = !_hidePassword;
});
},
),
),
obscureText: _hidePassword,
),
SizedBox(
height: 20,
),
TextLinkButton(
Locales.string(context, "forgot_password?"), () {}),
SizedBox(
height: 32,
),
FluidBigButton(Locales.string(context, "log_in"), onPress: () {
String phone = _phoneController.text.replaceAll(" ", "");
String password = _passwordController.text;
Provider.of<AuthProvider>(context, listen: false)
.login(phone, password)
.then((value) {
if (value.containsKey("status") && !value["status"]) {
print("You cannot log in!");
}
Navigator.of(context).pushNamedAndRemoveUntil(
NavigationalAppScreen.routeName, (route) => false);
});
}),
SizedBox(
height: 24,
),
Wrap(
children: [
LocaleText(
"no_profile?",
style: TextStyle(fontSize: 16),
),
SizedBox(width: 10),
TextLinkButton(Locales.string(context, "register"), () {
Navigator.of(context)
.pushReplacementNamed(RegisterScreen.routeName);
}),
],
)
],
),
),
),
),
);
}
}
In your NavigationalAppScreen you have the following code as part of the bottom navigation:
onTap: (index) {
if (index != 1) {
Provider.of<NavigationScreenProvider>(context, listen: false)
.clearData();
}
Provider.of<NavigationScreenProvider>(context, listen: false)
.changePageIndex(index, () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => LoginScreen()),
);
});
},
This will push the LoginScreen
every time the onTap
function is called, so the behavior you see is correct. Not knowing your code, perhaps you can do your "am I logged in?" test in onTap
and only if the result is false
you push the LoginScreen