Search code examples
flutterfilepicker

file_picker - onPressed does not delete a card neither refresh the view


I am using File_Picker.dart I am having an issue with one of the function I have written. When I press on the icon delete, the record is not deleted, and the screen is not refreshed. What I mean is that the card is still displayed. Also, what I am trying to do it to be able to add a file one by one, without removing the file previously selected. But each time I am clicking on the button 'Open File picker' which is calling '_openFileExplorer()', the file selected previously is removed from the screen. I have not find how to stop this to happen.

If you can help me to solve this it would be appreciated. Many thanks.

import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_storage/firebase_storage.dart';

class FilePickerDemo extends StatefulWidget {
  @override
  _FilePickerDemoState createState() => _FilePickerDemoState();
}

class _FilePickerDemoState extends State<FilePickerDemo> {
  //FirebaseStorage storage = FirebaseStorage.instance;
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
  late String _fileName;
  List<PlatformFile>? _paths;
  bool _loadingPath = false;
  bool _multiPick = true;
  FileType _fileType = FileType.media;

  /*
  @override
  void initState() {
    super.initState();
    Firebase.initializeApp();
  }
   */

  void _openPictureFileExplorer() async {
    setState(() => _loadingPath = true);

/*
try{
    var files = (await FilePicker.platform.pickFiles(
      type: _fileType,
      allowMultiple: _multiPick,
    ))
        ?.files;

    _paths = _paths!..addAll(files!.map((e) => e));
    _loadingPath = false;
} on PlatformException catch (e) {
  print("Unsupported operation" + e.toString());
} catch (ex) {
  print('$ex');
}

 */

    try {
      _paths = (await FilePicker.platform.pickFiles(
        type: _fileType,
        allowMultiple: _multiPick,
      ))
          ?.files;
    } on PlatformException catch (e) {
      print("Unsupported operation" + e.toString());
    } catch (ex) {
      print('$ex');
    }

    if (!mounted) return;

    setState(() {
      _loadingPath = false;
      print(_paths!.last.name);
      _fileName = _paths != null ?
      _paths!.map((e) => e.name).toString() : '...';
    });
  }

  void _openDocumentFileExplorer() async {
    setState(() => _loadingPath = true);


try{
    var files = (await FilePicker.platform.pickFiles(
      type: _fileType,
      allowMultiple: _multiPick,
    ))
        ?.files;

    _paths = _paths!..addAll(files!.map((e) => e));
    
    _loadingPath = false;
} on PlatformException catch (e) {
  print("Unsupported operation" + e.toString());
} catch (ex) {
  print('$ex');
}


/*
    try {
      _paths = (await FilePicker.platform.pickFiles(
        type: FileType.custom,
        allowMultiple: _multiPick,
        allowedExtensions: ['pdf','docx'],
      ))
          ?.files;
    } on PlatformException catch (e) {
      print("Unsupported operation" + e.toString());
    } catch (ex) {
      print('$ex');
    }

    if (!mounted) return;

    setState(() {
      _loadingPath = false;
      print(_paths!.last.name);
      _fileName = _paths != null ?
      _paths!.map((e) => e.name).toString() : '...';
    });

 */


  }


