จากเนื้อหาจากสองบทความที่แล้ว ที่เราดูเรื่องการบันทึกไฟล์เสียง
การบันทักไฟล์ภาพและไฟล์วิดีโอ เหล่านี้ล้วนเป็นการจัดการเกี่ยว
กับไฟล์ในแอปของเรา เนื้อหาตอนต่อไปนี้เราจะมาเพิ่มความสามารถ
ให้แอปของเราสามารถส่งไฟล์ต่างๆ ที่เราได้สร้างขึ้นไปใช้ยังภายนอก
แอปได้ นั่นคือ เราจะใช้ plugin ที่ชื่อว่า share_plus ในการส่งไฟล์
หรือแชร์ไฟล์ไปใช้งานนอกแอปหรือไปใช้งานยังแอปอื่นหรือส่งให้ผู้ใช้คน
อื่นๆ เหล่านี้เป็นต้น
เนื้อหานี้ต่อยอดจากตอนที่แล้ว มีโค้ดท้ายบทความ
การบันทึกรูปภาพ Image หรือวิดีโอ Video ใน Flutter http://niik.in/1118
ติดตั้ง package ที่จำเป็นเพิ่มเติม ตามรายการด้านล่าง
แพ็กเก็จที่จำเป็นต้องติดตั้งเพิ่มเติม สำหรับการทำงานมีดังนี้
share_plus: ^10.0.2
การใช้งาน Share Plus เพื่อแชร์ไฟล์
สำหรับการใช้งาน Share Plus นั้น มีรูปแบบและวิธีการใช้งานที่ไม่ยุ่งยากซับซ้อน ใช้คำสั่ง
เพียงไม่กี่บรรทัดก็สามารถทำงานได้ รองรับการแชร์เฉพาะข้อความ การแชร์ไฟล์หนึ่งไฟล์ หรือ
หลายๆ ไฟล์พร้อมกัน
ตัวอย่างการแชร์ข้อความ
ใช้รูปแบบดังนี้
ElevatedButton(
onPressed: () async {
final result = await Share.share('check out my website
https://www.ninenik.com');
if (result.status == ShareResultStatus.success) {
print('xdebug: Thank you for sharing my website!');
}
},
child: Text('Share Text'),
),
กรณีให้รองรับหัวข้อ สำหรับส่งผ่านอีเมล
สามารถเพิ่มส่วนของ Subject เข้าไปดังนี้
ElevatedButton(
onPressed: () async {
final result = await Share.share(
'check out my website https://www.ninenik.com',
subject: 'Look what I made!');
if (result.status == ShareResultStatus.success) {
print('xdebug: Thank you for sharing my website!');
}
},
child: Text('Share Text'),
),
ตัวอย่างการแชร์ไปยังอีเมล

การแชร์ไฟล์ต่างๆ
ElevatedButton(
onPressed: () async {
final result = await Share.shareXFiles(
[XFile('${directory.path}/image.jpg')],
text: 'Great picture');
if (result.status == ShareResultStatus.success) {
print('xdebug: Thank you for sharing the picture!');
}
},
child: Text('Share File'),
),
กรณีแชร์พร้อมกันหลายไฟล์ เราก็ส่งข้อมูลไฟล์เป็น List หรือ array เข้าไปตัวอย่าง
final result = await Share.shareXFiles(
[
XFile('${directory.path}/image.jpg'),
XFile('${directory.path}/image2.jpg'),
XFile('${directory.path}/video1.mp4')
XFile('${directory.path}/video1.mp4')
],
text: 'Great File');
หรือกรณีเรามีการกับ path ของไฟล์ที่เลือกเป็น List ไว้อยู่แล้ว สามารถวนลูป รวมเป็น List
รายการ XFile เพื่อใช้แชร์ได้ ตัวอย่างเช่น
// สรัางตัวแปรเก็บรายการไฟล์ที่จะแขร์
List<XFile> _fileShare = <XFile>[];
// วนลูปจากรายการไฟล์ที่เลือก _selectedItems
// _folders![index]! สมมติเป็นตัวอ้างอิงไฟล์
_selectedItems.forEach((index) async {
_fileShare.add(XFile(_folders![index]!.path));
});
if(_fileShare.isNotEmpty){
final result = await Share.shareXFiles(_fileShare);
if (result.status == ShareResultStatus.success) {
print('xdebug: Thank you for sharing!');
}
}
สถานะการแชร์ ShareResultStatus
ประกอบด้วยสถานะดังนี้:
- success: การแชร์สำเร็จ ผู้ใช้ได้แชร์ข้อมูลเรียบร้อยแล้ว
- dismissed: การแชร์ถูกยกเลิก (ผู้ใช้เปิดหน้าแชร์แต่ไม่ได้แชร์ข้อมูล และปิดออกไป)
- unavailable: ฟีเจอร์การแชร์ไม่สามารถใช้งานได้บนอุปกรณ์นี้
เราสามารถใช้งาน ShareResultStatus เพื่อจัดการสถานะของการแชร์ได้ดังนี้
final result = await Share.share(
'Check out my website https://www.ninenik.com',
subject: 'Look what I made!',
);
if (result.status == ShareResultStatus.success) {
print('xdebug: Sharing successful');
} else if (result.status == ShareResultStatus.dismissed) {
print('xdebug: Sharing dismissed');
} else if (result.status == ShareResultStatus.unavailable) {
print('xdebug: Sharing unavailable on this device');
}
การแชร์จากข้อมูลไฟล์ Share Data
เราสามารถสร้างไฟล์ที่แชร์ได้จากข้อมูลที่สร้างขึ้นแบบไดนามิกโดยไม่จำเป็นต้องมีไฟล์จริงอยู่ในระบบ
ไฟล์ก่อน โดยใช้คลาส XFile และเมธอด fromData จากแพ็กเกจ share_plus
ตัวอย่างแชร์จากไฟล์ใน assets
final data = await rootBundle.load('assets/flutter_logo.png');
final buffer = data.buffer;
final shareResult = await Share.shareXFiles(
[
XFile.fromData(
buffer.asUint8List(data.offsetInBytes, data.lengthInBytes),
name: 'flutter_logo.png',
mimeType: 'image/png',
),
],
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
);
ตัวอย่างแชร์ข้อมูลไฟล์ โดยสร้างเป็น pdf เพื่อส่งออก
// สร้างไฟล์จากข้อมูลที่เป็นไบต์
final XFile xFile = XFile.fromData(
fileData,
mimeType: 'application/pdf', // ระบุประเภทของไฟล์
);
// แชร์ไฟล์
final shareResult = await Share.shareXFiles([xFile],
text: 'Check out this file!'
fileNameOverrides: [fileName],
);
ตัวอย่างการแชร์จากข้อมูลที่เป็นข้อความ
final data = utf8.encode(text);
final shareResult = await Share.shareXFiles(
[
XFile.fromData(
data,
mimeType: 'text/plain',
),
],
fileNameOverrides: [fileName],
);
เราเพิ่มตัวอย่างการประยุกต์ใช้งานเข้าไปในไฟล์ explorer.dart โดยเมื่อกดค้างเพื่อเลือกไฟล์
จะมีไอคอนแชร์ขึ้นมา เราสามารถได้หลายไฟล์หรือไฟล์เดียวก็ได้เพื่อแชร์ไฟล์จากแอปของเรา
โค้ดส่วนที่เพิ่มเข้าไป เมื่อมีรายการเลือกไฟล์ และกดปุ่มแชร์ไฟล์
if (_selectedItems.isNotEmpty)
IconButton(
onPressed: () async {
print("xdebug: share");
try {
List<XFile> _fileShare = <XFile>[];
_selectedItems.forEach((index) async {
_fileShare.add(XFile(_folders![index]!.path));
});
if(_fileShare.isNotEmpty){
final result = await Share.shareXFiles(_fileShare);
if (result.status == ShareResultStatus.success) {
print('xdebug: Thank you for sharing!');
}
}
} catch (e) {
print(e);
}
},
icon: FaIcon(FontAwesomeIcons.share),
),
ไฟล์ explorer.dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:path_provider/path_provider.dart';
import 'package:file_picker/file_picker.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:share_plus/share_plus.dart';
import 'audioplayer.dart';
import 'fullscreenvideo.dart';
import 'viewphotoscreen.dart';
class Explorer extends StatefulWidget {
static const routeName = '/explorer';
const Explorer({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return _ExplorerState();
}
}
class _ExplorerState extends State<Explorer> {
List<FileSystemEntity?>? _folders;
String _currentPath = ''; // เก็บ path ปัจจุบัน
Directory? _currentFolder; // เก็บ โฟลเดอร์ที่กำลังใช้งาน
// ตัวแปรเก็บ index รายการที่เลือก
List<int> _selectedItems = [];
@override
void initState() {
// TODO: implement initState
super.initState();
_loadFolder();
}
void _loadFolder() async {
// ข้อมูลเกี่ยวกับโฟลเดอร์ Directory ต่างๆ
// final tempDirectory = await getTemporaryDirectory();
// final appSupportDirectory = await getApplicationSupportDirectory();
final appDocumentsDirectory = await getApplicationDocumentsDirectory();
// final externalDocumentsDirectory = await getExternalStorageDirectory();
// final externalStorageDirectories =
// await getExternalStorageDirectories(type: StorageDirectory.music);
// final externalCacheDirectories = await getExternalCacheDirectories();
/* print(tempDirectory);
print(appSupportDirectory);
print(appDocumentsDirectory);
print(externalDocumentsDirectory);
print(externalCacheDirectories);
print(externalStorageDirectories); */
// เมื่อโหลดขึ้นมา เาจะเปิดโฟลเดอร์ของ package เป้นโฟลเดอร์หลัก
_currentFolder = appDocumentsDirectory.parent;
_currentPath = appDocumentsDirectory.parent.path;
final myDir = Directory(_currentPath);
setState(() {
_folders = myDir.listSync(recursive: false, followLinks: false);
});
}
// เปิดโฟลเดอร์ และแสดงรายการในโฟลเดอร์
void _setPath(dir) async {
_currentFolder = dir;
_currentPath = dir.path;
final myDir = Directory(_currentPath);
try {
setState(() {
_folders = myDir.listSync(recursive: false, followLinks: false);
});
} catch (e) {
print(e);
}
_selectedItems.clear(); // ล้างค่าการเลือกทั้งหมด
}
// คำสังลบไฟล์
void _deleteFile(path) async {
final deletefile = File(path); // กำหนด file object
final isExits = await deletefile.exists(); // เช็คว่ามีไฟล์หรือไม่
if (isExits) {
// ถ้ามีไฟล์
try {
await deletefile.delete();
} catch (e) {
print(e);
}
}
// โหลดข้อมูลใหม่อีกครั้ง
setState(() {
_setPath(_currentFolder!);
});
}
// คำสั่งลบโฟลเดอร์
void _deleteFolder(path) async {
final deleteFolder = Directory(path); // สร้าง directory object
var isExits = await deleteFolder.exists(); // เช็คว่ามีแล้วหรือไม่
if (isExits) {
// ถ้ามีโฟลเดอร์
try {
await deleteFolder.delete(recursive: true);
} catch (e) {
print(e);
}
}
// โหลดข้อมูลใหม่อีกครั้ง
setState(() {
_setPath(_currentFolder!);
});
}
// ลบข้อมูลที่เลือกทั้งหมด
void _deleteAll() async {
bool _confirm; // สร้างตัวแปรรับค่า ยืนยันการลบ
_confirm = await showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text("Confirm"),
content: const Text("Are you sure you wish to delete selected item?"),
actions: <Widget>[
ElevatedButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text("DELETE")),
ElevatedButton(
onPressed: () => Navigator.of(context).pop(false),
child: const Text("CANCEL"),
),
],
);
},
);
if (_confirm) {
// ถ้ายืนยันการลบ เป็น true
try {
// วนลูป index แล้วอ้างอึงข้อมูลไฟล์ จากนั้นใช้คำสั่ง delete() แบบรองรับการลบข้อมูลด้านในถ้ามี
// ในกรณีเป็นโฟลเดอร์
_selectedItems.forEach((index) async {
await _folders![index]!.delete(recursive: true);
});
} catch (e) {
print(e);
}
// โหลดข้อมูลใหม่อีกครั้ง
setState(() {
_setPath(_currentFolder!);
});
}
}
// จำลองสร้างไฟล์ใหม่
void _newFile() async {
String newFile = "${_currentFolder!.path}/myfile.txt";
final myfile = File(newFile); // กำหนด file object
final isExits = await myfile.exists(); // เช็คว่ามีไฟล์หรือไม่
if (!isExits) {
// ถ้ายังไม่มีไฟล์
try {
// สร้างไฟล์ text
var file = await myfile.writeAsString('Hello World');
print(file);
} catch (e) {
print(e);
}
}
// โหลดข้อมูลใหม่อีกครั้ง
setState(() {
_setPath(_currentFolder!);
});
}
// คำสั่งจำลองการสร้างโฟลเดอร์
void _newFolder() async {
String newFolder = "${_currentFolder!.path}/mydir";
final myDir = Directory(newFolder); // สร้าง directory object
var isExits = await myDir.exists(); // เช็คว่ามีแล้วหรือไม่
if (!isExits) {
// ถ้ายังไม่มีสร้างโฟลเดอร์ขึ้นมาใหม่
try {
var directory = await Directory(newFolder).create(recursive: true);
print(directory);
} catch (e) {
print(e);
}
}
// โหลดข้อมูลใหม่อีกครั้ง
setState(() {
_setPath(_currentFolder!);
});
}
// คำสั่ง import ไฟล์ผ่าน file_picker
void _importFile() async {
try {
if (await Permission.storage.request().isGranted) {
// FilePickerResult? result = await FilePicker.platform.pickFiles();
FilePickerResult? result =
await FilePicker.platform.pickFiles(allowMultiple: true);
if (result != null) {
// File _file = File(result.files.single.path!);
// List<File> _file = result.paths.map((path) => File(path!)).toList();
result.files.forEach((file) async {
// วนลุป copy ไฟล์ไปยังโฟลเดอร์ที่ใช้งาน
// กำหนด path ของไฟล์ใหม่ แล้วทำการ copy จาก cache ไปไว้ใน app
String newPath = "${_currentFolder!.path}/${file.name}";
var cachefile = File(file.path!); // กำหนด file object
await cachefile.copy(newPath);
// var newFile = await cachefile.copy(newPath);
// ข้อมูลไฟล์
print(file.name);
print(file.bytes);
print(file.size);
print(file.extension);
print(file.path);
// ตัวสุดท้ายทำงานเสร็จ
if (file == result.files.last) {
// โหลดข้อมูลใหม่อีกครั้ง
setState(() {
_setPath(_currentFolder!);
});
}
});
} else {
print("User canceled the picker");
}
} else {
print("Storage permission is denied.");
}
} catch (e) {
print(e);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Explorer'),
actions: <Widget>[
IconButton(
onPressed: _importFile, //
icon: FaIcon(FontAwesomeIcons.fileImport),
),
IconButton(
onPressed: _newFolder, // สร้างโฟลเดอร์ใหม่
icon: FaIcon(FontAwesomeIcons.folderPlus),
),
IconButton(
onPressed: _newFile, // สร้างไฟล์ใหม่
icon: FaIcon(FontAwesomeIcons.fileLines),
),
if (_selectedItems.isNotEmpty)
IconButton(
onPressed: () async {
print("xdebug: share");
try {
List<XFile> _fileShare = <XFile>[];
_selectedItems.forEach((index) async {
_fileShare.add(XFile(_folders![index]!.path));
});
if(_fileShare.isNotEmpty){
final result = await Share.shareXFiles(_fileShare);
if (result.status == ShareResultStatus.success) {
print('xdebug: Thank you for sharing!');
}
}
} catch (e) {
print(e);
}
},
icon: FaIcon(FontAwesomeIcons.share),
),
if (_selectedItems.isNotEmpty)
IconButton(
onPressed: _deleteAll,
icon: FaIcon(FontAwesomeIcons.trashCan),
),
],
),
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
ListTile(
leading: FaIcon(FontAwesomeIcons.angleLeft),
title: Text(
'${_currentPath.replaceAll('/data/user/0/com.example.demo_app', '/')}'),
onTap: () {
_setPath(_currentFolder!.parent);
}),
Expanded(
child: _folders != null // เมื่อไม่ใช่ค่า null
? ListView.separated(
// กรณีมีรายการ แสดงปกติ
itemCount: _folders == null ? 0 : _folders!.length,
itemBuilder: (context, index) {
var isFolder = _folders![index] is Directory
? true
: false; // เช็คว่าเป็นโฟลเดอร์
var isFile = _folders![index] is File
? true
: false; // เช็คว่าเป็นไฟล์
if (_folders![index] != null) {
// เอาเฉพาะชื่อหลัง / ตัวสุดท้าย
String fileName =
_folders![index]!.path.split('/').last;
return Dismissible(
direction: DismissDirection.horizontal,
key: UniqueKey(),
// dismissThresholds: const { DismissDirection.endToStart:1.0, DismissDirection.startToEnd:1.0},
confirmDismiss: (direction) async {
return await showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text("Confirm"),
content: const Text(
"Are you sure you wish to delete this item?"),
actions: <Widget>[
ElevatedButton(
onPressed: () =>
Navigator.of(context).pop(true),
child: const Text("DELETE")),
ElevatedButton(
onPressed: () =>
Navigator.of(context).pop(false),
child: const Text("CANCEL"),
),
],
);
},
);
},
onDismissed: (direction) {
// ปัดไปทางขวา - บนลงล่าง
if (direction == DismissDirection.startToEnd) {}
// ปัดไปซ้าย - ล่างขึ้นบน
if (direction == DismissDirection.endToStart) {
try {
setState(() {
if (isFile) {
// ถ้าเป็นไฟล์ ส่ง path ไฟล์ไปลบ
_deleteFile(_folders![index]!.path);
}
if (isFolder) {
// ถ้าเป็นโฟลเดอร์ส่ง path โฟลเดอร์ไปลบ
_deleteFolder(_folders![index]!.path);
}
// ต้องลบข้อมูลก่อน แล้วค่อยลบรายการในลิส
_folders!.removeAt(index);
});
} catch (e) {
print(e);
}
}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('$index dismissed')));
},
background: Container(
color: Colors.green,
),
secondaryBackground: Container(
color: Colors.red,
child: Align(
alignment: Alignment.centerRight,
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: 10.0),
child: FaIcon(FontAwesomeIcons.trashAlt),
))),
child: ListTile(
selected:
_selectedItems.contains(index) ? true : false,
leading: isFolder
? FaIcon(FontAwesomeIcons.solidFolder)
: FaIcon(FontAwesomeIcons.file),
trailing: Visibility(
visible: _selectedItems.contains(index)
? true
: false,
child: FaIcon(FontAwesomeIcons.checkCircle),
),
title: Text('${fileName}'),
onLongPress: () {
if (!_selectedItems.contains(index)) {
setState(() {
_selectedItems.add(index);
});
}
},
onTap: (isFolder == true)
? () {
// กรณีเป้นโฟลเดอร์
if (_selectedItems.contains(index)) {
setState(() {
_selectedItems.removeWhere(
(val) => val == index);
});
} else {
if (_selectedItems.isNotEmpty) {
setState(() {
_selectedItems.add(index);
});
} else {
_setPath(_folders![
index]!); // ถ้ากด ให้ทำคำสั่งเปิดโฟลเดอร์
}
}
}
: () {
if (_selectedItems.contains(index)) {
setState(() {
_selectedItems.removeWhere(
(val) => val == index);
});
} else {
if (_selectedItems.isNotEmpty) {
setState(() {
_selectedItems.add(index);
});
} else {
List<String> audioExtensions = [
'.mp3',
'.wav',
'.flac',
'.aac',
'.ogg'
];
List<String> videoExtensions = [
'mp4'
];
List<String> photoExtensions = [
'png',
'jpeg',
'jpg'
];
if (audioExtensions.any((ext) =>
fileName
.toLowerCase()
.endsWith(ext))) {
/* if (fileName
.toLowerCase()
.endsWith('.wav')) { */
print(
'debug: The file is an audio file.');
print(
"debug: path ${_currentPath}");
String fullPathFile =
'$_currentPath/$fileName';
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
AudioPlayers(
fileName: fullPathFile,
),
),
);
} else if (videoExtensions.any(
(ext) => fileName
.toLowerCase()
.endsWith(ext))) {
print(
'debug: The file is a video file.');
print(
"debug: path ${_currentPath}");
String fullPathFile =
'$_currentPath/$fileName';
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
FullScreenVideo(
fullPathFile),
),
);
} else if (photoExtensions.any(
(ext) => fileName
.toLowerCase()
.endsWith(ext))) {
print(
'debug: The file is an image file.');
print(
"debug: path ${_currentPath}");
String fullPathFile =
'$_currentPath/$fileName';
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ViewPhotoScreen(
photos: fullPathFile,),
),
);
} else {
print(
'debug: The file is not an image audio or video file.');
}
}
}
}, // กรณีเป็นไฟล์
));
} else {
return Container();
}
},
separatorBuilder: (BuildContext context, int index) =>
const Divider(
height: 1,
),
)
: const Center(child: Text('No items')), // กรณีไม่มีรายการ
),
],
),
);
}
}
ผลลัพธ์ที่ได้

