动态更改 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
.
的 RxString
、RxInt
等...
您的 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());
}
当动态更新 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
.
RxString
、RxInt
等...
您的 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());
}