0%

Flutter 020 - Theme Dark mode & Light mode (part4)

前言

Hi, 今天要教如何利用Theme結合Bloc來切換主題配色,教學內容只會擷取片段程式碼,建議大家搭配完整程式碼來練習。

完整程式碼

會應用到的概念

Theme

使用Theme可以定義整個應用的主題,也可以使用元件來定義應用程式特定部分的顏色和字體樣式,例如 AppBars、按鈕、設置背景顏色和字體樣式等。

1
2
3
4
5
6
7
8
9
MaterialApp(
title: appName,
theme: ThemeData(
brightness: Brightness.dark,
primaryColor: Colors.amber,
accentColor: Colors.accents,
),
home: Home(),
);

套件

1
2
3
bloc: ^7.1.0
flutter_bloc: ^7.1.0
equatable: ^2.0.3

ThemeData:Dark mode & Light mode

在做主題切換前先來創建一個深色與淺色主題的顏色,利用上一篇day19的shared設定的顏色來做主題顏色。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import 'package:flutter/material.dart';
import 'package:stunning_tribble/shared/colors.dart';

class AppThemeData {
static ThemeData darkMode = ThemeData(
brightness: Brightness.dark,
backgroundColor: oil6Color,
primaryColorDark: oil2Color,
primaryColorLight: oil5Color,
textTheme: TextTheme(
bodyText1: TextStyle(
fontSize: 80.0,
color: oil2Color,
fontWeight: FontWeight.bold,
),
),
);

static ThemeData lightMode = ThemeData(
brightness: Brightness.light,
backgroundColor: oil2Color,
primaryColorDark: oil2Color,
primaryColorLight: oil5Color,
textTheme: TextTheme(
bodyText1: TextStyle(
fontSize: 80.0,
color: oil5Color,
fontWeight: FontWeight.bold,
),
),
);
}

Bloc:Theme

完成主題配置後就可以來建造熟悉的Bloc,不熟的同學可以去看day13的教學。首先是事件,由於我會使用 switch 這個小部件切換深淺主題,這個部件會回傳現在的狀態是開還是關,所以下面我就把true和false直接傳進去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
part of 'theme_bloc.dart';

abstract class ThemeEvent extends Equatable {
final bool isDark;
const ThemeEvent(this.isDark);

@override
List<Object> get props => [];
}

class ThemeChange extends ThemeEvent {
ThemeChange(bool isDark) : super(isDark);
}

傳進去以後呢我希望他回傳主題顏色和開關的狀態,這樣我們等等使用 BlocBuilder 的時候就可以改變UI和開關狀態。

1
2
3
4
5
6
7
8
9
10
11
12
13
part of 'theme_bloc.dart';

class ThemeState extends Equatable {
final ThemeData themeData;
final bool isDark;
const ThemeState({
required this.isDark,
required this.themeData,
});

@override
List<Object> get props => [themeData, isDark];
}

在Bloc中就按照著切換主題流程把它寫出來,當ThemeChange事件觸發時,把目前開關狀態傳入,然後判斷開關狀態切換主題。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:stunning_tribble/shared/theme.dart';

part 'theme_event.dart';
part 'theme_state.dart';

class ThemeBloc extends Bloc<ThemeEvent, ThemeState> {
ThemeBloc()
: super(ThemeState(themeData: AppThemeData.darkMode, isDark: true));

@override
Stream<ThemeState> mapEventToState(
ThemeEvent event,
) async* {
if (event is ThemeChange) {
yield* _mapThemeChangeToState(event.isDark);
}
}

Stream<ThemeState> _mapThemeChangeToState(bool _isDark) async* {
if (_isDark) {
yield ThemeState(themeData: AppThemeData.darkMode, isDark: true);
} else {
yield ThemeState(themeData: AppThemeData.lightMode, isDark: false);
}
}
}

切換深淺主題

首先創建剛剛寫好的ThemeBloc。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await ConfigReader.initializeApp(Environment.dev);
runApp(
MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => ThemeBloc(),
),
],
child: MyApp(),
),
);
}

接著利用BlocBuilder來查看themeData的狀態。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<ThemeBloc, ThemeState>(
builder: (context, state) {
return MaterialApp(
debugShowCheckedModeBanner: ConfigReader.config().DEBUG,
title: 'Flutter Demo',
theme: state.themeData,
darkTheme: ThemeData(),
home: HomeScreen(),
);
},
);
}
}

最後把我們的按鈕加入事件,你就可以切換主題囉!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class SwtichModeButton extends StatelessWidget {
const SwtichModeButton({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return BlocBuilder<ThemeBloc, ThemeState>(
builder: (context, state) {
return Switch(
inactiveThumbColor: Theme.of(context).primaryColorLight,
activeColor: Theme.of(context).primaryColorDark,
value: state.isDark,
onChanged: (isDark) {
BlocProvider.of<ThemeBloc>(context).add(ThemeChange(isDark));
},
);
},
);
}
}

Note

本次的主題切換順便幫大家複習一下Bloc的用法。