android开发歌词滑动效果_一些Flutter开发中的“坑”
刘望舒
读完需要
12分钟
速读仅需8分钟
作者:唯鹿
https://weilu.blog.csdn.net/article/details/90546727
考虑到可能有很多同学还没有接触 Flutter,其实这篇文章对应的代码也是个非常好的练手项目,UI 都是真正项目中的设计,做的非常精良。
另外文中的一些 tips 也能回答一些常见的问题,比如和 IOS 有哪些平台差异等。
学习Flutter也有一阵子了。闲着没事,用了公司一个已经凉凉的App设计图来练手。
当然了接口不可能用的了,所以都是些死数据,实现效果可以说是很完美了(得到了设计的认可。。。)。当然自己也是边查边写,也借鉴了许多Github上优秀的Flutter项目。
现在开源出来(附带设计图),供大家交流学习。希望多多Star、Fork支持,有问题可以Issue。附上链接:
https://github.com/simplezhli/flutter_deer
本篇主要分享一下自己在此项目中遇到的问题及心得,希望对你有所帮助!
异常大致如下:
1、部件溢出
A RenderFlex overflowed by 22 pixels on the bottom.
导致的原因就是在水平或者垂直方向上的内容超过了父部件的大小。一般来说我们的页面不存在这样的问题,因为根据页面的设计,事先可以预料到是否超出。不过要注意到有输入法弹出的页面。比如我下面的这个例子:
可以看到底部溢出了22个像素,可能在18:9的手机以上不太会出现这种问题,因为屏幕的高度足够。但是这种16:9的手机可能会暴露出来。
解决的方法有两种:
1. 包一层SingleChildScrollView,让你的页面可以滑动起来。
2. 在Scaffold中设置resizeToAvoidBottomInset为false。默认为ture,防止部件被遮挡。如果使用了这个方法,如果底部有输入框,则会造成遮挡。
大家可以根据实际需求选择。
2、输入框的遮挡
页面如下:
底部有输入框,同时“提交”的按钮固定在底部。一开始觉得既然固定在底部,那就使用Stack配合Positioned来实现,然而就导致输入法弹出时,发生遮挡。
上图中,我选中了最后一个输入框,但因为输入法默认都是在输入框的下方弹出,然而上面盖着这个“提交”按钮,发生了遮挡。
最终我的解决方法就是使用Column配合Expanded来实现。
修复后如下:
3、SafeArea
一旦有部件固定在顶部或者底部(严谨点的话可以说是在屏幕的四边)。那我我们最好使用SafeArea来包一下。因为Android 和 IOS都有状态栏,甚至IOS还有叫做“HomeIndicator”的横条。所以一不留神就会出现适配问题。
我们在Flutter中常使用的BottomNavigationBar 和 AppBar 其实就在内部处理了此类问题。以 AppBar源码为例:
class _AppBarState extends State<AppBar> {
@override Widget build(BuildContext context) {
if (widget.primary) { appBar = SafeArea( // top: true, child: appBar, ); }return Semantics( container: true, child: AnnotatedRegion( value: overlayStyle, child: Material( // color: widget.backgroundColor ?? appBarTheme.color ?? themeData.primaryColor, child: Semantics( explicitChildNodes: true, child: appBar, ), ), ), ); }}
所以使用方法为:
Material( // 需要颜色填充到边界区域可以使用 color: Colors.white, child: SafeArea( child: Container(), ),)
还是上面的页面,我们对比一下处理前后的效果:
4、善用Theme
Flutter 在开发中,让人诟病的就是大量的嵌套,而我们只能尽量避免。比如将一些部件、属性进行封装,避免重复的书写。不过封装也讲究使用场景。如果这种样式的部件仅仅只是某一两处使用,封装显得有点小题大做。并且封装的大而全也会增加使用的复杂度。那么这时就可以使用Theme这种办法。
举一个例子,在下图中圈起来的部分有三个按钮,它们的高度相同,文字、圆角大小也相同。如果每一个都去设定这些属性,未免太过麻烦。
这时我们使用Theme去统一修改它们的样式,就会很方便了。
Theme( data: Theme.of(context).copyWith( buttonTheme: ButtonThemeData( padding: const EdgeInsets.symmetric(horizontal: 16.0), minWidth: 64.0, height: 30.0, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, shape:RoundedRectangleBorder( borderRadius: BorderRadius.circular(4.0), ) ), textTheme: TextTheme( button: TextStyle( fontSize: 14.0, ) ) ), child: Row( children: [ FlatButton( color: Color(0xFFF6F6F6), onPressed: (){}, child: Text("联系客户"), ), ...... FlatButton( color: Color(0xFFF6F6F6), onPressed: (){}, child: Text("拒单"), ) ], ), )
同时使用Theme还可以修改许多默认的设置,比如FlatButton的默认宽度为88,高度为36,但是FlatButton中没有直接修改的属性,网上好多的方法都是通过包一层Container去修改,不仅增加的嵌套,有些需求还不能达到。所以善用Theme可以让你省时省力,不过缺点就是你需要去翻翻源码,寻找使用这些Theme的地方。
5、注意平台差异
注意部分组件在Android与IOS平台之间的差异。
1. Scaffold的 AppBar,AppBar中默认的title在Android中靠左显示,IOS中居中显示。如果需要两个平台效果统一,需要设置在AppBar中主动设置centerTitle属性。同时AppBar的返回箭头图标也不相同,统一的话需要自定义leading。
2. 页面跳转如果使用MaterialPageRoute来做过渡效果,注意Android中新的页面会从屏幕底部滑动到屏幕顶部,IOS中新的页面会从屏幕右侧滑动到屏幕左侧。
如果需要两个平台效果统一,我们不使用自带效果,可以自定义一个。
Navigator.push(context, PageRouteBuilder(transitionDuration: Duration(milliseconds: 300), pageBuilder: (context, animation, secondaryAnimation){ return new FadeTransition( //使用渐隐渐入过渡, opacity: animation, child: TestPage(), ); }));
要么修改Theme,统一两平台的实现。:
class MyApp extends StatelessWidget {
static const Map _defaultBuilders = { TargetPlatform.android: FadeUpwardsPageTransitionsBuilder(), TargetPlatform.iOS: FadeUpwardsPageTransitionsBuilder(), };@overrideWidget build(BuildContext context) {return MaterialApp( theme: ThemeData( pageTransitionsTheme: PageTransitionsTheme( builders: _defaultBuilders ) ), ... ); }}
3. ScrollPhysics效果,可以滑动的部件都有一个physics属性。滑动到边界时,Android平台为边缘阴影的效果ClampingScrollPhysics,IOS为回弹效果BouncingScrollPhysics。如果需要统一,可以指定physics属性。
4. 状态栏方面,Android平台默认是半透明的效果,IOS则是透明效果。比如Android要实现IOS的效果,可以设置状态栏为透明。不过IOS要实现Android的效果则不行。。。,难道只能自定义?有知道方法的可以分享一下。
void main(){ runApp(MyApp()); // 透明状态栏 if (Platform.isAndroid) { SystemUiOverlayStyle systemUiOverlayStyle = SystemUiOverlayStyle(statusBarColor: Colors.transparent); SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle); }}
5. 输入键盘
当TextField的keyboardType属性设置为TextInputType.phone 或TextInputType.number时,IOS系统弹出的数字输入键盘没有"完成"按钮,导致输入法无法关闭。当然了Android不存在这个问题。
比较成熟有效的方案是在键盘弹出的上方悬浮一个按钮,点击可以关闭键盘。当然了,这种问题也有对应的库可以解决,我使用的是flutter_keyboard_actions来解决了这个问题。因为在Android端我发现了部分输入法的兼容问题,所以只针对IOS做了处理。大家可以看一下前后对比图,具体实现代码可以参考flutter_keyboard_actions的文档和我的项目代码:
当然平台差异不仅仅是这么多,比如IOS自带侧滑返回等。具体我们可以去查看调用TargetPlatform枚举类的代码。
如果你觉得这样真麻烦,我给你支个大招,修改ThemeData的platform,指定一个平台。
class MyApp extends StatelessWidget {
@override Widget build(BuildContext context) {
return MaterialApp( theme: ThemeData( platform: TargetPlatform.android ), ... ); }}
其次就是使用TextInputType.number在IOS中弹起的键盘没有小数点符号。在输入金额类型数据时,需要将keyboardType属性设置为TextInputType.numberWithOptions(decimal: true)。
6、keyboardType
keyboardType属性主要含义为弹起的键盘类型,并不代表输入数据的类型。
而在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("[一-龥]"))] )
输入小数时,可以自定义TextInputFormatter来限制输入小数格式:
TextField( keyboardType: TextInputType.numberWithOptions(decimal: true), inputFormatters: [UsNumberTextInputFormatter()] )
//来源:https://www.cnblogs.com/yangyxd/p/9639588.htmlclass UsNumberTextInputFormatter extends TextInputFormatter { static const defaultDouble = 0.001; static double strToFloat(String str, [double defaultValue = defaultDouble]) { try { return double.parse(str); } catch (e) { return defaultValue; } }
@override TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) { String value = newValue.text; int selectionIndex = newValue.selection.end; if (value == ".") { value = "0."; selectionIndex++; } else if (value != "" && value != defaultDouble.toString() && strToFloat(value, defaultDouble) == defaultDouble) { value = oldValue.text; selectionIndex = oldValue.selection.end; } return new TextEditingValue( text: value, selection: new TextSelection.collapsed(offset: selectionIndex), ); }}
7、 InkWell
InkWell有的叫溅墨效果,有的叫水波纹效果。使用场景是给一些无点击事件的部件添加点击事件时使用(也支持长按、双击等事件),同时你也可以去修改它的颜色和形状。
InkWell( borderRadius: BorderRadius.circular(8.0), // 圆角 splashColor: Colors.transparent, // 溅墨色(波纹色) highlightColor: Colors.transparent, // 点击时的背景色(高亮色) onTap: () {},// 点击事件 child: Container(),);
不过有时你会发现并不是包一层InkWell就一定会有溅墨效果。主要原因是溅墨效果是在一个背景效果,并不是覆盖的前景效果。所以InkWell中的child一旦有设置背景图或背景色,那么就会遮住这个溅墨效果。如果你需要这个溅墨效果,有两种方式实现。
1. 包一层 Material,将背景色设置在 Material中的color里。
Material( color: Colors.white, child: InkWell(),)
2. 使用Stack布局,将InkWell放置在上层。这种适用于给图片添加点击效果,比如Banner图的点击。
Stack( children: <Widget>[ 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 _TestState extends State<Test> with AutomaticKeepAliveClientMixin{
@override Widget build(BuildContext context) { super.build(context); return Container(); }
@override bool get wantKeepAlive => true;}
详细的可以看这篇文章:Flutter 三种方式实现页面切换后保持原页面状态
https://zhuanlan.zhihu.com/p/58582876
9、依赖版本问题
首先这里建议凡是Flutter的插件在填写版本号时不要使用^符号。
^符号意味着你可以使用此插件的最新版本(大于等于当前版本)。这会导致什么问题呢?
可能你前一天代码还能跑起来,今天就编译出错了。因为这些插件中包括Android、IOS的所用依赖环境配置,常见的就是新版本使用了AndroidX的依赖,但是还有些插件并没有使用AndroidX,导致了两者的冲突。
我之前在看flutter-go的代码时,就是因为webview的插件突然升级了,导致了安装失败。具体问题可以看这里。所以在代码稳定的情况下不建议使用^符号。
发生了这种问题,有以下几个解决方法:
1. 使用非AndroidX的版本插件。(优点就是见效快。缺点就是此插件后续的更新无法使用)
2. 手动修改插件的冲突,因为Flutter插件的代码是可以直接修改的,所以你可以手动修改掉这些冲突,统一插件的版本(优点就是可以使用最新的版本。缺点就是这种方法首先麻烦,其次不利于团队开发使用)
https://flutter.dev/docs/development/packages-and-plugins/androidx-compatibility
我偏好使用第二种,只要做好修改的相关记录就行,算是一劳永逸。
10、Flutter Android 打包
打包本身流程没有问题,配置好签名文件,执行flutter build apk命令。但是发现打包后没有将插件中的AndroidManifest.xml文件合并。比如我有使用image_picker插件,它的AndroidManifest.xml文件如下:
可以看到有权限的及Android 7.0FileProvider的声明。诸如此类的信息没有打包进去(但是引用xml中的flutter_image_picker_file_paths文件却在),导致我实际使用这些功能时没有反应,但是在平时的调试过程中却是好的。
中间我发现打包后的App名称也是之前的,怀疑是缓存问题,所以我手动删除了项目根目录的build与.gradle文件夹,重新打包就好了。所以打包后最好检查一下AndroidManifest.xml文件,避免此类缓存造成的问题。
1. Container 功能强大,设置宽高、padding、margin、背景色、背景图、圆角、阴影等都可以使用它。
2. 有些widget 自带padding 属性,所以不必多套一层Padding部件。(比如ListView、GridView、Container、ScrollView、Button )
3. 尽量使用const来定义常量。比如padding、color、style 这些地方:
11、其他
class Colours { static const Color text_dark = Color(0xFF333333);}
Padding( padding: const EdgeInsets.all(8.0), child: Text( "Test", style: TextStyle( fontSize: 26.0, color: Colours.text_dark ) ))
Dart2中的new 关键字可选,所以就不要选了,哈哈!!
其实我在这中间遇到的小问题还有很多,有的暂时还没有找到好的方法去解决。不过这才刚刚开始,希望Flutter越来越好。
篇幅有限,那么先分享以上11条Tips,如果本篇对你有所帮助,可以点赞支持!最后再次奉上Github地址:
https://github.com/simplezhli/flutter_deer
-------- END ---------
推荐阅读
用Kotlin实现抖音爆红的文字时钟,征服产品小姐姐
看了这篇文章,再也不怕ANR了!
几个朋友逆袭进大厂的案例,有时你缺的就是那点运气!
如果你喜欢我的文章,就给公众号加个星标吧,方便阅读。
听说有人不敢点这里 ?
android开发歌词滑动效果_一些Flutter开发中的“坑”相关推荐
- android开发歌词滑动效果_android实现歌词自动滚动效果
最近在做Android 的MP3播放的项目,要实现歌词的自动滚动,以及同步显示. lyric的歌词解析主要用yoyoplayer里面的,显示部分参考了这里 ,这里只是模拟MP3歌词的滚动. 先上一下效 ...
- android开发歌词滑动效果_Android应用开发--MP3音乐播放器滚动歌词实现
[android]代码库2013年6月2日 简.美音乐播放器开发记录 -----主题 这篇博客的主题是:"滚动歌词的实现" 要的效果如下: ----实现过程 1. 建立歌词内容实体 ...
- android开发歌词滑动效果_android 实现歌词自动滚动+手指顺畅拖动
需求: 1.歌词可以跟随播放进度进行自动滑动: 2.可以手指进行歌词顺畅滑动: 3.当前歌词高亮,且置于屏幕的中心: 实现方式一: 也是网上可以搜到的做多的方式:自定义view,继承textview; ...
- android开发歌词滑动效果_Android 歌词同步滚动效果
歌词是播放器类App必不可少的组件,而一般的歌词组件都需要做到歌词的显示与播放进度同步.我们知道,歌词是如下所示的文件: lrc [ti:原来爱情这么伤] [ar:梁咏琪] [al:给自己的情歌] [ ...
- Android实现左右滑动效果
本示例演示在Android中实现图片左右滑动效果. 关于滑动效果,在Android中用得比较多,本示例实现的滑动效果是使用ViewFlipper来实现的,当然也可以使用其它的View来实现.接下来 ...
- Android实现圆弧滑动效果之ArcSlidingHelper篇
前言 我们平时在开发中,难免会遇到一些比较特殊的需求,就比如我们这篇文章的主题,一个关于圆弧滑动的,一般是比较少见的.其实在遇到这些东西时,不要怕,一步步分析他实现原理,问题便能迎刃而解. 前几天一位 ...
- flutter 图片路径_【Flutter开发工具推荐】Flutter资源管理利器:Flr
前言 作为一名Flutter开发者,如果你正在烦恼如何简单快捷地在pubspec.yaml中为多张图片.文本.字体资源添加声明,如果你正在烦恼如何简单安全地在代码中引用资源,那么现在你可以很简单地解决 ...
- android horizontalscrollview 动画,Android HorizontalScrollView左右滑动效果
本文实例为大家分享了Android HorizontalScrollView左右滑动的具体代码,供大家参考,具体内容如下 效果图 一.什么是HorizontalScrollView Horizonta ...
- android应用程序 多少钱_关于APP开发你最想了解的事,开发一个APP多少钱?
当我们在进行APP开发,最关注的问题一定是APP开发的价格问题,而作为软件开发行业的从业人员,经常会遇到这样的情况:"别说这么多,你只要告诉我开发一个软件多少钱就行了". 郑州AP ...
- android开发工具哪个好_小程序开发工具怎么用?哪个好用?
想要开发微信小程序,在没有太多资金找外包团队定制.自建团队开发时,就需要你自己通过小程序开发工具来生成小程序了.现在各种开发工具很多,到底小程序开发工具有哪些?这些微信小程序开发工具哪个好用?根据我的 ...
最新文章
- MO-JAVA-2.1学习--1
- 【Python-ML】感知器学习算法(perceptron)
- 从求生存到修体系,我在阿里找到了技术人的成长模式
- 如何查询spark版本_掌握Spark SQL中的查询执行
- 一二三系列之状压DP——Max Correct Set(一)Neko Rules the Catniverse (Large Version)(二)Make It Ascending(三)
- 靠谱的div引入任何外链内容
- php 获取localstorage,浅谈localStorage的本地存储
- Java Web Start实例
- Linux移植随笔:又遇困难
- 模拟jQuery,简单仿写API
- 笔记(2)-文本挖掘与机器学习
- [学习笔记]2020年win10最常用dos命令以及win+R即可运行的命令(使用命令打开自己想要的内容,总比找不到更好,赶快收藏)
- 小球碰撞python代码_Java 实现小球碰撞GUI
- 《动手学深度学习》学习总结
- 555集成定时器及其应用
- Unity 版本更新
- bmp格式图片缩放(位图缩放)
- 1451_TC275 DataSheet阅读笔记12_时钟、温度以及供电
- 华为云-容器引擎CCE-基本概念
- Teradata SQL 日期
热门文章
- 小战Java笔记_SE_Identifier(标识符)
- day25 在继承的背景下属性查找的顺序、组合、多态与接口、鸭子类型
- linux访问ftp服务器命令
- CentOS清除用户登录记录和命令历史方法
- 2018暑假第五周总结(8.6-8.12)
- Windows之IOCP
- PHP验证时有用的几段代码
- (原创)c#学习笔记03--变量和表达式03--变量04--变量的声明和赋值
- 过滤器如何配置(javax.servlet.Filter)?
- 最近要使用User Interface Process Application Block for .NET(微软net开发架构)