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'; import 'package:flutter/material.dart';
/// Material Design /// Material Design
@ -12,29 +12,55 @@ class SalesScreen extends StatelessWidget {
title: const Text('売上入力'), title: const Text('売上入力'),
actions: [ actions: [
IconButton( IconButton(
icon: const Icon(Icons.receipt_long), icon: const Icon(Icons.save),
onPressed: () => _showSaveDialog(context), onPressed: () => _showSaveDialog(context),
), ),
], ],
), ),
body: ListView( body: Column(
padding: const EdgeInsets.all(16),
children: [ children: [
// //
Card( Padding(
margin: EdgeInsets.zero, padding: const EdgeInsets.all(16),
child: Card(
elevation: 4,
child: Padding( child: Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
// //
Text( Text(
'合計¥0', 'レジモード',
style: const TextStyle( style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontSize: 32,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Colors.black87, ),
),
const SizedBox(height: 8),
//
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), const SizedBox(height: 16),
@ -43,54 +69,125 @@ class SalesScreen extends StatelessWidget {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text('税別¥0'), Column(
Text('税込¥0', style: const TextStyle(fontWeight: FontWeight.bold)), 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( Padding(
decoration: const InputDecoration( padding: const EdgeInsets.symmetric(horizontal: 16),
child: TextField(
decoration: InputDecoration(
labelText: '商品検索', labelText: '商品検索',
hintText: 'JAN コードまたは商品名を入力', hintText: 'JAN コードまたは商品名を入力して選択',
prefixIcon: Icon(Icons.search), prefixIcon: const Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
), ),
onChanged: (value) { /* TODO: 商品検索 */ }, filled: true,
), ),
),
),
const SizedBox(height: 8),
const SizedBox(height: 16), //
Expanded(
// child: ListView(
children: [
Card( Card(
margin: EdgeInsets.zero, margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
child: ExpansionTile( child: ExpansionTile(
title: const Text('売上商品'), title: Text(
'📦 売上商品',
style: TextStyle(fontWeight: FontWeight.bold),
),
subtitle: const Text('商品を登録'),
children: [ children: [
ListView.builder( ListView.builder(
shrinkWrap: true, shrinkWrap: true,
padding: EdgeInsets.zero, physics: const NeverScrollableScrollPhysics(),
itemCount: 0, // itemCount: 0, // TODO:
itemBuilder: (context, index) => Card( itemBuilder: (context, index) {
margin: const EdgeInsets.symmetric(vertical: 4), return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Card(
child: ListTile( child: ListTile(
leading: CircleAvatar( leading: CircleAvatar(
backgroundColor: Colors.orange.shade100, backgroundColor: Colors.orange.shade100,
child: Icon(Icons.store, color: Colors.orange), child: Icon(Icons.store, color: Colors.orange),
), ),
title: Text('商品${index + 1}'), title: Text('商品${index + 1}'),
subtitle: Text('数量0 pcs / 単価¥0'), 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('保存'),
),
], ],
), ),
); );