【问题标题】:Flutter Provider "notifyListener()" does not change the stateFlutter Provider "notifyListeners()" 不会改变状态
【发布时间】:2021-11-07 23:13:37
【问题描述】:

我的 Provider 类中有一个方法 login,在 HomeScreen()initState() 方法中调用,在 login function 中我基本上注册/检查用户信息是否存储在服务器中,然后我向服务器发布请求以获取该用户的信息。一切正常但是当我hot restartreopens 应用程序时,它会继续加载,因为显然三元运算不满足但如果我在login 函数中打印数据,它确实发生了变化,因此,notifyListeners() 无法正常工作,因此状态没有改变。 (现在我必须hot reloadctrl+s,然后状态发生变化,一切都加载成功)。

主屏幕():

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

coursesuser.data.categoryList 的长度相同)

Provider文件中的登录功能:

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();
  }

控制台输出:

应用首次加载或热重启时的输出:

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

应用热重载时的输出 (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

如您所见,热重载后,状态会发生变化,但不是没有,这意味着notifyListeners()login 函数中的行为不正常?

这里可能有什么问题,我真的很困惑......

任何帮助将不胜感激!

【问题讨论】:

    标签: flutter dart flutter-provider


    【解决方案1】:

    当您期待更新时,您需要设置收听true

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

    【讨论】:

    • 成功了!如果我们不提供listen 参数,默认不设置为true?我在某处读到,如果我们不提供 listen 参数,notifyListeners() 将继续更新小部件......
    • 如果不设置listen:true,它将无法正常工作。你基本上需要告诉它,因为从逻辑上讲,你已经知道你想让你的函数做什么。需要记住的是,如果您想在 onPressed 中使用您的提供程序。在那里,你只能选择设置listen:false,否则会抛出错误。但在你的帖子中情况并非如此。快乐编码! :)
    【解决方案2】:
       Class LogInProvider{
        
            bool _isLogin = false; 
       
            setIsLogin(bool isLogin){
               _isLogin = isLogin;
    
              notifyListeners();
    
             }
             isLogin(){
               return _isLogin;
              }
    
    
         }
    

    然后在ui类中。

    获取登录信息:

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

    设置登录信息:

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

    【讨论】:

    • 嗨,谢谢你的回复,你能解释一下你的答案吗?我的代码出了什么问题?
    • 基本上,notifyListeners() 像 setState({}) 一样工作意味着它将再次调用构建函数,这就是它一次又一次调用的原因。现在我们使用消费者来包装部分将被更新然后变量将被更新。
    猜你喜欢
    • 2020-12-26
    • 2020-11-19
    • 1970-01-01
    • 2021-03-09
    • 2019-11-15
    • 2019-01-30
    • 2019-05-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多