You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

341 lines
11 KiB

import 'dart:async';
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart' hide Image;
import 'package:flutter/rendering.dart';
import 'package:extended_image/extended_image.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:recook/constants/header.dart';
import 'package:recook/utils/image_utils.dart';
import 'package:recook/widgets/progress/re_toast.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 index;
// final List<PicSwiperItem> pics;
final Map arguments;
const PicSwiper({Key key, this.arguments});
static setArguments({int index, List<PicSwiperItem> pics}) {
return {"index": index, "pics": pics};
}
@override
_PicSwiperState createState() => _PicSwiperState();
}
class _PicSwiperState extends State<PicSwiper>
with SingleTickerProviderStateMixin {
var rebuildIndex = StreamController<int>.broadcast();
var rebuildSwiper = StreamController<bool>.broadcast();
AnimationController _animationController;
Animation<double> _animation;
Function animationListener;
// CancellationToken _cancelToken;
// CancellationToken get cancelToken {
// if (_cancelToken == null || _cancelToken.isCanceled)
// _cancelToken = CancellationToken();
//
// return _cancelToken;
// }
List<double> doubleTapScales = <double>[1.0, 2.0];
GlobalKey<ExtendedImageSlidePageState> slidePagekey =
GlobalKey<ExtendedImageSlidePageState>();
int currentIndex;
bool _showSwiper = true;
@override
void initState() {
currentIndex = widget.arguments["index"];
_animationController = AnimationController(
duration: const Duration(milliseconds: 150), vsync: this);
super.initState();
}
@override
void dispose() {
rebuildIndex.close();
rebuildSwiper.close();
_animationController?.dispose();
clearGestureDetailsCache();
//cancelToken?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
Widget result = Material(
/// if you use ExtendedImageSlidePage and slideType =SlideType.onlyImage,
/// make sure your page is transparent background
color: Colors.transparent,
shadowColor: Colors.transparent,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
ExtendedImageGesturePageView.builder(
itemBuilder: (BuildContext context, int index) {
var item = widget.arguments["pics"][index].picUrl;
Widget image = ExtendedImage.network(
item,
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),
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<double>(begin: begin, end: end));
_animation.addListener(animationListener);
_animationController.forward();
},
);
image = GestureDetector(
child: image,
onTap: () {
// slidePagekey.currentState.popPage();
Navigator.pop(context);
},
);
if (index == currentIndex) {
return Hero(
tag: item + index.toString(),
child: image,
flightShuttleBuilder: (BuildContext flightContext,
Animation<double> 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.arguments["pics"].length,
onPageChanged: (int index) {
currentIndex = index;
rebuildIndex.add(index);
},
controller: PageController(
initialPage: currentIndex,
),
scrollDirection: Axis.horizontal,
physics: BouncingScrollPhysics(),
// //move page only when scale is not more than 1.0
// canMovePage: (GestureDetails gestureDetails) =>
// gestureDetails.totalScale <= 1.0,
//physics: ClampingScrollPhysics(),
),
StreamBuilder<bool>(
builder: (c, d) {
if (d.data == null || !d.data) return Container();
return Positioned(
bottom: 0.0,
left: 0.0,
right: 0.0,
child: MySwiperPlugin(
widget.arguments["pics"],
currentIndex,
rebuildIndex,
),
);
},
initialData: true,
stream: rebuildSwiper.stream,
)
],
));
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<PicSwiperItem> pics;
final int index;
final StreamController<int> reBuild;
MySwiperPlugin(
this.pics,
this.index,
this.reBuild,
);
@override
Widget build(BuildContext context) {
return StreamBuilder<int>(
builder: (BuildContext context, data) {
return DefaultTextStyle(
style: TextStyle(color: Colors.blue),
child: Container(
height: 50.0 + ScreenUtil().bottomBarHeight,
alignment: Alignment.topCenter,
width: double.infinity,
color: Colors.grey.withOpacity(0.2),
child: Container(
height: 50.0,
child: Row(
children: _rowContainer(data, context),
),
)),
);
},
initialData: index,
stream: reBuild.stream,
);
}
_rowContainer(data, context) {
return <Widget>[
Container(
width: 10.0,
),
Text(
"${data.data + 1}",
),
Text(
" / ${pics.length}",
),
Expanded(
child: Text(pics[data.data].des ?? "",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 16.0, color: Colors.blue))),
Container(
width: 10.0,
),
GestureDetector(
child: Container(
padding: EdgeInsets.only(right: 10.0),
alignment: Alignment.center,
child: Text(
"保存到本地",
style: TextStyle(fontSize: 16.0, color: Colors.blue),
),
),
onTap: () {
//GSDialog.of(context).showLoadingDialog(context, "保存图片中...");
var cancel = ReToast.loading(text:"保存图片中...");
List<String> urls = [pics[data.data].picUrl];
ImageUtils.saveNetworkImagesToPhoto(urls, (index) {
DPrint.printf("保存好了---${urls[index]}");
DPrint.printf("保存好了---$index");
}, (success) {
cancel();
//GSDialog.of(context).dismiss(context);
success?ReToast.success(text: '保存完成!'):ReToast.err(text: '保存失败...');
// ? GSDialog.of(context).showSuccess(context, "保存完成!")
// : GSDialog.of(context).showError(context, "保存失败...");
});
},
),
];
}
}
class PicSwiperItem {
String picUrl;
String des;
PicSwiperItem(this.picUrl, {this.des = ""});
}