feat: 売上入力画面 basic UI 構築完了\n\n- Dashboard・合計表示エリア(ダッシュボード)\n- 商品検索 TextField の改善\n- ExpansionTile 内リスト構造のリファクタ\n- 保存ダイアログ機能の改修

This commit is contained in:
joe 2026-03-08 01:51:07 +09:00
parent c1f065c1ab
commit d87205effa

View file

@ -1,4 +1,4 @@
// Version: 1.0.0
// Version: 1.0.1 - Sprint 4-M2
import 'package:flutter/material.dart';
/// Material Design
@ -12,85 +12,182 @@ class SalesScreen extends StatelessWidget {
title: const Text('売上入力'),
actions: [
IconButton(
icon: const Icon(Icons.receipt_long),
icon: const Icon(Icons.save),
onPressed: () => _showSaveDialog(context),
),
],
),
body: ListView(
padding: const EdgeInsets.all(16),
body: Column(
children: [
//
Card(
margin: EdgeInsets.zero,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
//
Text(
'合計¥0',
style: const TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
color: Colors.black87,
//
Padding(
padding: const EdgeInsets.all(16),
child: Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
//
Text(
'レジモード',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 16),
const SizedBox(height: 8),
// /
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('税別¥0'),
Text('税込¥0', style: const TextStyle(fontWeight: FontWeight.bold)),
],
),
],
//
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'合計',
style: TextStyle(
fontSize: 18,
color: Colors.grey.shade600,
),
),
const Icon(Icons.payments, size: 32),
],
),
const SizedBox(height: 4),
//
Text(
'¥0',
style: const TextStyle(
fontSize: 48,
fontWeight: FontWeight.bold,
color: Colors.teal,
),
),
const SizedBox(height: 16),
// /
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('税別', style: TextStyle(fontSize: 14)),
Text('¥0'),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text('税込', style: TextStyle(fontWeight: FontWeight.bold)),
Text('¥0', style: const TextStyle(fontWeight: FontWeight.bold)),
],
),
],
),
],
),
),
),
),
const SizedBox(height: 16),
//
TextField(
decoration: const InputDecoration(
labelText: '商品検索',
hintText: 'JAN コードまたは商品名を入力',
prefixIcon: Icon(Icons.search),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: TextField(
decoration: InputDecoration(
labelText: '商品検索',
hintText: 'JAN コードまたは商品名を入力して選択',
prefixIcon: const Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
filled: true,
),
),
onChanged: (value) { /* TODO: 商品検索 */ },
),
const SizedBox(height: 8),
const SizedBox(height: 16),
//
Card(
margin: EdgeInsets.zero,
child: ExpansionTile(
title: const Text('売上商品'),
//
Expanded(
child: ListView(
children: [
ListView.builder(
shrinkWrap: true,
padding: EdgeInsets.zero,
itemCount: 0, //
itemBuilder: (context, index) => Card(
margin: const EdgeInsets.symmetric(vertical: 4),
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.orange.shade100,
child: Icon(Icons.store, color: Colors.orange),
),
title: Text('商品${index + 1}'),
subtitle: Text('数量0 pcs / 単価¥0'),
Card(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
child: ExpansionTile(
title: Text(
'📦 売上商品',
style: TextStyle(fontWeight: FontWeight.bold),
),
subtitle: const Text('商品を登録'),
children: [
ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: 0, // TODO:
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Card(
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.orange.shade100,
child: Icon(Icons.store, color: Colors.orange),
),
title: Text('商品${index + 1}'),
subtitle: const Row(
children: [
SizedBox(width: 8),
Icon(Icons.remove_circle_outline),
SizedBox(width: 4),
Text('数量0 pcs / 単価¥0'),
],
),
),
),
);
},
),
],
),
),
],
),
),
const SizedBox(height: 16),
],
),
);
}
/// TODO: DatabaseHelper.insertSales
void _showSaveDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('売上データを保存'),
content: const Text(
'入力した商品情報を販売アシストに保存します。\n\n DatabaseHelper.insertSales を呼び出す予定です。',
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('キャンセル'),
),
ElevatedButton(
onPressed: () {
// TODO: DatabaseHelper.insertSales(context)
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('売上データ保存処理中...'),
duration: Duration(seconds: 2),
),
);
Navigator.pop(context);
},
child: const Text('保存'),
),
],
),
);