先看效果:
- 用到的屏幕截屏插件(插件的用法大家参考该插件文档):
screenshot: ^0.2.0
-
封装思路
-
手势如何操作执行截屏呢? -> GestureDetector
-
多子叶布局用什么?考虑应该覆盖,选用了Stack
-
截图显示的位置应该可以显示和隐藏,且将其放到Stack最后一层 -> Offstage
-
最后就是截屏显示后的动画
布局代码
@override
Widget build(BuildContext context) {
return GestureDetector(
onLongPress: () {
_screenShot();
},
child: Screenshot(
controller: screenshotController,
child: Container(
width: double.infinity,
height: double.infinity,
child: Stack(
children: [
widget.child,
Positioned(
bottom: animationPadding.value,
right: 20,
child: Offstage(
offstage: isHideScreenArea,
child: Container(
decoration: BoxDecoration(
color: Colors.blueGrey, border: Border.all(width: 1)),
width: 120,
height: 200,
child: imageFile == null
? null
: Image.file(imageFile, fit: BoxFit.fill),
),
),
)
],
),
),
),
);
}
动画代码
动画想要实现跳动的感觉,用到补间对象tween和时间曲线函数对象Curve.
原理 : 根据时间曲线函数我们拿到时间内不同的值,然后给显示图片的组件设置padding值,从而达到移动的效果.
Cure动画效果可以参考这篇博客:
https://blog.csdn.net/qq_17766199/article/details/95632571
_initAnim() {
animationController = AnimationController(
value: 40, duration: Duration(seconds: 2), vsync: this)
..addListener(() {
setState(() {});
});
curve =
CurvedAnimation(parent: animationController, curve: Curves.bounceOut);
animationPadding = Tween(begin: 40.0, end: 10.0).animate(curve);
}
代码中设置了40 - 10的区间变化,也就是padding值会随着时间函数在这个范围内变化,达到移动效果.
注意:动画开始前最好执行reset
animationController.reset();
animationController.forward();
最后附上组件所有代码
class ScreenshotWidget extends StatefulWidget {
final Widget child;
final Duration duration;
ScreenshotWidget({Key key, this.child, this.duration});
@override
_ScreenshotWidgetState createState() => _ScreenshotWidgetState();
}
class _ScreenshotWidgetState extends State<ScreenshotWidget>
with TickerProviderStateMixin {
ScreenshotController screenshotController = ScreenshotController();
bool isHideScreenArea = true;
File imageFile;
Duration duration;
AnimationController animationController;
Animation animationPadding;
CurvedAnimation curve;
_screenShot() {
if (!isHideScreenArea) {
return;
}
screenshotController.capture().then((File image) {
setState(() {
imageFile = image;
isHideScreenArea = false;
_timerHideScreenWidget();
});
print(imageFile.path);
animationController.reset();
animationController.forward();
}).catchError((onError) {
print(onError);
});
}
_timerHideScreenWidget() {
Timer(duration, () {
setState(() {
isHideScreenArea = true;
animationController.stop();
});
});
}
_initAnim() {
animationController = AnimationController(
value: 40, duration: Duration(seconds: 2), vsync: this)
..addListener(() {
setState(() {});
});
curve =
CurvedAnimation(parent: animationController, curve: Curves.bounceOut);
animationPadding = Tween(begin: 40.0, end: 10.0).animate(curve);
}
@override
void initState() {
// TODO: implement initState
super.initState();
duration = widget.duration != null ? widget.duration : Duration(seconds: 3);
_initAnim();
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
animationController.dispose();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onLongPress: () {
_screenShot();
},
child: Screenshot(
controller: screenshotController,
child: Container(
width: double.infinity,
height: double.infinity,
child: Stack(
children: [
widget.child,
Positioned(
bottom: animationPadding.value,
right: 20,
child: Offstage(
offstage: isHideScreenArea,
child: Container(
decoration: BoxDecoration(
color: Colors.blueGrey, border: Border.all(width: 1)),
width: 120,
height: 200,
child: imageFile == null
? null
: Image.file(imageFile, fit: BoxFit.fill),
),
),
)
],
),
),
),
);
}
}