动态更改 docId 时流不更新

Stream not updating when docId is changed dynamically

当动态更新 mainDocId 的值时,streamDemo() 不会更新 doc('$mainDocId') 的值。我想在动态更改文档 ID 时更新小部件 HomeBody(),以便我可以根据用户选择的文档检索数据。

我正在使用 getx 作为 SM。我尝试使用 update() 方法更新值但没有用。

代码如下

控制器:

class Controller extends GetxController {

  // onInit
  @override
  void onInit() {
    finalNewsModel.bindStream(streamDemo());
    super.onInit();
  }

 
  // list of document ids.
  List docIdList = [
    'USA',
    'New York',
    'Canada',
  ];

  //
  RxString mainDocId = 'USA'.obs;

  // method to change document id based on docId index.
  changeDocId(int index) {
    mainDocId(docIdList[index]);
  }

  //
  Rxn<List<NewsModel>> finalNewsModel = Rxn<List<NewsModel>>();

  //
  List<NewsModel> get newsModelList => finalNewsModel.value;

  //
  Stream<List<NewsModel>> streamDemo() {
    return FirebaseFirestore.instance
        .collection('news')
        .doc('$mainDocId')
        .snapshots()
        .map((ds) {
      var mapData = ds.data();
      List mapList = mapData['list'];
      List<NewsModel> modelList = [];
      mapList.forEach((element) {
        modelList.add(NewsModel.fromMap(element));
      });
      return modelList;
    });
  }
}

// UI

class HomeBody extends StatefulWidget {
  @override
  _HomeBodyState createState() => _HomeBodyState();
}

class _HomeBodyState extends State<HomeBody> {
//
  final Controller _controller = Get.put<Controller>(Controller());

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Obx(() {
        if (_controller.newsModelList == null) {
          return Center(
              child: Text(
            'Please try later!',
          ));
        } else if (_controller.newsModelList.isEmpty) {
          return Text('Empty List');
        } else {
          return ListView.builder(
            itemCount: _controller.newsModelList.length,
            itemBuilder: (context, index) {
              final NewsModel _newsModel = _controller.newsModelList[index];
              return MyContainer(
                title: _newsModel.title,
                titleImage: _newsModel.titleImage,
                index: index,
              );
            },
          );
        }
      }),
    );
  }
}

底部导航栏:

bottomNavigationBar: Container(
        color: Colors.grey[300],
        height: 60.0,
        child: Padding(
          padding: EdgeInsets.all(8.0),
          child: GetBuilder<Controller>(
            builder: (context) => ListView.builder(
              shrinkWrap: true,
              scrollDirection: Axis.horizontal,
              itemCount: _controller.docIdList.length,
              itemBuilder: (context, index) {
                return FavCategoryTags(
                  tagName: _controller.docIdList[index],
                  onpress: () =>_controller.changeDocId(index),
                );
              },
            ),
          ),
        ),
      ),

型号:

class NewsModel {
  String title, titleImage, brief, source;
  List aList;

  NewsModel({this.title, this.titleImage, this.brief, this.aList, this.source});

  factory NewsModel.fromMap(dynamic fieldData) {
    return NewsModel(
      title: fieldData['title'],
      titleImage: fieldData['titleImage'],
      brief: fieldData['brief'],
      aList: fieldData['mediaDescList'],
      source: fieldData['source'],
    );
  }
}

试试这个:

changeDocId(int index) {
   mainDocId.value = docIdList[index];
 }

而不是

changeDocId(int index) {
   mainDocId.value(docIdList[index]);
 }

我相信通过调用方法 (rxVar()) 更新 Rx 不会触发小部件重建,但使用赋值更新会 (rxVar.value=)

希望这会修复或至少让您更接近修复。

此处不需要StreamBuilder。将常规 Stream 绑定到 RxType 后,您就可以开始了。它会在流数据更改时更新。您只需要更新绑定调用,如下所示。

一个问题是您应该只初始化为常规 RxList

而不是这个

  Rxn<List<NewsModel>> finalNewsModel = Rxn<List<NewsModel>>();

像这样初始化它。

  RxList<NewsModel> finalNewsModel = <NewsModel>[].obs;

然后你可以失去这个getter

  List<NewsModel> get newsModelList => finalNewsModel.value;

因为不需要 .value,并且不会在正确初始化的 RxList 上工作。您可以将 RxList 列表视为常规列表,而不是需要 .value.

RxStringRxInt 等...

您的 Obx 现在可以在 finalNewsModel 上构建并且您可能会丢失 null 检查,因为 finalNewsModel 被初始化为一个空列表,并且永远不会为 null。

Obx(() {
            if (_controller.finalNewsModel.isEmpty) {
              return Text('Empty List');
            } else {
              return Expanded(
                child: ListView.builder(
                  itemCount: _controller.finalNewsModel.length,
                  itemBuilder: (context, index) {
                    final NewsModel _newsModel =
                        _controller.finalNewsModel[index];
                    return MyContainer(
                      title: _newsModel.title,
                      titleImage: _newsModel.titleImage,
                      index: index,
                    );
                  },
                ),
              );
            }
          }),

至于你想用你的BottomNavBar做什么:

在这里,您试图通过更改 Document Id 来更改 Stream 本身的参数。当您绑定到 onInit 中的 Stream 时,它绑定到当时设置的任何 Document Id 。因此它只会更新 Document 内的更改,除非您将其绑定到新的 Stream。因此,在您的情况下,只需在 changeDocId() 方法中再次调用 finalNewsModel.bindStream(streamDemo()); 即可更新 Stream 参数。

  void changeDocId(int index) {
    mainDocId(docIdList[index]);
    finalNewsModel.bindStream(streamDemo()); // only needed because you're updating the Document Id
  }

您的 BottomNavBar 中也不需要 GetBuilder,除非您需要在视觉上更改 BottomNavBar 本身。您所做的只是根据硬编码 List 的值更新 RxString 的值。假设您确实需要 BottomNavBar 中的某些内容来重建,那将是您需要调用 update().

的唯一场景

我没有完整的 Firebase 集合结构,但我使用简化的 NewsModel 对其进行了测试,并在 Firebase 控制台中更新 String 会立即更新 Obx 小部件。并立即调用 changeDocId() returns 更新后的值 Document Id.

编辑:此外,对于您正在做的事情,mainDocId 不需要是可观察的字符串。流总是比原始数据类型更昂贵,所以除非你能证明它的合理性,否则最好只是使它成为如下所示的常规字符串。它的工作原理完全一样。

 String mainDocId = 'USA';

  // method to change document id based on docId index.
  void changeDocId(int index) {
    mainDocId = docIdList[index];
    finalNewsModel.bindStream(streamDemo());
  }