Form UI oluşturma ve node'larla çalışma
FocusNode class'ı hangi alanlara programlı olarak odaklanacağını yönetmek için kullanılır.
InputDecoration
Tüm input ve form field'ları, InputDecoration iletilen decoration adlı bir arguman alır. Bu, Flutter'da yaygın bir kalıptır.
Örneğin, Container widget'ının bir BoxDecoration iletilen decoration adlı bir argümanı vardır.
InputDecoration sınıfı, form alanınıza stil vermek için kullanabileceğiniz birçok argumanı (bağımsız değişkeni) kabul eder.
Arka plan rengini ayarlayabilir, alanın odak olup olmadığına göre renkleri değiştirebilir, alanın şeklini değiştirebilir, hem input hem de yardımcı etiketlerdeki tüm metnin stilini ve daha fazlasını yapabilirsiniz.
Aşağıdaki kodda input alanına odaklanıldığında label text yukarıya doğru hareket eder.
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextFormField(
focusNode: focusNode,
onSaved: (String val) => print(val),
decoration: InputDecoration(
border: OutlineInputBorder(),
helperText: "Optional",//bir validation error olmadığı sürece helper text her zaman görüntülenir. Hata olması durumunda, hata helper text'in yerini alır.
labelText: "State or Territory name", //label text her xaman görüntülenir.
),
validator: (String val) {
if (val.isEmpty) {
return "Field cannot be left blank";
}
return null;
},
),
),
Kullanıcı Arayüzünü FocusNodes ile İyileştirme
TextFormField(
onSaved: (String val) => _newCity.name = val,
decoration: InputDecoration(
border: OutlineInputBorder(),
helperText: "Required",
labelText: "City name",
),
autofocus: true, //sayfa oluşturulur oluşturulmaz bu form field'ına odaklanılmasını sağlar
autovalidate: _formChanged,
validator: (String val) {
if (val.isEmpty) return "Field cannot be left blank";
return null;
},
),
autofocus'un yanı sıra, FocusNode adlı bir nesneye sahip bir formda odağı programlı olarak hareket ettirebilirsiniz. Uygulamanızda, odağı harici bir olaya (external event) veya bir validation error'a göre değiştirmek isteyebilirsiniz. Örneğin, bir kullanıcı uygulamanıza kaydoluyor olabilir ve kayıt formunda yanlışlıkla gerekli bir alanı boş bırakabilir. İyi bir kullanıcı deneyimi, bu boş alana otomatik olarak odaklanmayı sağlar.
Hava durumu uygulamasında form, iki text field boşsa kullanıcının formu göndermesine izin vermeyecek şekilde ayarlanır. Özellikle, kullanıcı Submit'e basarsa ve State alanı boşsa, validation error gösterilir ve odak (focus) bu text field'a verilir.
Başlamak için bir focus node oluşturmanız gerekir. Yani bir FocusNode nesnesi tanımlıyor ve oluşturuyoruz. Focus node'ları (odak düğümleri) uzun ömürlü nesneler olduğundan, yaşam döngülerini bir State objesi kullanarak yönetirsiniz. Widget'ların yaşam döngüleri, widget'ın kendisiyle uyum içinde uzun ömürlü nesneler oluşturmasını veya yok etmesini sağlar.
Aşağıda gösterildiği gibi initState methodu içinde FocusNode örneği(instance) oluşturun.
class _AddNewCityPageState extends State<AddNewCityPage> {
City _newCity = City.fromUserInput();
bool _formChanged = false;
bool _isDefaultFlag = false;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
FocusNode focusNode; //focusNode'u tanımlar
@override
void initState() {
super.initState();
focusNode = FocusNode(); //Yaşam döngüsünü yönetmek ve daha sonra atılmasını sağlayabilmek için initState methodunda FocusNode() oluşturun.
}
Bir text field'a bir focus node iletilir.
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextFormField(
focusNode: focusNode,
onSaved: (String val) => print(val),
decoration: InputDecoration(
border: OutlineInputBorder(),
helperText: "Optional",
labelText: "State or Territory name",
),
validator: (String val) {
if (val.isEmpty) { //değer boşsa bir validation error verir.
return "Field cannot be left blank";
}
return null;
},
),
),
// weather_app/lib/page/add_city_page.dart -- line ~80
RaisedButton(
color: Colors.blue[400],
child: Text("Submit"),
onPressed: _formChanged
? () {
if (_formKey.currentState.validate()) { //_formKey.currentState.validate (), validator callback'leri başarısız olursa false döndürür.
_formKey.currentState.save();
_handleAddNewCity();
Navigator.pop(context);
} else {
FocusScope.of(context)
.requestFocus(focusNode); //FocusScope, odağı uygun düğümlere geçirmeyi yöneten widgettır.
}
}
: null,
),
Form methodlarıyla dormun state'ini yönetme
class _AddNewCityPageState extends State<AddNewCityPage> {
City _newCity = City.fromUserInput();
bool _formChanged = false;
bool _isDefaultFlag = false; //Bu değişken, form ilk kez güncellenir güncellenmez true olarak değiştirilir. Kullanıcının gerçekten formu kullanmaya çalışıp çalışmadığına bağlı olarak formun nasıl davrandığını işlemek için kullanabiliriz.
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
FocusNode focusNode;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
//...
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Form(
key: _formKey,
onChanged: _onFormChange, //Herhangi bir form alanı değiştirildiğinde çağrılır
onWillPop: _onWillPop, //Kullanıcı sayfadan ayrılacağı zaman çağrılır.
child: Column(
// ... form fields
),
),
),
);
Form.onChange
onChanged ve onWillPop, formun beklediği yalnızca iki methoddur. İlk olarak, hava durumu uygulamasında onChanged'ın nasıl kullanıldığına bir göz atın:
void _onFormChange() {
if (_formChanged) return;
setState(() {
_formChanged = true;
});
}
TextFormField(
onSaved: (String val) => _newCity.name = val,
decoration: InputDecoration(
border: OutlineInputBorder(),
helperText: "Required",
labelText: "City name",
),
autofocus: true,
autovalidate: _formChanged, //Kullanıcı formla etkileşime girene kadar otomatik olarak doğrulanmaz
validator: (String val) {
if (val.isEmpty) return "Field cannot be left blank";
return null;
},
),
Kullanıcının formu değiştirmediyse bir şey gönderebilmesi için hiçbir neden yoktur, bu nedenle butonu programlı olarak devre dışı bırakabiliriz:
RaisedButton(
color: Colors.blue[400],
child: Text("Submit"),
onPressed: _formChanged // formChanged false ise, callback, butonu devre dışı bırakmalı ve null olmalıdır.
? () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
_handleAddNewCity();
Navigator.pop(context);
} else {
FocusScope.of(context).requestFocus(focusNode);
}
}
: null,
),
FormState.save
Formların en önemli kısmı elbette verileri göndermektir. Form widget'ı, bu işlemi FormState.save methoduyla tamamlar.
// weather_app/lib/page/add_city_page.dart -- line ~80
RaisedButton(
color: Colors.blue[400],
child: Text("Submit"),
onPressed: _formChanged
? () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save(); //FormState.save'i çağıran yöntem. Anahtarlar sadece widget'lara referanstır. Widget state bilgisine sahipse(stateful), state'ine Key.currentState aracılığıyla erişebilirsiniz. Bu durumda, bu FormState'e bir başvurudur.
_handleAddNewCity();
Navigator.pop(context); //formu gönserdikten sonra kapatır
} else {
FocusScope.of(context).requestFocus(focusNode);
}
}
: null,
),
_formKey.currentState.save() methodu forma, uygulamanın pwidget ağacının bu bölümündeki tüm form field'larını bulmasını ve onSaved'i aramasını söyler.
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(...),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Form(
key: _formKey,
onChanged: _onFormChange,
onWillPop: _onWillPop,
child: ListView(
shrinkWrap: true,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
// Form field -- line ~42
child: TextFormField(
onSaved: (String val) =>
_newCity.name = val,
// ...
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextFormField(
focusNode: focusNode,
onSaved: (String val) =>
print(val),
// ...
FormField(
onSaved: (val) =>
_newCity.active = _isDefaultFlag,
builder: (context) {
// ...
Form.onWillPop
Kullanıcı formdan çıkmak istediğinde uygulamanın ne yapması gerektiğini belirleyebilmeyi sağlar ve kullanıcı herhangi bir nedenle bir form sayfasından çıkmak üzereyken size bir fonksiyonu yürütme şansını verir.
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Form(
key: _formKey,
onChanged: _onFormChange,
onWillPop: _onWillPop, //kullanıcı formdan ayrılırken çağrılır
child: Column(...)
),
);
Future<bool> _onWillPop() { //geriye bool bir değer döndürür
if (!_formChanged)
return Future<bool>.value(true); //kullanıcı formu değiştirmediğinde kaybolacak hiçbir bilgi olmadığı için form kapatılır
return showDialog<bool>( // kullanıcıya formu kapatıp kapatmak istemediğini soran bilr AlertDialog gösterilir
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: Text(
"Are you sure you want to abandon
the form? Any changes will be lost."
),
actions: <Widget>[
FlatButton(
child: Text("Cancel"),
onPressed: () =>
Navigator.pop(context, false),
textColor: Colors.black,
),
FlatButton(
child: Text("Abandon"),
textColor: Colors.red,
onPressed: () =>
Navigator.pop(context, true),
),
],
);
});
}
Not: Herhangi bir route, pop methodu aracılığıyla bir önceki route'a bir değer iletebilir.
Form.onWillPop methodu form tarafından çağrıldığında bir bool değer bekler ve bu değer false olursa form kapatılmaz, değer true olursa form kapatılır ve bir önceki sayfaya dönülür.
Özet
• Flutter'da kullanıcı etkileşimi iki tür widget aracılığıyla gerçekleştirilir: input ve gesture detector.
• Flutter, GestureDetectorwidget'ı aracılığıyla hareketleri ve kullanıcı etkileşimi olaylarını yönetir.
• Herhangi bir text field'a (metin alanına) programlı olarak odaklanmak istiyorsanız, bir FocusNodewidget kullanmanız ve bunun için focus talep etmeniz gerekir.
• Bir gesture detector, çeşitli callback'ler aracılığıyla birçok hareketi dinleyebilir. Bunlar yaklaşık 30'dan sadece 5'idir:
☼ onTap
☼ onLongPress
☼ onDoubleTap
☼ onVerticalDragDown
☼ onPanDown
• Yerleşik widget'lar şu hareketleri de dinler: Dismissible, Button, FormFieldve daha fazlası.
• Flutter formları, karmaşık formları yönetmeyi kolaylaştıran çeşitli input widget'larının etrafındaki kullanışlı sarmalayıcılardır.
• Bir formun state'i, bir FormState nesnesine referans olan GlobalKey<FormState> ile yönetilebilir.
• Form alanları birkaç yöntem sağlar: onChange, onSave, ve validator'dır. Bu yöntemler, kullanıcı eylemlerine yanıt vermek ve FormState'e bağlanmak için kullanılır.
Formların iki önemli yöntemi vardır: onChange ve onWillPop.
Resource: Flutter in Action Chapter: 5



Top comments (0)