Flutterで写真の特定部分を塗り潰す機能を試しに実装してみた

写真を撮った際に指定の部分だけを塗り潰す機能がほしくて、色々パッケージを探したんですが見つけられなかったので試しに実装してみました。

CustomPaintではなく、塗り潰し色のContainerを座標配置するだけの単純な実装です。
ImageウィジェットにStackでContainerを重ねているだけなので、ここから画像化して取り出すにはもうひと工夫必要となりそうです。

進展あればまた書きます。

import 'package:flutter/material.dart';

class ImageMaskingController {
  late _ImageMaskingState imageMaskingState;
  void clear() {
    imageMaskingState.clear();
  }
}

@immutable
class ImageMasking extends StatefulWidget {
  const ImageMasking({
    Key? key,
    required this.assetName,
    required this.controller,
  }) : super(key: key);

  final String assetName;
  final ImageMaskingController controller;

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

class _ImageMaskingState extends State<ImageMasking> {
  var _isDrag = false;
  final _rects = <Rect>[];
  var _startX = 0;
  var _startY = 0;

  @override
  void initState() {
    super.initState();
    widget.controller.imageMaskingState = this;
  }

  @override
  Widget build(BuildContext context) {
    final widgets = <Widget>[
      Image(
        image: AssetImage(widget.assetName),
        fit: BoxFit.contain,
      ),
    ];

    for (final rect in _rects) {
      widgets.add(
        Positioned(
          left: rect.left,
          top: rect.top,
          width: rect.width,
          height: rect.height,
          child: Container(
            color: Colors.yellow,
          ),
        ),
      );
    }

    return GestureDetector(
      onPanDown: _onPanDown,
      onPanStart: _onPanStart,
      onPanUpdate: _onPanUpdate,
      onPanEnd: _onPanEnd,
      onPanCancel: _onPanCancel,
      child: Container(
        color: Colors.yellow,
        child: Stack(
          children: widgets,
        ),
      ),
    );
  }

  void _onPanDown(DragDownDetails details) {}

  void _onPanStart(DragStartDetails details) {
    setState(() {
      _isDrag = true;

      _startX = details.localPosition.dx.toInt();
      _startY = details.localPosition.dy.toInt();

      _rects.add(Rect.fromLTWH(
        _startX.toDouble(),
        _startY.toDouble(),
        0,
        0,
      ));
    });
  }

  void _onPanUpdate(DragUpdateDetails details) {
    if (!_isDrag) {
      return;
    }
    setState(() {
      final endX = details.localPosition.dx.toInt();
      final endY = details.localPosition.dy.toInt();

      final rect = Rect.fromLTRB(
        (_startX < endX ? _startX : endX).toDouble(),
        (_startY < endY ? _startY : endY).toDouble(),
        (_startX < endX ? endX : _startX).toDouble(),
        (_startY < endY ? endY : _startY).toDouble(),
      );

      _rects
        ..removeLast()
        ..add(rect);
    });
  }

  void _onPanEnd(DragEndDetails details) {
    setState(() {
      _isDrag = false;
      _startX = _startY = 0;
      final lastRect = _rects[_rects.length - 1];
      if (lastRect.width <= 0 || lastRect.height <= 0) {
        _rects.removeLast();
      }
      debugPrint(lastRect.toString());
    });
  }

  void _onPanCancel() {}

  void clear() {
    setState(_rects.clear);
  }
}

コメント

タイトルとURLをコピーしました