Last updated:
0 purchases
katana form
Katana Form
[GitHub] | [YouTube] | [Packages] | [Twitter] | [Threads] | [LinkedIn] | [mathru.net]
Introduction #
Form implementation is a very important part of the application.
It has become an indispensable interface that allows users to enter their information into the application.
Simplifying the implementation of Forms can be very helpful in increasing the speed and safety of application implementation.
Flutter provides FormField-type widgets such as Form and TextFormField.
However, it does not address data handling, and data acquisition and storage require implementation for each state management system.
Also, although it is possible to change the design with InputDecoration, I would like to simplify it and use it like ButtonStyle because there are many setting items.
For this reason, I have created the following package.
Enables input/output using a form by storing values for use in the form in the FormController and passing them to the FormController.
Unify design specifications by making FormStyle available to all form widgets. Enables easy unification of design.
It can be easily written as follows.
final form = FormController(<String, dynamic>{});
return Scaffold(
appBar: AppBar(title: const Text("App Demo")),
body: ListView(
padding: const EdgeInsets.symmetric(vertical: 32),
children: [
const FormLabel("Name"),
FormTextField(
form: form,
initialValue: form.value["name"],
onSaved: (value) => {...form.value, "name": value},
),
const FormLabel("Description"),
FormTextField(
form: form,
minLines: 5,
initialValue: form.value["description"],
onSaved: (value) => {...form.value, "description": value},
),
FormButton(
"Submit",
icon: Icon(Icons.check),
onPressed: () {
final value = form.validate(); // Validate and get form values
if (value == null) {
return;
}
print(value);
// Save value as is.
},
),
]
)
);
copied to clipboard
This package can also be used with freezed to write code more safely.
Installation #
Import the following packages
flutter pub add katana_form
copied to clipboard
Implementation #
Create a Controller #
First, define the FormController with initial values.
For new data creation, pass an empty object; for existing data, insert values read from the database.
This example is for a case where Map<String, dynamic> is used to handle data for a database.
// New data
final form = FormController(<String, dynamic>{});
// Existing data
final Map<String, dynamic> data = getRepositoryData();
final form = FormController(data);
copied to clipboard
This is maintained by a state management mechanism such as StatefulWidget.
Since FormController inherits from ChangeNotifier, it can be used in conjunction with riverpod's ChangeNotifierProvider, etc.
Form Implementation #
Form widget installation is not required.
All you have to do is pass the FormController you created, and if you pass a FormController, you must also pass onSaved. (If you want to use only onChanged, you do not need to pass FormController.)
Pass the initial value to initialValue. When passing an initial value, pass the value obtained from FormController.value as is.
onSaved is passed the currently entered value as a callback, so be sure to return the changed FormController.value value as is.
FormTextField(
form: form,
initialValue: form.value["description"],
onSaved: (value) => {...form.value, "description": value},
),
copied to clipboard
Form Validation and Storage #
You can validate and save a form by executing FormController.validate.
Validation is performed first, and null is returned in case of failure.
If it succeeds, it returns the value changed by onSaved of each Form widget.
Update the database based on that value.
final value = form.validate(); // Validate and get form values
if (value == null) {
return;
}
print(value);
// Save value as is.
copied to clipboard
Sample code #
The above sequence of events can be written in summary as follows
When destroying a form page, FormController should also be disposed of by disposing of it together with the form page.
class FormPage extends StatefulWidget {
const FormPage({super.key});
@override
State<StatefulWidget> createState() => FormPageState();
}
class FormPageState extends State<FormPage> {
final form = FormController(<String, dynamic>{});
@override
void dispose() {
super.dispose();
form.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("App Demo")),
body: ListView(
padding: const EdgeInsets.symmetric(vertical: 32),
children: [
const FormLabel("Name"),
FormTextField(
form: form,
initialValue: form.value["name"],
onSaved: (value) => {...form.value, "name": value},
),
const FormLabel("Description"),
FormTextField(
form: form,
minLines: 5,
initialValue: form.value["description"],
onSaved: (value) => {...form.value, "description": value},
),
const SizedBox(height: 16),
FormButton(
"Submit",
icon: Icon(Icons.add),
onPressed: () {
final value = form.validate(); // Validate and get form values
if (value == null) {
return;
}
print(value);
// Save value as is.
},
),
],
),
);
}
}
copied to clipboard
Freezed allows you to write code more safely.
@freezed
class FormValue with _$FormValue {
const factory FormValue({
String? name,
String? description,
}) = _FormValue;
}
class FormPage extends StatefulWidget {
const FormPage({super.key});
@override
State<StatefulWidget> createState() => FormPageState();
}
class FormPageState extends State<FormPage> {
final form = FormController(FormValue());
@override
void dispose() {
super.dispose();
form.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text("App Demo")),
body: ListView(
padding: const EdgeInsets.symmetric(vertical: 32),
children: [
const FormLabel("Name"),
FormTextField(
form: form,
initialValue: form.value.name,
onSaved: (value) => form.value.copyWith(name: value),
),
const FormLabel("Description"),
FormTextField(
form: form,
minLines: 5,
initialValue: form.value.description,
onSaved: (value) => form.value.copyWith(description: value),
),
const SizedBox(height: 16),
FormButton(
"Submit",
icon: Icon(Icons.add),
onPressed: () {
final value = form.validate(); // Validate and get form values
if (value == null) {
return;
}
print(value);
// Save value as is.
},
),
],
),
);
}
}
copied to clipboard
Style Change #
The style of each FormWidget can be changed together with FormStyle.
The default style is plain, but if you specify the following, the style will be changed to the Material design with a border.
FormTextField(
form: form,
initialValue: form.value["name"],
onSaved: (value) => {...form.value, "name": value},
style: FormStyle(
border: OutlineInputBorder(),
padding: const EdgeInsets.symmetric(horizontal: 16),
contentPadding: const EdgeInsets.all(16)),
),
copied to clipboard
Type of FormWidget #
Currently available FormWidget are
Adding as needed.
FormTextField
Field for entering text
FormDateTimeField
Field to select and enter the date and time in the Flutter dialog
FormDateField
Field to select the date (month and day) from a choice
FormNumField
Field to select a numerical value from a list of choices.
FormEnumField
Field to select from the Enum definition.
FormMapField
Field where you pass a Map and choose from its options.
The widgets that assist Form are as follows.
FormLabel
The label portion of the form is displayed separately. It also serves as a Divider.
FormButton
Used for confirm and cancel buttons for forms.
FormStyle is available.
GitHub Sponsors #
Sponsors are always welcome. Thank you for your support!
https://github.com/sponsors/mathrunet
For personal and professional use. You cannot resell or redistribute these repositories in their original state.
There are no reviews.