Flutter 使用SafeArea终结沉浸式状态栏、刘海屏与虚拟键之乱(竖屏篇)

本文的目的为使用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。

你可能感兴趣的:(Flutter 使用SafeArea终结沉浸式状态栏、刘海屏与虚拟键之乱(竖屏篇))