Flutter — Как установить focusNode из родительского виджета в дочерний?

#dart #flutter

#dart #flutter

Вопрос:

У меня есть нижняя навигация в родительском виджете и несколько текстовых полей в дочернем виджете. Когда пользователь нажимает на вкладку навигации, и если одно из текстовых полей пусто, он установит фокус на определенные текстовые поля.

Я использую метод конструктора, которому научился у одного из разработчиков, однако я не смог заставить его работать. Похоже, я неправильно передал контекст. Я не уверен.

Кто-нибудь может заметить мои ошибки или посоветовать другие методы, которые могут достичь того же результата?

login.dart

 class Login extends StatefulWidget{

  @override
  State<StatefulWidget> createState() {
    return _LoginState();
  }
}

class _LoginState extends State<Login> {

  FocusNode focusNode;
  Page1 focus;

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

  int currentBottomNavIndex = 0;

  List<Widget> bottomNav = [
    Page1(),
    Page2(),
  ];

  onTapped(int index) {

    //if(textfield not empty) {
      //setState(() {
      //currentBottomNavIndex = index;
      //});
    //}else {
      focus.setFocus(context);
    //}
  }

  @override
  Widget build(BuildContext context){

          return new Scaffold(
            appBar: new AppBar(
              title: Text('Login Page'),
            ),

            body: bottomNav[currentBottomNavIndex],

            bottomNavigationBar: BottomNavigationBar(
              onTap: onTapped,
              //onTap: requestFocus(context),
              currentIndex: currentBottomNavIndex,
              type: BottomNavigationBarType.fixed,
              items: [
                BottomNavigationBarItem(
                  icon: Icon(Icons.home),
                  title: Text("Page1"),
                ),
                BottomNavigationBarItem(
                  icon: Icon(Icons.mail),
                  title: Text('Page2'),
                ),
              ],
            ),
          );
  }
}
  

страница 1.dart

 class Page1 extends StatefulWidget {

  final FocusNode focusNode;

  const Page1({Key key, this.focusNode}) : super(key: key);

  void setFocus(BuildContext context) {
    print("$focusNode requestFocus...");
    FocusScope.of(context).requestFocus(focusNode);
  }

  @override
  State<StatefulWidget> createState() {
    return _Page1State();
  }
}

class _Page1State extends State<Page1> {

  TextEditingController name1 = TextEditingController();

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {


    return new Scaffold(

        body: SingleChildScrollView(
            child: Column(

              children: <Widget>[
                nameApp(),
              ],
            )
        )
    );

  }

  Widget nameApp(){
    return Container(

        margin: EdgeInsets.all(50.0),
        //width: 185,
        child: Center(

            child: Row(

                children: [

                  Container(
                    child: Text("Name :", style: TextStyle(fontSize: 15), ),
                  ),

                  Container(

                    child: Flexible(

                      child: TextField(

                        focusNode: widget.focusNode,
                        controller: name1,
                        onTap: (){
                          name1.clear();
                        },
                        onChanged: (String str){

                        },
                        textAlign: TextAlign.center,
                        decoration: InputDecoration(
                          contentPadding: EdgeInsets.symmetric(vertical: 5),
                          hintText: "Full Name",
                          hintStyle: TextStyle(fontSize: 14),
                        ),
                      ),
                    ),
                  ),
                ]

            )
        )

    );
  }
}
  

Когда пользователь нажимает на нижнюю вкладку, я ожидаю увидеть, что текстовое поле находится в фокусе, однако ничего не происходит.

Я заметил, что метод в дочернем виджете был вызван:

флаттер: запрос фокусного узла #419f4 на фокусировку … флаттер: запрос фокусного узла #419f4 (СФОКУСИРОВАННЫЙ) на фокусировку…

однако текстовое поле по-прежнему не в фокусе.

Комментарии:

1. Я попробовал «распечатать (context.hashcode)» для родительского и дочернего виджетов. Разные значения. Есть ли способ вызвать дочерний контекст из родительского виджета?

2. Я решил проблему, я неправильно передал параметр дочернему виджету. Поместите приведенный ниже код в разделе Сборка виджета в родительском виджете и отредактируйте список параметров страницы 1<Виджет> bottomNav = [ Страница 1(focusNode: focusNode), Страница 2(), ];

Ответ №1:

Я создал простой пример проекта для этого, и у меня он работает просто отлично. Пожалуйста, ознакомьтесь с моим решением:

Домашняя страница:

 import 'package:flutter/material.dart';
import 'package:focus_node/widgets/MyInputWidget.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
 // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
     title: 'Flutter Demo',
     theme: ThemeData(
       primarySwatch: Colors.blue,
     ),
     home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
   }
 }

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

   final String title;

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

class _MyHomePageState extends State<MyHomePage> {

 FocusNode field1FocusNode = FocusNode(); //Create first FocusNode
 FocusNode field2FocusNode = FocusNode(); //Create second FocusNode

 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(

    title: Text(widget.title),
  ),
  body: Center(
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      mainAxisSize: MainAxisSize.min,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: <Widget>[
        Padding(
          padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 35),
          child: MyInputWidget(
            focusNode: field1FocusNode, //Provide the first FocusNode in the constructor
            hint: "Email",
            onEditCompleted: (){
              FocusScope.of(context).requestFocus(field2FocusNode); //Request focus
            },
          ),
        ),
        Padding(
          padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 35),
          child: MyInputWidget(
            focusNode: field2FocusNode, //Provide the second FocusNode 
            hint: "Password",
            onEditCompleted: (){
              FocusScope.of(context).requestFocus(field1FocusNode); //Request focus
            },
          ),
        )
      ],
    ),
   ),
  );
 }
}
  

Пользовательский виджет требовал фокусировки:

 class MyInputWidget extends StatefulWidget {
  final FocusNode focusNode;
  final String hint;
  final VoidCallback onEditCompleted;

  MyInputWidget({this.focusNode, this.hint, this.onEditCompleted});

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

 class _MyInputWidgetState extends State<MyInputWidget> {
    @override
    Widget build(BuildContext context) {
      return Container(
            padding: EdgeInsets.all(8),
            child: TextField(
            focusNode: widget.focusNode, //The FocusNode provided by the parent widget
            decoration: InputDecoration(
            hintText: widget.hint
           ),
          onEditingComplete: widget.onEditCompleted,
         ),
        );
       }
      }
  

Надеюсь, это поможет.

Комментарии:

1. Привет, Моти, после просмотра твоего кода я понимаю, что неправильно передал параметры. Я решил проблему, разместив приведенный ниже код в разделе Сборка виджета в родительском виджете и отредактировав список параметров страницы 1<Виджет> bottomNav = [Страница 1 (focusNode: focusNode), страница 2(), ]; Спасибо за помощь!!

2. Правильно! пропустил это .. Ура! Однако в следующий раз добавьте аннотацию @required к полям конструктора, это избавит вас от забывания параметров!