Hibok
25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 
 
 
 

169 satır
5.1 KiB

  1. import 'dart:io';
  2. import 'dart:typed_data';
  3. import 'package:chat/chat/download_item.dart';
  4. import 'package:chat/generated/i18n.dart';
  5. import 'package:chat/models/ChatMsg.dart';
  6. import 'package:flutter/material.dart';
  7. import 'package:image_gallery_saver/image_gallery_saver.dart';
  8. import 'package:oktoast/oktoast.dart';
  9. import '../r.dart';
  10. class PhotoPage extends StatefulWidget {
  11. final MsgModel msg;
  12. PhotoPage({this.msg});
  13. @override
  14. _PhotoPageState createState() => _PhotoPageState();
  15. }
  16. class _PhotoPageState extends State<PhotoPage>
  17. with SingleTickerProviderStateMixin {
  18. AnimationController _controller;
  19. Animation<Offset> _animation;
  20. Offset _offset = Offset.zero;
  21. double _scale = 1.0;
  22. Offset _normalizedOffset;
  23. double _previousScale;
  24. double _kMinFlingVelocity = 600.0;
  25. ImageProvider provider;
  26. @override
  27. void initState() {
  28. super.initState();
  29. getImgData();
  30. _controller = AnimationController(vsync: this);
  31. _controller.addListener(() {
  32. setState(() {
  33. _offset = _animation.value;
  34. });
  35. });
  36. }
  37. getImgData() {
  38. if (widget.msg.localFile != null) {
  39. var fileData = File(widget.msg.localFile).readAsBytesSync();
  40. provider = MemoryImage(fileData);
  41. } else {
  42. provider = MemoryImage(Uint8List.fromList(widget.msg.msgContent));
  43. }
  44. }
  45. @override
  46. void dispose() {
  47. _controller.stop();
  48. _controller.dispose();
  49. super.dispose();
  50. }
  51. Offset _clampOffset(Offset offset) {
  52. final Size size = context.size;
  53. // widget的屏幕宽度
  54. final Offset minOffset = Offset(size.width, size.height) * (1.0 - _scale);
  55. // 限制他的最小尺寸
  56. return Offset(
  57. offset.dx.clamp(minOffset.dx, 0.0), offset.dy.clamp(minOffset.dy, 0.0));
  58. }
  59. void _handleOnScaleStart(ScaleStartDetails details) {
  60. setState(() {
  61. _previousScale = _scale;
  62. _normalizedOffset = (details.focalPoint - _offset) / _scale;
  63. // 计算图片放大后的位置
  64. _controller.stop();
  65. });
  66. }
  67. void _handleOnScaleUpdate(ScaleUpdateDetails details) {
  68. setState(() {
  69. _scale = (_previousScale * details.scale).clamp(1.0, 3.0);
  70. // 限制放大倍数 1~3倍
  71. _offset = _clampOffset(details.focalPoint - _normalizedOffset * _scale);
  72. // 更新当前位置
  73. });
  74. }
  75. void _handleOnScaleEnd(ScaleEndDetails details) {
  76. final double magnitude = details.velocity.pixelsPerSecond.distance;
  77. if (magnitude < _kMinFlingVelocity) return;
  78. final Offset direction = details.velocity.pixelsPerSecond / magnitude;
  79. // 计算当前的方向
  80. final double distance = (Offset.zero & context.size).shortestSide;
  81. // 计算放大倍速,并相应的放大宽和高,比如原来是600*480的图片,放大后倍数为1.25倍时,宽和高是同时变化的
  82. _animation = _controller.drive(Tween<Offset>(
  83. begin: _offset, end: _clampOffset(_offset + direction * distance)));
  84. _controller
  85. ..value = 0.0
  86. ..fling(velocity: magnitude / 1000.0);
  87. }
  88. @override
  89. Widget build(BuildContext context) {
  90. return GestureDetector(
  91. onTap: () {
  92. Navigator.pop(context);
  93. },
  94. onScaleStart: _handleOnScaleStart,
  95. onScaleUpdate: _handleOnScaleUpdate,
  96. onScaleEnd: _handleOnScaleEnd,
  97. child: Material(
  98. child: DownloadItem(
  99. msg: widget.msg,
  100. isAutoDown: false,
  101. onComplete: () {
  102. getImgData();
  103. setState(() {});
  104. },
  105. child: Transform(
  106. transform: Matrix4.identity()
  107. ..translate(_offset.dx, _offset.dy)
  108. ..scale(_scale),
  109. child: Stack(
  110. children: <Widget>[
  111. Image(
  112. fit: BoxFit.fitWidth,
  113. image: provider ?? AssetImage(R.assetsImagesIcAlbum),
  114. ),
  115. Positioned(
  116. right: 10,
  117. bottom: 10,
  118. child: InkWell(
  119. onTap: saveToGallery,
  120. child: Container(
  121. padding: EdgeInsets.all(5),
  122. decoration: BoxDecoration(
  123. color: Colors.grey.withAlpha(150),
  124. borderRadius: BorderRadius.circular(8)),
  125. child: Icon(Icons.save_alt, color: Colors.white70),
  126. )))
  127. ],
  128. )),
  129. )),
  130. );
  131. }
  132. saveToGallery() async {
  133. if (widget.msg.localFile != null) {
  134. var data= File(widget.msg.localFile).readAsBytesSync();
  135. ImageGallerySaver.saveImage(data).then((res) {
  136. print(res);
  137. if (res != null) {
  138. showToast(I18n.of(context).successfully_saved);
  139. }
  140. });
  141. } else {
  142. ImageGallerySaver.saveImage(Uint8List.fromList(widget.msg.msgContent))
  143. .then((res) {
  144. print(res);
  145. if (res != null) {
  146. showToast(I18n.of(context).successfully_saved);
  147. }
  148. });
  149. }
  150. }
  151. }