【问题标题】:How to make Before-After image slider view in Flutter如何在 Flutter 中制作前后图像滑块视图
【发布时间】:2021-07-18 04:29:59
【问题描述】:

我想创建一个包含两张堆叠图像的前后视图,并在我的 Flutter 应用中为用户提供前后视图的体验。

如何创建像 https://iosexample.com/fancy-slider-for-before-after-images-with-swift/ 这样的 Before/After 控件

图片来源https://iosexample.com

【问题讨论】:

    标签: image flutter effect after-effects before-filter


    【解决方案1】:

    这是我自己的问题的答案,以传播知识

    创建一个 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,
                  widget.imageHeight,
                  widget.imageWidth,
                  widget.imageCornerRadius,
                ),
              ),
            ),
            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,
                        onChanged: (double factor) => setState(() => this._clipFactor = factor),
                      ),
              ),
            ),
          ],
        );
      }
    }
    
    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, 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'),
            ),
          ),
        );
      }
    }
    

    【讨论】:

      【解决方案2】:

      这就是答案,一个很酷的库 唯一的问题是不支持像 remini 这样的放大图像

      https://pub.dev/packages/before_after

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-01-21
        • 2021-10-07
        • 2022-09-27
        • 1970-01-01
        • 1970-01-01
        • 2022-08-16
        • 2022-01-15
        • 1970-01-01
        相关资源
        最近更新 更多