Flutter开发中的一些Tips(三)

距离flutter_deer开源快3个月了,目前为止收获了1600+的Star,感谢你们的对此项目的承认支持。不过虽然表面看上去光鲜亮丽,但我知道仍是有不少不规范不合理的用法及写法,为了避免对初学者形成误导做用,因此这期间我几乎天天都在完善优化它(如今应该还不错吧)。html

今天继续分享一些在Flutter开发中须要注意的点,但愿对你有所帮助。本篇的全部例子,都在我开源的flutter_deer中。但愿Star、Fork支持,有问题建议能够Issue。附上连接:github.com/simplezhli/…java

本系列前两篇:android

Flutter开发中的一些Tipsgit

Flutter开发中的一些Tips(二)github

在这里插入图片描述

1.多语言配置(国际化)

默认状况下,Flutter是没有进行多语言配置。因此不管咱们的手机系统环境是不是中文,一些Widget的文字都是英文显示。好比常见的输入框(TextField)的操做菜单、日期选择(showDatePicker)上的年月日。api

在这里插入图片描述

既然没有配置,那我咱们添加上便可。缓存

  1. 在 pubspec.yaml 中添加依赖:
 flutter_localizations:
 sdk: flutter
复制代码
  1. MaterialApp 中配置 localizationsDelegatessupportedLocales两个属性。
import 'package:flutter_localizations/flutter_localizations.dart';

class MyApp extends StatelessWidget {
  
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Deer',
      home: SplashPage(),
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: [
        const Locale('zh', 'CH'), const Locale('en', 'US') ] );
  }
}
复制代码

这里我只配置了中英文两个语言的支持。有其余语言的须要能够自行添加。性能优化

  1. 若是是iOS平台,还须要在Info.plist文件中作以下配置:
    在这里插入图片描述

其中:Localized resources can be mixed 为 YES 表示容许应用程序获取框架库内语言。app

若是你完成了上述的配置,那么在系统为中文环境时,就会自动将英文替换为中文。框架

在这里插入图片描述

其实翻看flutter_localizations的源码,你会发现它内置了多国语言的翻译。若是你以为文字不恰当,也能够继承对应的Localizations去修改。

在这里插入图片描述
本问题详细的代码见: 点击查看

2.文字字号

默认文字配置是ThemeData经过Typography(platform: platform).black方法获取的。

在这里插入图片描述

好比咱们经常使用的Text文字配置就是TextTheme中的body1Typography中按照Material的规范将语言分为三大类别

  1. 英语类字体(englishLike) 西欧、中欧、东欧和非洲大部分地区的语言一般用拉丁字母书写。越南语是一个明显的例外,虽然它使用了拉丁文书写系统的本地化形式,但它的重音符号可能比西欧语言中的要高得多。希腊语和西里尔语的书写系统与拉丁文很是类似。

  2. 高字体(tall) 语言脚本,须要额外的行高来容纳较大的象形文字,包括南亚、东南亚和中东语言,如阿拉伯语、印地语、泰语和越南语。

  3. 密集字体(dense) 须要额外的行高才能容纳更大的象形文字的语言脚本,包括中文、日语和韩文。

那么针对这三种类型的语言,默认的文字大小也会有调整。好比下图中englishLikedense的对比(点击查看源码):

在这里插入图片描述
能够看到默认状况下,中文、日文、韩文会比英文类的文字大一个字号。好比经常使用的 Text文字配置 body1在切换为中文环境后,文字的默认大小变为了 15.0,然而在英文环境下是 14.0

这个问题也是我是在作完多语言配置后发现的一个问题,由于文字变大致使个别页面形成了文字溢出组件。因此尽可能指定文字大小以免没必要要的这类问题。

3.预先缓存图片

在Flutter中,加载本地图片会存在一个加载过程。好比点击图标作图标的切换时,那么首次会发生闪动的状况。尤为是作相似引导页这类需求是,经过左右滑动切换图片时会发生比较明显的白屏一闪而过。

在这里插入图片描述

解决方法很简单,就是使用 precacheImage,它将图像预存到图像缓存中。若是图像稍后被ImageBoxDecationFadeInImage使用,它会被加载得更快。

precacheImage(AssetImage("assets/logo"), context);
复制代码

本问题详细的代码见:点击查看

4. 屏幕方向

新建的Flutter项目默认并无限制屏幕的横竖屏,因此若是你的项目并无适配横竖屏,须要限制某一方向。我以限制竖屏为例:

Flutter方法:

void main(){
  SystemChrome.setPreferredOrientations([
    DeviceOrientation.portraitUp,
    DeviceOrientation.portraitDown
  ]).then((_){
    runApp(MyApp());
  });
}
复制代码

原生方法:

Android在android -> app -> src-> main -> AndroidManifest.xml中的activity标签添加 screenOrientation属性。

<activity ... android:screenOrientation="portrait">
    </activity>
复制代码

iOS在Runner ->Info.plist中删除UISupportedInterfaceOrientations中的 UIInterfaceOrientationLandscapeLeft与·UIInterfaceOrientationLandscapeRight。最终以下:

<key>UISupportedInterfaceOrientations</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
	</array>
复制代码

上面的方法都是针对整个应用的。若是你但愿部分页面能够横屏或者竖屏,只能使用Flutter的方法在对应的页面去指定方向。

