#flutter #filepicker
Вопрос:
Я использую File_Picker.dart У меня возникла проблема с одной из функций, которые я написал. Когда я нажимаю на значок удалить, запись не удаляется, и экран не обновляется. Я имею в виду, что карта все еще отображается. Кроме того, я пытаюсь сделать это, чтобы иметь возможность добавлять файл один за другим, не удаляя ранее выбранный файл. Но каждый раз, когда я нажимаю на кнопку «Открыть средство выбора файлов», которое вызывает » _openFileExplorer ()», файл, выбранный ранее, удаляется с экрана. Я не нашел, как остановить это, чтобы это произошло.
Если вы можете помочь мне решить эту проблему, я был бы признателен. Большое спасибо.
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 amp;amp; _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 amp;amp; _paths!.isNotEmpty
? _paths!.length
: 1,
itemBuilder:
(BuildContext context, int index) {
final bool isMultiPath =
_paths != null amp;amp; _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')),
),
],
),
),
)),
),
);
}
}
Ответ №1:
Проблема здесь в том, что paths
переменная состоит из списка файлов платформы, и вы пытаетесь удалить строковое значение, которого нет в этом списке.
Вы можете увидеть это, если выведете значение paths
в своем методе _delete (). Это должно быть заметно [Instance of 'PlatformFile', Instance of 'PlatformFile']
.
В двух словах то, что вы пытаетесь сделать, можно было бы представить как таковое
[PlatformFile(), PlatformFile()].remove("some/path/string/value")
Для этого может быть простое исправление, если предположить, что список не отсортирован, вы можете просто отправить индекс в качестве ссылки из onTap
.
Это выглядело бы так
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...
);
}
В качестве альтернативы, если вы не можете быть уверены в расположении элементов в списке путей, это был бы лучший подход.
void _delete(String ref) {
var _platformFileObject = _paths!.where((element) => element.path == ref).first;
_paths!.remove(_platformFileObject);
setState(() {});
}
Отредактируйте, чтобы ответить на второй вопрос;
В своем _openFileExplorer()
методе вы переназначаете новые файловые объекты, а не добавляете их в _paths
список. Решением было бы добавить новые файловые объекты в список.
FilePickerResult? filePickerResult = await
FilePicker.platform.pickFiles(
type: _fileType,
allowMultiple: _multiPick,
);
_paths?.addAll(filePickerResult!.files.map((e) => e));
Отредактировано, чтобы ответить на третий вопрос
Указанная вами ошибка возникает из-за этого [строки 150-153]
itemCount:
_paths != null amp;amp; _paths!.isNotEmpty
? _paths!.length
: 1
Поэтому, когда он переходит к построителю, он пытается создать элемент списка, но там нечего получить, так как список на самом деле пуст. Вы должны изменить 1 на 0.
Чтобы отобразить текст, когда _paths
список пуст, вы должны попробовать что-то вроде этого.
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"),
);
Кроме того, я бы рекомендовал вам провести рефакторинг кода для лучшей читабельности.
Комментарии:
1. Большое спасибо. Сейчас onTap работает нормально. Могу я, пожалуйста, спросить вас, нашли ли вы решение моего второго вопроса? Это был способ добавлять картинку одну за другой. Каждый раз, когда я нажимаю кнопку «Открыть средство выбора файлов», она удаляет все файлы из моего представления. Чтобы предоставить вам больше цветов, я хотел бы разрешить пользователям добавлять изображения по одному, а затем добавлять PDF или другой файл. В конце я должен отобразить список из нескольких изображений и файлов. Затем, нажав на кнопку, все эти файлы будут сохранены в Firebase. Я надеюсь, что это ясно. Большое спасибо
2. Я отредактировал свое решение, чтобы ответить на ваш второй вопрос @LaurentThomas
3. Надеюсь, я ответил на ваш вопрос @LaurentThomas
4. Я изменил свой код с помощью ‘var files = (ожидание FilePicker.platform.pickFiles(…». Тогда приложение ведет себя неправильно. Он отображает индикатор CircularProgressIndicator и не останавливается. Он не отображает изображения. Пожалуйста, у вас есть какие-нибудь идеи, почему?
5. Да. удаление записи работает нормально. Единственная проблема, которая у меня все еще есть, — это та, о которой я упоминал ранее. Добавляем изображение одно за другим. похоже, что приложение застряло на CircularProgressIndicator