lib/aliplayer_widget.dart (578 lines of code) (raw):
// Copyright © 2025 Alibaba Cloud. All rights reserved.
//
// Author: keria
// Date: 2025/2/6
// Brief: Player Widget, used to play videos
part of 'aliplayer_widget_lib.dart';
/// 一个用于播放视频的 Widget,支持自定义控制器和覆盖层。
///
/// A widget for video playback that supports custom controllers and overlay layers.
class AliPlayerWidget extends StatefulWidget {
/// 视频播放控制器,用于控制视频的播放、暂停等操作。
///
/// The video player controller used to control playback, pause, etc.
final AliPlayerWidgetController _controller;
/// 自定义覆盖层,允许在视频上方显示额外的 UI 元素。
///
/// Custom overlay layers that allow displaying additional UI elements above the video.
final List<Widget> overlays;
/// 构造函数,用于创建 [AliPlayerWidget] 实例。
///
/// Constructor to create an instance of [AliPlayerWidget].
///
/// 参数:
/// - [_controller]:视频播放控制器,必须提供,用于管理视频播放逻辑。
/// - [key]:可选参数,用于标识 Widget 的唯一性。
/// - [overlays]:可选参数,默认为空列表,用于定义覆盖在视频上的 UI 元素。
///
/// Parameters:
/// - _controller: The video player controller, required to manage video playback logic.
/// - key: Optional parameter used to identify the uniqueness of the widget.
/// - overlays: Optional parameter, defaults to an empty list, used to define UI elements overlaid on the video.
const AliPlayerWidget(
this._controller, {
super.key,
this.overlays = const [],
});
@override
State<StatefulWidget> createState() => AliPlayerWidgetState();
}
/// 播放器组件状态
///
/// Player Widget State
class AliPlayerWidgetState extends State<AliPlayerWidget>
with WidgetsBindingObserver, TickerProviderStateMixin {
/// 播放组件控制器
late AliPlayerWidgetController _playController;
/// 共享动画控制器
late SharedAnimationManager _animationManager;
/// 当前播放场景类型
late SceneType _sceneType;
/// 播放状态显示视图的内容类型,默认为 none 不显示
final SafeValueNotifier<ContentViewType> _contentViewTypeNotifier =
SafeValueNotifier(ContentViewType.none);
/// 是否展示设置菜单面板控件
final SafeValueNotifier<bool> _isShowSettingMenuPanelNotifier =
SafeValueNotifier(false);
/// 是否正在拖动进度条
final SafeValueNotifier<bool> _isDraggingNotifier = SafeValueNotifier(false);
/// 当前拖动进度条的时间
final SafeValueNotifier<Duration> _currentSeekTimeNotifier =
SafeValueNotifier(Duration.zero);
/// 构建播放器视图
///
/// Builds the main player view.
@override
Widget build(BuildContext context) {
return PopScope(
canPop: !FullScreenUtil.isFullScreen(), // 如果是全屏模式,则阻止默认返回操作
onPopInvoked: (bool didPop) {
if (didPop) return; // 如果已经处理了返回操作,则直接返回
_handleBackPress();
},
child: _buildContentBody(),
);
}
/// 构建主体内容
Widget _buildContentBody() {
// 根据视频尺寸计算渲染尺寸
return ValueListenableBuilder(
valueListenable: _playController.videoSizeNotifier,
builder: (context, videoSize, __) {
// 如果视频尺寸为空,则不渲染
if (videoSize == Size.zero) {
return const SizedBox.shrink();
}
// 调用工具类计算渲染尺寸
Size playerViewSize = ScreenUtil.calculateRenderSize(
context,
videoSize: videoSize,
isFullScreenMode: FullScreenUtil.isFullScreen(),
);
logi("[build]: videoSize: $videoSize, playerViewSize: $playerViewSize");
final double width = playerViewSize.width;
final double height = playerViewSize.height;
return ConstrainedBox(
constraints: BoxConstraints.tight(playerViewSize),
child: Stack(
children: [
_buildPlaySurfaceView(width, height),
if (_playController._widgetData?.coverUrl.isNotEmpty ?? false)
_buildPlayCoverView(width, height),
_buildPlayControlView(),
_buildTopBarWidget(),
_buildBottomBarWidget(),
if (isNotScene(_playController._widgetData, SceneType.live))
_buildSeekThumbnailWidget(),
_buildCenterDisplayWidget(),
_buildPlayStateView(),
// 添加浮层
..._buildOverlays(),
_buildSettingMenuPanel(),
],
),
);
},
);
}
/// 构建播放器视图
///
/// build player view
Widget _buildPlaySurfaceView(double width, double height) {
return AliPlayerView(
onCreated: (int viewId) => _playController._setPlayerView(viewId),
x: 0,
y: 0,
width: width,
height: height,
);
}
/// 构建封面视图
///
/// build cover view
Widget _buildPlayCoverView(double width, double height) {
// 监听播放器是否渲染完成,如果没有渲染完成,则显示封面图片
return ValueListenableBuilder(
valueListenable: _playController.isRenderedNotifier,
builder: (context, isRendered, __) {
// 如果播放器已经渲染完成,则不显示封面图片
if (isRendered) {
return const SizedBox.shrink();
}
// 获取封面图片的 URL
var imageUrl = _playController._widgetData?.coverUrl ?? "";
return AliPlayerCoverImageWidget(
imageUrl: imageUrl,
width: width,
height: height,
fit: BoxFit.cover,
);
},
);
}
/// 构建播放控制视图
Widget _buildPlayControlView() {
// 长按控制
bool enableLongPress = !_isSceneLive();
// 拖动控制
bool enableDrag = !_isSceneLive();
// 竖向手势控制
bool enableVerticalGestures = !_isSceneListPlayer();
return AliPlayerPlayControlWidget(
autoHide: true,
onVisibilityChanged: (bool isVisible) {
_togglePlayControlVisibility(forceHide: !isVisible);
},
onDoubleTap: _onPlayerViewDoubleTap,
onLongPressStart: enableLongPress ? _onPlayerViewLongPress : null,
onLongPressEnd: enableLongPress ? _onPlayerViewLongPressEnd : null,
onHorizontalDragUpdate:
enableDrag ? _onPlayerViewHorizontalDragUpdate : null,
onHorizontalDragEnd: enableDrag ? _onPlayerViewHorizontalDragEnd : null,
onLeftVerticalDragUpdate:
enableVerticalGestures ? _onPlayerViewLeftVerticalDragUpdate : null,
onLeftVerticalDragEnd:
enableVerticalGestures ? _onPlayerViewLeftVerticalDragEnd : null,
onRightVerticalDragUpdate:
enableVerticalGestures ? _onPlayerViewRightVerticalDragUpdate : null,
onRightVerticalDragEnd:
enableVerticalGestures ? _onPlayerViewRightVerticalDragEnd : null,
);
}
/// 切换播放控制视图的可见性
void _togglePlayControlVisibility({bool forceHide = false}) {
if (forceHide || _animationManager.isVisible) {
_animationManager.hide(); // 隐藏
} else {
_animationManager.show(); // 显示
}
}
/// 构建顶部栏控件
Widget _buildTopBarWidget() {
return Positioned(
top: 0,
left: 0,
right: 0,
child: AliPlayerTopBarWidget(
animationManager: _animationManager,
title: _playController._widgetData?.videoTitle ?? "",
onBackPressed: _handleBackPress,
onSettingsPressed: _toggleSettingMenuPanel,
// TODO keria; download feature to be implemented
isDownload: false,
onDownloadPressed: _isSceneLive() ? null : _onDownloadPressed,
onSnapshotPressed: _isSceneLive() ? null : _onSnapshotPressed,
onPIPPressed: _isSceneLive() ? null : _onPIPPressed,
),
);
}
/// 返回事件处理
bool _handleBackPress() {
if (FullScreenUtil.isFullScreen()) {
// 如果当前是全屏模式,退出全屏
FullScreenUtil.exitFullScreen();
return false; // 阻止默认的返回操作
} else {
// 如果不是全屏模式,执行正常的返回操作
Navigator.of(context).pop();
return true;
}
}
/// 下载按钮点击回调
void _onDownloadPressed(bool value) {
// TODO keria; download feature to be implemented
SnackBarUtil.warning(context, "download feature to be implemented");
}
/// 截图按钮点击回调
void _onSnapshotPressed() {
// TODO keria; snapshot feature to be implemented
SnackBarUtil.warning(context, "snapshot feature to be implemented");
}
/// PIP 按钮点击回调
void _onPIPPressed() {
// TODO keria; PIP feature to be implemented
SnackBarUtil.warning(context, "PIP feature to be implemented");
}
/// 构建底部栏控件
Widget _buildBottomBarWidget() {
// 拖动控制
bool enableDrag = !_isSceneLive();
// seek 控制
bool enableSeek = !_isSceneLive();
// 监听播放状态、当前播放位置、缓冲位置、总时长等
Listenable listenable = Listenable.merge([
_playController.playStateNotifier,
_playController.currentPositionNotifier,
_playController.bufferedPositionNotifier,
_playController.totalDurationNotifier,
]);
return ListenableBuilder(
listenable: listenable,
builder: (context, _) {
// 获取播放状态
final playState = _playController.playStateNotifier.value;
// 获取当前播放位置、缓冲位置、总时长
final currentPosition = _playController.currentPositionNotifier.value;
final bufferedPosition = _playController.bufferedPositionNotifier.value;
final totalDuration = _playController.totalDurationNotifier.value;
return Positioned(
bottom: 0,
left: 0,
right: 0,
child: AliPlayerBottomBarWidget(
animationManager: _animationManager,
isPlaying: playState == FlutterAvpdef.started,
currentPosition: currentPosition,
bufferedPosition: bufferedPosition,
totalDuration: totalDuration,
onPlayIconPressed: _onPlayerViewTap,
onFullScreenPressed: _onFullScreenPressed,
onDragUpdate: enableDrag ? _onDragUpdate : null,
onDragEnd: enableDrag ? _onDragEnd : null,
onSeekEnd: enableSeek ? _onSeekEnd : null,
),
);
},
);
}
/// 全屏状态切换
void _onFullScreenPressed() {
FullScreenUtil.toggleFullScreen();
}
/// 拖拽进度更新回调
void _onDragUpdate(Duration duration) {
_playController.requestThumbnailBitmap(duration);
_isDraggingNotifier.value = true;
_currentSeekTimeNotifier.value = duration;
}
/// 拖拽进度结束回调
void _onDragEnd(Duration duration) {
_isDraggingNotifier.value = false;
_currentSeekTimeNotifier.value = Duration.zero;
}
/// 拖拽进度结束回调
void _onSeekEnd(Duration duration) {
_playController.seek(duration);
}
/// 构建设置菜单面板控件
Widget _buildSettingMenuPanel() {
// 监听设置面板的可见性
return ValueListenableBuilder(
valueListenable: _isShowSettingMenuPanelNotifier,
builder: (context, visible, __) {
return AliPlayerSettingMenuPanel(
isVisible: visible,
onVisibilityChanged: (bool isVisible) {
_isShowSettingMenuPanelNotifier.value = isVisible;
},
settingItems: _buildSettingItems(),
);
},
);
}
/// 构建设置菜单面板控件
List<SettingItem> _buildSettingItems() {
return [
// 构建声音滑块控件
SettingItem(
type: SettingItemType.slider,
text: "声音",
startIcon: Icons.volume_down_rounded,
endIcon: Icons.volume_up_rounded,
initialValue: _playController.volumeNotifier.value,
onChanged: (value) => _playController.setVolume(value),
),
// 构建亮度滑块控件
SettingItem(
type: SettingItemType.slider,
text: "亮度",
startIcon: Icons.brightness_low_rounded,
endIcon: Icons.brightness_high_rounded,
initialValue: _playController.brightnessNotifier.value,
onChanged: (value) => _playController.setBrightness(value),
),
// 构建倍速选择控件
if (!_isSceneLive())
SettingItem(
type: SettingItemType.selector,
text: "倍速",
startIcon: Icons.speed_rounded,
options: SettingConstants.speedOptions,
initialValue: _playController.speedNotifier.value,
onChanged: (value) => _playController.setSpeed(speed: value),
displayFormatter: (option) => "${option}x",
),
// 构建清晰度选择控件
if (!_isSceneLive())
SettingItem(
type: SettingItemType.selector,
text: "清晰度",
startIcon: Icons.hd_rounded,
options: _playController.trackInfoListNotifier.value,
initialValue: _playController.currentTrackInfoNotifier.value,
onChanged: (value) => _playController.selectTrack(value),
displayFormatter: (option) => TrackInfoUtil.getQuality(option),
),
// 构建循环播放开关控件
if (!_isSceneLive())
SettingItem(
type: SettingItemType.switcher,
text: "循环播放",
startIcon: Icons.loop_rounded,
initialValue: _playController.isLoopNotifier.value,
onChanged: (value) => _playController.setLoop(value),
),
// 构建静音播放开关控件
SettingItem(
type: SettingItemType.switcher,
text: "静音播放",
startIcon: _playController.isMuteNotifier.value
? Icons.volume_off_rounded
: Icons.volume_up_rounded,
initialValue: _playController.isMuteNotifier.value,
onChanged: (value) => _playController.setMute(value),
),
// 构建镜像模式选择控件
SettingItem(
type: SettingItemType.selector,
text: "镜像模式",
startIcon: Icons.swap_horiz_rounded,
options: SettingConstants.mirrorModeOptions,
initialValue: _playController.mirrorModeNotifier.value,
onChanged: (value) => _playController.setMirrorMode(value),
displayFormatter: (option) => FormatUtil.formatMirrorMode(option),
),
// 构建旋转模式选择控件
SettingItem(
type: SettingItemType.selector,
text: "旋转模式",
startIcon: Icons.crop_rotate_rounded,
options: SettingConstants.rotateModeOptions,
initialValue: _playController.rotateModeNotifier.value,
onChanged: (value) => _playController.setRotateMode(value),
displayFormatter: (option) => "$option°",
),
// 构建渲染填充选择控件
SettingItem(
type: SettingItemType.selector,
text: "渲染填充",
startIcon: Icons.crop_rounded,
options: SettingConstants.scaleModeOptions,
initialValue: _playController.scaleModeNotifier.value,
onChanged: (value) => _playController.setScaleMode(value),
displayFormatter: (option) => FormatUtil.formatScaleMode(option),
),
];
}
/// 调起设置菜单面板控件
void _toggleSettingMenuPanel() {
_togglePlayControlVisibility(forceHide: true);
_isShowSettingMenuPanelNotifier.value = true;
}
/// 构建 seek 缩略图控件
Widget _buildSeekThumbnailWidget() {
// 监听播放进度、总时长、是否正在拖拽、当前拖拽进度等
Listenable listener = Listenable.merge([
_playController.totalDurationNotifier,
_playController.thumbnailNotifier,
_isDraggingNotifier,
_currentSeekTimeNotifier,
]);
return ListenableBuilder(
listenable: listener,
builder: (context, _) {
final totalDuration = _playController.totalDurationNotifier.value;
final thumbnail = _playController.thumbnailNotifier.value;
final isDragging = _isDraggingNotifier.value;
final currentSeekTime = _currentSeekTimeNotifier.value;
return Positioned(
bottom: 30,
left: 0,
right: 0,
child: AliPlayerSeekThumbnailWidget(
isVisible: isDragging,
currentSeekTime: currentSeekTime,
totalDuration: totalDuration,
thumbnail: thumbnail,
),
);
},
);
}
/// 构建播放速度显示视图
Widget _buildSpeedDisplayView() {
// 监听播放速度
return ValueListenableBuilder(
valueListenable: _playController.speedNotifier,
builder: (context, speed, __) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 10.0,
horizontal: 20.0,
),
child: Text(
'$speed倍速',
style: const TextStyle(
color: Colors.black,
fontSize: 16,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
);
},
);
}
/// 构建声音滑块控件
Widget _buildVolumeSlider() {
// 监听音量状态
return ValueListenableBuilder(
valueListenable: _playController.volumeNotifier,
builder: (builder, volume, __) {
return AliPlayerCustomSliderWidget(
text: "声音",
startIcon: Icons.volume_down_rounded,
endIcon: Icons.volume_up_rounded,
initialValue: volume,
isInteractive: false,
);
},
);
}
/// 构建亮度滑块控件
Widget _buildBrightnessSlider() {
// 监听亮度状态
return ValueListenableBuilder(
valueListenable: _playController.brightnessNotifier,
builder: (context, brightness, __) {
return AliPlayerCustomSliderWidget(
text: "亮度",
startIcon: Icons.brightness_low_rounded,
endIcon: Icons.brightness_high_rounded,
initialValue: brightness,
isInteractive: false,
);
},
);
}
/// 构建中心显示控件
Widget _buildCenterDisplayWidget() {
// 监听播放状态显示视图的内容类型
return ValueListenableBuilder(
valueListenable: _contentViewTypeNotifier,
builder: (context, contentViewType, __) {
// 如果不显示任何内容,则返回空组件
if (contentViewType == ContentViewType.none) {
return const SizedBox.shrink();
}
return AliPlayerCenterDisplayWidget(
contentWidget: _buildCenterDisplayContentWidget(contentViewType),
);
},
);
}
/// 根据 _centerDisplayViewContentType 获取对应的内容组件
Widget _buildCenterDisplayContentWidget(ContentViewType contentViewType) {
switch (contentViewType) {
case ContentViewType.brightness:
return _buildBrightnessSlider();
case ContentViewType.volume:
return _buildVolumeSlider();
case ContentViewType.speed:
return _buildSpeedDisplayView();
default: // 不显示时返回空组件
return const SizedBox.shrink();
}
}
/// 切换显示内容类型
void _toggleCenterDisplayContentType(ContentViewType contentType) {
_contentViewTypeNotifier.value = contentType;
}
/// 播放器视图点击回调
void _onPlayerViewTap() {
_togglePlayControlVisibility(forceHide: true);
_playController.togglePlayState();
}
/// 播放器视图双击回调
void _onPlayerViewDoubleTap() {
_playController.togglePlayState();
}
/// 播放器视图长按回调
void _onPlayerViewLongPress() {
// 触发设备振动
VibrationUtil.vibrate(duration: 100);
// 设置显示内容类型为倍速
_toggleCenterDisplayContentType(ContentViewType.speed);
_playController.setSpeed(speed: 2.0);
}
/// 播放器视图长按结束回调
void _onPlayerViewLongPressEnd() {
// 触发设备振动
VibrationUtil.vibrate(duration: 50);
// 设置显示内容类型为空
_toggleCenterDisplayContentType(ContentViewType.none);
_playController.setSpeed();
}
/// 播放器视图水平拖动回调
void _onPlayerViewHorizontalDragUpdate(double delta) {
// 触发设备振动
VibrationUtil.vibrate(duration: 50);
// Update dragging state.
_isDraggingNotifier.value = true;
// Calculate based on delta and total duration.
final totalDuration = _playController.totalDurationNotifier.value;
final currentPosition = _playController.currentPositionNotifier.value;
Duration seekDuration = currentPosition + totalDuration * delta;
// Clamp the value within valid range.
int seekMills = seekDuration.inMilliseconds.clamp(
0,
totalDuration.inMilliseconds,
);
// Update current seek time.
var seekTimeDuration = Duration(milliseconds: seekMills);
_currentSeekTimeNotifier.value = seekTimeDuration;
// Request thumbnail update.
_playController.requestThumbnailBitmap(seekTimeDuration);
}
/// 播放器视图水平拖动结束回调
void _onPlayerViewHorizontalDragEnd(double delta) {
// 触发设备振动
VibrationUtil.vibrate(duration: 100);
// Seek to the current seek time.
final currentSeekTime = _currentSeekTimeNotifier.value;
_playController.seek(currentSeekTime);
// Reset dragging state.
_isDraggingNotifier.value = false;
_currentSeekTimeNotifier.value = Duration.zero;
}
/// 播放器视图左垂直拖动回调
void _onPlayerViewLeftVerticalDragUpdate(double delta) {
// 触发设备振动
VibrationUtil.vibrate(duration: 50);
// 设置显示内容类型为亮度
_toggleCenterDisplayContentType(ContentViewType.brightness);
_playController.setBrightnessWithDelta(delta);
}
/// 播放器视图左垂直拖动结束回调
void _onPlayerViewLeftVerticalDragEnd(double delta) {
// 触发设备振动
VibrationUtil.vibrate(duration: 100);
// 设置显示内容类型为空
_toggleCenterDisplayContentType(ContentViewType.none);
}
/// 播放器视图右垂直拖动回调
void _onPlayerViewRightVerticalDragUpdate(double delta) {
// 触发设备振动
VibrationUtil.vibrate(duration: 50);
// 设置显示内容类型为声音
_toggleCenterDisplayContentType(ContentViewType.volume);
_playController.setVolumeWithDelta(delta);
}
/// 播放器视图右垂直拖动结束回调
void _onPlayerViewRightVerticalDragEnd(double delta) {
// 触发设备振动
VibrationUtil.vibrate(duration: 100);
// 设置显示内容类型为空
_toggleCenterDisplayContentType(ContentViewType.none);
}
/// 构建播放状态视图
Widget _buildPlayStateView() {
// 监听播放状态、错误码、错误信息等变化
Listenable listenable = Listenable.merge([
_playController.playStateNotifier,
_playController.playErrorNotifier,
]);
return ListenableBuilder(
listenable: listenable,
builder: (context, _) {
// 获取播放状态
final playState = _playController.playStateNotifier.value;
// 如果不需要构建播放状态视图,则返回空组件
if (!playState.shouldBuildWidget) {
return const SizedBox.shrink();
}
// 获取错误码和错误信息
final playError = _playController.playErrorNotifier.value;
final errorCode = playError?.keys.firstOrNull;
final errorMsg = playError?.values.firstOrNull;
return AliPlayerPlayStateWidget(
errorCode: errorCode,
errorMsg: errorMsg,
);
},
);
}
/// 构建浮层视图
List<Widget> _buildOverlays() {
return widget.overlays;
}
/// 判断当前场景是否为直播
bool _isSceneLive() {
return _sceneType == SceneType.live;
}
/// 判断当前场景是否为列表播放
bool _isSceneListPlayer() {
return _sceneType == SceneType.listPlayer;
}
/// 初始化状态
/// StatefulWidget 的状态类中第一个被调用的方法,用于初始化状态,可以执行一些一次性的初始化工作
///
/// Called when the state is first created. Used for one-time initialization.
@override
void initState() {
super.initState();
logi("[lifecycle] initState");
/// 获取播放控制器
_playController = widget._controller;
/// 初始化共享动画管理器
_animationManager = SharedAnimationManager(this);
/// 初始化场景类型
_sceneType = _playController._widgetData?.sceneType ?? SceneType.vod;
/// 添加观察者
WidgetsBinding.instance.addObserver(this);
}
/// 清理资源
/// 在 StatefulWidget 被从树中移除并销毁时调用的,这个方法用于清理资源。
///
/// Called when the widget is removed from the tree permanently. Used to release resources.
@override
void dispose() {
logi("[lifecycle] dispose");
// 销毁 ValueNotifier
_disposeValueNotifiers();
// 销毁共享动画管理器
_animationManager.dispose();
// 移除观察者
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
/// 销毁 ValueNotifier
void _disposeValueNotifiers() {
_contentViewTypeNotifier.dispose();
_isShowSettingMenuPanelNotifier.dispose();
_isDraggingNotifier.dispose();
_currentSeekTimeNotifier.dispose();
}
/// 状态更新回调
/// 当 Widget 的状态被更新时,该方法被调用。
///
/// Called when the state of the widget is updated.
@override
void didUpdateWidget(covariant AliPlayerWidget oldWidget) {
super.didUpdateWidget(oldWidget);
logi("[lifecycle] didUpdateWidget");
// 如果控制器发生变化,则更新内部状态
if (widget._controller != oldWidget._controller) {
_playController = widget._controller;
setState(() {});
}
}
/// 应用程序生命周期变化回调
/// 当应用程序生命周期的状态发生变化时(如暂停、恢复),该方法被调用。
///
/// Called when the application's lifecycle state changes (e.g., paused, resumed).
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
switch (state) {
case AppLifecycleState.resumed:
logi("App resumed");
break;
case AppLifecycleState.inactive:
logi("App inactive");
break;
case AppLifecycleState.paused:
logi("App paused");
break;
case AppLifecycleState.detached:
logi("App detached");
break;
case AppLifecycleState.hidden:
logi("App hidden");
break;
}
}
/// 窗口尺寸变化回调
/// 当窗口尺寸变化时被调用,通常是旋转设备或者是窗口大小调整时。
///
/// Called when the window size changes (e.g., device rotation or resizing).
@override
void didChangeMetrics() {
super.didChangeMetrics();
loge("Window metrics changed");
}
/// 主题模式变化回调
/// 当系统的主题模式(亮/暗模式)发生变化时调用。
///
/// Called when the system theme mode changes (light/dark mode).
@override
void didChangePlatformBrightness() {
super.didChangePlatformBrightness();
var widgetsBinding = WidgetsBinding.instance;
final brightness = widgetsBinding.platformDispatcher.platformBrightness;
final themeMode = (brightness == Brightness.light ? 'Light' : 'Dark');
loge("Theme mode changed to $themeMode");
}
}