下一篇:Flutter几行代码处理点击空白关闭键盘及添加键盘ToolBar
前言
在原生App开放中有个常见的功能,那就是当点击输入框时键盘弹出,同时输入框上移到键盘上方,同时点击非输入框的空白部分时,键盘自动关闭,同时在键盘的上方有一个ToolBar,可以点击上一个、下一个切换输入框
1、解决点击非输入框的地方关闭键盘
解决思路:将Scaffold
的body设置成GestureDetector
,点击空白处时就将焦点响应设置成自定义的FocusNode
import 'package:flutter/material.dart';
class LoginPage3 extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return LoginPage3State();
}
}
class LoginPage3State extends State<LoginPage3>{
// 响应空白处的焦点的Node
FocusNode blankNode = FocusNode();
TextEditingController nameController = TextEditingController();
TextEditingController pwdController = TextEditingController();
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('登录'),),
body: GestureDetector(
onTap: (){
// 点击空白页面关闭键盘
FocusScope.of(context).requestFocus(blankNode);
},
child: createBody(),
),
);
}
Widget createBody(){
return ListView(
padding: EdgeInsets.only(left: 20,right: 20),
children: <Widget>[
SizedBox(height: 30),
createInputText(nameController,hint: '请输入用户名',icon: Icons.people),
SizedBox(height: 30),
createInputText(pwdController,hint: '请输入密码',icon: Icons.power,obscureText:true),
SizedBox(height: 30),
FlatButton(color: Colors.blue,child: Text('登录'),onPressed: checkLogin,)
],
);
}
// 创建输入行
Widget createInputText(TextEditingController controller,{obscureText: false,String hint,IconData icon}){
// 输入框
TextField textField = TextField(
controller: controller,
keyboardType: TextInputType.text,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(10.0),
hintText: hint,
),
obscureText: obscureText,
);
List<Widget> rowList = [];
// 输入框前的提示图标
rowList.add(SizedBox(width: 10));
rowList.add(Icon(icon));
// 输入框
rowList.add(Expanded(child: textField));
return Row(children: rowList);
}
// 点击登录处理
void checkLogin(){
print(nameController.text);
print(pwdController.text);
}
}
2、解决在键盘弹出时一起出现一个ToolBar
在上一个键盘的基础上添加一个toolbar,原理是使用给每个输入框都绑定一个FocusNode
,同时使用Stack
,Column
,Flexible
,根据是否有处于焦点的node动态设置是否显示toolbar
import 'dart:math' as math;
import 'package:flutter/material.dart';
class LoginPage4 extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return LoginPage4State();
}
}
class LoginPage4State extends State<LoginPage4>{
// 响应空白处的焦点的Node
FocusNode blankNode = FocusNode();
Map<String,dynamic> nodeMap = {};
TextEditingController nameController = TextEditingController();
TextEditingController pwdController = TextEditingController();
@override
void initState() {
super.initState();
}
@override
void dispose(){
for(Map map in nodeMap.values){
FocusNode focusNode = map['node'];
focusNode.removeListener(focusNodeListener);
}
super.dispose();
}
Future<Null> focusNodeListener() async {
// 用async的方式实现这个listener
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('登录'),),
body: Stack(
children: <Widget>[
Positioned(top: 0,left: 0,bottom: 0,right: 0,
child: GestureDetector(
onTap: blankClicked,
child: createBody(),
),
),
Positioned(top: 0,left: 0,bottom: 0,right: 0,
child: Column(
children: <Widget>[
Flexible(child: Container()),
createToolBar()
],
),
),
],
)
);
}
// 点击了空白的地方
void blankClicked(){
// 点击空白页面关闭键盘
FocusScope.of(context).requestFocus(blankNode);
}
Map currentEditingFocusNode(){
for(Map map in nodeMap.values){
FocusNode focusNode = map['node'];
if(focusNode.hasFocus){
return map;
}
}
return null;
}
void focusNodeAtIndex(int selectIndex){
for(Map map in nodeMap.values){
int index = map['index'];
if(selectIndex == index){
FocusNode focusNode = map['node'];
focusNode.requestFocus();
return;
}
}
}
Widget createToolBar(){
Map map = currentEditingFocusNode();
if(map == null){
return Container(height: 0);
}
int currentIndex = map['index'];
bool isFirst = currentIndex==0;
bool isLast = currentIndex==(nodeMap.length-1);
Widget preBtn = Transform(
transform: Matrix4.identity()..rotateZ(math.pi),// 旋转的角度
origin: Offset(10,10),
child: Icon(Icons.arrow_forward_ios,
color: isFirst?Colors.grey:Colors.blue,size: 20.0,)
);
Widget nextBtn = Icon(Icons.arrow_forward_ios,
color:isLast?Colors.grey:Colors.blue,size: 20,);
return Container(
height: 40,
color: Color(0xffeeeeee),
padding: EdgeInsets.only(left: 10,right: 10),
child: Row(
children: <Widget>[
InkWell(
child: preBtn,onTap: (){
// 点击上一个
if(!isFirst){
focusNodeAtIndex(currentIndex-1);
}
},),
SizedBox(width: 40,),
InkWell(
child: nextBtn,onTap: (){
// 点击下一个
if(!isLast){
focusNodeAtIndex(currentIndex+1);
}
},),
Flexible(child: Container(),),
InkWell(child: Text('关闭',style: TextStyle(color: Colors.blue,)),onTap: blankClicked),
],
),
);
}
// 创建展示内容
Widget createBody(){
return ListView(
padding: EdgeInsets.only(left: 20,right: 20),
children: <Widget>[
SizedBox(height: 30),
createInputText(nameController,hint: '请输入用户名',icon: Icons.people),
SizedBox(height: 30),
createInputText(pwdController,hint: '请输入密码',icon: Icons.power,obscureText:true),
SizedBox(height: 30),
FlatButton(color: Colors.blue,child: Text('登录'),onPressed: checkLogin,)
],
);
}
// 创建输入行
Widget createInputText(TextEditingController controller,{obscureText: false,String hint,IconData icon}){
// controller的唯一code
String key = controller.hashCode.toString();
Map map = nodeMap[key];
FocusNode focusNode;
if(map == null){
focusNode = FocusNode();
map = {};
map['node'] = focusNode;
map['index'] = nodeMap.length;
focusNode.addListener(focusNodeListener);
nodeMap[key] = map;
}else{
focusNode = map['node'];
}
// 输入框
TextField textField = TextField(
controller: controller,
keyboardType: TextInputType.text,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(10.0),
hintText: hint,
),
obscureText: obscureText,
focusNode: focusNode,
keyboardAppearance:Brightness.light
);
List<Widget> rowList = [];
// 输入框前的提示图标
rowList.add(SizedBox(width: 10));
rowList.add(Icon(icon));
// 输入框
rowList.add(Expanded(child: textField));
return Row(children: rowList);
}
// 点击登录处理
void checkLogin(){
print(nameController.text);
print(pwdController.text);
}
}