fix: keep invoice list visible when keyboard opens
This commit is contained in:
parent
976ac6be20
commit
54135fa466
1 changed files with 148 additions and 134 deletions
|
|
@ -46,14 +46,11 @@ class _InvoiceHistoryScreenState extends State<InvoiceHistoryScreen> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _showInvoiceActions(Invoice invoice) async {
|
Future<void> _showInvoiceActions(Invoice invoice) async {
|
||||||
|
if (!_requireUnlock()) return;
|
||||||
if (invoice.isLocked) {
|
if (invoice.isLocked) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("ロック中の伝票は操作できません")));
|
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("ロック中の伝票は操作できません")));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!_isUnlocked) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("操作するにはアンロックが必要です")));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await showModalBottomSheet(
|
await showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(16))),
|
||||||
|
|
@ -93,24 +90,26 @@ class _InvoiceHistoryScreenState extends State<InvoiceHistoryScreen> {
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.edit),
|
leading: const Icon(Icons.edit),
|
||||||
title: const Text("編集"),
|
title: const Text("編集"),
|
||||||
onTap: () async {
|
onTap: _isUnlocked
|
||||||
Navigator.pop(context);
|
? () async {
|
||||||
await Navigator.push(
|
await Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => InvoiceInputForm(
|
builder: (context) => InvoiceDetailPage(
|
||||||
existingInvoice: invoice,
|
invoice: invoice,
|
||||||
onInvoiceGenerated: (inv, path) {},
|
isUnlocked: _isUnlocked, // 状態を渡す
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
_loadData();
|
_loadData();
|
||||||
},
|
}
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.delete, color: Colors.redAccent),
|
leading: const Icon(Icons.delete, color: Colors.redAccent),
|
||||||
title: const Text("削除", style: TextStyle(color: Colors.redAccent)),
|
title: const Text("削除", style: TextStyle(color: Colors.redAccent)),
|
||||||
onTap: () async {
|
onTap: _isUnlocked
|
||||||
|
? () async {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
final confirm = await showDialog<bool>(
|
final confirm = await showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -127,7 +126,8 @@ class _InvoiceHistoryScreenState extends State<InvoiceHistoryScreen> {
|
||||||
await _invoiceRepo.deleteInvoice(invoice.id);
|
await _invoiceRepo.deleteInvoice(invoice.id);
|
||||||
_loadData();
|
_loadData();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -135,6 +135,12 @@ class _InvoiceHistoryScreenState extends State<InvoiceHistoryScreen> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _requireUnlock() {
|
||||||
|
if (_isUnlocked) return true;
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("スライドでロック解除してください")));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _loadVersion() async {
|
Future<void> _loadVersion() async {
|
||||||
final packageInfo = await PackageInfo.fromPlatform();
|
final packageInfo = await PackageInfo.fromPlatform();
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|
@ -193,9 +199,10 @@ class _InvoiceHistoryScreenState extends State<InvoiceHistoryScreen> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final amountFormatter = NumberFormat("#,###");
|
final amountFormatter = NumberFormat("#,###");
|
||||||
final dateFormatter = DateFormat('yyyy/MM/dd');
|
final dateFormatter = DateFormat('yyyy/MM/dd');
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
drawer: Drawer(
|
resizeToAvoidBottomInset: false,
|
||||||
|
drawer: _isUnlocked
|
||||||
|
? Drawer(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -214,7 +221,9 @@ class _InvoiceHistoryScreenState extends State<InvoiceHistoryScreen> {
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.receipt_long),
|
leading: const Icon(Icons.receipt_long),
|
||||||
title: const Text("伝票マスター"),
|
title: const Text("伝票マスター"),
|
||||||
onTap: () => Navigator.pop(context),
|
onTap: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: const Icon(Icons.people),
|
leading: const Icon(Icons.people),
|
||||||
|
|
@ -251,7 +260,8 @@ class _InvoiceHistoryScreenState extends State<InvoiceHistoryScreen> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
|
: null,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
// leading removed
|
// leading removed
|
||||||
title: GestureDetector(
|
title: GestureDetector(
|
||||||
|
|
@ -317,7 +327,8 @@ class _InvoiceHistoryScreenState extends State<InvoiceHistoryScreen> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: Column(
|
body: SafeArea(
|
||||||
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
|
@ -342,7 +353,8 @@ class _InvoiceHistoryScreenState extends State<InvoiceHistoryScreen> {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: ListView.builder(
|
: ListView.builder(
|
||||||
padding: const EdgeInsets.only(bottom: 100), // キーボードやFAB考慮
|
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
|
||||||
|
padding: const EdgeInsets.only(bottom: 120), // 固定: FAB+安全余白
|
||||||
itemCount: _filteredInvoices.length,
|
itemCount: _filteredInvoices.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final invoice = _filteredInvoices[index];
|
final invoice = _filteredInvoices[index];
|
||||||
|
|
@ -383,23 +395,20 @@ class _InvoiceHistoryScreenState extends State<InvoiceHistoryScreen> {
|
||||||
),
|
),
|
||||||
subtitle: Text("${dateFormatter.format(invoice.date)} - ${invoice.invoiceNumber}"),
|
subtitle: Text("${dateFormatter.format(invoice.date)} - ${invoice.invoiceNumber}"),
|
||||||
trailing: SizedBox(
|
trailing: SizedBox(
|
||||||
height: 56,
|
height: 60,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
Text("¥${amountFormatter.format(invoice.totalAmount)}",
|
Text("¥${amountFormatter.format(invoice.totalAmount)}",
|
||||||
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 13)),
|
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 13)),
|
||||||
const SizedBox(height: 2),
|
|
||||||
if (invoice.isSynced)
|
if (invoice.isSynced)
|
||||||
const Icon(Icons.sync, size: 14, color: Colors.green)
|
const Icon(Icons.sync, size: 14, color: Colors.green)
|
||||||
else
|
else
|
||||||
const Icon(Icons.sync_disabled, size: 14, color: Colors.orange),
|
const Icon(Icons.sync_disabled, size: 14, color: Colors.orange),
|
||||||
const SizedBox(height: 4),
|
|
||||||
IconButton(
|
IconButton(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
constraints: const BoxConstraints.tightFor(width: 32, height: 28),
|
constraints: const BoxConstraints.tightFor(width: 32, height: 26),
|
||||||
icon: const Icon(Icons.edit, size: 18),
|
icon: const Icon(Icons.edit, size: 18),
|
||||||
tooltip: invoice.isLocked ? "ロック中" : (_isUnlocked ? "編集" : "アンロックして編集"),
|
tooltip: invoice.isLocked ? "ロック中" : (_isUnlocked ? "編集" : "アンロックして編集"),
|
||||||
onPressed: (invoice.isLocked || !_isUnlocked)
|
onPressed: (invoice.isLocked || !_isUnlocked)
|
||||||
|
|
@ -420,7 +429,8 @@ class _InvoiceHistoryScreenState extends State<InvoiceHistoryScreen> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () async {
|
onTap: _isUnlocked
|
||||||
|
? () async {
|
||||||
await Navigator.push(
|
await Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
|
|
@ -431,16 +441,19 @@ class _InvoiceHistoryScreenState extends State<InvoiceHistoryScreen> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
_loadData();
|
_loadData();
|
||||||
},
|
}
|
||||||
onLongPress: () => _showInvoiceActions(invoice),
|
: () => _requireUnlock(),
|
||||||
|
onLongPress: _isUnlocked ? () => _showInvoiceActions(invoice) : () => _requireUnlock(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
floatingActionButton: FloatingActionButton.extended(
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
onPressed: () async {
|
onPressed: _isUnlocked
|
||||||
|
? () async {
|
||||||
await Navigator.push(
|
await Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
|
|
@ -448,7 +461,8 @@ class _InvoiceHistoryScreenState extends State<InvoiceHistoryScreen> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
_loadData();
|
_loadData();
|
||||||
},
|
}
|
||||||
|
: _requireUnlock,
|
||||||
label: const Text("新規伝票作成"),
|
label: const Text("新規伝票作成"),
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
backgroundColor: Colors.indigo,
|
backgroundColor: Colors.indigo,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue