Flutter中Generate PDF按钮失效,MemoryImage引发索引越界错误
问题
开发Flutter应用时,使用package:pdf/pdf.dart包实现点击按钮生成PDF功能,但"Generate PDF"按钮无法正常工作,控制台抛出如下错误:
E/flutter ( 7975): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)]
Unhandled Exception: RangeError (index): Index out of range: no indices are valid: 0 E/flutter ( 7975): #0 Uint8List.[]
(dart:typed_data-patch/typed_data_patch.dart:2271:7) E/flutter (
7975): #1 InputBuffer.[]
(package:image/src/util/input_buffer.dart:44:39) E/flutter ( 7975): #2
JpegData.validate
(package:image/src/formats/jpeg/jpeg_data.dart:64:17) E/flutter (
7975): #3 JpegDecoder.isValidFile
(package:image/src/formats/jpeg_decoder.dart:18:50) E/flutter ( 7975):
#4 findDecoderForData (package:image/src/formats/formats.dart:116:11) E/flutter ( 7975): #5
new MemoryImage (package:pdf/src/widgets/image_provider.dart:101:24)
E/flutter ( 7975): #6 CetakinvoicePage._generatePDF. (package:smta/cetakinvoicepage.dart:421:22) E/flutter (
7975): #7 Page.postProcess
(package:pdf/src/widgets/page.dart:142:21) E/flutter ( 7975): #8
Document.save (package:pdf/src/widgets/document.dart:130:14) E/flutter
( 7975): #9 CetakinvoicePage._generatePDF
(package:smta/cetakinvoicepage.dart:442:39) E/flutter ( 7975):
E/flutter ( 7975):
相关代码如下:
import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:pdf/pdf.dart'; import 'package:pdf/widgets.dart' as pw; import 'package:path_provider/path_provider.dart'; import 'package:open_file/open_file.dart'; import 'package:flutter/services.dart' show rootBundle; class CetakinvoicePage extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: Scaffold( body: SafeArea( child: SingleChildScrollView( child: Column( children: [ // Your existing content here... Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: EdgeInsets.symmetric(horizontal: 20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox(height: 10.0,), Padding( padding: EdgeInsets.symmetric(horizontal: 10.0), child: Row( children: [ Image.asset( 'assets/images/logosgntulisan.png', width: 120, ), Spacer(), Column( children: [ Image.asset( 'assets/images/barcodedummy.png', width: 150, ), SizedBox(height: 5.0,), const Text( 'SG10202312060001', style: TextStyle( fontSize: 16, color: Colors.black, fontWeight: FontWeight.w400, ), ), ], ), ], ), ), Padding( padding: EdgeInsets.symmetric(horizontal: 5.0), child: Container( width: double.infinity, child: const Divider( color: Colors.black, thickness: 1.0, ), ), ), Column( children: [ const Text( 'PT SINERGI GULA NUSANTARA', style: TextStyle( fontSize: 16, color: Colors.black, fontWeight: FontWeight.w500, ), ), ], ), ] ), ), ] ), // Button to trigger PDF generation SizedBox(height: 10.0,), ElevatedButton( onPressed: () { _generatePDF(context); }, child: Text('Generate PDF'), ), ], ), ), ), ), ); } // Function to generate PDF Future<void> _generatePDF(BuildContext context) async { final pdf = pw.Document(); // Add content to the PDF pdf.addPage(pw.Page( pageFormat: PdfPageFormat.a4, build: (pw.Context context) { return pw.Row( children: [ pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.start, children: [ ], ), pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.start, mainAxisAlignment: pw.MainAxisAlignment.end, children: [ pw.Text( 'Waktu Masuk', style: pw.TextStyle( fontSize: 14, color: PdfColors.black, fontWeight: pw.FontWeight.bold, ), ), pw.SizedBox(height: 5.0), pw.Text( '14-12/2023 19.00', style: pw.TextStyle( fontSize: 14, color: PdfColors.black, fontWeight: pw.FontWeight.normal, ), ), pw.SizedBox(height: 10.0), pw.Image( pw.MemoryImage( // Convert your image to bytes (you might need to change this part) // For example: (await rootBundle.load('assets/images/truktebugambar.jpg')).buffer.asUint8List() Uint8List.fromList([/* your image bytes go here */]), ), height: 100, width: 120, ), pw.SizedBox(height: 20.0), ], ), ], ); }, )); // Get the application documents directory final directory = await getApplicationDocumentsDirectory(); // Create the PDF file final file = File('${directory.path}/example.pdf'); await file.writeAsBytes(await pdf.save()); // Open the PDF using a PDF viewer OpenFile.open(file.path); } }
解决方案
错误根源在于pw.MemoryImage传入了空的Uint8List,导致图片解码器无法处理无效数据,抛出索引越界错误。按以下步骤修复:
导入缺失依赖:代码中使用
File但未导入dart:io,需在文件顶部添加:import 'dart:io';加载实际图片字节:在生成PDF前,通过
rootBundle异步加载assets中的图片,转换为Uint8List后传入pw.MemoryImage。修改后的_generatePDF函数如下:Future<void> _generatePDF(BuildContext context) async { final pdf = pw.Document(); // 异步加载assets中的图片字节 final imageBytes = (await rootBundle.load('assets/images/truktebugambar.jpg')).buffer.asUint8List(); pdf.addPage(pw.Page( pageFormat: PdfPageFormat.a4, build: (pw.Context context) { return pw.Row( children: [ pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.start, children: [], ), pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.start, mainAxisAlignment: pw.MainAxisAlignment.end, children: [ pw.Text( 'Waktu Masuk', style: pw.TextStyle( fontSize: 14, color: PdfColors.black, fontWeight: pw.FontWeight.bold, ), ), pw.SizedBox(height: 5.0), pw.Text( '14-12/2023 19.00', style: pw.TextStyle( fontSize: 14, color: PdfColors.black, fontWeight: pw.FontWeight.normal, ), ), pw.SizedBox(height: 10.0), pw.Image( pw.MemoryImage(imageBytes), height: 100, width: 120, ), pw.SizedBox(height: 20.0), ], ), ], ); }, )); final directory = await getApplicationDocumentsDirectory(); final file = File('${directory.path}/example.pdf'); await file.writeAsBytes(await pdf.save()); OpenFile.open(file.path); }验证assets配置:确保
pubspec.yaml中已正确声明图片资源,例如:flutter: assets: - assets/images/truktebugambar.jpg - assets/images/logosgntulisan.png - assets/images/barcodedummy.png
完成以上修改后,点击"Generate PDF"按钮即可正常生成包含图片的PDF文件。
内容的提问来源于stack exchange,提问作者Asa Marsal

