前言

Flutter开发中,涉及到适配相关的解决方案,持续更新~

已适配

  • 本地图片资源多分辨率适配
  • 启动图适配
  • 深色模式适配

本地图片资源多分辨率适配

本地图片资源适配,资源目录假设为assets/images

支持多倍图,常见的2倍和3倍,可以在images目录下新建2.0x和3.0x文件夹分别放同名文件

在pubspec.yaml中配置assets

1
2
3
4
assets:
- assets/images/
- assets/images/2.0x/
- assets/images/3.0x/

images/目录下代表1倍图,如果需要像上述配置,必须在该目录下配置1倍图。【有些UI没有切1倍图,我的做法是把二倍图全部拷到images目录下充当1倍图,因为现在需要1倍图的手机几乎没有了】

经过实践,多倍图2.0x 3.0x中也可以添加文件夹,方便归类。(上述的做法是将所有的图片放到了不同分辨率目录下而已)

最佳实践应该是通过在不同分辨率文件夹下建立分类文件夹,结构如下:

资源目录配置

1
2
3
4
5
6
7
8
9
10
11
12
13
- assets
- images // 存放本地图片资源文件夹
- type1
- type2 // 分类文件夹,和2.0x和3.0x中的文件夹名一致。注意这些文件在是和2.0x和3.0x同级的,代表1倍图,必须添加!
- typeN
- 2.0x // 二倍图存放处
- type1
- type2 // 分类文件夹,和3.0x和images中(代表1倍图)的文件夹名一致
- typeN
- 3.0x // 三倍图存放处
- type1
- type2 // 分类文件夹,和2.0x和images中(代表1倍图)的文件夹名一致
- typeN

pubspec.yaml配置

在pubspec.yaml中配置图片路径时,如果是配的文件夹,一定不要忘了加’/‘

1
2
3
4
5
6
7
8
9
10
assets:
- assets/images/type1/
- assets/images/type2/ # 1倍图配置【一定不要忘了加'/'】
- assets/images/typeN/
- assets/images/2.0x/type1/
- assets/images/2.0x/type2/ # 2倍图配置【一定不要忘了加'/'】
- assets/images/2.0x/typeN/
- assets/images/3.0x/type1/
- assets/images/3.0x/type2/ # 3倍图配置【一定不要忘了加'/'】
- assets/images/3.0x/typeN/

备用,不推荐 如果在一倍图即assets/images目录下没有配置图片,则要在pubspec.yaml中把图片名挨个声明一次(不推荐,太繁琐,不整洁),如:

1
2
3
4
5
6
7
8
9
10
11
assets:
- assets/images/
- assets/images/tab_chat_off.png # 即使images目录下没有该图片,也需要声明,Flutter通过该名字查找,找不到去找其他倍数的资源
- assets/images/tab_chat_on.png
- assets/images/tab_discover_off.png
- assets/images/tab_discover_on.png
- assets/images/tab_me_off.png
- assets/images/tab_square_off.png
- assets/images/tab_square_on.png
- assets/images/2.0x/
- assets/images/3.0x/

启动图适配

实现效果:启动图一启动,全屏显示,状态栏也不会显示,程序启动后,显示状态栏,不出现启动图跳跃的问题

安卓

  1. android > app > src > main > res > values > styles.xml【设置LaunchTheme】
1
2
3
4
5
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">@drawable/launch_background</item>
<!--加上这句配置,确保LaunchTheme和NormalTheme过渡没有跳一下的感觉-->
<item name="android:windowDisablePreview">true</item>
</style>
  1. android > app > src > main > res > drawable > launch_background.xml【配置启动图样式】
1
2
3
4
5
6
7
8
9
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 背景色,我们设置了启动图就可以不用 -->
<!-- <item android:drawable="@android:color/white" /> -->
<item>
<bitmap
android:gravity="fill" <!-- 设置启动图的伸展方式 -->
android:src="@mipmap/splash" /> <!-- 指定启动图,请在不同分辨率目录下防止不同的图片,这里的启动图名字叫splash -->
</item>
</layer-list>
  1. android > app > src > main > AndroidManifest.xml【检查设置launch_background是否配置】
