nextjs+material UI实现换肤功能
一、搭建nextjs开发环境
1、创建一个命名为nextjs-mui-theme的项目
pnpm dlx create-next-app@14.2.16 nextjs-mui-theme
注释:这是使用的是nextjs的14.2.16版本
2、安装material-ui依赖
pnpm add @mui/material @emotion/react @emotion/styled
material-ui官网
3、安装Roboto font和nextjs兼容mui所需的依赖
pnpm add @fontsource/roboto
pnpm add @mui/material-nextjs @emotion/cache
安装完毕后,在layout布局页面引入字体,以及引入这个依赖:
import '@fontsource/roboto/300.css';
import '@fontsource/roboto/400.css';
import '@fontsource/roboto/500.css';
import '@fontsource/roboto/700.css';
import { AppRouterCacheProvider } from '@mui/material-nextjs/v15-appRouter';
import type { Metadata } from 'next';
import './globals.css';
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app'
};
export default function RootLayout({
children
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en" suppressContentEditableWarning>
<body>
{/* 调用我们自己的css层,因此这选择关闭 */}
<AppRouterCacheProvider>{children}</AppRouterCacheProvider>
</body>
</html>
);
}
在 HTML 或 React 中,suppressContentEditableWarning
属性的作用是 抑制内容可编辑元素 (contentEditable
) 的警告。
4、安装material icon
pnpm add @mui/icons-material
然后在globals.css当中引入material icon的样式:
@import url(https://fonts.googleapis.com/icon?family=Material+Icons);
@tailwind base;
@tailwind components;
@tailwind utilities;
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
html,
body {
max-width: 100vw;
overflow-x: hidden;
}
.myBg {
color: var(--mui-palette-common-background);
}
@layer utilities {
.text-balance {
text-wrap: balance;
}
}
注意@import url(https://fonts.googleapis.com/icon?family=Material+Icons);引入要放到tailwindcss上面,否则会爆出错误
二、自定义material主题
1、创建主题文件
在src目录下创建上下文context目录,在该目录下创建一个AppThemeContext.tsx文件,代码如下:
'use client';
import { createTheme, CssBaseline, responsiveFontSizes } from '@mui/material';
import { ThemeProvider } from '@mui/material/styles';
import type {} from '@mui/material/themeCssVarsAugmentation';
import { createContext, useContext, useMemo } from 'react';
export const AppThemeContext = createContext(null);
const AppThemeProvider = ({
children
}: Readonly<{
children: React.ReactNode;
}>) => {
const theme = useMemo(() => {
return responsiveFontSizes(
createTheme({
// 使用CSS变量的有点,防止暗黑模式SS、闪烁
cssVariables: {
colorSchemeSelector: 'class',
disableCssColorScheme: true
},
palette: {
mode: 'light',
primary: {
main: 'rgb(10, 18, 42)',
contrastText: 'rgb(255, 255, 255)'
},
secondary: {
main: 'rgb(27, 59, 111)',
contrastText: 'rgb(255, 255, 255)'
}
},
// 在这里你可以定义不同的颜色方案
colorSchemes: {
// 这是亮色的配置设置
light: {
palette: {
mode: 'light',
primary: {
main: 'rgb(10, 18, 42)'
},
secondary: {
main: 'rgb(27, 59, 111)'
}
}
},
// 这是深色的配置设置
dark: {
palette: {
mode: 'dark',
primary: {
main: 'rgb(10, 18, 42)'
},
secondary: {
main: 'rgb(27, 59, 111)'
}
}
}
}
})
);
}, []);
return (
<AppThemeContext.Provider value={null}>
{/* defaultMode默认是system,可选项有system、light、dark ;disableTransitionOnChange禁用切换动画*/}
<ThemeProvider defaultMode="dark" theme={theme} disableTransitionOnChange>
{/*在所有设备上, 启用颜色方案 */}
<CssBaseline enableColorScheme />
{children}
</ThemeProvider>
</AppThemeContext.Provider>
);
};
export const useAppThemeContext = () => useContext(AppThemeContext);
export default AppThemeProvider;
然后引入到layout布局组件当中:
import AppThemeProvider from '@/context/AppThemeContext';
import '@fontsource/roboto/300.css';
import '@fontsource/roboto/400.css';
import '@fontsource/roboto/500.css';
import '@fontsource/roboto/700.css';
import { AppRouterCacheProvider } from '@mui/material-nextjs/v15-appRouter';
import InitColorSchemeScript from '@mui/material/InitColorSchemeScript';
import type { Metadata } from 'next';
import './globals.css';
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app'
};
export default function RootLayout({
children
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en" suppressContentEditableWarning>
<body>
{/* 调用我们自己的css层,因此这选择关闭 */}
<AppRouterCacheProvider options={{ enableCssLayer: false }}>
<AppThemeProvider>
<InitColorSchemeScript defaultMode="dark" attribute="class" />
{children}
</AppThemeProvider>
</AppRouterCacheProvider>
</body>
</html>
);
}
2、创建切换主题按钮
在src下创建components文件夹,定一个ToggleModeColor.tsx组件:
'use client';
import DarkModeOutlinedIcon from '@mui/icons-material/DarkModeOutlined';
import LightModeOutlinedIcon from '@mui/icons-material/LightModeOutlined';
import { Box, IconButton } from '@mui/material';
import { useColorScheme } from '@mui/material/styles';
import { useCallback } from 'react';
const ToggleModeColor = () => {
const { mode, setMode } = useColorScheme();
const toggleDarkMode = useCallback(() => {
if (mode) {
const currMode = mode === 'light' ? 'dark' : 'light';
setMode(currMode);
}
}, [mode, setMode]);
return (
<Box sx={{ flexGrow: 0, pr: 2 }}>
<IconButton
size="large"
aria-label="account of current user"
aria-controls="menu-appbar"
aria-haspopup="true"
onClick={toggleDarkMode}
color="inherit"
>
{mode === 'dark' ? <DarkModeOutlinedIcon /> : <LightModeOutlinedIcon />}
</IconButton>
</Box>
);
};
export default ToggleModeColor;
三、重启运行