photo_card_item.dart 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import 'dart:io';
  2. import 'dart:typed_data';
  3. import 'package:flutter/material.dart';
  4. import 'package:image_gallery_saver/image_gallery_saver.dart';
  5. import 'package:image_picker/image_picker.dart';
  6. import 'package:lszlgl/base/base_lifecycle_state.dart';
  7. import 'package:lszlgl/utils/permission_utils.dart';
  8. import 'package:lszlgl/widget/card_item.dart';
  9. import 'package:lszlgl/widget/gallery_photo_view_wrapper.dart';
  10. import 'package:permission_handler/permission_handler.dart';
  11. class PhotoCardItem extends StatefulWidget {
  12. final String title;
  13. final String? imgUrl;
  14. final bool isDetail;
  15. final Function(String filePath) filePathCallBack;
  16. const PhotoCardItem({super.key, required this.title, required this.isDetail, this.imgUrl, required this.filePathCallBack});
  17. @override
  18. State<PhotoCardItem> createState() => _PhotoCardItemState();
  19. }
  20. class _PhotoCardItemState extends State<PhotoCardItem> {
  21. final ImagePicker _picker = ImagePicker();
  22. XFile? photoPic;
  23. // 弹窗选择 照片或拍照
  24. void showBotSheet() {
  25. showModalBottomSheet(
  26. context: context,
  27. shape: const RoundedRectangleBorder(
  28. borderRadius:
  29. BorderRadius.only(topLeft: Radius.circular(14), topRight: Radius.circular(14))),
  30. builder: (context) {
  31. return Column(
  32. mainAxisSize: MainAxisSize.min,
  33. crossAxisAlignment: CrossAxisAlignment.stretch,
  34. children: [
  35. const SizedBox(height: 8),
  36. TextButton(
  37. child: const Text('相册'),
  38. onPressed: () {
  39. Navigator.pop(context);
  40. pickImage(false);
  41. }),
  42. TextButton(
  43. child: const Text('拍照'),
  44. onPressed: () async {
  45. Navigator.pop(context);
  46. bool res = await PermissionHandler.handleWith(Permission.storage);
  47. if (res) {
  48. pickImage(true);
  49. }
  50. }),
  51. const SizedBox(height: 8),
  52. const Divider(color: Color(0xFFF3F3F3), height: 0, thickness: 1),
  53. TextButton(
  54. child: const Text('取消', style: TextStyle(color: Colors.grey)),
  55. onPressed: () {
  56. Navigator.pop(context);
  57. }),
  58. const SizedBox(height: 4),
  59. ],
  60. );
  61. },
  62. );
  63. }
  64. /// 保存照片到本地
  65. void savePicture(String? path) async {
  66. if (path == null) return;
  67. final File imageFile = File(path);
  68. final Uint8List imageBytes = await imageFile.readAsBytes();
  69. await ImageGallerySaver.saveImage(imageBytes);
  70. }
  71. /// 选择照片或拍照
  72. void pickImage(bool isCamera) async {
  73. try {
  74. XFile? photo = await _picker.pickImage(
  75. source: isCamera ? ImageSource.camera : ImageSource.gallery,
  76. imageQuality: 25,
  77. );
  78. if (photo == null) return;
  79. if (isCamera) {
  80. savePicture(photo.path); // 保存到本地
  81. }
  82. setState(() {
  83. photoPic = photo;
  84. });
  85. widget.filePathCallBack.call(photo.path);
  86. } catch (e) {
  87. MyNavigator.showToast('$e');
  88. }
  89. }
  90. void showBigImage({XFile? file}) {
  91. FocusManager.instance.primaryFocus?.unfocus();
  92. Navigator.push(
  93. context,
  94. MaterialPageRoute(
  95. builder: (context) {
  96. return file != null
  97. ? GalleryPhotoViewWrapper(
  98. galleryItems: [file],
  99. )
  100. : GalleryPhotoViewWrapper(
  101. netImgList: [widget.imgUrl!],
  102. );
  103. },
  104. ),
  105. );
  106. }
  107. @override
  108. Widget build(BuildContext context) {
  109. return Stack(
  110. alignment: Alignment.centerLeft,
  111. children: [
  112. CardItemWidget(
  113. widget.title,
  114. bottomLine: true,
  115. // rightText: widget.isDetail ? '点击查看' : '点击上传',
  116. rightChild: widget.isDetail || photoPic == null
  117. ? const SizedBox.shrink()
  118. : GestureDetector(
  119. child: Image.file(
  120. File(photoPic!.path),
  121. width: 44,
  122. height: 44,
  123. fit: BoxFit.cover,
  124. ),
  125. onTap: () => showBigImage(file: photoPic!),
  126. ),
  127. trailing: GestureDetector(
  128. child: Text(
  129. widget.isDetail
  130. ? '点击查看'
  131. : photoPic == null
  132. ? '点击选择'
  133. : '重新选择',
  134. style: const TextStyle(fontSize: 14, color: Color(0xFF01B2C8)),
  135. ),
  136. onTap: () {
  137. if (widget.isDetail) {
  138. // 详情 查看照片
  139. if (widget.imgUrl == null) {
  140. MyNavigator.showToast('地址错误');
  141. return;
  142. }
  143. showBigImage();
  144. } else {
  145. showBotSheet(); // 编辑-选择照片
  146. }
  147. },
  148. ),
  149. onTap: () {},
  150. ),
  151. Container(width: 4, height: 4, color: const Color(0xFF01B2C8))
  152. ],
  153. );
  154. }
  155. }