Browse Source

添加保存分享统计图

weicu
tanlongsheng 1 year ago
parent
commit
d344486644
  1. 442
      lib/modules/meeting/views/bottomSheet/statistics_bottom_sheet.dart
  2. 2
      pubspec.yaml

442
lib/modules/meeting/views/bottomSheet/statistics_bottom_sheet.dart

@ -1,8 +1,15 @@
import 'dart:io'; import 'dart:io';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:image_gallery_saver_plus/image_gallery_saver_plus.dart';
import 'package:path_provider/path_provider.dart';
import 'package:share_plus/share_plus.dart';
import '../../../profile/controllers/profile_controller.dart'; import '../../../profile/controllers/profile_controller.dart';
import '../../controllers/meeting_mine_controller.dart'; import '../../controllers/meeting_mine_controller.dart';
@ -18,6 +25,8 @@ class _StatisticsBottomSheetState extends State<StatisticsBottomSheet> {
final _mineController = Get.find<MeetingMineController>(); final _mineController = Get.find<MeetingMineController>();
final _profileCtrl = Get.find<ProfileController>(); final _profileCtrl = Get.find<ProfileController>();
final GlobalKey _posterKey = GlobalKey();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Stack( return Stack(
@ -30,225 +39,263 @@ class _StatisticsBottomSheetState extends State<StatisticsBottomSheet> {
height: 1.sh, height: 1.sh,
), ),
), ),
Column( Padding(
mainAxisAlignment: MainAxisAlignment.center, padding: EdgeInsets.symmetric(horizontal: 10.w),
children: [ child: Column(
Container( mainAxisAlignment: MainAxisAlignment.center,
padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 20.w), children: [
margin: EdgeInsets.symmetric(horizontal: 10.w), RepaintBoundary(
decoration: BoxDecoration( key: _posterKey,
color: Colors.white, child: Container(
borderRadius: BorderRadius.circular(16.w), padding:
image: const DecorationImage( EdgeInsets.symmetric(horizontal: 15.w, vertical: 20.w),
alignment: Alignment.topCenter, decoration: BoxDecoration(
image: AssetImage('assets/images/mine-background.png'), color: Colors.white,
fit: BoxFit.fitWidth, borderRadius: BorderRadius.circular(16.w),
), image: const DecorationImage(
), alignment: Alignment.topCenter,
child: Column( image: AssetImage('assets/images/mine-background.png'),
children: [ fit: BoxFit.fitWidth,
Container(
width: 50.w,
height: 50.w,
margin: EdgeInsets.only(bottom: 10.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50.w),
),
child: CircleAvatar(
radius: 25.w,
backgroundImage: _profileCtrl.localAvatar.value.isNotEmpty
? FileImage(File(_profileCtrl.localAvatar.value))
: (_profileCtrl.userInfo.value.avatar.isNotEmpty
? NetworkImage(_profileCtrl.userInfo.value.avatar)
: null) as ImageProvider<Object>?,
backgroundColor: Colors.grey[200],
child: (_profileCtrl.localAvatar.value.isEmpty &&
_profileCtrl.userInfo.value.avatar.isEmpty)
? Icon(
Icons.person,
size: 25.r,
color: Colors.grey[600],
)
: null,
),
),
Text(
_profileCtrl.userInfo.value.name,
style: TextStyle(
color: Colors.black87,
fontSize: 20.sp,
fontWeight: FontWeight.bold,
), ),
), ),
Padding( child: Column(
padding: EdgeInsets.symmetric(vertical: 20.w),
child: Wrap(
spacing: 5.w,
runSpacing: 5.w,
children: [
_item(
'天',
'${_mineController.days.value}',
Colors.amber,
),
_item(
'文件',
'${_mineController.numberFiles.value}',
Colors.blueGrey,
),
_item(
'小时',
'${_mineController.hourage.value}',
Colors.cyan,
),
_item(
'连续录音天数',
'${_mineController.continuousDays.value}',
Colors.deepOrangeAccent,
),
_item(
'单日最多录音',
'${_mineController.maxNumberFiles.value}',
Colors.teal,
),
_item(
'单日最长录音',
'${_mineController.maxHourage.value}',
Colors.pink,
),
],
),
),
Row(
children: [ children: [
Text( Container(
'过去12个月的热力图', width: 50.w,
style: TextStyle( height: 50.w,
color: Colors.grey, margin: EdgeInsets.only(bottom: 10.w),
fontSize: 12.sp, decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50.w),
),
child: CircleAvatar(
radius: 25.w,
backgroundImage: _profileCtrl
.localAvatar.value.isNotEmpty
? FileImage(File(_profileCtrl.localAvatar.value))
: (_profileCtrl.userInfo.value.avatar.isNotEmpty
? NetworkImage(
_profileCtrl.userInfo.value.avatar)
: null) as ImageProvider<Object>?,
backgroundColor: Colors.grey[200],
child: (_profileCtrl.localAvatar.value.isEmpty &&
_profileCtrl.userInfo.value.avatar.isEmpty)
? Icon(
Icons.person,
size: 25.r,
color: Colors.grey[600],
)
: null,
), ),
), ),
const Spacer(),
Text( Text(
'较冷', _profileCtrl.userInfo.value.name,
style: TextStyle( style: TextStyle(
color: Colors.grey, color: Colors.black87,
fontSize: 12.sp, fontSize: 20.sp,
fontWeight: FontWeight.bold,
), ),
), ),
5.horizontalSpace, Padding(
Container( padding: EdgeInsets.symmetric(vertical: 20.w),
width: 10.w, child: Wrap(
height: 10.w, spacing: 5.w,
margin: EdgeInsets.symmetric(horizontal: 1.w), runSpacing: 5.w,
decoration: BoxDecoration( children: [
color: Colors.grey[200], _item(
borderRadius: BorderRadius.circular(2.w), '天',
'${_mineController.days.value}',
Colors.amber,
),
_item(
'文件',
'${_mineController.numberFiles.value}',
Colors.blueGrey,
),
_item(
'小时',
'${_mineController.hourage.value}',
Colors.cyan,
),
_item(
'连续录音天数',
'${_mineController.continuousDays.value}',
Colors.deepOrangeAccent,
),
_item(
'单日最多录音',
'${_mineController.maxNumberFiles.value}',
Colors.teal,
),
_item(
'单日最长录音',
'${_mineController.maxHourage.value}',
Colors.pink,
),
],
), ),
), ),
Container( Row(
width: 10.w, children: [
height: 10.w, Text(
margin: EdgeInsets.symmetric(horizontal: 1.w), '过去12个月的热力图',
decoration: BoxDecoration( style: TextStyle(
color: Colors.red[100], color: Colors.grey,
borderRadius: BorderRadius.circular(2.w), fontSize: 12.sp,
), ),
),
const Spacer(),
Text(
'较冷',
style: TextStyle(
color: Colors.grey,
fontSize: 12.sp,
),
),
5.horizontalSpace,
Container(
width: 10.w,
height: 10.w,
margin: EdgeInsets.symmetric(horizontal: 1.w),
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(2.w),
),
),
Container(
width: 10.w,
height: 10.w,
margin: EdgeInsets.symmetric(horizontal: 1.w),
decoration: BoxDecoration(
color: Colors.red[100],
borderRadius: BorderRadius.circular(2.w),
),
),
Container(
width: 10.w,
height: 10.w,
margin: EdgeInsets.symmetric(horizontal: 1.w),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(2.w),
),
),
5.horizontalSpace,
Text(
'较热',
style: TextStyle(
color: Colors.grey,
fontSize: 12.sp,
),
),
],
), ),
Container( Container(
width: 10.w, height: 50.w,
height: 10.w, alignment: Alignment.center,
margin: EdgeInsets.symmetric(horizontal: 1.w), child: GridView.builder(
decoration: BoxDecoration( shrinkWrap: true,
color: Colors.red, physics: const NeverScrollableScrollPhysics(),
borderRadius: BorderRadius.circular(2.w), gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 52,
mainAxisSpacing: 1.w,
crossAxisSpacing: 1.w,
),
itemCount: 364,
itemBuilder: (BuildContext context, int index) {
final col = index % 52;
final row = index ~/ 52;
final vIndex = row + col * 7;
int type = 0;
if (_mineController.statistics
.containsKey(vIndex + 1)) {
if (_mineController.statistics[vIndex + 1]
['count'] <=
_mineController.averageNumberFiles) {
type = 1;
} else {
type = 2;
}
}
return Container(
color: type == 0
? Colors.grey[200]
: type == 1
? Colors.red[100]
: Colors.red,
);
},
), ),
), ),
5.horizontalSpace, DefaultTextStyle(
Text(
'较热',
style: TextStyle( style: TextStyle(
color: Colors.grey, color: Colors.grey,
fontSize: 12.sp, fontSize: 10.sp,
),
child: const Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text('Jan'),
Text('Feb'),
Text('Mar'),
Text('Apr'),
Text('May'),
Text('Jun'),
Text('Jul'),
Text('Aug'),
Text('Sep'),
Text('Oct'),
Text('Nov'),
Text('Dec'),
],
), ),
), ),
], ],
), ),
Container( ),
height: 50.w,
alignment: Alignment.center,
child: GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 52,
mainAxisSpacing: 1.w,
crossAxisSpacing: 1.w,
),
itemCount: 364,
itemBuilder: (BuildContext context, int index) {
final col = index % 52;
final row = index ~/ 52;
final vIndex = row + col * 7;
int type = 0;
if (_mineController.statistics
.containsKey(vIndex + 1)) {
if (_mineController.statistics[vIndex + 1]['count'] <=
_mineController.averageNumberFiles) {
type = 1;
} else {
type = 2;
}
}
return Container(
color: type == 0
? Colors.grey[200]
: type == 1
? Colors.red[100]
: Colors.red,
);
},
),
),
DefaultTextStyle(
style: TextStyle(
color: Colors.grey,
fontSize: 10.sp,
),
child: const Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text('Jan'),
Text('Feb'),
Text('Mar'),
Text('Apr'),
Text('May'),
Text('Jun'),
Text('Jul'),
Text('Aug'),
Text('Sep'),
Text('Oct'),
Text('Nov'),
Text('Dec'),
],
),
),
],
), ),
), SizedBox(height: 10.w),
SizedBox(height: 10.w), Row(
Padding(
padding: EdgeInsets.symmetric(horizontal: 10.w),
child: Row(
children: [ children: [
_button(Icons.share, '分享图片', Colors.deepOrange, () {}), _button(Icons.share, '分享图片', Colors.deepOrange, () async {
EasyLoading.show();
Uint8List pngBytes = await _generate();
final tempDir = await getTemporaryDirectory();
final String fileName =
'share_${DateTime.now().millisecondsSinceEpoch}.png';
final file = File('${tempDir.path}/$fileName');
await file.writeAsBytes(pngBytes);
final result = await SharePlus.instance.share(ShareParams(
text: '分享 DEAPSOUND',
files: [XFile(file.path)],
));
if (result.status == ShareResultStatus.success) {
Get.back();
EasyLoading.showToast('分享完成!');
}
EasyLoading.dismiss();
}),
10.horizontalSpace, 10.horizontalSpace,
_button(Icons.download, '保存图片', Colors.cyan, () {}), _button(Icons.download, '保存图片', Colors.cyan, () async {
EasyLoading.show();
Uint8List pngBytes = await _generate();
final result = await ImageGallerySaverPlus.saveImage(
pngBytes,
quality: 100,
);
if (result['isSuccess']) {
Get.back();
EasyLoading.showToast('保存成功');
} else {
EasyLoading.showToast('保存失败');
}
EasyLoading.dismiss();
}),
], ],
), ),
), ],
], ),
), ),
], ],
); );
@ -324,4 +371,13 @@ class _StatisticsBottomSheetState extends State<StatisticsBottomSheet> {
), ),
); );
} }
Future<Uint8List> _generate() async {
RenderRepaintBoundary boundary =
_posterKey.currentContext!.findRenderObject()! as RenderRepaintBoundary;
ui.Image image = await boundary.toImage(pixelRatio: 6.0);
ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png);
Uint8List pngBytes = byteData!.buffer.asUint8List();
return pngBytes;
}
} }

2
pubspec.yaml

@ -69,7 +69,7 @@ dependencies:
flutter_facebook_auth: ^7.1.1 flutter_facebook_auth: ^7.1.1
flutter_image_compress: ^2.4.0 flutter_image_compress: ^2.4.0
common_utils: ^2.1.0 common_utils: ^2.1.0
# image_cropper: ^9.0.0 image_gallery_saver_plus: ^4.0.1
dio: ^5.8.0+1 dio: ^5.8.0+1
sqflite: ^2.4.2 sqflite: ^2.4.2
path: ^1.9.1 path: ^1.9.1

Loading…
Cancel
Save