Search code examples
fluttergoogle-cloud-firestorestreamstream-builder

Flutter App - Having Trouble Refreshing an Element in StreamBuilder


I have a flutter application that primarily uploads and retrieves images to and from Firebase. I have a paticular screen, where users can see their uploaded memories.

My goal is to allow users to view all of their memories in the gallery, and be able to upload a memory on the same screen. I have been succesful with this, but only to a point where the whole screen needs to refresh for the new image to display. I want to have my builder for the screen only have to load in the newley uploaded image. To achieve this, I have switched from FutureBuilder to StreamBuilder, however the newly uploaded image still requires a full screen reload to load in.

Here's my code:


class _BrowseMemoriesScreenState extends State<BrowseMemoriesScreen> {
  var index = 0;
  var heroTag = "photo";
  late Widget currentPage;
  int dummy = 0;
  late Stream<List<String>> streamData;

  @override
  void initState() {
    super.initState();
    streamData = BrowseMemoriesScreen(childID: widget.childID, childUID: widget.childUID, childKey: widget.childKey, childName: widget.childName, profilePicture: widget.profilePicture,)
                    .getMemories();
  }

  @override
  void dispose() {
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final themeNotifier = Provider.of<ThemeNotifier>(context);
    final isDarkMode = themeNotifier.theme == themeNotifier.darkTheme;
    StreamController streamController = StreamController();
    String imageURL = "";
    String download;
    return Scaffold(
      appBar: SettingsAppBar(leadingIcon: true, title: Text("Memories")),
      backgroundColor: isDarkMode ? Color.fromARGB(255, 47, 47, 47) : Colors.white,
      body: SafeArea(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // Child Icon
            Center(
              child: ChildButton(
                key: widget.childKey,
                childName: widget.childName,
                childID: widget.childID,
                profilePicture: widget.profilePicture,
                childUID: widget.childUID,
                editable: true,
              ),
            ),
            Expanded(
              child: Container(
                padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
                decoration: BoxDecoration(
                  color: isDarkMode ? Color.fromARGB(255, 47, 47, 47) : Colors.white,
                  borderRadius: BorderRadius.only(
                    topLeft: Radius.circular(30),
                    topRight: Radius.circular(30),
                  ),
                ),
                child: StreamBuilder(
                    stream: streamData,
                    initialData: const [""],
                    builder: (context, AsyncSnapshot<List<String>> snapshot) {
                      if (snapshot.connectionState == ConnectionState.done) {
                        if (snapshot.data!.isEmpty) {
                          return Center(
                            child: Text(
                              "No memories found yet. Uploaded Memories will appear here",
                              textAlign: TextAlign.center,
                              style: GoogleFonts.montserrat(
                                fontWeight: FontWeight.bold,
                                fontSize: 18,
                              ),
                            ),
                          );
                        }
                        try {
                          return GridView.builder(
                              gridDelegate:
                                  SliverGridDelegateWithFixedCrossAxisCount(
                                crossAxisCount: 3,
                                crossAxisSpacing: 5,
                                mainAxisSpacing: 5,
                              ),
                              itemCount: snapshot.data?.length,
                              itemBuilder: (context, index) {
                                var imgURL = snapshot.data?[index];
                                return GestureDetector(
                                  onTap: () {
                                    Navigator.of(context).push(
                                        MaterialPageRoute(
                                            builder: (_) => InspectPhoto(
                                                snapshot: snapshot,
                                                index: index,
                                                heroTag: heroTag +
                                                    index.toString())));
                                  },
                                  child: Hero(
                                    tag: heroTag + index.toString(),
                                      child: GestureDetector(
                                      onLongPress: () async {
                                        ScreenshotController screenshotController = ScreenshotController();
                                              
                                        await screenshotController.captureFromWidget(
                                          CachedNetworkImage(
                                            imageUrl: imgURL!,
                                            // Placeholder appears as a loading indicator
                                            //placeholder: (context, url) => Container(
                                            // color: Colors.grey,
                                            //),
                                            errorWidget: (context, url, error) => Container(
                                              child: Center(child: Text("Error loading requested image.")),
                                            ),
                                          )
                                        ).then((Uint8List capturedImage) async {
                                          final appDir = await getApplicationDocumentsDirectory();

                                          File file = await File('${appDir.path}/sharedMemory.png').create();

                                          await file.writeAsBytes(capturedImage);

                                          await Share.shareXFiles(
                                            [XFile(file.path)],
                                            text: "Choose Where to Send Memory",
                                          );

                                          file.delete();
                                        });
                                      }, child:CachedNetworkImage(
                                        imageUrl: imgURL!,
                                        fit: BoxFit.cover,
                                      ),
                                    )
                                  ),
                                );
                              });
                        } catch (e) {
                          return Center(
                              child: Text(
                                  "No memories found. Uploaded Memories will appear here."));
                        }
                      } else if (snapshot.connectionState ==
                          ConnectionState.none) {
                        return Center(
                            child: Text(
                                "No network connection, try again later."));
                      }
                      return SizedBox(
                        height: 200,
                        width: 200,
                        child: Center(
                          child: CircularProgressIndicator(strokeWidth: 10),
                        ),
                      );
                    }
                    /*child: GridView.builder(
                    gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                      crossAxisCount: 3,
                      crossAxisSpacing: 10,
                      mainAxisSpacing: 10,
                    ),
                    itemBuilder: (context, index) {
                      return RawMaterialButton(
                        onPressed: () {},
                        child: Container(
                          decoration: BoxDecoration(
                            borderRadius: BorderRadius.circular(15),
                            image: DecorationImage(
                              image: AssetImage(_images[index]),
                              fit: BoxFit.cover,
                            ),
                          ),
                        ),
                      );
                    },
                    itemCount: _images.length,
                  */
                  ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

/ Grab all of the memories that are associated with the childID
  Stream<List<String>> getMemories() async* {
    List<String> URLS = [];
    DocumentSnapshot doc = await FirebaseMethods(childID: childID)
        .getChildDoc(childUID);
    var data = doc.data() as Map<String, dynamic>;

    // These two lists will hold all of the child's memories - name of the file
    // in Firebase plus the corresponding date that photo was created
    List<String> memoryNames = [];
    List<String> creationDates = [];

    for (String memory in data['memories']['memoryNames']) {
      memoryNames.add(memory);
    }

    for (String creationDate in data['memories']['creationDates']) {
      creationDates.add(creationDate);
    }

    for (String memory in memoryNames) {
      var URLRef = await firebase_storage.FirebaseStorage.instance
          .ref()
          .child('files/memories/')
          .child('${memory}');
      var imgURL = await URLRef.getDownloadURL();
      URLS.add(imgURL);
    }
    yield URLS;
  }

Solution

  • Fixed this by realizing a mistake.

    Never updated my function that would upadte streamData, as it would return a future. Needed to also change how I accessed data as well afterwards.

      Stream<DocumentSnapshot> getChildDocStream(childUID) async* {
        yield* FirebaseFirestore.instance
            .collection('child')
            .doc(childUID)
            .snapshots();
      }```