fix: 顶栏颜色跟随小程序主题并保留跟随系统

This commit is contained in:
2026-08-06 19:54:05 +08:00
parent 9716148f15
commit f4f54a8720
84 changed files with 484 additions and 124 deletions
+66 -31
View File
@@ -1,6 +1,7 @@
import { createContext, useContext, useState, useCallback, useEffect } from 'react'
import Taro, { useDidShow } from '@tarojs/taro'
import { getTheme, setTheme as saveTheme, resolveTheme, type ThemeMode } from '../utils/store'
import { View } from '@tarojs/components'
import { getTheme, setTheme as saveTheme, resolveTheme, setDetectedSystemTheme, type ThemeMode } from '../utils/store'
import { applyPageBackground } from '../utils/themeBackground'
interface ThemeContextValue {
@@ -19,16 +20,14 @@ const ThemeContext = createContext<ThemeContextValue>({
export { ThemeContext }
type ThemeChangeListener = (result: { theme?: string }) => void
type ThemeChangeApi = {
onThemeChange?: (listener: ThemeChangeListener) => void
offThemeChange?: (listener: ThemeChangeListener) => void
}
/** 主题广播事件名:自定义 tabBar 等无法继承 React Context 的独立组件通过它同步主题 */
export const THEME_CHANGE_EVENT = 'themeChange'
type ThemeChangeApi = {
onThemeChange?: (listener: (res: { theme?: string }) => void) => void
offThemeChange?: (listener: (res: { theme?: string }) => void) => void
}
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, set] = useState<ThemeMode>(getTheme())
const [resolvedTheme, setResolved] = useState<'light' | 'dark'>(resolveTheme(getTheme()))
@@ -48,6 +47,57 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
setTheme(next)
}, [resolvedTheme, setTheme])
/**
* darkmode 只作为系统主题探测通道;选中“跟随系统”时才跟着系统切换,
* 固定浅色/深色时由 setTheme 直接决定,不被动绑定系统。
*/
const updateFromSystem = useCallback((t: 'light' | 'dark') => {
setDetectedSystemTheme(t)
if (getTheme() === 'auto') {
setResolved(t)
}
}, [])
const detectSystemTheme = useCallback(() => {
try {
const info = Taro.getAppBaseInfo()
updateFromSystem(info.theme === 'dark' ? 'dark' : 'light')
} catch {
/* ignore */
}
// CSS 探测器作为兜底:个别机型 getAppBaseInfo().theme 延迟时再读一次
try {
Taro.createSelectorQuery()
.select('.system-theme-detector')
.fields({ computedStyle: ['opacity'] })
.exec((res: any) => {
const opacity = res && res[0] && res[0].computedStyle && res[0].computedStyle.opacity
updateFromSystem(opacity === '1' ? 'dark' : 'light')
})
} catch {
/* ignore */
}
}, [updateFromSystem])
useEffect(() => {
detectSystemTheme()
}, [detectSystemTheme])
// 系统实时切换深浅色时更新“跟随系统”模式
useEffect(() => {
const listener = (res: { theme?: string }) => {
if (res && (res.theme === 'dark' || res.theme === 'light')) {
updateFromSystem(res.theme)
}
}
const themeApi = Taro as typeof Taro & ThemeChangeApi
themeApi.onThemeChange?.(listener)
return () => {
themeApi.offThemeChange?.(listener)
}
}, [updateFromSystem])
// 广播实际生效主题,供 custom-tab-bar 等独立组件订阅
useEffect(() => {
Taro.eventCenter.trigger(THEME_CHANGE_EVENT, resolvedTheme)
@@ -59,35 +109,20 @@ export function ThemeProvider({ children }: { children: React.ReactNode }) {
applyPageBackground(resolvedTheme)
}, [resolvedTheme])
// 页面每次显示时(从小程序后台切回前台)重新测系统主题
// 页面每次显示时(从小程序后台切回前台)重新测系统主题
useDidShow(() => {
if (theme === 'auto') {
setResolved(resolveTheme('auto'))
detectSystemTheme()
}
})
// auto模式下监听系统主题变化
useEffect(() => {
if (theme === 'auto') {
// 先同步一次当前系统主题
setResolved(resolveTheme('auto'))
const listener: ThemeChangeListener = (res) => {
setResolved(res.theme === 'dark' ? 'dark' : 'light')
}
const themeApi = Taro as typeof Taro & ThemeChangeApi
themeApi.onThemeChange?.(listener)
return () => {
themeApi.offThemeChange?.(listener)
}
} else {
updateResolved(theme)
}
}, [theme, updateResolved])
return (
<ThemeContext.Provider value={{ theme, resolvedTheme, toggleTheme, setTheme }}>
{children}
</ThemeContext.Provider>
<>
<View className='system-theme-detector' />
<ThemeContext.Provider value={{ theme, resolvedTheme, toggleTheme, setTheme }}>
{children}
</ThemeContext.Provider>
</>
)
}