I'm considerably new to Flutter and I'm to build a Messenger Chap App on Flutter, and I face the issue of "LateInitilization: Field 'searchSnapShot' has not been initialized. Following is the snippet of code that is causing the issue:
Widget searchList() {
return searchSnapShot != null ? ListView.builder(
itemCount: searchSnapShot.docs.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return SearchTile(
userName: searchSnapShot.docs[index].data()["name"],
userEmail: searchSnapShot.docs[index].data()["email"],
);
}
) : Container();
}
What this snippet is supposed to do is return a list of users that match the search query. Following is the code for the entire search.dart:
import 'package:chat_app/services/database.dart';
import 'package:chat_app/widgets/widgets.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class SearchScreen extends StatefulWidget {
#override
_SearchScreenState createState() => _SearchScreenState();
}
class _SearchScreenState extends State<SearchScreen> {
DatabaseMethods databaseMethods = new DatabaseMethods();
TextEditingController searchTextEditingController = new TextEditingController();
late QuerySnapshot <Map<String, dynamic>> searchSnapShot;
initiateSearch() async {
await databaseMethods
.getUserByUsername(searchTextEditingController.text)
.then((val) {
setState(() {
searchSnapShot = val;
});
});
}
Widget searchList() {
return searchSnapShot != null ? ListView.builder(
itemCount: searchSnapShot.docs.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return SearchTile(
userName: searchSnapShot.docs[index].data()["name"],
userEmail: searchSnapShot.docs[index].data()["email"],
);
}
) : Container();
}
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: appBarMain(context),
body: Container(
child: Column(
children: [
Container(
color: Color(0xffFFC200),
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 16),
child: Row(
children: [
Expanded(
child: TextField(
controller: searchTextEditingController,
decoration: InputDecoration(
hintText: "search username..",
hintStyle: TextStyle(
color: Colors.black,
),
border: InputBorder.none,
),
)
),
GestureDetector(
onTap: () {
initiateSearch();
},
child: Container(
height: 30,
child: Image.asset("assets/images/search_white.png")),
),
],
),
),
searchList()
],
),
),
);
}
}
class SearchTile extends StatelessWidget {
late final String userName;
late final String userEmail;
SearchTile({required this.userName, required this.userEmail});
#override
Widget build(BuildContext context) {
return Container(
child: Row(
children: [
Column(
children: [
Text(
userName,
style: simpleTextStyle()
),
Text(
userEmail,
style: simpleTextStyle(),
),
],
),
Spacer(),
Container(
decoration: BoxDecoration(
color: Colors.amber,
borderRadius: BorderRadius.circular(40)
),
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Text("Message"),
)
],
),
);
}
}
Error Description in Android Studio:
======== Exception caught by widgets library =======================================================
The following LateError was thrown building SearchScreen(dirty, state: _SearchScreenState#f41e2):
LateInitializationError: Field 'searchSnapShot' has not been initialized.
What am I doing wrong here? I'd really appreciate some help. Thank you.
When you use late it means that variable can be lazily initialised, So, before use it somewhere, you need to Initialise thus you cannot make null checks on that, If you want to make null checks on it then try ? which makes that variable a nullable.
So remove late and add ?
QuerySnapshot <Map<String, dynamic>>? searchSnapShot;
initialize searchSnapshot using:
QuerySnapshot<dynamic>? searchSnapshot;
also, add null checks in searchTitle:
SearchTitle(
userName: searchSnapshot!.docs[index].data()['name'],
userEmail: searchSnapshot!.docs[index].data()['email'],
);SearchTitle(
userName: searchSnapshot!.docs[index].data()['name'],
userEmail: searchSnapshot!.docs[index].data()['email'],
);
Try this please
QuerySnapshot? <Map<String, dynamic>> searchSnapShot;
If you're using a setter under class make sure that the parameter in your setter is different from the variable you're initializing
Coupon? coupon;
void setCoupon(Coupon myCoupon){
coupon = myCoupon;
}
Related
I am working on an attendance application where I assign wages to the workers. I want to store all the wages given to the workers into the database. But the problem is I want to access all the given values on button click. I have no idea how it can be done in flutter. I am a beginner.
I have given all the codes and the image of what output i want.
Image of Emulator
Here is my code...
ATTENDANCE SCREEN
...rest code...
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Upload Patti'),
content: SingleChildScrollView(
child: ListBody(
children: [
TextFormField(
controller: _mainWagesController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: "Enter Amount",
prefixIcon: Icon(Icons.wallet, color: Colors.blue),
),
),
],
),
),
actions: <Widget>[
ElevatedButton(
onPressed: () {
Navigator.pop(context);
newWages = _mainWagesController.text;
setState(() {});
},
child: const Text("Assign Wages"),
),
],
);
},
);
},
child: const Icon(Icons.check_circle),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(8.00),
child: Column(children: [
const SizedBox(
height: 20,
),
Center(
child: Text(
"Date : ${DateFormat.yMMMEd().format(DateTime.parse(widget.attendanceDate.toString()))}",
style: const TextStyle(fontSize: 20),
),
),
const SizedBox(
height: 20,
),
FutureBuilder(
future: SupervisorAttendanceServices.getAttendancesDetailsList(
widget.attendanceId),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
var data = snapshot.data['hamal'];
return ListView.builder(
itemCount: data.length,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return HamalAttendanceWidget(
workerId: data[index]['worker_id'],
name: data[index]['worker_name'],
wages: newWages,
masterAttendanceId: widget.attendanceId,
isPrensent: data[index]
['attendance_worker_presense']
.toString());
});
} else if (snapshot.hasError) {
return const Center(
child: Text("Something went wrong !"),
);
} else {
return const Center(child: LinearProgressIndicator());
}
},
),
]),
),
),
...rest code
widget
Widget build(BuildContext context) {
return Card(
child: Column(children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(
width: 10,
height: 50,
),
const Icon(FeatherIcons.user),
const SizedBox(
width: 20,
),
Text(
widget.name,
style: const TextStyle(fontSize: 18),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(
width: 150,
height: 60,
child: TextFormField(
// onChanged: _onChangeHandler,
initialValue: widget.wages.toString(),
decoration: const InputDecoration(
hintText: "Wages",
prefixIcon: Icon(
Icons.wallet,
color: Colors.blue,
)),
)),
],
)
]),
);
}
I suggest you use a StateManager for your application, for example GetX
is a good solution. Create a controller file like the below:
// define this enum outside of class to handle the state of the page for load data
enum AppState { initial, loading, loaded, error, empty, disabled }
Rx<AppState> pageState = AppState.initial.obs;
class AttendanceCntroller extends GetxController{
RxList<dynamic> dataList=RxList<dynamic>();
#override
void onInit() {
//you can write other codes in here to handle data
pageState(AppState.loading);
dataList.value=
SupervisorAttendanceServices.getAttendancesDetailsList(attendanceId);
pageState(AppState.loaded);
super.onInit();
}
}
and in your view(UI) page, handle it in this way:
class AttendanceView extends GetView<AttendanceCntroller>{
#override
Widget body(BuildContext context) {
// TODO: implement body
return Obx( ()=> controller.pageState.value==AppState.loading ? const
Center(child: LinearProgressIndicator()) : ListView.builder(
itemCount: controller.dataList.length,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
return HamalAttendanceWidget(
workerId: controller.dataList['worker_id'],
name: controller.dataList['worker_name'],
wages: newWages,
masterAttendanceId: widget.attendanceId,
isPrensent: controller.dataList[index]
['attendance_worker_presense']
.toString());
})
)
}
}
for more data read the GetX link and read clean architecture with the GetX sample repository of my GitHub it have advanced management of states with GetX with dependency injection handling.
If you want to have prefilled value in TextFormField, you can either use initialValue or controller parameter.
The value of controller parameter will help you to get/update the value of TextFormField.
For controller parameter refer below.
TextEditingController controller = TextEditingController(text: 'This is text will be pre-filled in TextFormField');
...
TextFormField(
controller: controller,
);
Create List or Map of those controllers.
List<TextEditingController> listOfControllers = [ controller1, controlle2,...];
Use for loop through this List on onClick() method of Button.
ElevatedButton(
onPressed: () {
for(var controllerItem in listOfControllers) {
print(controllerItem.text); // the value of TextFormField
}
},
)
There is a photo like this in Firebase Storage:
I want to place this photo in a column. I wrote a code like this:
class MainScreen extends StatefulWidget {
#override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Container(
height: 200,
width: double.infinity,
decoration: BoxDecoration(
color: Colors.blueAccent,
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(30),
bottomRight: Radius.circular(30),
),
),
child: Column(
children: [
FutureBuilder<Widget>(
future: downloadURLExample(),
builder: (BuildContext context, AsyncSnapshot<Widget> snapshot) {
if(snapshot.hasData)
return snapshot.data;
}
),
SizedBox(height: 10),
Text(
"Hello",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
],
),
),
]
);
}
}
Future<void> downloadURLExample() {
var downloadURL = FirebaseStorage.instance.ref('defaultProfilePhoto').getDownloadURL();
return downloadURL;
}
When I run this code, I get an error like this:
_TypeError (type 'Future<String>' is not a subtype of type 'Future<Widget>?')
How can I solve this problem? Thanks in advance for the help.
I've looked at other threads but no solution.
You are getting this error because you are returning Future<String> from _downloadURLExample method but future property of your FutureBuilder widget is having type of Future<Widget> and hence you are getting type mismatch error.
Use this code
FutureBuilder<String>(
future: downloadURLExample(),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if(snapshot.hasData)
return Image.network(snapshot.data);
}
),
Future<String> downloadURLExample() {
var downloadURL = FirebaseStorage.instance.ref('defaultProfilePhoto').getDownloadURL();
return downloadURL;
}
I'm assuming you want to show the image in your UI.
Try this:
Future<Widget> downloadURLExample() async {
var downloadURL = await FirebaseStorage.instance.ref('defaultProfilePhoto').getDownloadURL();
return Image.network(downloadURL);
}
I'm trying to make a slider with the carousel_slider package. Image:
When I press the button written Next, I want it to go to the next page. I use it as it says in the document, but I get an error. Document
The error I got, It first redirects to a file named carousel_controller.dart, and then gives this error:
_CastError (Null check operator used on a null value)
Codes:
import 'package:flutter/material.dart';
import 'package:carousel_slider/carousel_slider.dart';
import 'package:getwidget/getwidget.dart';
final CarouselController _controller = CarouselController();
class selamlasmaLearn extends StatefulWidget {
#override
State<selamlasmaLearn> createState() => _selamlasmaLearnState();
}
class _selamlasmaLearnState extends State<selamlasmaLearn> {
List<wordAndMeaning> wordsList = [
wordAndMeaning("Hello", "Merhaba", false),
wordAndMeaning("Go", "Gehen", false)
];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.amber,
body: Builder(builder: (context) {
final double height = MediaQuery.of(context).size.height - 75;
return Column(
children: [
CarouselSlider(
options: CarouselOptions(
height: height,
viewportFraction: 1.0,
enlargeCenterPage: false,
),
items: wordsList.map((wordAndMeaning word) {
return Builder(
builder: (BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(color: Colors.amber),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(word.word,
style:
TextStyle(fontSize: 45, color: Colors.white)),
if (word.showMeaning) ...[
Text(word.meaning,
style: TextStyle(
fontSize: 20, color: Colors.white))
]
],
),
const SizedBox(
width: 10,
),
IconButton(
icon: Icon(Icons.remove_red_eye_sharp),
color: Colors.white,
iconSize: 25,
onPressed: () {
setState(() {
word.showMeaning = !word.showMeaning;
});
},
),
],
),
);
},
);
}).toList(),
),
Column(
children: [
GFButton(
text: "Next",
onPressed: () => _controller.nextPage( // <<<<<<<<<<
duration: const Duration(),
curve: Curves.easeInCirc),
)
],
)
],
);
}),
);
}
}
class wordAndMeaning {
String word;
String meaning;
bool showMeaning;
wordAndMeaning(this.word, this.meaning, this.showMeaning);
}
I marked the line that gave the error.
The line causing the error:
onPressed: () => _controller.nextPage(
How can I solve it? Thanks in advance for the help.
You need to assign your CarouselController to your CarouselSlider
CarouselSlider(
controller: _controller,
//...
)
Also, You should define your CarouselController inside your state class
class _selamlasmaLearnState extends State<selamlasmaLearn> {
final CarouselController _controller = CarouselController();
//...
}
(Friendly Advice: please always name your classes & variables in English and a class should always start with a capital letter)
I faced the same problem. Here is how I fixed it:
go to carousel_controller.dart file
search for "nextPage" method
inside this method, change "isNeedResetTimer" from:
final bool isNeedResetTimer = _state!.options.pauseAutoPlayOnManualNavigate;
to:
final bool isNeedResetTimer = true;
This is a carousel slider bug... _state is undefined.
I'm trying to implement a search function into my app. Right now I got a ListView, which displays the data I get from a Firebase Database as a Stream. Do you have any Ideas how to search this List?
Here is my code for the ListView:
import "package:rate_my_food_1/models/imbiss.dart";
import "package:provider/provider.dart";
import "package:rate_my_food_1/widgets/imbiss_tile.dart";
class ImbissList extends StatefulWidget {
#override
_ImbissListState createState() => _ImbissListState();
}
class _ImbissListState extends State<ImbissList> {
#override
Widget build(BuildContext context) {
final imbiss = Provider.of<List<Imbiss>>(context);
return Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
decoration: InputDecoration(hintText: "Suchen...", hintStyle: TextStyle(color: Colors.white)),
style: TextStyle(color: Colors.white),
),
),
SizedBox(height: 10,),
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: imbiss.length ?? 0,
itemBuilder: (context, index){
return ImbissTile(imbiss: imbiss[index]);
},
),
),
],
);
}
}
And here is how I implemented the ListView in my homescreen of the App:
import 'package:rate_my_food_1/models/imbiss.dart';
import "package:rate_my_food_1/services/database.dart";
import "package:provider/provider.dart";
import "package:rate_my_food_1/widgets/imbiss_list.dart";
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
return StreamProvider<List<Imbiss>>.value(
value: DatabaseService().imbisses,
child: Scaffold(
backgroundColor: Colors.grey[800],
//Add Button
floatingActionButton: FloatingActionButton(
onPressed: (){
Navigator.pushNamed(context, "/add");
},
child: Icon (Icons.add),
backgroundColor: Colors.cyanAccent,
),
body: Column(
children: <Widget>[
SafeArea(child: SizedBox(height: 10.0,)),
//Imbissliste
Expanded(child: ImbissList()),
],
)
)
);
}
}
I was thinking about making a second List in which I can Filter only for objects which contain what I'm writing into the TextField, but I have no clue how to do that properly.
Do you have any Ideas on how to search in this ListView?
If you need any more code from my Project, please let me know.
Thanks for all answers!
Yoqora
EDIT:
This is my Code atm, with the problem that it doesn't show the ListView and TextField when I start the app:
import "package:rate_my_food_1/models/imbiss.dart";
import "package:provider/provider.dart";
import "package:rate_my_food_1/widgets/imbiss_tile.dart";
class ImbissList extends StatefulWidget {
#override
_ImbissListState createState() => _ImbissListState();
}
class _ImbissListState extends State<ImbissList> {
List<Imbiss> imbiss = [];
List<Imbiss> filteredImbiss = [];
#override
initState() {
imbiss = Provider.of<List<Imbiss>>(context);
filteredImbiss = imbiss;
super.initState();
}
#override
Widget build(BuildContext context) {
return Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
decoration: InputDecoration(hintText: "Suchen...", hintStyle: TextStyle(color: Colors.white)),
style: TextStyle(color: Colors.white),
onChanged: (value) {
setState(() {
filteredImbiss = imbiss.where((imbiss) => imbiss.name.contains(value)).toList();
});
},
),
),
SizedBox(height: 10,),
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: filteredImbiss.length,
itemBuilder: (context, index){
return ImbissTile(imbiss: filteredImbiss[index]);
},
),
),
],
);
}
}`
We can filter the retrieved data using a key in state. This key kan be updated using the onChanged(String) method from the TextField widget. We simply filter the list on every build, please see the example below:
class ImbissList extends StatefulWidget {
#override
_ImbissListState createState() => _ImbissListState();
}
class _ImbissListState extends State<ImbissList> {
// The search key to filter the imbisses.
String key = '';
#override
Widget build(BuildContext context) {
var imbisses = Provider.of<List<Imbiss>>(context);
// Filter the imbisses using the key.
imbisses = imbisses.where((imbiss) {
return imbiss.name.contains(key);
}).toList();
return Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
decoration: InputDecoration(
hintText: "Suchen...",
hintStyle: TextStyle(color: Colors.white)),
style: TextStyle(color: Colors.white),
onChanged: (value) {
// Update the key when the value changes.
setState(() => key = value);
},
),
),
SizedBox(
height: 10,
),
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemCount: imbisses.length,
itemBuilder: (context, index) {
return ImbissTile(imbiss: imbisses[index]);
},
),
),
],
);
}
}
Hey i tried the same thing but i'm getting a blank screen, if possible can you please share the source code.
Edit: I figured out where i was going wrong. Thank you so much for the solution this was really helpful
I'm trying to build a chat application which displays time along with the message. Here is the main code:
import 'package:flutter/material.dart';
import 'package:flash_chat/constants.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
final _fireStore = Firestore.instance;
FirebaseUser loggedInUser;
class ChatScreen extends StatefulWidget {
static String chatScreen = 'ChatScreenpage1';
#override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final messageTextEditingController = TextEditingController();
String messageText;
final _auth = FirebaseAuth.instance;
#override
void initState() {
super.initState();
getUserDetail();
}
void getUserDetail() async {
try {
final createdUser = await _auth.currentUser();
if (createdUser != null) {
loggedInUser = createdUser;
}
} catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: null,
actions: <Widget>[
IconButton(
icon: Icon(Icons.close),
onPressed: () {
_auth.signOut();
Navigator.pop(context);
}),
],
title: Text('⚡️Chat'),
backgroundColor: Colors.lightBlueAccent,
),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
StreambuilderClass(),
Container(
decoration: kMessageContainerDecoration,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextField(
controller: messageTextEditingController,
onChanged: (value) {
messageText = value;
},
decoration: kMessageTextFieldDecoration,
),
),
FlatButton(
onPressed: () {
messageTextEditingController.clear();
_fireStore.collection('messages').add({
'sender': loggedInUser.email,
'text': messageText,
'time': FieldValue.serverTimestamp()
});
},
child: Text(
'Send',
style: kSendButtonTextStyle,
),
),
],
),
),
],
),
),
);
}
}
class StreambuilderClass extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _fireStore
.collection('messages')
.orderBy('time', descending: false)
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.blueAccent,
),
);
}
final messages = snapshot.data.documents.reversed;
List<MessageBubble> messageBubbles = [];
for (var message in messages) {
final messageText = message.data['text'];
final messageSender = message.data['sender'];
final messageTime = message.data['time'] as Timestamp;
final currentUser = loggedInUser.email;
print('check time: $messageTime'); //print(message.data['time']); both gives null
print('check sender: $messageSender');
print('check sender: $messageText');
print(snapshot.connectionState);
final messageBubble = MessageBubble(
sender: messageSender,
text: messageText,
isMe: currentUser == messageSender,
time: messageTime,
);
messageBubbles.add(messageBubble);
}
return Expanded(
child: ListView(
reverse: true,
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 20),
children: messageBubbles),
);
});
}
}
class MessageBubble extends StatelessWidget {
final String text;
final String sender;
final bool isMe;
final Timestamp time;
MessageBubble({this.text, this.sender, this.isMe, this.time});
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment:
isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: <Widget>[
Text(
' $sender ${DateTime.fromMillisecondsSinceEpoch(time.seconds * 1000)}',
style: TextStyle(color: Colors.black54, fontSize: 12),
),
Material(
color: isMe ? Colors.blueAccent : Colors.white,
borderRadius: isMe
? BorderRadius.only(
topLeft: Radius.circular(30),
bottomLeft: Radius.circular(30),
bottomRight: Radius.circular(30))
: BorderRadius.only(
topRight: Radius.circular(30),
bottomLeft: Radius.circular(30),
bottomRight: Radius.circular(30)),
elevation: 6,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 15),
child: Text(
text,
style: TextStyle(
fontSize: 20, color: isMe ? Colors.white : Colors.black),
),
),
),
],
),
);
}
}
But I get this exception for a moment(almost a second) with a red screen and then everything works fine:
By printing the snapshot data field values(The highlighted code in the image) for like 100 times with 100 messages, I realized that the StreamBuilder is sending updated snapshot twice.
(You can see in the output that the first snapshot is with just time field being null and immediately in the second snapshot all values are being present, this happens for every new message I send.)
Everything works as expected in my other app which doesn't use timestamp field in cloud firestore.
My question is shouldn't the StreamBuilder should just send one snapshot for every one update with all the data values being present at once?
Please tell me if I've made a mistake. Any help would be really appreciated!
This is actually expected behaviour for a StreamBuilder. As you can see in this Community Answer:
StreamBuilder makes two build calls when initialized, once for the
initial data and a second time for the stream data.
Streams do not guarantee that they will send data right away so an
initial data value is required. Passing null to initialData throws an
InvalidArgument exception.
StreamBuilders will always build twice even when the stream passed is
null.
So, in order to mitigate that exception and red screen glitch, you will have to take this into consideration and treat this scenario in your code.