不过Flutter这个方法在iOS端有点问题,它并不能强制屏幕旋转(也就是屏幕当前为竖屏,你指定页面横屏显示,它并不会生效)。因此有这方面需求的同窗可使用flutter_orientation这个插件。

5.拆分widget

在书写Flutter的页面时,不免会嵌套的层级很深或者存在许多重复使用widget。因此通常咱们都会将一些widget抽离出来。抽离的方法有两种,一种是直接抽成方法(函数)返回。一种是抽出一个自定义的widget来使用。我下面举例说明一下:

class _TestPageState extends State<MyHomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Test"),
      ),
      body: Column(
        children: <Widget>[
          const Text("Android", style: const TextStyle(fontSize: 12.0, fontStyle: FontStyle.italic, color: Colors.blue),), const Text("iOS", style: const TextStyle(fontSize: 12.0, fontStyle: FontStyle.italic, color: Colors.blue),), const Text("Flutter", style: const TextStyle(fontSize: 12.0, fontStyle: FontStyle.italic, color: Colors.blue),), ], ), );
  }
}
复制代码

上面代码中存在着三个样式一致,只是文字不一样的Text。咱们将它抽离出来(固然你也能够直接抽离Column)。

使用第一种方式:

Widget _buildText(String text){
    return Text(text, style: const TextStyle(fontSize: 12.0, fontStyle: FontStyle.italic, color: Colors.blue),);
  }
复制代码

使用方式:

Column(
    children: <Widget>[
      _buildText("Android"),
      _buildText("iOS"),
      _buildText("Flutter"),
    ],
  )
复制代码

第二种方式:

class _MyText extends StatelessWidget {
  
  const _MyText(this.text, {Key key}) : super(key: key);
  
  final String text;
  
  @override
  Widget build(BuildContext context) {
    return Text(text, style: const TextStyle(fontSize: 12.0, fontStyle: FontStyle.italic, color: Colors.blue),);
  }
}
复制代码

使用方式:

Column(
    children: <Widget>[
      const _MyText("Android"), const _MyText("iOS"), const _MyText("Flutter"), ], ) 复制代码

看起来是第一种很方便,但不知道你有没有发现第一种方式没法添加 const关键字。其实问题就出在了这里。在我以前的博客中就有提到尽可能使用const关键字 来定义常量。那么这是为何呢?

咱们来再看一个小例子:

在这里插入图片描述

我直接调用上面抽出的widget,每次点击按钮setState刷新页面,输出widget的hashCode。

在这里插入图片描述
能够看到 使用了const 关键字修饰的widget,并不会重复建立。看来养成随手添加 const的好习惯会在无形中提高应用的性能,好比经常使用的Color、TextStyle、分隔块咱们 整合起来,直接调用就是很不错的作法。
在这里插入图片描述

这个问题是由Provider的做者Rémi Rousselet提出的,What is the difference between functions and classes to create widgets?

做者得出的结论是:永远不要使用方法返回的形式建立可重用的widget,始终将它们封装到StatelessWidget中。 注意这个结论中的可重用

在上面的例子中,咱们的文字是固定的(可重用),这致使咱们能够直接在widget上添加 const。实际中,咱们的展现的数据都是请求的并非固定的,咱们即便抽离出StatelessWidget,也没法直接添加 const来使用。因此若是你的widget并没有法重用,使用上述两种方法的哪种效果都是同样的。

上述的例子中,即便Text没法使用const标记,可是Text中的TextStyle确能够重用。这一切取决于你的widget拆分的颗粒度是否足够合理,来尽量的避免这种性能上的浪费。

固然我更推荐StatelessWidget的方式。正如做者说的,它具有如下优势:

  • 容许性能优化(const 构造函数,更精细粒度的重建)
  • 有热重载
  • 集成到widget检查器中(debugFillProperties
  • 能够定义Key(关于Key的做用能够看这里的解答
  • 能够方便的使用context
  • 规范全部widget以相同的方式使用(始终使用构造函数)
  • 能够确保在两个不一样布局之间切换时,正确的配置信息(函数可能重用一些之前的状态)

若是你以前已经写了大量的方法建立返回widget的代码,可使用Rémi Rousselet的functional_widget来改善这个问题。

6.其余

  • 上面有说道抽离出StatelessWidget。其实为了不因刷新局部widget调用setState而致使整个页面刷新形成的性能损耗,咱们能够将局部刷新的地方抽离为StatefulWidget。我曾经写过一个页面通过这样的优化,耗时由25ms降到了6ms,所以控制刷新范围是很必要的。

  • 通常状况下不建议使用 Offstage来作隐藏功能,虽然说它能够隐藏指定的widget。可是它仍是会建立出对应的widget,只是放在了看不见的“后台”。我以前就将一个CupertinoActivityIndicator()这样隐藏了起来,结果在PerformanceOverlay 中就看到页面不断在绘制。。。因此若是你须要隐藏widget,可使用 isGone ? const SizedBox() : CupertinoActivityIndicator()这类三元运算符的方式处理。

  • 能够将数据解析放在 isolate 中处理,避免某些性能很差的设备在解析数据时形成的卡顿。详细例子能够查看文档


这篇断断续续写了半个月,终于完成了!我能够安心的去参加GDD了。码字不易,但愿点赞支持!最后再次奉上Deer的Github地址,顺手也能够支持一波!

相关文章
相关标签/搜索