포스트

Flutter Simple 그림판 (5) - 텍스트 컨테이너 줄바꿈 감지, 맞추기(character-breaking)(2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart'; // Ensure GetX is still used for dependency injection.

class TapController extends GetxController {
  var startStr = 'Tap Somewhere'.obs;
  var endStr = ''.obs;
  var dragStr = ''.obs; // Add this line for drag coordinates
  bool isDrawing = false;
  // starting point
  Offset? start;
  // dragging point
  Offset? drag;
  // ending point
  Offset? end;

  void updateUI() {
    // Calls GetxController's update method to update the UI
    update();
  }

  void reset() {
    start = null;
    drag = null;
    end = null;
    isDrawing = false;
    updateUI();
  }

  void notifyEnd() {
    isDrawing = false;
    updateUI();
  }

  // Add this method for updating drag coordinates
  void updateDragCoordinates(Offset newCoordinates) {
    if (!isDrawing) {
      // If the user is not dragging, then the current drag coordinates are the starting point
      updateStartingPoint(newCoordinates);
    }
    // Update the drag coordinates
    updateDragPoint(newCoordinates);

    isDrawing = true;
  }

  void updateStartingPoint(Offset newStart) {
    startStr.value = newStart.toString();
    start = newStart;
    updateUI();
  }
  void updateDragPoint(Offset newDrag) {
    dragStr.value = newDrag.toString();
    drag = newDrag;
    updateUI();
  }
  void updateEndingPoint(Offset newEnd) {
    endStr.value = newEnd.toString();
    end = newEnd;
    updateUI();
  }
}

void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final TapController tapController = Get.put(TapController());

    return GetMaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('GetX Dragging End Coordinates App'),
        ),
        body: GestureDetector(
          onTapDown: (TapDownDetails details) {

          },
          onPanUpdate: (DragUpdateDetails details) {
            // Apply grid to the details.localPosition to make the drag coordinates snap to the grid
            final x = (details.localPosition.dx / 10).round() * 10;
            final y = (details.localPosition.dy / 10).round() * 10;
            // Keep updating the drag coordinates and the end position as the user drags
            tapController.updateDragCoordinates(Offset(x.toDouble(), y.toDouble()));
            tapController.updateEndingPoint(Offset(x.toDouble(), y.toDouble()));
          },
          onPanEnd: (DragEndDetails details) {
            // When the user stops dragging, notify the controller
            tapController.notifyEnd();

            if (tapController.start != null && tapController.end != null) {
              // Calculate the width and height of the rectangle
              final width = (tapController.end!.dx - tapController.start!.dx).abs();
              final height = (tapController.end!.dy - tapController.start!.dy).abs();
              // Navigate to the SquareDetailsScreen and pass the width and height as arguments
              Get.to(() => SquareDetailsScreen(width: width, height: height));
            }
          },

          child: Stack(
            children: [
              Container(
                color: Colors.lightBlueAccent,
                alignment: Alignment.center,
                child: GetBuilder(
                  builder: (_) => Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      Text("Start: ${_.startStr.value}"),
                      SizedBox(height: 20),
                      Text("Drag: ${_.dragStr.value}"),
                      SizedBox(height: 20),
                      Text("End: ${_.endStr.value}"),
                    ],
                  ),
                ),
              ),
              GetBuilder(
                builder: (_) {
                  final start = _.start;
                  final end = _.end;
                  print('start: $start, end: $end');
                  if (start != null && end != null) {
                    final rect = Rect.fromPoints(start, end);
                    return CustomPaint(
                      painter: RectanglePainter(rect: rect),
                      child: Container(),
                    );
                  } else {
                    return SizedBox.shrink();
                  }
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class RectanglePainter extends CustomPainter {
  final Rect? rect;
  RectanglePainter({this.rect});

  @override
  void paint(Canvas canvas, Size size) {
    if (rect != null) {
      final paint = Paint()
        ..color = Colors.blue
        ..style = PaintingStyle.stroke
        ..strokeWidth = 2.0;
      canvas.drawRect(rect!, paint);
    }
  }

  @override
  bool shouldRepaint(covariant RectanglePainter oldDelegate) => true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
class SquareDetailsScreen extends StatefulWidget {
  final double width;
  final double height;

  SquareDetailsScreen({Key? key, required this.width, required this.height})
      : super(key: key);

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

class _SquareDetailsScreenState extends State {
  final TextEditingController _textController = TextEditingController();
  String formattedText = ""; // State variable for holding formatted text
  late int maxLength;

  final double fontSizeDefault = 20;
  final FontWeight fontWeightDefault = FontWeight.bold;

  // Initialize textStyle using the default font size and weight
  late TextStyle textStyle;
  final TextAlign textAlignment = TextAlign.center;

  @override
  void initState() {
    super.initState();
    // Initialize textStyle here to use it in textChanged
    textStyle = TextStyle(
      fontSize: fontSizeDefault,
      fontWeight: fontWeightDefault,
      height: 1.0,
    );

    maxLength = 1000; // Initial high limit to not constrain input initially
    _textController.addListener(textChanged);
  }

  @override
  void dispose() {
    _textController.removeListener(textChanged);
    _textController.dispose();
    super.dispose();
  }

  void textChanged() {
    final String currentText = _textController.text;
    formattedText = breakCharacterWithMeasurement(
        currentText, textStyle, widget.width, widget.height, textAlignment);
    setState(() {
      this.formattedText = formattedText;
    });
  }

  int calculateLinesCount(TextStyle style, double containerHeight) {
    // Using a space character to measure the height might be more reliable as it should
    // give us the height of an empty line of text, including leading.
    final TextPainter textPainter = TextPainter(
      text: TextSpan(text: ' ', style: style),
      maxLines: 1,
      textDirection: TextDirection.ltr,
    )..layout(minWidth: 0, maxWidth: double.infinity);

    // The height property acts as a multiplier to the font size.
    double styleHeight = style.fontSize! * (style.height ?? 1.0);
    // Measure the height of a space character as a proxy for line height.
    double measuredHeight = textPainter.size.height;
    print("measuredHeight: $measuredHeight");

    // Regard the line height as the maximum of the measured height and the style height.
    double lineHeight = measuredHeight > styleHeight ? measuredHeight : styleHeight;
    print("lineHeight: $lineHeight");

    // Calculate the number of lines that can fit in the container.
    int linesCount = containerHeight ~/ lineHeight;
    return linesCount;
  }

  String breakCharacterWithMeasurement(
      String input, TextStyle textStyle, double containerWidth, double containerHeight, TextAlign textAlign) {
    List lines = [];
    TextPainter textPainter = TextPainter(
      textDirection: TextDirection.ltr,
      textAlign: textAlign,
    );
    String currentTextLine = '';
    print("containerWidth: $containerWidth");
    print("containerHeight: $containerHeight");
    //
    double textWidth;
    double textWidthPadding = textStyle.fontSize! * 0.5;
    // modify the containerHeight to be the multiple of the text height
    int linesCount = calculateLinesCount(textStyle, containerHeight);
    print("linesCount: $linesCount");

    // Split the input text into lines that fit within the container height
    for (int i = 0; i = containerWidth) {
        if (lines.length < linesCount) {
          lines.add(currentTextLine);
          currentTextLine &#x3D; input[i]; // start a new line
        } else {
          break;
        }
      } else {
        currentTextLine &#x3D; testTextLine; // the testLine fits within the container width
      }
      print("new currentTextLine: $currentTextLine");
    }
    if (lines.length < linesCount){
      lines.add(currentTextLine);
    }

    print("result lines: $lines");

    String resultText &#x3D; lines.join(&#x27;\n&#x27;);
    return resultText;
  }


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(&#x27;Square Details&#x27;)),
      body: SingleChildScrollView(
        child: Column(
          children: [
            Divider(),
            Text(
              &#x27;Text Sticker&#x27;,
              style: TextStyle(fontWeight: FontWeight.bold),
            ),
            // button to capture the green container as an image
            ButtonBar(
              alignment: MainAxisAlignment.center,
              children: [
                ElevatedButton(
                  onPressed: () {
                    // Capture the green container as an image
                    print(&#x27;Capture the green container as an image&#x27;);
                  },
                  // Text to display in the button and width, height of the green container
                  child: Text(&#x27;Capture Image, ${widget.width}x${widget.height}&#x27;),
                ),
              ],
            ),

            Container(
              width: widget.width,
              height: widget.height,
              color: Colors.green,
              alignment: Alignment.center,
              child: Text(
                formattedText, // Display the formatted text here
                style: textStyle,
                textAlign: textAlignment,
              ),
            ),
            Divider(),
            Text(
              &#x27;Formatted Text:&#x27;,
              style: TextStyle(fontWeight: FontWeight.bold),
            ),
            Container(
              width: double.infinity,
              child: Text(formattedText, style: textStyle,textAlign: textAlignment),
            ),
            Divider(),
            Padding(
              padding: EdgeInsets.all(16.0),
              child: TextField(
                controller: _textController,
                maxLength: maxLength,
                decoration: InputDecoration(
                  border: OutlineInputBorder(),
                  labelText: &#x27;Enter your comments&#x27;,
                ),
                style: textStyle, // Apply the specified textStyle to the TextField
              ),
            ),
          ],
        ),
      ),
    );
  }
}

멀티라인 인풋필드로 하려면,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
            Padding(
              padding: EdgeInsets.all(16.0),
              child: TextField(
                controller: _textController,
                maxLength: maxLength,
                // To allow for unlimited lines, set maxLines to null.
                // If you want to limit the number of lines, set maxLines to an integer value greater than 1.
                maxLines: null,
                keyboardType: TextInputType.multiline, // Set the keyboard type to multiline
                textInputAction: TextInputAction.newline, // Set the input action to support newline for multiline input
                decoration: InputDecoration(
                  border: OutlineInputBorder(),
                  labelText: &#x27;Enter your comments&#x27;,
                ),
                style: textStyle, // Apply the specified textStyle to the TextField
                // Optional: Remove the maxLength restriction while typing (but still show the counter)
                maxLengthEnforcement: MaxLengthEnforcement.none,
              ),
            ),

sticker

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.