Commit bf8df2fa by tanghuan

支持在指令中设置手机顶部状态栏的图标和文字颜色

1 parent c0cfeaad
...@@ -34,18 +34,11 @@ class App extends StatelessWidget { ...@@ -34,18 +34,11 @@ class App extends StatelessWidget {
), ),
); );
// 全局默认状态栏样式兑底:透明背景 + 深色图标。 // 不再使用全局 AnnotatedRegion 包裹应用。
// 作为最外层 AnnotatedRegion 只挂载一次,不会随页面状态变化反复 // 原因:全局 AnnotatedRegion 会在每帧 drawFrame 时检查并应用其样式,
// 下发命令,避免污染 Android decorView 上 H5 <video> 全屏依赖的 // 导致各页面通过 SystemChrome.setSystemUIOverlayStyle 手动设置的值被覆盖。
// systemUiVisibility flag。带 AppBar 的页面由 AppBar.systemOverlayStyle // 默认状态栏样式改在 main.dart 中通过 SystemChrome 一次性设置,
// 在其 region 区域局部覆盖,隐藏 AppBar 后自动回到本全局值。 // 各页面通过 AppBar.systemOverlayStyle 或手动 SystemChrome 调用管理状态栏。
return AnnotatedRegion<SystemUiOverlayStyle>( return app;
value: const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark,
statusBarBrightness: Brightness.light,
),
child: app,
);
} }
} }
...@@ -40,6 +40,8 @@ class WebState extends Equatable { ...@@ -40,6 +40,8 @@ class WebState extends Equatable {
final String title; final String title;
final int titleColor; final int titleColor;
final int bgColor; final int bgColor;
// 手机状态栏字体和图标的颜色
final int statusBarColor;
final String opIcon; final String opIcon;
final bool setting; final bool setting;
final bool showAppBar; final bool showAppBar;
...@@ -88,6 +90,7 @@ class WebState extends Equatable { ...@@ -88,6 +90,7 @@ class WebState extends Equatable {
this.title = '', this.title = '',
this.titleColor = 0xFFFFFFFF, this.titleColor = 0xFFFFFFFF,
this.bgColor = 0xFF7691FA, this.bgColor = 0xFF7691FA,
this.statusBarColor = 0xFF000000,
this.opIcon = 'none', this.opIcon = 'none',
this.setting = true, this.setting = true,
this.showAppBar = false, this.showAppBar = false,
...@@ -120,6 +123,7 @@ class WebState extends Equatable { ...@@ -120,6 +123,7 @@ class WebState extends Equatable {
String? title, String? title,
int? titleColor, int? titleColor,
int? bgColor, int? bgColor,
int? statusBarColor,
String? opIcon, String? opIcon,
bool? setting, bool? setting,
bool? showAppBar, bool? showAppBar,
...@@ -151,6 +155,7 @@ class WebState extends Equatable { ...@@ -151,6 +155,7 @@ class WebState extends Equatable {
title: title ?? this.title, title: title ?? this.title,
titleColor: titleColor ?? this.titleColor, titleColor: titleColor ?? this.titleColor,
bgColor: bgColor ?? this.bgColor, bgColor: bgColor ?? this.bgColor,
statusBarColor: statusBarColor ?? this.statusBarColor,
opIcon: opIcon ?? this.opIcon, opIcon: opIcon ?? this.opIcon,
setting: setting ?? this.setting, setting: setting ?? this.setting,
showAppBar: showAppBar ?? this.showAppBar, showAppBar: showAppBar ?? this.showAppBar,
...@@ -185,6 +190,7 @@ class WebState extends Equatable { ...@@ -185,6 +190,7 @@ class WebState extends Equatable {
title, title,
titleColor, titleColor,
bgColor, bgColor,
statusBarColor,
opIcon, opIcon,
setting, setting,
showAppBar, showAppBar,
...@@ -672,11 +678,12 @@ class WebCubit extends Cubit<WebState> with WidgetsBindingObserver { ...@@ -672,11 +678,12 @@ class WebCubit extends Cubit<WebState> with WidgetsBindingObserver {
_sendResponse(resp); _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 parsedTitleColor = _hexStringToInt(color);
int parsedBgColor = _hexStringToInt(bgColor); 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; return true;
} }
......
...@@ -24,10 +24,11 @@ class TitleBarHandler extends MessageHandler { ...@@ -24,10 +24,11 @@ class TitleBarHandler extends MessageHandler {
final String title = params['title'] as String; final String title = params['title'] as String;
final String color = params['color'] as String; final String color = params['color'] as String;
final String bgColor = params['bgColor'] 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 String icon = params['icon'] as String;
final bool setting = params['setting'] as bool? ?? true; 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 { } finally {
_unfollowCubit(); _unfollowCubit();
} }
......
...@@ -16,6 +16,17 @@ import 'app.dart'; ...@@ -16,6 +16,17 @@ import 'app.dart';
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
// 设置默认状态栏样式:透明背景 + 深色图标。
// 不使用 app.dart 中的全局 AnnotatedRegion,避免其覆盖各页面的
// 手动 SystemChrome.setSystemUIOverlayStyle 调用。
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.dark,
statusBarBrightness: Brightness.light,
),
);
await setupLocator(); await setupLocator();
await _initAppVersion(); await _initAppVersion();
await _initH5Version(); await _initH5Version();
......
...@@ -5,6 +5,7 @@ import 'package:appframe/config/env_config.dart'; ...@@ -5,6 +5,7 @@ import 'package:appframe/config/env_config.dart';
import 'package:appframe/config/locator.dart'; import 'package:appframe/config/locator.dart';
import 'package:appframe/config/routes.dart'; import 'package:appframe/config/routes.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart' hide MessageHandler;
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
...@@ -119,11 +120,21 @@ class WebPage extends StatelessWidget { ...@@ -119,11 +120,21 @@ class WebPage extends StatelessWidget {
: null, : null,
); );
// 不论 showAppBar 是否为 true,都直接使用 scaffold。 // 当 AppBar 隐藏时,通过 _StatusBarStyleApplier 直接调用
// 原因:Android 平台上 setSystemUIOverlayStyle 会修改 decorView 的 // SystemChrome.setSystemUIOverlayStyle 设置状态栏样式。
// systemUiVisibility flag,污染 H5 <video> 全屏插件赖赖的 flag, // 不使用 AnnotatedRegion 包裹 Scaffold,因为其 layer 直接位于
// 导致全屏画面卡死。状态栏样式应在 main.dart 启动时一次性设置。 // WebView 平台视图上方,H5 <video> 全屏时 layer tree 变化会
final child = scaffold; // 触发 setSystemUIOverlayStyle 重新执行,修改 decorView 的
// systemUiVisibility flag,与 WebView 全屏管理产生冲突导致卡死。
// 当 AppBar 显示时,由 AppBar.systemOverlayStyle 通过其内部
// AnnotatedRegion 局部覆盖状态栏样式(位于 appBar 插槽内,
// 不在 WebView 所在层级,不会干扰全屏)。
final child = _StatusBarStyleApplier(
showAppBar: state.showAppBar,
statusBarColor: state.statusBarColor,
child: scaffold,
);
// ios 不使用 PopScope // ios 不使用 PopScope
if (Platform.isIOS) { if (Platform.isIOS) {
...@@ -234,6 +245,7 @@ class WebPage extends StatelessWidget { ...@@ -234,6 +245,7 @@ class WebPage extends StatelessWidget {
centerTitle: true, centerTitle: true,
automaticallyImplyLeading: false, automaticallyImplyLeading: false,
backgroundColor: Color(state.bgColor), backgroundColor: Color(state.bgColor),
systemOverlayStyle: _computeSystemOverlayStyle(state.statusBarColor),
actionsIconTheme: IconThemeData(color: Color(state.titleColor)), actionsIconTheme: IconThemeData(color: Color(state.titleColor)),
leading: state.opIcon == 'back' leading: state.opIcon == 'back'
? IconButton( ? IconButton(
...@@ -341,3 +353,84 @@ class WebPage extends StatelessWidget { ...@@ -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;
}
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!