  void _clearCachedFiles() {
    FilePicker.platform.clearTemporaryFiles().then((result) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          backgroundColor: result! ? Colors.green : Colors.red,
          content: Text((result
              ? 'Temporary files removed with success.'
              : 'Failed to clean temporary files')),
        ),
      );
    });
  }

  // Select and image from the gallery or take a picture with the camera
  // Then upload to Firebase Storage

 /*
  Future<void> _upload() async {

    try {

      final String fileName = _fileName;
      File imageFile = File(_paths.toString());

      try {
        // Uploading the selected image with some custom meta data
        await storage.ref(fileName).putFile(
            imageFile,
            );

        // Refresh the UI
        setState(() {});
      } on FirebaseException catch (error) {
        print(error);
      }
    } catch (err) {
      print(err);
    }
  }


  */


  // Delete the selected image
  // This function is called when a trash icon is pressed
  void _delete(int index) {
    if(_paths!=null || _paths!.length> 1) {
      _paths!.removeAt(index);
      setState(() {});
      print ('2:' +_paths!.length.toString());
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        key: _scaffoldKey,
        appBar: AppBar(
          title: const Text('File Picker example app'),
        ),
        body: Center(
            child: Padding(
              padding: const EdgeInsets.only(left: 10.0, right: 10.0),
              child: SingleChildScrollView(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    Padding(
                      padding: const EdgeInsets.only(top: 20.0),

                      child:Card(
                          child:
                          Container(
                            // color: Colors.red,
                            alignment: Alignment.center,
                            child: Row(
                              mainAxisAlignment: MainAxisAlignment.center,
                              children:[
                                //Attachement
                                FlatButton(
                                  onPressed: () {  },
                                  child:
                                  InkWell(
                                    child: Container(
                                      //  color: Colors.white,
                                        child: Column(
                                          mainAxisAlignment: MainAxisAlignment.center,
                                          children: [
                                            Icon(Icons.attach_file),
                                            Text('Attachement'),
                                          ],
                                        )
                                    ),
                                    onTap: () {

                                    },
                                  ),
                                ),

                                //Photo
                                FlatButton(
                                  onPressed: () {  },
                                  child:
                                  InkWell(
                                    child: Container(
                                      //   color: Colors.white,
                                        child: Column(
                                          mainAxisAlignment: MainAxisAlignment.center,
                                          children: [
                                            Icon(Icons.add_a_photo_rounded),
                                            Text('Photo'),
                                          ],
                                        )
                                    ),
                                    onTap: () {_openPictureFileExplorer(); },
                                  ),
                                ),
                              ],
                            ),
                          )),
                    ),


                    Builder(
                      builder: (BuildContext context) => _loadingPath ?
                          Padding(
                        padding: const EdgeInsets.only(bottom: 10.0),
                           child:const CircularProgressIndicator(),
                      )
                          : _paths != null && _paths!.isNotEmpty
                          ? Container(
                        padding: const EdgeInsets.only(bottom: 30.0),
                        height:
                        MediaQuery.of(context).size.height * 0.50,
                          child: Scrollbar(
                            child: ListView.separated(
                              itemCount:
                              _paths != null && _paths!.isNotEmpty
                                  ? _paths!.length
                                  : 1,
                              itemBuilder:
                                  (BuildContext context, int index) {
                                final bool isMultiPath =
                                    _paths != null && _paths!.isNotEmpty;
                                final String name =
                                    (isMultiPath
                                        ? _paths!
                                        .map((e) => e.name)
                                        .toList()[index]
                                        : _fileName ?? '...');

                                final path = _paths!
                                    .map((e) => e.path)
                                    .toList()[index]
                                    .toString();

                                return Container(
                                  height: 114,
                                  child: Card(
                                    shape: RoundedRectangleBorder(
                                      borderRadius: BorderRadius.circular(15.0),
                                    ),
                                    elevation: 10,
                                      child: ClipPath(
                                        clipper: ShapeBorderClipper(
                                            shape: RoundedRectangleBorder(
                                                borderRadius: BorderRadius.circular(15))),
                                        child: Row(
                                          crossAxisAlignment: CrossAxisAlignment.center,
                                          mainAxisAlignment: MainAxisAlignment.start,
                                          children: [
                                            Container(
                                              height: 113,width: 113,
                                              child: Image.file(File(path),
                                                fit: BoxFit.fill,
                                                width: double.infinity,),
                                            ),
                                            Expanded(
                                              child: Padding(
                                                padding: const EdgeInsets.all(10.0),
                                                child: Text(name,
                                                  style: TextStyle(fontWeight: FontWeight.bold),),
                                              ),
                                            ),
                                              Padding(
                                                padding: const EdgeInsets.only(right:25.0),
                                                child: IconButton(onPressed: (){
                                                  //delete a record and the card displaying this record
                                                  _delete(index);
                                                },

                                                  icon:Icon (Icons.delete, color: Colors.red,),),
                                              )
                                          ],
                                        ),
                                      ),
                                      //subtitle: Text(path),
                                  ),
                                );
                              },
                              separatorBuilder:
                                  (BuildContext context, int index) =>
                               const SizedBox(),)),)
                              :const SizedBox(child:Text('No documents')),
                          ),
                      ],
                ),
              ),
            )),
      ),
    );
  }
}

Solution

  • The problem here is that the paths variable is made up of a list of PlatformFile and you are trying to remove a string value which doesn't exist in that list.

    You can see this if you print the value of paths in your _delete() method. It should show [Instance of 'PlatformFile', Instance of 'PlatformFile'].

    In a nutshell what you're trying to do could be visualized as such

    [PlatformFile(), PlatformFile()].remove("some/path/string/value")
    

    There can be a simple fix for this assuming the list isn't sorted, you can just send the index as the reference from the onTap .

    It would look like this

      void _delete(int ref) {
        // remove the element at the passed index
        _paths!.removeAt(ref);
        setState(() {});
      }
    
       ... some code...
       itemBuilder: (BuildContext context, int index) {
          ... some code...
           IconButton(
            onPressed: () {
             _delete(index);
             setState(() {});
            },
            icon: Icon(
             Icons.delete,
             color: Colors.red,
        ),
        ... some code...
      );
     }
    
    

    Alternatively if you can't be sure of the positions of the elements in the path list, this would be the best approach.

      void _delete(String ref) {
        var _platformFileObject = _paths!.where((element) => element.path == ref).first;
        _paths!.remove(_platformFileObject);
        setState(() {});
      }
    

    Edit to answer the second question;

    In your _openFileExplorer() method, you are reassigning the new file objects rather than adding them to the _paths list. The solution would be to add the new file objects to the list.

    FilePickerResult? filePickerResult = await 
           FilePicker.platform.pickFiles(
            type: _fileType,
            allowMultiple: _multiPick,
    );
    
    _paths?.addAll(filePickerResult!.files.map((e) => e));
    

    Edited to answer the third question

    The error you stated occurs because of this [lines 150-153]

    itemCount:
      _paths != null && _paths!.isNotEmpty
      ? _paths!.length
      : 1
    

    So when it goes to the builder it is trying build a list item but there's nothing to get since the list is actually empty. You should change 1 to 0.

    In order to show a text when the _paths list is empty, you should try something like this.

    Scrollbar(
        child: !_paths!.isEmpty
            ? ListView.separated(
                itemCount: _paths!.length,
                itemBuilder: (BuildContext context, int index) {
                  return Container();
                },
                separatorBuilder: (BuildContext context, int index) {
                  return Container();
                },
              )
            : Text("Text to display when _paths is empty"),
    );
    

    Additionally, I would recommend that you refactor your code for better readability.