问题描述
我想使用堆叠的两张图片创建一个 Before-After 视图,并在我的 Flutter 应用程序中提供有关前后视图的用户体验。
如何创建像这样的 Before/After 控件https://iosexample.com/fancy-slider-for-before-after-images-with-swift/
解决方法
这是我自己的问题的答案以传播知识
创建一个 BeforeAfter 类
import 'package:flutter/material.dart';
import 'RectClipper.dart';
class BeforeAfter extends StatefulWidget {
final Widget beforeImage;
final Widget afterImage;
final double imageHeight;
final double imageWidth;
final double imageCornerRadius;
final Color thumbColor;
final double thumbRadius;
final Color overlayColor;
final bool isVertical;
const BeforeAfter({
Key key,@required this.beforeImage,@required this.afterImage,this.imageHeight,this.imageWidth,this.imageCornerRadius = 8.0,this.thumbColor = Colors.white,this.thumbRadius = 16.0,this.overlayColor,this.isVertical = false,}) : assert(beforeImage != null),assert(afterImage != null),super(key: key);
@override
_BeforeAfterState createState() => _BeforeAfterState();
}
class _BeforeAfterState extends State<BeforeAfter> {
double _clipFactor = 0.5;
@override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,children: <Widget>[
Padding(
padding: widget.isVertical ? const EdgeInsets.symmetric(vertical: 24.0) : const EdgeInsets.symmetric(horizontal: 24.0),child: SizedImage(
widget.afterImage,widget.imageHeight,widget.imageWidth,widget.imageCornerRadius,),Padding(
padding: widget.isVertical ? const EdgeInsets.symmetric(vertical: 24.0) : const EdgeInsets.symmetric(horizontal: 24.0),child: ClipPath(
clipper: widget.isVertical ? RectClipperVertical(_clipFactor) : RectClipper(_clipFactor),child: SizedImage(
widget.beforeImage,Positioned.fill(
child: SliderTheme(
data: SliderThemeData(
trackHeight: 0.0,overlayColor: widget.overlayColor,thumbShape: CustomThumbShape(widget.thumbRadius,widget.thumbColor),child: widget.isVertical
? RotatedBox(
quarterTurns: 1,child: Slider(
value: _clipFactor,onChanged: (double factor) => setState(() => this._clipFactor = factor),)
: Slider(
value: _clipFactor,],);
}
}
class SizedImage extends StatelessWidget {
final Widget _image;
final double _height,_width,_imageCornerRadius;
const SizedImage(this._image,this._height,this._width,this._imageCornerRadius,{Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.circular(_imageCornerRadius),child: SizedBox(
height: _height,width: _width,child: _image,);
}
}
class CustomThumbShape extends SliderComponentShape {
final double _thumbRadius;
final Color _thumbColor;
CustomThumbShape(this._thumbRadius,this._thumbColor);
@override
Size getPreferredSize(bool isEnabled,bool isDiscrete) {
return Size.fromRadius(_thumbRadius);
}
@override
void paint(
PaintingContext context,Offset center,{
Animation<double> activationAnimation,Animation<double> enableAnimation,bool isDiscrete,TextPainter labelPainter,RenderBox parentBox,SliderThemeData sliderTheme,TextDirection textDirection,double value,double textScaleFactor,Size sizeWithOverflow,}) {
final Canvas canvas = context.canvas;
final Paint paint = Paint()
..isAntiAlias = true
..strokeWidth = 4.0
..color = _thumbColor
..style = PaintingStyle.fill;
final Paint paintStroke = Paint()
..isAntiAlias = true
..strokeWidth = 4.0
..color = _thumbColor
..style = PaintingStyle.stroke;
canvas.drawCircle(
center,_thumbRadius,paintStroke,);
canvas.drawCircle(
center,_thumbRadius - 6,paint,);
canvas.drawRect(Rect.fromCenter(center: center,width: 4.0,height: parentBox.size.height),paint);
}
}
然后创建一个 RectClipper 类
import 'package:flutter/material.dart';
class RectClipper extends CustomClipper<Path> {
final double clipFactor;
RectClipper(this.clipFactor);
@override
Path getClip(Size size) {
Path path = Path();
path.lineTo(size.width * clipFactor,0.0);
path.lineTo(size.width * clipFactor,size.height);
path.lineTo(0.0,size.height);
path.close();
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) => true;
}
class RectClipperVertical extends CustomClipper<Path> {
final double clipFactor;
RectClipperVertical(this.clipFactor);
@override
Path getClip(Size size) {
Path path = Path();
path.lineTo(0.0,size.height * clipFactor);
path.lineTo(size.width,0.0);
path.close();
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) => true;
}
最后在屏幕上使用这些类
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class BeforeAfterImageScreen extends StatefulWidget {
static String tag = '/BeforeAfterImageScreen';
@override
BeforeAfterImageScreenState createState() => BeforeAfterImageScreenState();
}
class BeforeAfterImageScreenState extends State<BeforeAfterImageScreen> {
@override
Widget build(BuildContext context) {
changeStatusColor(appColorPrimary);
return Scaffold(
appBar: appBar(context,"Before After Image"),body: Center(
child: BeforeAfter(
beforeImage: Image.asset('images/integrations/img/after.jpg'),afterImage: Image.asset('images/integrations/img/before.jpg'),);
}
}
,
这是答案,一个很酷的图书馆 唯一的问题是不支持像remini一样放大图像