file_info_dialog.dart 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. import "dart:io";
  2. import "package:exif/exif.dart";
  3. import "package:flutter/cupertino.dart";
  4. import "package:flutter/material.dart";
  5. import "package:photos/core/configuration.dart";
  6. import "package:photos/ente_theme_data.dart";
  7. import "package:photos/models/file.dart";
  8. import "package:photos/models/file_type.dart";
  9. import "package:photos/services/collections_service.dart";
  10. import "package:photos/ui/viewer/file/exif_info_dialog.dart";
  11. import "package:photos/utils/date_time_util.dart";
  12. import "package:photos/utils/exif_util.dart";
  13. import "package:photos/utils/file_util.dart";
  14. import "package:photos/utils/magic_util.dart";
  15. import "package:photos/utils/toast_util.dart";
  16. class FileInfoWidget extends StatefulWidget {
  17. final File file;
  18. const FileInfoWidget(
  19. this.file, {
  20. Key key,
  21. }) : super(key: key);
  22. @override
  23. State<FileInfoWidget> createState() => _FileInfoWidgetState();
  24. }
  25. class _FileInfoWidgetState extends State<FileInfoWidget> {
  26. Map<String, IfdTag> _exif;
  27. final Map<String, dynamic> _exifData = {
  28. "focalLength": null,
  29. "fNumber": null,
  30. "resolution": null,
  31. "takenOnDevice": null,
  32. "exposureTime": null,
  33. "ISO": null,
  34. "megaPixels": null
  35. };
  36. bool _isImage = false;
  37. Color infoColor;
  38. @override
  39. void initState() {
  40. debugPrint('file_info_dialog initState');
  41. _isImage = widget.file.fileType == FileType.image ||
  42. widget.file.fileType == FileType.livePhoto;
  43. if (_isImage) {
  44. getExif(widget.file).then((exif) {
  45. setState(() {
  46. _exif = exif;
  47. });
  48. });
  49. }
  50. super.initState();
  51. }
  52. @override
  53. Widget build(BuildContext context) {
  54. final file = widget.file;
  55. final dateTime = DateTime.fromMicrosecondsSinceEpoch(file.creationTime);
  56. final dateTimeForUpdationTime =
  57. DateTime.fromMicrosecondsSinceEpoch(file.updationTime);
  58. infoColor =
  59. Theme.of(context).colorScheme.onSurface.withOpacity(0.85); //remove
  60. if (_isImage && _exif != null) {
  61. _generateExifForDetails(_exif);
  62. }
  63. final bool showExifListTile = _exifData["focalLength"] != null ||
  64. _exifData["fNumber"] != null ||
  65. _exifData["takenOnDevice"] != null ||
  66. _exifData["exposureTime"] != null ||
  67. _exifData["ISO"] != null;
  68. var listTiles = <Widget>[
  69. ListTile(
  70. leading: const Padding(
  71. padding: EdgeInsets.only(top: 8, left: 6),
  72. child: Icon(Icons.calendar_today_rounded),
  73. ),
  74. title: Text(
  75. getFullDate(
  76. DateTime.fromMicrosecondsSinceEpoch(file.creationTime),
  77. ),
  78. ),
  79. subtitle: Text(
  80. getTimeIn12hrFormat(dateTime) + " " + dateTime.timeZoneName,
  81. style: Theme.of(context)
  82. .textTheme
  83. .bodyText2
  84. .copyWith(color: Colors.black.withOpacity(0.5)),
  85. ),
  86. trailing: (widget.file.ownerID == null ||
  87. widget.file.ownerID ==
  88. Configuration.instance.getUserID()) &&
  89. widget.file.uploadedFileID != null
  90. ? IconButton(
  91. onPressed: () {
  92. PopupMenuItem(
  93. value: 2,
  94. child: Row(
  95. children: [
  96. Icon(
  97. Platform.isAndroid
  98. ? Icons.access_time_rounded
  99. : CupertinoIcons.time,
  100. color: Theme.of(context).iconTheme.color,
  101. ),
  102. const Padding(
  103. padding: EdgeInsets.all(8),
  104. ),
  105. const Text("Edit time"),
  106. ],
  107. ),
  108. );
  109. },
  110. icon: const Icon(Icons.edit),
  111. )
  112. : const SizedBox.shrink(),
  113. ),
  114. const DividerWithPadding(),
  115. ListTile(
  116. leading: const Padding(
  117. padding: EdgeInsets.only(top: 8, left: 6),
  118. child: Icon(
  119. Icons.image,
  120. ),
  121. ),
  122. title: Text(
  123. file.getDisplayName(),
  124. ),
  125. subtitle: Row(
  126. children: [
  127. Padding(
  128. padding: const EdgeInsets.only(right: 10),
  129. child: _getFileSize(),
  130. ),
  131. !_isImage ? _getVideoDuration() : const SizedBox.shrink(),
  132. ],
  133. ),
  134. trailing: file.uploadedFileID == null ||
  135. file.ownerID != Configuration.instance.getUserID()
  136. ? const SizedBox.shrink()
  137. : IconButton(
  138. onPressed: () async {
  139. await editFilename(context, file);
  140. setState(() {});
  141. },
  142. icon: const Icon(Icons.edit),
  143. ),
  144. ),
  145. const DividerWithPadding(),
  146. ListTile(
  147. leading: const Padding(
  148. padding: EdgeInsets.only(left: 6),
  149. child: Icon(Icons.folder_outlined),
  150. ),
  151. title: Text(
  152. file.deviceFolder ??
  153. CollectionsService.instance
  154. .getCollectionByID(file.collectionID)
  155. .name,
  156. ),
  157. ),
  158. const DividerWithPadding(),
  159. showExifListTile
  160. ? ListTile(
  161. leading: const Padding(
  162. padding: EdgeInsets.only(left: 6),
  163. child: Icon(Icons.camera_rounded),
  164. ),
  165. title: Text(_exifData["takenOnDevice"] ?? "--"),
  166. subtitle: Row(
  167. children: [
  168. _exifData["fNumber"] != null
  169. ? Padding(
  170. padding: const EdgeInsets.only(right: 10),
  171. child: Text('ƒ/' + _exifData["fNumber"].toString()),
  172. )
  173. : const SizedBox.shrink(),
  174. _exifData["exposureTime"] != null
  175. ? Padding(
  176. padding: const EdgeInsets.only(right: 10),
  177. child: Text(_exifData["exposureTime"]),
  178. )
  179. : const SizedBox.shrink(),
  180. _exifData["focalLength"] != null
  181. ? Padding(
  182. padding: const EdgeInsets.only(right: 10),
  183. child:
  184. Text(_exifData["focalLength"].toString() + "mm"),
  185. )
  186. : const SizedBox.shrink(),
  187. _exifData["ISO"] != null
  188. ? Padding(
  189. padding: const EdgeInsets.only(right: 10),
  190. child: Text("ISO" + _exifData["ISO"].toString()),
  191. )
  192. : const SizedBox.shrink(),
  193. ],
  194. ),
  195. )
  196. : const SizedBox.shrink(),
  197. showExifListTile ? const DividerWithPadding() : const SizedBox.shrink(),
  198. (file.uploadedFileID != null && file.updationTime != null)
  199. ? ListTile(
  200. leading: const Padding(
  201. padding: EdgeInsets.only(top: 8, left: 6),
  202. child: Icon(Icons.cloud_upload_outlined),
  203. ),
  204. title: Text(
  205. getFullDate(
  206. DateTime.fromMicrosecondsSinceEpoch(file.updationTime),
  207. ),
  208. ),
  209. subtitle: Text(
  210. getTimeIn12hrFormat(dateTimeForUpdationTime) +
  211. " " +
  212. dateTimeForUpdationTime.timeZoneName,
  213. style: Theme.of(context)
  214. .textTheme
  215. .bodyText2
  216. .copyWith(color: Colors.black.withOpacity(0.5)),
  217. ),
  218. )
  219. : const SizedBox.shrink(),
  220. ];
  221. var items = <Widget>[
  222. Row(
  223. children: [
  224. Icon(Icons.calendar_today_outlined, color: infoColor),
  225. const SizedBox(height: 8),
  226. Text(
  227. getFormattedTime(
  228. DateTime.fromMicrosecondsSinceEpoch(file.creationTime),
  229. ),
  230. style: TextStyle(color: infoColor),
  231. ),
  232. ],
  233. ),
  234. const SizedBox(height: 12),
  235. Row(
  236. children: [
  237. Icon(Icons.folder_outlined, color: infoColor),
  238. const Padding(padding: EdgeInsets.all(4)),
  239. Text(
  240. file.deviceFolder ??
  241. CollectionsService.instance
  242. .getCollectionByID(file.collectionID)
  243. .name,
  244. style: TextStyle(color: infoColor),
  245. ),
  246. ],
  247. ),
  248. const SizedBox(height: 12),
  249. ];
  250. items.addAll(
  251. [
  252. Row(
  253. children: [
  254. Icon(Icons.sd_storage_outlined, color: infoColor),
  255. const Padding(padding: EdgeInsets.all(4)),
  256. _getFileSize(),
  257. ],
  258. ),
  259. const SizedBox(height: 12),
  260. ],
  261. );
  262. if (file.localID != null && !_isImage) {
  263. //remove
  264. items.addAll(
  265. [
  266. Row(
  267. children: [
  268. Icon(Icons.timer_outlined, color: infoColor),
  269. const Padding(padding: EdgeInsets.all(4)),
  270. FutureBuilder(
  271. future: file.getAsset(),
  272. builder: (context, snapshot) {
  273. if (snapshot.hasData) {
  274. return Text(
  275. snapshot.data.videoDuration.toString().split(".")[0],
  276. style: TextStyle(color: infoColor),
  277. );
  278. } else {
  279. return Center(
  280. child: SizedBox.fromSize(
  281. size: const Size.square(24),
  282. child: const CupertinoActivityIndicator(
  283. radius: 8,
  284. ),
  285. ),
  286. );
  287. }
  288. },
  289. ),
  290. ],
  291. ),
  292. const SizedBox(height: 12),
  293. ],
  294. );
  295. }
  296. if (_isImage && _exif != null) {
  297. //remove
  298. // items.add(_getExifWidgets(_exif));
  299. _generateExifForDetails(_exif);
  300. }
  301. if (file.uploadedFileID != null && file.updationTime != null) {
  302. items.addAll(
  303. [
  304. Row(
  305. children: [
  306. Icon(Icons.cloud_upload_outlined, color: infoColor),
  307. const Padding(padding: EdgeInsets.all(4)),
  308. Text(
  309. getFormattedTime(
  310. DateTime.fromMicrosecondsSinceEpoch(file.updationTime),
  311. ),
  312. style: TextStyle(color: infoColor),
  313. ),
  314. ],
  315. ),
  316. ],
  317. );
  318. }
  319. items.add(
  320. const SizedBox(height: 12),
  321. );
  322. items.add(
  323. Row(
  324. mainAxisAlignment:
  325. _isImage ? MainAxisAlignment.spaceBetween : MainAxisAlignment.end,
  326. children: _getActions(),
  327. ),
  328. );
  329. Widget titleContent;
  330. if (file.uploadedFileID == null ||
  331. file.ownerID != Configuration.instance.getUserID()) {
  332. titleContent = Text(file.getDisplayName());
  333. } else {
  334. titleContent = InkWell(
  335. child: Row(
  336. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  337. children: [
  338. Flexible(
  339. child: Text(
  340. file.getDisplayName(),
  341. style: Theme.of(context).textTheme.headline5,
  342. ),
  343. ),
  344. const SizedBox(width: 16),
  345. Icon(Icons.edit, color: infoColor),
  346. ],
  347. ),
  348. onTap: () async {
  349. await editFilename(context, file);
  350. setState(() {});
  351. },
  352. );
  353. }
  354. // return AlertDialog(
  355. // title: titleContent,
  356. // content: SingleChildScrollView(
  357. // child: ListBody(
  358. // children: items,
  359. // ),
  360. // ),
  361. // );
  362. return Column(
  363. mainAxisSize: MainAxisSize.min,
  364. children: [
  365. Padding(
  366. padding: const EdgeInsets.all(10),
  367. child: Row(
  368. crossAxisAlignment: CrossAxisAlignment.center,
  369. children: [
  370. IconButton(
  371. onPressed: () {
  372. Navigator.pop(context);
  373. },
  374. icon: const Icon(
  375. Icons.close,
  376. ),
  377. ),
  378. const SizedBox(width: 6),
  379. Padding(
  380. padding: const EdgeInsets.only(bottom: 2),
  381. child: Text(
  382. "Details",
  383. style: Theme.of(context).textTheme.bodyText1,
  384. ),
  385. ),
  386. ],
  387. ),
  388. ),
  389. ...listTiles
  390. ],
  391. );
  392. }
  393. List<Widget> _getActions() {
  394. final List<Widget> actions = [];
  395. if (_isImage) {
  396. if (_exif == null) {
  397. actions.add(
  398. TextButton(
  399. child: Row(
  400. mainAxisAlignment: MainAxisAlignment.spaceAround,
  401. children: [
  402. Center(
  403. child: SizedBox.fromSize(
  404. size: const Size.square(24),
  405. child: const CupertinoActivityIndicator(
  406. radius: 8,
  407. ),
  408. ),
  409. ),
  410. const Padding(padding: EdgeInsets.all(4)),
  411. Text(
  412. "EXIF",
  413. style: TextStyle(color: infoColor),
  414. ),
  415. ],
  416. ),
  417. onPressed: () {
  418. showDialog(
  419. context: context,
  420. builder: (BuildContext context) {
  421. return ExifInfoDialog(widget.file);
  422. },
  423. barrierColor: Colors.black87,
  424. );
  425. },
  426. ),
  427. );
  428. } else if (_exif.isNotEmpty) {
  429. actions.add(
  430. TextButton(
  431. child: Row(
  432. mainAxisAlignment: MainAxisAlignment.spaceAround,
  433. children: [
  434. Icon(Icons.feed_outlined, color: infoColor),
  435. const Padding(padding: EdgeInsets.all(4)),
  436. Text(
  437. "View raw EXIF",
  438. style: TextStyle(color: infoColor),
  439. ),
  440. ],
  441. ),
  442. onPressed: () {
  443. showDialog(
  444. context: context,
  445. builder: (BuildContext context) {
  446. return ExifInfoDialog(widget.file);
  447. },
  448. barrierColor: Colors.black87,
  449. );
  450. },
  451. ),
  452. );
  453. } else {
  454. actions.add(
  455. TextButton(
  456. child: Row(
  457. mainAxisAlignment: MainAxisAlignment.spaceAround,
  458. children: [
  459. Icon(
  460. Icons.feed_outlined,
  461. color: Theme.of(context)
  462. .colorScheme
  463. .defaultTextColor
  464. .withOpacity(0.5),
  465. ),
  466. const Padding(padding: EdgeInsets.all(4)),
  467. Text(
  468. "No exif",
  469. style: TextStyle(
  470. color: Theme.of(context)
  471. .colorScheme
  472. .defaultTextColor
  473. .withOpacity(0.5),
  474. ),
  475. ),
  476. ],
  477. ),
  478. onPressed: () {
  479. showShortToast(context, "This image has no exif data");
  480. },
  481. ),
  482. );
  483. }
  484. }
  485. actions.add(
  486. TextButton(
  487. child: Text(
  488. "Close",
  489. style: TextStyle(
  490. color: infoColor,
  491. ),
  492. ),
  493. onPressed: () {
  494. Navigator.of(context, rootNavigator: true).pop("dialog");
  495. },
  496. ),
  497. );
  498. return actions;
  499. }
  500. _generateExifForDetails(Map<String, IfdTag> exif) {
  501. if (exif["EXIF FocalLength"] != null) {
  502. _exifData["focalLength"] =
  503. (exif["EXIF FocalLength"].values.toList()[0] as Ratio).numerator /
  504. (exif["EXIF FocalLength"].values.toList()[0] as Ratio)
  505. .denominator;
  506. }
  507. if (exif["EXIF FNumber"] != null) {
  508. _exifData["fNumber"] =
  509. (exif["EXIF FNumber"].values.toList()[0] as Ratio).numerator /
  510. (exif["EXIF FNumber"].values.toList()[0] as Ratio).denominator;
  511. }
  512. if (exif["EXIF ExifImageWidth"] != null &&
  513. exif["EXIF ExifImageLength"] != null) {
  514. _exifData["resolution"] = exif["EXIF ExifImageWidth"].toString() +
  515. " x " +
  516. exif["EXIF ExifImageLength"].toString();
  517. _exifData['megaPixels'] = ((exif["Image ImageWidth"].values.firstAsInt() *
  518. exif["Image ImageLength"].values.firstAsInt()) /
  519. 1000000)
  520. .toStringAsFixed(1);
  521. } else if (exif["Image ImageWidth"] != null &&
  522. exif["Image ImageLength"] != null) {
  523. _exifData["resolution"] = exif["Image ImageWidth"].toString() +
  524. " x " +
  525. exif["Image ImageLength"].toString();
  526. }
  527. if (exif["Image Make"] != null && exif["Image Model"] != null) {
  528. _exifData["takenOnDevice"] =
  529. exif["Image Make"].toString() + " " + exif["Image Model"].toString();
  530. }
  531. if (exif["EXIF ExposureTime"] != null) {
  532. _exifData["exposureTime"] = exif["EXIF ExposureTime"].toString();
  533. }
  534. if (exif["EXIF ISOSpeedRatings"] != null) {
  535. _exifData['ISO'] = exif["EXIF ISOSpeedRatings"].toString();
  536. }
  537. }
  538. Widget _getExifWidgets(Map<String, IfdTag> exif) {
  539. final focalLength = exif["EXIF FocalLength"] != null
  540. ? (exif["EXIF FocalLength"].values.toList()[0] as Ratio).numerator /
  541. (exif["EXIF FocalLength"].values.toList()[0] as Ratio)
  542. .denominator //to remove
  543. : null;
  544. final fNumber = exif["EXIF FNumber"] != null
  545. ? (exif["EXIF FNumber"].values.toList()[0] as Ratio).numerator /
  546. (exif["EXIF FNumber"].values.toList()[0] as Ratio)
  547. .denominator //to remove
  548. : null;
  549. final List<Widget> children = [];
  550. if (exif["EXIF ExifImageWidth"] != null &&
  551. exif["EXIF ExifImageLength"] != null) {
  552. children.addAll([
  553. Row(
  554. children: [
  555. Icon(Icons.photo_size_select_actual_outlined, color: infoColor),
  556. const Padding(padding: EdgeInsets.all(4)),
  557. Text(
  558. exif["EXIF ExifImageWidth"].toString() +
  559. " x " +
  560. exif["EXIF ExifImageLength"].toString(),
  561. style: TextStyle(color: infoColor),
  562. ),
  563. ],
  564. ),
  565. const Padding(padding: EdgeInsets.all(6)),
  566. ]);
  567. } else if (exif["Image ImageWidth"] != null &&
  568. exif["Image ImageLength"] != null) {
  569. children.addAll([
  570. Row(
  571. children: [
  572. Icon(Icons.photo_size_select_actual_outlined, color: infoColor),
  573. const Padding(padding: EdgeInsets.all(4)),
  574. Text(
  575. exif["Image ImageWidth"].toString() +
  576. " x " +
  577. exif["Image ImageLength"].toString(),
  578. style: TextStyle(color: infoColor),
  579. ),
  580. ],
  581. ),
  582. const Padding(padding: EdgeInsets.all(6)),
  583. ]);
  584. }
  585. if (exif["Image Make"] != null && exif["Image Model"] != null) {
  586. children.addAll(
  587. [
  588. Row(
  589. children: [
  590. Icon(Icons.camera_outlined, color: infoColor),
  591. const Padding(padding: EdgeInsets.all(4)),
  592. Flexible(
  593. child: Text(
  594. exif["Image Make"].toString() +
  595. " " +
  596. exif["Image Model"].toString(),
  597. style: TextStyle(color: infoColor),
  598. overflow: TextOverflow.clip,
  599. ),
  600. ),
  601. ],
  602. ),
  603. const Padding(padding: EdgeInsets.all(6)),
  604. ],
  605. );
  606. }
  607. if (fNumber != null) {
  608. children.addAll([
  609. Row(
  610. children: [
  611. Icon(CupertinoIcons.f_cursive, color: infoColor),
  612. const Padding(padding: EdgeInsets.all(4)),
  613. Text(
  614. fNumber.toString(),
  615. style: TextStyle(color: infoColor),
  616. ),
  617. ],
  618. ),
  619. const Padding(padding: EdgeInsets.all(6)),
  620. ]);
  621. }
  622. if (focalLength != null) {
  623. children.addAll([
  624. Row(
  625. children: [
  626. Icon(Icons.center_focus_strong_outlined, color: infoColor),
  627. const Padding(padding: EdgeInsets.all(4)),
  628. Text(
  629. focalLength.toString() + " mm",
  630. style: TextStyle(color: infoColor),
  631. ),
  632. ],
  633. ),
  634. const Padding(padding: EdgeInsets.all(6)),
  635. ]);
  636. }
  637. if (exif["EXIF ExposureTime"] != null) {
  638. children.addAll([
  639. Row(
  640. children: [
  641. Icon(Icons.shutter_speed, color: infoColor),
  642. const Padding(padding: EdgeInsets.all(4)),
  643. Text(
  644. exif["EXIF ExposureTime"].toString(),
  645. style: TextStyle(color: infoColor),
  646. ),
  647. ],
  648. ),
  649. const Padding(padding: EdgeInsets.all(6)),
  650. ]);
  651. }
  652. return Column(
  653. children: children,
  654. );
  655. }
  656. Widget _getFileSize() {
  657. return FutureBuilder(
  658. future: getFile(widget.file).then((f) => f.length()),
  659. builder: (context, snapshot) {
  660. if (snapshot.hasData) {
  661. return Text(
  662. (snapshot.data / (1024 * 1024)).toStringAsFixed(2) + " MB",
  663. );
  664. } else {
  665. return Center(
  666. child: SizedBox.fromSize(
  667. size: const Size.square(24),
  668. child: const CupertinoActivityIndicator(
  669. radius: 8,
  670. ),
  671. ),
  672. );
  673. }
  674. },
  675. );
  676. }
  677. Widget _getVideoDuration() {
  678. if (widget.file.duration != 0) {
  679. return Text(
  680. secondsToHHMMSS(widget.file.duration),
  681. );
  682. }
  683. return FutureBuilder(
  684. future: widget.file.getAsset(),
  685. builder: (context, snapshot) {
  686. if (snapshot.hasData) {
  687. return Text(
  688. snapshot.data.videoDuration.toString().split(".")[0],
  689. );
  690. } else {
  691. return Center(
  692. child: SizedBox.fromSize(
  693. size: const Size.square(24),
  694. child: const CupertinoActivityIndicator(
  695. radius: 8,
  696. ),
  697. ),
  698. );
  699. }
  700. },
  701. );
  702. }
  703. }
  704. class DividerWithPadding extends StatelessWidget {
  705. const DividerWithPadding({Key key}) : super(key: key);
  706. @override
  707. Widget build(BuildContext context) {
  708. return const Padding(
  709. padding: EdgeInsets.fromLTRB(70, 0, 20, 0),
  710. child: Divider(
  711. thickness: 0.5,
  712. ),
  713. );
  714. }
  715. }