2

As per: How to shift focus to next textfield in flutter?, I used FocusScope.of(context).nextFocus() to shift focus. But this doesn't work when you use a reusable textfield class. It only works when you directly use TextField class inside Column.

import 'package:flutter/material.dart';

void main() {
  return runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final focus = FocusScope.of(context);
    return MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(
        body: SafeArea(
          child: Column(
            children: <Widget>[
              CustomTextField(
                textInputAction: TextInputAction.next,
                onEditingComplete: () => focus.nextFocus(),
              ),
              const SizedBox(height: 10),
              CustomTextField(
                textInputAction: TextInputAction.done,
                onEditingComplete: () => focus.unfocus(),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class CustomTextField extends StatelessWidget {
  final TextInputAction textInputAction;
  final VoidCallback onEditingComplete;

  const CustomTextField({
    this.textInputAction = TextInputAction.done,
    this.onEditingComplete = _onEditingComplete,
  });

  static _onEditingComplete() {}

  @override
  Widget build(BuildContext context) {
    return TextField(
      textInputAction: textInputAction,
      onEditingComplete: onEditingComplete,
    );
  }
}

In this code, if I click next in keyboard it will not shift focus to next textfield. Please help me with this.

Axel
  • 4,365
  • 11
  • 63
  • 122

3 Answers3

3

That's because the context doesn't have anything it could grab the focus from. Replace your code with this:

void main() => runApp(MaterialApp(home: MyApp()));

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final focus = FocusScope.of(context);
    return Scaffold(
      appBar: AppBar(),
      body: Column(
        children: <Widget>[
          CustomTextField(
            textInputAction: TextInputAction.next,
            onEditingComplete: () => focus.nextFocus(),
          ),
          SizedBox(height: 10),
          CustomTextField(
            textInputAction: TextInputAction.done,
            onEditingComplete: () => focus.unfocus(),
          ),
        ],
      ),
    );
  }
}
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
1

You need to wrap your fields in a form widget with a form key and use a TextFormField instead of textField widget. Set the action to TextInputAction.next and it should work! You can also use TextInput.done to trigger the validation.

Here a fully working exemple:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class LogInPage extends StatefulWidget {
  LogInPage({Key key}) : super(key: key);

  @override
  _LogInPageState createState() => _LogInPageState();
}

class _LogInPageState extends State<LogInPage> {
  final _formKey = new GlobalKey<FormState>();

  bool isLoading = false;

  String firstName;
  String lastName;
  String password;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      backgroundColor: Colors.black,
      body: body(),
    );
  }

  Widget body() {
    return Form(
      key: _formKey,
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          showInput(
              firstName,
              TextInputType.name,
              Icons.drive_file_rename_outline,
              "FirstName",
              TextInputAction.next,
              onSaved: (value) => firstName = value.trim()),
          showInput(lastName, TextInputType.name,
              Icons.drive_file_rename_outline, "LastName", TextInputAction.next,
              onSaved: (value) => lastName = value.trim()),
          showInput(null, TextInputType.text, Icons.drive_file_rename_outline,
              "Password", TextInputAction.done,
              isPassword: true, onSaved: (value) => password = value),
          Padding(
            padding: EdgeInsets.symmetric(vertical: 10),
          ),
          showSaveButton(),
        ],
      ),
    );
  }

  Widget showInput(String initialValue, TextInputType textInputType,
      IconData icon, String label, TextInputAction textInputAction,
      {@required Function onSaved, bool isPassword = false}) {
    return Padding(
      padding: EdgeInsets.fromLTRB(16.0, 20.0, 16.0, 0.0),
      child: new TextFormField(
        style: TextStyle(color: Theme.of(context).primaryColorLight),
        maxLines: 1,
        initialValue: initialValue,
        keyboardType: textInputType,
        textInputAction: textInputAction,
        autofocus: false,
        obscureText: isPassword,
        enableSuggestions: !isPassword,
        autocorrect: !isPassword,
        decoration: new InputDecoration(
            fillColor: Theme.of(context).primaryColor,
            hintText: label,
            hintStyle: TextStyle(color: Theme.of(context).primaryColorDark),
            filled: true,
            contentPadding: new EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0),
            border: new OutlineInputBorder(
              borderRadius: new BorderRadius.circular(12.0),
            ),
            icon: new Icon(
              icon,
              color: Theme.of(context).primaryColorLight,
            )),
        validator: (value) {
          return value.isEmpty && !isPassword
              ? "You didn't filled this field."
              : null;
        },
        onSaved: onSaved,
        onFieldSubmitted:
            textInputAction == TextInputAction.done ? (value) => save() : null,
      ),
    );
  }

  Widget showSaveButton() {
    return RaisedButton(
      shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.all(Radius.circular(100))),
      color: Theme.of(context).primaryColor,
      padding: EdgeInsets.symmetric(vertical: 12, horizontal: 25),
      child: isLoading
          ? SizedBox(height: 17, width: 17, child: CircularProgressIndicator())
          : Text(
              "Sauvegarder",
              style: TextStyle(color: Theme.of(context).primaryColorLight),
            ),
      onPressed: save,
    );
  }

  void save() async {
    if (_formKey.currentState.validate()) {
      _formKey.currentState.save();
      //TODO
    }
  }
}
Milvintsiss
  • 1,420
  • 1
  • 18
  • 34
0
FocusNode textSecondFocusNode = new FocusNode();

TextFormField textFirst = new TextFormField(
  onFieldSubmitted: (String value) {
    FocusScope.of(context).requestFocus(textSecondFocusNode);
  },
);

TextFormField textSecond = new TextFormField(
  focusNode: textSecondFocusNode,
);

// render textFirst and textSecond where you want