DEV Community

Cover image for Flutter Form Widget 💫 🌌 ✨
Gülsen Keskin
Gülsen Keskin

Posted on • Updated on

Flutter Form Widget 💫 🌌 ✨

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.

Image description
şekil:1

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:
Image description

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(...);
  }
}
Enter fullscreen mode Exit fullscreen mode

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(
        //...
Enter fullscreen mode Exit fullscreen mode

Üç 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

Image description

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;
  },
),


Enter fullscreen mode Exit fullscreen mode

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;
      },
    ),
  ),
}


Enter fullscreen mode Exit fullscreen mode

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.

Image description

Widget result = DefaultTextStyle(
       style: _textStyle,
       child: Container(
        // ...
         child: Row(
           mainAxisAlignment: MainAxisAlignment.spaceBetween,
           mainAxisSize: MainAxisSize.min,
           children: <Widget>[
             widget.isExpanded
                ? Expanded(child: innerItemsWidget)
                : innerItemsWidget,
             // ...


Enter fullscreen mode Exit fullscreen mode

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(),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

Image description

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
                  );
                },
              ),
            ],
          );
        },
      ),
  }


Enter fullscreen mode Exit fullscreen mode

Resource: Flutter in Action Chapter: 5

Top comments (0)