จากตัวอย่างผลลัพธ์เราสามารถเลือกไฟล์ที่จะแชร์พร้อมกันได้หลายไฟล์ อย่างไรก็ดี เพื่อให้เห็นภาพ
หรือเป็นแนวทางการประยุกต์ เราจะสร้างหน้าสำหรับแสดงข้อมูลไฟล์ในโฟลเดอร์ app_flutter โดย
ที่ถ้าเป็นไฟล์ภาพ เสียง และ วิดีโอ ก็จะสามารถกด เพื่อเปิดไฟล์นั้นได้ และถ้ากดค้างที่ไฟล์นั้น ก็จะเป็น
การเริ่มต้นการเลือกไฟล์ เมื่อเลือก 1 รายการแล้ว สามารถกดไปที่รายการอื่นๆ เพื่อเลือกไฟล์เพิ่ม หรือ
กดซ้ำที่รายการที่เลือกแล้ว เพื่อยกเลิกการเลือกไฟล์ได้ โดยไฟล์ที่เราเลือก สามารถทำการแชร์ได้ และ
สามารถลบได้ โดยจะมีปุ่มแสดงตรงมุมขวา
ไฟล์ mediaexplorer.dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:share_plus/share_plus.dart';
import 'audioplayer.dart';
import 'fullscreenvideo.dart';
import 'viewphotoscreen.dart';
class MediaExplorer extends StatefulWidget {
static const routeName = '/gallery';
const MediaExplorer({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() {
return _MediaExplorerState();
}
}
class _MediaExplorerState extends State<MediaExplorer> {
List<FileSystemEntity>? _files;
List<int> _selectedItems = [];
String _currentPath = '';
@override
void initState() {
super.initState();
_requestPermissionsAndLoadMedia();
}
// ฟังก์ชันสำหรับขอสิทธิ์และโหลดไฟล์
void _requestPermissionsAndLoadMedia() async {
// ขอสิทธิ์การเข้าถึงไฟล์สื่อ
PermissionStatus status = await Permission.storage.request();
if (status.isGranted) {
// ถ้าได้รับสิทธิ์แล้วให้โหลดไฟล์
_loadMediaFiles();
} else {
// กรณีไม่ได้รับสิทธิ์
print("Permission Denied");
}
}
void _loadMediaFiles() async {
Directory appDir = await getApplicationDocumentsDirectory();
String folderPath = '${appDir.path}'; // เปลี่ยนเป็นโฟลเดอร์ที่ต้องการ
Directory folderDir = Directory(folderPath);
setState(() {
_currentPath = folderDir.path;
_files = folderDir.listSync()
.where((file) => file is File)
.toList();
});
}
Widget _buildIcon(FileSystemEntity file) {
String ext = file.path.split('.').last.toLowerCase();
if (['jpg', 'jpeg', 'png', 'gif'].contains(ext)) {
return FaIcon(FontAwesomeIcons.image, color: Colors.blue);
} else if (['mp3', 'wav'].contains(ext)) {
return FaIcon(FontAwesomeIcons.music, color: Colors.green);
} else if (['mp4', 'avi'].contains(ext)) {
return FaIcon(FontAwesomeIcons.video, color: Colors.red);
} else {
return FaIcon(FontAwesomeIcons.file, color: Colors.grey);
}
}
void _onLongPress(int index) {
setState(() {
if (_selectedItems.contains(index)) {
_selectedItems.remove(index);
} else {
_selectedItems.add(index);
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Media Explorer'),
actions: [
if (_selectedItems.isNotEmpty)
IconButton(
onPressed: () async {
print("xdebug: share");
try {
List<XFile> _fileShare = <XFile>[];
_selectedItems.forEach((index) async {
_fileShare.add(XFile(_files![index]!.path));
});
if(_fileShare.isNotEmpty){
final result = await Share.shareXFiles(_fileShare);
if (result.status == ShareResultStatus.success) {
print('xdebug: Thank you for sharing!');
}
}
} catch (e) {
print(e);
}
},
icon: FaIcon(FontAwesomeIcons.share),
),
if(_selectedItems.isNotEmpty)
IconButton(
icon: FaIcon(FontAwesomeIcons.trashCan),
onPressed: () async {
bool _confirm; // สร้างตัวแปรรับค่า ยืนยันการลบ
_confirm = await showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text("Confirm"),
content: const Text("Are you sure you wish to delete selected item?"),
actions: <Widget>[
ElevatedButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text("DELETE")),
ElevatedButton(
onPressed: () => Navigator.of(context).pop(false),
child: const Text("CANCEL"),
),
],
);
},
);
if (_confirm) {
// ถ้ายืนยันการลบ เป็น true
try {
// โหลดข้อมูลใหม่อีกครั้ง
setState(() {
_selectedItems.forEach((index) {
File file = _files![index] as File;
file.deleteSync();
});
_selectedItems.clear();
_loadMediaFiles(); // Reload after deletion
});
} catch (e) {
print(e);
}
}
},
),
]
),
body: _files != null
? GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 4.0,
mainAxisSpacing: 4.0,
),
itemCount: _files?.length ?? 0,
itemBuilder: (context, index) {
FileSystemEntity file = _files![index];
bool isSelected = _selectedItems.contains(index);
String ext = file.path.split('.').last.toLowerCase();
return GestureDetector(
onTap: (){
// ถ้ามีการเลือกไฟล์
if(_selectedItems.isNotEmpty){
_onLongPress(index);
}else{ // ถ้าเป็นการกดเปิดปกติ
if (['jpg', 'jpeg', 'png', 'gif'].contains(ext)) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ViewPhotoScreen(
photos: file.path,),
),
);
} else if (['mp3', 'wav'].contains(ext)) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
AudioPlayers(
fileName: file.path,
),
),
);
} else if (['mp4', 'avi'].contains(ext)) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
FullScreenVideo(
file.path),
),
);
} else {
}
}
},
onLongPress: () => _onLongPress(index),
child: GridTile(
footer: GridTileBar(
backgroundColor: isSelected ? Colors.blue.withOpacity(0.5) : Colors.black54,
title: Text(
file.path.split('/').last,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 12),
),
),
child: Container(
color: isSelected ? Colors.blue.withOpacity(0.3) : Colors.white,
child: Center(child: _buildIcon(file)),
),
),
);
},
)
: Center(child: CircularProgressIndicator()),
);
}
}
ผลลัพธ์ที่ได้

เท่านี้เราก็สามารถทำระบบจัดการไฟล์ที่เราสร้างหรือโหลดเข้ามาใช้งานได้ และยังสามารถแชร์ไฟล์นั้น
ไปใช้งานยังแอปอื่นหรือส่งให้คนอื่นได้
หว้งว่าเนื้อหานี้จะเป็นแนวทางนำไปปรับประยุกต์ใช้งานต่อไปได้ไม่มากก็น้อย สำหรับในตอนหน้า
จะเป็นอะไรรอติดตาม