เนื้อหาตอนต่อไปนี้ก็เป็นการประยุกต์รายการใน
ListView เป็นวิธีการที่ทำให้เราสามารถกดเลือกรายการ
เฉพาะที่ต้องการ มาทำงานต่อได้ จากตอนที่แล้ว เราสามารถ
เลือกที่จะลบแต่ละรายการโดยการปัดรายการนั้นไปทางซ้าย
เพื่อทำการลบ ทบทวนได้ที่บทความ
การใช้งาน Dismissible Widget ร่วมกับ ListView ใน Flutter http://niik.in/1067
https://www.ninenik.com/content.php?arti_id=1067 via @ninenik
*เนื้อหานี้ใช้เนื้อหาต่อเนื่องจากบทความ http://niik.in/1067
แต่สมมติว่าเราต้องการเลือกหลายรายการ และลบในครั้งเดียวจะทำอย่างไร จึงมาสู่เนื้อหาใน
บทความตอนนี้ ที่เราสามารถที่จะกดเลือกรายการโฟล์หรือโฟลเดอร์ที่ต้องการ จากนั้นก็ทำการลบ
รายการที่เลือกในครั้งเดียว โดยไม่ต้องไล่ลบทีละรายการ
ตัวอย่างผลลัพธ์และการทำงาน

