Search code examples
flutterdartnavigationprovider

Flutter: Pushed LoginScreen Widget is rebuilding the UI when clicked the TextField


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);
                    }),
                  ],
                )
              ],
            ),
          ),
        ),
      ),
    );
  }
}

Solution

  • 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