- widgets ディレクトリに MasterTextField, MasterNumberField, MasterDropdownField, MasterTextArea, MasterCheckBox を作成 - 各マスタ画面(product, customer, employee, supplier, warehouse)で統一ウィジェット化 - pubspec.yaml: flutter_form_builder の依存を整理(Flutter の標準機能で対応可能に)
198 lines
No EOL
5.7 KiB
Dart
198 lines
No EOL
5.7 KiB
Dart
// 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();
|
||
}
|
||
},
|
||
);
|
||
}
|
||
} |