run_share.dart 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import 'dart:io';
  2. import 'dart:typed_data';
  3. import 'dart:ui' as ui;
  4. import 'package:flutter/material.dart';
  5. import 'package:flutter/rendering.dart';
  6. import 'package:image_gallery_saver/image_gallery_saver.dart';
  7. import 'package:path_provider/path_provider.dart';
  8. import 'package:sport/bean/jog/detail.dart';
  9. import 'package:sport/pages/home/home_info_page.dart';
  10. import 'package:sport/pages/run/run_share_long.dart';
  11. import 'package:sport/pages/run/run_share_simple.dart';
  12. import 'package:sport/utils/toast.dart';
  13. import 'package:sport/widgets/appbar.dart';
  14. import 'package:sport/widgets/button_primary.dart';
  15. import 'package:sport/widgets/dialog/request_dialog.dart';
  16. import 'package:sport/widgets/menu_share_bottom.dart';
  17. class RunShare extends StatefulWidget {
  18. final JogDetail detail;
  19. final File map;
  20. const RunShare({Key? key, required this.detail, required this.map}) : super(key: key);
  21. @override
  22. State<StatefulWidget> createState() {
  23. return _PageState();
  24. }
  25. }
  26. class _PageState extends State<RunShare> with SingleTickerProviderStateMixin {
  27. GlobalKey repaintKeySimple = GlobalKey();
  28. GlobalKey repaintKeyLong = GlobalKey();
  29. late TabController _controller;
  30. int index = 0;
  31. @override
  32. void initState() {
  33. _controller = TabController(length: 2, vsync: this)
  34. ..addListener(() {
  35. index = _controller.animation?.value.toInt() ?? 0;
  36. });
  37. super.initState();
  38. }
  39. @override
  40. void dispose() {
  41. _controller.dispose();
  42. super.dispose();
  43. }
  44. @override
  45. Widget build(BuildContext context) {
  46. return Scaffold(
  47. backgroundColor: Colors.white,
  48. appBar: buildAppBar(context, title: "运动分享"),
  49. body: DefaultTabController(
  50. length: 2,
  51. child: Column(
  52. children: [
  53. Padding(
  54. padding: const EdgeInsets.all(12.0),
  55. child: indexTabBar(["图片", "长图"], tabController: _controller, fontSize: 14.0, padding: 0.0),
  56. ),
  57. Expanded(
  58. child: TabBarView(
  59. controller: _controller,
  60. children: <Widget>[
  61. Padding(
  62. padding: const EdgeInsets.symmetric(horizontal: 12.0),
  63. child: RunShareSimple(
  64. detail: widget.detail,
  65. map: widget.map,
  66. repaintWidgetKey: repaintKeySimple,
  67. ),
  68. ),
  69. Padding(
  70. padding: const EdgeInsets.symmetric(horizontal: 12.0),
  71. child: RunShareLongPage(
  72. detail: widget.detail,
  73. map: widget.map,
  74. repaintWidgetKey: repaintKeyLong,
  75. ),
  76. ),
  77. ],
  78. )),
  79. Padding(
  80. padding: const EdgeInsets.all(12.0),
  81. child: Row(
  82. children: [
  83. Expanded(
  84. child: GestureDetector(
  85. onTap: () {
  86. runShare(context, index, index == 0 ? repaintKeySimple : repaintKeyLong, true);
  87. },
  88. child: Container(
  89. height: 44,
  90. decoration: BoxDecoration(border: Border.all(color: Theme.of(context).accentColor, width: 1), borderRadius: BorderRadius.circular(50)),
  91. child: Center(
  92. child: Text(
  93. "保存",
  94. style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Theme.of(context).accentColor, fontSize: 16.0),
  95. strutStyle: StrutStyle(height: 1.1),
  96. ),
  97. ),
  98. ),
  99. )),
  100. const SizedBox(
  101. width: 20.0,
  102. ),
  103. Expanded(
  104. child: PrimaryButton(
  105. height: 44.0,
  106. callback: () {
  107. runShare(context, index, index == 0 ? repaintKeySimple : repaintKeyLong, false);
  108. },
  109. content: "分享",
  110. )),
  111. ],
  112. ),
  113. ),
  114. ],
  115. ),
  116. ),
  117. );
  118. }
  119. }
  120. _takePicture(int index, GlobalKey key) async {
  121. try {
  122. RenderObject? boundary = key.currentContext?.findRenderObject();
  123. if (!(boundary is RenderRepaintBoundary)) return;
  124. double dpr = ui.window.devicePixelRatio; // 获取当前设备的像素比
  125. ui.Image image = await boundary.toImage(pixelRatio: dpr);
  126. ui.PictureRecorder pictureRecorder = new ui.PictureRecorder(); // 图片记录仪
  127. Canvas canvas = new Canvas(pictureRecorder); //canvas接受一个图片
  128. canvas.drawColor(Colors.white, BlendMode.clear);
  129. Paint _paint = new Paint()..isAntiAlias = true;
  130. canvas.drawImage(image, Offset(0, 0), _paint);
  131. ui.Image picture = await pictureRecorder.endRecording().toImage(image.width, image.height); //设置生成图片的宽和高
  132. ByteData? pngImageBytes = await picture.toByteData(format: ui.ImageByteFormat.png);
  133. if (pngImageBytes == null) return;
  134. Uint8List pngBytes = pngImageBytes.buffer.asUint8List();
  135. String sTempDir = (await getTemporaryDirectory()).path;
  136. bool isDirExist = await Directory(sTempDir).exists();
  137. if (!isDirExist) {
  138. Directory(sTempDir).create();
  139. }
  140. File file = await File(sTempDir + "/poster-temp-${DateTime.now().millisecondsSinceEpoch}.png").writeAsBytes(pngBytes.buffer.asUint8List());
  141. print("save file $file");
  142. return file.path;
  143. } catch (e) {
  144. print(e);
  145. }
  146. return null;
  147. }
  148. runShare(BuildContext context, int index, GlobalKey key, bool save, {bool app = false}) async {
  149. var path = await request(context, () async {
  150. return await _takePicture(index, key);
  151. });
  152. if (path == null) return;
  153. print("share path $path");
  154. if (save) {
  155. final result = await ImageGallerySaver.saveFile(path);
  156. if (result != null && result["isSuccess"] == true) ToastUtil.show("已保存至相册");
  157. } else {
  158. await showModalBottomSheet(
  159. context: context,
  160. backgroundColor: Colors.white,
  161. elevation: 10,
  162. shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
  163. builder: (context) => MenuShareBottomContent(
  164. "Img",
  165. hasDownload: false,
  166. file: path,
  167. app: app,
  168. ),
  169. );
  170. }
  171. }