我有一个StreamBuilder和一个int来增加数字,我使用setState函数。问题是StreamBuilder总是在每次setState发生时获取新数据,无论如何都要使StreamBuilder在每次setState发生时不受setState的影响。总结一下,每次使用setstate时,停止StreamBuilder从后端获取新数据
完整代码
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);
}
}
随意修改我的代码
问题
offsetNotifier可以更改,但当我使用onPanUpdate: _offsetNotifier,
时,我不希望FutureBuilder也更改
final ValueNotifier<Offset> offsetNotifier = ValueNotifier(const Offset(0, 0));
void _offsetNotifier(DragUpdateDetails details) => offsetNotifier.value += details.delta;
ValueListenableBuilder(
valueListenable: offsetNotifier,
builder: (_, value, __) {
return Positioned(
left: offsetNotifier.value.dx,
top: offsetNotifier.value.dy,
child: FutureBuilder(
future: downloadURL(profileUrl),
builder: (BuildContext context,
AsyncSnapshot<String> snapshot) {
if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
return CircleAvatar(
backgroundColor: Colors.transparent,
backgroundImage: NetworkImage(snapshot.data!),
radius: 10.0,
);
}
return Container();
}),
);
}),
Future<String> downloadURL(String file) async {
try {
String downloadURL =
await storage.ref('widgetsProfile/$file').getDownloadURL();
print(downloadURL);
return downloadURL;
} on FirebaseException catch (e) {
print(e);
}
return downloadURL(file);
}
}
2条答案
按热度按时间gg58donl1#
由于您在小部件的
build
方法中有Firestore API调用,因此每次小部件被(重新)呈现时都会执行它。虽然这是可行的,但它确实是浪费的。所以你要做的是:
1.从小部件的
initState
method调用Firestore API一次。1.将从
snapshots()
调用返回的Stream
放入状态。1.然后在
build
方法中使用来自状态的流。Randal Schwartz还录制了一段很棒的视频来解释,所以我建议你看看:Fixing a common FutureBuilder and StreamBuilder problem
ymdaylpp2#
您还可以使用ValueNotifier和ValueListenableBuilder来避免将整个小部件重新构建为_counter通知程序,您可以尝试dartpad示例
https://dartpad.dev/?id=1e52b13f6a9727e541baa74b58b1b558