内部小部件树不能大于页面:一个小部件不能在一个页面上部分绘制,而其余部分在另一页面上:它是不可分割的

Posted

技术标签:

【中文标题】内部小部件树不能大于页面:一个小部件不能在一个页面上部分绘制,而其余部分在另一页面上:它是不可分割的【英文标题】:An inner widget tree cannot be bigger than a page: A Widget cannot be drawn partially on one page and the remaining on another page: It's insecable 【发布时间】:2022-01-22 19:45:15 【问题描述】:

在这个 Multipage 类 https://pub.dev/documentation/pdf/latest/widgets/MultiPage-class.html 中明确提到内部小部件树不能大于页面:小部件不能部分绘制在一个页面上,而其余部分则绘制在另一个页面上: 这是不安全的。

我想使用这个pdf 插件生成视图。

import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import '../utils/page_data.dart';

makeTemplate3(pw.Document doc, pw.PageTheme pageTheme, PageData pageData,
    pw.MemoryImage profileImagee) 
  List<LineItems> _product = [];
  for (var i = 0; i < 100; i++) 
    int pos = i + 1;
    _product.add(
      LineItems(
        id: '$pos',
        name: 'Product',
        price: '100$i',
        quantity: '200$i',
        total: 'Total Sum: ' + (i * 200).toString(),
        desc:
            'Test widget $i: An inner widget tree cannot be bigger than a page: A Widget cannot be drawn partially on one page and the remaining on another page: Its insecable. A small set of Widget can automatically span over multiple pages, and can be used as a direct child of the build method: Flex, Partition, Table, Wrap, GridView, and Column.',
      ),
    );
  

  doc.addPage(
    pw.MultiPage(
        pageTheme: pageTheme,
        build: (pw.Context context) => <pw.Widget>[
              _headerLayout(),
              // ignore: sdk_version_ui_as_code
              for (int i = 0; i < _product.length; i++)
                _productLayout(_product[i]),
            ]),
  );

  return doc;


_headerLayout() 
  return new pw.Container(
      padding: pw.EdgeInsets.all(20.0),
      height: 80.0,
      color: PdfColors.blue,
      child: pw.Row(
        crossAxisAlignment: pw.CrossAxisAlignment.start,
        children: [
          pw.Container(
            child: pw.Text(
              'Header',
              style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 25),
            ),
          ),
        ],
      ));


_productLayout(LineItems item) 
  return new pw.Container(
      margin: pw.EdgeInsets.all(20.0),
      child: pw.Column(
        crossAxisAlignment: pw.CrossAxisAlignment.start,
        children: <pw.Widget>[
          pw.Column(crossAxisAlignment: pw.CrossAxisAlignment.start, children: [
            pw.Container(
              padding: pw.EdgeInsets.only(bottom: 20),
              child: pw.Text(
                item.name.toString() + ' - ID: ' + item.id.toString(),
                style:
                    pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 20),
              ),
            ),
            pw.Container(
              padding: pw.EdgeInsets.only(bottom: 10),
              child: pw.Text(
                  'Qty: ' +
                      item.quantity.toString() +
                      ' x Cost: £' +
                      item.price,
                  style: pw.TextStyle(
                      fontWeight: pw.FontWeight.normal, fontSize: 16)),
            ),
            pw.Container(
                padding: pw.EdgeInsets.only(bottom: 10),
                child: pw.Text(
                  '£' + item.total.toString(),
                  style: pw.TextStyle(
                      fontWeight: pw.FontWeight.normal, fontSize: 14),
                )),
            pw.Text('Description: ' + item.desc,
                style: pw.TextStyle(
                    fontWeight: pw.FontWeight.normal, fontSize: 16)),
            getSubItemList()
          ]),
        ],
      ));


pw.Widget getSubItemList() 
  List<pw.Container> list = [];
  for (int i = 0; i < 25; i++) 
    list.add(
      pw.Container(
          child: pw.Text(
              'Sub Product: $i                         Total Sum:   $i-475',
              style: pw.TextStyle(
                  fontWeight: pw.FontWeight.normal, fontSize: 16))),
    );
  

  return pw.Container(
      padding: pw.EdgeInsets.all(20), child: pw.Column(children: list));


class LineItems 
  String total, price, quantity, name, id, desc;
  LineItems(
      this.total, this.price, this.quantity, this.name, this.id, this.desc);

【问题讨论】:

【参考方案1】:

这不是一个复杂的布局。您需要做的就是将每个块扁平化为 MultiPage 子级。

试试这个:

import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:printing/printing.dart';

Future<void> main() async 
  runApp(const MyApp('Printing Demo'));


