From 9ba63c4f935eab39f5fcba966fabf152261995e1 Mon Sep 17 00:00:00 2001 From: "23028876916@qq.com" Date: Mon, 8 Feb 2021 17:19:05 +0800 Subject: [PATCH] =?UTF-8?q?0208=20=20=E6=96=B0=E5=A2=9E=E6=9C=8B=E5=8F=8B?= =?UTF-8?q?=E5=9C=88=E5=88=86=E4=BA=AB=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../goods_share_template.dart | 16 +- .../goods_share_template/qr_code.dart | 39 ++ .../share/models/share_data_model.dart | 2 +- .../share/models/share_select_pic_model.dart | 15 + lib/widgets/share/share_alert_select.dart | 554 ++++++++++++++++++ 5 files changed, 618 insertions(+), 8 deletions(-) create mode 100644 lib/template/goods_share_template/qr_code.dart create mode 100644 lib/widgets/share/models/share_select_pic_model.dart create mode 100644 lib/widgets/share/share_alert_select.dart diff --git a/lib/template/goods_share_template/goods_share_template.dart b/lib/template/goods_share_template/goods_share_template.dart index 22b0983..caa2d62 100644 --- a/lib/template/goods_share_template/goods_share_template.dart +++ b/lib/template/goods_share_template/goods_share_template.dart @@ -4,6 +4,7 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:zhiying_base_widget/pages/goods_share_page/models/goods_share_poster_model.dart'; import 'package:zhiying_base_widget/pages/goods_share_page/models/goods_share_poster_style_model.dart'; +import 'package:zhiying_base_widget/template/goods_share_template/qr_code.dart'; import 'package:zhiying_base_widget/widgets/home/home_quick_entry/cached_network_image_util.dart'; import 'package:zhiying_comm/zhiying_comm.dart'; @@ -116,13 +117,14 @@ class GoodsShareTemplate extends StatelessWidget { ), Column( children: [ - Container( - margin: EdgeInsets.only(bottom: 12), - width: 124, - height: 124, - // color: Colors.redAccent, - child: Image.memory(convert.base64Decode(model.qrcode)), - ), + QrCode(model.qrcode), + // Container( + // margin: EdgeInsets.only(bottom: 12), + // width: 124, + // height: 124, + // // color: Colors.redAccent, + // child: Image.memory(convert.base64Decode(model.qrcode)), + // ), Container( child: Row( children: [ diff --git a/lib/template/goods_share_template/qr_code.dart b/lib/template/goods_share_template/qr_code.dart new file mode 100644 index 0000000..2154a55 --- /dev/null +++ b/lib/template/goods_share_template/qr_code.dart @@ -0,0 +1,39 @@ +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'dart:convert' as convert; + +class QrCode extends StatefulWidget { + final String qrcode; + + const QrCode(this.qrcode, {Key key}) : super(key: key); + + @override + _QrCodeState createState() => _QrCodeState(); +} + +class _QrCodeState extends State with AutomaticKeepAliveClientMixin { + Uint8List src; + + @override + void initState() { + // TODO: implement initState + src = convert.base64Decode(widget.qrcode); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Container( + margin: EdgeInsets.only(bottom: 12), + width: 124, + height: 124, + // color: Colors.redAccent, + child: Image.memory(src), + ); + } + + @override + // TODO: implement wantKeepAlive + bool get wantKeepAlive => true; +} diff --git a/lib/widgets/share/models/share_data_model.dart b/lib/widgets/share/models/share_data_model.dart index 662e754..9080ba3 100644 --- a/lib/widgets/share/models/share_data_model.dart +++ b/lib/widgets/share/models/share_data_model.dart @@ -9,7 +9,7 @@ class ShareDataModel { String title; String content; Uint8List poster; // 海报 - List image; // 图片地址列表 + List image; // 图片地址列表 String url; ShareDataModel({ diff --git a/lib/widgets/share/models/share_select_pic_model.dart b/lib/widgets/share/models/share_select_pic_model.dart new file mode 100644 index 0000000..fbcb18b --- /dev/null +++ b/lib/widgets/share/models/share_select_pic_model.dart @@ -0,0 +1,15 @@ +import 'package:zhiying_base_widget/pages/goods_share_page/models/goods_share_poster_model.dart'; + +class ShareSelectPicModel { + List picList; + GoodsSharePosterModel posterModel; + + ShareSelectPicModel(this.posterModel, this.picList); +} + +class SharePicItem { + String pic; + bool select = false; + + SharePicItem(this.pic, this.select); +} diff --git a/lib/widgets/share/share_alert_select.dart b/lib/widgets/share/share_alert_select.dart new file mode 100644 index 0000000..1f82b99 --- /dev/null +++ b/lib/widgets/share/share_alert_select.dart @@ -0,0 +1,554 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; +import 'dart:ui'; +import 'dart:ui' as ui; + +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_swiper/flutter_swiper.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:share_extend/share_extend.dart'; +import 'package:sharesdk_plugin/sharesdk_plugin.dart'; +import 'package:zhiying_base_widget/dialog/loading/loading.dart'; +import 'package:zhiying_base_widget/utils/image_download_util/image_download_util.dart'; +import 'package:zhiying_base_widget/widgets/share/models/share_alert_model.dart'; +import 'package:zhiying_base_widget/widgets/share/models/share_data_model.dart'; +import 'package:zhiying_base_widget/widgets/share/models/share_icon_model.dart'; +import 'package:zhiying_base_widget/widgets/share/share_alert_content.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; +import 'package:zhiying_base_widget/widgets/share/models/share_select_pic_model.dart'; +import 'package:zhiying_base_widget/template/goods_share_template/goods_share_template.dart'; + +class ShareAlertSelect extends StatefulWidget { + final String skipIdentifier; + final bool isContentShow; + final ShareDataModel model; + final bool isPicShow; + final ShareSelectPicModel selectPicModel; + + const ShareAlertSelect(this.model, this.skipIdentifier, + {Key key, this.isContentShow = false, this.selectPicModel, this.isPicShow}) + : super(key: key); // 中间视图 + + @override + _ShareAlertSelectState createState() => _ShareAlertSelectState(); +} + +class _ShareAlertSelectState extends State { + ShareAlertModel _iconModel; + bool isPicShow; + ShareSelectPicModel selectPicModel; + GlobalKey _globalKey = GlobalKey(); + + @override + void initState() { + NetUtil.request('/api/v1/mod/${widget.skipIdentifier}', method: NetMethod.GET, + onCache: (data) { + // try{ + // _parseData(data); + // }catch(e){ + // print(e); + // } + }, onSuccess: (data) { + print(data); + _parseData(data); + }, onError: (err) {}); + + if (!EmptyUtil.isEmpty(widget.isPicShow)) { + isPicShow = widget.isPicShow; + } else { + isPicShow = false; + } + selectPicModel = widget.selectPicModel; + super.initState(); + } + + void _parseData(Map data) { + List modList = data['mod_list']; + Map d = modList.first; + if (d != null) { + String dString = d['data']; + _iconModel = + ShareAlertModel.fromJson(Map.from(jsonDecode(dString))); + + setState(() {}); + } + } + + _selectPic(List picList, int position) async { + ShareDataModel model = ShareDataModel(); + model.image = []; + for (int i = 0; i < picList.length; i++) { + if (picList[i].select) { + if (i == 0) { + ShareDataModel shareDataModel = await _updateModel(_globalKey); + model.poster = shareDataModel.poster; + } else { + model.image.add(picList[i].pic); + } + } + } + EventUtil.instance.fire(model); + } + + Future _updateModel(GlobalKey _globalKey) async { + ShareDataModel _shareModel = ShareDataModel(); + BuildContext buildContext = _globalKey.currentContext; + if (null != buildContext) { + RenderRepaintBoundary boundary = buildContext.findRenderObject(); + ui.Image image = await boundary.toImage(pixelRatio: 1); + // 注意:png是压缩后格式,如果需要图片的原始像素数据,请使用rawRgba + ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png); + Uint8List pngBytes = byteData.buffer.asUint8List(); + _shareModel.poster = pngBytes; + } else { + _shareModel.poster = null; + } + // _shareModel.content = _isContentSelected ? _content : ''; + return _shareModel; + } + + @override + Widget build(BuildContext context) { + return WillPopScope( + onWillPop: () async { + Loading.dismiss(); + Navigator.canPop(context); + return true; + }, + child: GestureDetector( + child: Scaffold( + backgroundColor: Colors.transparent, + body: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), //背景 + child: Container( + child: Column( + children: [ + widget.isContentShow + ? Expanded( + child: Center(child: ShareAlertContent(_iconModel)), + ) + : Container(), + isPicShow + ? Expanded( + child: Swiper( + loop: false, + itemBuilder: (BuildContext context, int index) { + Widget picWidget; + if (index == 0) { + picWidget = Expanded( + child: Transform.scale( + alignment: Alignment.center, + scale: 0.75, + child: GoodsShareTemplate( + selectPicModel.posterModel, + contentKey: _globalKey, + ), + ), + ); + } else { + picWidget = CachedNetworkImage( + imageUrl: selectPicModel.picList[index].pic, + fit: BoxFit.fitWidth, + ); + } + return Container( + alignment: Alignment.bottomCenter, + margin: EdgeInsets.only(top: 50, bottom: 10), + child: Stack( + children: [ + picWidget, + // CachedNetworkImage( + // imageUrl: selectPicModel.picList[index].pic, + // fit: BoxFit.fitWidth, + // ), + Positioned( + bottom: 10, + right: 15, + child: GestureDetector( + onTap: () { + selectPicModel.picList[index].select = + !selectPicModel.picList[index].select; + setState(() {}); + _selectPic(selectPicModel.picList, index); + }, + child: Container( + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(50), + color: selectPicModel.picList[index].select + ? Colors.red + : Colors.grey, + ), + child: Icon( + CupertinoIcons.check_mark, + color: Colors.white, + ), + ), + ), + ) + ], + ), + ); + }, + itemCount: selectPicModel.picList.length, + viewportFraction: 0.8, + scale: 0.8, + ), + ) + : Container(), + _ShareAlertContent( + widget.model, widget.skipIdentifier, _iconModel, isPicShow), + ], + ), + ), // 模糊化 + ), + ), + onTap: () { + // Navigator.of(context).pop(); + }, + ), + ); + } +} + +class _ShareAlertContent extends StatefulWidget { + final ShareDataModel model; + final String skipIdentifier; + final ShareAlertModel iconModel; + final bool isSelectPic; + + const _ShareAlertContent( + this.model, this.skipIdentifier, this.iconModel, this.isSelectPic, + {Key key}) + : super(key: key); + + @override + _ShareAlertContentState createState() => _ShareAlertContentState(); +} + +class _ShareAlertContentState extends State<_ShareAlertContent> { + StreamSubscription listen; + bool isSelectPic; + + ShareDataModel _shareDataModel; + + @override + void dispose() { + // TODO: implement dispose + listen?.cancel(); + super.dispose(); + } + + @override + void initState() { + // TODO: implement initState + isSelectPic = widget.isSelectPic; + + if (isSelectPic) { + _shareDataModel = null; + } else { + _shareDataModel = widget?.model; + } + listen = EventUtil.instance.on().listen((ShareDataModel event) { + if (isSelectPic) { + _shareDataModel = event; + } + }); + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () {}, + child: Container( + width: double.infinity, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(12), + topRight: Radius.circular(12), + ), + ), + child: SafeArea( + top: false, + child: Column( + children: [ + Container( + margin: EdgeInsets.only(top: 8, bottom: 8), + width: 62, + height: 4, + decoration: BoxDecoration( + color: Color(0xffd8d8d8), borderRadius: BorderRadius.circular(2)), + ), + Text( + '分享至', + style: TextStyle( + fontSize: 15, color: Color(0xff333333), fontWeight: FontWeight.bold), + ), + Container( + margin: EdgeInsets.only(left: 12, right: 12, top: 10, bottom: 10), + child: _createIcons(), + ), + GestureDetector( + child: Container( + margin: EdgeInsets.only(left: 12, right: 12, bottom: 10), + padding: EdgeInsets.all(12), + decoration: BoxDecoration( + color: Color(0xfff3f3f3), borderRadius: BorderRadius.circular(8)), + child: Center( + child: Text( + '取消', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + color: Color(0xff999999)), + ), + ), + ), + onTap: () { + Navigator.of(context).pop(); + }, + ) + ], + ), + ), + ), + ); + } + + Widget _createIcons() { + return Wrap( + spacing: 10, + runSpacing: 10, + children: widget.iconModel?.icons?.map((item) { + return _createIcon(item); + })?.toList() ?? + [], + ); + } + + Widget _createIcon(ShareIconModel item) { + return GestureDetector( + child: Container( + width: 60, + child: Column( + children: [ + Container( + width: 40, + height: 40, + child: CachedNetworkImage( + imageUrl: item.icon, + fit: BoxFit.contain, + ), + ), + Padding( + padding: const EdgeInsets.only(top: 2, bottom: 2), + child: Text( + item.name, + style: TextStyle( + fontSize: 12, color: Color(0xff333333), fontWeight: FontWeight.bold), + ), + ), + ], + ), + ), + onTap: () async { + //检查是否有存储权限 + var status = await Permission.storage.status; + if (!status.isGranted) { + status = await Permission.storage.request(); + print(status); + return; + } + + if (item.type == 'wx') { + _shareByMob(ShareSDKPlatforms.wechatSession); + } else if (item.type == 'pyq') { + _shareByMob(ShareSDKPlatforms.wechatTimeline); + } else if (item.type == 'qq') { + _shareByMob(ShareSDKPlatforms.qq); + } else if (item.type == 'qq_space') { + _shareByMob(ShareSDKPlatforms.qZone); + } else if (item.type == 'weibo') { + _shareByMob(ShareSDKPlatforms.sina); + } else if (item.type == 'more_setting') { + _shareBySystem(); + } + }, + ); + } + + // mob分享,只能单图分享,多图分享调用系统分享 + void _shareByMob(ShareSDKPlatform plateform) async { + if (isSelectPic && + EmptyUtil.isEmpty(_shareDataModel) && + EmptyUtil.isEmpty(_shareDataModel?.poster) && + EmptyUtil.isEmpty(_shareDataModel?.image)) { + Fluttertoast.showToast(msg: '请选择分享图片'); + return; + } + int count = 0; + if (_shareDataModel.poster != null) { + count++; + } + count += (_shareDataModel?.image?.length ?? 0); + // 多图分享 + if (count > 1) { + _shareMultipleImages(); + return; + } + + Loading.show(context); + Timer(Duration(milliseconds: 2000), () { + Loading.dismiss(); + }); + + SSDKMap params; + if (_shareDataModel.poster != null) { + String path = await _savePoster(); + if (path != null && path != '') { + params = SSDKMap() + ..setGeneral( + _shareDataModel?.title ?? '', + _shareDataModel?.content ?? '', + Platform.isIOS ? path : null, + null, + Platform.isAndroid ? path : null, + null, + null, + null, + null, + null, + SSDKContentTypes.image, + ); + } + } else { + var type = SSDKContentTypes.auto; + + if (_shareDataModel?.image?.first != null && _shareDataModel?.url != null) { + type = SSDKContentTypes.webpage; + } else if (_shareDataModel?.image?.first != null) { + type = SSDKContentTypes.image; + } else if (_shareDataModel?.title != null || _shareDataModel?.content != null) { + type = SSDKContentTypes.text; + } + if (plateform == ShareSDKPlatforms.qZone) { + _shareDataModel?.title = null; + type = SSDKContentTypes.message; + } + + params = SSDKMap() + ..setGeneral( + _shareDataModel?.title ?? '', + _shareDataModel?.content ?? '', + Platform.isIOS ? _shareDataModel.image : null, + Platform.isAndroid ? _shareDataModel?.image?.first : null, + null, + _shareDataModel.url, + null, + null, + null, + null, + type, + ); + } + SharesdkPlugin.share(plateform, params, + (SSDKResponseState state, Map userdata, Map contentEntity, SSDKError error) { + print(error); + if (state == SSDKResponseState.Fail) { + Fluttertoast.showToast(msg: '分享失败'); + } else if (state == SSDKResponseState.Success) { + //Fluttertoast.showToast(msg: '分享成功'); + } else if (state == SSDKResponseState.Cancel) { + Fluttertoast.showToast(msg: '取消分享'); + } + Logger.debug('${state}, ${error.rawData}'); + Loading.dismiss(); + }); + } + + // 系统分享,只能分享图片或者文字,不能组合分享 + void _shareBySystem() async { + if (isSelectPic && + EmptyUtil.isEmpty(_shareDataModel) && + EmptyUtil.isEmpty(_shareDataModel?.poster) && + EmptyUtil.isEmpty(_shareDataModel?.image)) { + Fluttertoast.showToast(msg: '请选择分享图片'); + return; + } + int count = 0; + if (_shareDataModel.poster != null) { + count++; + } + count += (_shareDataModel?.image?.length ?? 0); + // 多图分享 + if (count > 1) { + _shareMultipleImages(); + return; + } + + if (_shareDataModel.poster != null) { + String path = await _savePoster(); + if (path != null && path != '') { + ShareExtend.share(path, 'image'); + } + } else { + ShareExtend.share(_shareDataModel.content, 'text'); + } + } + + Future _savePoster() async { + String path; + if (_shareDataModel.poster != null) { + // 检查并请求权限 + var status = await Permission.storage.status; + if (status != PermissionStatus.granted) { + status = await Permission.storage.request(); + } + if (status == PermissionStatus.denied) { + Fluttertoast.showToast(msg: '暂无权限,分享失败'); + return null; + } + + try { + // 保存到本地路径 + final tempDir = await getTemporaryDirectory(); + final file = await File('${tempDir.path}/image.jpg').create(); + file.writeAsBytesSync(_shareDataModel.poster); + + path = file.path; + Logger.debug(file.path); + } catch (err, s) { + Logger.error(err.toString(), s.toString()); + Fluttertoast.showToast(msg: '分享失败'); + return null; + } + } + return path; + } + + // 多图分享,调用系统分享 + void _shareMultipleImages() async { + List paths = List(); + String path = await _savePoster(); + if (path != null && path != '') { + paths.add(path); + } + + Loading.show(context); + List downPaths = await ImageDownloadUtil.download(_shareDataModel.image); + paths.addAll(downPaths); + ShareExtend.shareMultiple(paths, "image", subject: ""); + Loading.dismiss(); + } +}