美文网首页
Flutter状态栏(Android)解析,不同页面不同效果优雅

Flutter状态栏(Android)解析,不同页面不同效果优雅

作者: 爱抖腿的飞飞 | 来源:发表于2022-01-20 17:21 被阅读0次

一. 状态栏交互逻辑

1.FlutterActivity默认设置

FlutterActivity初始化配置状态栏。

FlutterActivity-onCreate()-configureStatusBarForFullscreenFlutterExperience()

     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
       Window window = getWindow();
       window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
       window.setStatusBarColor(0x40000000);
       window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI);
     }

Android版本大于等于Android5.0(API-21),默认设置状态栏阴影。

2.Flutter设置状态栏

SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle style);

   static void setSystemUIOverlayStyle(SystemUiOverlayStyle style) {
     //上次设置的style任务完成,不允许加入新的任务,只是修改style的值
     if (_pendingStyle != null) {
       _pendingStyle = style;
       return;
     }
     //防止重复设置,PageA-设置light成功,PageB-设置light不执行
     if (style == _latestStyle) {
       return;
     }
     _pendingStyle = style;
     scheduleMicrotask(() {
       if (_pendingStyle != _latestStyle) {
         //将style转成map,调用原生设置style
         SystemChannels.platform.invokeMethod<void>(
           'SystemChrome.setSystemUIOverlayStyle',
           _pendingStyle!._toMap(),
         );
         //记录上次设置的style
         _latestStyle = _pendingStyle;
       }
       _pendingStyle = null;
     });
   }

3.Android设置Style

   //FlutterActivity
   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
     ...
     super.onCreate(savedInstanceState);
     
     //创建Delegate,里面包含Plugin
     delegate = new FlutterActivityAndFragmentDelegate(this);
     delegate.onAttach(this);
     delegate.onRestoreInstanceState(savedInstanceState);
     ...
   }
   
   //FlutterActivityAndFragmentDelegate#onAttach(Context)
   void onAttach(@NonNull Context context) {
     ...
     //host为FlutterActivity
     platformPlugin = host.providePlatformPlugin(host.getActivity(), flutterEngine);
 
     host.configureFlutterEngine(flutterEngine);
   }
   
   //FlutterActivity
   public PlatformPlugin providePlatformPlugin(
       @Nullable Activity activity, @NonNull FlutterEngine flutterEngine) {
     return new PlatformPlugin(getActivity(), flutterEngine.getPlatformChannel(), this);
   }
   
   //PlatformPlugin构造方法
   public PlatformPlugin(
       Activity activity, PlatformChannel platformChannel, PlatformPluginDelegate delegate) {
     this.activity = activity;
     this.platformChannel = platformChannel;
     //mPlatformMessageHandler负责处理Flutter传递的事件,中间省略部分赋值代码~
     this.platformChannel.setPlatformMessageHandler(mPlatformMessageHandler);
     this.platformPluginDelegate = delegate;
 
     mEnabledOverlays = DEFAULT_SYSTEM_UI;
   }

