0

I am trying to display a horizontally scrollable ListView of different images. This is supposed to be achieved with the Widget MultipleImageDemo. I use MultipleImagePicker in loadAssets() to create a list of Assets of the images that are selected from the iPhone gallery. Then, I convert these Assets into Files so that I can display them in a ListView using getImageFileFromAssets(Asset asset) from How to convert asset image to File?. However, the function for converting assets to Files is asynchronous so I need to use a FutureBuilder when I try to use this function inside the Widget build(BuildContext context).

This is how I tried to implement it:

Future<File> getImageFileFromAssets(Asset asset) async {
  final byteData = await asset.getByteData();

  final tempFile =
      File("${(await getTemporaryDirectory()).path}/${asset.name}");
  final file = await tempFile.writeAsBytes(
    byteData.buffer.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes),
  );

class MultipleImageDemo extends StatefulWidget {
  @override
  _MultipleImageDemoState createState() => _MultipleImageDemoState();
}

class _MultipleImageDemoState extends State<MultipleImageDemo> {
  List<Asset> images = <Asset>[];

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

  Future<ListView> DisplayPhotos() async {
    List<File> displayedPhotos = <File>[];
    for (var i = 0; i < images.length; i++) {
      File image_file = await getImageFileFromAssets(images[i]);
      displayedPhotos.add(image_file);
    }
    return ListView(
        scrollDirection: Axis.horizontal,
        children: List.generate(displayedPhotos.length, (index) {
          File displayedPhoto = displayedPhotos[index];
          return Container(
              height: MediaQuery.of(context).size.height,
              width: MediaQuery.of(context).size.width,
              child: Image.file(displayedPhoto));
        }));
  }

Future<void> loadAssets() async {
    List<Asset> resultList = <Asset>[];
    String error = 'No Error Detected';

    try {
      resultList = await MultiImagePicker.pickImages(
        maxImages: 300,
        enableCamera: true,
        selectedAssets: images,
        cupertinoOptions: CupertinoOptions(takePhotoIcon: "chat"),
        materialOptions: MaterialOptions(
          actionBarColor: "#abcdef",
          actionBarTitle: "Example App",
          allViewTitle: "All Photos",
          useDetailsView: false,
          selectCircleStrokeColor: "#000000",
        ),
      );
    } on Exception catch (e) {
      error = e.toString();
    }

    if (!mounted) return;

    setState(() {
      images = resultList;
      // _error = error;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        height: MediaQuery.of(context).size.height,
        width: MediaQuery.of(context).size.width,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
              child: Text("Pick images"),
              onPressed: loadAssets,
            ),
            Expanded(
                child: FutureBuilder<ListView>(
                    future: DisplayPhotos(),
                    builder: (BuildContext context,
                        AsyncSnapshot<ListView> snapshot) {
                      List<Widget> children;
                      if (snapshot.hasData) {
                        children = <Widget>[
                          const Icon(
                            Icons.check_circle_outline,
                            color: Colors.green,
                            size: 60,
                          ),
                          Padding(
                            padding: const EdgeInsets.only(top: 16),
                            child: Text('Result: ${snapshot.data}'),
                          )
                        ];
                      }
                      return Center(
                        child: Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          crossAxisAlignment: CrossAxisAlignment.center,
                          children: children,
                        ),
                      );
                    })),
          ],
        ),
      ),
    );
  }
}

This is the error I get:

The following assertion was thrown building FutureBuilder<ListView>(dirty, state: _FutureBuilderState<ListView>#66af4):
'package:flutter/src/widgets/framework.dart': Failed assertion: line 1741 pos 14: 'children != null': is not true.
2

Either the assertion indicates an error in the framework itself, or we should provide substantially more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
  https://github.com/flutter/flutter/issues/new?template=2_bug.md
The relevant error-causing widget was
FutureBuilder<ListView>
package:project_startup/screens/addPage.dart:279
When the exception was thrown, this was the stack
#2      new MultiChildRenderObjectWidget
package:flutter/…/widgets/framework.dart:1741
#3      new Flex
package:flutter/…/widgets/basic.dart:4371
#4      new Column
package:flutter/…/widgets/basic.dart:4939
#5      _MultipleImageDemoState.build.<anonymous closure>
package:project_startup/screens/addPage.dart:323
#6      _FutureBuilderState.build
package:flutter/…/widgets/async.dart:775

How do I correctly implement the FutureBuilder? Is it even necessary to use it?

This is what is the simulator shows when I run this app even after I have selected several images from the gallery

2 Answers2

1

EDIT

To display the images you could do something like this:

return Center(
  child: Column(
    mainAxisAlignment: MainAxisAlignment.center,
    crossAxisAlignment: CrossAxisAlignment.center,
    children: [snapshot.data, ...children], // this line
  ),
);

Change List<Widget> children; to List<Widget> children = []; so that way it's initalized.

The problem comes from this:

return Center(
  child: Column(
    mainAxisAlignment: MainAxisAlignment.center,
    crossAxisAlignment: CrossAxisAlignment.center,
    children: children, // this line
  ),
);

Because children is null when the snapshot is loading (has no data), you get that error in the console.

Benjamin
  • 5,783
  • 4
  • 25
  • 49
  • Thank you for your answer, however, I tried it and the simulator still does not display the images I select in the gallery although the error thankfully has disappeared now! Do you see any other issues? How do I make it so that the ListView returned by DisplayPhotos() is displayed? – thebasqueinterdisciplinarian May 30 '21 at 00:54
  • Can you pass snapshot.data instead of children? return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: snapshot.data, ), ) – Andrija May 30 '21 at 06:47
  • @Andrija I agree, that would make sense but doing this yields the following error: `The argument type 'ListView' can't be assigned to the parameter type 'List'.` Do you see a different way by which I could return snapshot.data instead of children? – thebasqueinterdisciplinarian May 30 '21 at 11:33
0

Inserting snapshot.data into children and then displaying children in the returned container seems to have solved the problem. A big thank you to the people who helped :).

Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        height: MediaQuery.of(context).size.height,
        width: MediaQuery.of(context).size.width,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
              child: Text(PICKIMAGES),
              onPressed: loadAssets,
            ),
            Expanded(
                child: FutureBuilder<Container>(
                    future: DisplayPhotos(),
                    builder: (BuildContext context,
                        AsyncSnapshot<Container> snapshot) {
                      List<Widget> children = [];
                      if (snapshot.hasData) {
                        children = <Widget>[
                          snapshot.data,
                        ];
                      }
                      return Container(
                          height: MediaQuery.of(context).size.height,
                          width: MediaQuery.of(context).size.width,
                          child: ListView(
                              children: children));
                    })),
          ],
        ),
      ),
    );
  }
}