【问题标题】:Flutter custom shape using CustomPaint使用 CustomPaint Flutter 自定义形状
【发布时间】:2021-10-04 19:08:34
【问题描述】:

我只想要我创建的形状,这样我就可以堆叠一个小部件来实现下面的图像。我试图在 X 和 Love 的背景上获得透明的形状。我尝试使用形状制作工具,但我的鼠标设计并不完美。这是从形状生成器生成的代码

    child: CustomPaint(
      size: Size(400,(400*0.2857142857142857).toDouble()),
      painter: RPSCustomPainter(),
    ),

class RPSCustomPainter extends CustomPainter{
  
  @override
  void paint(Canvas canvas, Size size) {
    
    

  Paint paint_0 = new Paint()
      ..color = Color.fromARGB(255, 33, 150, 243)
      ..style = PaintingStyle.stroke
      ..strokeWidth = 1;
     
         
        Path path_0 = Path();
        path_0.moveTo(size.width*0.2137714,size.height*0.2524000);
        path_0.cubicTo(size.width*0.1736143,size.height*0.4775500,size.width*0.1973000,size.height*0.6711500,size.width*0.2153286,size.height*0.7510000);
        path_0.cubicTo(size.width*0.2270429,size.height*0.7777500,size.width*0.2705286,size.height*0.9439500,size.width*0.3556000,size.height*0.7521500);
        path_0.cubicTo(size.width*0.3856000,size.height*0.6504000,size.width*0.3970143,size.height*0.6162000,size.width*0.4283571,size.height*0.7526000);
        path_0.cubicTo(size.width*0.4669286,size.height*0.8264000,size.width*0.5172429,size.height*0.9022500,size.width*0.5719714,size.height*0.7500000);
        path_0.cubicTo(size.width*0.6146429,size.height*0.5440500,size.width*0.5914429,size.height*0.3101000,size.width*0.5713714,size.height*0.2514000);
        path_0.cubicTo(size.width*0.5520714,size.height*0.1778000,size.width*0.4875429,size.height*0.0767500,size.width*0.4296571,size.height*0.2527000);
        path_0.cubicTo(size.width*0.4023714,size.height*0.3646000,size.width*0.3816857,size.height*0.3850000,size.width*0.3557143,size.height*0.2523000);
        path_0.cubicTo(size.width*0.3438571,size.height*0.2086000,size.width*0.2652143,size.height*0.0579000,size.width*0.2137714,size.height*0.2524000);
        path_0.close();
    
        canvas.drawPath(path_0, paint_0);
      
        
      }
    
      @override
      bool shouldRepaint(covariant CustomPainter oldDelegate) {
        return true;
      }
      
    }

我想要达到的目标

我的结果。形状并不完美。

谢谢