mPlatformMessageHandler源码

   final PlatformChannel.PlatformMessageHandler mPlatformMessageHandler =
       new PlatformChannel.PlatformMessageHandler() {
         @Override
         public void playSystemSound(@NonNull PlatformChannel.SoundType soundType) {
           PlatformPlugin.this.playSystemSound(soundType);
         }
 
         @Override
         public void vibrateHapticFeedback(
             @NonNull PlatformChannel.HapticFeedbackType feedbackType) {
           PlatformPlugin.this.vibrateHapticFeedback(feedbackType);
         }
 
         @Override
         public void setPreferredOrientations(int androidOrientation) {
           setSystemChromePreferredOrientations(androidOrientation);
         }
 
         @Override
         public void setApplicationSwitcherDescription(
             @NonNull PlatformChannel.AppSwitcherDescription description) {
           setSystemChromeApplicationSwitcherDescription(description);
         }
 
         @Override
         public void showSystemOverlays(@NonNull List<PlatformChannel.SystemUiOverlay> overlays) {
           setSystemChromeEnabledSystemUIOverlays(overlays);
         }
 
         @Override
         public void showSystemUiMode(@NonNull PlatformChannel.SystemUiMode mode) {
           setSystemChromeEnabledSystemUIMode(mode);
         }
 
         @Override
         public void setSystemUiChangeListener() {
           setSystemChromeChangeListener();
         }
 
         @Override
         public void restoreSystemUiOverlays() {
           restoreSystemChromeSystemUIOverlays();
         }
 
         //真实处理Flutter设置style的方法
         //SystemChannels.platform.invokeMethod<void>(
         //  'SystemChrome.setSystemUIOverlayStyle',
         //  _pendingStyle!._toMap(),
         // );
         @Override
         public void setSystemUiOverlayStyle(
             @NonNull PlatformChannel.SystemChromeStyle systemUiOverlayStyle) {
           setSystemChromeSystemUIOverlayStyle(systemUiOverlayStyle);
         }
 
         @Override
         public void popSystemNavigator() {
           PlatformPlugin.this.popSystemNavigator();
         }
 
         @Override
         public CharSequence getClipboardData(
             @Nullable PlatformChannel.ClipboardContentFormat format) {
           return PlatformPlugin.this.getClipboardData(format);
         }
 
         @Override
         public void setClipboardData(@NonNull String text) {
           PlatformPlugin.this.setClipboardData(text);
         }
 
         @Override
         public boolean clipboardHasStrings() {
           return PlatformPlugin.this.clipboardHasStrings();
         }
       };
 

setSystemChromeSystemUIOverlayStyle源码如下,这块就是纯安卓代码了。

   private void setSystemChromeSystemUIOverlayStyle(
       PlatformChannel.SystemChromeStyle systemChromeStyle) {
     Window window = activity.getWindow();
     View view = window.getDecorView();
     WindowInsetsControllerCompat windowInsetsControllerCompat =
         new WindowInsetsControllerCompat(window, view);
 
     // SYSTEM STATUS BAR -------------------------------------------------------------------
     // You can't change the color of the system status bar until SDK 21, and you can't change the
     // color of the status icons until SDK 23. We only allow both starting at 23 to ensure buttons
     // and icons can be visible when changing the background color.
     // If transparent, SDK 29 and higher may apply a translucent scrim behind the bar to ensure
     // proper contrast. This can be overridden with
     // SystemChromeStyle.systemStatusBarContrastEnforced.
     if (Build.VERSION.SDK_INT >= 23) {
       if (systemChromeStyle.statusBarIconBrightness != null) {
         switch (systemChromeStyle.statusBarIconBrightness) {
           case DARK:
             // Dark status bar icon brightness.
             // Light status bar appearance.
             windowInsetsControllerCompat.setAppearanceLightStatusBars(true);
             break;
           case LIGHT:
             // Light status bar icon brightness.
             // Dark status bar appearance.
             windowInsetsControllerCompat.setAppearanceLightStatusBars(false);
             break;
         }
       }
 
       if (systemChromeStyle.statusBarColor != null) {
         window.setStatusBarColor(systemChromeStyle.statusBarColor);
       }
     }
     // You can't override the enforced contrast for a transparent status bar until SDK 29.
     // This overrides the translucent scrim that may be placed behind the bar on SDK 29+ to ensure
     // contrast is appropriate when using full screen layout modes like Edge to Edge.
     if (systemChromeStyle.systemStatusBarContrastEnforced != null && Build.VERSION.SDK_INT >= 29) {
       window.setStatusBarContrastEnforced(systemChromeStyle.systemStatusBarContrastEnforced);
     }
 
     // SYSTEM NAVIGATION BAR --------------------------------------------------------------
     // You can't change the color of the system navigation bar until SDK 21, and you can't change
     // the color of the navigation buttons until SDK 26. We only allow both starting at 26 to
     // ensure buttons can be visible when changing the background color.
     // If transparent, SDK 29 and higher may apply a translucent scrim behind 2/3 button navigation
     // bars to ensure proper contrast. This can be overridden with
     // SystemChromeStyle.systemNavigationBarContrastEnforced.
     if (Build.VERSION.SDK_INT >= 26) {
       if (systemChromeStyle.systemNavigationBarIconBrightness != null) {
         switch (systemChromeStyle.systemNavigationBarIconBrightness) {
           case DARK:
             // Dark navigation bar icon brightness.
             // Light navigation bar appearance.
             windowInsetsControllerCompat.setAppearanceLightNavigationBars(true);
             break;
           case LIGHT:
             // Light navigation bar icon brightness.
             // Dark navigation bar appearance.
             windowInsetsControllerCompat.setAppearanceLightNavigationBars(false);
             break;
         }
       }
 
       if (systemChromeStyle.systemNavigationBarColor != null) {
         window.setNavigationBarColor(systemChromeStyle.systemNavigationBarColor);
       }
     }
     // You can't change the color of the navigation bar divider color until SDK 28.
     if (systemChromeStyle.systemNavigationBarDividerColor != null && Build.VERSION.SDK_INT >= 28) {
       window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
       window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
       window.setNavigationBarDividerColor(systemChromeStyle.systemNavigationBarDividerColor);
     }
 
     // You can't override the enforced contrast for a transparent navigation bar until SDK 29.
     // This overrides the translucent scrim that may be placed behind 2/3 button navigation bars on
     // SDK 29+ to ensure contrast is appropriate when using full screen layout modes like
     // Edge to Edge.
     if (systemChromeStyle.systemNavigationBarContrastEnforced != null
         && Build.VERSION.SDK_INT >= 29) {
       window.setNavigationBarContrastEnforced(
           systemChromeStyle.systemNavigationBarContrastEnforced);
     }
 
     currentTheme = systemChromeStyle;
   }

