Showing
8 changed files
with
192 additions
and
34 deletions
| @@ -187,48 +187,156 @@ class FractionColumnWidth extends TableColumnWidth { | @@ -187,48 +187,156 @@ class FractionColumnWidth extends TableColumnWidth { | ||
| 187 | } | 187 | } |
| 188 | } | 188 | } |
| 189 | 189 | ||
| 190 | +typedef OnCellFormat = String Function(int index, dynamic data); | ||
| 191 | + | ||
| 190 | /// A widget that uses the table layout algorithm for its children. | 192 | /// A widget that uses the table layout algorithm for its children. |
| 191 | class Table extends Widget implements SpanningWidget { | 193 | class Table extends Widget implements SpanningWidget { |
| 192 | - Table( | ||
| 193 | - {this.children = const <TableRow>[], | ||
| 194 | - this.border, | ||
| 195 | - this.defaultVerticalAlignment = TableCellVerticalAlignment.top, | ||
| 196 | - this.columnWidths, | ||
| 197 | - this.defaultColumnWidth = const IntrinsicColumnWidth(), | ||
| 198 | - this.tableWidth = TableWidth.max}) | ||
| 199 | - : assert(children != null), | 194 | + Table({ |
| 195 | + this.children = const <TableRow>[], | ||
| 196 | + this.border, | ||
| 197 | + this.defaultVerticalAlignment = TableCellVerticalAlignment.top, | ||
| 198 | + this.columnWidths, | ||
| 199 | + this.defaultColumnWidth = const IntrinsicColumnWidth(), | ||
| 200 | + this.tableWidth = TableWidth.max, | ||
| 201 | + }) : assert(children != null), | ||
| 200 | assert(defaultColumnWidth != null), | 202 | assert(defaultColumnWidth != null), |
| 201 | assert(defaultVerticalAlignment != null), | 203 | assert(defaultVerticalAlignment != null), |
| 202 | super(); | 204 | super(); |
| 203 | 205 | ||
| 204 | factory Table.fromTextArray({ | 206 | factory Table.fromTextArray({ |
| 205 | - @required Context context, | ||
| 206 | - @required List<List<String>> data, | ||
| 207 | - EdgeInsets margin = const EdgeInsets.all(5), | 207 | + Context context, |
| 208 | + @required List<List<dynamic>> data, | ||
| 209 | + @deprecated EdgeInsets margin, | ||
| 210 | + EdgeInsets cellPadding = const EdgeInsets.all(5), | ||
| 211 | + double cellHeight = 0, | ||
| 212 | + Alignment cellAlignment = Alignment.topLeft, | ||
| 213 | + Map<int, Alignment> cellAlignments, | ||
| 214 | + TextStyle cellStyle, | ||
| 215 | + TextStyle oddCellStyle, | ||
| 216 | + OnCellFormat cellFormat, | ||
| 217 | + int headerCount = 1, | ||
| 218 | + List<dynamic> headers, | ||
| 219 | + EdgeInsets headerPadding, | ||
| 220 | + double headerHeight, | ||
| 221 | + Alignment headerAlignment = Alignment.center, | ||
| 222 | + Map<int, Alignment> headerAlignments, | ||
| 223 | + TextStyle headerStyle, | ||
| 224 | + OnCellFormat headerFormat, | ||
| 225 | + TableBorder border = const TableBorder(), | ||
| 226 | + Map<int, TableColumnWidth> columnWidths, | ||
| 227 | + IntrinsicColumnWidth defaultColumnWidth = const IntrinsicColumnWidth(), | ||
| 228 | + TableWidth tableWidth = TableWidth.max, | ||
| 229 | + BoxDecoration headerDecoration, | ||
| 230 | + BoxDecoration rowDecoration, | ||
| 231 | + BoxDecoration oddRowDecoration, | ||
| 208 | }) { | 232 | }) { |
| 233 | + assert(data != null); | ||
| 234 | + assert(headerCount != null && headerCount >= 0); | ||
| 235 | + assert(cellHeight != null); | ||
| 236 | + | ||
| 237 | + if (margin != null) { | ||
| 238 | + cellPadding = margin; | ||
| 239 | + } | ||
| 240 | + | ||
| 241 | + if (context != null) { | ||
| 242 | + final ThemeData theme = Theme.of(context); | ||
| 243 | + headerStyle ??= theme.tableHeader; | ||
| 244 | + cellStyle ??= theme.tableCell; | ||
| 245 | + } | ||
| 246 | + | ||
| 247 | + headerPadding ??= cellPadding; | ||
| 248 | + headerHeight ??= cellHeight; | ||
| 249 | + oddRowDecoration ??= rowDecoration; | ||
| 250 | + oddCellStyle ??= cellStyle; | ||
| 251 | + cellAlignments ??= const <int, Alignment>{}; | ||
| 252 | + headerAlignments ??= cellAlignments; | ||
| 253 | + | ||
| 209 | final List<TableRow> rows = <TableRow>[]; | 254 | final List<TableRow> rows = <TableRow>[]; |
| 210 | - for (List<String> row in data) { | 255 | + |
| 256 | + int rowNum = 0; | ||
| 257 | + if (headers != null) { | ||
| 211 | final List<Widget> tableRow = <Widget>[]; | 258 | final List<Widget> tableRow = <Widget>[]; |
| 212 | - if (row == data.first) { | ||
| 213 | - for (String cell in row) { | ||
| 214 | - tableRow.add(Container( | ||
| 215 | - alignment: Alignment.center, | ||
| 216 | - margin: margin, | ||
| 217 | - child: Text(cell, style: Theme.of(context).tableHeader))); | 259 | + |
| 260 | + for (final dynamic cell in headers) { | ||
| 261 | + tableRow.add( | ||
| 262 | + Container( | ||
| 263 | + alignment: headerAlignments[tableRow.length] ?? headerAlignment, | ||
| 264 | + padding: headerPadding, | ||
| 265 | + constraints: BoxConstraints(minHeight: headerHeight), | ||
| 266 | + child: Text( | ||
| 267 | + headerFormat == null | ||
| 268 | + ? cell.toString() | ||
| 269 | + : headerFormat(tableRow.length, cell), | ||
| 270 | + style: headerStyle, | ||
| 271 | + ), | ||
| 272 | + ), | ||
| 273 | + ); | ||
| 274 | + } | ||
| 275 | + rows.add(TableRow( | ||
| 276 | + children: tableRow, | ||
| 277 | + repeat: true, | ||
| 278 | + decoration: headerDecoration, | ||
| 279 | + )); | ||
| 280 | + rowNum++; | ||
| 281 | + } | ||
| 282 | + | ||
| 283 | + for (final List<dynamic> row in data) { | ||
| 284 | + final List<Widget> tableRow = <Widget>[]; | ||
| 285 | + final bool isOdd = (rowNum - headerCount) % 2 != 0; | ||
| 286 | + | ||
| 287 | + if (rowNum < headerCount) { | ||
| 288 | + for (final dynamic cell in row) { | ||
| 289 | + tableRow.add( | ||
| 290 | + Container( | ||
| 291 | + alignment: headerAlignments[tableRow.length] ?? headerAlignment, | ||
| 292 | + padding: headerPadding, | ||
| 293 | + constraints: BoxConstraints(minHeight: headerHeight), | ||
| 294 | + child: Text( | ||
| 295 | + headerFormat == null | ||
| 296 | + ? cell.toString() | ||
| 297 | + : headerFormat(tableRow.length, cell), | ||
| 298 | + style: headerStyle, | ||
| 299 | + ), | ||
| 300 | + ), | ||
| 301 | + ); | ||
| 218 | } | 302 | } |
| 219 | } else { | 303 | } else { |
| 220 | - for (String cell in row) { | ||
| 221 | - tableRow.add(Container( | ||
| 222 | - margin: margin, | ||
| 223 | - child: Text(cell, style: Theme.of(context).tableCell))); | 304 | + for (final dynamic cell in row) { |
| 305 | + tableRow.add( | ||
| 306 | + Container( | ||
| 307 | + alignment: cellAlignments[tableRow.length] ?? cellAlignment, | ||
| 308 | + padding: cellPadding, | ||
| 309 | + constraints: BoxConstraints(minHeight: cellHeight), | ||
| 310 | + child: Text( | ||
| 311 | + cellFormat == null | ||
| 312 | + ? cell.toString() | ||
| 313 | + : cellFormat(tableRow.length, cell), | ||
| 314 | + style: isOdd ? oddCellStyle : cellStyle, | ||
| 315 | + ), | ||
| 316 | + ), | ||
| 317 | + ); | ||
| 224 | } | 318 | } |
| 225 | } | 319 | } |
| 226 | - rows.add(TableRow(children: tableRow, repeat: row == data.first)); | 320 | + |
| 321 | + BoxDecoration decoration = isOdd ? oddRowDecoration : rowDecoration; | ||
| 322 | + if (rowNum < headerCount) { | ||
| 323 | + decoration = headerDecoration; | ||
| 324 | + } | ||
| 325 | + | ||
| 326 | + rows.add(TableRow( | ||
| 327 | + children: tableRow, | ||
| 328 | + repeat: rowNum < headerCount, | ||
| 329 | + decoration: decoration, | ||
| 330 | + )); | ||
| 331 | + rowNum++; | ||
| 227 | } | 332 | } |
| 228 | return Table( | 333 | return Table( |
| 229 | - border: const TableBorder(), | ||
| 230 | - tableWidth: TableWidth.max, | ||
| 231 | - children: rows); | 334 | + border: border, |
| 335 | + tableWidth: tableWidth, | ||
| 336 | + children: rows, | ||
| 337 | + columnWidths: columnWidths, | ||
| 338 | + defaultColumnWidth: defaultColumnWidth, | ||
| 339 | + ); | ||
| 232 | } | 340 | } |
| 233 | 341 | ||
| 234 | @override | 342 | @override |
| @@ -4,7 +4,7 @@ description: A pdf producer for Dart. It can create pdf files for both web or fl | @@ -4,7 +4,7 @@ description: A pdf producer for Dart. It can create pdf files for both web or fl | ||
| 4 | homepage: https://github.com/DavBfr/dart_pdf/tree/master/pdf | 4 | homepage: https://github.com/DavBfr/dart_pdf/tree/master/pdf |
| 5 | repository: https://github.com/DavBfr/dart_pdf | 5 | repository: https://github.com/DavBfr/dart_pdf |
| 6 | issue_tracker: https://github.com/DavBfr/dart_pdf/issues | 6 | issue_tracker: https://github.com/DavBfr/dart_pdf/issues |
| 7 | -version: 1.7.1 | 7 | +version: 1.8.0 |
| 8 | 8 | ||
| 9 | environment: | 9 | environment: |
| 10 | sdk: ">=2.3.0 <3.0.0" | 10 | sdk: ">=2.3.0 <3.0.0" |
| @@ -186,6 +186,52 @@ void main() { | @@ -186,6 +186,52 @@ void main() { | ||
| 186 | ); | 186 | ); |
| 187 | }); | 187 | }); |
| 188 | 188 | ||
| 189 | + test('Table fromTextArray', () { | ||
| 190 | + pdf.addPage(Page( | ||
| 191 | + build: (Context context) => Table.fromTextArray( | ||
| 192 | + context: context, | ||
| 193 | + tableWidth: TableWidth.min, | ||
| 194 | + data: <List<dynamic>>[ | ||
| 195 | + <dynamic>['One', 'Two', 'Three'], | ||
| 196 | + <dynamic>[1, 2, 3], | ||
| 197 | + <dynamic>[4, 5, 6], | ||
| 198 | + ], | ||
| 199 | + ), | ||
| 200 | + )); | ||
| 201 | + }); | ||
| 202 | + | ||
| 203 | + test('Table fromTextArray with formatting', () { | ||
| 204 | + pdf.addPage(Page( | ||
| 205 | + build: (Context context) => Table.fromTextArray( | ||
| 206 | + border: null, | ||
| 207 | + cellAlignment: Alignment.center, | ||
| 208 | + headerDecoration: const BoxDecoration( | ||
| 209 | + borderRadius: 2, | ||
| 210 | + color: PdfColors.indigo, | ||
| 211 | + ), | ||
| 212 | + headerHeight: 25, | ||
| 213 | + cellHeight: 40, | ||
| 214 | + headerStyle: TextStyle( | ||
| 215 | + color: PdfColors.white, | ||
| 216 | + fontWeight: FontWeight.bold, | ||
| 217 | + ), | ||
| 218 | + rowDecoration: const BoxDecoration( | ||
| 219 | + border: BoxBorder( | ||
| 220 | + bottom: true, | ||
| 221 | + color: PdfColors.indigo, | ||
| 222 | + width: .5, | ||
| 223 | + ), | ||
| 224 | + ), | ||
| 225 | + headers: <dynamic>['One', 'Two', 'Three'], | ||
| 226 | + data: <List<dynamic>>[ | ||
| 227 | + <dynamic>[1, 2, 3], | ||
| 228 | + <dynamic>[4, 5, 6], | ||
| 229 | + <dynamic>[7, 8, 9], | ||
| 230 | + ], | ||
| 231 | + ), | ||
| 232 | + )); | ||
| 233 | + }); | ||
| 234 | + | ||
| 189 | tearDownAll(() { | 235 | tearDownAll(() { |
| 190 | final File file = File('widgets-table.pdf'); | 236 | final File file = File('widgets-table.pdf'); |
| 191 | file.writeAsBytesSync(pdf.save()); | 237 | file.writeAsBytesSync(pdf.save()); |
| @@ -164,7 +164,7 @@ void main() { | @@ -164,7 +164,7 @@ void main() { | ||
| 164 | build: (Context context) => <Widget>[ | 164 | build: (Context context) => <Widget>[ |
| 165 | Table.fromTextArray( | 165 | Table.fromTextArray( |
| 166 | context: context, | 166 | context: context, |
| 167 | - margin: const EdgeInsets.all(3), | 167 | + cellPadding: const EdgeInsets.all(3), |
| 168 | data: <List<String>>[ | 168 | data: <List<String>>[ |
| 169 | <String>['Company', 'Contact', 'Country'], | 169 | <String>['Company', 'Contact', 'Country'], |
| 170 | <String>['Alfreds Futterkiste', 'Maria Anders', 'Germany'], | 170 | <String>['Alfreds Futterkiste', 'Maria Anders', 'Germany'], |
No preview for this file type
No preview for this file type
No preview for this file type
-
Please register or login to post a comment