接触 Flutter 已经有一阵子了,期间记录了很多开发小问题,苦于忙碌没时间整理,最近项目进度步上正轨,借此机会抽出点时间来统一记录这些问题,并分享项目开发中的一点心得以及多平台打包的一些注意事项,希望能对大家有所帮助😁。
UI 组件使用
官方为我们提供了大量原生效果的组件,如以 Android 中常见的 Material Design 系列组件和 iOS 系统中让设计师们“欲罢不能”的 Cupertino 系列组件。从我这一个月左右对于 Flutter UI 组件的使用情况来看,不得不感慨一句:“真香”。由于本人之前是做 Android 开发的,所以对于 Android 方面的一些“诟病”深有体会。例如,设计师经常让我们还原设计稿中的阴影效果,一般需要设置阴影颜色、x/y偏移量和模糊度等,然而 Android 原生并没有提供支持所有这些属性的一款组件,所以只能我们自己通过自定义控件去实现,现在还有多少人依然通过 CardView 来“鱼目混珠”呢?然而,在 Flutter 中就无需担心这种问题,通过类似前端中常用的盒子组件—— Container 就可以轻松实现。
当然,Flutter 虽然很强大,但 UI 组件也不是万能的,跨平台之路注定漫长而布满荆棘,偶尔也会伴随着一些小问题。
TextField
-
软键盘弹起后组件溢出的问题
由于页面不支持滚动,一旦使用 TextField,软键盘弹起后很容易会覆盖一些UI组件,如果不以为意,那么下面这个问题就会成为“家常便饭”:
A RenderFlex overflowed by xx pixels on the bottom.
常用的解决方案就是通过嵌套一层
SingleChildScrollView
来规避,当软键盘弹起时,下方的组件会被软键盘自动顶上去。 -
HintText 不居中问题
这个问题很多人应该都遇到过,当我们在项目中设置中文 Locale 后,在 TextField 的 InputDecoration 中设置
hintText
时,会发现提示文本向下偏移几个像素,这应该属于 Flutter 的bug。如何解决这个问题呢?很简单,只需要设置textBaseine
属性,如下代码所示:TextFormField( decoration: InputDecoration( prefixIcon: Icon( Icons.lock_outline ), hintText: S.of(context).loginPasswordHint, ), style: TextStyle( /// handle hint text offset problem. textBaseline: TextBaseline.alphabetic ), keyboardType: TextInputType.number, onSaved: (password) {}, )
具体可参考:https://github.com/flutter/flutter/issues/40118
-
焦点问题
输入框的焦点问题主要体现在两点:
- 前往另一个页面返回后自动弹出了软键盘(即自动获取了焦点)
- iOS手机上切换至数字键盘后无法关闭软键盘
这两个问题其实都可以借助
FocusNode
来解决,先来看下面一段代码:FocusNode _writingFocusNode = FocusNode(); ... void _clearTextFieldFocus() { if (_writingFocusNode.hasFocus) { _writingFocusNode.unfocus(); } }
上述代码创建了一个
FocusNode
对象,并声明了移除焦点的方法,相信大家不难判断出。此外,我们需要给TextField
的focusNode
属性传入我们创建的_writingFocusNode
。问题一中,我们可以在页面跳转前先移除焦点,这样,从二级页面返回后输入框就不会自动弹出软键盘。问题二中,我们可以在用户点击空白区域后自动移除焦点(关闭软键盘),以下代码供参考:Widget _buildInputArea() => Stack( children: <Widget>[ // 通过空白区域的点击事件来关闭软键盘 GestureDetector( onTap: () { _clearTextFieldFocus(); }, child: Container( /// 此处注意设置背景颜色,否则默认透明色可能会穿透,无法响应点击事件 color: AppTheme.surfaceColor, width: MediaQuery.of(context).size.width, height: MediaQuery.of(context).size.height, ), ), Column( children: <Widget>[ ScreenUtils.verticalSpace(32), // account input edit text Padding( padding: EdgeInsets.only(bottom: AutoSize.covert.dpToDp(12)), child: TextField( controller: _accountTextController, decoration: InputDecoration( prefixIcon: Padding( padding: EdgeInsets.all(AutoSize.covert.dpToDp(12)), child: ImageIcon(AssetImage(ImageAssets.ic_login_user_input)), ), hintText: S.of(context).loginAccountHint, ), keyboardType: TextInputType.number, ), ), // password input edit text Padding( padding: EdgeInsets.only(bottom: AutoSize.covert.dpToDp(12)), child: ValueListenableBuilder( valueListenable: obscureTextModel, builder: (context, value, child) => TextField( controller: _passwordTextController, obscureText: value, decoration: InputDecoration( prefixIcon: Padding( padding: EdgeInsets.all(AutoSize.covert.dpToDp(12)), child: ImageIcon(AssetImage(ImageAssets.ic_login_pwd_input)), ), suffixIcon: IconButton( icon: Icon(value ? Icons.visibility_off : Icons.visibility, size: AutoSize.covert.dpToDp(20)), onPressed: () { obscureTextModel.value = !value; } ), hintText: S.of(context).loginPasswordHint, ), keyboardType: TextInputType.text, ), ), ), ], ), ], );
Container
-
盒子模型特点
对于接触过前端的人来说,应该都领略过“盒子模型”的强大了,所以,Container 的强大之处相信也不用我多说了:它几乎是一个万能的“容器”,既能设置 margin、padding、aligment,又可以装饰它的背景 docoration 属性,例如阴影效果、渐变色、圆角效果等等。
-
设置背景色问题
Container虽好,但也需要在使用时注意一些问题,例如,它的源码注释中就说到:我们可以通过
color
和decoration
来设置盒子背景,但两者却不能同时存在,如果我们既希望保留背景色,又想使用装饰器 (decoration),我们可以直接设置BoxDecoration
的color
属性。