1

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?

John Joe
  • 12,412
  • 16
  • 70
  • 135

1 Answers1

0

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.

nvoigt
  • 75,013
  • 26
  • 93
  • 142
  • If dont need `FutureBuilder`,where should I put `loadPdfFirstPage`? – John Joe Aug 05 '21 at 07:33
  • Inside the BLoC because it's business logic. But in general it's hard to say because that's your application's architecture, which I know nothing about. What I can say with certainty is that the call of loadPdfFirstPage belongs into your state management code, not into your build method. – nvoigt Aug 05 '21 at 09:00
  • can you share me some code sample to fix this? Thanks. – John Joe Aug 05 '21 at 14:44
  • I cannot share a code sample. This is not something that 2 lines of code will fix. You have to find the spot in your program where your logic runs. *There* you have to create a variable of type "Future" and *that* variable has to go to your FutureBuilder in the build method. You cannot have state management in your build method. – nvoigt Aug 07 '21 at 10:04