Skip to content
Toggle navigation
Toggle navigation
This project
Loading...
Sign in
ethan
/
appframe
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Network
Compare
Branches
Tags
Commit bf8df2fa
authored
2026-08-25 10:58:24 +0800
by
tanghuan
Browse Files
Options
Browse Files
Tag
Download
Email Patches
Plain Diff
支持在指令中设置手机顶部状态栏的图标和文字颜色
1 parent
c0cfeaad
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
126 additions
and
21 deletions
lib/app.dart
lib/bloc/web_cubit.dart
lib/data/repositories/message/title_bar_handler.dart
lib/main.dart
lib/ui/pages/web_page.dart
lib/app.dart
View file @
bf8df2f
...
...
@@ -34,18 +34,11 @@ class App extends StatelessWidget {
),
);
// 全局默认状态栏样式兑底:透明背景 + 深色图标。
// 作为最外层 AnnotatedRegion 只挂载一次,不会随页面状态变化反复
// 下发命令,避免污染 Android decorView 上 H5 <video> 全屏依赖的
// systemUiVisibility flag。带 AppBar 的页面由 AppBar.systemOverlayStyle
// 在其 region 区域局部覆盖,隐藏 AppBar 后自动回到本全局值。
return
AnnotatedRegion
<
SystemUiOverlayStyle
>(
value:
const
SystemUiOverlayStyle
(
statusBarColor:
Colors
.
transparent
,
statusBarIconBrightness:
Brightness
.
dark
,
statusBarBrightness:
Brightness
.
light
,
),
child:
app
,
);
// 不再使用全局 AnnotatedRegion 包裹应用。
// 原因:全局 AnnotatedRegion 会在每帧 drawFrame 时检查并应用其样式,
// 导致各页面通过 SystemChrome.setSystemUIOverlayStyle 手动设置的值被覆盖。
// 默认状态栏样式改在 main.dart 中通过 SystemChrome 一次性设置,
// 各页面通过 AppBar.systemOverlayStyle 或手动 SystemChrome 调用管理状态栏。
return
app
;
}
}
lib/bloc/web_cubit.dart
View file @
bf8df2f
...
...
@@ -40,6 +40,8 @@ class WebState extends Equatable {
final
String
title
;
final
int
titleColor
;
final
int
bgColor
;
// 手机状态栏字体和图标的颜色
final
int
statusBarColor
;
final
String
opIcon
;
final
bool
setting
;
final
bool
showAppBar
;
...
...
@@ -88,6 +90,7 @@ class WebState extends Equatable {
this
.
title
=
''
,
this
.
titleColor
=
0xFFFFFFFF
,
this
.
bgColor
=
0xFF7691FA
,
this
.
statusBarColor
=
0xFF000000
,
this
.
opIcon
=
'none'
,
this
.
setting
=
true
,
this
.
showAppBar
=
false
,
...
...
@@ -120,6 +123,7 @@ class WebState extends Equatable {
String
?
title
,
int
?
titleColor
,
int
?
bgColor
,
int
?
statusBarColor
,
String
?
opIcon
,
bool
?
setting
,
bool
?
showAppBar
,
...
...
@@ -151,6 +155,7 @@ class WebState extends Equatable {
title:
title
??
this
.
title
,
titleColor:
titleColor
??
this
.
titleColor
,
bgColor:
bgColor
??
this
.
bgColor
,
statusBarColor:
statusBarColor
??
this
.
statusBarColor
,
opIcon:
opIcon
??
this
.
opIcon
,
setting:
setting
??
this
.
setting
,
showAppBar:
showAppBar
??
this
.
showAppBar
,
...
...
@@ -185,6 +190,7 @@ class WebState extends Equatable {
title
,
titleColor
,
bgColor
,
statusBarColor
,
opIcon
,
setting
,
showAppBar
,
...
...
@@ -672,11 +678,12 @@ class WebCubit extends Cubit<WebState> with WidgetsBindingObserver {
_sendResponse
(
resp
);
}
bool
setTitleBar
(
bool
hidden
,
String
title
,
String
color
,
String
bgColor
,
String
icon
,
bool
setting
)
{
bool
setTitleBar
(
bool
hidden
,
String
title
,
String
color
,
String
bgColor
,
String
statusBarColor
,
String
icon
,
bool
setting
)
{
int
parsedTitleColor
=
_hexStringToInt
(
color
);
int
parsedBgColor
=
_hexStringToInt
(
bgColor
);
int
parsedStatusBarColor
=
_hexStringToInt
(
statusBarColor
);
emit
(
state
.
copyWith
(
showAppBar:
!
hidden
,
title:
title
,
titleColor:
parsedTitleColor
,
bgColor:
parsedBgColor
,
opIcon:
icon
,
setting:
setting
));
emit
(
state
.
copyWith
(
showAppBar:
!
hidden
,
title:
title
,
titleColor:
parsedTitleColor
,
bgColor:
parsedBgColor
,
statusBarColor:
parsedStatusBarColor
,
opIcon:
icon
,
setting:
setting
));
return
true
;
}
...
...
lib/data/repositories/message/title_bar_handler.dart
View file @
bf8df2f
...
...
@@ -24,10 +24,11 @@ class TitleBarHandler extends MessageHandler {
final
String
title
=
params
[
'title'
]
as
String
;
final
String
color
=
params
[
'color'
]
as
String
;
final
String
bgColor
=
params
[
'bgColor'
]
as
String
;
final
String
statusBarColor
=
params
[
'statusBarColor'
]
as
String
?
??
'#000000'
;
final
String
icon
=
params
[
'icon'
]
as
String
;
final
bool
setting
=
params
[
'setting'
]
as
bool
?
??
true
;
return
_webCubit
!.
setTitleBar
(
hidden
,
title
,
color
,
bgColor
,
icon
,
setting
);
return
_webCubit
!.
setTitleBar
(
hidden
,
title
,
color
,
bgColor
,
statusBarColor
,
icon
,
setting
);
}
finally
{
_unfollowCubit
();
}
...
...
lib/main.dart
View file @
bf8df2f
...
...
@@ -16,6 +16,17 @@ import 'app.dart';
void
main
(
)
async
{
WidgetsFlutterBinding
.
ensureInitialized
();
// 设置默认状态栏样式:透明背景 + 深色图标。
// 不使用 app.dart 中的全局 AnnotatedRegion,避免其覆盖各页面的
// 手动 SystemChrome.setSystemUIOverlayStyle 调用。
SystemChrome
.
setSystemUIOverlayStyle
(
const
SystemUiOverlayStyle
(
statusBarColor:
Colors
.
transparent
,
statusBarIconBrightness:
Brightness
.
dark
,
statusBarBrightness:
Brightness
.
light
,
),
);
await
setupLocator
();
await
_initAppVersion
();
await
_initH5Version
();
...
...
lib/ui/pages/web_page.dart
View file @
bf8df2f
...
...
@@ -5,6 +5,7 @@ import 'package:appframe/config/env_config.dart';
import
'package:appframe/config/locator.dart'
;
import
'package:appframe/config/routes.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/services.dart'
hide
MessageHandler
;
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:go_router/go_router.dart'
;
import
'package:shared_preferences/shared_preferences.dart'
;
...
...
@@ -119,11 +120,21 @@ class WebPage extends StatelessWidget {
:
null
,
);
// 不论 showAppBar 是否为 true,都直接使用 scaffold。
// 原因:Android 平台上 setSystemUIOverlayStyle 会修改 decorView 的
// systemUiVisibility flag,污染 H5 <video> 全屏插件赖赖的 flag,
// 导致全屏画面卡死。状态栏样式应在 main.dart 启动时一次性设置。
final
child
=
scaffold
;
// 当 AppBar 隐藏时,通过 _StatusBarStyleApplier 直接调用
// SystemChrome.setSystemUIOverlayStyle 设置状态栏样式。
// 不使用 AnnotatedRegion 包裹 Scaffold,因为其 layer 直接位于
// WebView 平台视图上方,H5 <video> 全屏时 layer tree 变化会
// 触发 setSystemUIOverlayStyle 重新执行,修改 decorView 的
// systemUiVisibility flag,与 WebView 全屏管理产生冲突导致卡死。
// 当 AppBar 显示时,由 AppBar.systemOverlayStyle 通过其内部
// AnnotatedRegion 局部覆盖状态栏样式(位于 appBar 插槽内,
// 不在 WebView 所在层级,不会干扰全屏)。
final
child
=
_StatusBarStyleApplier
(
showAppBar:
state
.
showAppBar
,
statusBarColor:
state
.
statusBarColor
,
child:
scaffold
,
);
// ios 不使用 PopScope
if
(
Platform
.
isIOS
)
{
...
...
@@ -234,6 +245,7 @@ class WebPage extends StatelessWidget {
centerTitle:
true
,
automaticallyImplyLeading:
false
,
backgroundColor:
Color
(
state
.
bgColor
),
systemOverlayStyle:
_computeSystemOverlayStyle
(
state
.
statusBarColor
),
actionsIconTheme:
IconThemeData
(
color:
Color
(
state
.
titleColor
)),
leading:
state
.
opIcon
==
'back'
?
IconButton
(
...
...
@@ -341,3 +353,84 @@ class WebPage extends StatelessWidget {
}
}
}
/// 根据 statusBarColor 计算状态栏文字和图标的颜色样式。
/// statusBarIconBrightness 用于 Android,statusBarBrightness 用于 iOS(两者取反)。
SystemUiOverlayStyle
_computeSystemOverlayStyle
(
int
colorValue
)
{
final
Color
color
=
Color
(
colorValue
);
final
Brightness
iconBrightness
=
color
.
computeLuminance
()
>
0.5
?
Brightness
.
light
:
Brightness
.
dark
;
return
SystemUiOverlayStyle
(
statusBarColor:
Colors
.
transparent
,
statusBarIconBrightness:
iconBrightness
,
statusBarBrightness:
iconBrightness
==
Brightness
.
light
?
Brightness
.
dark
:
Brightness
.
light
,
);
}
/// 当 AppBar 隐藏时,通过直接调用 SystemChrome.setSystemUIOverlayStyle
/// 设置状态栏样式,替代 AnnotatedRegion 包裹方案。
///
/// 不使用 AnnotatedRegion 的原因:包裹 Scaffold 的 AnnotatedRegion 其 layer
/// 直接位于 WebView 平台视图上方,H5 <video> 全屏时 layer tree 变化会触发
/// setSystemUIOverlayStyle 重新执行,修改 decorView 的 systemUiVisibility flag,
/// 与 WebView 全屏管理产生冲突导致画面卡死。
///
/// 本组件仅在 statusBarColor 实际变化或从“显示 AppBar”切换到“隐藏 AppBar”时,
/// 通过 addPostFrameCallback 在帧后调用 SystemChrome,避免在 paint 阶段干扰全屏。
/// 当 AppBar 显示时,由 AppBar.systemOverlayStyle 通过其内部 AnnotatedRegion 管理。
class
_StatusBarStyleApplier
extends
StatefulWidget
{
final
bool
showAppBar
;
final
int
statusBarColor
;
final
Widget
child
;
const
_StatusBarStyleApplier
({
required
this
.
showAppBar
,
required
this
.
statusBarColor
,
required
this
.
child
,
});
@override
State
<
_StatusBarStyleApplier
>
createState
()
=>
_StatusBarStyleApplierState
();
}
class
_StatusBarStyleApplierState
extends
State
<
_StatusBarStyleApplier
>
{
bool
_lastShowAppBar
=
true
;
int
?
_lastAppliedColor
;
@override
void
initState
()
{
super
.
initState
();
_maybeApplyStyle
();
}
@override
void
didUpdateWidget
(
_StatusBarStyleApplier
oldWidget
)
{
super
.
didUpdateWidget
(
oldWidget
);
_maybeApplyStyle
();
}
/// 当 AppBar 隐藏时,检查是否需要重新应用状态栏样式。
/// 仅在 statusBarColor 变化或从“显示 AppBar”切换到“隐藏 AppBar”时触发,
/// 避免在 H5 视频全屏期间产生不必要的 setSystemUIOverlayStyle 调用。
void
_maybeApplyStyle
()
{
final
shouldApply
=
!
widget
.
showAppBar
&&
(
widget
.
statusBarColor
!=
_lastAppliedColor
||
_lastShowAppBar
);
_lastShowAppBar
=
widget
.
showAppBar
;
if
(
shouldApply
)
{
_lastAppliedColor
=
widget
.
statusBarColor
;
WidgetsBinding
.
instance
.
addPostFrameCallback
((
_
)
{
if
(!
mounted
||
widget
.
showAppBar
)
return
;
SystemChrome
.
setSystemUIOverlayStyle
(
_computeSystemOverlayStyle
(
widget
.
statusBarColor
),
);
});
}
}
@override
Widget
build
(
BuildContext
context
)
=>
widget
.
child
;
}
Write
Preview
Styling with
Markdown
is supported
Attach a file
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to post a comment