I have a StreamBuilder and an int To increment number to do that I use setState function. And the problem is the StreamBuilder always getting new data every time setState is accur is there anyway to make the StreamBuilder not affect by setState every time the setState accur. To summarise is that Stop StreamBuilder from getting new data from backend every time when using setstate
full code
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
class StreamBuildTest2 extends StatefulWidget {
const StreamBuildTest2({super.key});
@override
State<StreamBuildTest2> createState() => _StreamBuildTest2State();
}
class _StreamBuildTest2State extends State<StreamBuildTest2> {
String userUid = FirebaseAuth.instance.currentUser!.uid;
final FirebaseStorage storage = FirebaseStorage.instance;
//Variable to increment number
int number = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
//The StreamBuilder that’s getting new data every time setState accur
body: StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance
.collection('users')
.doc(userUid)
.snapshots(),
builder: (context, snapshot) {
//Data from firebase backend
var fullName = snapshot.data?.get('fullName');
var email = snapshot.data?.get('email');
var profileUrl = snapshot.data?.get('profileUrl');
//
if (snapshot.connectionState == ConnectionState.waiting) {
return Container();
}
return Column(
children: [
//To get user profile
FutureBuilder(
future: downloadURL(profileUrl),
builder:
(BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
return SizedBox(
height: 110,
width: 110,
child: FittedBox(
fit: BoxFit.contain,
//The user profile always getting new data every time setState accur
child: CircleAvatar(
backgroundColor: Colors.transparent,
backgroundImage: NetworkImage(snapshot.data!),
radius: 10.0,
),
),
);
}
if (snapshot.connectionState == ConnectionState.waiting ||
snapshot.hasData) {
return Container();
}
return Container();
},
),
Text(fullName),
Text(email),
Text(number.toString()),
//Here’s the setState
ElevatedButton(
onPressed: () => setState(() {
number += 1;
}),
style: ElevatedButton.styleFrom(
foregroundColor: Colors.black,
backgroundColor: Colors.grey,
elevation: 0.0,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.zero,
),
),
child: const Text('SetState Button'),
),
],
);
},
),
);
}
//Doesn’t matter here
Future<String> downloadURL(String file) async {
try {
String downloadURL =
await storage.ref('usersProfile/$file').getDownloadURL();
print(downloadURL);
return downloadURL;
} on FirebaseException catch (e) {
print(e);
}
return downloadURL(file);
}
}
feel free to Modified my code
2
Answers
Since you have the Firestore API call in the
build
method of your widget, it gets executed each time the widget gets (re)rendered. While this works, it is indeed wasteful.So what you’ll want to do instead is:
initState
method of your widget.Stream
you get back from thesnapshots()
call there, into the state.build
method.Randal Schwartz also recorded a great video explaining, so I recommend checking that out: Fixing a common FutureBuilder and StreamBuilder problem
You can also use ValueNotifier and ValueListenableBuilder to avoid rebuilding your entire widget to a _counter notifier, you can try that dartpad example
https://dartpad.dev/?id=1e52b13f6a9727e541baa74b58b1b558