本文的目的为使用Flutter技术时,实现如下效果:
1、安卓或苹果手机为刘海屏手机、虚拟键手机时,在竖屏状态下正常显示刘海屏和虚拟键,沉浸式状态栏
2、之外的手机全屏显示,没有状态栏,虚拟键正常显示
1、安卓端设置
包括识别O版部分机型的刘海屏,P版的所有刘海屏并进行设置。状态栏背景颜色设置为透明
private void useSafeArea(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
getHighNotchParams();
} else {
getLowNotchParams(context);
}
}
public void getLowNotchParams(Context context) {
if (hasNotchInHuawei(context)) {
_topHeight = getNotchSize(context);
} else if (hasNotchInOppo(context)) {
_topHeight = getStatusBarHeight(context);
} else if (hasNotchInVivo(context)) {
_topHeight = getStatusBarHeight(context);
} else if (getInt("ro.miui.notch", context) == 1) {
int resourceId = context.getResources().getIdentifier("notch_height", "dimen", "android");
if (resourceId > 0) {
_topHeight = context.getResources().getDimensionPixelSize(resourceId);
}
}
lastSet(_topHeight > 0);
}
// 是否是小米手机
public static boolean isXiaomi() {
return "Xiaomi".equals(Build.MANUFACTURER);
}
/**
* 小米刘海屏判断.
*
* @return 0 if it is not notch ; return 1 means notch
* @throws IllegalArgumentException if the key exceeds 32 characters
*/
public static int getInt(String key, Context context) {
int result = 0;
if (isXiaomi()) {
try {
ClassLoader classLoader = context.getClassLoader();
@SuppressWarnings("rawtypes")
Class SystemProperties = classLoader.loadClass("android.os.SystemProperties");
//参数类型
@SuppressWarnings("rawtypes")
Class[] paramTypes = new Class[2];
paramTypes[0] = String.class;
paramTypes[1] = int.class;
Method getInt = SystemProperties.getMethod("getInt", paramTypes);
//参数
Object[] params = new Object[2];
params[0] = new String(key);
params[1] = new Integer(0);
result = (Integer) getInt.invoke(SystemProperties, params);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
return result;
}
//判断该华为手机是否刘海屏
public static boolean hasNotchInHuawei(Context context) {
boolean hasNotch = false;
try {
ClassLoader cl = context.getClassLoader();
Class HwNotchSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");
Method hasNotchInScreen = HwNotchSizeUtil.getMethod("hasNotchInScreen");
if (hasNotchInScreen != null) {
hasNotch = (boolean) hasNotchInScreen.invoke(HwNotchSizeUtil);
}
} catch (Exception e) {
e.printStackTrace();
}
return hasNotch;
}
//获取刘海的高度
public static int getNotchSize(Context context) {
int[] ret = new int[]{0, 0};
try {
ClassLoader cl = context.getClassLoader();
Class HwNotchSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");
Method get = HwNotchSizeUtil.getMethod("getNotchSize");
ret = (int[]) get.invoke(HwNotchSizeUtil);
} catch (ClassNotFoundException e) {
Log.e("test", "getNotchSize ClassNotFoundException");
} catch (NoSuchMethodException e) {
Log.e("test", "getNotchSize NoSuchMethodException");
} catch (Exception e) {
Log.e("test", "getNotchSize Exception");
} finally {
return ret[1];
}
}
public static boolean hasNotchInOppo(Context context) {
return context.getPackageManager().hasSystemFeature("com.oppo.feature.screen.heteromorphism");
}
public static int getStatusBarHeight(Context context) {
int statusBarHeight = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
}
return statusBarHeight;
}
public static boolean hasNotchInVivo(Context context) {
boolean hasNotch = false;
try {
ClassLoader cl = context.getClassLoader();
Class ftFeature = cl.loadClass("android.util.FtFeature");
Method[] methods = ftFeature.getDeclaredMethods();
if (methods != null) {
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
if (method != null) {
if (method.getName().equalsIgnoreCase("isFeatureSupport")) {
hasNotch = (boolean) method.invoke(ftFeature, 0x00000020);
break;
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
hasNotch = false;
}
return hasNotch;
}
private int _topHeight = 0;
@TargetApi(28)
public void getHighNotchParams() {
final View decorView = getWindow().getDecorView();
decorView.post(new Runnable() {
@Override
public void run() {
DisplayCutout displayCutout = decorView.getRootWindowInsets().getDisplayCutout();
if (displayCutout != null) {
//是刘海屏。顶部状态栏默认存在,高度传入flutter里
_topHeight = displayCutout.getSafeInsetTop();
}
lastSet(_topHeight > 0);
}
});
}
public void lastSet(boolean isNotch) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
//P+版本下的调整
if (isNotch) {
//有刘海
} else {
//没有刘海,设置全屏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
} else {
//P-版本
if (isNotch) {
//有刘海,不做任何操作,默认
} else {
//不是刘海屏,全屏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
}
//修改状态栏背景颜色为透明
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().setStatusBarColor(0x00000000);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WindowManager.LayoutParams localLayoutParams = getWindow().getAttributes();
localLayoutParams.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | localLayoutParams.flags);
}
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
GeneratedPluginRegistrant.registerWith(MainActivity.this);
}
在AndroidManifest中加入全局代码用于识别华为刘海屏
2、苹果端无需设置
判断是否为苹果刘海屏的方法非常简单,在Flutter中判断即可,无需对IOS代码进行操作
if (Platform.isIOS) {
//手机的状态栏默认为打开的
//判断是否为苹果手机。如果是,并且padding top不为0即为x系列
//其他系列关闭状态栏
if (MediaQuery.of(context).padding.top == null ||
MediaQuery.of(context).padding.top == 0) {
SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.top]);
}
}
3、Flutter设置
在需要适配的页面使用SafeArea
例:
Scaffold(
//沉浸式状态栏在这里设置背景颜色
backgroundColor: Colors.blue,
body: SafeArea(top:true,child:???));//把ui全部包在SafeArea里
MediaQueryData的padding属性会在非全屏模式下得到顶部bar与底部bar的高度,分别体现在top与bottom属性上,如果不为0则表示存在状态栏或虚拟键
SafeArea的高度通过MediaQueryData确认,具体为size.height-padding.top-padding.bottom。