【问题讨论】:

    标签: flutter flutter-layout


    【解决方案1】:

    起初我想向您描述获得所需形状的方法等等......

    但被这个有趣的编程挑战冲昏了头脑,最终创建了一个实际的小部件:)

    它依赖于 font_awesome_flutter 包,所以不要忘记安装它(用于心形图标)。 font_awesome_flutter

    所以小部件的源代码是:

    import 'package:flutter/material.dart';
    import 'dart:math' as math;
    import 'package:font_awesome_flutter/font_awesome_flutter.dart';
    
    extension ToRadians on int {
      double get toRadians => this * (math.pi / 180.0);
    }
    
    enum _ButtonType { like, dislike }
    
    class LikeOrNot extends StatelessWidget {
      final VoidCallback onLike;
      final VoidCallback onDislike;
    
      // Percents from total widget width, default - 2%
      final _gapSizeRatio = 0.02;
    
      final _likeIconColor = const Color(0xffb85076);
      final _dislikeIconColor = Colors.white;
    
      const LikeOrNot({
        Key? key,
        required this.onLike,
        required this.onDislike,
      }) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return AspectRatio(
          aspectRatio: 2,
          child: LayoutBuilder(
            builder: (context, constraints) {
              final buttonPaddings = constraints.maxHeight * 0.1;
              final halfWidth = constraints.maxWidth / 2;
              return Stack(
                children: [
                  Positioned.fill(
                    child: CustomPaint(
                      painter: RPSCustomPainter(
                        gapSizeRatio: _gapSizeRatio,
                      ),
                    ),
                  ),
                  Positioned(
                    left: 0,
                    bottom: 0,
                    top: 0,
                    right: halfWidth + constraints.maxWidth * _gapSizeRatio,
                    child: SizedBox.expand(
                      child: Padding(
                        padding: EdgeInsets.all(buttonPaddings),
                        child: _buildButton(_ButtonType.dislike),
                      ),
                    ),
                  ),
                  Positioned(
                    right: 0,
                    bottom: 0,
                    top: 0,
                    left: halfWidth + constraints.maxWidth * _gapSizeRatio,
                    child: SizedBox.expand(
                      child: Padding(
                        padding: EdgeInsets.all(buttonPaddings),
                        child: _buildButton(_ButtonType.like),
                      ),
                    ),
                  ),
                ],
              );
            },
          ),
        );
      }
    
      Widget _buildButton(_ButtonType buttonType) {
        final isPositiveAction = buttonType == _ButtonType.like;
    
        return ElevatedButton(
          style: ElevatedButton.styleFrom(
            shape: const CircleBorder(),
            primary: isPositiveAction ? _dislikeIconColor : _likeIconColor,
            onPrimary: isPositiveAction ? _likeIconColor : _dislikeIconColor,
            padding: EdgeInsets.zero,
            elevation: 10,
            shadowColor: Colors.black54,
          ),
          onPressed: onDislike,
          child: FractionallySizedBox(
            widthFactor: 0.35,
            heightFactor: 0.35,
            child: FittedBox(
              child: isPositiveAction
                  ? const FaIcon(FontAwesomeIcons.heart)
                  : const Icon(Icons.close),
            ),
          ),
        );
      }
    }
    
    class RPSCustomPainter extends CustomPainter {
      final double gapSizeRatio;
    
      RPSCustomPainter({
        required this.gapSizeRatio,
      });
    
      @override
      void paint(Canvas canvas, Size size) {
        final paint = Paint()
          ..color = Colors.black.withOpacity(0.08)
          ..style = PaintingStyle.fill
          ..strokeWidth = 1;
    
        final path = Path();
    
        final gapSize = size.width * gapSizeRatio;
        final arcRadius = size.height / 2 - gapSize / 2;
    
        final leftCircleCenter = Offset(
          size.width * 0.25 - gapSize / 2,
          size.height / 2,
        );
        final rightCircleCenter = Offset(
          size.width * 0.75 + gapSize / 2,
          size.height / 2,
        );
    
        path.arcTo(
          Rect.fromCircle(
            center: leftCircleCenter,
            radius: arcRadius,
          ),
          45.toRadians,
          270.toRadians,
          false,
        );
    
        final bezierOffset = arcRadius * (105 / 360);
    
        path.quadraticBezierTo(
          size.width / 2,
          size.height * 0.30,
          rightCircleCenter.dx - arcRadius + bezierOffset,
          rightCircleCenter.dy - arcRadius + bezierOffset,
        );
    
        path.arcTo(
          Rect.fromCircle(
            center: rightCircleCenter,
            radius: arcRadius,
          ),
          225.toRadians,
          270.toRadians,
          false,
        );
    
        path.quadraticBezierTo(
          size.width / 2,
          size.height * 0.70,
          leftCircleCenter.dx + arcRadius - bezierOffset,
          leftCircleCenter.dy + arcRadius - bezierOffset,
        );
    
        canvas.drawPath(path, paint);
      }
    
      @override
      bool shouldRepaint(covariant CustomPainter oldDelegate) {
        return false;
      }
    }
    

    它是动态的,按钮是内置的。您可以使用 2 个选项 - onDislike()onLike() 回调。
    使用不同尺寸的示例。

    class MyHomePage extends StatelessWidget {
      const MyHomePage({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return DecoratedBox(
          decoration: const BoxDecoration(
            gradient: LinearGradient(
              colors: [Color(0xffc0496f), Color(0xffdb6b59)],
              begin: Alignment.centerLeft,
              end: Alignment.centerRight,
            ),
          ),
          child: Scaffold(
            backgroundColor: Colors.transparent,
            appBar: AppBar(
              title: const Text('Test'),
              backgroundColor: Colors.transparent,
              elevation: 0,
            ),
            body: Container(
              width: double.infinity,
              padding: const EdgeInsets.all(20.0),
              child: Column(
                children: [
                  for (final size in List.generate(5, (index) => index++))
                    FractionallySizedBox(
                      widthFactor: 1.0 - size * 0.2,
                      child: LikeOrNot(
                        onLike: () {},
                        onDislike: () {},
                      ),
                    ),
                ],
              ),
            ),
          ),
        );
      }
    }
    

    有一个 _gapSize 参数,它是两个圆之间的间隙。您需要的已经在里面了(默认为 2%),但是您可以通过更改它来获得一些很酷的其他变体。例如,以下是总宽度 20% 的间隙:

    【讨论】:

    • 哇!!!!你只是救了我。我已经为此工作了 2 天。非常感谢。
    猜你喜欢
    • 2021-04-17
    • 2020-04-12
    • 1970-01-01
    • 2022-01-04
    • 2023-01-12
    • 2018-11-18
    • 2022-12-13
    • 2021-09-04
    • 2019-04-15
    相关资源
    最近更新 更多