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

前言

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


<<:  每个人都该学的30个Python技巧|技巧 20:Python容器—集合(set)(字幕、衬乐、练习)

>>:  [Day20]The Huge One

学习Python纪录Day1 - 学习动机与目标

学习动机与目标: 和身边对程序有兴趣的朋友聊一些程序相关问题时,其中最常遇到的就是Python,但是...

[Python 爬虫这样学,一定是大拇指拉!] DAY27 - 实战演练:重复使用 TCP 连线

重复使用 TCP 连线 本篇章请搭配以下一起服用: HTTP - 复习传送门 TCP / UDP -...

Day13_附录A.控制项(A5~A7)

google一下:ISO 27001 附录A 会找到蛮多行业的应用~一直换关键字~有助於理解不懂的地...

MYSQL IMPORT 乱码 ON OSX,LINUX

IMPORT csv LOAD DATA INFILE 'test.csv' INTO TABLE ...

Day 28 铁人赛章节回顾

前言 很快的时间来到了28天 首先先讲结论 很可惜的我们团队挑战失败了 虽然前几天依旧还是有写文章 ...