import 'dart:async'; import 'dart:io'; import 'dart:math'; import 'package:chat/data/UserData.dart'; import 'package:chat/data/WebData.dart'; import 'package:chat/data/constants.dart'; import 'package:chat/generated/i18n.dart'; import 'package:chat/home/VipPage.dart'; import 'package:chat/models/money_change.dart'; import 'package:chat/utils/CustomUI.dart'; import 'package:chat/utils/HttpUtil.dart'; import 'package:chat/utils/MessageMgr.dart'; import 'package:chat/utils/TokenMgr.dart'; import 'package:chat/utils/flutter_windowmanager.dart'; import 'package:chat/utils/screen.dart'; import 'package:dio/dio.dart'; import 'package:extended_image/extended_image.dart'; import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:oktoast/oktoast.dart'; import 'package:provider/provider.dart'; import 'ChargeMoney.dart'; double initScale({Size imageSize, Size size, double initialScale}) { var n1 = imageSize.height / imageSize.width; var n2 = size.height / size.width; if (n1 > n2) { final FittedSizes fittedSizes = applyBoxFit(BoxFit.contain, imageSize, size); //final Size sourceSize = fittedSizes.source; Size destinationSize = fittedSizes.destination; return size.width / destinationSize.width; } else if (n1 / n2 < 1 / 4) { final FittedSizes fittedSizes = applyBoxFit(BoxFit.contain, imageSize, size); //final Size sourceSize = fittedSizes.source; Size destinationSize = fittedSizes.destination; return size.height / destinationSize.height; } return initialScale; } class PicSwiper extends StatefulWidget { final int id; final List pics; PicSwiper({this.id, this.pics}); @override _PicSwiperState createState() => _PicSwiperState(); } class _PicSwiperState extends State with SingleTickerProviderStateMixin { var rebuildIndex = StreamController.broadcast(); var rebuildSwiper = StreamController.broadcast(); AnimationController _animationController; Animation _animation; Function animationListener; // CancellationToken _cancelToken; // CancellationToken get cancelToken { // if (_cancelToken == null || _cancelToken.isCanceled) // _cancelToken = CancellationToken(); // // return _cancelToken; // } List doubleTapScales = [1.0, 2.0]; GlobalKey slidePagekey = GlobalKey(); int currentIndex; bool _showSwiper = true; int _seconds = 0; bool isMyself = false; @override void initState() { setDisableScreenshots(); for (int i = 0; i < widget.pics.length; i++) { if (widget.id == widget.pics[i].id) { currentIndex = i; break; } } isMyself = widget.pics[0].userId == UserData().basicInfo.userId; _animationController = AnimationController( duration: const Duration(milliseconds: 150), vsync: this); super.initState(); } setDisableScreenshots() async { print('Platform.isAndroid ${Platform.isAndroid}'); if (Platform.isAndroid) { await FlutterWindowManager.addFlags(FlutterWindowManager.FLAG_SECURE); } } clearFalgs() async { if (Platform.isAndroid) { await FlutterWindowManager.clearFlags(FlutterWindowManager.FLAG_SECURE); } } @override void dispose() { rebuildIndex.close(); rebuildSwiper.close(); _animationController?.dispose(); clearGestureDetailsCache(); clearFalgs(); //cancelToken?.cancel(); super.dispose(); } void browsePhoto(id, userId, type, callback, {price = 0}) async { if (Provider.of(context).money < price) { CustomUI.buildOneConfirm(context, I18n.of(context).balance_insufficien, I18n.of(context).recharge, () { Navigator.of(context).pop(); ChargeMoney.showChargeSheet(context, () { setState(() {}); }); }); return; } var data = { "visitUserId": UserData().basicInfo.userId, "userId": userId, "pId": id, 'type': type, }; data['sign'] = TokenMgr().getSign(data); data['price'] = price; Response res = await HttpUtil().post('user/browse/photo', data: data,isShowLoading: true); Map resData = res.data; print(resData); if (resData['code'] == 0) { MessageMgr().emit('refresh_photo', id); callback(); } } @override Widget build(BuildContext context) { var size = MediaQuery.of(context).size; bool isAgree = (widget.pics[currentIndex].type == PhotoType.destroy.index || widget.pics[currentIndex].type == PhotoType.destroyMoney.index); var id = widget.pics[currentIndex].id; Widget result = Scaffold( // StreamBuilder( // builder: (BuildContext context, data) {}, // initialData: true, // stream: rebuildSwiper.stream, // ), appBar: AppBar( backgroundColor: AppColors.NewAppbarBgColor, title: StreamBuilder( builder: (BuildContext context, data) { return Text( '${data.data + 1} / ${widget.pics.length}', style: TextStyle( fontSize: 16, color: AppColors.NewAppbarTextColor), textScaleFactor: 1.0, ); }, initialData: currentIndex, stream: rebuildIndex.stream, ), leading: CustomUI.buildCustomLeading(context), centerTitle: true, elevation: 1, actions: [ isMyself ? InkWell( onTap: () { CustomUI.buildOneConfirm( context, I18n.of(context).sure_delete, I18n.of(context).determine, () { var id = widget.pics[currentIndex].id; HttpUtil().setPhote(context, id, 2, () { widget.pics.removeAt(currentIndex); MessageMgr().emit('refresh_photo', id); if (widget.pics.length == 0) { Navigator.of(context).pop(); } else { setState(() { if (currentIndex != 0) currentIndex--; }); } }); Navigator.of(context).pop(); }); }, child: Container( margin: EdgeInsets.only(right: 10), child: CircleAvatar( backgroundColor: Constants.GreyBackgroundColor, radius: 13.75, child: Icon( Icons.delete, color: Constants.BlackTextColor, size: 20, ))), ) : Container() ], ), backgroundColor: Colors.black, /// if you use ExtendedImageSlidePage and slideType =SlideType.onlyImage, /// make sure your page is transparent background // color: Colors.white, // shadowColor: Colors.transparent, bottomNavigationBar: isMyself ? Container( decoration: BoxDecoration( color: Colors.white, border: Border(top: Constants.GreyBorderSide)), height: 40, child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Checkbox( value: isAgree, activeColor: Colors.blue, onChanged: (bool val) { if(widget.pics[currentIndex].isCheck){ showToast(I18n.of(context).reviewing); return; } HttpUtil().setPhote(context, id, isAgree ? 0 : 1, () { this.setState(() { isAgree = !isAgree; //如果是红包照片焚阅照片则变成红包照片 //如果是普通焚阅照片则变成普通照片 MessageMgr().emit('refresh_photo'); if (isAgree) { if (widget.pics[currentIndex].type == PhotoType.free.index) { widget.pics[currentIndex].type = PhotoType.destroy.index; } if (widget.pics[currentIndex].type == PhotoType.money.index) { widget.pics[currentIndex].type = PhotoType.destroyMoney.index; } } else { if (widget.pics[currentIndex].type == PhotoType.destroy.index) { widget.pics[currentIndex].type = PhotoType.free.index; } if (widget.pics[currentIndex].type == PhotoType.destroyMoney.index) { widget.pics[currentIndex].type = PhotoType.money.index; } } }); }); }, ), fixedText(I18n.of(context).destroy_after) ], ), ) : Container( height: 0, ), body: Stack( //fit: StackFit.expand, children: [ ExtendedImageGesturePageView.builder( itemBuilder: (BuildContext context, int index) { var item = widget.pics[index]; var picUrl = item.picUrl; var file = item.file; var _timer; bool isShow = _seconds != 0; _cancelTimer() { item.isWatch = true; _seconds = 0; _timer?.cancel(); } _startTimer() { if (UserData().isMan()) { _seconds = UserData().isSuperVip ? 6 : (UserData().isVip ? 4 : 2); } else { _seconds = UserData().basicInfo.isAttestation ? 6 : 2; } _timer = new Timer.periodic(new Duration(seconds: 1), (timer) { _seconds--; setState(() {}); if (_seconds <= 0) { _cancelTimer(); } }); } Widget image; if (file != null) { image = ExtendedImage.file( file, fit: BoxFit.contain, //enableSlideOutPage: true, mode: ExtendedImageMode.gesture, initGestureConfigHandler: (state) { double initialScale = 1.0; if (state.extendedImageInfo != null && state.extendedImageInfo.image != null) { initialScale = initScale( size: size, initialScale: initialScale, imageSize: Size( state.extendedImageInfo.image.width.toDouble(), state.extendedImageInfo.image.height .toDouble())); } return GestureConfig( inPageView: true, //initialScale: initialScale, maxScale: max(initialScale, 5.0), initialAlignment: InitialAlignment.topCenter, animationMaxScale: max(initialScale, 5.0), //you can cache gesture state even though page view page change. //remember call clearGestureDetailsCache() method at the right time.(for example,this page dispose) cacheGesture: false); }, onDoubleTap: (ExtendedImageGestureState state) { //you can use define pointerDownPosition as you can, //default value is double tap pointer down postion. var pointerDownPosition = state.pointerDownPosition; double begin = state.gestureDetails.totalScale; double end; //remove old _animation?.removeListener(animationListener); //stop pre _animationController.stop(); //reset to use _animationController.reset(); if (begin == doubleTapScales[0]) { end = doubleTapScales[1]; } else { end = doubleTapScales[0]; } animationListener = () { //print(_animation.value); state.handleDoubleTap( scale: _animation.value, doubleTapPosition: pointerDownPosition); }; _animation = _animationController .drive(Tween(begin: begin, end: end)); _animation.addListener(animationListener); _animationController.forward(); }, ); } else { image = ExtendedImage.network( picUrl, fit: BoxFit.contain, // enableSlideOutPage: true, mode: ExtendedImageMode.gesture, initGestureConfigHandler: (state) { double initialScale = 1.0; if (state.extendedImageInfo != null && state.extendedImageInfo.image != null) { initialScale = initScale( size: size, initialScale: initialScale, imageSize: Size( state.extendedImageInfo.image.width.toDouble(), state.extendedImageInfo.image.height .toDouble())); } return GestureConfig( inPageView: true, //initialScale: initialScale, maxScale: max(initialScale, 5.0), initialAlignment: InitialAlignment.topCenter, animationMaxScale: max(initialScale, 5.0), //you can cache gesture state even though page view page change. //remember call clearGestureDetailsCache() method at the right time.(for example,this page dispose) cacheGesture: false); }, onDoubleTap: (ExtendedImageGestureState state) { //you can use define pointerDownPosition as you can, //default value is double tap pointer down postion. var pointerDownPosition = state.pointerDownPosition; double begin = state.gestureDetails.totalScale; double end; //remove old _animation?.removeListener(animationListener); //stop pre _animationController.stop(); //reset to use _animationController.reset(); if (begin == doubleTapScales[0]) { end = doubleTapScales[1]; } else { end = doubleTapScales[0]; } animationListener = () { //print(_animation.value); state.handleDoubleTap( scale: _animation.value, doubleTapPosition: pointerDownPosition); }; _animation = _animationController .drive(Tween(begin: begin, end: end)); _animation.addListener(animationListener); _animationController.forward(); }, ); } image = GestureDetector( child: Container( child: image, ), onTap: () { slidePagekey.currentState.popPage(); Navigator.pop(context); }, ); //滤镜背景 var bg = Center( child: ClipRect( //裁切长方形 child: BackdropFilter( //背景滤镜器 filter: ui.ImageFilter.blur( sigmaX: 20.0, sigmaY: 20.0), //图片模糊过滤,横向竖向都设置5.0 child: Opacity( //透明控件 opacity: 0.5, child: Container( alignment: Alignment.center, width: Screen.width, height: Screen.height, color: Colors.black, ), ), ), ), ); //焚阅照片 if ((item.type == PhotoType.destroy.index || item.type == PhotoType.destroyMoney.index && item.isBuy) && item.userId != UserData().basicInfo.userId) { image = GestureDetector( onLongPress: item.isWatch ? null : () { browsePhoto(item.id, item.userId, 1, () { _startTimer(); setState(() {}); }); }, onLongPressEnd: item.isWatch ? null : (detail) { _cancelTimer(); setState(() {}); }, onTap: () { slidePagekey.currentState.popPage(); Navigator.pop(context); }, child: Container( color: Colors.grey.shade200, child: Stack( alignment: Alignment.center, children: [ image, isShow ? Container() : Stack( alignment: Alignment.center, children: [ bg, item.isWatch ? Container( height: 211, width: 275.5, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.all( Radius.circular( 5))), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( IconData( 0xe634, fontFamily: 'iconfont', ), color: const Color( 0xFFA7A7A7), size: 50, ), fixedText( I18n.of(context) .destroyed, fontSize: 16, color: const Color( 0xFFA7A7A7)), UserData().isVip || UserData() .basicInfo .sex == 2 ? Container() : fixedText( I18n.of(context) .longTime, color: Colors .grey[700], fontSize: 12), UserData().isVip || UserData() .basicInfo .sex == 2 ? Container() : SizedBox(height: 10), UserData().isVip || UserData() .basicInfo .sex == 2 ? Container() : RaisedButton( color: Colors.blue, onPressed: () { Navigator.of( context) .push( new MaterialPageRoute( builder: (context) { return VipPage(); }, ), ); }, child: fixedText( I18n.of(context) .joinvip, color: Colors .white), ) ], )) : Container( height: 211, width: 275.5, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.all( Radius.circular( 5))), child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ Padding( padding: EdgeInsets.only( top: 10, bottom: 2), child: Icon( IconData( 0xe634, fontFamily: 'iconfont', ), color: const Color( 0xFFF44236), size: 50, )), fixedText( I18n.of(context) .destroy_after, color: const Color( 0xFFF44236), fontSize: 15), Padding( padding: EdgeInsets.only( top: 25, bottom: 15), child: fixedText( I18n.of(context) .longClick, color: const Color( 0xFFFA8880), fontSize: 12), ), Image.asset( 'assets/images/zhiwen.png', height: 55, ) ], )) ], ) ], ))); } //红包照片 if ((item.type == PhotoType.money.index || (item.type == PhotoType.destroyMoney.index && !item.isBuy)) && item.userId != UserData().basicInfo.userId) { image = GestureDetector( onTap: () { slidePagekey.currentState.popPage(); Navigator.pop(context); }, child: Container( color: Colors.grey.shade200, child: Stack( alignment: Alignment.center, children: [ image, item.isBuy ? Container() : Stack( alignment: Alignment.center, children: [ bg, Stack( alignment: Alignment.center, children: [ Image.asset( 'assets/images/login/money.png', fit: BoxFit.fitWidth, width: 220, ), Container( margin: EdgeInsets.only(top: 65), child: fixedText( I18n.of(context) .less_coin .replaceFirst( '/s1', UserData() .redPhotoPrice .toString()), color: const Color(0xFFFAE6B4), fontSize: 25), ), InkWell( onTap: () { CustomUI.buildOneConfirm( context, I18n.of(context) .pay_unlock .replaceFirst( '/s1', UserData() .redPhotoPrice .toString()), I18n.of(context) .confirm_pay, () { browsePhoto( item.id, item.userId, 2, () { setState(() { Navigator.of(context) .pop(); MessageMgr().emit( 'refresh_photo', item.id); item.isBuy = true; }); }, price: UserData() .redPhotoPrice); }); }, child: Container( decoration: BoxDecoration( color: const Color( 0xFFFAE6B4), borderRadius: BorderRadius.all( Radius.circular( 30))), padding: EdgeInsets.only( left: 40, right: 40, top: 5, bottom: 5), margin: EdgeInsets.only(top: 140), child: fixedText( I18n.of(context) .determine, color: const Color( 0xFF7B0408)), )) ], ) ], ) ], ))); } if (index == currentIndex) { return Hero( tag: picUrl + index.toString(), child: image, flightShuttleBuilder: (BuildContext flightContext, Animation animation, HeroFlightDirection flightDirection, BuildContext fromHeroContext, BuildContext toHeroContext) { final Hero hero = flightDirection == HeroFlightDirection.pop ? fromHeroContext.widget : toHeroContext.widget; return hero.child; }, ); } else { return image; } }, itemCount: widget.pics.length, onPageChanged: (int index) { currentIndex = index; rebuildIndex.add(index); }, controller: PageController( initialPage: currentIndex, ), scrollDirection: Axis.horizontal, physics: BouncingScrollPhysics(), ), _seconds == 0 ? Container() : Container( padding: EdgeInsets.only(bottom: 30), alignment: Alignment.bottomCenter, child: Stack( alignment: Alignment.center, children: [ CircularProgressIndicator( strokeWidth: 4.0, backgroundColor: Colors.grey, value: _seconds / 6, valueColor: new AlwaysStoppedAnimation(Colors.red), ), fixedText(_seconds.toString(), color: Colors.red) // Positioned( // top: 8, // left: 13, // child: // fixedText(_seconds.toString(), color: Colors.red), // ) ], )), ], )); result = ExtendedImageSlidePage( key: slidePagekey, child: result, slideAxis: SlideAxis.both, slideType: SlideType.onlyImage, onSlidingPage: (state) { ///you can change other widgets' state on page as you want ///base on offset/isSliding etc //var offset= state.offset; var showSwiper = !state.isSliding; if (showSwiper != _showSwiper) { // do not setState directly here, the image state will change, // you should only notify the widgets which are needed to change // setState(() { // _showSwiper = showSwiper; // }); _showSwiper = showSwiper; rebuildSwiper.add(_showSwiper); } }, ); return result; } } class MySwiperPlugin extends StatelessWidget { final List pics; final int index; final StreamController reBuild; final bool isMyself; final callback; MySwiperPlugin( this.pics, this.index, this.reBuild, this.isMyself, this.callback); @override Widget build(BuildContext context) { return StreamBuilder( builder: (BuildContext context, data) { return DefaultTextStyle( style: TextStyle(color: Colors.white), child: Container( //alignment: Alignment.center, height: 50.0, width: double.infinity, color: Colors.white, child: Row( children: [ InkWell( onTap: () { Navigator.of(context).pop(); }, child: Container( margin: EdgeInsets.only(left: 10), child: Icon( Icons.chevron_left, color: Constants.BlackTextColor, )), ), Expanded( child: Container( alignment: Alignment.center, child: fixedText("${data.data + 1} / ${pics.length}", color: Constants.BlackTextColor))), isMyself ? InkWell( onTap: () { CustomUI.buildOneConfirm( context, I18n.of(context).sure_delete, I18n.of(context).determine, () { callback(index); Navigator.of(context).pop(); }); }, child: Container( margin: EdgeInsets.only(right: 10), child: Icon( Icons.delete, color: Constants.BlackTextColor, )), ) : Container(), ], )), ); }, initialData: index, stream: reBuild.stream, ); } } class PicSwiperItem { var userId; int id; String picUrl; String des; File file; int type; bool isBuy; //是否购买 bool isWatch; //是否观看 bool isCheck;//是否审核中 PicSwiperItem( this.picUrl, { this.file, this.id, this.des = "", this.type = 0, this.isBuy = false, this.isWatch = false, this.userId, this.isCheck = false, }); }