I have a screen that listens for changes in a document using a stream builder. If the document has data then it shows Widget1
, otherwise it will show Widget2
.
The problem:
When the stream builder builds for the first time it uses Widget2
since there is no data, but when it receives the data it does not update the widget to Widget2
.
Here is my simplified implementation:
class Test extends ConsumerStatefulWidget {
const Test({ Key? key }) : super(key: key);
@override
ConsumerState<Test> createState() => _TestState();
}
class _TestState extends ConsumerState<Test> {
@override
Widget build(BuildContext context) {
// final match = ref.watch(matchedEventProvider);
final user = ref.read(currentUserProvider);
return Scaffold(
body:StreamBuilder<Object>(
stream: db
.collection(kMatchedCollection)
.doc(user.currentMatch)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.data != null) {
kLogger.d('snapshot data: ${snapshot.data.toString()}');
final snap =
snapshot.hasData ? snapshot.data as DocumentSnapshot : null;
matchData = snap?.data() as Map<String, dynamic>;
kLogger.d("Got real match data: $matchData");
matchData['CreatedAt'] = DateTime.fromMillisecondsSinceEpoch(
matchData['CreatedAt'].seconds * 1000)
.toString();
final Event event = Event.fromJson(matchData);
final String status = event.EventStatus;
if (event.AssignedTo == ref.read(currentUserProvider).id) {
//only push new screens if current user is assigned.
if (status == Status.dateCancelled) {
Navigator.pop(context);
} else if (status == Status.dateConfirmed) {
Navigator.pushReplacementNamed(
context, FinalConfirmationScreen.id);
} else if (status == Status.deciding) {
Navigator.pushReplacementNamed(
context, FinalConfirmationScreen.id);
}
}
return Widget1();
} else {
kLogger.d("has data ${snapshot.hasData}");
return Widget2();
}
},
),
);
}
}
Here are the corresponding logs:
/flutter ( 988): ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
I/flutter ( 988): │ #0 _LocationWaitScreenState.build.<anonymous closure> (package:app/screens/location_wait_screen.dart:154:21)
I/flutter ( 988): │ #1 StreamBuilder.build (package:flutter/src/widgets/async.dart:437:81)
I/flutter ( 988): ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
I/flutter ( 988): │ 🐛 has data false
I/flutter ( 988): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
I/flutter ( 988): ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
I/flutter ( 988): │ #0 _LocationWaitScreenState.build.<anonymous closure> (package:app/screens/location_wait_screen.dart:44:21)
I/flutter ( 988): │ #1 StreamBuilder.build (package:flutter/src/widgets/async.dart:437:81)
I/flutter ( 988): ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
I/flutter ( 988): │ 🐛 snapshot data: Instance of '_JsonDocumentSnapshot'
I/flutter ( 988): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
I/flutter ( 988): ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
I/flutter ( 988): │ #0 _LocationWaitScreenState.build.<anonymous closure> (package:app/screens/location_wait_screen.dart:49:21)
I/flutter ( 988): │ #1 StreamBuilder.build (package:flutter/src/widgets/async.dart:437:81)
I/flutter ( 988): ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
I/flutter ( 988): │ 🐛 Got real match data: {StartedWith: bCO38DzmjrU9DsT4FTvU7uqR4r42, AssignedTo: bCO38DzmjrU9DsT4FTvU7uqR4r42, DocID: a1776e3d180543b084, Resets: 3, CreatedAt: Timestamp(seconds=1703706807, nanoseconds=365138000), EventStatus: Start, Time: , Participants: [RaLpzb2BZBUnn8AYYJ5UiApmmtx2, bCO38DzmjrU9DsT4FTvU7uqR4r42], Location: }
I/flutter ( 988): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
From the logs we can observe that it first builds Widget2
as it says 🐛 has data false
and then when the snapshot receives data it tries to build Widget1
but these build changes are not reflected in the UI.
Any help on this would be appreciated
2
Answers
To all the new people stumbling on this question, the problem is with an animation library that I was using. Don't try to modify the Text Widget child inside the animation widget and instead only use one animation widget while conditionally rendering.
So the code will go from this:
to :
Removing the
Animation
widget while rendering conditionally fixed this issue for me.Note: Here
Animation
widget could be any generic animation library widget from pub.dev .Instead of checking if snapshot.data is null. Consider using the function hasData i.e
Here is a simple example to simulate a stream please try it out and update your code accordingly if it works. If it works it means something is wrong with your ConsumerStateful widget and ConsumerState