2025-09-17 15:32:18 +08:00
|
|
|
import 'dart:typed_data';
|
|
|
|
import 'dart:ui' as ui;
|
|
|
|
|
|
|
|
import 'package:cached_network_image/cached_network_image.dart';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter/rendering.dart';
|
|
|
|
import 'package:get/get.dart';
|
|
|
|
import 'package:image_gallery_saver_plus/image_gallery_saver_plus.dart';
|
|
|
|
import 'package:loopin/IM/controller/im_user_info_controller.dart';
|
|
|
|
import 'package:loopin/IM/im_friend_listeners.dart';
|
|
|
|
import 'package:loopin/components/my_toast.dart';
|
|
|
|
import 'package:loopin/styles/index.dart';
|
|
|
|
import 'package:loopin/utils/scan_code_type.dart';
|
|
|
|
import 'package:pretty_qr_code/pretty_qr_code.dart';
|
|
|
|
|
|
|
|
class MyQrcode extends StatelessWidget {
|
2025-09-18 16:13:37 +08:00
|
|
|
MyQrcode({
|
|
|
|
super.key,
|
|
|
|
this.prefix,
|
2025-09-22 14:41:47 +08:00
|
|
|
this.text,
|
2025-09-18 16:13:37 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
final String? prefix;
|
2025-09-22 14:41:47 +08:00
|
|
|
final String? text;
|
2025-09-17 15:32:18 +08:00
|
|
|
|
|
|
|
final controller = Get.find<ImUserInfoController>();
|
|
|
|
|
|
|
|
// GlobalKey 用于截图 Widget
|
|
|
|
final GlobalKey _qrKey = GlobalKey();
|
|
|
|
|
|
|
|
/// 将 Widget 渲染为 Uint8List
|
2025-09-18 16:13:37 +08:00
|
|
|
Future<Uint8List?> capturePng(BuildContext context) async {
|
2025-09-17 15:32:18 +08:00
|
|
|
try {
|
|
|
|
final boundary = _qrKey.currentContext?.findRenderObject() as RenderRepaintBoundary?;
|
|
|
|
if (boundary == null) return null;
|
|
|
|
|
2025-09-18 16:13:37 +08:00
|
|
|
// final image = await boundary.toImage(pixelRatio: ui.window.devicePixelRatio);
|
|
|
|
final pixelRatio = View.of(context).devicePixelRatio;
|
|
|
|
final image = await boundary.toImage(pixelRatio: pixelRatio);
|
2025-09-17 15:32:18 +08:00
|
|
|
final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
|
|
|
|
return byteData?.buffer.asUint8List();
|
|
|
|
} catch (e) {
|
|
|
|
logger.e("截图失败: $e");
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 保存二维码到相册
|
|
|
|
Future<void> saveQrToGallery() async {
|
|
|
|
logger.w('长安了');
|
2025-09-18 16:13:37 +08:00
|
|
|
final pngBytes = await capturePng(Get.context!);
|
2025-09-17 15:32:18 +08:00
|
|
|
if (pngBytes == null) return;
|
|
|
|
|
|
|
|
final result = await ImageGallerySaverPlus.saveImage(
|
|
|
|
pngBytes,
|
|
|
|
quality: 100,
|
|
|
|
name: "my_qr_${DateTime.now().millisecondsSinceEpoch}",
|
|
|
|
);
|
|
|
|
|
|
|
|
logger.w("保存结果: $result");
|
|
|
|
MyToast().tip(
|
|
|
|
title: '图片已保存',
|
|
|
|
position: 'top',
|
|
|
|
type: 'success',
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
final userID = controller.userID.value;
|
|
|
|
final faceUrl = controller.faceUrl.value;
|
|
|
|
ImageProvider face;
|
|
|
|
if (faceUrl.isEmpty) {
|
|
|
|
face = AssetImage('assets/images/logo/logo.png');
|
|
|
|
} else {
|
|
|
|
face = CachedNetworkImageProvider(faceUrl);
|
|
|
|
}
|
|
|
|
|
2025-09-18 16:13:37 +08:00
|
|
|
final data = '${prefix ?? QrTypeCode.hym}$userID';
|
|
|
|
|
2025-09-17 15:32:18 +08:00
|
|
|
return GestureDetector(
|
|
|
|
onLongPress: () {
|
|
|
|
showModalBottomSheet(
|
|
|
|
context: Get.context!,
|
|
|
|
backgroundColor: Colors.white,
|
|
|
|
shape: const RoundedRectangleBorder(
|
|
|
|
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
|
|
|
|
),
|
|
|
|
builder: (context) {
|
|
|
|
return SafeArea(
|
|
|
|
child: Column(
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
children: [
|
|
|
|
ListTile(
|
|
|
|
leading: const Icon(Icons.save, color: Colors.black),
|
|
|
|
title: const Text('保存到相册', style: TextStyle(color: Colors.black)),
|
|
|
|
onTap: () async {
|
|
|
|
await saveQrToGallery();
|
|
|
|
Get.back();
|
|
|
|
},
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
},
|
|
|
|
child: Center(
|
|
|
|
child: Container(
|
|
|
|
alignment: Alignment.topCenter,
|
2025-09-22 14:41:47 +08:00
|
|
|
decoration: const BoxDecoration(
|
2025-09-17 15:32:18 +08:00
|
|
|
color: Colors.transparent,
|
|
|
|
),
|
|
|
|
child: RepaintBoundary(
|
|
|
|
key: _qrKey,
|
2025-09-22 14:41:47 +08:00
|
|
|
child: Column(
|
|
|
|
mainAxisSize: MainAxisSize.min, // 根据内容高度自适应
|
|
|
|
children: [
|
|
|
|
PrettyQrView.data(
|
|
|
|
errorCorrectLevel: QrErrorCorrectLevel.H,
|
|
|
|
data: data, // 二维码内容
|
|
|
|
decoration: PrettyQrDecoration(
|
|
|
|
background: Colors.transparent,
|
|
|
|
shape: const PrettyQrShape.custom(
|
|
|
|
PrettyQrSmoothSymbol(color: FStyle.primaryColor),
|
|
|
|
finderPattern: PrettyQrSmoothSymbol(color: FStyle.primaryColor),
|
|
|
|
alignmentPatterns: PrettyQrSmoothSymbol(color: FStyle.primaryColor),
|
|
|
|
),
|
|
|
|
image: PrettyQrDecorationImage(
|
|
|
|
image: face,
|
|
|
|
scale: 0.3,
|
|
|
|
opacity: 1,
|
|
|
|
padding: const EdgeInsets.all(8.0),
|
|
|
|
),
|
|
|
|
quietZone: const PrettyQrQuietZone.modules(2),
|
|
|
|
),
|
2025-09-17 15:32:18 +08:00
|
|
|
),
|
2025-09-22 14:41:47 +08:00
|
|
|
if (text != null && text!.trim().isNotEmpty) ...[
|
|
|
|
const SizedBox(height: 4),
|
|
|
|
Text(
|
|
|
|
text!,
|
|
|
|
style: const TextStyle(
|
|
|
|
fontSize: 14,
|
|
|
|
color: Colors.grey,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
const SizedBox(height: 4),
|
|
|
|
],
|
|
|
|
],
|
2025-09-17 15:32:18 +08:00
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|