dart StreamBuilder在使用SetState Flutter时总是获取新数据

lyfkaqu1  于 2023-09-28  发布在  Flutter
关注(0)|答案(2)|浏览(143)

我有一个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);
  }
}
gg58donl

gg58donl1#

由于您在小部件的build方法中有Firestore API调用,因此每次小部件被(重新)呈现时都会执行它。虽然这是可行的,但它确实是浪费的。
所以你要做的是:
1.从小部件的initState method调用Firestore API一次。
1.将从snapshots()调用返回的Stream放入状态。
1.然后在build方法中使用来自状态的流。
Randal Schwartz还录制了一段很棒的视频来解释,所以我建议你看看:Fixing a common FutureBuilder and StreamBuilder problem

ymdaylpp

ymdaylpp2#

您还可以使用ValueNotifier和ValueListenableBuilder来避免将整个小部件重新构建为_counter通知程序,您可以尝试dartpad示例
https://dartpad.dev/?id=1e52b13f6a9727e541baa74b58b1b558

// Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final String title;

  const MyHomePage({
    Key? key,
    required this.title,
  }) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final ValueNotifier<int> _counterNotifier = ValueNotifier(0);

  void _increment() => _counterNotifier.value = _counterNotifier.value + 1;
  void _decrement() => _counterNotifier.value = _counterNotifier.value - 1;

  @override
  Widget build(BuildContext context) {
    debugPrint('this does not re-render');
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              'You have pushed the button this many times:',
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                TextButton(
                  onPressed: _decrement,
                  child: const Icon(Icons.remove),
                ),
                SizedBox(
                  width: 48,
                  child: Center(
                  child: ValueListenableBuilder(
                    valueListenable: _counterNotifier,
                    builder: (_, count, __) {
                      return Text(
                        '$count',
                        style: Theme.of(context).textTheme.headlineMedium,
                      );
                    },
                  ),
                   ),
                ),
                TextButton(
                  onPressed: _increment,
                  child: const Icon(Icons.add),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

相关问题