Flutter狀態(tài)欄(Android)解析露戒,不同頁面不同效果優(yōu)雅實(shí)現(xiàn)

一. 狀態(tài)欄交互邏輯

1.FlutterActivity默認(rèn)設(shè)置

FlutterActivity初始化配置狀態(tài)欄腺兴。

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)坡垫,默認(rèn)設(shè)置狀態(tài)欄陰影。

2.Flutter設(shè)置狀態(tài)欄

SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle style);

   static void setSystemUIOverlayStyle(SystemUiOverlayStyle style) {
     //上次設(shè)置的style任務(wù)完成阴汇,不允許加入新的任務(wù),只是修改style的值
     if (_pendingStyle != null) {
       _pendingStyle = style;
       return;
     }
     //防止重復(fù)設(shè)置节槐,PageA-設(shè)置light成功搀庶,PageB-設(shè)置light不執(zhí)行
     if (style == _latestStyle) {
       return;
     }
     _pendingStyle = style;
     scheduleMicrotask(() {
       if (_pendingStyle != _latestStyle) {
         //將style轉(zhuǎn)成map,調(diào)用原生設(shè)置style
         SystemChannels.platform.invokeMethod<void>(
           'SystemChrome.setSystemUIOverlayStyle',
           _pendingStyle!._toMap(),
         );
         //記錄上次設(shè)置的style
         _latestStyle = _pendingStyle;
       }
       _pendingStyle = null;
     });
   }

3.Android設(shè)置Style

   //FlutterActivity
   @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
     ...
     super.onCreate(savedInstanceState);
     
     //創(chuàng)建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構(gòu)造方法
   public PlatformPlugin(
       Activity activity, PlatformChannel platformChannel, PlatformPluginDelegate delegate) {
     this.activity = activity;
     this.platformChannel = platformChannel;
     //mPlatformMessageHandler負(fù)責(zé)處理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();
         }
 
         //真實(shí)處理Flutter設(shè)置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. 混合開發(fā)帶來的問題

如果是混合開發(fā)咆蒿,調(diào)用棧如下:Navtive-FlutterFlutterActivity關(guān)閉后蚂子,再次打開FlutterActivity沃测,在Flutter設(shè)置狀態(tài)欄不生效。

Flutter可使用SystemChrome.restoreSystemUIOverlays();食茎,恢復(fù)最后一次設(shè)置的Style蒂破,源碼如下:

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

Android可在FlutterActivity中調(diào)用updateSystemUiOverlays()恢復(fù)最后一次設(shè)置的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);
     //上次設(shè)置的style
     if (currentTheme != null) {
       setSystemChromeSystemUIOverlayStyle(currentTheme);
     }
   }

二.Flutter不同頁面設(shè)置不同Style實(shí)現(xiàn)

假如有如下邏輯:不同頁面的狀態(tài)欄配置不同别渔。

PageA(light)-PageB(dark)

打開PageA設(shè)置狀態(tài)欄白色圖標(biāo)附迷,打開PageB設(shè)置狀態(tài)欄黑色圖標(biāo),返回到PageA再次設(shè)置狀態(tài)欄白色圖標(biāo)哎媚。

通過監(jiān)聽Flutter頁面變化實(shí)現(xiàn)喇伯,需要使用命名路由:繼承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;
   }
 
   ///設(shè)置亮色狀態(tài)欄和導(dǎo)航欄
   ///android 默認(rèn)statusBarColor = Color(0x40000000)拨与,只對安卓生[L]及以上生效
   final SystemUiOverlayStyle light =
       SystemUiOverlayStyle.light.copyWith(statusBarColor: Colors.transparent);
 
   ///設(shè)置暗色狀態(tài)欄和導(dǎo)航欄
   ///android 默認(rèn)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) {
       //這里沒有調(diào)用Flutter自帶的方法是為了解決混合開發(fā)帶來的問題
       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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市截珍,隨后出現(xiàn)的幾起案子攀甚,更是在濱河造成了極大的恐慌箩朴,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秋度,死亡現(xiàn)場離奇詭異炸庞,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)荚斯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門埠居,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人事期,你說我怎么就攤上這事滥壕。” “怎么了兽泣?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵绎橘,是天一觀的道長。 經(jīng)常有香客問我唠倦,道長称鳞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任稠鼻,我火速辦了婚禮冈止,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘候齿。我一直安慰自己熙暴,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布慌盯。 她就那樣靜靜地躺著怨咪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪润匙。 梳的紋絲不亂的頭發(fā)上诗眨,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天,我揣著相機(jī)與錄音孕讳,去河邊找鬼匠楚。 笑死帝火,一個胖子當(dāng)著我的面吹牛酪耳,可吹牛的內(nèi)容都是我干的晾浴。 我是一名探鬼主播泉坐,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼徒爹!你這毒婦竟也來了网持?” 一聲冷哼從身側(cè)響起尼斧,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎撩穿,沒想到半個月后磷支,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡食寡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年雾狈,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抵皱。...
    茶點(diǎn)故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡善榛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出呻畸,到底是詐尸還是另有隱情移盆,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布伤为,位于F島的核電站咒循,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏钮呀。R本人自食惡果不足惜剑鞍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一昨凡、第九天 我趴在偏房一處隱蔽的房頂上張望爽醋。 院中可真熱鬧,春花似錦便脊、人聲如沸蚂四。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽遂赠。三九已至,卻和暖如春晌杰,著一層夾襖步出監(jiān)牢的瞬間跷睦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工肋演, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留抑诸,地道東北人。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓爹殊,卻偏偏與公主長得像蜕乡,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子梗夸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容