- widgets ディレクトリに MasterTextField, MasterNumberField, MasterDropdownField, MasterTextArea, MasterCheckBox を作成 - 各マスタ画面(product, customer, employee, supplier, warehouse)で統一ウィジェット化 - pubspec.yaml: flutter_form_builder の依存を整理(Flutter の標準機能で対応可能に)
127 lines
No EOL
5.8 KiB
Dart
127 lines
No EOL
5.8 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'screens/estimate_screen.dart';
|
|
import 'screens/invoice_screen.dart';
|
|
import 'screens/order_screen.dart';
|
|
import 'screens/sales_return_screen.dart';
|
|
import 'screens/sales_screen.dart';
|
|
import 'screens/master/product_master_screen.dart';
|
|
import 'screens/master/customer_master_screen.dart';
|
|
import 'screens/master/supplier_master_screen.dart';
|
|
import 'screens/master/warehouse_master_screen.dart';
|
|
import 'screens/master/employee_master_screen.dart';
|
|
import 'screens/master/inventory_master_screen.dart';
|
|
|
|
void main() {
|
|
runApp(const MyApp());
|
|
}
|
|
|
|
class MyApp extends StatelessWidget {
|
|
const MyApp({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return MaterialApp(
|
|
title: 'H-1Q',
|
|
debugShowCheckedModeBanner: false,
|
|
theme: ThemeData(useMaterial3: true),
|
|
home: const Dashboard(),
|
|
routes: {
|
|
'/M1. 商品マスタ': (context) => const ProductMasterScreen(),
|
|
'/M2. 得意先マスタ': (context) => const CustomerMasterScreen(),
|
|
'/M3. 仕入先マスタ': (context) => const SupplierMasterScreen(),
|
|
'/M4. 倉庫マスタ': (context) => const WarehouseMasterScreen(),
|
|
'/M5. 担当者マスタ': (context) => const EmployeeMasterScreen(),
|
|
'/S1. 見積入力': (context) => const EstimateScreen(),
|
|
'/S2. 請求書発行': (context) => const InvoiceScreen(),
|
|
'/S3. 発注入力': (context) => const OrderScreen(),
|
|
'/S4. 売上入力(レジ)': (context) => const SalesScreen(),
|
|
'/S5. 売上返品入力': (context) => const SalesReturnScreen(),
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
class Dashboard extends StatefulWidget {
|
|
const Dashboard({super.key});
|
|
|
|
@override
|
|
State<Dashboard> createState() => _DashboardState();
|
|
}
|
|
|
|
class _DashboardState extends State<Dashboard> {
|
|
// カテゴリ展開状態管理
|
|
bool _masterExpanded = true;
|
|
|
|
final Color _headerColor = Colors.blue.shade50;
|
|
final Color _iconColor = Colors.blue.shade700;
|
|
final Color _accentColor = Colors.teal.shade400;
|
|
|
|
/// カテゴリヘッダー部品
|
|
Widget get _header {
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
|
color: _headerColor,
|
|
child: Row(
|
|
children: [
|
|
Icon(Icons.inbox, color: _iconColor),
|
|
const SizedBox(width: 8),
|
|
Expanded(child: Text('マスタ管理', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16))),
|
|
AnimatedSwitcher(
|
|
duration: const Duration(milliseconds: 200),
|
|
transitionBuilder: (Widget child, Animation<double> animation) {
|
|
return ScaleTransition(
|
|
scale: Tween(begin: 0.8, end: 1.0).animate(CurvedAnimation(parent: animation, curve: Curves.easeInOut)),
|
|
child: FadeTransition(opacity: animation, child: child),
|
|
);
|
|
},
|
|
child: IconButton(
|
|
key: ValueKey('master'),
|
|
icon: Icon(_masterExpanded ? Icons.keyboard_arrow_down : Icons.keyboard_arrow_up),
|
|
padding: EdgeInsets.zero,
|
|
constraints: const BoxConstraints(),
|
|
onPressed: () => setState(() => _masterExpanded = !_masterExpanded),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// コンテンツ部品(展開時のみ)
|
|
Widget? get _masterContent {
|
|
if (!_masterExpanded) return null;
|
|
return Container(
|
|
color: Colors.white,
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(top: 1, bottom: 8),
|
|
child: ListView.builder(
|
|
shrinkWrap: true,
|
|
physics: NeverScrollableScrollPhysics(),
|
|
itemCount: 6,
|
|
itemBuilder: (context, index) {
|
|
switch (index) {
|
|
case 0: return Card(margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), child: ListTile(leading: Icon(Icons.store, color: _accentColor), title: Text('M1. 商品マスタ'), subtitle: Text('実装済み'), onTap: () => Navigator.pushNamed(context, '/M1. 商品マスタ')));
|
|
case 1: return Card(margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), child: ListTile(leading: Icon(Icons.person, color: _accentColor), title: Text('M2. 得意先マスタ'), subtitle: Text('実装済み'), onTap: () => Navigator.pushNamed(context, '/M2. 得意先マスタ')));
|
|
case 2: return Card(margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), child: ListTile(leading: Icon(Icons.card_membership, color: _accentColor), title: Text('M3. 仕入先マスタ'), subtitle: Text('実装済み'), onTap: () => Navigator.pushNamed(context, '/M3. 仕入先マスタ')));
|
|
case 3: return Card(margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), child: ListTile(leading: Icon(Icons.storage, color: _accentColor), title: Text('M4. 倉庫マスタ'), subtitle: Text('実装済み'), onTap: () => Navigator.pushNamed(context, '/M4. 倉庫マスタ')));
|
|
case 4: return Card(margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), child: ListTile(leading: Icon(Icons.badge, color: _accentColor), title: Text('M5. 担当者マスタ'), subtitle: Text('実装済み'), onTap: () => Navigator.pushNamed(context, '/M5. 担当者マスタ')));
|
|
case 5: return Card(margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), child: ListTile(leading: Icon(Icons.inventory_2, color: _accentColor), title: Text('M6. 在庫管理'), subtitle: Text('実装済み'), onTap: () => Navigator.pushNamed(context, '/M6. 在庫管理')));
|
|
default: return const SizedBox();
|
|
}
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(title: const Text('H-1Q')),
|
|
body: ListView(
|
|
padding: EdgeInsets.zero,
|
|
children: [_header, _masterContent ?? const SizedBox.shrink()],
|
|
),
|
|
);
|
|
}
|
|
} |