file_info_dialog.dart 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. import 'package:exif/exif.dart';
  2. import 'package:flutter/cupertino.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:photos/models/file.dart';
  5. import 'package:photos/models/file_type.dart';
  6. import 'package:photos/services/collections_service.dart';
  7. import 'package:photos/ui/exif_info_dialog.dart';
  8. import 'package:photos/utils/date_time_util.dart';
  9. import 'package:photos/utils/file_util.dart';
  10. class FileInfoWidget extends StatelessWidget {
  11. final File file;
  12. const FileInfoWidget(
  13. this.file, {
  14. Key key,
  15. }) : super(key: key);
  16. @override
  17. Widget build(BuildContext context) {
  18. bool isImage = file.fileType == FileType.image;
  19. var items = <Widget>[
  20. Row(
  21. children: [
  22. Icon(
  23. Icons.calendar_today_outlined,
  24. color: Colors.white.withOpacity(0.85),
  25. ),
  26. Padding(padding: EdgeInsets.all(4)),
  27. Text(
  28. getFormattedTime(
  29. DateTime.fromMicrosecondsSinceEpoch(file.creationTime),
  30. ),
  31. style: TextStyle(
  32. color: Colors.white.withOpacity(0.85),
  33. ),
  34. ),
  35. ],
  36. ),
  37. Padding(padding: EdgeInsets.all(6)),
  38. Row(
  39. children: [
  40. Icon(
  41. Icons.folder_outlined,
  42. color: Colors.white.withOpacity(0.85),
  43. ),
  44. Padding(padding: EdgeInsets.all(4)),
  45. Text(
  46. file.deviceFolder ??
  47. CollectionsService.instance
  48. .getCollectionByID(file.collectionID)
  49. .name,
  50. style: TextStyle(
  51. color: Colors.white.withOpacity(0.85),
  52. ),
  53. ),
  54. ],
  55. ),
  56. Padding(padding: EdgeInsets.all(6)),
  57. ];
  58. items.addAll(
  59. [
  60. Row(
  61. children: [
  62. Icon(
  63. Icons.sd_storage_outlined,
  64. color: Colors.white.withOpacity(0.85),
  65. ),
  66. Padding(padding: EdgeInsets.all(4)),
  67. _getFileSize(),
  68. ],
  69. ),
  70. Padding(padding: EdgeInsets.all(6)),
  71. ],
  72. );
  73. if (file.localID != null && !isImage) {
  74. items.addAll(
  75. [
  76. Row(
  77. children: [
  78. Icon(
  79. Icons.timer_outlined,
  80. color: Colors.white.withOpacity(0.85),
  81. ),
  82. Padding(padding: EdgeInsets.all(4)),
  83. FutureBuilder(
  84. future: file.getAsset(),
  85. builder: (context, snapshot) {
  86. if (snapshot.hasData) {
  87. return Text(
  88. snapshot.data.videoDuration.toString().split(".")[0],
  89. style: TextStyle(
  90. color: Colors.white.withOpacity(0.85),
  91. ),
  92. );
  93. } else {
  94. return Center(
  95. child: SizedBox.fromSize(
  96. size: Size.square(24),
  97. child: CupertinoActivityIndicator(
  98. radius: 8,
  99. ),
  100. ),
  101. );
  102. }
  103. },
  104. ),
  105. ],
  106. ),
  107. Padding(padding: EdgeInsets.all(6)),
  108. ],
  109. );
  110. }
  111. if (isImage) {
  112. items.add(
  113. FutureBuilder(
  114. future: _getExif(),
  115. builder: (c, snapshot) {
  116. if (snapshot.hasData) {
  117. return _getExifWidgets(snapshot.data);
  118. } else {
  119. return Container();
  120. }
  121. },
  122. ),
  123. );
  124. }
  125. if (file.uploadedFileID != null) {
  126. items.addAll(
  127. [
  128. Row(
  129. children: [
  130. Icon(
  131. Icons.cloud_upload_outlined,
  132. color: Colors.white.withOpacity(0.85),
  133. ),
  134. Padding(padding: EdgeInsets.all(4)),
  135. Text(
  136. getFormattedTime(
  137. DateTime.fromMicrosecondsSinceEpoch(file.updationTime)),
  138. style: TextStyle(
  139. color: Colors.white.withOpacity(0.85),
  140. ),
  141. ),
  142. ],
  143. ),
  144. ],
  145. );
  146. }
  147. items.add(
  148. Padding(padding: EdgeInsets.all(12)),
  149. );
  150. final List<Widget> actions = [];
  151. if (isImage) {
  152. actions.add(
  153. TextButton(
  154. child: Row(
  155. mainAxisAlignment: MainAxisAlignment.spaceAround,
  156. children: [
  157. Icon(
  158. Icons.feed_outlined,
  159. color: Colors.white.withOpacity(0.85),
  160. ),
  161. Padding(padding: EdgeInsets.all(4)),
  162. Text(
  163. "view exif",
  164. style: TextStyle(
  165. color: Colors.white.withOpacity(0.85),
  166. ),
  167. ),
  168. ],
  169. ),
  170. onPressed: () {
  171. showDialog(
  172. context: context,
  173. builder: (BuildContext context) {
  174. return ExifInfoDialog(file);
  175. },
  176. barrierColor: Colors.black87,
  177. );
  178. },
  179. ),
  180. );
  181. }
  182. actions.add(
  183. TextButton(
  184. child: Text(
  185. "close",
  186. style: TextStyle(
  187. color: Colors.white.withOpacity(0.8),
  188. ),
  189. ),
  190. onPressed: () {
  191. Navigator.of(context, rootNavigator: true).pop('dialog');
  192. },
  193. ),
  194. );
  195. items.add(
  196. Row(
  197. mainAxisAlignment:
  198. isImage ? MainAxisAlignment.spaceBetween : MainAxisAlignment.end,
  199. children: actions,
  200. ),
  201. );
  202. return AlertDialog(
  203. title: Text(file.title),
  204. content: SingleChildScrollView(
  205. child: ListBody(
  206. children: items,
  207. ),
  208. ),
  209. );
  210. }
  211. Widget _getExifWidgets(Map<String, IfdTag> exif) {
  212. final focalLength = exif["EXIF FocalLength"] != null
  213. ? (exif["EXIF FocalLength"].values.toList()[0] as Ratio).numerator /
  214. (exif["EXIF FocalLength"].values.toList()[0] as Ratio).denominator
  215. : null;
  216. final fNumber = exif["EXIF FNumber"] != null
  217. ? (exif["EXIF FNumber"].values.toList()[0] as Ratio).numerator /
  218. (exif["EXIF FNumber"].values.toList()[0] as Ratio).denominator
  219. : null;
  220. final List<Widget> children = [];
  221. if (exif["EXIF ExifImageWidth"] != null &&
  222. exif["EXIF ExifImageLength"] != null) {
  223. children.addAll([
  224. Row(
  225. children: [
  226. Icon(
  227. Icons.photo_size_select_actual_outlined,
  228. color: Colors.white.withOpacity(0.85),
  229. ),
  230. Padding(padding: EdgeInsets.all(4)),
  231. Text(
  232. exif["EXIF ExifImageWidth"].toString() +
  233. " x " +
  234. exif["EXIF ExifImageLength"].toString(),
  235. style: TextStyle(
  236. color: Colors.white.withOpacity(0.85),
  237. ),
  238. ),
  239. ],
  240. ),
  241. Padding(padding: EdgeInsets.all(6)),
  242. ]);
  243. } else if (exif["Image ImageWidth"] != null &&
  244. exif["Image ImageLength"] != null) {
  245. children.addAll([
  246. Row(
  247. children: [
  248. Icon(
  249. Icons.photo_size_select_actual_outlined,
  250. color: Colors.white.withOpacity(0.85),
  251. ),
  252. Padding(padding: EdgeInsets.all(4)),
  253. Text(
  254. exif["Image ImageWidth"].toString() +
  255. " x " +
  256. exif["Image ImageLength"].toString(),
  257. style: TextStyle(
  258. color: Colors.white.withOpacity(0.85),
  259. ),
  260. ),
  261. ],
  262. ),
  263. Padding(padding: EdgeInsets.all(6)),
  264. ]);
  265. }
  266. if (exif["Image Make"] != null && exif["Image Model"] != null) {
  267. children.addAll(
  268. [
  269. Row(
  270. children: [
  271. Icon(
  272. Icons.camera_outlined,
  273. color: Colors.white.withOpacity(0.85),
  274. ),
  275. Padding(padding: EdgeInsets.all(4)),
  276. Text(
  277. exif["Image Make"].toString() +
  278. " " +
  279. exif["Image Model"].toString(),
  280. style: TextStyle(
  281. color: Colors.white.withOpacity(0.85),
  282. ),
  283. ),
  284. ],
  285. ),
  286. Padding(padding: EdgeInsets.all(6)),
  287. ],
  288. );
  289. }
  290. if (fNumber != null) {
  291. children.addAll([
  292. Row(
  293. children: [
  294. Icon(
  295. CupertinoIcons.f_cursive,
  296. color: Colors.white.withOpacity(0.85),
  297. ),
  298. Padding(padding: EdgeInsets.all(4)),
  299. Text(
  300. fNumber.toString(),
  301. style: TextStyle(
  302. color: Colors.white.withOpacity(0.85),
  303. ),
  304. ),
  305. ],
  306. ),
  307. Padding(padding: EdgeInsets.all(6)),
  308. ]);
  309. }
  310. if (focalLength != null) {
  311. children.addAll([
  312. Row(
  313. children: [
  314. Icon(
  315. Icons.center_focus_strong_outlined,
  316. color: Colors.white.withOpacity(0.85),
  317. ),
  318. Padding(padding: EdgeInsets.all(4)),
  319. Text(focalLength.toString() + " mm",
  320. style: TextStyle(
  321. color: Colors.white.withOpacity(0.85),
  322. )),
  323. ],
  324. ),
  325. Padding(padding: EdgeInsets.all(6)),
  326. ]);
  327. }
  328. if (exif["EXIF ExposureTime"] != null) {
  329. children.addAll([
  330. Row(
  331. children: [
  332. Icon(
  333. Icons.shutter_speed,
  334. color: Colors.white.withOpacity(0.85),
  335. ),
  336. Padding(padding: EdgeInsets.all(4)),
  337. Text(
  338. exif["EXIF ExposureTime"].toString(),
  339. style: TextStyle(
  340. color: Colors.white.withOpacity(0.85),
  341. ),
  342. ),
  343. ],
  344. ),
  345. Padding(padding: EdgeInsets.all(6)),
  346. ]);
  347. }
  348. return Column(
  349. children: children,
  350. );
  351. }
  352. Widget _getFileSize() {
  353. return FutureBuilder(
  354. future: getFile(file).then((f) => f.lengthSync()),
  355. builder: (context, snapshot) {
  356. if (snapshot.hasData) {
  357. return Text(
  358. (snapshot.data / (1024 * 1024)).toStringAsFixed(2) + " MB",
  359. style: TextStyle(
  360. color: Colors.white.withOpacity(0.85),
  361. ),
  362. );
  363. } else {
  364. return Center(
  365. child: SizedBox.fromSize(
  366. size: Size.square(24),
  367. child: CupertinoActivityIndicator(
  368. radius: 8,
  369. ),
  370. ),
  371. );
  372. }
  373. },
  374. );
  375. }
  376. Future<Map<String, IfdTag>> _getExif() async {
  377. return await readExifFromFile(await getFile(file));
  378. }
  379. }