4. 混合开发带来的问题

如果是混合开发,调用栈如下:Navtive-FlutterFlutterActivity关闭后,再次打开FlutterActivity,在Flutter设置状态栏不生效。

Flutter可使用SystemChrome.restoreSystemUIOverlays();,恢复最后一次设置的Style,源码如下:

   static Future<void> restoreSystemUIOverlays() async {
     await SystemChannels.platform.invokeMethod<void>(
       'SystemChrome.restoreSystemUIOverlays',
       null,
     );
   }

Android可在FlutterActivity中调用updateSystemUiOverlays()恢复最后一次设置的Style。源码如下:

  //FlutterActivity 
  @Override
   public void updateSystemUiOverlays() {
     if (delegate != null) {
       delegate.updateSystemUiOverlays();
     }
   }
   
   //FlutterActivityAndFragmentDelegate
   void updateSystemUiOverlays() {
     if (platformPlugin != null) {
       // TODO(mattcarroll): find a better way to handle the update of UI overlays than calling
       // through to platformPlugin. We're implicitly entangling the Window, Activity,
       // Fragment, and engine all with this one call.
       platformPlugin.updateSystemUiOverlays();
     }
   }
   
   //PlatformPlugin
   public void updateSystemUiOverlays() {
     activity.getWindow().getDecorView().setSystemUiVisibility(mEnabledOverlays);
     //上次设置的style
     if (currentTheme != null) {
       setSystemChromeSystemUIOverlayStyle(currentTheme);
     }
   }

二.Flutter不同页面设置不同Style实现

假如有如下逻辑:不同页面的状态栏配置不同。

PageA(light)-PageB(dark)

打开PageA设置状态栏白色图标,打开PageB设置状态栏黑色图标,返回到PageA再次设置状态栏白色图标。

