การแชร์ไฟล์ Sharing files ด้วย Share Plus ใน Flutter

บทความใหม่ ไม่กี่เดือนก่อน โดย Ninenik Narkdee
flutter share plus

คำสั่ง การ กำหนด รูปแบบ ตัวอย่าง เทคนิค ลูกเล่น การประยุกต์ การใช้งาน เกี่ยวกับ flutter share_plus

ดูแล้ว 299 ครั้ง


จากเนื้อหาจากสองบทความที่แล้ว ที่เราดูเรื่องการบันทึกไฟล์เสียง
การบันทักไฟล์ภาพและไฟล์วิดีโอ เหล่านี้ล้วนเป็นการจัดการเกี่ยว
กับไฟล์ในแอปของเรา เนื้อหาตอนต่อไปนี้เราจะมาเพิ่มความสามารถ
ให้แอปของเราสามารถส่งไฟล์ต่างๆ ที่เราได้สร้างขึ้นไปใช้ยังภายนอก
แอปได้ นั่นคือ เราจะใช้ 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()),
    );
  }
}
 

ผลลัพธ์ที่ได้

 


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


   เพิ่มเติมเนื้อหา ครั้งที่ 1 วันที่ 28-09-2024


ดาวน์โหลดโค้ดตัวอย่าง สามารถนำไปประยุกต์ หรือ run ทดสอบได้

http://niik.in/download/flutter/demo_048_28092024_source.rar


กด Like หรือ Share เป็นกำลังใจ ให้มีบทความใหม่ๆ เรื่อยๆ น่ะครับ



อ่านต่อที่บทความ



ทบทวนบทความที่แล้ว









เนื้อหาที่เกี่ยวข้อง






เนื้อหาพิเศษ เฉพาะสำหรับสมาชิก

กรุณาล็อกอิน เพื่ออ่านเนื้อหาบทความ

ยังไม่เป็นสมาชิก

สมาชิกล็อกอิน



( หรือ เข้าใช้งานผ่าน Social Login )




URL สำหรับอ้างอิง





คำแนะนำ และการใช้งาน

สมาชิก กรุณา ล็อกอินเข้าระบบ เพื่อตั้งคำถามใหม่ หรือ ตอบคำถาม สมาชิกใหม่ สมัครสมาชิกได้ที่ สมัครสมาชิก


  • ถาม-ตอบ กรุณา ล็อกอินเข้าระบบ
  • เปลี่ยน


    ( หรือ เข้าใช้งานผ่าน Social Login )







เว็บไซต์ของเราให้บริการเนื้อหาบทความสำหรับนักพัฒนา โดยพึ่งพารายได้เล็กน้อยจากการแสดงโฆษณา โปรดสนับสนุนเว็บไซต์ของเราด้วยการปิดการใช้งานตัวปิดกั้นโฆษณา (Disable Ads Blocker) ขอบคุณครับ