Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic Footer Space Allocation in MultiPage #1826

Open
tomaspalaoro opened this issue Mar 7, 2025 · 4 comments
Open

Dynamic Footer Space Allocation in MultiPage #1826

tomaspalaoro opened this issue Mar 7, 2025 · 4 comments
Labels
needs triage support Asking for help writing an application

Comments

@tomaspalaoro
Copy link

I'm using the MultiPage widget. I have a scenario where the footer should only appear on the last page, so I'm conditionally rendering it like so:

footer: (context) {
  if (context.pageNumber == context.pagesCount) {
    return pw.Container(
      alignment: pw.Alignment.center,
      margin: const pw.EdgeInsets.only(top: 20),
      child: pw.Text('Footer content'),
    );
  }
  return pw.SizedBox.shrink();
},

In my implementation, it seems that the maximum footer height is pre-calculated and reserved on every page, even if the footer content is only present on the last page. Is there a way to dynamically allocate footer space so that pages without footer content do not reserve that extra space?

@tomaspalaoro tomaspalaoro added needs triage support Asking for help writing an application labels Mar 7, 2025
@matteo-convert
Copy link

you can put it in build for rendering only at the end

Image

Image

btw yes the package seem to miss some good features in multipage like page report

@tomaspalaoro
Copy link
Author

Thanks for the suggestion. However, when my footer, which contains a list of widgets, is wrapped in an Expanded widget, it results in the following error:

Cannot have a spanning widget flexible

Here's an example

build: (context) => [
          pw.Text("content"),
          pw.Expanded(
              child: pw.Align(
                  alignment: pw.Alignment.bottomCenter,
                  child: pw.Column(
                    children: footerContentList(),
                  )))
        ],
List<pw.Widget> footerContentList() {
    return [
      pw.Text("content example 1"),
      pw.Text("content example 2"),
      pw.Text("content example 2")
    ];
  }

@leocirto
Copy link

Interesting discussion. The vertical space is allotted to the footer beforehand, and is based on the largest footer. I believe the Spacer( ) widget can help you push a fake footer inside Build to the bottom of the page.

Example:

  Future<Uint8List> _builPdfPreview(final PdfPageFormat format) async {
      final pdf = pw.Document( );
      const bool useFakeFooter = false;
   // const bool useFakeFooter = true;
      pdf.addPage(
        pw.MultiPage(
            pageFormat: format,
            margin: const pw.EdgeInsets.symmetric( horizontal: 50, vertical: 0 ),
            build: (final pw.Context ctx) {
              return [
                 for( int k = 0; k < 40; k++ )  pw.Text( '$k ---------' ),
                 if( useFakeFooter ) ... [
                   pw.Spacer( ),
                   pw.Footer(
                      padding: const pw.EdgeInsets.all( 0 ),
                      margin:  const pw.EdgeInsets.all( 0 ),
                      leading: pw.Text('useFakeFooter = $useFakeFooter Footer.\n\n\n\n'),
                   // NOTE: CANNOT ACCESS pageNumber AND pagesCount USING THIS CONTEXT, LOOK:
                   //       Null check operator used on a null value
                   // leading: pw.Text('Fake Footer. Page ${ctx.pageNumber}/${ctx.pagesCount}\n\n\n\n'),
                   ),
                 ]
              ];
            },
            footer: ( useFakeFooter == true) ? null : ( final pw.Context ctx ){
              if (ctx.pageNumber != ctx.pagesCount) {
                 return pw.Footer(
                     padding: const pw.EdgeInsets.all( 0 ),
                     margin:  const pw.EdgeInsets.all( 0 ),
                     leading: null,
                 );
              }
              return pw.Footer(
                     padding: const pw.EdgeInsets.all( 0 ),
                     margin:  const pw.EdgeInsets.all( 0 ),
                     leading: pw.Text('useFakeFooter = $useFakeFooter. Page ${ctx.pageNumber}/${ctx.pagesCount}\n\n\n\n'),
              );
            }
        )
      );
      return pdf.save();
  }

Outputs:

True Footer Fake Footer (footer inside build)
Image Image

@matteo-convert
Copy link

Yes it works with my example because my footer is wrapped in a container with fixed height

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs triage support Asking for help writing an application
Projects
None yet
Development

No branches or pull requests

3 participants