// version: 1.0.2 (Japanese Support & SHA-256 Edition) import 'dart:io'; import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart' show rootBundle; import 'package:pdf/widgets.dart' as pw; import 'package:path_provider/path_provider.dart'; import 'package:crypto/crypto.dart'; import 'package:intl/intl.dart'; void main() => runApp(const MaterialApp(home: InvoiceApp())); class InvoiceApp extends StatefulWidget { const InvoiceApp({super.key}); @override State createState() => _InvoiceAppState(); } class _InvoiceAppState extends State { final _clientController = TextEditingController(text: "現場太郎 様"); final _amountController = TextEditingController(text: "1500"); String _status = "フォントを配置してPDFを発行してください"; Future _generateInvoice() async { final pdf = pw.Document(); // 1. 日本語フォントの読み込み (assets/fonts/ に配置済み前提) // もしまだ配置してなければ、この3行をコメントアウトしてください final fontData = await rootBundle.load("assets/fonts/ipaexg.ttf"); final ttf = pw.Font.ttf(fontData); final myTheme = pw.ThemeData.withFont(base: ttf, bold: ttf); final clientName = _clientController.text; final int unitPrice = int.tryParse(_amountController.text) ?? 0; final int tax = (unitPrice * 0.1).floor(); final int total = unitPrice + tax; final dateStr = DateFormat('yyyy/MM/dd HH:mm').format(DateTime.now()); // PDFドキュメントの作成 pdf.addPage( pw.Page( theme: myTheme, // 日本語テーマ適用 build: (context) => pw.Column( crossAxisAlignment: pw.CrossAxisAlignment.start, // 'cross' を 'crossAxisAlignment' に修正 children: [ pw.Text("請求書 (Invoice)", style: pw.TextStyle(fontSize: 30)), pw.Divider(), pw.Text("宛名: $clientName"), pw.Text("日付: $dateStr"), pw.SizedBox(height: 20), pw.Text("単価(税抜): $unitPrice 円"), pw.Text("消費税 (10%): $tax 円"), pw.Text( "合計金額(税込): $total 円", style: pw.TextStyle(fontWeight: pw.FontWeight.bold, fontSize: 20), ), pw.SizedBox(height: 40), pw.Text("※本書類はシステムにより自動生成され、改ざん防止ハッシュが付与されています。"), ], ), ), ); final Uint8List bytes = await pdf.save(); // 2. SHA-256ハッシュ計算 final hash = sha256.convert(bytes).toString().substring(0, 10); final fileName = "${DateFormat('yyyyMMdd').format(DateTime.now())}_${total}円_hash_$hash.pdf"; // 3. 保存 final directory = await getExternalStorageDirectory(); final file = File("${directory!.path}/$fileName"); await file.writeAsBytes(bytes); setState(() { _status = "【発行成功】\nファイル: $fileName\nハッシュ: $hash"; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("現場のじぇみエモン請求書 V1.0")), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ TextField( controller: _clientController, decoration: const InputDecoration(labelText: "取引先名"), ), TextField( controller: _amountController, decoration: const InputDecoration(labelText: "単価(10%抜き)"), ), const SizedBox(height: 20), ElevatedButton( onPressed: _generateInvoice, style: ElevatedButton.styleFrom( minimumSize: const Size(double.infinity, 50), ), child: const Text("日本語PDF発行(ハッシュ付与)"), ), const SizedBox(height: 20), SelectableText( _status, style: const TextStyle( color: Colors.blue, fontWeight: FontWeight.bold, ), ), ], ), ), ); } }