当项目已被选中时,FocusNode不会自动滚动到TextFormField- flutter

3df52oht  于 2022-12-27  发布在  Flutter
关注(0)|答案(2)|浏览(164)

场景:这是一个大表单,用户选择TextFormField,然后手动向下滚动到“保存”按钮。如果_formKey.currentState.validate()检测到验证问题,我们可以将焦点移回出现问题的TextFormField
首先,我们调用_myFocusNode.unfocus();//这是有效的,因为关键字是自动关闭的。接下来,我们调用FocusScope.of(context).requestFocus(_myFocusNode)//这也是有效的,因为我们可以在之后立即开始键入。但是,FocusScope.of(context).requestFocus不会自动滚动回TextFormField
如果我们尝试另一个TextFormField(Tfield 2)而不是最后选择的,Tfield 2获得焦点,滚动也使其可见。
注:如果我们再次开始键入,则会执行滚动,并且TextFormField变为可见。
https://github.com/flutter/flutter/issues/58877

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Material App',
      theme: ThemeData.dark(),
      home: Home(),
    );
  }
}

class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
  bool show = false;
  TextEditingController cnt1 = new TextEditingController();
  TextEditingController cnt2 = new TextEditingController();
  TextEditingController cnt3 = new TextEditingController();
  TextEditingController cnt4 = new TextEditingController();
  TextEditingController cnt5 = new TextEditingController();
  TextEditingController cnt6 = new TextEditingController();
  TextEditingController cnt7 = new TextEditingController();
  TextEditingController cnt8 = new TextEditingController();

  FocusNode _focuserr;
  FocusNode _focus1;
  FocusNode _focus2;
  FocusNode _focus3;
  FocusNode _focus4;
  FocusNode _focus5;
  FocusNode _focus6;
  FocusNode _focus7;
  FocusNode _focus8;

 @override
  void dispose() {
    // Clean up the focus node when the Form is disposed.
    _focuserr.dispose();
    _focus1.dispose();
    _focus2.dispose();
    _focus3.dispose();
    _focus4.dispose();
    _focus5.dispose();
    _focus6.dispose();
    _focus7.dispose();
    _focus8.dispose();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    _focus1 = new FocusNode();
    _focus2 = new FocusNode();
    _focus3 = new FocusNode();
    _focus4 = new FocusNode();
    _focus5 = new FocusNode();
    _focus6 = new FocusNode();
    _focus7 = new FocusNode();
    _focus8 = new FocusNode();

    return Scaffold(
      appBar: AppBar(
        title: Text('Material App Bar'),
      ),
      body: Center(
        child: Container(
          child: Text('Hello World'),
        ),
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: () {
          showModalBottomSheet(
            context: context,
            isScrollControlled: true,
            builder: (context) => StatefulBuilder(
              builder: (context, setState) {
                return Padding(
                  padding: EdgeInsets.only(left:10, right: 10, bottom: MediaQuery.of(context).viewInsets.bottom + 5),
                  child: Container(
                    height: 300,
                    child: SingleChildScrollView(
                      child: Form(
                        key: _formKey,
                        child: Column(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            createTextField(cnt1, _focus1, 1),
                            createTextField(cnt2, _focus2, 2),
                            createTextField(cnt3, _focus3, 3),
                            createTextField(cnt4, _focus4, 4),
                            createTextField(cnt5, _focus5, 5),
                            createTextField(cnt6, _focus6, 6),
                            createTextField(cnt7, _focus7, 7),
                            createTextField(cnt8, _focus8, 8),
                            RaisedButton(
                                child: Text('Validate'),
                                onPressed: () {
                                  _validateInputs(context);
                                })
                          ],
                        ),
                      ),
                    ),
                  ),
                );
              },
            ),
          );
        },
        icon: Icon(Icons.add),
        label: Text('bottomsheet'),
      ),
    );
  }

  Widget createTextField(TextEditingController c, FocusNode f, int id){
  return Padding(
      padding: const EdgeInsets.only(
          top: 5, bottom: 5, left: 10, right: 10),
      child: Container(
        color: Colors.lightBlue.withOpacity(0.3),
        child:
        TextFormField(
          controller: c,
          focusNode: f,
          validator: (val) {
            var result = isNotNull(val);
            if(result != null && _focuserr == null)
              _focuserr = f;
              
            return result;
          },
          decoration: InputDecoration(
            counterText : "",
            hintStyle: TextStyle(fontSize: 17),
            hintText: 'TXT$id',
            border: InputBorder.none,
            focusedBorder: UnderlineInputBorder(
                borderSide: BorderSide(color: Colors.blue[300], width: 0.3)),
            enabledBorder: InputBorder.none,
            errorBorder: InputBorder.none,
            disabledBorder: InputBorder.none,
            contentPadding: const EdgeInsets.only(left: 10, top: 0),
          ),
        ),
      )
    );
  }

  void _validateInputs(BuildContext cnt) {
    final form = _formKey.currentState;
    _focuserr = null;
    
    if (form.validate()) {
      form.save();
    }
    else
    {
      setState(() {
        FocusManager.instance.primaryFocus.unfocus();
        FocusScope.of(cnt).requestFocus(_focuserr);
      });
    }
  }

  String isNotNull(String val) =>
      (val.length == 0) ? 'Cannot be empty' : null;
}

9jyewag0

9jyewag01#

碰巧是一个已确认的问题,在稳定的v17以及较新的开发通道

drkbr07n

drkbr07n2#

这个问题一直存在到现在,you can followup it here,所以我临时解决了这个问题,如下所示:
1-将FocusNode定义为如下列表:

final _formKey = GlobalKey<FormState>();
  final List<Map<String, FocusNode>> _focusNodes = [];

  @override
  void initState() {
    super.initState();
    
    _focusNodes.add({'nickname' : FocusNode()});
    _focusNodes.add({'firstName' : FocusNode()});
    _focusNodes.add({'lastName' : FocusNode()});
    _focusNodes.add({'email' : FocusNode()});
    // ... to map your inputs with nodes
    
  }

  void _unFocusNodes(){
    for (var element in _focusNodes) {
      element.values.first.unfocus();
    }
  }

2-将FocusNode传递到输入,如下所示:

TextFormField(
  focusNode: _focusNodes.firstWhere((element) => element.keys.first == 'email').values.first;
)

3-在调用validate()之前调用_unFocusNodes(),如下所示(技巧如下):

setState(() {
      _unFocusNodes();
      _formKey.currentState?.validate();
    });

相关问题