class MyApp extends StatelessWidget 
  const MyApp(this.title, Key? key) : super(key: key);

  final String title;

  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text(title)),
        body: PdfPreview(
          build: (format) => _generatePdf(format, title),
        ),
      ),
    );
  

  Future<Uint8List> _generatePdf(PdfPageFormat format, String title) async 
    final pdf = pw.Document();

    List<LineItems> _product = [];
    for (var i = 0; i < 100; i++) 
      int pos = i + 1;
      _product.add(
        LineItems(
          id: '$pos',
          name: 'Product',
          price: '100$i',
          quantity: '200$i',
          total: 'Total Sum: ' + (i * 200).toString(),
          desc:
              'Test widget $i: An inner widget tree cannot be bigger than a page: A Widget cannot be drawn partially on one page and the remaining on another page: Its insecable. A small set of Widget can automatically span over multiple pages, and can be used as a direct child of the build method: Flex, Partition, Table, Wrap, GridView, and Column.',
        ),
      );
    

    pdf.addPage(
      pw.MultiPage(
        pageFormat: format,
        header: _headerLayout,
        footer: (pw.Context context) 
          return pw.Container(
              alignment: pw.Alignment.centerRight,
              margin: const pw.EdgeInsets.only(top: 1.0 * PdfPageFormat.cm),
              child: pw.Text(
                  'Page $context.pageNumber of $context.pagesCount',
                  style: pw.Theme.of(context)
                      .defaultTextStyle
                      .copyWith(color: PdfColors.grey)));
        ,
        build: (pw.Context context) => <pw.Widget>[
          for (int i = 0; i < _product.length; i++)
            ..._productLayout(_product[i]),
        ],
      ),
    );

    return pdf.save();
  


pw.Widget _headerLayout(pw.Context context) 
  return pw.Container(
    padding: const pw.EdgeInsets.all(20.0),
    height: 80.0,
    color: PdfColors.blue,
    child: pw.Row(
      crossAxisAlignment: pw.CrossAxisAlignment.start,
      children: [
        pw.Container(
          child: pw.Text(
            'Header',
            style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 25),
          ),
        ),
      ],
    ),
  );


Iterable<pw.Widget> _productLayout(LineItems item) sync* 
  yield pw.Container(
    padding: const pw.EdgeInsets.all(20),
    child: pw.Text(
      item.name.toString() + ' - ID: ' + item.id.toString(),
      style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 20),
    ),
  );
  yield pw.Container(
    padding: const pw.EdgeInsets.only(bottom: 10, left: 20, right: 20),
    child: pw.Text(
      'Qty: ' + item.quantity.toString() + ' x Cost: £' + item.price,
      style: pw.TextStyle(
        fontWeight: pw.FontWeight.normal,
        fontSize: 16,
      ),
    ),
  );
  yield pw.Container(
    padding: const pw.EdgeInsets.only(bottom: 10, left: 20, right: 20),
    child: pw.Text(
      '£' + item.total.toString(),
      style: pw.TextStyle(fontWeight: pw.FontWeight.normal, fontSize: 14),
    ),
  );
  yield pw.Container(
    padding: const pw.EdgeInsets.only(left: 20, right: 20),
    child: pw.Text(
      'Description: ' + item.desc,
      style: pw.TextStyle(
        fontWeight: pw.FontWeight.normal,
        fontSize: 16,
      ),
    ),
  );
  yield pw.SizedBox(height: 20);
  yield* getSubItemList();
  yield pw.SizedBox(height: 20);


Iterable<pw.Widget> getSubItemList() sync* 
  for (int i = 0; i < 25; i++) 
    yield pw.Container(
      padding: const pw.EdgeInsets.only(left: 30, right: 20),
      child: pw.Text(
        'Sub Product: $i                         Total Sum:   $i-475',
        style: pw.TextStyle(
          fontWeight: pw.FontWeight.normal,
          fontSize: 16,
        ),
      ),
    );
  


class LineItems 
  String total, price, quantity, name, id, desc;
  LineItems(
    required this.total,
    required this.price,
    required this.quantity,
    required this.name,
    required this.id,
    required this.desc,
  );

【讨论】:

它在这个简单的案例中工作。但是当孩子有动态内容到它的标题时。我们如何解决这个问题? 什么样的内容? 动态内容,它使用新的容器小部件及其自己的子列表。

以上是关于内部小部件树不能大于页面:一个小部件不能在一个页面上部分绘制,而其余部分在另一页面上:它是不可分割的的主要内容,如果未能解决你的问题,请参考以下文章

如何从另一个小部件类更新小部件树 - Flutter

Flutter:检测任何在屏幕上不可见但在小部件树中的小部件的重建

在小部件树中检测到 Flutter Duplicate GlobalKey

在颤振中如何使带有平移手势的内部小部件不会与PageView冲突

在多个屏幕中使用表单颤振“在小部件树中检测到重复的 GlobalKey”错误

在 https 页面上获取 SEC7111 尝试使用 XMLHttpRequest 联系本地机器小部件