iT邦幫忙

2021 iThome 鐵人賽

DAY 20
0
Mobile Development

30天 - Flutter 日常系列 第 20

[Day20] Flutter - Theme: Dark mode & Light mode(part4)

  • 分享至 

  • xImage
  •  

前言

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

完整程式碼

會應用到的概念

Theme

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

MaterialApp(
  title: appName,
  theme: ThemeData(
    brightness: Brightness.dark,
    primaryColor: Colors.amber,
    accentColor: Colors.accents,
  ),
  home: Home(),
);

套件

bloc: ^7.1.0
flutter_bloc: ^7.1.0
equatable: ^2.0.3

ThemeData:Dark mode & Light mode

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

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直接傳進去。

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和開關狀態。

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事件觸發時,把目前開關狀態傳入,然後判斷開關狀態切換主題。

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。

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await ConfigReader.initializeApp(Environment.dev);
  runApp(
    MultiBlocProvider(
      providers: [
        BlocProvider(
          create: (context) => ThemeBloc(),
        ),
      ],
      child: MyApp(),
    ),
  );
}

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

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

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

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的用法。


上一篇
[Day19] Flutter - Const: Shared(part3)
下一篇
[Day21] Flutter - Presentation AutoRouter(part5)
系列文
30天 - Flutter 日常30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言