import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; import 'package:app_settings/app_settings.dart'; import 'package:dio/dio.dart'; import 'package:fast/constants.dart'; import 'package:fast/model/model.dart'; import 'package:fast/utils/api.dart'; import 'package:fast/utils/global.dart'; import 'package:fast/utils/http_utils.dart'; import 'package:fast/utils/size_fit.dart'; import 'package:fast/view/component/toast.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:fluwx/fluwx.dart'; import 'package:image_gallery_saver/image_gallery_saver.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:screenshot/screenshot.dart'; import 'alert_widget.dart'; import 'challenge_result.dart'; import 'loading.dart'; // import 'package:widget_to_image/widget_to_image.dart'; // ignore: must_be_immutable class Share extends StatefulWidget { String type; FastResultBean fastResultBean = FastResultBean(); Share({Key? key, required this.type, FastResultBean? resultBean}) : super(key: key) { if (resultBean != null) { fastResultBean = resultBean; } } @override State createState() => _ShareState(); } const baseUrl = 'https://api.fast.dev.liveplus.fun/static/image/'; class _ShareState extends State { Uint8List? byteData; Uint8List? qrcodeData; ScreenshotController screenshotController = ScreenshotController(); @override void initState() { getQRCode(); super.initState(); } Future getQRCode() async { Dio dio = Dio(); // 注意:这里使用bytes dio.options.responseType = ResponseType.bytes; // 如果headers有东西,则添加 Map headers = { 'Authorization': 'Bearer ${Global().token}' }; dio.options.headers = headers; try { String platform = Platform.isIOS?'IOS':'ANDROID'; String path = Api.qrcode+'client='+platform; Response response = await dio.get(path); final Uint8List bytes = consolidateHttpClientResponseBytes(response.data); setState(() { qrcodeData = bytes; }); Timer(const Duration(seconds: 2), () { loadData(); }); } catch (e) { return await null; } // var data = await HttpUtils.get(Api.qrcode); // if (data != null) { // String aa = data as String; // Uint8List bytes = encode(aa); // setState(() { // qrcodeData = bytes; // }); // } // Timer(const Duration(seconds: 2), () { // loadData(); // }); } consolidateHttpClientResponseBytes(List data) { // response.contentLength is not trustworthy when GZIP is involved // or other cases where an intermediate transformer has been applied // to the stream. final List> chunks = >[]; int contentLength = 0; chunks.add(data); contentLength += data.length; final Uint8List bytes = Uint8List(contentLength); int offset = 0; for (List chunk in chunks) { bytes.setRange(offset, offset + chunk.length, chunk); offset += chunk.length; } return bytes; } Uint8List encode(String s) { var encodedString = utf8.encode(s); var encodedLength = encodedString.length; var data = ByteData(encodedLength + 4); data.setUint32(0, encodedLength, Endian.big); var bytes = data.buffer.asUint8List(); bytes.setRange(4, encodedLength + 4, encodedString); return bytes; } void loadData() { screenshotController.capture().then((value) { setState(() { byteData = value as Uint8List; }); }); } // Future loadData() async{ // ByteData data = await WidgetToImage.widgetToImage(detail()); // setState(() { // byteData=data; // }); // } Future share(int type) async { if (byteData == null) { return; } if (type == 2) { var status = await Permission.storage.request().isGranted; if (Platform.isIOS) { status = await Permission.photos.request().isGranted; var perStatus = await Permission.photos.status; if (perStatus.isLimited) { status = true; } } if (status == true) { var result = await ImageGallerySaver.saveImage(byteData!); //toast 保存成功 Navigator.of(context).pop(); showDialog( context: context, barrierDismissible: false, barrierColor: Colors.transparent, builder: (BuildContext context) { return Toast( title: '分享图已保存', content: const SizedBox( width: 0, ), ); }); } else { //处理拒绝的情况 showDialog( context: context, barrierDismissible: false, barrierColor: const Color(0xF2000D1F), builder: (BuildContext context) { return AlertWidget( title: '为了确保您的完整体验\n请开启fast16cc使用你的相册访问权限', confirm: '去开启', confirmCallback: () { AppSettings.openNotificationSettings(); Navigator.of(context).pop(); }); }); } return; } bool isSuccess = await registerWxApi( appId: 'wxa8557217acf4f532', universalLink: 'https://api.fast.liveplus.fun/'); if (isSuccess) { weChatResponseEventHandler.listen((event) { if (event is WeChatPaymentResponse) { } else if (event is WeChatAuthResponse) { print(event.code); } else if (event is WeChatShareResponse) { print(event.toString()); } }); } shareToWeChat( // WeChatShareTextModel("hello world") WeChatShareImageModel(WeChatImage.binary(byteData!), title: '风靡全球的16/8间歇性断食法\n高效又好玩,等你来挑战~', scene: type == 0 ? WeChatScene.SESSION : WeChatScene.TIMELINE)); } @override Widget build(BuildContext context) { SizeFit.initialize(context); return Material( child: Stack( children: [ Opacity( opacity: 0.01, child: Screenshot( child: widget.type == 'me' ? detail() : challenge(), controller: screenshotController), ), Container( width: 375, height: 750, color: kBgColor, ), Opacity( opacity: 1, child: Container( color: kBgColor, alignment: Alignment.center, child: Stack( children: [ Container( margin: EdgeInsets.only(top: 204.px), width: 335.px, height: 302.px, decoration: BoxDecoration( color: const Color(0xFF313F52), borderRadius: BorderRadius.all(Radius.circular(32.px))), child: Stack( children: [ Positioned( top: 12.px, right: 12.px, child: GestureDetector( onTap: () { Navigator.of(context).pop(); }, child: Container( alignment: Alignment.center, decoration: BoxDecoration( color: const Color(0x1A000000), borderRadius: BorderRadius.all( Radius.circular(18.px))), width: 36.px, height: 36.px, child: Image.asset( 'assets/images/close.png', width: 20.px, height: 20.px, ), ), )), Positioned( left: 0.0, right: 0.0, bottom: 32.px, child: Opacity( opacity: byteData == null ? 0.4 : 1.0, child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ GestureDetector( onTap: () { share(0); }, child: Column( children: [ Image.asset( 'assets/images/share_friend.png', width: 60.px, height: 60.px, ), SizedBox( height: 10.px, ), Text( '发送给好友', style: TextStyle( color: Colors.white, fontSize: 14.px), ) ], ), ), SizedBox( width: 20.px, ), GestureDetector( onTap: () { share(1); }, child: Column( children: [ Image.asset( 'assets/images/share_timeline.png', width: 60.px, height: 60.px, ), SizedBox( height: 10.px, ), Text( '分享到朋友圈', style: TextStyle( color: Colors.white, fontSize: 14.px), ) ], ), ), SizedBox( width: 20.px, ), GestureDetector( onTap: () { share(2); }, child: Column( children: [ Image.asset( 'assets/images/share_save.png', width: 60.px, height: 60.px, ), SizedBox( height: 10.px, ), Text( '保存到相册', style: TextStyle( color: Colors.white, fontSize: 14.px), ) ], ), ) ], )), ) ], ), ), Container( margin: EdgeInsets.only(left: 78.px), width: 180.px, height: 360.px, decoration: BoxDecoration( color: const Color(0xFF313F52), boxShadow: [ BoxShadow( offset: Offset(0.px, 8.px), blurRadius: 16.px, color: const Color(0x66000000)) ]), child: previewContent(), ) ], ))) ], ), ); } Widget previewContent() { // return Container(width: 180.px,height: 360.px,color: Colors.pink,); if (byteData != null) { return Image.memory( byteData!, width: 180.px, height: 360.px, fit: BoxFit.cover, ); } return Container( width: 180.px, height: 360.px, alignment: Alignment.center, margin: EdgeInsets.only(bottom: 20.px), child: Loading( showBackground: false, )); } challenge() { var size = MediaQuery.of(context).size; double rate = 1.0; if (size.height / size.width > 2.0) { if (size.width < 375.px) { rate = size.width / 375.px * 0.95; } } else { if (size.height < 750.px) { rate = size.height / 750.px * 0.95; } } return Container( width: 375.px * rate, height: 750.px * rate, color: Colors.transparent, child: Stack(alignment: AlignmentDirectional.topStart, children: [ Image.network( '${baseUrl}share_${widget.fastResultBean.days}.png', width: 375.px * rate, height: 750.px * rate, fit: BoxFit.cover, ), Column(crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox( width: 375.px * rate, height: 42.px * rate, ), Container( width: 80.px * rate, height: 80.px * rate, decoration: BoxDecoration(boxShadow: [ BoxShadow( offset: Offset(0, 6.px * rate), blurRadius: 12.px * rate, color: const Color(0x80000D1F)) ]), child: ClipOval( child: Image.network( Global().userBean!.avatar!, width: 80.px * rate, height: 80.px * rate, fit: BoxFit.cover, ), ), ), SizedBox( height: 11.px * rate, ), Text( Global().userBean!.nickname!, style: TextStyle( color: Colors.white, fontSize: 20.px * rate, height: 1.0, fontWeight: FontWeight.bold), ), Container( width: 324.px * rate, height: 260.px * rate, margin: EdgeInsets.only(top: 94.px * rate), decoration: BoxDecoration( color: const Color(0xCC000D1F), border: Border.all(color: kThemeColor, width: 1), borderRadius: BorderRadius.all(Radius.circular(34.px * rate)), image: const DecorationImage( image: AssetImage('assets/images/statistic.png'), alignment: Alignment.topRight, scale: 2.9)), child: Statistic( resultBean: widget.fastResultBean, rate: rate, )) ]), if (qrcodeData != null) Positioned( left: 245.px * rate, bottom: 106.px * rate, child: Image.memory( qrcodeData!, width: 100.px * rate, height: 100.px * rate, )), Positioned( left: 30.px * rate, bottom: 102.px * rate, child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: 5.px * rate, ), Text( Global().userBean!.inviteCode!, style: TextStyle( color: kThemeColor, fontSize: 36.px * rate, fontFamily: 'Exo2', fontWeight: FontWeight.w600), ), SizedBox(height: 2.px,), Text( '使用我的邀请码加入', style: TextStyle( color: Colors.white, fontSize: 14.px * rate, fontWeight: FontWeight.bold), ), Row( children: [ Text( '立即获得逆龄石', style: TextStyle( color: Colors.white, fontSize: 20.px * rate, fontWeight: FontWeight.bold), ), SizedBox( width: 4.px * rate, ), Text( '+3', style: TextStyle( color: const Color(0x99FFFFFF), fontSize: 20.px * rate, fontFamily: 'Exo2', fontWeight: FontWeight.w600), ), SizedBox( width: 4.px * rate, ), Image.asset( 'assets/images/stone.png', width: 24.px * rate, height: 24.px * rate, ) ], ) ], )), ])); } detail() { var size = MediaQuery.of(context).size; double rate = 1.0; if (size.height / size.width > 2.0) { if (size.width < 375.px) { rate = size.width / 375.px * 0.95; } } else { if (size.height < 750.px) { rate = size.height / 750.px * 0.95; } } return Container( width: rate * 375.px, height: rate * 750.px, color: Colors.transparent, child: Stack( alignment: AlignmentDirectional.topStart, children: [ Image.network( '${baseUrl}share_bg.png', width: rate * 375.px, height: rate * 750.px, fit: BoxFit.cover, ), Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox( width: rate * 375.px, height: rate * 46.px, ), Container( width: rate * 80.px, height: rate * 80.px, decoration: BoxDecoration(boxShadow: [ BoxShadow( offset: Offset(0, rate * 6.px), blurRadius: rate * 12.px, color: const Color(0x80000D1F)) ]), child: ClipOval( child: Image.network( Global().userBean!.avatar!, width: rate * 80.px, height: rate * 80.px, fit: BoxFit.cover, ), ), ), SizedBox( height: rate * 16.px, ), Text( Global().userBean!.nickname!, style: TextStyle( color: Colors.white, fontSize: rate * 20.px, fontWeight: FontWeight.bold), ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( margin: EdgeInsets.only(top: rate * 8.px), padding: EdgeInsets.only( left: rate * 9.px, right: rate * 9.px), height: rate * 24.px, alignment: Alignment.center, decoration: BoxDecoration( border: Border.all( color: const Color(0x80FFFFFF), width: 1, ), borderRadius: BorderRadius.all(Radius.circular(rate * 12.px)), color: const Color(0x33000D1F)), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( margin:EdgeInsets.only(bottom: 1.px), child: const Text( '逆龄石', style: TextStyle(color: Colors.white, fontSize: 12), ),), SizedBox( width: rate * 4.px, ), Image.asset( 'assets/images/stone.png', width: rate * 16.px, height: rate * 16.px, ), SizedBox( width: rate * 4.px, ), Container( margin:EdgeInsets.only(bottom: 2.px), child:Text( '×${Global().userBean!.rjvBalance}', style: TextStyle( color: kThemeColor, fontSize: rate * 14.px, fontFamily: 'Exo2', fontWeight: FontWeight.w600), )) ], ), ) ], ), ], ), if (qrcodeData != null) Positioned( left: rate * 245.px, bottom: rate * 106.px, child: Image.memory( qrcodeData!, width: rate * 100.px, height: rate * 100.px, )), Positioned( left: rate * 2.px, top: rate * 244.px, child: Text( Global().userBean!.nickname!, style: TextStyle( color: const Color(0xFF000D1F), fontSize: rate * 10.px, fontWeight: FontWeight.bold), )), Positioned( left: rate * 106.px, top: rate * 297.px, child: ClipOval( child: Image.network( Global().userBean!.avatar!, width: rate * 32.px, height: rate * 32.px, fit: BoxFit.cover, ), )), Positioned( left: rate * 143.px, top: rate * 298.px, child: Text( Global().userBean!.nickname!, style: TextStyle( color: Colors.white, fontSize: rate * 10.px, fontWeight: FontWeight.bold), )), Positioned( left: rate * 30.px, bottom: rate * 102.px, child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: rate * 5.px, ), Text( Global().userBean!.inviteCode!, style: TextStyle( color: kThemeColor, fontSize: rate * 36.px, fontFamily: 'Exo2', fontWeight: FontWeight.w600), ), SizedBox( height: rate * 2.px, ), Text( '使用我的邀请码加入', style: TextStyle( color: Colors.white, fontSize: rate * 14.px, fontWeight: FontWeight.bold), ), Row( children: [ Text( '立即获得逆龄石', style: TextStyle( color: Colors.white, fontSize: rate * 20.px, fontWeight: FontWeight.bold), ), SizedBox( width: rate * 4.px, ), Text( '+3', style: TextStyle( color: const Color(0x99FFFFFF), fontSize: rate * 20.px, fontFamily: 'Exo2', fontWeight: FontWeight.w600), ), SizedBox( width: rate * 4.px, ), Image.asset( 'assets/images/stone.png', width: rate * 24.px, height: rate * 24.px, ) ], ) ], )), ], )); } }