Flutter Provider "notifyListener()" does not change the state
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;
dev1ce
Updated on January 01, 2023Comments
-
dev1ce over 1 year
I have a method
login
in my Provider class which is called ininitState()
method of myHomeScreen()
, inlogin 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 Ihot restart
orreopens
the app, it keeps on loading because obviously the ternary operation doesn't satisfy but If I print the data inside thelogin
function, it does get changed, so as a result, thenotifyListeners()
is not working properly and hence the state is not getting changed. (Right now I have tohot 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
anduser.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 inlogin
function?What could possible be wrong here, I'm really confused...
Any help would be really appreciated!
-
dev1ce over 2 yearsHi, thank you for your response, Can you explain your answer a bit? and what was wrong with my code?
-
Hasib Akon over 2 yearsBasically, notifyListeners() work like setState({}) means it will call the build function again that's why it is calling again and again. Now we case use consumer to wrap the section will be updated then the variable will update .
-
dev1ce over 2 yearsIt worked! If we do not provide
listen
argument, it is not set totrue
by default? and I have read somewhere that if we do not providerlisten
argument,notifyListeners()
will keep on updating the widget... -
Huthaifa Muayyad over 2 yearsIt will not work as desired without setting listen:true. You basically need to tell it, because logically, you already know what you want your function to do. Something to keep in mind, is if you want to use your provider inside an
onPressed
. There, you only have the option to set listen:false, otherwise it will throw an error. But this is not the case in your post. Happy coding! :)