Flutter自定义Widget-可以监听键盘高度的TextField

现在我们有个需求,要求输入完成后所有字母都转大写,看起来很容易,直接写个TextField,然后在onEditingComplete里处理不就行了,但是,你会发现,键盘里还有个下拉按钮,就上面那个按钮,点击它虽然键盘消失了,但根本不执行onEditingComplete,只有点击下面那个按钮才会执行,这样测试肯定不通过,那就在onChange方法里处理?也不行,因为我输入完成后还得访问后台接口,所以不能在onChange方法里处理。

背景讲完了,上代码:

1,使用扩展类extension...on...,扩展下TextField,混入class WidgetsBindingObserver来监听界面的变化,具体监听尺寸变化的方法是didChangeMetrics()

extension TextFieldExtension on TextField{

  Widget getKeyboardListenerTextField(){
    if(focusNode == null) return this;
    return KeyboardListenerTextField(child: this);
  }

}
class KeyboardListenerTextField extends StatefulWidget {
  TextField child;
  KeyboardListenerTextField({
     this.child,
  });

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

class _KeyboardListenerTextFieldState extends State<KeyboardListenerTextField> with WidgetsBindingObserver {
  //标记键盘是否现实中,默认不显示
  bool isKeyboardActived = false;
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }
  @override
  void dispose() {
    super.dispose();
    WidgetsBinding.instance.removeObserver(this);
    widget.child.focusNode.unfocus();
  }

  @override
  void didChangeMetrics() {
    super.didChangeMetrics();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      // 当前是安卓系统并且在焦点聚焦的情况下
      if (Platform.isAndroid && widget.child.focusNode.hasFocus) {
        if (isKeyboardActived) {
          isKeyboardActived = false;
          // 使输入框失去焦点
          widget.child.focusNode.unfocus();
          return;
        }
        isKeyboardActived = true;
      } else {
        isKeyboardActived = false;
      }
    });
  }
  @override
  Widget build(BuildContext context) {
    return widget.child;
  }
}

2,在使用时直接 

TextField(
  focusNode: focusNode,
  onEditingComplete: () {
    focusNode?.unfocus();
  },
  onSubmitted: (text) {
    focusNode?.unfocus();
  },
).getKeyboardListenerTextField();

3,当然,需要监听焦点

focusNode.addListener(() {
  if (!focusNode.hasFocus) {
    //失去焦点,说明输入完成,该转大写的转大写,该数据请求的数据请求
  }
});

最后:这样写也有缺点,因为如果一个界面TextField很多,如果焦点直接从这个TextField到另一个TextField,那第二个 TextField当键盘隐藏时因为键盘状态未变化,所以不会失去焦点,所以这只适合界面有一个TextField的情况。

最后的最后:如果界面就是很多TextField,那可以给整个界面混入WidgetsBindingObserver,监听整个界面的尺寸,设置监听时酱紫写:

double oldBottom = 0;
@override
void didChangeMetrics() {
  super.didChangeMetrics();
  WidgetsBinding.instance.addPostFrameCallback((_) {
    var currentBottom = MediaQuery.of(context).viewInsets.bottom;
    // 如果currentBottom为0,表示键盘消失了,这时候整个界面获取焦点,那TextField自然就失去焦点了
    // 为什么还要判断oldBottom,因为在界面没变化的时候,个别机型也会调用这个方法。。。
    // 所以必须在oldBottom不为0的时候,才获取焦点,这里坑的我好惨。。。
    if(oldBottom != 0 && currentBottom==0){
      FocusScope.of(context).requestFocus(FocusNode());
    }
    oldBottom = currentBottom;
  });
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,我可以为您讲解一下Flutter定义Tab导航的实现方法。 对于顶部导航,可以使用TabBar和TabBarView来实现。TabBar是一个水平的导航栏,TabBarView是一个可以滚动的控件,可以用来展示不同的页面内容。下面是一个简单的示例代码: ``` TabController _tabController; @override void initState() { super.initState(); _tabController = TabController(length: 2, vsync: this); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('顶部导航'), bottom: TabBar( controller: _tabController, tabs: [ Tab(text: 'Tab1'), Tab(text: 'Tab2'), ], ), ), body: TabBarView( controller: _tabController, children: [ // Tab1页面内容 Container( child: Text('Tab1'), ), // Tab2页面内容 Container( child: Text('Tab2'), ), ], ), ); } ``` 对于底部导航,可以使用BottomNavigationBar来实现。BottomNavigationBar是一个底部导航栏,可以用来切换不同的页面。下面是一个简单的示例代码: ``` int _currentIndex = 0; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('底部导航'), ), body: Center( child: Text('当前页面: $_currentIndex'), ), bottomNavigationBar: BottomNavigationBar( currentIndex: _currentIndex, onTap: (index) { setState(() { _currentIndex = index; }); }, items: [ BottomNavigationBarItem( icon: Icon(Icons.home), title: Text('首页'), ), BottomNavigationBarItem( icon: Icon(Icons.search), title: Text('搜索'), ), BottomNavigationBarItem( icon: Icon(Icons.person), title: Text('个人中心'), ), ], ), ); } ``` 对于自定义Tab导航,可以使用自定义控件来实现。比如,可以使用Row和GestureDetector来构建一个自定义的Tab导航栏。下面是一个简单的示例代码: ``` int _currentIndex = 0; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('自定义Tab导航'), ), body: Center( child: Text('当前页面: $_currentIndex'), ), bottomNavigationBar: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ GestureDetector( onTap: () { setState(() { _currentIndex = 0; }); }, child: Column( children: [ Icon(Icons.home, color: _currentIndex == 0 ? Colors.blue : Colors.grey), Text('首页', style: TextStyle(color: _currentIndex == 0 ? Colors.blue : Colors.grey)), ], ), ), GestureDetector( onTap: () { setState(() { _currentIndex = 1; }); }, child: Column( children: [ Icon(Icons.search, color: _currentIndex == 1 ? Colors.blue : Colors.grey), Text('搜索', style: TextStyle(color: _currentIndex == 1 ? Colors.blue : Colors.grey)), ], ), ), GestureDetector( onTap: () { setState(() { _currentIndex = 2; }); }, child: Column( children: [ Icon(Icons.person, color: _currentIndex == 2 ? Colors.blue : Colors.grey), Text('个人中心', style: TextStyle(color: _currentIndex == 2 ? Colors.blue : Colors.grey)), ], ), ), ], ), ); } ``` 以上是三种常见的Tab导航实现方法,您可以根据自己的需求选择合适的方式来实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值