Form widget'ı
Form
widget'ı, bazı kullanışlı methodlar sağlayan ve alt ağacındaki tüm form field widget
'larıyla bütünleşen bir tür sarmalayıcıdır.
Formdaki tüm field'ların (alanların) state'ini yönetir ve her field için ayrı ayrı state işleme ihtiyacını ortadan kaldırır.
Ayrıntılara fazla dalmadan önce, şekil 1'deki hava durumu uygulamasındaki üst düzey örneğe bir göz atın.
Form ile etkileşim kurmanın yolu ona FormState
türünde bir anahtar (key) iletmektir. Widget bu global key
'i formun state nesnesiyle ilişkilendirerek state nesnesine her yerden erişmenizi sağlar. (Global anahtarların kullanılmasının önerildiği tek durum budur. anahtarlar hakkında daha fazla bilgi için bkz)
Tüm widget'lar gibi Form
da ilişkili bir Element
tarafından yönetilir. Bu elementin, Flutter'da dahili (internally) olarak oluşturulan bir FormState
nesnesine referansı vardır. Bu state nesnesi, herhangi bir StatefulWidget
için oluşturulan State
sınıfının bir örneğidir, ancak daha fazla işlevsellik ile genişletilmiştir. Aradaki fark, global key'i oluşturduğunuzda bu state nesnesinin de dahili olarak oluşturulmasıdır. Pratikte bu, bu anahtar vasıtasıyla formunuzun tamamına erişebileceğiniz anlamına gelir.
Formun state'i element tree'ye kaydedilir:
GlobalKey
Bir form key
kullanmak (FormState alt türünün global anahtarı), bir widget'ta controller
kullanmaya benzer. Örneğin, önceki bölümde TabController'a bakmıştık. FormState
, form mantığını korumak için bir dizi kullanışlı method sağlayan yerleşik bir sınıftır. Kullanmak isteyeceğiniz FormState methodlarından bazıları FormState.save
, FormState.reset
ve FormState.validate
'dir.
Formlarla çalışırken, FormState
nesnesine başvuru sağlayabilen anahtarların kullanılması yaygındır. FormState nesnesi üzerindeki tüm mantık ve özelliklere, oluşturacağınız bu anahtar vasıtasıyla erişilebilirsiniz. Bu da formunuzda'ki tüm widget'larla, çocuklar da dahil olmak üzere etkileşim kurabileceğiniz anlamına gelir.
Not: Bu, global anahtar kullanmanın kabul edilebilir olduğu çok az yerden biridir. bkz.
import ...
class AddNewCityPage extends StatefulWidget {
final AppSettings settings;
const AddNewCityPage({Key key, this.settings}) : super(key: key);
@override
_AddNewCityPageState createState() => _AddNewCityPageState();
}
class _AddNewCityPageState extends State<AddNewCityPage> {
City _newCity = City.fromUserInput();
bool _formChanged = false;
bool _isDefaultFlag = false;
FocusNode focusNode;
final GlobalKey<FormState> _formKey =
GlobalKey<FormState>(); //formun mevcut durumu için kullanılır
@override
void initState() {...}
@override
void dispose() {...}
bool validateTextFields() {...}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(...),
body: Padding(
padding: ...
child: Form(
key: _formKey,
onChanged: _onFormChange,
onWillPop: _onWillPop,
child: Column(
children: <Widget>[
Padding(
padding: ...
child: TextFormField(...),
),
Padding(
padding: ...
child: TextFormField(...),
),
CountryDropdownField(...),
FormField(...),
Divider(...),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: ...
child: FlatButton(...),
),
Padding(
padding: ...
child: RaisedButton(
color: Colors.blue[400],
child: Text("Submit"),
onPressed: _formChanged
? () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
_handleAddNewCity();
Navigator.pop(context);
} else {
FocusScope.of(context)
.requestFocus(focusNode);
}
}
: null,
),
)
// ... many more closing brackets
);
}
void _onFormChange() {...}
void _handleAddNewCity() {...}
Future<bool> _onWillPop() {
if (!_formChanged) return Future<bool>.value(true);
return showDialog<bool>(
context: context,
builder: (BuildContext context) {
return AlertDialog(...);
}
}
FormField widget'ları
Formun girdileri yönetebilmesi için FormField widget'ları gerekir. Herhangi bir input widget, yalnızca text input widget'larına değil, bir form field'ına da sarılabilir.
Örneğin, formunuzda bir Checkbox
kullanabilirsiniz, ancak bunun bir form alanına sarılması gerekir:
return FormField(
child: Checkbox(
//...
Üç FormField
widget'ı vardır:
1.) FormField
: Herhangi bir input widget'ını bir form field'ına dönüştürebilen standart alandır.
2.) TextFormField
: Text field'ı saran özel bir form alanı.
3.) DropdownButtonFormField
: DropdownButton
öğesini form field'ına saran widget
1.) TextFormField Widget
// weather_app/lib/page/add_city_page.dart -- line ~77
Padding(
padding: const EdgeInsets.symetric(verical: 8.0),
child: TextFormField( //TextFormField, TextField ve FormField'in birleşimidir ve aynı argümanların çoğunu alır.
onSaved: (String val) => _newCity.name = val,
decoration: InputDecoration(
border: OutlineInputBorder(),
helperText: "Required",
labelText: "City name",
),
autofocus: true,
autoValidate: true,
validator: (String val) {
if (val.isEmpty) return "Field cannot be left blank";
return null;
},
),
TextFormField'ın özellikleri: validator
, autoValidate
, onSaved
validator
, callback bekleyen tüm form field'larındaki bir argümandır. Text form field söz konusu olduğunda, callback, bu alanın girişinden bir String
olarak iletilir. Bu callback'den ne döndürülürse, field'a error text olarak eklenir. Hiçbir şey veya null döndürmezse, form field herhangi bir error text göstermez. Kullanıcının form field'larındaki girişini, form feld'nın tüm validator calback'lerini döndüren ve çağıran FormState.validate() öğesini çağırarak doğrularsınız.
autoValidate
, true
olarak ayarlandığında, form alanı değiştiğinde validator callback'i hemen çağırır. Bu yöntem kullanıcıya anında geri bildirim sağlar.
onSaved
, validator ile aynı şekilde çalışır. Callback ilettiğiniz bir argümandır ve bu callback, FormState.save() çağrıldığında yürütülür.
Widget get _titleField {
// ...
child: TextFormField(
onSaved: (String val) => _newCity.name = val,
decoration: InputDecoration(
// ...
autofocus: true,
autoValidate: true,
validator: (String val) {
if (val.isEmpty)
return "Field cannot be left blank";
return null;
},
),
),
}
2.) DropdownFormButton Widget'ı
DropdownFormButton
, FormField
widget'ının başka bir uzantısıdır. Görüntülediği veriler ve kullanıcının nasıl seçim yaptığı dışında, TextFormField
gibi çalışır.
Widget result = DefaultTextStyle(
style: _textStyle,
child: Container(
// ...
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
widget.isExpanded
? Expanded(child: innerItemsWidget)
: innerItemsWidget,
// ...
isExpanded'ı geçmenin fark yarattığı yer burasıdır. Telefonun genişliği çok küçükse, çocukları Expanded'a sarmak önemlidir, çünkü bu olmadan widget olabildiğince büyük olmaya çalışır.
class CountryDropdownField extends StatelessWidget {
final Function onChanged;
final Country country;
const CountryDropdownField({
Key key,
this.onChanged,
this.country,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: DropDownExpanded<Country>(
isExpanded: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: "Country",
),
value: country ?? Country.AD, //value dropdown kapatıldığında görüntülenmesi gereken seçili değerdir.
onChanged: (Country newSelection)
=> onChanged(newSelection), //onChanged, yeni bir seçim yapıldığında çağrılır. Flutter'ın dropdown field widget'larında, yeni seçim onChanged'ın callback'ine iletilir.
items: Country.ALL.map((Country country) {
return DropdownMenuItem(
value: country,
child: Text(country.name),
);
}).toList(),
),
);
}
}
Generic form fields
Check box, date picker veya slider gibi başka bir input türü kullanmak istiyorsanız, herhangi bir witget'ı FormField widget'ına sarabilirsiniz.
class _AddNewCityPageState extends State<AddNewCityPage> {
City _newCity = City.fromUserInput();
bool _formChanged = false;
bool _isDefaultFlag = false; //check box'ın state'ini yönetmek için kullanılan boolean değişken
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
FocusNode focusNode;
Widget build(BuildContext context) {
return Scaffold(
FormField(
onSaved: (val) => _newCity.active =
_isDefaultFlag,
builder: (context) { //FormField, bir çocuk yerine bir builder alır. İstediğiniz widget'ı return edebilirsiniz. Herhangi bir widget teknik olarak bir form field olabilir.
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text("Default city?"),
Checkbox(
value: _isDefaultFlag, //Bu check box, formun dışındaki herhangi bir check box gibi davranır. Seçili olup olmadığı bilgisi için boolean bir değer alır.
onChanged: (val) {
setState(
() => _isDefaultFlag = val //_isDefaultFlag değerini değiştirmek için setState'i çağırmalısınız
);
},
),
],
);
},
),
}
Resource: Flutter in Action Chapter: 5
Top comments (0)