内部小部件树不能大于页面:一个小部件不能在一个页面上部分绘制,而其余部分在另一页面上:它是不可分割的
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 Duplicate GlobalKey
在颤振中如何使带有平移手势的内部小部件不会与PageView冲突