flutterdartstream-builderflutter-futurebuilderisolation

Compute method not calling in FutureBuilder


I notice my app will get freezed once a large file is selected. So I came out with an idea, which let the bytes generate in isolate thread. Once done generate, let it display in Image widget.

First selected file will be added in urlImageSink.

@override
  Future<String>userImage(File images) async {
    if (images.path.contains(".pdf")) {
      urlListImages.add(images.path);
      _bloc.urlImageSink.add(urlListImages);
    } 
  }

Next it will run the StreamBuilder, call the loadPdfFirstPage in FutureBuilder.

 Widget _showAttachFile() {
    return Padding(
      padding: EdgeInsets.all(10),
      child: StreamBuilder<List<dynamic>>(
          stream: _bloc.urlImageStream,
          builder: (context, snapshot) {
            if (snapshot.hasData) {
              return GridView.builder(
                  shrinkWrap: true,
                  physics: NeverScrollableScrollPhysics(),
                  gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(
                    crossAxisCount: 3,
                    mainAxisSpacing: 5.0,
                    crossAxisSpacing: 5.0,
                  ),
                  itemCount: snapshot.data.length + 1,
                  itemBuilder: (BuildContext context, int index) => new Padding(
                        padding: const EdgeInsets.all(5.0),
                        child: Container(
                          padding: EdgeInsets.zero,
                          height: 150,
                          width: 150,
                          child: FutureBuilder<Uint8List>(
                            future:
                                loadPdfFirstPage(File(snapshot.data[index])),
                            builder: (context, snapshot) {
                              switch (snapshot.connectionState) {
                                case ConnectionState.done:
                                  if (snapshot.hasData) {
                                    return Image(
                                        image: MemoryImage(snapshot.data));
                                     );
                                  } else {
                                    return Text("Null");
                                  }
                                  break;

                                case ConnectionState.waiting:
                                  return CircularProgressIndicator();
                                  break;

                                default:
                                  return Text("Error");
                              }
                            },
                          ),
                        ),
                      ));
            } else {
              return Text("No Data");
            }
          }),
    );
  }

In loadPdfFirstPage method, I running the compute method to generate bytes.

Future<Uint8List> loadPdfFirstPage(File pdfFile) =>
      compute(generateBytes, pdfFile);

  Future<Uint8List> generateBytes(File pdfFile) async {
    final document = await PdfDocument.openFile(pdfFile.path);
    final page = await document.getPage(1);
    final pageImage = await page.render(width: page.width, height: page.height);
    await page.close();

    return pageImage.bytes;
  }

Everytime a file selected, I will straight away get output Null from FutureBuilder snapshot.data. It seems like loadPdfFirstPage is not calling.

What am I doing wrong here?


Solution

  • Your current problem seems to be, that you have not created a variable to hold your future once. Your FutureBuilder, upon being created from the build method, will call the method over and over and over. It will call it every time the build method is called.

    You need a place where you create the future you want to wait on and it has to be somewhere else then in the build method.

    That said, if I interpret your variable named _bloc correctly, you are using the BLoC pattern? You should have events and states. You should not need a FutureBuilder, having a new file should be an event, waiting for it to load should be a state, loading finished should be a state of it's own. Then you have a logic in your BLoC class that has a flow and you have a UI that does not need to know about what triggers what and just displays states.