Flutter 常用最佳实践

172 阅读4分钟

1. Text 以字符的方式截断

github.com/flutter/flu…

在 flutter 中,Text 控件默认的溢出显示模式是 TextOverflow.fade ,就是淡出

在 iOS 或者 Android 平台默认的文件截断模式一般是...省略,flutter 里面对应的截断模式为TextOverflow.ellipsis ,不过这里的截断是英文按照单词来的,这样的模式会导致如果最后一个单词很长时,截断显示会整理省略而不是最后超出的字符省略,导致模块可能有一大块空白。

Dart 系统定义的截断模式

/// How overflowing text should be handled.
///
/// A [TextOverflow] can be passed to [Text] and [RichText] via their
/// [Text.overflow] and [RichText.overflow] properties respectively.
enum TextOverflow {
/// Clip the overflowing text to fix its container.
clip,

/// Fade the overflowing text to transparent.
fade,

/// Use an ellipsis to indicate that the text has overflowed.
ellipsis,

/// Render overflowing text outside of its container.
visible
}

如何解决英文单词被整体截断呢?

将单词的每个字符切割开,插入宽度0的占位字符,打破系统默认的机制,这样就可以以字符为单位省略了

需要注意。这种方式相当于修改了文本的内容,一般文本最大一行显示可以用,如果文本支持2行以及以上的显示的话,将会导致换行不再按照字符进行而按照单词进行

下面是 Example 实现

extension TextOverflowUtil on String {
  /// 将flutter系统默认的单词截断模式转换成字符截断模式
  /// 通过向文本中插入宽度为0的空格实现
  /// https://github.com/flutter/flutter/issues/18761
  static String? toCharacterBreakStr(String? word) {
    if (word == null || word.isEmpty) {
      return word;
    }


    return Characters(word)
        .replaceAll(Characters(''), Characters('\u{200B}'))
        .toString();
  }
}

2. 文本划线

通过 Text 的 decoration 属性来实现划线

• TextDecoration.none        没有
• TextDecoration.underline   下划线
• TextDecoration.overline    上划线
• TextDecoration.lineThrough 中间的线(删除线)


// 划线相关的属性设置
• decorationColor   decoration划线的颜色
• decorationStyle   decoration划线的样式
        • TextDecorationStyle.solid   实线
        • TextDecorationStyle.double  画两条线
        • TextDecorationStyle.dotted  点线(一个点一个点的)
        • TextDecorationStyle.dashed  虚线(一个长方形一个长方形的线)
        • TextDecorationStyle.wavy    正玄曲

效果图如下

3. VS Code 实现保存时自动代码格式化

VS Code中代码格式化默认快捷键:【Shift】+【Alt】+ F

实现手动保存(Ctril + S)时自动触发代码格式化:

1)Code → Perference → Settting 点击右上角(Open Setting(JSON) )

2)在settings.json下的【工作区设置】中添加以下语句:

"editor.formatOnSave": true

4. 解决 setState() called after diapose()

网络请求成功前退出了页面,该 State 被从对象树卸载掉,而这时回调了网络请求的方法,方法中带有 setState 的调用,也就导致了该问题。

if (mounted) {
  setState(() {
    this._books = dataModel.books
  });
}

5. Waiting for another flutter command to release the startup lock…等待另一个flutter命令释放启动锁

  1. 退出 VS Code。
  2. 打开 flutter 安装目录 如:...\flutter\bin\cache 删除里面的 lockfile。
  3. 重新打开 VS Code。

原因:当你的项目异常关闭,下次启动就会出现上面的一行话, 此时需要打开 flutter/bin/cache/lockfile,删除就行了, 或者直接用下面的命令:rm ./flutter/bin/cache/lockfile。

6. 在 Stateless 控件内部或者浮层内部刷新,可以使用 StatefullBuilder

showDialog<void>(
  context: context,
  builder: (BuildContext context) {
    int selectedRadio = 0;
    return AlertDialog(
      content: StatefulBuilder(
        builder: (BuildContext context, StateSetter setState) {
          return Column(
            mainAxisSize: MainAxisSize.min,
            children: List<Widget>.generate(4, (int index) {
              return Radio<int>(
                value: index,
                groupValue: selectedRadio,
                onChanged: (int value) {
                  setState(() => selectedRadio = value);
                },
              );
            }),
          );
        },
      ),
    );
  },
);

这里通过 selectedRadio 变量记录 Radio 的是否选中的状态

7. 平台相关的判断

只关心是否是 iOS 和 Android 的情况下不需要依赖 context,优先使用 Platform

Platform.isAndroid
Platform.isIOS

需要详细知道具体哪个平台才使用 TargetPlatform

这个API的缺点是需要依赖 context 这个参数

final platform = Theme.of(context).platform;
if (platform == TargetPlatform.android) {
        ...
} else if (platform == TargetPlatform.iOS) {
        ...
}

8. ScrollView 滑动隐藏键盘

stackoverflow.com/questions/5…

使用ScrollView的keyboardDismissBehavior属性

ListView(
    keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag
)

9. 回调写法

a.无参数回调

VoidCallback

b.有一个参数回调

ValueChanged

c.参数大于一个

可以通过typedef自定义一个函数

下面是Example用法

final VoidCallback onPressed;
final ValueChanged<T> onSelectHandler;
typedef ImageSwiperOnTap = void Function(int index, List<String> imgUrls);

10. flutter pub get is stuck

可以通过切换flutter镜像到中文站点来解决

使用系统shell,请编辑

使用oh_my_zsh, 需要编辑.zshrc

export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn

保存文件后,关闭,下次重新打开终端生效

再执行flutter pub get 查看速度是否正常

11. 如何计算Text的行数,或者是否超过几行

stackoverflow.com/questions/5…

计算是否超过几行

return LayoutBuilder(builder: (context, size) {
  final span = TextSpan(text: yourText, style: yourStyle);
  final tp = TextPainter(text: span, maxLines: 3);
  tp.layout(maxWidth: size.maxWidth);


  if (tp.didExceedMaxLines) {
    // The text has more than three lines.
    // TODO: display the prompt message
    return Container(color: Colors.red);
  } else {
    return Text(yourText, style: yourStyle);
  }
});

计算行数

List<ui.LineMetrics> lines = textPainter.computeLineMetrics();
int numberOfLines = lines.length;

12. 元素查找 (firstWhere)

stackoverflow.com/questions/5…

final Cards selectedCard = cards.length > 0
        ? cards.firstWhere((element) => element.id == selectedTag.id,
            orElse: () => null)
        : null;

13. Gesturer 事件处理

优化点击事件的范围

使child组件的周围空白区域也有效

不设置的话,只会响应在child范围内的点击事件

GestureDetector(
        behavior: HitTestBehavior.translucent,
        onTap: () {
            //  do something
        },
        child: Container()
)