تطوير تطبيق لانشاء ملف pdf مخصص لشركتك في Flutter
كما نعلم ان تنفيذ ملف pdf في Flutter امر مهمه خصوصا للتطبيقات التي تحتوي على عمليات حسابية بشكل كبير وفي هذه المقالة سوف نشرح معكم كيف تقوم بتنفيذ تصميم لانشاء ملف pdf مخصص في تطبيق flutter الخاص بك بطريقة بسيطة جدا والامر سهل وليس معقد حيث ستكون قادر على تطوير الpdf بسهوله من خلال تطبيق فلاتر الخاص بك وهذا سوف يكون عن طريق استخدام بعض ال packages التي تساعدنا في هذه المهمه مع تنفيذ التصميم الذي ترغب بإدراجه في التطبيق حتى يكون الامر بسيط .
وصف Flutter. تتيح مجموعة أدوات واجهة المستخدم المحمولة من Google ، Flutter ، للمطورين إنشاء تطبيقات مذهلة ومصممة محليًا من قاعدة شفرة واحدة لسطح المكتب والجوال والويب. Flutter هو برنامج مجاني ومفتوح المصدر ، ويتكامل مع الكود الحالي ، ويستخدمه المطورون والشركات في جميع أنحاء العالم.
وصف Flutter. تتيح منصة Flutter مفتوحة المصدر من Google تطوير تطبيقات سطح المكتب والجوال والتطبيقات عبر الإنترنت من قاعدة كود واحدة. Flutter ، على عكس البدائل الأخرى المحبوبة ، هي مجموعة SDK كاملة ، أو مجموعة تطوير برمجيات. 04 أغسطس 2022
قدمت Google Flutter ، وهي عبارة عن حزمة SDK للجوال مفتوحة المصدر ومتعددة المنصات تتيح للمبرمجين إنشاء تطبيقات جوال لمنصتي Android و iOS من قاعدة بيانات واحدة.
10 يونيو 2022
يتم استخدام كل من Java و Flutter لإنشاء تطبيقات عبر الأنظمة الأساسية. يُطلق على إطار العمل المحمول عبر الأنظمة الأساسية Flutter.
add packages :
path_provider: ^2.0.11
open_file: ^3.2.1
pdf: ^3.8.4
intl: ^0.17.0
Open pdf after click .
اول شيئ تحتاج اليه هو امكانية لفتح ملف الpdf بعد النقر عليه والحصول على العنوان وايضا تخزينه بداخل جهازك وكل هذا سوف يكون من خلال استخدام هذه ال methode .
pdfApi.dart
class PdfApi {
static Future<File> saveDocument({
required String name,
required Document pdf,
}) async {
final bytes = await pdf.save();
final dir = await getApplicationDocumentsDirectory();
final file = File('${dir.path}/$name');
await file.writeAsBytes(bytes);
return file;
}
static Future openFile(File file) async {
final url = file.path;
await OpenFile.open(url);
}
}
Design pdf viewer
هذا الجزء يكون عباره عن تصميم ملف ال pdf الذي يظهر بعد النقر على الزر والتحويل اليه كل هذا سوف يكون من خلال هذا الجزء البسيط والذي سوف يكون التصميم النهائي الذي يظهر ولهذا يمكنك عمل اي تصميم ترغب بعرضه للمستخدم بدون اي مشاكل من خلال التعديل على هذا الجزء .
pdf_invoice_api.dart
import 'package:pdf/widgets.dart' as pw;
class PdfInvoiceApi {
static Future<File> generate(Invoice invoice) async {
final pdf = Document();
pdf.addPage(MultiPage(
build: (context) => [
buildHeader(invoice),
SizedBox(height: 3 * PdfPageFormat.cm),
buildTitle(invoice),
buildInvoice(invoice),
Divider(),
buildTotal(invoice),
],
footer: (context) => buildFooter(invoice),
));
return PdfApi.saveDocument(name: 'my_invoice.pdf', pdf: pdf);
}
static Widget buildHeader(Invoice invoice) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 1 * PdfPageFormat.cm),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
buildSupplierAddress(invoice.supplier),
// Barcode .
// Container(
// height: 50,
// width: 50,
// child: BarcodeWidget(
// barcode: Barcode.qrCode(),
// data: invoice.info.number,
// ),
// ),
],
),
SizedBox(height: 1 * PdfPageFormat.cm),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
buildCustomerAddress(invoice.customer),
buildInvoiceInfo(invoice.info),
],
),
],
);
static Widget buildCustomerAddress(Customer customer) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(customer.name, style: TextStyle(fontWeight: FontWeight.bold)),
Text(customer.address),
],
);
static Widget buildInvoiceInfo(InvoiceInfo info) {
final paymentTerms = '${info.dueDate.difference(info.date).inDays} days';
final titles = <String>[
'Invoice Number:',
'Invoice Date:',
'Payment Terms:',
'Due Date:'
];
final data = <String>[
info.number,
Utils.formatDate(info.date),
paymentTerms,
Utils.formatDate(info.dueDate),
];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: List.generate(titles.length, (index) {
final title = titles[index];
final value = data[index];
return buildText(title: title, value: value, width: 200);
}),
);
}
static Widget buildSupplierAddress(Supplier supplier) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(supplier.name, style: TextStyle(fontWeight: FontWeight.bold)),
SizedBox(height: 1 * PdfPageFormat.mm),
Text(supplier.address),
],
);
static Widget buildTitle(Invoice invoice) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'INVOICE',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
SizedBox(height: 0.8 * PdfPageFormat.cm),
Text(invoice.info.description),
SizedBox(height: 0.8 * PdfPageFormat.cm),
],
);
static Widget buildInvoice(Invoice invoice) {
final headers = [
'Description',
'Address',
'Date',
'Quantity',
'Unit Price',
'VAT',
'Total',
];
final data = invoice.items.map((item) {
final total = item.unitPrice * item.quantity * (1 + item.vat);
return [
item.description,
'${item.address}',
Utils.formatDate(item.date),
'${item.quantity}',
'\$ ${item.unitPrice}',
'${item.vat} %',
'\$ ${total.toStringAsFixed(2)}',
];
}).toList();
return Table.fromTextArray(
headers: headers,
data: data,
border: null,
headerStyle: TextStyle(fontWeight: FontWeight.bold),
headerDecoration: BoxDecoration(color: PdfColors.grey300),
cellHeight: 30,
cellAlignments: {
0: Alignment.centerLeft,
1: Alignment.centerRight,
2: Alignment.centerRight,
3: Alignment.centerRight,
4: Alignment.centerRight,
5: Alignment.centerRight,
},
);
}
static Widget buildTotal(Invoice invoice) {
final netTotal = invoice.items
.map((item) => item.unitPrice * item.quantity)
.reduce((item1, item2) => item1 + item2);
final vatPercent = invoice.items.first.vat;
final vat = netTotal * vatPercent;
final total = netTotal + vat;
return Container(
alignment: Alignment.centerRight,
child: Row(
children: [
Spacer(flex: 6),
Expanded(
flex: 4,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildText(
title: 'Net total',
value: Utils.formatPrice(netTotal),
unite: true,
),
buildText(
title: 'Vat ${vatPercent * 100} %',
value: Utils.formatPrice(vat),
unite: true,
),
Divider(),
buildText(
title: 'Total amount due',
titleStyle: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
),
value: Utils.formatPrice(total),
unite: true,
),
SizedBox(height: 2 * PdfPageFormat.mm),
Container(height: 1, color: PdfColors.grey400),
SizedBox(height: 0.5 * PdfPageFormat.mm),
Container(height: 1, color: PdfColors.grey400),
],
),
),
],
),
);
}
static Widget buildFooter(Invoice invoice) => Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Divider(),
SizedBox(height: 2 * PdfPageFormat.mm),
buildSimpleText(title: 'Address', value: invoice.supplier.address),
SizedBox(height: 1 * PdfPageFormat.mm),
buildSimpleText(title: 'Paypal', value: invoice.supplier.paymentInfo),
],
);
static buildSimpleText({
required String title,
required String value,
}) {
final style = TextStyle(fontWeight: FontWeight.bold);
return Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: pw.CrossAxisAlignment.end,
children: [
Text(title, style: style),
SizedBox(width: 2 * PdfPageFormat.mm),
Text(value),
],
);
}
static buildText({
required String title,
required String value,
double width = double.infinity,
TextStyle? titleStyle,
bool unite = false,
}) {
final style = titleStyle ?? TextStyle(fontWeight: FontWeight.bold);
return Container(
width: width,
child: Row(
children: [
Expanded(child: Text(title, style: style)),
Text(value, style: unite ? style : null),
],
),
);
}
}
create pdf model
هذا الجزء سوف يكون عن ال model والذي يكون مسؤول عن تغيير البيانات في التطبيق للبيانات الخاصه بالمستخدم .
class Invoice {
final InvoiceInfo info;
final Supplier supplier;
final Customer customer;
final List<InvoiceItem> items;
const Invoice({
required this.info,
required this.supplier,
required this.customer,
required this.items,
});
}
class InvoiceInfo {
final String description;
final String number;
final DateTime date;
final DateTime dueDate;
const InvoiceInfo({
required this.description,
required this.number,
required this.date,
required this.dueDate,
});
}
class InvoiceItem {
final String description;
final String address;
final DateTime date;
final int quantity;
final double vat;
final double unitPrice;
const InvoiceItem({
required this.address,
required this.description,
required this.date,
required this.quantity,
required this.vat,
required this.unitPrice,
});
}
class Customer {
final String name;
final String address;
const Customer({
required this.name,
required this.address,
});
}
class Supplier {
final String name;
final String address;
final String paymentInfo;
const Supplier({
required this.name,
required this.address,
required this.paymentInfo,
});
}
How to create pdf creator in Flutter
class Utils {
static formatPrice(double price) => '\$ ${price.toStringAsFixed(2)}';
static formatDate(DateTime date) => DateFormat.yMd().format(date);
}
class PdfPage extends StatefulWidget {
@override
_PdfPageState createState() => _PdfPageState();
}
class _PdfPageState extends State<PdfPage> {
@override
Widget build(BuildContext context) =>
Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
title: const Text('title'),
centerTitle: true,
),
body: Container(
padding: const EdgeInsets.all(32),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Column(
children: const [
Icon(Icons.picture_as_pdf, size: 100, color: Colors.white),
const SizedBox(height: 16),
Text(
'Generate Invoice',
style: TextStyle(
fontSize: 42,
fontWeight: FontWeight.w400,
color: Colors.white,
),
textAlign: TextAlign.center,
),
],
),
const SizedBox(height: 48),
ElevatedButton(
style: ElevatedButton.styleFrom(
minimumSize: Size.fromHeight(40),
),
child: FittedBox(
child: Text(
'Invoice PDF',
style: TextStyle(fontSize: 20, color: Colors.white),
),
),
onPressed: createPDF,
),
],
),
),
),
);
void createPDF() async {
// today date
final date = DateTime.now();
// payment terms .
final dueDate = date.add(Duration(days: 7));
final invoice = Invoice(
// data client .
supplier: Supplier(
name: 'Sarah Field',
address: 'Sarah Street 9, Beijing, China',
paymentInfo: 'https://paypal.me/sarahfieldzz',
),
// data supervisor.
customer: Customer(
name: 'Apple Inc.',
address: 'Apple Street, Cupertino, CA 95014',
),
info: InvoiceInfo(
date: date,
dueDate: dueDate,
description: 'My description...',
number: '${DateTime.now().year}-9999',
),
items: [
// data table ..
InvoiceItem(
address: 'aaaaa',
description: 'Coffee',
date: DateTime.now(),
quantity: 3,
vat: 0.19,
unitPrice: 5.99,
),
InvoiceItem(
address: 'aaaaa',
description: 'Water',
date: DateTime.now(),
quantity: 8,
vat: 0.19,
unitPrice: 0.99,
),
InvoiceItem(
address: 'aaaaa',
description: 'Orange',
date: DateTime.now(),
quantity: 3,
vat: 0.19,
unitPrice: 2.99,
),
InvoiceItem(
address: 'aaaaa',
description: 'Apple',
date: DateTime.now(),
quantity: 8,
vat: 0.19,
unitPrice: 3.99,
),
InvoiceItem(
address: 'aaaaa',
description: 'Mango',
date: DateTime.now(),
quantity: 1,
vat: 0.19,
unitPrice: 1.59,
),
InvoiceItem(
address: 'aaaaa',
description: 'Blue Berries',
date: DateTime.now(),
quantity: 5,
vat: 0.19,
unitPrice: 0.99,
),
InvoiceItem(
address: 'aaaaa',
description: 'Lemon',
date: DateTime.now(),
quantity: 4,
vat: 0.19,
unitPrice: 1.29,
),
InvoiceItem(
address: 'aaaaa',
description: 'Lemon',
date: DateTime.now(),
quantity: 4,
vat: 0.19,
unitPrice: 1.29,
),
InvoiceItem(
address: 'aaaaa',
description: 'Lemon',
date: DateTime.now(),
quantity: 4,
vat: 0.19,
unitPrice: 1.29,
),
InvoiceItem(
address: 'aaaaa',
description: 'Lemon',
date: DateTime.now(),
quantity: 4,
vat: 0.19,
unitPrice: 1.29,
),
InvoiceItem(
address: 'aaaaa',
description: 'Lemon',
date: DateTime.now(),
quantity: 4,
vat: 0.19,
unitPrice: 1.29,
),
InvoiceItem(
address: 'aaaaa',
description: 'Lemon',
date: DateTime.now(),
quantity: 4,
vat: 0.19,
unitPrice: 1.29,
),
InvoiceItem(
address: 'aaaaa',
description: 'Lemon',
date: DateTime.now(),
quantity: 4,
vat: 0.19,
unitPrice: 1.29,
),
InvoiceItem(
address: 'aaaaa',
description: 'Lemon',
date: DateTime.now(),
quantity: 4,
vat: 0.19,
unitPrice: 1.29,
),
InvoiceItem(
address: 'aaaaa',
description: 'Lemon',
date: DateTime.now(),
quantity: 4,
vat: 0.19,
unitPrice: 1.29,
),
InvoiceItem(
address: 'aaaaa',
description: 'Lemon',
date: DateTime.now(),
quantity: 4,
vat: 0.19,
unitPrice: 1.29,
),
InvoiceItem(
address: 'aaaaa',
description: 'Lemon',
date: DateTime.now(),
quantity: 4,
vat: 0.19,
unitPrice: 1.29,
),
InvoiceItem(
address: 'aaaaa',
description: 'Lemon',
date: DateTime.now(),
quantity: 4,
vat: 0.19,
unitPrice: 1.29,
),
InvoiceItem(
address: 'aaaaa',
description: 'Lemon',
date: DateTime.now(),
quantity: 4,
vat: 0.19,
unitPrice: 1.29,
),
InvoiceItem(
address: 'aaaaa',
description: 'Lemon',
date: DateTime.now(),
quantity: 4,
vat: 0.19,
unitPrice: 1.29,
),
InvoiceItem(
description: 'Lemon',
date: DateTime.now(),
quantity: 4,
vat: 0.19,
unitPrice: 1.29,
address: 'aaaaa',
),
InvoiceItem(
description: 'Lemon',
date: DateTime.now(),
quantity: 4,
address: 'aaaaa',
vat: 0.19,
unitPrice: 1.29,
),
],
);
final pdfFile = await PdfInvoiceApi.generate(invoice);
PdfApi.openFile(pdfFile);
}
}