[Flutter] shared_preferences 와 provider로 라이트/다크모드 설정하기

조경호·2022년 7월 20일
2

Flutter

목록 보기
11/11

안녕하세요!

요즘 어플리케이션들 보면 라이트/다크모드 기능은 거의 기본적으로 탑재하는 것 같습니다. 그런데 Flutter 공부하면서 여러 유투브나 구글링, 스텍센세에게 물어봐도 앱이 실행될 때의 토글만 있지 다른 상용 어플처럼 어플리케이션을 껐다가 재실행 해도 테마모드 설정을 기억해서 라이트/다크모드를 설정하는 예제는 잘 없더라구요.

그래서 연구해봤습니다.

실제 기기에서 테스트해보지 않았습니다. 하지만 시뮬레이터에서 테마모드 설정 후 어플을 껐다가 다시 실행할 경우, 다시 빌드했을 경우 설정해놓은 테마모드가 잘 적용되었습니다.

1. 패키지 설치

필수 패키지들을 설치합니다.

flutter pub add shared_preferences
flutter pub add provider

2. 커스텀 테마 작성

main의 MaterialApp에 추가하기 위해 간단하게 ligthTheme과 darkTheme을 만듭니다.

lib/theme.dart

class MyThemes {
  static final lightTheme = ThemeData().copyWith(
    primaryColor: Colors.purple,
  );

  static final darkTheme = ThemeData.dark().copyWith(
    primaryColor: Colors.purple,
  );
}

3. ThemeProvider 작성

Flutter의 ChangeNotifier를 활용하기 위해 ThemeProvider를 작성합니다.

구글의 많은 예제를 보면 isDark 등으로 toggleTheme() 함수를 작성합니다. 하지만 저는 light/dark 모드만이 아니라 '시스템(테마) 설정과 같이' 도 추가하고 싶었습니다 ^^
때문에 shared_preferences로 저장할 키값을 themeMode로 하고 벨류값을 String 타입의 light/dark/system으로 설정했습니다.

코드를 대충 작동하게만 작성해서.. 본인 스타일에 맞게 정리하시면 됩니다 ㅎㅎ..

lib/theme_provider.dart

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;
  }
}

4. 메인함수 작성 및 모드선택 버튼 만들기

lib/main.dart

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"),
                  ),
                ],
              ),
            ),
          ),
        );
      },
    );
  }
}

최소한의 코드로 최대한 간단하게 라이트/다크모드를 설정하고 싶었습니다.

질문이나 코드 태클은 언제나 환영입니다.

profile
개발자

0개의 댓글