Skip to content

Container disappears when Text too long #1922

@petri-lipponen-movesense

Description

Describe the bug

I have an app that prints a PDF report. The top of report has a header-section which has multiple columns with labels (1st, 3rd column) and values (2nd, 4th column). The basic structure is:

  • pw.Container (whole header)
    • pw.Column (Header area + separator line)
      • pw.Row (Header label and string boxes)
        • pw.SizedBox (Label or value "box")
          • pw.Column (vertical list of values or labels)
            • pw.Text("label1 here")
            • pw.Text("label2 here")
            • pw.Text("label3 here")
            • pw.Text("label4 here")
          • pw.Column (vertical list of values or labels)
            • pw.Text("value1 here")
            • pw.Text("value2 here")
            • pw.Text("value3 here")
            • pw.Text("value4 here")
              etc.

Everything worked fine with just english, but now we've translated the app for more languages and noticed the issue: for some languages (pt) the whole header section was empty i.e. nothing inside the first pw.Container was visible. After trying different things, I noticed that the issue was easily triggered off / on by making one of the labels a couple of characters shorter or longer. This indicates that overflowing text causes the whole area to disappear.

I tried to use OverflowBox and changing Text.overflow from visible to clip, but neither worked.

To Reproduce

Here's a code that reproduces the problem. Removing XXXXXXXXXX from label "Bandwidth" makes header visible.

    /// XXXXX TEST CODE
    pageFmt = await PdfReport.getDefaultPageFmt();
    // Set reasonable margins and landscape orientation

    const double topMargin = 10;
    const double bottomMargin = 10;
    const double leftMargin = 14;
    const double rightMargin = 17;
    pageFmt = pageFmt!
        .copyWith(
          marginLeft: leftMargin * PdfPageFormat.mm,
          marginRight: rightMargin * PdfPageFormat.mm,
          marginTop: topMargin * PdfPageFormat.mm,
          marginBottom: bottomMargin * PdfPageFormat.mm,
        )
        .landscape;

    var note;
    var startTime = DateTime.now();
    var stopTime = DateTime.now().add(Duration(seconds: 123));
    var durationStr = "123";
    var bandwithStr = "unknown";
    var textStyle = pw.TextStyle(fontSize: 8, font: pw.Font.helvetica());
    pdf.addPage(pw.Page(
      pageFormat: pageFmt,
      build: (pw.Context context) {
        return pw.Padding(
        padding: const pw.EdgeInsets.only(
            left: 5 * PdfPageFormat.mm, right: 1 * PdfPageFormat.mm),
        child: pw.Container(
            height: headerHeight * PdfPageFormat.mm,
            child: pw.Column(children: [
              pw.Row(
                  mainAxisAlignment: pw.MainAxisAlignment.start,
                  crossAxisAlignment: pw.CrossAxisAlignment.start,
                  children: [
                    pw.SizedBox(
                        width: headerColumn1Width * PdfPageFormat.mm,
                        child: pw.Column(
                          mainAxisAlignment: pw.MainAxisAlignment.start,
                          crossAxisAlignment: pw.CrossAxisAlignment.start,
                          children: [
                            pw.Text(
                              "Recording ID:",
                              style: textStyle,
                              overflow: pw.TextOverflow.clip,
                            ),
                            pw.Text(
                              "Age:",
                              style: textStyle,
                              overflow: pw.TextOverflow.clip,  
                            ),
                            pw.Text(
                              "Gender:",
                              style: textStyle,
                              overflow: pw.TextOverflow.clip,    
                            ),
                            if (note != null && note.isNotEmpty) 
                              pw.Text(
                              "Note:",
                              style: textStyle,
                              overflow: pw.TextOverflow.clip,
                            ),
                          ],
                        )),
                    // Column 2. values for above
                    pw.SizedBox(
                        width: headerColumn2Width * PdfPageFormat.mm,
                        child: pw.Column(
                          mainAxisAlignment: pw.MainAxisAlignment.start,
                          crossAxisAlignment: pw.CrossAxisAlignment.start,
                          children: [
                            pw.Text(
                              "Recording 33",
                              style: textStyle,
                              overflow: pw.TextOverflow.clip,
                            ),
                            pw.Text(
                              "47",
                              style: textStyle,
                              overflow: pw.TextOverflow.clip,
                            ),
                            pw.Text(
                              "Male",
                              style: textStyle,
                              overflow: pw.TextOverflow.clip,
                            ),
                            if (note != null && note.isNotEmpty) 
                              pw.Text(
                              note,
                              style: textStyle,
                              overflow: pw.TextOverflow.clip,
                            ),
                          ],
                        )),
                    // Column 3. titles for 4
                    pw.SizedBox(
                        width: headerColumn3Width * PdfPageFormat.mm,
                        child: pw.Column(
                          mainAxisAlignment: pw.MainAxisAlignment.start,
                          crossAxisAlignment: pw.CrossAxisAlignment.start,
                          children: [
                            pw.Text(
                              "Start time:",
                              style: textStyle,
                              overflow: pw.TextOverflow.clip,
                            ),
                            pw.Text(
                              "Stop time:",
                              style: textStyle,
                              overflow: pw.TextOverflow.clip,
                            ),
                            pw.Text(
                              "Duration:",
                              style: textStyle,
                              overflow: pw.TextOverflow.clip,
                            ),
                            pw.Text(
                              "Bandwidth XXXXXXXXXX:",
                              style: textStyle,
                              overflow: pw.TextOverflow.clip,
                            ),
                          ],
                        )),
                    // Column 4. values for above
                    pw.SizedBox(
                        width: headerColumn4Width * PdfPageFormat.mm,
                        child: pw.Column(
                          mainAxisAlignment: pw.MainAxisAlignment.start,
                          crossAxisAlignment: pw.CrossAxisAlignment.start,
                          children: [
                            pw.Text(
                              // TODO: Use localized date format(?) or just use "non-T" ISO8601 like now?
                              startTime
                                  .toLocal()
                                  .toIso8601String()
                                  .split('.')
                                  .first
                                  .replaceFirst("T", " "),
                              style: textStyle,
                            ),
                            pw.Text(
                              stopTime
                                  .toLocal()
                                  .toIso8601String()
                                  .split('.')
                                  .first
                                  .replaceFirst("T", " "),
                              style: textStyle,
                            ),
                            pw.Text(
                              durationStr,
                              style: textStyle,
                            ),
                            pw.Text(
                              bandwithStr,
                              style: textStyle,
                            ),
                          ],
                        )),

                    // Column 5. Empty space
                    pw.Spacer(),

                    // Column 6. Time scale marker

                    pw.Container(
                        alignment: pw.Alignment.bottomCenter,
                        height: headerHeight * PdfPageFormat.mm,
                        width: 6.0 * ecgGraphXScalePtPerSec +
                            10 * PdfPageFormat.mm,
                        padding: const pw.EdgeInsets.only(
                          bottom: 1 * PdfPageFormat.mm,
                          right: 6 * PdfPageFormat.mm,
                        ),
                        // decoration: pw.BoxDecoration(
                        //   border: pw.Border.all(
                        //     color: PdfColor.fromHex("#ff0000"),
                        //     width: 0.2,
                        //   ),
                        // ),
                        child: _drawTimeScaleMarker()),

                    // Column 7. AVG HR text on the right
                    pw.Container(
                      alignment: pw.Alignment.bottomCenter,
                      margin: const pw.EdgeInsets.only(
                          bottom: 1 * PdfPageFormat.mm),
                      height: (headerHeight - 1) * PdfPageFormat.mm,
                      width: 25 * PdfPageFormat.mm,
                      child: pw.Row(
                          crossAxisAlignment: pw.CrossAxisAlignment.end,
                          children: [
                            pw.CustomPaint(
                              size: const PdfPoint(9 * PdfPageFormat.mm,
                                  headerHeight * PdfPageFormat.mm),
                              painter: (canvas, size) {
                                canvas.setStrokeColor(PdfColors.black);
                                canvas.setLineWidth(ecgLineThicknessPt);

                                // Draw a pulse signal with 1mV amplitude
                                const pulseWidthPt = 3.0 * PdfPageFormat.mm;
                                double pulseHeight1mVPt = ecgRowHeight /
                                    (2.0 * yRangeMillivolts) *
                                    PdfPageFormat.mm;

                                canvas.moveTo(0, 0);
                                canvas.lineTo(pulseWidthPt, 0);
                                canvas.lineTo(pulseWidthPt, pulseHeight1mVPt);
                                canvas.lineTo(
                                    2 * pulseWidthPt, pulseHeight1mVPt);
                                canvas.lineTo(2 * pulseWidthPt, 0);
                                canvas.lineTo(3 * pulseWidthPt, 0);
                                canvas.strokePath();
                              },
                            ),
                            pw.Container(
                                margin: const pw.EdgeInsets.only(
                                    bottom: 2 * PdfPageFormat.mm),
                                child: pw.Text("1 mV",
                                    style: textStyle.copyWith(fontSize: 6))),
                          ]),
                    ),
                    pw.Container(
                        alignment: pw.Alignment.bottomCenter,
                        height: headerHeight * PdfPageFormat.mm,
                        child: pw.Text(
                          textAlign: pw.TextAlign.center,
                          "AVG\nHR",
                          style: textStyle.copyWith(
                            fontSize: 7,
                          ),
                        )),
                  ]),
              pw.Expanded(child: pw.Divider(thickness: 0.5)),
            ])));
      },
    ));



    // Save the document.
    final pdfFile2 = File(outputPath);
    await pdf.saveFile(pdfFile2);

    log.info("PDF file created. ${pdfFile2.lengthSync()} bytes.");
    onProgress?.call(1.0);

    return pdfFile2;
    /// XXXXX TEST CODE

Expected behavior
The header column is visible even if some strings overflow to the next area.

Screenshots

Image Image

Flutter Doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.38.4, on macOS 26.3.1 25D2128 darwin-arm64, locale en-US)
[✓] Android toolchain - develop for Android devices (Android SDK version 36.1.0)
[✓] Xcode - develop for iOS and macOS (Xcode 26.3)
[✓] Chrome - develop for the web
[✓] Connected device (3 available)
! Error: Browsing on the local area network for Petri’s iPhone. Ensure the device is unlocked and
attached with a cable or associated with the same local area network as this Mac.
The device must be opted into Developer Mode to connect wirelessly. (code -27)
[✓] Network resources

• No issues found!

Desktop (please complete the following information):

  • iOS
  • Android
  • Browser
  • Windows
  • Linux

Smartphone (please complete the following information):

  • Device: iPhone SE 2nd gen, OnePlus 12
  • OS: iOS 26.3, Android 16
  • Browser: N/A
  • Version: N/A

Additional context

It's quite possible that I have misunderstood something about how the widgets work. Debugging into the code of dart_pdf did not reveal any exceptions or parts of code that would indicate "skipping rendering".

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions