포스트

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

  • List를 사용하는 방법
1
2
3
4
5
6
7
8
9
10
11
12
13
Map myFunction() {
  String myString = "Hello";
  int myInt = 42;
  return {"myString": myString, "myInt": myInt}; // Return both values in a map
}

void main() {
  var result = myFunction();
  String resultString = result["myString"];
  int resultInt = result["myInt"];
  print('String: $resultString, Int: $resultInt');
}

  1. Map을 사용하는 방법
1
2
3
4
5
6
7
8
9
10
11
12
13
Map myFunction() {
  String myString = "Hello";
  int myInt = 42;
  return {"myString": myString, "myInt": myInt}; // Return both values in a map
}

void main() {
  var result = myFunction();
  String resultString = result["myString"];
  int resultInt = result["myInt"];
  print('String: $resultString, Int: $resultInt');
}

  1. Tuple을 사용하는 방법
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import 'package:tuple/tuple.dart';

Tuple2 myFunction() {
  String myString = "Hello";
  int myInt = 42;
  return Tuple2(myString, myInt); // Return both values in a tuple
}

void main() {
  var result = myFunction();
  String resultString = result.item1;
  int resultInt = result.item2;
  print('String: $resultString, Int: $resultInt');
}

  1. 커스텀 Class를 사용하는 방법
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MyResult {
  String myString;
  int myInt;
  MyResult(this.myString, this.myInt);
}

MyResult myFunction() {
  String myString = "Hello";
  int myInt = 42;
  return MyResult(myString, myInt); // Return both values in a custom class
}

void main() {
  var result = myFunction();
  print('String: ${result.myString}, Int: ${result.myInt}');
}

여기서 잦은 호출에 의한 계산 부하를 최소화할 수 있는 방법은 커스텀 Class를 사용하는 방법이라고 한다.

줄 바꿈 횟수(lineBreakCount)를 세어서 함께 리턴하는 클래스를 만들었다.

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
class BreakTextResult {
  final String text;
  final int lineBreakCount;

  BreakTextResult(this.text, this.lineBreakCount);
}

class TextBreaker {
  final String input;
  final TextStyle textStyle;
  final double containerWidth;
  final double containerHeight;
  final TextAlign textAlign;

  TextBreaker({
    required this.input,
    required this.textStyle,
    required this.containerWidth,
    required this.containerHeight,
    this.textAlign = TextAlign.left,
  });

  BreakTextResult breakCharacterWithMeasurement() {
    List lines = [];
    TextPainter textPainter = TextPainter(
      textDirection: TextDirection.ltr,
      textAlign: textAlign,
    );
    List words = input.split(' '); // Split the input into words.
    String currentLine = '';
    double textWidth;
    double textWidthPadding = textStyle.fontSize! * 0.5;
    int linesCount = calculateLinesCount(textStyle, containerHeight);
    int currentLineCount = 0;

    print("containerWidth: $containerWidth");
    print("containerHeight: $containerHeight");

    for (String word in words) {
      // Handle each word
      bool isFirstCharacterOfWord = true;
      for (int i = 0; i  containerWidth) {
          if (!isFirstCharacterOfWord || currentLine.isNotEmpty) {
            // If the line is not empty, or if it's not the first character of the word, add the current line to the lines list
            lines.add(currentLine);
            currentLineCount++;
            if (currentLineCount >= linesCount) break; // Stop if the maximum number of lines is reached
            currentLine = character; // Start a new line with the current character
          } else {
            // If it's the first character of the word and the line is empty
            currentLine = character; // Add the character to the current line
          }
          isFirstCharacterOfWord = false;
        } else {
          // If the character fits, add it to the current line
          currentLine = testLine;
          isFirstCharacterOfWord = false;
        }
      }
      // After processing a word, add a space if it's not the end of a line
      if (currentLineCount = linesCount) break;
    }

    // Add any remaining text in the currentLine to lines, if there's space
    if (!currentLine.isEmpty && currentLineCount  styleHeight ? measuredHeight : styleHeight;
    print("lineHeight: $lineHeight");

    // Calculate the number of lines that can fit in the container.
    int linesCount = containerHeight ~/ lineHeight;
    return linesCount;
  }
}
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
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
  int maxLength = 1; // State variable for holding the maximum length of the text

  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,
    );

    _textController.addListener(textChanged);
  }

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

  void textChanged() {
    final String currentText = _textController.text;
    // Format the text to fit the container
    TextBreaker textBreaker = TextBreaker(
      input: currentText, // Specify the input text
      textStyle: textStyle, // Specify the text style
      containerWidth: widget.width, // Specify container width
      containerHeight: widget.height, // Specify container height
      textAlign: textAlignment, // Specify text alignment
    );
    BreakTextResult result = textBreaker.breakCharacterWithMeasurement();
    formattedText = result.text;

    // Update the state to display formatted text
    setState(() {
      this.maxLength = formattedText.length> 0 ? formattedText.length - result.lineBreakCount : maxLength;
      this.formattedText = formattedText;
    });
  }



  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Square Details')),
      body: SingleChildScrollView(
        child: Column(
          children: [
            Divider(),
            Text(
              'Text Sticker',
              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('Capture the green container as an image');
                  },
                  // Text to display in the button and width, height of the green container
                  child: Text('Capture Image, ${widget.width}x${widget.height}'),
                ),
              ],
            ),

            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(
              'Formatted Text:',
              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,
                // 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: 'Enter your comments',
                ),
                style: textStyle, // Apply the specified textStyle to the TextField
                // Optional: Remove the maxLength restriction while typing (but still show the counter)
                maxLengthEnforcement: MaxLengthEnforcement.none,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

[영상]

그런데 inputfield에서 직접 enter(줄바꿈)하는 것에 대한 처리에 버그가 있음 ㅎㅎㅎㅎ

[영상]

sticker

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