포스트

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

문제 해결 알고리즘

문자열을 space, tab, 혹은 enter로 끊어서 리스트에 담고, 앞에서부터 한개, 두개, … n개 만큼 textPainter에 넣어서 textPainter.width를 확인하고, 만약 containerWidth보다 커지는 시점이 오거나 더 이상 단어가 남아있지 않았을 때 그 바로 직전 단어(혹은 마지막 단어)의 인덱스와 시작 인덱스를 묶어서 함께 result 리스트에 저장한다. 예를 들어 1번째 단어부터 시작하여 6번째 단어에서 그 조건에 해당하면, result = [[1,5]] 이다. 이어서 진행하면 예를 들어 다음과 같이 될 수 있다. 전체 단어 개수가 15개일 때, result = [[1,5], [6,12], [13,15]]

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
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Example text style and container width for demonstration
    TextStyle textStyle = const TextStyle(fontSize: 20, fontWeight: FontWeight.bold);
    TextAlign textAlign = TextAlign.left;
    double containerWidth = 300.0; // Example container width

    // Sample long text input
    String inputText = "This is a sample text to demonstrate how we can format text "
        "based on container width and text style in Flutter. "
        "This particular string is designed to exceed the container width "
        "and demonstrate line breaking and clipping functionality.";

    // Format the text with measurement
    String formattedText = breakWordsWithMeasurement(inputText, textStyle, containerWidth, textAlign);
    print("Formatted Text: $formattedText");
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Formatted Text Demo'),
        ),
        body: SingleChildScrollView(
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            // Display original and formatted text
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                // show width
                Text(
                  'Container Width: $containerWidth',
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
                // Original text
                Text(
                  'Original Text:',
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
                Container(
                  color: Colors.blueAccent.withOpacity(0.2),
                  width: containerWidth,
                  child: Text(inputText, style: textStyle, textAlign: textAlign),
                ),
                Divider(),
                // Formatted text
                Text(
                  'Formatted Text:',
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
                Container(
                  width: containerWidth,
                  child: Text(formattedText, style: textStyle),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

String breakWordsWithMeasurement(
    String input, TextStyle textStyle, double containerWidth, TextAlign textAlign) {
  List words = input.split(RegExp(r'\s+')); // Split input by any whitespace
  List> result = []; // To store result as [[start, end]...]
  TextPainter textPainter = TextPainter(
    textDirection: TextDirection.ltr,
    textAlign: textAlign, // Apply textAlign
  );
  int startIndex = 0; // Start index for each line
  String currentLine = "";

  for (int i = 0; i  containerWidth && !currentLine.isEmpty) {
      // If current line width exceeds containerWidth, store indices and reset
      result.add([startIndex, i - 1]);
      startIndex = i; // Next line starts with current word
      currentLine = words[i]; // Reset currentLine to current word
    } else {
      currentLine = testLine; // Append word to current line
    }
  }

  // Add the last line if there's any content left
  if (currentLine.isNotEmpty) {
    result.add([startIndex, words.length - 1]);
  }

  // Formatting result for demonstration, not part of the core logic
  return result.map((range) => "Words from ${range[0] + 1} to ${range[1] + 1}").join(", \n");
}

문자열 줄바꿈 위치 확인 테스트

커스텀 기능 구현함.

다음 번에는 추가로 캐릭터단위의 character-breaking 도 만들어놓아야 할 듯 함.

sticker

+) 추가 문자 브레이킹

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
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    TextStyle textStyle = const TextStyle(fontSize: 20, fontWeight: FontWeight.bold);
    TextAlign textAlign = TextAlign.left;
    double containerWidth = 300.0; // Example container width

    String inputText = "This is a sample text to demonstrate how we can format text "
        "based on container width and text style in Flutter. "
        "This particular string is designed to exceed the container width "
        "and demonstrate line breaking and clipping functionality.";

    // Use the new function
    String formattedText = breakCharacterWithMeasurement(inputText, textStyle, containerWidth, textAlign);
    print("Formatted Text: $formattedText");
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Formatted Text Demo'),
        ),
        body: SingleChildScrollView(
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  'Container Width: $containerWidth',
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
                Text(
                  'Original Text:',
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
                Container(
                  color: Colors.blueAccent.withOpacity(0.2),
                  width: containerWidth,
                  child: Text(inputText, style: textStyle, textAlign: textAlign),
                ),
                Divider(),
                Text(
                  'Formatted Text:',
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
                Container(
                  // full width
                  width: double.infinity,
                  child: Text(formattedText, style: textStyle),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

String breakCharacterWithMeasurement(
    String input, TextStyle textStyle, double containerWidth, TextAlign textAlign) {
  List> result = [];
  TextPainter textPainter = TextPainter(
    textDirection: TextDirection.ltr,
    textAlign: textAlign,
  );
  int startIndex = 0;
  String currentLine = "";

  for (int i = 0; i  containerWidth && !currentLine.isEmpty) {
      result.add([startIndex, i - 1]);
      startIndex = i;
      currentLine = input[i];
    } else {
      currentLine = testLine;
    }
  }

  if (currentLine.isNotEmpty) {
    result.add([startIndex, input.length - 1]);
  }

  // Formatting result for demonstration
  return result.map((range) => input.substring(range[0], range[1] + 1)).join("\n");
}

다 유용하게 쓸 데가 있음!

sticker

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