Flutter如何自定义应用程序内键盘
导读:本文共8845.5字符,通常情况下阅读需要29分钟。同时您也可以点击右侧朗读,来听本文内容。按键盘←(左) →(右) 方向键可以翻页。
摘要: 效果:创建关键小部件Flutter的优点是,通过组合更简单的小部件,可以轻松构建键盘等复杂布局。首先,您将创建几个简单的按键小部件。文本键我已经圈出了由您首先制作的TextKey小部件制作的键。显示文本键(包括空格键)的自定义写意红色圆圈将以下TextKey小部件添加到您的项目中:classTextKeyextendsStatelessWidget{const... ...
目录
(为您整理了一些要点),点击可以直达。效果:
创建关键小部件
Flutter的优点是,通过组合更简单的小部件,可以轻松构建键盘等复杂布局。首先,您将创建几个简单的按键小部件。
文本键
我已经圈出了由您首先制作的TextKey
小部件制作的键。
显示文本键(包括空格键)的自定义写意红色圆圈
将以下TextKey
小部件添加到您的项目中:
classTextKeyextendsStatelessWidget{constTextKey({Keykey,@requiredthis.text,this.onTextInput,this.flex=1,}):super(key:key);finalStringtext;finalValueSetter<String>onTextInput;finalintflex;@overrideWidgetbuild(BuildContextcontext){returnExpanded(flex:flex,child:Padding(padding:constEdgeInsets.all(1.0),child:Material(color:Colors.blue.shade300,child:InkWell(onTap:(){onTextInput?.call(text);},child:Container(child:Center(child:Text(text)),),),),),);}}
以下是有趣的部分:
flex
属性允许您的按键均匀分布在一行之间,甚至占据行的更大比例(如上图中的空格键)。按下按键后,它将以anonTextInput回调的形式将其值传递给键盘。
Backspace键
您还需要一个与TextKey
小部件具有不同外观和功能的退格键。
退格键
将以下小部件添加到您的项目中:
classBackspaceKeyextendsStatelessWidget{constBackspaceKey({Key?key,this.onBackspace,this.flex=1,}):super(key:key);finalVoidCallback?onBackspace;finalintflex;@overrideWidgetbuild(BuildContextcontext){returnExpanded(flex:flex,child:Padding(padding:constEdgeInsets.all(1.0),child:Material(color:Colors.blue.shade300,child:InkWell(onTap:(){onBackspace?.call();},child:Container(child:Center(child:Icon(Icons.backspace),),),),),),);}
备注:
TextKey
代码有点重复,因此一些重构是为了使其更加简介。onBackspace
是VoidCallback
,因为不需要将任何文本传递回键盘。
将按键组成键盘
一旦有了按键,键盘就很容易布局,因为它们只是列中的行。
包含三行的列
这是代码。我省略了一些重复的部分,以便简洁。不过,你可以在文章的末尾找到它。
classCustomKeyboardextendsStatelessWidget{CustomKeyboard({Key?key,this.onTextInput,this.onBackspace,}):super(key:key);finalValueSetter<String>?onTextInput;finalVoidCallback?onBackspace;void_textInputHandler(Stringtext)=>onTextInput?.call(text);void_backspaceHandler()=>onBackspace?.call();@overrideWidgetbuild(BuildContextcontext){returnContainer(height:160,color:Colors.blue,child:Column(children:[buildRowOne(),buildRowTwo(),buildRowThree(),buildRowFour(),buildRowFive()],),);}ExpandedbuildRowOne(){returnExpanded(child:Row(children:[TextKey(text:'坚',onTextInput:_textInputHandler,),TextKey(text:'果',onTextInput:_textInputHandler,),TextKey(text:'祝',onTextInput:_textInputHandler,),],),);}ExpandedbuildRowTwo(){returnExpanded(child:Row(children:[TextKey(text:'I',onTextInput:_textInputHandler,),TextKey(text:'n',onTextInput:_textInputHandler,),TextKey(text:'f',onTextInput:_textInputHandler,),TextKey(text:'o',onTextInput:_textInputHandler,),TextKey(text:'Q',onTextInput:_textInputHandler,),],),);}ExpandedbuildRowThree(){returnExpanded(child:Row(children:[TextKey(text:'十',onTextInput:_textInputHandler,),TextKey(text:'五',onTextInput:_textInputHandler,),TextKey(text:'周',onTextInput:_textInputHandler,),TextKey(text:'年',onTextInput:_textInputHandler,),],),);}ExpandedbuildRowFour(){returnExpanded(child:Row(children:[TextKey(text:'生',onTextInput:_textInputHandler,),TextKey(text:'日',onTextInput:_textInputHandler,),TextKey(text:'快',onTextInput:_textInputHandler,),TextKey(text:'乐',onTextInput:_textInputHandler,),TextKey(text:'!',onTextInput:_textInputHandler,),],),);}ExpandedbuildRowFive(){returnExpanded(child:Row(children:[TextKey(text:'????',flex:2,onTextInput:_textInputHandler,),TextKey(text:'????',flex:2,onTextInput:_textInputHandler,),TextKey(text:'????',flex:2,onTextInput:_textInputHandler,),TextKey(text:'????',flex:2,onTextInput:_textInputHandler,),BackspaceKey(onBackspace:_backspaceHandler,),],),);}}
有趣的部分:
键盘收集按键的回调并传递它们。这样,任何使用
CustomKeyboard
的人都会收到回调。您可以看到第三行如何使用
flex
。空格键的弯曲为4
,而退格的默认弯曲为1。这使得空格键占用了后空键宽度的四倍。
在应用程序中使用键盘
现在,您可以像这样使用自定义键盘小部件:
代码看起来是这样的:
CustomKeyboard(onTextInput:(myText){_insertText(myText);},onBackspace:(){_backspace();},),
处理文本输入
以下是_insertText
方法的样子:
void_insertText(StringmyText){finaltext=_controller.text;finaltextSelection=_controller.selection;finalnewText=text.replaceRange(textSelection.start,textSelection.end,myText,);finalmyTextLength=myText.length;_controller.text=newText;_controller.selection=textSelection.copyWith(baseOffset:textSelection.start+myTextLength,extentOffset:textSelection.start+myTextLength,);}
_controller
是TextField
的TextEditingController
。你必须记住,可能有一个选择,所以如果有的话,请用密钥传递的文本替换它。
感谢这个,以提供帮助。*
处理退格
您会认为退格很简单,但有一些不同的情况需要考虑:
有一个选择(删除选择)
光标在开头(什么都不要做)
其他任何事情(删除之前的角色)
以下是_backspace
方法的实现:
void_backspace(){finaltext=_controller.text;finaltextSelection=_controller.selection;finalselectionLength=textSelection.end-textSelection.start;//Thereisaselection.if(selectionLength>0){finalnewText=text.replaceRange(textSelection.start,textSelection.end,'',);_controller.text=newText;_controller.selection=textSelection.copyWith(baseOffset:textSelection.start,extentOffset:textSelection.start,);return;}//Thecursorisatthebeginning.if(textSelection.start==0){return;}//DeletethepreviouscharacterfinalpreviousCodeUnit=text.codeUnitAt(textSelection.start-1);finaloffset=_isUtf16Surrogate(previousCodeUnit)?2:1;finalnewStart=textSelection.start-offset;finalnewEnd=textSelection.start;finalnewText=text.replaceRange(newStart,newEnd,'',);_controller.text=newText;_controller.selection=textSelection.copyWith(baseOffset:newStart,extentOffset:newStart,);}bool_isUtf16Surrogate(intvalue){returnvalue&0xF800==0xD800;}
即使删除之前的角色也有点棘手。如果您在有表情符号或其他代理对时只回退单个代码单元这将导致崩溃。作为上述代码中的变通办法,我检查了上一个字符是否是UFT-16代理,如果是,则后退了两个字符。(我从Flutter TextPainter
源代码中获得了_isUtf16Surrogate
方法。)然而,这仍然不是一个完美的解决方案,因为它不适用于像????????或????‍????‍????这样的字素簇,它们由多个代理对组成。不过,至少它不会
以下是象形文字和表情符号键盘作为演示:
????????????‍????‍
如果您对此有意见,请参阅此堆栈溢出问题。
防止系统键盘显示
如果您想将自定义键盘与aTextField一起使用,但系统键盘不断弹出,那会有点烦人。这毕竟是默认行为。
防止系统键盘显示的方法是将TextField
的readOnly
属性设置为true
。
TextField(...showCursor:true,readOnly:true,),
此外,将showCursor
设置为true
,使光标在您使用自定义键盘时仍然可以工作。
在系统键盘和自定义键盘之间切换
如果您想让用户选择使用系统键盘或自定义键盘,您只需为readOnly
使用不同的值进行重建。
以下是演示应用程序中TextField的设置方式:
class_KeyboardDemoStateextendsState<KeyboardDemo>{TextEditingController_controller=TextEditingController();bool_readOnly=true;@overrideWidgetbuild(BuildContextcontext){returnScaffold(resizeToAvoidBottomInset:false,body:Column(children:[...TextField(controller:_controller,decoration:...,style:TextStyle(fontSize:24),autofocus:true,showCursor:true,readOnly:_readOnly,),IconButton(icon:Icon(Icons.keyboard),onPressed:(){setState((){_readOnly=!_readOnly;});},),
有趣的部分:
当按下键盘
IconButton
时,更改_readOnly
的值,然后重建布局。这会导致系统键盘隐藏或显示。将
Scaffold
上的resizeToAvoidBottomInset
设置为false
,允许系统键盘覆盖自定义键盘。另一个选项是在显示系统键盘时隐藏自定义键盘。然而,当我在实验中这样做时,我发现我必须使用单独的布尔值来隐藏自定义键盘,这样我就可以延迟显示它,直到系统键盘消失。否则,它会跳到系统键盘顶部一秒钟。
就这样!如您所见,制作自己的应用程序内键盘并不难。
完整代码
以下是我在本文中使用的演示应用程序的完整代码:
import'package:flutter/material.dart';voidmain()=>runApp(MyApp());classMyAppextendsStatelessWidget{@overrideWidgetbuild(BuildContextcontext){returnMaterialApp(home:KeyboardDemo(),);}}classKeyboardDemoextendsStatefulWidget{@override_KeyboardDemoStatecreateState()=>_KeyboardDemoState();}class_KeyboardDemoStateextendsState<KeyboardDemo>{TextEditingController_controller=TextEditingController();bool_readOnly=true;@overrideWidgetbuild(BuildContextcontext){returnScaffold(appBar:AppBar(title:Text("大前端之旅的自定义键盘"),),resizeToAvoidBottomInset:false,body:Column(children:[Text("微信:xjg13690"),SizedBox(height:50),TextField(controller:_controller,decoration:InputDecoration(border:OutlineInputBorder(borderRadius:BorderRadius.circular(3),),),style:TextStyle(fontSize:24),autofocus:true,showCursor:true,readOnly:_readOnly,),IconButton(icon:Icon(Icons.keyboard),onPressed:(){setState((){_readOnly=!_readOnly;});},),Spacer(),CustomKeyboard(onTextInput:(myText){_insertText(myText);},onBackspace:(){_backspace();},),],),);}void_insertText(StringmyText){finaltext=_controller.text;finaltextSelection=_controller.selection;finalnewText=text.replaceRange(textSelection.start,textSelection.end,myText,);finalmyTextLength=myText.length;_controller.text=newText;_controller.selection=textSelection.copyWith(baseOffset:textSelection.start+myTextLength,extentOffset:textSelection.start+myTextLength,);}void_backspace(){finaltext=_controller.text;finaltextSelection=_controller.selection;finalselectionLength=textSelection.end-textSelection.start;//Thereisaselection.if(selectionLength>0){finalnewText=text.replaceRange(textSelection.start,textSelection.end,'',);_controller.text=newText;_controller.selection=textSelection.copyWith(baseOffset:textSelection.start,extentOffset:textSelection.start,);return;}//Thecursorisatthebeginning.if(textSelection.start==0){return;}//DeletethepreviouscharacterfinalpreviousCodeUnit=text.codeUnitAt(textSelection.start-1);finaloffset=_isUtf16Surrogate(previousCodeUnit)?2:1;finalnewStart=textSelection.start-offset;finalnewEnd=textSelection.start;finalnewText=text.replaceRange(newStart,newEnd,'',);_controller.text=newText;_controller.selection=textSelection.copyWith(baseOffset:newStart,extentOffset:newStart,);}bool_isUtf16Surrogate(intvalue){returnvalue&0xF800==0xD800;}@overridevoiddispose(){_controller.dispose();super.dispose();}}classCustomKeyboardextendsStatelessWidget{CustomKeyboard({Key?key,this.onTextInput,this.onBackspace,}):super(key:key);finalValueSetter<String>?onTextInput;finalVoidCallback?onBackspace;void_textInputHandler(Stringtext)=>onTextInput?.call(text);void_backspaceHandler()=>onBackspace?.call();@overrideWidgetbuild(BuildContextcontext){returnContainer(height:160,color:Colors.blue,child:Column(children:[buildRowOne(),buildRowTwo(),buildRowThree(),buildRowFour(),buildRowFive()],),);}ExpandedbuildRowOne(){returnExpanded(child:Row(children:[TextKey(text:'坚',onTextInput:_textInputHandler,),TextKey(text:'果',onTextInput:_textInputHandler,),TextKey(text:'祝',onTextInput:_textInputHandler,),],),);}ExpandedbuildRowTwo(){returnExpanded(child:Row(children:[TextKey(text:'I',onTextInput:_textInputHandler,),TextKey(text:'n',onTextInput:_textInputHandler,),TextKey(text:'f',onTextInput:_textInputHandler,),TextKey(text:'o',onTextInput:_textInputHandler,),TextKey(text:'Q',onTextInput:_textInputHandler,),],),);}ExpandedbuildRowThree(){returnExpanded(child:Row(children:[TextKey(text:'十',onTextInput:_textInputHandler,),TextKey(text:'五',onTextInput:_textInputHandler,),TextKey(text:'周',onTextInput:_textInputHandler,),TextKey(text:'年',onTextInput:_textInputHandler,),],),);}ExpandedbuildRowFour(){returnExpanded(child:Row(children:[TextKey(text:'生',onTextInput:_textInputHandler,),TextKey(text:'日',onTextInput:_textInputHandler,),TextKey(text:'快',onTextInput:_textInputHandler,),TextKey(text:'乐',onTextInput:_textInputHandler,),TextKey(text:'!',onTextInput:_textInputHandler,),],),);}ExpandedbuildRowFive(){returnExpanded(child:Row(children:[TextKey(text:'????',flex:2,onTextInput:_textInputHandler,),TextKey(text:'????',flex:2,onTextInput:_textInputHandler,),TextKey(text:'????',flex:2,onTextInput:_textInputHandler,),TextKey(text:'????',flex:2,onTextInput:_textInputHandler,),BackspaceKey(onBackspace:_backspaceHandler,),],),);}}classTextKeyextendsStatelessWidget{constTextKey({Key?key,@requiredthis.text,this.onTextInput,this.flex=1,}):super(key:key);finalString?text;finalValueSetter<String>?onTextInput;finalintflex;@overrideWidgetbuild(BuildContextcontext){returnExpanded(flex:flex,child:Padding(padding:constEdgeInsets.all(1.0),child:Material(color:Colors.blue.shade300,child:InkWell(onTap:(){onTextInput?.call(text!);},child:Container(child:Center(child:Text(text!)),),),),),);}}classBackspaceKeyextendsStatelessWidget{constBackspaceKey({Key?key,this.onBackspace,this.flex=1,}):super(key:key);finalVoidCallback?onBackspace;finalintflex;@overrideWidgetbuild(BuildContextcontext){returnExpanded(flex:flex,child:Padding(padding:constEdgeInsets.all(1.0),child:Material(color:Colors.blue.shade300,child:InkWell(onTap:(){onBackspace?.call();},child:Container(child:Center(child:Icon(Icons.backspace),),),),),),);}}
</div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
Flutter如何自定义应用程序内键盘的详细内容,希望对您有所帮助,信息来源于网络。