Flutter:单击 TextField 时,推送的 LoginScreen Widget 正在重建 UI
Flutter: Pushed LoginScreen Widget is rebuilding the UI when clicked the TextField
我有一个导航应用程序屏幕,如果您点击底部导航中的其中一个按钮,它会检查用户是否已登录。如果没有,它会推送登录屏幕。问题是,如果您点击文本字段,则 LoginScreen UI 正在重建。我不知道为什么。我尝试了很多选择,但都没有效果。请帮助我!
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 = {};
}
}
登录屏幕:
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);
}),
],
)
],
),
),
),
),
);
}
}
在您的 NavigationalAppScreen 中,您将以下代码作为底部导航的一部分:
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()),
);
});
},
这将在每次调用 onTap
函数时推送 LoginScreen
,因此您看到的行为是正确的。不知道你的代码,也许你可以做你的“我登录了吗?”在 onTap
中测试,只有当结果是 false
时,你才按下 LoginScreen
我有一个导航应用程序屏幕,如果您点击底部导航中的其中一个按钮,它会检查用户是否已登录。如果没有,它会推送登录屏幕。问题是,如果您点击文本字段,则 LoginScreen UI 正在重建。我不知道为什么。我尝试了很多选择,但都没有效果。请帮助我!
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 = {};
}
}
登录屏幕:
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);
}),
],
)
],
),
),
),
),
);
}
}
在您的 NavigationalAppScreen 中,您将以下代码作为底部导航的一部分:
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()),
);
});
},
这将在每次调用 onTap
函数时推送 LoginScreen
,因此您看到的行为是正确的。不知道你的代码,也许你可以做你的“我登录了吗?”在 onTap
中测试,只有当结果是 false
时,你才按下 LoginScreen