通过监听Flutter页面变化实现,需要使用命名路由:继承NavigatorObserver,代码如下:

 void main() {
   runApp(MaterialApp(
       ...
       routes: {
         "/A": (context) => PageA(),
         "/B": (context) => PageB(),
       },
       navigatorObservers: [RouteObserver()],
   ));
 }
 
 class RouteObserver extends NavigatorObserver {
 
   final Map<String, SystemUiOverlayStyle> _map = {};
 
   RouteObserver() {
     _map["/"] = dark;
     _map["/A"] = light;
     _map["/B"] = dark;
   }
 
   ///设置亮色状态栏和导航栏
   ///android 默认statusBarColor = Color(0x40000000),只对安卓生[L]及以上生效
   final SystemUiOverlayStyle light =
       SystemUiOverlayStyle.light.copyWith(statusBarColor: Colors.transparent);
 
   ///设置暗色状态栏和导航栏
   ///android 默认statusBarColor = Color(0x40000000),只对安卓生[L]及以上生效
   final SystemUiOverlayStyle dark =
       SystemUiOverlayStyle.dark.copyWith(statusBarColor: Colors.transparent);
 
   @override
   void didPop(Route route, Route? previousRoute) {
     _setSystemUIOverlayStyle(previousRoute, route);
   }
 
   @override
   void didPush(Route route, Route? previousRoute) {
     _setSystemUIOverlayStyle(route, previousRoute);
   }
 
   @override
   void didReplace({Route? newRoute, Route? oldRoute}) {
     _setSystemUIOverlayStyle(newRoute, oldRoute);
   }
 
   void _setSystemUIOverlayStyle(Route? toRoute, Route? formRoute) {
     String? toName = _getRoutNameFromRoute(toRoute);
     String? formName = _getRoutNameFromRoute(formRoute);
     if (toName == null) {
       return;
     }
     SystemUiOverlayStyle? toStyle = _map[toName];
     SystemUiOverlayStyle? fromStyle = _map[formName];
     if (toStyle != null && toStyle != fromStyle) {
       //这里没有调用Flutter自带的方法是为了解决混合开发带来的问题
       SystemChannels.platform.invokeMethod<void>(
         'SystemChrome.setSystemUIOverlayStyle',
         _style2Map(toStyle),
       );
     }
   }
 
   Map<String, dynamic> _style2Map(SystemUiOverlayStyle style) {
     return <String, dynamic>{
       'systemNavigationBarColor': style.systemNavigationBarColor?.value,
       'systemNavigationBarDividerColor':
           style.systemNavigationBarDividerColor?.value,
       'systemStatusBarContrastEnforced': style.systemStatusBarContrastEnforced,
       'statusBarColor': style.statusBarColor?.value,
       'statusBarBrightness': style.statusBarBrightness?.toString(),
       'statusBarIconBrightness': style.statusBarIconBrightness?.toString(),
       'systemNavigationBarIconBrightness':
           style.systemNavigationBarIconBrightness?.toString(),
       'systemNavigationBarContrastEnforced':
           style.systemNavigationBarContrastEnforced,
     };
     SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light);
     SystemChrome.restoreSystemUIOverlays();
   }
 
   String? _getRoutNameFromRoute(Route? route) {
     if (route == null) {
       return null;
     }
     RouteSettings routeSettings = route.settings;
     return routeSettings.name;
   }
 }

源代码见GitHub

相关文章

  • Flutter状态栏(Android)解析,不同页面不同效果优雅

    一. 状态栏交互逻辑 1.FlutterActivity默认设置 FlutterActivity初始化配置状态栏。...

  • 在已有Android项目中使用Flutter

    实现效果,在已存在的android项目中接入flutter,即android调用开启flutter页面(使用and...

  • 浏览器兼容

    1.什么是 CSS hack 由于不同厂商的浏览器不同,对于css的解析不同,页面效果不一样。所以针对不同的浏览器...

  • mui页面传值失败及解决办法

    传值 接收 传值改变状态栏-从不同页面返回设置不同的状态栏颜色 打印提示 isTrusted":false原因:需...

  • 最全!Android 开发状态栏配色详解

    感觉 Android 状态栏一直是一个坑啊!!并且国内不同 Android 的手机厂商也对状态栏做了不同的适配和修...

  • Android 状态栏配色详解

    感觉 Android 状态栏一直是一个坑啊!!并且国内不同 Android 的手机厂商也对状态栏做了不同的适配和修...

  • CSS hack技巧大全

    一、什么是CSS Hack? 不同的浏览器对CSS的解析结果是不同的,因此会导致相同的CSS输出的页面效果不同,这...

  • flutter doctor mac下不通过

    现象:flutter SDK升级后Mac 下 flutter doctor 不同 Android Studio (...

  • 判断环境为iOS或Android

    因在Hybird开发中,iOS与Android的浏览器内核的不同,对页面的各种行为也会有不同的解析情况,所以我们需...

  • 浏览器兼容

    什么是CSS hack? 不同浏览器,或者相同浏览器的不同版本,对CSS的解析可能不同,导致页面展现效果不一致。为...

网友评论

      本文标题:Flutter状态栏(Android)解析,不同页面不同效果优雅

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