Flutter 开发小结 | Tips

接触 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

  • 焦点问题

    输入框的焦点问题主要体现在两点:

    1. 前往另一个页面返回后自动弹出了软键盘(即自动获取了焦点)
    2. iOS手机上切换至数字键盘后无法关闭软键盘

    这两个问题其实都可以借助 FocusNode 来解决,先来看下面一段代码:

    FocusNode _writingFocusNode = FocusNode();
    ...
      
      void _clearTextFieldFocus() {
        if (_writingFocusNode.hasFocus) {
          _writingFocusNode.unfocus();
        }
      }  
    

    上述代码创建了一个 FocusNode 对象,并声明了移除焦点的方法,相信大家不难判断出。此外,我们需要给 TextFieldfocusNode 属性传入我们创建的 _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虽好,但也需要在使用时注意一些问题,例如,它的源码注释中就说到:我们可以通过 colordecoration 来设置盒子背景,但两者却不能同时存在,如果我们既希望保留背景色,又想使用装饰器 (decoration),我们可以直接设置 BoxDecorationcolor 属性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值