This article is mainly to talk about the whole function of an implementation of the idea and the use of technical points, more detailed pleaseRefer to the source

One, the implementation of the effect diagram is as follows

Two, the realization of the function and the use of technical points

  • function
    • The label appears as a dot when dragged
    • The label can only be dragged within the scope of the image display
    • Labels can be dragged to a specified location for deletion
    • Drag the label to the left or right to automatically change the label layout direction based on the remaining width
  • Technical point
    • DraggableDrag the component
    • The customRenderObject,RenderBoxParticipate in component drawing and placement process
    • Images usingBoxFitAfter, calculate the actual position in the container

Third, the real size of the picture, the position on the screen to obtain

  • Analysis of the effect drawing, annotation to facilitate description

  • Component drag effect implementation
Widget label = LabelWidget();
Draggable(
  /// The component to drag
  child: label,
  /// The component that displays when dragging
  feedback: Material(color: Colors.transparent, child: _feedbackWidget()),
  /// The component displayed in its original position when dragging
  childWhenDragging: Offstage(child: label),
  /// drag the position update callback
  onDragUpdate:(detail){}
  /// Drag the end position callback
  onDragEnd: (detail){}
),
Copy the code

1. Since the Draggable component can be dragged across the entire screen when it is dragged; So the Offset position you get in the callback is relative to the upper-left corner of the screen. 2. Therefore, to determine whether the dragged position is in the picture, we need to know the position information of the picture rectangle in the whole screen; Then use the contains() function of Rect to determine whether the point is in the rectangle.

1. Calculate the position of the picture rectangle on the screen, as shown in the figure above; We need to knowStackPosition and size on the screen:

  • To obtainStackPosition and size on the screen, it’s a little bit easier to set a key on the stack and get it after the first frame is drawn
@override
void initState(a) {
  super.initState(); WidgetsBinding.instance! .addPostFrameCallback((callback) { containerSize = _getWidgetSize(_stackKey); containerOffset = _location(_stackKey); }); }/// Get the size of the component
Size _getWidgetSize(GlobalKey key) {
  returnkey.currentContext! .size! ; }/// Get the position of the component on the screen
Offset _location(GlobalKey key) { RenderBox? renderBox = key.currentContext! .findRenderObject() as RenderBox? ;returnrenderBox! .localToGlobal(Offset.zero); }Copy the code
  • Through the above operation: you have obtained the labeled abovePoint AOffset A = containerOffset;
  • So how do I get the size and position of the image?

Process: Get the picture size -- > calculate the width and height of the picture, the aspect ratio of the picture -- > calculate the real display size of the picture according to the size of the Stack container -- > calculate the position in the Stack according to the real size of the picture

2, get the image size, used hereExtended_image library:

ExtendedImage.network(
  'Picture address',
  fit: BoxFit.contain,
  loadStateChanged: (ExtendedImageState state) {
    if (state.extendedImageLoadState == LoadState.completed) {
       /// load the following code to get the height and width of the imagestate.extendedImageInfo? .image; }}),Copy the code
  • Due to the image zoom mode used aboveBoxFit.contain, so the image will display the image according to the given container size; So how do you calculate that? The answer is to be found in the source code
    • The code is located in theflutterSDk/packages/flutter/lib/src/painting/box_fit.dartIn the fileapplyBoxFitfunction

inputSizeThe size of the image,outputSizeGiven the size of the container, it’s easy to calculate

3. Calculate the true size of the picture in the given container

/// Calculate the true size of the image
///[image] Image mode
///[boxfit. contain] specifies the folder type
Size _calcImgSize(ui.Image? image) {
  Size result = Size.zero;
  double imageAspectRatio = image.width.toDouble() / image.height.toDouble();
  doublecontainerRatio = containerSize! .width / containerSize! .height;if(containerRatio > imageAspectRatio) { result = Size(imageAspectRatio * containerSize! .height, containerSize! .height); }else{ result = Size(containerSize! .width, image.height.toDouble() * containerSize! .width / image.width.toDouble()); }return result;
}
Copy the code

3. Calculate the position of the picture in the container, that is, the picture aboveBThe position of a point; And with the position of point B, you can get the rectangle on the screen where the image actually is

ImgStartOffset = imgStartOffset = imgStartOffset = imgStartOffset = imgStartOffset = imgStartOffset = imgStartOffset = imgStartOffset

Size realImgSize = _calcImgSize(image);
doubleimgOffsetX = (containerSize! .width - realImgSize.width) /2;
doubleimgOffsetY = (containerSize! .height - realImgSize.height) /2;
/// This is the position of B (relative to the origin of A).
Offset imgStartOffset = Offset(imgOffsetX, imgOffsetY);
/// Calculate the position of the upper left corner of the picture on the screen
Offset imgOffset = containerOffset + imgStartOffset;
/// The position of the image rectangle on the screen
Rect rect = imgOffset & realImgSize;
Copy the code

Three, label drag effect implementation

  • Hide the text of the label at the beginning of the drag and display it at the end of the drag
  • Labels can only drag dots, not text
  • At the end of the drag, determine whether the text is left or right based on the position and remaining width

1, here focuses on the tag component, customRenderObjectReaches the part where you can only drag dots

As can be seen from the figure above, the Draggable component’s direct size is only the red part, and the text part does not occupy any size. In this way, except for the red part that can respond to the touch event, the other parts are not able to respond.

  • The structure of the label component is as follows
class LabelWidget extends StatefulWidget {

  @override
  State<StatefulWidget> createState(a) {
    returnLabelWidgetState(); }}class LabelWidgetState extends State<LabelWidget> {
 
  @override
  Widget build(BuildContext context) {
  	/// omit some code....
    return ShopGoodsLabelRenderObjectWidget();
  }
  /// omit some code....
}

class ShopGoodsLabelRenderObjectWidget extends SingleChildRenderObjectWidget {

  @override
  RenderObject createRenderObject(BuildContext context) {
    return ShopGoodsLabelBox();
  }
  /// omit some code....
}

class ShopGoodsLabelBox extends RenderProxyBox with RenderProxyBoxMixin {

  /// omit some code....
  
  @override
  void performLayout(a) {
    super.performLayout();
    realSize = size;
    /// Call back to the original sizesizeCallback? .call(realSize);// change the size of RenderObject
    size = circleSize;
  }

  @override
  void paint(PaintingContext context, Offset offset) {
    /// Draw elements
    if(child ! =null) context.paintChild(child!, offset);
  }
}
Copy the code
  • rewriteperformLayout()Change size to the size of the red rectangle above, and notify the true size; At the end of the drag to determine whether the text part of the label is displayed on the left or right side of the need to use.
  • rewritepaint()Draw the content. When the label text is displayed on the left, you need to calculate the value of offset to draw

2, label drag position calculation, drag delete processing

  • The position of the image rectangle on the screen has been calculated, so just check whether the position is in the rectangle at the end of the drag, as follows:
/ / / pseudo code
imgRect.contains(offset);
Copy the code
  • Similarly, label deletion only needs to know the position of the deleted rectangle on the screen

Four, to add a good label to display

  • The displayed logic is basically the same as the added logic
    • Know the aspect ratio and aspect ratio of the picture to calculate the true position of the picture
    • Calculate the proportion of label points (x,y) on the picture; The scale value and the size of the picture at display are then used to calculate the position of the label