h-1.flutter.4/lib/widgets/master_edit_fields.dart
joe 13f7e3fcc6 feat: マスタ編集モジュール統合と汎用フィールド実装
- widgets ディレクトリに MasterTextField, MasterNumberField, MasterDropdownField,
  MasterTextArea, MasterCheckBox を作成
- 各マスタ画面(product, customer, employee, supplier, warehouse)で統一ウィジェット化
- pubspec.yaml: flutter_form_builder の依存を整理(Flutter の標準機能で対応可能に)
2026-03-09 22:49:39 +09:00

198 lines
No EOL
5.7 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.

// Version: 1.0 - 汎用マスタ編集フィールドFlutter 標準)
import 'package:flutter/material.dart';
/// マスタ編集用の統一 TextField
class MasterTextField extends StatelessWidget {
final String label;
final TextEditingController controller;
final String? hint;
final TextInputType keyboardType;
final bool obscureText;
final int maxLines;
final TextInputAction textInputAction;
final FormFieldValidator<String>? validator;
// TextEditingController を直接使うため、onChanged は不要。nullable の形に定義する
final void Function(String)? onChanged;
const MasterTextField({
super.key,
required this.label,
required this.controller,
this.hint,
this.keyboardType = TextInputType.text,
this.obscureText = false,
this.maxLines = 1,
this.textInputAction = TextInputAction.next,
this.validator,
this.onChanged,
});
@override
Widget build(BuildContext context) {
return TextFormField(
controller: controller,
decoration: InputDecoration(
labelText: label,
hintText: hint,
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
),
keyboardType: keyboardType,
obscureText: obscureText,
maxLines: maxLines,
textInputAction: textInputAction,
validator: (value) => onChanged?.call(value) ?? validator?.call(value),
onChanged: onChanged,
);
}
}
/// マスタ編集用の数値入力 TextField
class MasterNumberField extends StatelessWidget {
final String label;
final TextEditingController controller;
final String? hint;
final FormFieldValidator<String>? validator;
// Nullable の形で定義
final void Function(String)? onChanged;
const MasterNumberField({
super.key,
required this.label,
required this.controller,
this.hint,
this.validator,
this.onChanged,
});
@override
Widget build(BuildContext context) {
return TextFormField(
controller: controller,
decoration: InputDecoration(
labelText: label,
hintText: hint,
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
),
keyboardType: TextInputType.number,
validator: (value) => onChanged?.call(value) ?? validator?.call(value),
onChanged: onChanged,
);
}
}
/// ドロップダウンフィールド
class MasterDropdownField<T> extends StatelessWidget {
final String label;
final TextEditingController controller;
final T? initialSelectedValue;
final List<T> dataSource;
final FormFieldValidator<String>? validator;
// DropdownButtonFormField の onChanged は void Function(T)? を要求
final void Function(T)? onChanged;
const MasterDropdownField({
super.key,
required this.label,
required this.controller,
this.initialSelectedValue,
required this.dataSource,
this.validator,
this.onChanged,
});
@override
Widget build(BuildContext context) {
final items = dataSource.map((value) => DropdownMenuItem<T>(
value: value,
child: Text(value.toString()),
)).toList();
return DropdownButtonFormField<T>(
decoration: InputDecoration(
labelText: label,
hintText: '選択してください',
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
),
value: initialSelectedValue != null ? initialSelectedValue : null,
items: items,
onChanged: onChanged,
);
}
}
/// テキストエリアフィールド(長文章用)
class MasterTextArea extends StatelessWidget {
final String label;
final TextEditingController controller;
final String? hint;
final FormFieldValidator<String>? validator;
// Nullable の形で定義
final void Function(String)? onChanged;
final bool readOnly;
const MasterTextArea({
super.key,
required this.label,
required this.controller,
this.hint,
this.validator,
this.onChanged,
this.readOnly = false,
});
@override
Widget build(BuildContext context) {
return TextFormField(
controller: controller,
decoration: InputDecoration(
labelText: label,
hintText: hint,
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
),
maxLines: 4,
readOnly: readOnly,
validator: (value) => onChanged?.call(value) ?? validator?.call(value),
onChanged: onChanged,
);
}
}
/// チェックボックスフィールド(フラグ用)
class MasterCheckBox extends StatelessWidget {
final String label;
final bool initialValue;
final FormFieldValidator<bool>? validator;
// SwitchListTile の onChanged は void Function(bool)? を要求
// Validator とコールバックを分離する形に
final VoidCallback? onCheckedCallback;
const MasterCheckBox({
super.key,
required this.label,
required this.initialValue,
this.validator,
this.onCheckedCallback,
});
@override
Widget build(BuildContext context) {
return SwitchListTile(
title: Text(label),
subtitle: initialValue ? const Text('有効') : const Text('無効'),
value: initialValue,
onChanged: (value) {
if (validator?.call(value) != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(validator!.call(value) ?? ''), backgroundColor: Colors.red),
);
} else if (onCheckedCallback?.call() ?? false) {
onCheckedCallback?.call();
}
},
);
}
}