เราเข้าไปในโฟลเดอร์ที่ต้องการเลือกรายการ จากนั้นกดค้างที่รายการใด รายการหนึ่ง ก็จะเริ่มเข้าสู่โหมด
การเลือกรายการ สามารถกดเลือกรายการอื่นๆ ต่อได้ เมื่อมีการเลือกรายการอย่างน้อย 1 รายการ ปุ่มลบทั้งหมด
จะแสดงตรงมุมบนขวา ถ้าเรากดที่ปุ่มนั้น ก็จะมีหน้าต่างแจ้งยืนยันการลบ หากยืนยันการลบ ก็จะทำการลบ
รายการที่เลือกทั้งหมดออกไป
ในขั้นตอนการเลือกรายการ หากเราเปลี่ยนใจ ก็กดที่รายการที่เลือกนั้นอีกครั้ง ก็จะเป็นการยกเลิกการเลือก
รายการนั้นๆ
แนวทางการเลือกรายการใน ListView
ใน ListView จะมีค่า index ที่เริ่มจาก 0 เป็นตัวระบุว่าเป็นรายการตัวที่เท่าไหร่ ซึ่งค่าจะไม่ซ้ำกัน ดังนั้น เรา
จะใช้ค่านี้ไปบันทึกไว้ว่าเลือกรายการใดมาบ้าง
เริ่มต้นให้เรากำหนดตัวแปร สำหรับเก็บ index ที่เลือก ดังนี้
1 2 | // ตัวแปรเก็บ index รายการที่เลือก List<int> _selectedItems = []; |
คำสั่งที่จะเช็คว่า มีการเลือกแล้วหรือไม่ ก็จะใช้วิธีนับจำนวนของรายการในตัวแปร _selectedItems ดังนี้
1 2 3 | // ใช้คำสั่ง _selectedItems.isNotEmpty // เช่น ถ้าไม่ว่าง ให้เป็น true ถ้าว่างยังไม่ได้เลือกรายการใดๆ ให้เป็น false _selectedItems.isNotEmpty ? true : false |
คำสั่งที่จะหาว่า มีรายการไหนอยู่ในรายการที่เลือกหรือไม่ จะใช้คำสั่งดังนี้
1 2 3 4 5 | // มี index อยู่ในรายการที่เลือกหรือไม่ _selectedItems.contains(index) // ตัวอย่าง สมมติเช่น // _selectedItems = [2,4,5]; // _selectedItems.contains(1) จะเท่ากับ false คือไม่มี |
ต่อไปส่วนของปุ่มลบทั้งหมดที่เลือก เราใช้ Visibility widget กำหนดเงื่อนไขการแสดงปุ่มนี้ก็ต่อเมื่อ
มีการเลือกรายการอย่างน้อย 1 รายการ ดังนี้
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | appBar: AppBar( title: Text( 'Home' ), actions: <Widget>[ // IconButton( onPressed: _newFolder, // สร้างโฟลเดอร์ใหม่ icon: FaIcon(FontAwesomeIcons.folderPlus), ), IconButton( onPressed: _newFile, // สร้างไฟล์ใหม่ icon: FaIcon(FontAwesomeIcons.fileAlt), ), Visibility( visible: _selectedItems.isNotEmpty ? true : false , // ถ้ามีการเลือกให้แสดง child: IconButton( onPressed: _deleteAll, icon: FaIcon(FontAwesomeIcons.trashAlt), ), ) ], ), |
ใน Flutter เราสามารถกำหนดเงื่อนไขโดยไม่ต้องใช้ Visibility widget ก็ได้ โดยสามารถใช้เป็นดังนี้
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | appBar: AppBar( title: Text( 'Home' ), actions: <Widget>[ // IconButton( onPressed: _newFolder, // สร้างโฟลเดอร์ใหม่ icon: FaIcon(FontAwesomeIcons.folderPlus), ), IconButton( onPressed: _newFile, // สร้างไฟล์ใหม่ icon: FaIcon(FontAwesomeIcons.fileAlt), ), if (_selectedItems.isNotEmpty) IconButton( onPressed: _deleteAll, icon: FaIcon(FontAwesomeIcons.trashAlt), ), ], ), |
จะเห็นว่าเราแทรกเงื่อนไข if เข้าไปตามตัวอย่าง ตัว widget แรกที่อยู่ต่อจากเงื่อนไข จะเป็นส่วนที่เข้าเงื่อนไข
ที่กำหนด วิธีนี้อาจจะสะดวก แต่บางครั้งก็ทำให้เราดูโค้ดเข้าใจได้ยากขึ้น ดังนั้นเราจึงใช้ Visibility widget แทน
การเพิ่ม และการลบ รายการที่เลือก
รูปแบบการทำงานเดิมของเราคือ การทำงานใน onTap หรือการเลือกที่รายการไฟล์ หรือโฟลเดอร์ สำหรับไฟล์
เรายังไม่ได้กำหนดการทำงานไว้ แต่สำหรับโฟลเดอร์ เรากำหนดว่า ถ้ากดแตะที่เราการ ก็จะเป็นการเปิดโฟลเดอร์
นั้นขึ้นมา ดังนั้นในขั้นตอนการเริ่มต้นการเลือกรายการ เราจะใช้เป็น onLongPress แทน หรือก็คือการกดค้างที่ราย
การนั้นๆ ก็จะเป็นการเริ่มการเลือกรายการ
อย่างไรก็ตาม เราคงไม่ต้องการเลือกรายการ โดยการกดค้างนานๆ ทุกรายการที่ต้องการ เราจึงต้องประยุกต์กับ
onTap ก็คือ ถ้ามีเลือก 1 รายการก็ให้สามารถเลือกโดยใช้ onTap แทนได้ ดูโค้ดส่วนของการทำงานการเลือก
และการลบรายการที่เลือก ดังนี้
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | // กรณีกดค้างนานๆ เราจะเริ่มการเลือก 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); // เพิ่มรายการที่เลือกนี้เข้าไป }); } } }, |
จะเห็นว่าส่วนของ onLongPress จะเป็นการเริ่มต้นการเลือก ดังนั้นเมื่อกดค้างที่รายการใด ก็จะเช็คดว่าถ้ายังไม่มี
การเลือกรายการใดๆ ก็จะทำการเพิ่มรายการที่กดค้าง เป็นรายการที่เลือกรายการแรก พอมีอย่างน้อย 1 รายการที่เลือก
แล้ว เราไม่จำเป็นต้องกดค้างทุกๆ รายการที่จะเลือก เพราะเราเอามากำหนดใน onTap แทน โดยเช็คว่า ถ้ามีเลือก
รายการใดๆ แล้ว สามารถกดปกติ เพื่อเลือกรายการนี้ได้
การเพิ่มรายการก็จะเป็นการ เพิ่ม index ของรายการที่เลือกเข้าไปในตัวแปร _selectedItems
1 | _selectedItems.add(index); // เพิ่มรายการที่เลือกนี้เข้าไป |
การลบรายการก็จะเป็นการ ลบ index ของรายการที่เลือกออกจากตัวแป _selectedItems
1 | _selectedItems.removeWhere((val) => val == index); |
สถานะรายการที่เลือก
ใน ListTile จะมีสถานะการเลือกใน property ที่ชื่อ selected รองรับค่า true หรือ false โดยถ้าเป็น true
สีของรายการก็จะเป็นสีของ theme หรือ primary color เราสามารถกำหนดสถานะ โดยเทียบว่า รายการนี้
ถูกเลือกแล้วหรือไม่ในรูปแบบ
1 2 3 4 | ListTile( selected: _selectedItems.contains(index) ? true : false , .... .. |
นอกจากนั้นเรายังสามารถกำหนดรูปไอคอนให้กับ trailing เป็นเครื่องหมายถูก ว่าเลือกแล้ว ได้ดังนี้
1 2 3 4 5 6 7 8 | ListTile( selected: _selectedItems.contains(index) ? true : false , .... .. trailing: Visibility( visible: _selectedItems.contains(index) ? true : false , child: FaIcon(FontAwesomeIcons.checkCircle), ), |
การลบรายการที่เลือกทั้งหมด
เนื่องจากตัวแปร _selectedItems จะเก็บ index ของข้อมูลไฟล์และโฟลเดอร์ที่เราจะลบทั้งหมด คำสั่งลบ
ข้อมูลทั้งหมดจะเป็นดังนี้
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | // ลบข้อมูลที่เลือกทั้งหมด 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!); }); } } |
เนื่องจากทั้งไฟล์และโฟลเดอร์มีคำสั่งลบในรุปแบบด้วยกันคือ delete() ดังนั้น ถ้าเราสามารถอ้างอิงข้อมูล
ผ่าน index ได้ ก็สามารถวนลูปลบรายการได้ง่าย เหมือนโค้ดด้านบน
ตอนนี้เหลือส่วนสุดท้ายก็คือ การล้างค่าการเลือก รายการ เราสามารถยกเลิกรายการที่เลือก โดยกดซ้ำ
อีกครั้งในรายการที่เลือก แต่ถ้าสมมติว่า เราเลือกไว้แล้ว แต่เปลี่ยน โฟลเดอร์อื่น กรณีนี้ค่าของ _selectedItems
ยังค้างอยู่ ดังนั้นเมื่อเปลี่ยนโฟลเดอร์ที่กำลังใช้งาน เราต้องทำการ clear รายการที่เลือก เพื่อเลือกใหม่เสมอ
ด้วยคำสั่งดังนี้
1 | _selectedItems.clear(); // ล้างค่าการเลือกทั้งหมด |
คำสั่งนี้เราจะเพิ่มเข้าไปในคำสั่ง _setPath() ให้ทำงานทุกครั้งที่เปลี่ยนโฟลเดอร์หรือเปิดโฟลเดอร์ใหม่
ไฟล์ explorer.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 | import 'dart:io' ; import 'package:flutter/material.dart' ; import 'package:font_awesome_flutter/font_awesome_flutter.dart' ; import 'package:path_provider/path_provider.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!); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text( 'Explorer' ), actions: <Widget>[ // IconButton( onPressed: _newFolder, // สร้างโฟลเดอร์ใหม่ icon: FaIcon(FontAwesomeIcons.folderPlus), ), IconButton( onPressed: _newFile, // สร้างไฟล์ใหม่ icon: FaIcon(FontAwesomeIcons.fileAlt), ), if (_selectedItems.isNotEmpty) IconButton( onPressed: _deleteAll, icon: FaIcon(FontAwesomeIcons.trashAlt), ), ], ), 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 { return Container(); } }, separatorBuilder: (BuildContext context, int index) => const Divider(height: 1,), ) : const Center(child: Text( 'No items' )), // กรณีไม่มีรายการ ), ], ), ); } } |
แนวทางนี้สามารถนำไปปรับประยุกต์เพิ่มเติมตามต้องการได้ หวังว่าจะมีประโยชน์ไม่มากก็น้อย
เนื้อหาตอนหน้าจะเป็นอะไร รอติดตาม