// lib/screens/pdf_list_screen.dart // Version: 1.2.0 (DB Integration: List Invoices from SQLite) import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import '../data/database.dart' as db; import '../main.dart'; import '../models/invoice_models.dart'; import '../models/app_model.dart' as app_model; import 'invoice_detail_page.dart'; class PdfListScreen extends StatefulWidget { const PdfListScreen({Key? key}) : super(key: key); @override State createState() => _PdfListScreenState(); } class _PdfListScreenState extends State { late Stream> _invoiceStream; @override void initState() { super.initState(); // データベースから「顧客情報付きの請求書一覧」をストリームで取得 // これにより、保存した瞬間に一覧が自動更新されます _invoiceStream = database.watchAllInvoices(); } @override Widget build(BuildContext context) { final currencyFormat = NumberFormat("#,###"); final dateFormat = DateFormat('yyyy/MM/dd'); return Scaffold( appBar: AppBar( title: const Text("請求書 履歴一覧"), backgroundColor: Colors.blueGrey, foregroundColor: Colors.white, ), body: StreamBuilder>( stream: _invoiceStream, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { return Center(child: Text("エラーが発生しました: ${snapshot.error}")); } final invoices = snapshot.data ?? []; if (invoices.isEmpty) { return const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.description_outlined, size: 64, color: Colors.grey, ), SizedBox(height: 16), Text( "保存された請求書はまだありません。", style: TextStyle(color: Colors.grey), ), ], ), ); } return ListView.separated( itemCount: invoices.length, separatorBuilder: (context, index) => const Divider(height: 1), itemBuilder: (context, index) { final item = invoices[index]; final invoice = item.invoice; final customer = item.customer; return ListTile( leading: CircleAvatar( backgroundColor: Colors.blueGrey.shade100, child: const Icon( Icons.picture_as_pdf, color: Colors.blueGrey, ), ), title: Text( customer.formalName, style: const TextStyle(fontWeight: FontWeight.bold), ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "No: ${invoice.id} | ${dateFormat.format(invoice.date)}", ), if (invoice.notes != null && invoice.notes!.isNotEmpty) Text( invoice.notes!, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle(fontSize: 12), ), ], ), trailing: Text( "¥${currencyFormat.format(invoice.totalAmount)}", style: const TextStyle( color: Colors.blueGrey, fontWeight: FontWeight.bold, fontSize: 16, ), ), onTap: () { // タップ時に Invoice モデルを再構築して詳細画面へ _navigateToDetail(item); }, ); }, ); }, ), ); } /// 詳細画面へ遷移する(DBモデルからアプリ内モデルに変換) void _navigateToDetail(db.InvoiceWithCustomer data) async { // 明細項目をDBから取得 final items = await (database.select( database.invoiceItems, )..where((t) => t.invoiceId.equals(data.invoice.id))).get(); final mappedItems = items .map( (i) => InvoiceItem( description: i.description, quantity: i.quantity, unitPrice: i.unitPrice, ), ) .toList(); final invoiceModel = Invoice( invoiceNumber: data.invoice.id, customer: app_model.Customer( id: data.customer.id, displayName: data.customer.displayName, formalName: data.customer.formalName, address: data.customer.address ?? "", department: data.customer.department ?? "", ), items: mappedItems, date: data.invoice.date, notes: data.invoice.notes, filePath: data.invoice.filePath, ); if (!mounted) return; Navigator.push( context, MaterialPageRoute( builder: (context) => InvoiceDetailPage(invoice: invoiceModel), ), ); } }