Skip to content

Commit 3b846fa

Browse files
committed
添加暗色模式监听
1 parent b21ad4b commit 3b846fa

File tree

2 files changed

+117
-1
lines changed

2 files changed

+117
-1
lines changed

tdesign-component/example/lib/main.dart

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:flutter/foundation.dart' show kIsWeb;
12
import 'package:flutter/material.dart';
23
import 'package:flutter/services.dart';
34
import 'package:provider/provider.dart';
@@ -12,6 +13,9 @@ import 'l10n/app_localizations.dart';
1213
import 'provider/locale_provider.dart';
1314
import 'provider/theme_mode_provider.dart';
1415

16+
// 仅在 Web 平台导入
17+
import 'dart:html' as html if (dart.library.html) 'dart:html';
18+
1519
Future<void> main() async {
1620
WidgetsFlutterBinding.ensureInitialized();
1721

@@ -82,6 +86,13 @@ class _MyAppState extends State<MyApp> {
8286
],
8387
child: Consumer2<ThemeModeProvider, LocaleProvider>(
8488
builder: (context, themeModeProvider, localeProvider, child) {
89+
// 在 Web 平台设置 postMessage 监听
90+
if (PlatformUtil.isWeb) {
91+
WidgetsBinding.instance.addPostFrameCallback((_) {
92+
_setupThemeModeListener(context, themeModeProvider);
93+
});
94+
}
95+
8596
return MaterialApp(
8697
title: 'TDesign Flutter Example',
8798
theme: _themeData.systemThemeDataLight,
@@ -129,4 +140,33 @@ class _MyAppState extends State<MyApp> {
129140
return const {};
130141
}
131142
}
143+
144+
static bool _listenerSetup = false;
145+
146+
void _setupThemeModeListener(
147+
BuildContext context, ThemeModeProvider themeModeProvider) {
148+
// 只设置一次监听器
149+
if (_listenerSetup) return;
150+
_listenerSetup = true;
151+
152+
// 仅在 Web 平台执行
153+
if (!PlatformUtil.isWeb) return;
154+
155+
// ignore: undefined_prefixed_name, avoid_web_libraries_in_flutter
156+
if (kIsWeb) {
157+
html.window.onMessage.listen((event) {
158+
if (event.data is Map) {
159+
final data = event.data as Map;
160+
if (data['type'] == 'theme-mode-change') {
161+
final themeMode = data['themeMode'] as String?;
162+
if (themeMode == 'dark') {
163+
themeModeProvider.themeMode = ThemeMode.dark;
164+
} else if (themeMode == 'light') {
165+
themeModeProvider.themeMode = ThemeMode.light;
166+
}
167+
}
168+
}
169+
});
170+
}
171+
}
132172
}

tdesign-site/site/plugin-tdoc/component.vue

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<div name="DEMO" v-html="info.demoMd"></div>
88
<td-doc-phone headless>
99
<iframe
10+
ref="demoIframe"
1011
:src="liveUrl"
1112
frameborder="0"
1213
width="100%"
@@ -73,7 +74,7 @@ export default defineComponent({
7374
7475
mounted() {
7576
const { info } = this;
76-
const { tdDocContent, tdDocHeader, tdDocTabs } = this.$refs;
77+
const { tdDocContent, tdDocHeader, tdDocTabs, demoIframe } = this.$refs;
7778
7879
if (info.isComponent) {
7980
tdDocTabs.onchange = ({ detail: currentTab }) => (this.tab = currentTab);
@@ -86,6 +87,81 @@ export default defineComponent({
8687
this.$emit('loaded', () => {
8788
tdDocContent.pageStatus = 'show';
8889
});
90+
91+
// 监听暗色模式变化并通知 iframe
92+
if (demoIframe) {
93+
this.setupThemeModeListener(demoIframe);
94+
}
95+
},
96+
97+
beforeUnmount() {
98+
// 清理监听器
99+
const observer = (this as any).themeObserver;
100+
if (observer) {
101+
observer.disconnect();
102+
(this as any).themeObserver = null;
103+
}
104+
},
105+
106+
data() {
107+
return {
108+
themeObserver: null as MutationObserver | null,
109+
};
110+
},
111+
112+
methods: {
113+
setupThemeModeListener(iframe: HTMLIFrameElement) {
114+
// 获取当前主题模式
115+
const getThemeMode = () => {
116+
const root = document.documentElement;
117+
return root.getAttribute('theme-mode') === 'dark' ? 'dark' : 'light';
118+
};
119+
120+
// 发送主题模式到 iframe
121+
const sendThemeMode = () => {
122+
if (iframe && iframe.contentWindow) {
123+
const themeMode = getThemeMode();
124+
iframe.contentWindow.postMessage(
125+
{
126+
type: 'theme-mode-change',
127+
themeMode: themeMode,
128+
},
129+
'*'
130+
);
131+
}
132+
};
133+
134+
// 初始发送一次
135+
iframe.addEventListener('load', () => {
136+
sendThemeMode();
137+
});
138+
139+
// 如果 iframe 已经加载,立即发送
140+
if (iframe.contentWindow) {
141+
sendThemeMode();
142+
}
143+
144+
// 使用 MutationObserver 监听根元素的 theme-mode 属性变化
145+
const observer = new MutationObserver((mutations) => {
146+
mutations.forEach((mutation) => {
147+
if (
148+
mutation.type === 'attributes' &&
149+
mutation.attributeName === 'theme-mode'
150+
) {
151+
sendThemeMode();
152+
}
153+
});
154+
});
155+
156+
// 保存 observer 引用以便清理
157+
(this as any).themeObserver = observer;
158+
159+
// 开始观察根元素的属性变化
160+
observer.observe(document.documentElement, {
161+
attributes: true,
162+
attributeFilter: ['theme-mode'],
163+
});
164+
},
89165
},
90166
});
91167
</script>

0 commit comments

Comments
 (0)