dev1ce
dev1ce

Reputation: 1737

Flutter Provider "notifyListener()" does not change the state

I have a method login in my Provider class which is called in initState() method of my HomeScreen(), in login function I basically register/check if a user information is stored in server or not, and then I post request to server to get that user's info. Everything is working fine but when I hot restart or reopens the app, it keeps on loading because obviously the ternary operation doesn't satisfy but If I print the data inside the login function, it does get changed, so as a result, the notifyListeners() is not working properly and hence the state is not getting changed. (Right now I have to hot reload ctrl+s, and then the state changes and everything loads successfully).

HomeScreen():

class HomeScreen extends StatefulWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  List<Course> courses = [];

  @override
  void initState() {
    super.initState();
    courses = Course().getAllCourses();
    Future.delayed(Duration.zero, () async {
      Provider.of<LoginProvider>(context, listen: false).login();
    });
  }

  @override
  Widget build(BuildContext context) {
    bool isLoading =
        Provider.of<LoginProvider>(context, listen: false).isLoading;
    User user = Provider.of<LoginProvider>(context, listen: false).user;
    print("INITIAL: $isLoading");
    print("INITIAL: ${user.data!.categoryList!.length}");

    return Scaffold(
      backgroundColor: Colors.white,
      body: !isLoading && user.data != null
          ? ListView.separated(
              itemCount: courses.length,
              separatorBuilder: (context, index) {
                return Divider();
              },
              itemBuilder: (context, index) {
                final course = courses[index];
                final courseNew = user.data!.categoryList![index];
                print("INSIDE: $isLoading");
                print("INSIDE: ${user.data!.categoryList!.length}");
                if (courses.length == user.data!.categoryList!.length) {
                  return ... // full custom
                }
                return Center(
                  child: Text("No Data recieved from backend"),
                );
              },
            )
          : Center(
              child: CircularProgressIndicator(
              strokeWidth: 4,
            )),
    );
  }
}

(Both courses and user.data.categoryList have same length)

Login function in Provider file:

login() async {
    await _checkIsUserLoggedIn();
    if (_deviceDetails.deviceID == "") {
      print("[INFO] NEW USER DEVICE DETECTED");
      print("[INFO] LOGGING IN...");

      await _getDeviceDetails();
      // fetching platform details, if success, carry-on, else throw error

      try {
        if (_deviceDetails.deviceID != "") {
          // post request to server
          Map<String, dynamic> requestBody = {
            'device_id': _deviceDetails.deviceID
          };

          http.Response response = await http
              .post(Uri.parse(Constants.LOGIN_ENDPOINT), body: requestBody);

          _user = userFromJson(response.body);
          _deviceDetails.userID = _user.data!.userId!;
          _deviceDetails.userName = _user.data!.name!;
          _deviceDetails.token = _user.data!.token!;

          await _saveLoginDetails(_deviceDetails);
          print("[INFO] USER LOGGED IN SUCCESSFULLY");
        }
      } catch (error) {
        print("[INFO] ERROR WHILE FETCHING DEVICE DETAILS: $error");
      }
    } else {
      print("[INFO] THIS USER DEVICE IS ALREADY LOGGED IN");
      print("[INFO] FETCHING EXISTING USER DETAILS");

      Map<String, dynamic> requestBody = {'device_id': _deviceDetails.deviceID};

      http.Response response = await http
          .post(Uri.parse(Constants.LOGIN_ENDPOINT), body: requestBody);
      _user = userFromJson(response.body);
      print("INSIDE LOGIN FUNCTION: ${_user.data!.categoryList!.length}"); // prints 2 in console (correct)
    }
    _isLoading = false;
    notifyListeners();
  }

Console output:

Output when app first loads or Hot Restart:

I/flutter ( 3495): INITIAL: true
I/flutter ( 3495): INITIAL: 1
I/flutter ( 3495): [INFO] THIS USER DEVICE IS ALREADY LOGGED IN
I/flutter ( 3495): [INFO] FETCHING EXISTING USER DETAILS
I/flutter ( 3495): INSIDE LOGIN FUNCTION: 2

Ouput when app Hot Reloads (Ctrl+S):

I/flutter ( 3495): INITIAL: false
I/flutter ( 3495): INITIAL: 2
I/flutter ( 3495): INSIDE: false
I/flutter ( 3495): INSIDE: 2
I/flutter ( 3495): INSIDE: false
I/flutter ( 3495): INSIDE: 2

As you can see, after hot reloading, the state changes but not without it, so that means notifyListeners() is not behaving properly in login function?

What could possible be wrong here, I'm really confused...

Any help would be really appreciated!

Upvotes: 2

Views: 2586

Answers (2)

Huthaifa Muayyad
Huthaifa Muayyad

Reputation: 12373

You need to set listen to true when you are expecting updates:

 bool isLoading =
        Provider.of<LoginProvider>(context, listen: true).isLoading;
    User user = Provider.of<LoginProvider>(context, listen: true).user;

Upvotes: 2

Monu
Monu

Reputation: 714

   Class LogInProvider{
    
        bool _isLogin = false; 
   
        setIsLogin(bool isLogin){
           _isLogin = isLogin;

          notifyListeners();

         }
         isLogin(){
           return _isLogin;
          }


     }

Then in the ui class .

Get LoginInfo :

...
  child : Consumer<LoginInProvider>(
  builder: (context,_loginProvider, child) {
    return Text("${_loginProvider.isLogin}");
  },

SetLogin Info :

  Provider.of<LoginInfoProvider>(context).setIslogIn(true);

Upvotes: 0

Related Questions