배경 설명
Flutter 로 기차 예매 서비스를 만들고 있는데 시스템 환경에 따라 라이트/다크 모드로 나오게끔 테마 설정을 해주었다. 하지만 시스템 환경이 라이트 모드이지만 앱은 다크 모드로 쓰고 싶을수도 있는 법. 앱 내에서도 테마 지정이 가능하면 좋을 거 같다는 생각에 아이콘을 눌렀을 때 테마 변경하는 기능을 추가하도록 하고싶다.
동작 방식 구상
테마를 변경하는 방법을 찾아보기 전에 간단하게 동작 방식을 구상해보았다.
먼저 테마가 지정되는 순간부터 적용되는 순서를 알아야 한다. MyApp 이 build 가 될 때 themeMode 는 시스템 설정 값을 가져와 light 이면 lightTheme 를 dark 면 darkTheme 로 지정이 된다. 그렇다면 themeMode 의 값을 다른 페이지에서 가져와서 MyApp 을 빌드를 다시하면 되지 않을까 라는 생각을 한다. themeMode 타입을 가진 currentMode 변수에 사용자의 입력(아이콘을 클릭하거나 toggle 이 움직이는 등)이 들어오면 light 혹은 dark 타입으로 변경이 되도록 한다. 값이 변경된 currentMode 는 MyApp 에서 themeMode 를 다시 지정해준다. currentMode 값이 변경된 것을 확인하려면 상태 관리 라이브러리를 사용하거나 값이 변경되었는지 체크하는 방법이 있을 것이라는 생각을 했다.
구현 방법 1 - 상태 관리 라이브러리 사용
앱의 테마 모드를 변경하는 방법을 찾아봤을 때 가장 먼저 보였던 Riverpod 라이브러리를 사용해서 구현하는 것이었다. 상태 관리 라이브러리인 Riverpod 사용하면 쉬웠겠지만 이 기능만을 위해 써드파티 라이브러리 사용하는 것이 싫었고, 기껏 StatefulWidget 상속받아서 사용했던 HomePage 위젯을 변경해야된다는 문제가 있기 때문에 나는 다른 방법을 찾아봤다. 하지만 누군가는 Riverpod 를 사용하는 방법도 궁금할수도 있으니 아래에 링크를 첨부하겠다.
How app theme mode can be saved in flutter according to user
I have added dark and light mode in my app but I am unable to save the mode selected. I am new so that's why I'm unable to understand what to do. Here's my code of dark and light mode button though...
stackoverflow.com
구현 방법 2 - 값이 변경되는지 체크
Flutter 내장 라이브러리에서 제공하는 클래스에서 상태 변경을 캐치하는 위젯을 찾았다. ValueNotifier 를 활용하여 값이 변경될 때 해당 값으로 테마 모드를 지정하는 방법을 채택했다.
ValueNotifier 란?
ChangeNotifier 의 하위 위젯으로 값(value) 이 기존의 값과 다르다면(!=) 리스너에게 알린다. ValueNotifier 는 값의 id 가 변경될 때만 리스너에게 알리기 때문에 가변 타입의 값이 바뀔때는 해당 리스너가 동작하지 않는다.
예를 들어 ValueNotifier<List<int>> 타입의 notifier 는 list 값이 변할 때(add, remove 등)는 리스너가 동작하지 않는다.
ValueNotifier<List<int>> notifier = ValueNotifier([0, 1, 2]);
// list 가 변해도 리스너는 동작하지 않음
notifier.add(3);
notifier.removeLast();
// notifier 값(value) 자체를 새로 할당했을 때만 리스너가 동작함
notifier.value = [0];
대표적인 불변 타입인 String 타입의 notifier 를 설정한 예시이다. value 가 다르면 리스너에게 알리는 데 notifier.value 가 두번째 변경할 때에는 id 뿐만 아니라 등식 연산자(==) 가 달라지는 것 까지 확인한다.
ValueNotifier<String> notifier = ValueNotifier('notifier1');
// 리스너가 동작함
// notifier1 == notifier2 => false
notifier.value = 'notifier2';
// 리스너가 동작하지 않음
// notifier2 == notifier2 => true
notifier.value = 'notifier2';
// 리스너가 동작함
// notifier2 == notifier1 => false
notifier.value = 'notifier1'
ValueNotifier 클래스에 대해 알아보았으니 이제 실제로 테마 변경 기능을 구현해보자.
구현
1. MyApp 에서 ValueNotifier 타입의 notifier 를 생성한다.
2. ValueListenableBuilder 에서 valueListenalbe 을 themeNotifier 로 지정한다.
3. themeMode 를 currentMode 값을 받아온다.
class MyApp extends StatelessWidget {
const MyApp({super.key});
// ValueNotifier 생성
static final ValueNotifier<ThemeMode> themeNotifier = ValueNotifier(ThemeMode.system);
@override
Widget build(BuildContext context) {
// ValueListenableBuilder 에 themeNotifier
return ValueListenableBuilder<ThemeMode>(
valueListenable: themeNotifier,
// notifier 의 제네릭 타입의 변수를 지정
// themeNotifier.value 가 변경되면 해당 builder 가 다시 호출되어 UI 를 그린다
builder: (_, ThemeMode currentMode, __) {
return MaterialApp(
home: HomePage(),
themeMode: currentMode,
theme: lightTheme,
darkTheme: darkTheme,
);
},
);
}
}
4. icon 을 클릭했을 때 지금이 light 모드라면 themeNotifier.value 의 값을 ThemeMode.dark 로 변경하고 dark 모드라면 Theme.light 로 변경한다.
5. icon 모양도 위와 동일하게 맞춰준다.
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 테마 모드 확인
bool isLightMode = Theme.of(context).brightness == Brightness.light;
return Scaffold(
appBar: AppBar(
title: Text('기차 예매'),
actions: [
IconButton(
onPressed: () {
// icon 클릭할 때 Brightness 가 light 일 경우 value 값을 ThemeMode.dark 값으로 지정 아니면 ThemeMode.light
MyApp.themeNotifier.value =
isLightMode ? ThemeMode.dark : ThemeMode.light;
},
// 모드가 light 일 경우 icon 이 dark_mode 아니면 light_mode
icon: Icon(isLightMode ? Icons.dark_mode : Icons.light_mode),
),
],
),
...
);
}
}
아래와 같이 AppBar 오른쪽에 아이콘을 두어 클릭했을 때 테마가 바뀌는 것을 볼 수 있다.
참고: https://heeeju4lov.tistory.com/17
[Flutter Dev] 라이트 / 다크 모드 테마 변경하기
요즘 괜찮다는 앱들은 사용자 편의를 위해 다크 모드를 제공하고 있습니다. Flutter에서는 다크모드를 어떻게 사용할수 있는지 알아보도록 하겠습니다. 샘플은 Flutter 프로젝트 생성시 자동은 생
heeeju4lov.tistory.com
'Flutter' 카테고리의 다른 글
[InAppWebView] 기본 설정 (1) | 2024.12.05 |
---|---|
[Flutter] TextFormField 로 여러 개의 입력 데이터 받아오기 (0) | 2024.11.27 |
[Flutter] light, dark 테마 확인 방법 (0) | 2024.11.18 |
[Flutter] 위젯의 구조화 (1) | 2024.11.14 |
[Flutter] 상태 관리 (0) | 2024.10.23 |
댓글