Search code examples
flutterfirebasedartgoogle-cloud-firestorestream-builder

Streambuilder always getting new data when using setstate flutter


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

Problem

The offsetNotifier can change but when I use onPanUpdate: _offsetNotifier, I don’t want the FutureBuilder to change as well

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);
  }
}

Solution

  • 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

    // 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),
                    ),
                  ],
                ),
              ],
            ),
          ),
        );
      }
    }