Moti Bartov
Moti Bartov

Reputation: 3592

Flutter Widget Focus issue

I've create a custom widget for time picking.

The widget contains an icon which opens a TimePicker, and a TextFormField so the user can type the time value manually.

This is how it looks like: enter image description here

When the user types a value it immediately validated and also when the focus is off, it validate the value and update the field with correct time format.

For example, if the user types 8 and then clicks next widget, it will update to 8:00

Checkout the form image with 2 TimePickers: enter image description here

What I want is that when user types StartTime, and then clicks the keyboard Next button, the focus will move to the EndTime picker. Then when the user clicks Next button on the EndTime Picker the focus will move to the next widget in the form

The problem is that the FocusNode is inside the TimePicker custom widget (which is StateFull) and I couldn't figure out how it can be exposed outside of it.

You can see the code for it here: https://github.com/moti0375/tikal_time_tracker/blob/develop/lib/ui/time_picker.dart

Any idea will be appreciated.

Upvotes: 2

Views: 10964

Answers (2)

guvanch amanov
guvanch amanov

Reputation: 41

FocusScope.of(context).unfocus()

Upvotes: 3

Moti Bartov
Moti Bartov

Reputation: 3592

Finally I've figured it out,

Instead of creating the FocusNode inside the picker widget (a child widget), I've created the FocusNode in the parent widget (the form) and provide it to the child widget in its constructor, by this the focus node created in the parent widget context.

Then, I've added a request focus method to the child widgets so the parent can call them and FocusScope.of(context).requestFocus(focusNode); is called inside the child widgets but on the focusNode that provided by the parent widget.

Here is a portion of the code:

Child widget:

class TimeTrackerTimePicker extends StatefulWidget {
final FocusNode focusNode;

 TimeTrackerTimePicker({ this.focusNode});

 //This can be called from the parent widget with the parent context
 void requestFocus(BuildContext  context){
   print("${this.pickerName} requestFocus...");
   FocusScope.of(context).requestFocus(focusNode);
 }
 ....
 ....
  @override
   State<StatefulWidget> createState() {
   return TimePickerState();
   }
  }

State class:

class TimePickerState extends State<TimeTrackerTimePicker> {

 @override
 Widget build(BuildContext context) {
   return Container(
    ....
     child: new Flexible(
            child: new TextFormField(
                textInputAction: TextInputAction.next,
                focusNode: widget.focusNode, //linking to the focusNode
                onFieldSubmitted: onSubmitButtonClicked,
                decoration: InputDecoration(
                    hintText: widget.hint != null ? widget.hint : "",
                    contentPadding:
                    EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0),
                    border: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(20.0))),
                maxLines: 1,
                controller: pickerController))
   )
 }
}

Then in the parent widget when you need to set focus:

FocusNode focusNode;
TimeTrackerTimePicker timePicker;

@override
void initState() {
 super.initState();
 focusNode = new FocusNode();
 timePicker = new TimeTrackerTimePicker(focusNode: focusNode); 
}

.....
//request focus when required..
void requestPickerFocus(){
    timePicker.requestFocus(context);
}

Upvotes: 7

Related Questions