Riverpod 不需要的小部件重建我认为

问题描述

我正在尝试使用 riverpod 来存储登录时存储在 Firestore 中的用户信息。 我要存储的两条信息是用户的手机号码和他订阅的帐户。 如果这些字段为空,则用户显示一个 QR 阅读器,该阅读器应读取此信息并将其存储在 Firestore 中,并在本地更新 riverpod

我的用户加载页面如下: 我收到错误 setState() or markNeedsBuild() called during build.。 这是为什么? 我认为这是因为代码正在更新构建方法,同时它仍在构建。 但我对文档的理解是 context.read() 不应该重建小部件。

final subscribedToProvider = ChangeNotifierProvider((_) => SubscribedTo());
final userMobileProvider = ChangeNotifierProvider((_) => UserMobileProvider());

class LandingRouting extends StatelessWidget {
  const LandingRouting({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    User user = context.watch<User>();

    if (user == null) return LoginScreen();

    return FutureBuilder(
      future: FirebaseFirestore.instance.collection('users').doc(user.uid).get(),builder: (context,AsyncSnapshot<DocumentSnapshot> snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) return Loading();

        if (snapshot.hasError)
          return AlertDialog(
            title: Text('Error loading user'),content: Text('${snapshot.error.toString}'),);
        else {
          Map<String,dynamic> userData = snapshot.data.data();

          if (userData['userType'] == 'baqala') {
            return Theme(data: baqalaTheme,child: const BaqalaHomeScreen());
          } else {
            String subscribedTo = userData['subscribedTo'];
            String mobile = userData['mobile'];

            context.read(userMobileProvider).update(mobile);  // <--
            context.read(subscribedToProvider).update(subscribedTo); // <--

            return Theme(
              data: userTheme,child: UserHomeScreen(subscribedTo: subscribedTo,userMobile: mobile),);
          }
        }
      },);
  }
}

在 QR 页面中,riverpod 值为 null,但 firestore 值不为 null。 因此,上一页中的值没有正确存储在 riverpod 中。 我认为这是因为加载页面中的异常。

我在二维码页面内部的构建方法如下:

final subscribedToProvider = ChangeNotifierProvider((ref) => SubscribedTo());
final userMobileProvider = ChangeNotifierProvider((ref) => UserMobileProvider());

....

  @override
  Widget build(BuildContext context) {

    return Consumer(
      builder: (context,watch,child) {
        String sub = watch(subscribedToProvider).subscribedTo;
        String mob = watch(userMobileProvider).mobile;
        if (sub == null || mob == null) {
          return Column(
            children: [
              Expanded(
                flex: 3,child: QRView(key: qrKey,onQRViewCreated: _onQRViewCreated),),Expanded(
                flex: 1,child: Center(
                  child: (result != null)
                      ? Text('Barcode Type: ${describeEnum(result.format)}   Data: ${result.code}')
                      : Text(
                          // 'Scan your code at your baqala to get your hisab history','Go to your baqala and request your QR code.\n'
                          'If your baqala has added you as a subscriber,you will be able to see\n'
                          'your account history\n\n',style: TextStyle(fontSize: 18),textAlign: TextAlign.center,],);
        }

        return History2(baqalaUID: sub,mobile: mob);
      },);

  }

  void _onQRViewCreated(QRViewController controller) {
    this.controller = controller;
    controller.scannedDataStream.listen(
      (scanData) async {
        setState(() => result = scanData);
        List<String> qrcode = scanData.code.split('-');

        context.read(subscribedToProvider).update(qrcode[0]);  // <-- Am I updating correctly
        context.read(userMobileProvider).update(qrcode[1]);

        await FirebaseFirestore.instance.collection('users').doc(CurrentUser.getCurrentUser().uid).update(
          {
            'subscribedTo': qrcode[0],'mobile': qrcode[1],},);
      },);
  }

我的提供商:

class SubscribedTo extends ChangeNotifier {
  String _subscribedTo;
  String get subscribedTo => _subscribedTo;

  void update(String subscribedTo) {
    _subscribedTo = subscribedTo;
    notifyListeners();
  }
}

class UserMobileProvider extends ChangeNotifier {
  String _mobile;
  String get mobile => _mobile;

  void update(String mobile) {
    _mobile = mobile;
    notifyListeners();
  }
}

解决方法

您可以在 build 方法中读取状态但不能更改它,因为根 ProviderScope 正在侦听它。这会导致您的错误。

要纠正它,您应该创建一个 firebase 实例提供程序,并在您的用户/firebase 实例完成加载时使用提供程序之间的依赖项来更新状态。