hanbai1/lib/screens/pdf_list_screen.dart
2026-02-09 09:06:36 +09:00

168 lines
5.4 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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<PdfListScreen> createState() => _PdfListScreenState();
}
class _PdfListScreenState extends State<PdfListScreen> {
late Stream<List<db.InvoiceWithCustomer>> _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<List<db.InvoiceWithCustomer>>(
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),
),
);
}
}