[Mobile]/[Flutter]
[Flutter] Form 빌더 동적 생성하기
HiSmith
2023. 8. 26. 18:29
반응형
FormBuilder는 폼을 관리하기 위해 편하게 제공해주는 모듈이다.
특정 액션시 Form의요소를 동적으로 변경해보자
우선 동적 요소 추가를 해주기 위한, 위젯이다.
Expanded(
child: MaterialButton(
color: Theme.of(context).colorScheme.secondary,
child: const Text(
"Add Score",
style: TextStyle(color: Colors.white),
),
onPressed: () {
setState(() {
fields.add(
NewScore(
name: 'score_${fields.length}',
));
});
},
),
),
해당 버튼을 클릭하면, fields에 NewScore라는 위젯이 추가가 된다.
그리고 삭제를 하면? Widget List에서 마지막 원소를 빼준다.
Expanded(
child: MaterialButton(
color: Theme.of(context).colorScheme.secondary,
child: const Text(
"Dec Score",
style: TextStyle(color: Colors.white),
),
onPressed: () {
setState(() {
fields.removeAt(fields.length - 1);
});
},
),
),
위는 remove시키는 모듈이다.
무튼 이렇게 하고, FormBuilder안에서 감싸주면, 해당 위젯에 대한 정보가 Json stringify 되서 저장된다.
class NewScore extends StatelessWidget {
const NewScore({
super.key,
required this.name,
});
final String name;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Row(
children: [
Expanded(
child: FormBuilderTextField(
name: name,
validator: FormBuilderValidators.numeric(errorText: 'score is number'),
decoration: const InputDecoration(
label: Text('Score'),
),
),
),
],
),
);
}
}
생성되는 newScore는 이렇다.
해당 객체가 그냥 단순히 반복되는 것이다.
뭐 사실 데모용으로 시현을 위해 필요한 부분이라, 디자인이나 별도 신경은 쓰지않았다.
단순히 위젯 추가 삭제 관리, Form으로 동적 대응이 전부이다.
아래는 전체 소스이다.
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:form_builder_validators/form_builder_validators.dart';
class CreatePopup extends StatefulWidget {
@override
State<CreatePopup> createState() => _CreatePopupState();
}
class _CreatePopupState extends State<CreatePopup> {
final _formKey = GlobalKey<FormBuilderState>();
final List<Widget> fields = [];
String savedValue = '';
var subjectList = ['수학', '영어', '체육'];
@override
void initState() {
savedValue = _formKey.currentState?.value.toString() ?? '';
super.initState();
}
@override
Widget build(BuildContext context) {
return FormBuilder(
key: _formKey,
// IMPORTANT to remove all references from dynamic field when delete
clearValueOnUnregister: true,
child: Column(
children: <Widget>[
const SizedBox(height: 20,),
FormBuilderDropdown<String>(
name: 'subject',
decoration: InputDecoration(
labelText: "subject"
),
items: subjectList.map((subject) => DropdownMenuItem(
alignment: AlignmentDirectional.center,
value: subject,
child: Text(subject),
)).toList(),
valueTransformer: (val) => val?.toString(),
),
FormBuilderTextField(
name: 'small-subject',
validator: FormBuilderValidators.required(errorText: 'small-subject is required!!'),
decoration: const InputDecoration(
label: Text('small-subject'),
),
),
...fields,
const SizedBox(height: 10),
Row(
children: <Widget>[
const SizedBox(width: 20),
Expanded(
child: MaterialButton(
color: Theme.of(context).colorScheme.secondary,
child: const Text(
"Add Score",
style: TextStyle(color: Colors.white),
),
onPressed: () {
setState(() {
fields.add(
NewScore(
name: 'score_${fields.length}',
));
});
},
),
),
Expanded(
child: MaterialButton(
color: Theme.of(context).colorScheme.secondary,
child: const Text(
"Dec Score",
style: TextStyle(color: Colors.white),
),
onPressed: () {
setState(() {
fields.removeAt(fields.length - 1);
});
},
),
),
],
),
MaterialButton(
color: Theme.of(context).colorScheme.secondary,
height: 30,
child: const Text(
"Save",
style: TextStyle(color: Colors.white),
),
onPressed: () {
_formKey.currentState!.saveAndValidate();
setState(() {
savedValue =
_formKey.currentState?.value.toString() ?? '';
print(savedValue.toString());
});
},
),
// const Divider(height: 20),
// Text('Saved value: $savedValue'),
],
),
);
}
}
class NewScore extends StatelessWidget {
const NewScore({
super.key,
required this.name,
});
final String name;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Row(
children: [
Expanded(
child: FormBuilderTextField(
name: name,
validator: FormBuilderValidators.numeric(errorText: 'score is number'),
decoration: const InputDecoration(
label: Text('Score'),
),
),
),
],
),
);
}
}
반응형