요즘 어플리케이션들 보면 라이트/다크모드 기능은 거의 기본적으로 탑재하는 것 같습니다. 그런데 Flutter 공부하면서 여러 유투브나 구글링, 스텍센세에게 물어봐도 앱이 실행될 때의 토글만 있지 다른 상용 어플처럼 어플리케이션을 껐다가 재실행 해도 테마모드 설정을 기억해서 라이트/다크모드를 설정하는 예제는 잘 없더라구요.
실제 기기에서 테스트해보지 않았습니다. 하지만 시뮬레이터에서 테마모드 설정 후 어플을 껐다가 다시 실행할 경우, 다시 빌드했을 경우 설정해놓은 테마모드가 잘 적용되었습니다.
필수 패키지들을 설치합니다.
flutter pub add shared_preferences
flutter pub add provider
main의 MaterialApp에 추가하기 위해 간단하게 ligthTheme과 darkTheme을 만듭니다.
class MyThemes {
static final lightTheme = ThemeData().copyWith(
primaryColor: Colors.purple,
);
static final darkTheme = ThemeData.dark().copyWith(
primaryColor: Colors.purple,
);
}
Flutter의 ChangeNotifier를 활용하기 위해 ThemeProvider를 작성합니다.
구글의 많은 예제를 보면 isDark 등으로 toggleTheme() 함수를 작성합니다. 하지만 저는 light/dark 모드만이 아니라 '시스템(테마) 설정과 같이' 도 추가하고 싶었습니다 ^^
때문에 shared_preferences로 저장할 키값을 themeMode로 하고 벨류값을 String 타입의 light/dark/system으로 설정했습니다.
코드를 대충 작동하게만 작성해서.. 본인 스타일에 맞게 정리하시면 됩니다 ㅎㅎ..
class ThemeProvider extends ChangeNotifier {
ThemeMode themeMode = ThemeMode.light;
// themeMode를 편하게 String으로 바꿔 저장하려고 replaceAll 함수를 사용했습니다.
setThemeMode(ThemeMode newThemeMode) {
themeMode = newThemeMode;
saveThemeModePrefs(themeMode.toString().replaceAll("ThemeMode.", ""));
notifyListeners();
}
saveThemeModePrefs(String value) async {
final prefs = await SharedPreferences.getInstance();
// key값은 "themeMode", 저장하는 value값은 String 타입의 "light" or "dark" or "system" 입니다.
await prefs.setString("themeMode", value);
}
ThemeProvider({ThemeMode initThemeMode = ThemeMode.light}) {
themeMode = initThemeMode;
}
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// shared_preferences 초기화.
final prefs = await SharedPreferences.getInstance();
// ThemeMode값 초기화. 기본값은 light로 정했습니다.
ThemeMode themeMode = ThemeMode.light;
// 저장된 테마모드 가져오기.
final String? savedThemeMode = prefs.getString('themeMode');
// 기존 themeMode 설정을 안해놨을 경우(null) 시작 테마를 light로 지정합니다.
// savedThemeMode가 null이 아닐 경우 저장된 테마모드에 따라 themeMode를 설정합니다.
if (savedThemeMode == null) {
themeMode = ThemeMode.light;
} else if (savedThemeMode == "light") {
themeMode = ThemeMode.light;
} else if (savedThemeMode == "dark") {
themeMode = ThemeMode.dark;
} else if (savedThemeMode == "system") {
themeMode = ThemeMode.system;
}
// 설정된, 또는 불러온 테마모드를 MyApp에 넘겨줍니다.
runApp(MyApp(themeMode: themeMode));
}
class MyApp extends StatelessWidget {
// 테마모드를 ThemeProvider에 넘겨주기 위해 main() 함수로부터 themeMode 파라미터를 받습니다.
final themeMode;
const MyApp({
Key? key,
required this.themeMode,
}) : super(key: key);
Widget build(BuildContext context) {
// Provider 사용.
return MultiProvider(
providers: [
ChangeNotifierProvider(
// 어플리케이션이 실행되면서 Provider를 적용할 때 불러온 테마모드를 ThemeProvider에 넘겨줍니다.
create: (_) => ThemeProvider(initThemeMode: themeMode)),
],
builder: (context, _) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Petmily',
// lib/theme.dart 에서 작성한 테마를 사용합니다.
theme: MyThemes.lightTheme,
darkTheme: MyThemes.darkTheme,
// ThemeProvider에서 현재 테마 모드를 불러옵니다.
themeMode: Provider.of<ThemeProvider>(context).themeMode,
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
MaterialButton(
onPressed: () {
// 이제 Provider를 통해 다른 위젯에서도 ThemeProvider를 호출하여 현재 테마 모드를 확인하거나 설정할 수 있습니다.
final themeProvider =
Provider.of<ThemeProvider>(context, listen: false);
themeProvider.setThemeMode(ThemeMode.light);
},
child: const Text("ligth"),
),
MaterialButton(
onPressed: () {
final themeProvider =
Provider.of<ThemeProvider>(context, listen: false);
themeProvider.setThemeMode(ThemeMode.dark);
},
child: const Text("dark"),
),
MaterialButton(
onPressed: () {
final themeProvider =
Provider.of<ThemeProvider>(context, listen: false);
themeProvider.setThemeMode(ThemeMode.system);
},
child: const Text("system"),
),
],
),
),
),
);
},
);
}
}
최소한의 코드로 최대한 간단하게 라이트/다크모드를 설정하고 싶었습니다.
질문이나 코드 태클은 언제나 환영입니다.