Flutter:如何在画布上绘制小部件?

问题描述 投票:6回答:1

我正在尝试自定义滑块小部件以具有自定义的拇指形状。为此,我必须扩展SliderComponentShape。此类要求我实现自己的绘画方法。绘制方法仅给我一个Canvas可供绘制。不幸的是,我要使用的拇指形状相当复杂。手动绘制它会很繁琐,而使用flutters进行构建则要容易得多。有没有办法将Widget绘制为Canvas

flutter flutter-layout
1个回答
0
投票

您可以使用可绘制的图像或矢量作为拇指图像,并降低绘制的复杂度。

例如,如下所示获取地图滑块

enter image description here

您可以使用以下代码,并将图像文件用作拇指图标

 class DistanceSlider extends StatefulWidget {
  const DistanceSlider({
    Key key,
    @required this.imageUrl,
    @required this.minDistance,
    @required this.maxDistance,
  }) : super(key: key);
  final String imageUrl;
  final double maxDistance;
  final double minDistance;

  @override
  _DistanceSliderState createState() => _DistanceSliderState();
}

class _DistanceSliderState extends State<DistanceSlider> {
  double distance = 20;

  ui.Image customImage;
  Future<ui.Image> loadImage(String imageUrl) async {
    ByteData data = await rootBundle.load(imageUrl);
    ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
    ui.FrameInfo fi = await codec.getNextFrame();
    return fi.image;
  }

  @override
  void initState() {
    loadImage(widget.imageUrl).then((image) {
      setState(() {
        customImage = image;
      });
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: SliderTheme(
        data: SliderThemeData(
          trackHeight: 8,
          thumbShape: SliderThumbImage(
              image: customImage,
              thumbRadius: 0,
              max: widget.maxDistance.toInt()),
          overlayColor: kSecondaryColorLight.withAlpha(32),
          overlayShape: RoundSliderOverlayShape(overlayRadius: 28.0),
          activeTrackColor: kSecondaryColorDark,
          inactiveTrackColor: kSecondaryColorLight.withOpacity(0.5),
          valueIndicatorShape: PaddleSliderValueIndicatorShape(),
          valueIndicatorTextStyle: TextStyle(
            color: Colors.white,
          ),
          valueIndicatorColor: Colors.white,
        ),
        child: Stack(
          children: <Widget>[
            Slider(
              label: distance.abs().toString(),
              value: distance,
              min: widget.minDistance,
              max: widget.maxDistance,
              onChanged: (value) {
                setState(() {
                  distance = value;
                });
              },
            ),
            Container(
              margin: EdgeInsets.only(left: 25, right: 25, top: 40),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                  Text(widget.minDistance.toInt().toString()),
                  Text(widget.maxDistance.toInt().toString())
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class SliderThumbImage extends SliderComponentShape {
  final ui.Image image;
  final double thumbRadius;
  final int max;
  SliderThumbImage({this.image, this.thumbRadius, this.max});

  @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}) {
    final canvas = context.canvas;
    final imageWidth = image?.width ?? 10;
    final imageHeight = image?.height ?? 10;

    Offset imageOffset = Offset(
      center.dx - (imageWidth / 2),
      center.dy - (imageHeight / 0.935),
    );

    Paint paint = Paint()..filterQuality = FilterQuality.high;

    if (image != null) {
      canvas.drawImage(image, imageOffset, paint);
    }

    TextSpan span = new TextSpan(
        style: new TextStyle(
            fontSize: imageHeight * .3,
            fontWeight: FontWeight.w700,
            color: sliderTheme.valueIndicatorColor,
            height: 0.9),
        text: '${getValue(value)}');
    TextPainter tp = new TextPainter(
        text: span,
        textAlign: TextAlign.left,
        textDirection: TextDirection.ltr);
    tp.layout();
    Offset textCenter = Offset(
      center.dx - (tp.width / 2),
      center.dy - (tp.height / 0.32),
    );
    tp.paint(canvas, textCenter);
  }

  String getValue(double value) {
    return ((max * value).round()).toString();
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.