Flutter PDF generated document margins - flutter

I am creating my first PDF document in Flutter using the pdf package.
Here you have a partial screenshot from the pdf part that I need to change:
You may see 6 rows, each of them has a time and a string.
Here you have the code for that screenshot:
static Widget buildTablaDiario(List<dynamic> listaDiarioActual){
return ListView.builder(
itemCount: listaDiarioActual.length,
itemBuilder: (pw.Context context, index){
DiarioModelo diario = listaDiarioActual[index];
DateTime tempDateI =
new DateFormat("yyyy-MM-dd hh:mm:ss")
.parse(diario.fecha_diario);
String date1 = DateFormat("dd-MM-yyyy HH:mm")
.format(tempDateI);
String hora = DateFormat("HH:mm")
.format(tempDateI);
return Row(
children: [
pw.Text("${hora}"),
pw.SizedBox(width: 10),
pw.Text(diario.descripcion,overflow: TextOverflow.clip)
]
);
}
);
}
I would like to set a right margin in order to avoid the text lines to end at the right edge from the document.

I know it's late, but in case anyone is still looking for an answer...
inside pw.Page or pw.MultiPage there is a margin attribute where you can add paddings
pdf.addPage(
pw.MultiPage(
pageFormat: PdfPageFormat.a4,
**margin: const pw.EdgeInsets.only(top: 100),**
orientation: pw.PageOrientation.portrait,...

Another approach is the following:
Before pdf.save() run the following to add your custom margins
for (int i = 1; i < pdf.document.pdfPageList.pages.length; i++) {
pdf.document.pdfPageList.pages
.elementAt(i)
.pageFormat
.copyWith(marginLeft: 50, marginTop: 100, marginRight: 50, marginBottom: 50);
}

Related

PDF library for Flutter not showing long text

I am using the pdf library in my Flutter project (https://pub.dev/packages/pdf).
This is part of the class that generates a PDF document
static Widget buildTablaDiario(List<dynamic> listaDiarioActual){
return ListView.builder(
itemCount: listaDiarioActual.length,
itemBuilder: (pw.Context context, index){
DiarioModelo diario = listaDiarioActual[index];
DateTime tempDateI =
new DateFormat("yyyy-MM-dd HH:mm:ss")
.parse(diario.fecha_diario);
String date1 = DateFormat("dd-MM-yyyy HH:mm")
.format(tempDateI);
String hora = DateFormat("HH:mm")
.format(tempDateI);
return pw.Row(
children: [
pw.Text("${hora}"),
pw.SizedBox(width: 10),
pw.Text(diario.descripcion,maxLines: 10)
]
);
}
);
}
My issue is at line
pw.Text(diario.descripcion,maxLines: 10)
The output from the PDF document is not taking into account the lenght of the text if it is longer than one line. The text ends at the end of the line and a new line is not created.
What can I do to show the text correctly if its lenght is greater than one line?

Flutter: Display grid of images on PDF

I'm using flutter with the pdf plugin to generate a PDF document where I need to display a grid of images. The images are stored in a List<File>.
Here's what I've tried. Keep in mind that all these widgets are from the pdf package and not from material.
GridView(
crossAxisCount: 5,
childAspectRatio: 1,
children: door.images.map((image) {
return Container(
child: Image(MemoryImage(?????), fit: BoxFit.cover),
);
}).toList(),
),
Note: door.images is the List<File>
How do I convert the File image (from the List<File>) to the Uint8List bytes needed for the MemoryImage widgets?
Convert your File to imagebytes.
final List<int> _imageBytes = await image.readAsBytes();
And then convert the imagebytes to your Uint8List.
final String _uint8List = base64Encode(_imageBytes);

Flutter: How do I stretch the last Element in a Wrap Widget?

I think this doesn't need much explaining so I didn't include any code or screenshots.
As the title, suggests, I have a Wrap Widget and I want the last Element of it to take up all the available horizontal space to the right, (the same way as if you put an Expanded Widget in a Row).
I this case though, I can not use Expanded or Flexible, as Wrap doesn't allow that.
Edit, added a bit of (simplified) code and screenshots:
_buildTileDragTarget is just a function that returns a DragTarget.
void _buildAnswerWidgetsFromStrings() {
_answerWidgets.clear(); //start building from 0
_answerWidgets.add(_buildTileDragTarget(index: 0)); //add initial DragTarget
//for each word in _answerStrings, add Draggable Chip & DragTarget
for (int i = 0; i < _answerStrings.length; i++) {
WordTile wordTile = _answerStrings[i];
_answerWidgets.add(
Draggable<WordTile>(
key: UniqueKey(),
data: wordTile,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
child: Chip(
label: Text(wordTile.word),
padding: EdgeInsets.all(0),
),
onTap: () {
//seems to work: tap from top to bottom (5)
setState(() {
_options[wordTile.word] = true;
_answerStrings.remove(wordTile);
});
},
),
feedback: Material(child: Chip(label: Text(wordTile.word))),
childWhenDragging: Chip(label: Text(' ' * wordTile.word.length)),
),
);
_answerWidgets.add(_buildTileDragTarget(index: i + 1)); //the position AFTER the current word (last)
}
//remove last DragTarget and add one wrapped in Expanded
_answerWidgets.removeLast();
_answerWidgets.add(
SizedBox(
// width: double.infinity,
child: _buildTileDragTarget(index: _answerStrings.length),
),
); //TODO try wrapping the whole thing in 1 big DragTarget (for last index)
// _answerWidgets.add(_buildTileDragTarget(index: _answerStrings.length));
}
Screenshots:
I wrapped the Wrap widget in a purple Container, each DragTarget is wrapped in a green Container. (you can ignore the blue part)
(1) This is what I have (without SizedBox)
(2) This is what I get when I try using SizedBox(width:double.infinity...)
(3) This is what I want
Try SizedBox(width: double.infinity)
There's also expand constructor you might use
SizedBox.Expand(
child: //your widget
)

How to add items to a Row object at runtime in Flutter?

The most amazing part of Flutter is that every widget is an object. But, when I try to make an object from a Row and add a List of widgets to its children, I get this error on runtime:
Cannot add to an unmodifiable list
I'm currently creating my Row like this:
Row rowStar = Row();
rowStar.children.addAll(rowChildren);
Is this wrong? If so, what's the correct way?
1. Why it won't work
If all you want to do is add a list to a Row, you should create the list and then initialize the Row with it. You cannot add the list afterwards because Flutter's widgets typically have final fields/properties, which are not modifiable at runtime.
2. The Flutter Way
However, what you could do to dynamically modify the list is pass a function that builds your list internally at runtime. And that's exactly what the ListView.builder() constructor of ListView does.
For example, this the docs example for dynamically creating a List of Containers based on the the Lists entries and colorCodes.
final List<String> entries = <String>['A', 'B', 'C'];
final List<int> colorCodes = <int>[600, 500, 100];
ListView.builder(
padding: const EdgeInsets.all(8),
itemCount: entries.length,
itemBuilder: (BuildContext context, int index) {
return Container(
height: 50,
color: Colors.amber[colorCodes[index]],
child: Center(child: Text('Entry ${entries[index]}')),
);
}
);
You could add a button to .add, say, the letter D and the colorCode 400, to the respective Lists, and Flutter would render the respective Containers dynamically on the screen.
I'm not sure if I get it right, but what you're trying to do doesn't make sense. If you want to reuse a Row with widgets and information you can just build it once and save the whole thing as widget.
Try it like this:
Build a Row.
Create widgets for your children.
Display them as widgets of the row.
Row(
children: <Widget>[
customWidget1(),
customWidget2(),
customWidget3(),
],
)
Your custom widgets are the content of the Row then.
Hope it helps.
In my case, I can make a Row from a previously created List<Widget>:
Row calculateListOfStars(ranking) {
final List<Widget> rowList = [];
rowList.addAll([
// make my custom widget
]);
final Row rowStars = Row(
children: rowList,
);
return rowStars;
}
Use Listview:
ListView(
children: rowStar((data) {
return ListTile();
}).toList();

Update Text() based on user input in a TextFormField() within a container in a ListVIew

I'm new to flutter and have a ... challenge with the above.
I retrive data about several objects / documents from FireStore and create a ListView.
The logic, or lack there of, look like this:
Container
StreamBuilder
ListView
Container
Rows, Columns, Text and whatever to display each document from StreamBuilder.
This ListView contains several Containers where each Container display data from one object. Within each Container there is a TextFormField that takes a double. Based on this value, I want to change a Text() within the same container - a calculation.
Is this possible? If so - how? I understand I can use TextEditController somehow, but then the result of tha calculation will be editable. Or?
return new ListView(//
children: getMedicationItem(asyncSnapshot
shrinkWrap: true,
children: asyncSnapshot.data.documents.map((DocumentSnapshot document) {
MyObject object = new MyObject(document, integerValue);
backgroundColor = !backgroundColor;
return new Container(
color: backgroundColor? Colors.black26: Colors.white,
child: new Row(
children: <Widget>[
new Flexible(
child: TextFormField(
onChanged: (text) {
double calculation = 0.0;
if (text.length > 0)
calculation = double.tryParse(text) * 23;
**UPDATE "$calculation" in Text() below**
},
keyboardType: TextInputType.number,
inputFormatters: [_decimal_validator],
)
),
new Flexible(
child: Text("$calculation"),
)
]
),
);
}
).toList(),
)
);
All you should need to do is use a setState to tell Flutter that the variable has changed and you want whichever Widgets that use it rebuilt:
if (text.length > 0){
setState((){
calculation = double.tryParse(text) * 23;
});
}