1
2
3
4
5
6
7
<activity
...其他配置...
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background" /> <!-- 使用drawable/launch_background作为启动图,一般默认会配置 -->
...其他配置...
</activity>
  1. android > app > src > main > 找到MainActivity.kt【设置状态栏的隐藏于显示控制代码 不同IDE该文件的路径显示不同,能找到就行】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 引入头文件
import android.os.Bundle
import android.view.WindowManager

// 重写onCreate和onFlutterUiDisplayed方法,控制状态栏显隐
class MainActivity: FlutterActivity() {
// ...

/// 启动默认隐藏状态栏
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// window.statusBarColor = 0x00000000; // 修改装填栏颜色,如有需求
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}

/// 显示Flutter界面后显示状态栏
override fun onFlutterUiDisplayed() {
super.onFlutterUiDisplayed()
window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
}
}
  1. 备用其他设置。因为不同的Flutter版本,配置好像也不一样,以下配置记录备用,在没有达到效果的情况下可尝试添加

    1. 使用NormalTheme,Flutter启动是先走LaunchTheme,然后走NormalTheme,如果没有NormalTheme,则会继续显示LaunchTheme,所以按道理来说上述四点设置了就OK。添加NormalTheme的地方有:

      styles.xml中:

    1
    2
    3
    <style name="NormalTheme" parent="Theme.MaterialComponents.DayNight">
    <item name="android:windowBackground">@drawable/normal_background</item>
    </style>

    ​ drawable目录下,即launch_background.xml同级目录添加normal_background.xml,内容和launch_background一样

    ​ AndroidManifest.xml中:

    1
    2
    3
    <meta-data
    android:name="io.flutter.embedding.android.NormalTheme"
    android:resource="@style/NormalTheme" />
    1. AndroidManifest.xml中设置SplashScreenUntilFirstFrame
    1
    2
    3
    <meta-data
    android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
    android:value="true" />
  2. 参考

    flutter_native_splash插件实现方式,查看器代码工程配置,但是是老版本,设置状态栏的显示和隐藏方法不一样,用的老API

iOS

  1. LaunchScreen.storyboard中配置启动图,提供@2x和@3x,图片ContentMode设置为AspectFill
  2. 工程名 > General > Deployment Info中勾选Hide status bar,隐藏状态栏
  3. AppDelegate中,在didFinishLaunchingWithOptions中加入显示状态栏代码:
1
UIApplication.shared.setStatusBarHidden(false, with: .fade)

深色模式适配

  1. 在顶层一般为MyApp中对MaterialApp的theme/darkTheme/themeMode进行包裹监听比如使用Provider,这样在应用内改变了就可以及时通知,使得整个应用响应theme变更,进行重新渲染
  2. MaterialApp中theme参数对应的是light主题的配置,可以配置整个应用浅色相关的themeData
  3. MaterialApp中darkTheme参数对应的是dark主题的配置,可以配置整个应用深色相关的themeData
  4. MaterialApp中themeMode代表当前系统的颜色主题风格,有三种可选,light-浅色,dark-深色,system-跟随系统,正是通过这个themeMode做到颜色主题的判断进行构建。如果是跟随系统,则如果在设置更改了颜色模式,则再次进入App会自动检测到变更进行刷新。如果是在应用中设置了light或dark,则下次启动是从本地读取上次保存的颜色主题,确定themeMode,是light或者dark就不会跟随系统变化而变化【保存和读取配置一般通过shared_preferences等进行操作】
  5. 通过Theme.of(context).brightness == Brightness.dark进行颜色主题的判断,除了顶层配置的themeData(只能设置一些系统级的主题如导航栏、标题,分割线等),在实际业务中也需要适配,通过判断是否是dark进行不同颜色、不同图片的代码逻辑适配。
  6. Theme.of(context).brightness == Brightness.dark中Brightness.dark是根据themeMode来判断的,是light就是light,是dark就是dark,如果是system就会根据系统设置而定

TODO: 补上深色适配的代码