当前位置: 首页 > 科技观察

Flutter开发中的一些Tips

时间:2023-03-15 00:27:43 科技观察

学习Flutter有一段时间了。闲着没事干,就用公司的一个很酷的App设计图来练习。当然,接口是没法用的,所以都是死数据,实现效果可以说是非常不错了(已经被设计认可了。。。)。当然,我也是边查边写,也借鉴了Github上很多优秀的Flutter项目。现开源(附设计图)供大家交流学习。希望大家多多Star和Fork支持,有问题可以Issue。附上链接:github.com/simplezhli/...本文主要分享我在这个项目中遇到的问题和经验,希望对大家有所帮助!1、组件溢出异常大致如下:ARenderFlexoverflowedby22pixelsonthebottom。原因是在内容水平或垂直方向超出了父widget的尺寸。一般来说,我们的页面是不会出现这样的问题的,因为根据页面的设计,是可以提前预知是否超标的。但是要注意弹出的页面有输入法。比如我下面的例子:可以看到底部溢出了22个像素,而在18:9比例的手机上可能不会出现这个问题,因为屏幕的高度足够了。不过这款16:9的手机可能会曝光。解决方案有两种:包裹一层SingleChildScrollView,这样你的页面就可以向上滑动了。在脚手架中将resizeToAvoidBottomInset设置为false。默认为true,防止部件被遮挡。如果使用这种方式,如果底部有输入框,会造成遮挡。您可以根据自己的实际需要进行选择。2、输入框封面如下:底部有一个输入框,底部固定有“提交”按钮。一开始我想既然固定在底部,那就用Stack和Positioned来实现吧。然而,当输入法弹出时,却被挡住了。上图中,我选择了最后一个输入框,但是由于输入法默认是在输入框下方弹出,而“提交”按钮却被盖在了上面,被挡住了。最后,我的解决方案是使用ColumnwithExpanded来实现。修复后如下:3.一旦SafeArea有组件固定在顶部或底部(严格来说可以说是在屏幕的四边)。然后我将使用SafeArea将其包裹起来。因为Android和IOS都有状态栏,甚至IOS都有一个单杠叫“HomeIndicator”。所以稍不注意就会出现适应问题。我们在Flutter中经常使用的BottomNavigationBar和AppBar其实内部是处理这样的问题的。以AppBar源码为例:child:AnnotatedRegion(value:overlayStyle,child:Material(//<---2color:widget.backgroundColor??appBarTheme.color??themeData.primaryColor,child:Semantics(explicitChildNodes:true,child:appBar,),),),),);}}所以使用方法是:Material(//你需要给边框区域填充颜色,你可以使用Material(//你可以使用color:Colors.white,child:SafeArea(child:Container(),)如果需要给边框区域填充颜色,)还是上面这个页面,我们来对比一下处理前后的效果:4.善用ThemeFlutter在开发中,大家诟病很多嵌套,以及只能尽量避免,比如封装一些组件和属性,避免重复写。但是封装也是有讲究使用场景的。isstyle只用在一两个地方,封装就显得有点大材小用了。而大而全的封装也会增加使用的复杂度。然后就可以使用Theme的方法了。举个例子,下图中圈出的部分有3个按钮。它们具有相同的高度、文字和圆角。一个一个设置这些属性就太麻烦了。这样当我们使用Theme统一修改他们的样式时,会很方便。主题(数据:Theme.of(上下文)。copyWith(buttonTheme:ButtonThemeData(填充:constEdgeInsets.symmetric(水平:16.0),minWidth:64.0,height:30.0,materialTapTargetSize:MaterialTapTargetSize.shrinkWrap,形状:RoundedRectangleBorder:borderRadius(圆形(4.0),)),textTheme:TextTheme(button:TextStyle(fontSize:14.0,))),child:Row(children:[FlatButton(color:Color(0xFFF6F6F6),onPressed:(){},child:Text("Contactcustomer"),),......FlatButton(color:Color(0xFFF6F6F6),onPressed:(){},child:Text("Rejectorder"),)],),)并在同时很多默认设置也可以修改。比如FlatButton默认的宽度是88,高度是36,但是FlatButton里面没有属性可以直接修改。网上很多方法都是通过包裹一层Container来修改的。不仅嵌套增加了,一些需求还没有得到满足。所以用好Themes可以省时省力,但缺点是需要翻遍源码才能找到使用这些Themes的地方。5.注意平台差异。注意Android和IOS平台部分组件的差异。(1)Scaffold的AppBar,AppBar中默认的title在Android中显示在左侧,在IOS中显示在中间。如果需要两个平台的统一效果,需要在AppBar中主动设置centerTitle属性。同时,AppBar的后退箭头图标也有所不同。如果它们是统一的,则需要自定义行距。(2)页面跳转如果使用MaterialPageRoute作为过渡效果,注意Android中新页面会从屏幕底部滑动到屏幕顶部,IOS中新页面会从右侧滑动屏幕到屏幕的左侧。如果需要统一两个平台的效果,我们不使用自带的效果,可以自定义一个。Navigator.push(context,PageRouteBuilder(transitionDuration:Duration(milliseconds:300),pageBuilder:(context,animation,secondaryAnimation){returnnewFadeTransition(//使用淡入过渡,opacity:animation,child:TestPage(),);}));要么修改Theme,统一两个平台的实现。:classMyAppextendsStatelessWidget{staticconstMap_defaultBuilders={TargetPlatform.android:FadeUpwardsPageTransitionsBuilder(),TargetPlatform.iOS:FadeUpwardsPageTransitionsBuilder(),};@overrideWidgetbuild(BuildContextcontext){returnMaterialApp(主题:ThemeData(pagedeTransitionsTheme:TransitionsTheme:TransitionsBuilders));}失败(3)ScrollPhysics效果,可以滑动的部分具有物理属性。滑动到边界时,Android平台对边缘阴影有ClampingScrollPhysics的效果,IOS有BouncingScrollPhysics的反弹效果。如果需要均匀性,可以指定物理属性。(4)关于状态栏,Android平台默认为半透明效果,IOS则采用透明效果。比如Android要实现IOS的效果,可以将状态栏设置为透明。但是IOS无法达到Android的效果。..,只能定制吗?如果你知道怎么做,你可以分享。voidmain(){runApp(MyApp());//透明状态栏if(Platform.isAndroid){SystemUiOverlayStylesystemUiOverlayStyle=SystemUiOverlayStyle(statusBarColor:Colors.transparent);SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);}}5.输入键盘为TextField时keyboardType属性设置为TextInputType.phone或TextInputType.number,IOS系统弹出的数字输入键盘没有“Done”按钮,所以无法关闭输入法。当然,安卓不存在这个问题。比较成熟有效的方案是在键盘弹出的上方悬浮一个按钮,点击关闭键盘。当然,有相应的库可以解决这类问题。我使用flutter_keyboard_actions来解决这个问题。因为在Android端发现了一些输入法的兼容性问题,所以我只处理了IOS。大家可以看看前后对比图。具体的实现代码可以参考flutter_keyboard_actions的文档和我的项目代码:当然,平台差异不止这么多,比如IOS自带侧滑。具体可以查看调用TargetPlatform枚举类的代码。如果你觉得这样真的很麻烦,我给你一个大招,修改ThemeData的平台,指定一个平台。classMyAppextendsStatelessWidget{@overrideWidgetbuild(BuildContextcontext){returnMaterialApp(theme:ThemeData(platform:TargetPlatform.android),...);}}其次IOS中使用TextInputType.number弹出的键盘没有小数点符号。输入金额类型数据时,需要将keyboardType属性设置为TextInputType.numberWithOptions(decimal:true)。6.keyboardTypekeyboardType属性主要表示弹出的键盘类型,不代表输入数据的类型。在Android开发中,在EditText中设置android:inputType,不仅可以指定弹出键盘的类型,还可以判断输入数据的类型,即内置的数据格式校验。后者在Flutter中是没有的,所以你可以从TextInputType.number入手,但是在输入法中切换成中文键盘,也可以输入汉字。所以数据的校验需要我们自己使用inputFormatters来处理。例如TextInputType.phone可以使用WhitelistingTextInputFormatter白名单验证,只允许0~9:TextField(keyboardType:TextInputType.phone,inputFormatters:[WhitelistingTextInputFormatter(RegExp([0-9]"))])可以输入密码时使用使用BlacklistingTextInputFormatter黑名单验证去除汉字:TextField(keyboardType:TextInputType.text,inputFormatters:[BlacklistingTextInputFormatter(RegExp([\u4e00-\u9fa5]"))])输入小数时,可以自定义TextInputFormatter限制输入Decimal格式:TextField(keyboardType:TextInputType.numberWithOptions(decimal:true),inputFormatters:[UsNumberTextInputFormatter()])//来源:https://www.cnblogs.com/yangyxd/p/9639588.htmlclassUsNumberTextInputFormatterextendsTextInputFormatter1=staticconst00ub.??Do0ubstaticdoublestrToFloat(Stringstr,[doubledefaultValue=defaultDouble]){try{returndouble.parse(str);}catch(e){returndefaultValue;}}@overrideTextEditingValueformatEditUpdate(TextEditingValueoldValue,TextEditingValuenewValue){Stringvalue=newValue.text;结尾;如果(值ue=="."){value="0.";selectionIndex++;}elseif(value!=""&&value!=defaultDouble.toString()&&strToFloat(value,defaultDouble)==defaultDouble){value=oldValue.text;selectionIndex=oldValue.selection.end;}returnnewTextEditingValue(text:value,selection:newTextSelection.collapsed(offset:selectionIndex),);}}7.InkWellInkWell有的叫泼墨效果,有的叫水波纹效果.一些没有点击事件的组件在添加点击事件时使用(也支持长按、双击等事件),你也可以修改它的颜色和形状。InkWell(borderRadius:BorderRadius.circular(8.0),//圆角splashColor:Colors.transparent,//泼墨颜色(波纹色)highlightColor:Colors.transparent,//点击onTap时的背景色(高亮颜色):(){},//点击事件child:Container(),);但是,有时候你会发现,不包裹一层InkWell,肯定会有泼墨的效果。主要原因是飞溅效果是在背景效果上,而不是叠加在前景效果上。因此,一旦InkWell中的child有了背景图片或背景色,就会覆盖墨水飞溅效果。如果您需要这种墨水飞溅效果,有两种方法可以实现。(1)包裹一层Material,设置背景色为Material中的颜色。Material(color:Colors.white,child:InkWell(),)(2)使用Stack布局将InkWell放在上层。这个适合给图片添加点击效果,比如点击Banner图片。堆栈(孩子:[Positioned.fill(child:Image(),),Positioned.fill(child:Material(color:Colors.transparent,child:InkWell(splashColor:Color(0X40FFFFFF),highlightColor:Colors.transparent,onTap:(){},),),),)])8.保持页面状态。例如,点击导航栏可以来回切换页面。默认情况下,原来的页面状态会丢失,即每次切换都会重新初始化页面。这种情况的解决方法是PageView结合BottomNavigationBar使用,在子页面State中继承AutomaticKeepAliveClientMixin,将wantKeepAlive改写为true。代码大致如下:class_TestStateextendsStatewithAutomaticKeepAliveClientMixin{@overrideWidgetbuild(BuildContextcontext){super.build(context);returnContainer();}@overrideboolgetwantKeepAlive=>true;}9.依赖版本问题首先,建议所有Flutter插件填写版本号时不要使用-ins^符号。^符号表示您可以使用该插件的最新版本(大于或等于当前版本)。这会造成什么问题?也许您的代码前一天可以运行,但今天编译错误。由于这些插件包含了Android和IOS使用的依赖环境配置,所以新版本使用AndroidX依赖是很常见的,但是有些插件并没有使用AndroidX,导致两者冲突。之前在看flutter-go代码的时候,是因为突然升级了webview插件,导致安装失败。具体问题可以在这里找到。所以不建议在代码稳定的情况下使用^符号。出现这种问题,有几种解决方法:使用非AndroidX版本的插件。(优点是起效快,缺点是这个插件后续更新不能用)手动修改插件的冲突,因为Flutter插件的代码可以直接修改,所以可以手动修改这些冲突和统一插件版本(好处是可以使用最新版本。缺点是这种方式一是比较麻烦,二是不利于团队开发和使用)我比较喜欢用第二种方式,只要把修改的相关记录做好,也算是一劳永逸全部。10、FlutterAndroid的打包过程没有问题。配置签名文件后,执行flutterbuildapk命令。但是发现插件中的AndroidManifest.xml文件打包后并没有合并。比如我使用image_picker这个插件,它的AndroidManifest.xml文件如下:可以看到授权和Android7.0FileProvider的声明。像这样的信息没有打包(但是参考xml中的flutter_image_picker_file_paths文件是打包的),导致我实际使用这些函数时没有反应,但是在平时的调试过程中是好的。中间发现打包后的App名称也和之前一样。怀疑是缓存问题,于是手动删除了项目根目录下的build和.gradle文件夹,重新打包。所以打包后最好检查一下AndroidManifest.xml文件,避免这类缓存带来的问题。11.其他Container功能强大,可以用它来设置width,height,padding,margin,backgroundcolor,backgroundimage,roundcorners,shadow等,有些widget自带padding属性,不用设置额外的一层填充组件。(如ListView、GridView、Container、ScrollView、Button)尽量使用const来定义常量。比如padding、color、style:classColours{staticconstColortext_dark=Color(0xFF333333);}Padding(padding:constEdgeInsets.all(8.0),child:Text("Test",style:TextStyle(fontSize:26.0,color:Colours.text_dark)))4.Dart2中的new关键字是可选的,不要选,哈哈!!其实中间遇到的小问题还有很多,有的还没有找到很好的解决方法。但这只是一个开始,希望Flutter会越来越好。篇幅有限,先分享以上11个Tips。如果本文对您有帮助,请点赞支持!***Github地址又是:github.com/simplezhli/...