美文网首页FlutterFlutterFlutter
Flutter开发中的一些Tips(三)

Flutter开发中的一些Tips(三)

作者: 唯鹿_weilu | 来源:发表于2019-09-26 17:15 被阅读0次

    距离flutter_deer开源快3个月了,目前为止收获了1600+的Star,感谢大家的对此项目的认可支持。不过虽然表面看上去光鲜亮丽,但我知道还是有很多不规范不合理的用法及写法,为了不对初学者造成误导作用,所以这期间我几乎每天都在完善优化它(现在应该还不错吧)。

    今天继续分享一些在Flutter开发中需要注意的点,希望对你有所帮助。本篇的所有例子,都在我开源的flutter_deer中。希望Star、Fork支持,有问题建议可以Issue。附上链接:https://github.com/simplezhli/flutter_deer

    本系列前两篇:

    Flutter开发中的一些Tips

    Flutter开发中的一些Tips(二)

    在这里插入图片描述

    1.多语言配置(本地化)

    默认情况下,Flutter是没有进行多语言配置。所以无论我们的手机系统环境是否是中文,一些Widget的文字都是英文显示。比如常见的输入框(TextField)的操作菜单、日期选择(showDatePicker)上的年月日。

    在这里插入图片描述

    既然没有配置,那我我们添加上即可。

    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', 'CN'),
            const Locale('en', 'US')
          ]
        );
      }
    }
    

    这里我只配置了中英文两个语言的支持。有其他语言的需要可以自行添加。

    1. 如果是iOS平台,还需要在Info.plist文件中做如下配置:


      在这里插入图片描述

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

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

    在这里插入图片描述

    其实翻看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地址,顺手也可以支持一波!

    相关文章

      网友评论

        本文标题:Flutter开发中的一些Tips(三)

        本文链接:https://www.haomeiwen.com/subject/odzpyctx.html