You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter中结合BLoC实现主题切换并设置系统主题

在AuthBloc中实现主题持久化与启动时自动设置

步骤1:定义主题类型枚举

先创建枚举明确主题选项:

enum ThemeType { system, light, dark }

步骤2:扩展AuthState以包含主题信息

修改现有AuthState,让所有状态携带主题配置:

// 基础状态,包含主题信息
abstract class AuthState {
  final ThemeType themeType;
  const AuthState({this.themeType = ThemeType.system});
}

class AuthInitial extends AuthState {
  const AuthInitial({super.themeType});
}

class Authenticated extends AuthState {
  final String token;
  const Authenticated({required this.token, super.themeType});
}

class Unauthenticated extends AuthState {
  final String token;
  const Unauthenticated({required this.token, super.themeType});
}

步骤3:添加主题相关事件

在AuthEvent中新增加载、更新主题的事件:

abstract class AuthEvent {}

// 原有事件保持不变...

class LoadSavedTheme extends AuthEvent {}

class UpdateTheme extends AuthEvent {
  final ThemeType themeType;
  const UpdateTheme({required this.themeType});
}

步骤4:修改AuthBloc,加入主题持久化逻辑

先在pubspec.yaml添加shared_preferences依赖:

dependencies:
  shared_preferences: ^2.2.2

再修改AuthBloc,实现主题加载与保存逻辑:

import 'package:shared_preferences/shared_preferences.dart';

class AuthBloc extends Bloc<AuthEvent, AuthState> {
  final AbstractAuth authRepo;
  late StreamSubscription _authenticationStatusSubscription;
  late SharedPreferences _prefs;

  AuthBloc({required this.authRepo}) : super(const AuthInitial()) {
    _initPrefs();
    authRepo.getCookie();
    authRepo.getFirebaseToken();
    _checkAuthenticationStatus();
    add(const LoadSavedTheme()); // 启动时加载保存的主题

    // 处理原有事件
    on<LoggedIn>((event, emit) {
      emit(Authenticated(token: event.token, themeType: state.themeType));
    });
    on<LogoutEvent>((event, emit) {
      emit(Unauthenticated(token: '', themeType: state.themeType));
    });

    // 处理主题事件
    on<LoadSavedTheme>((event, emit) async {
      final savedTheme = _prefs.getString('theme_type') ?? 'system';
      ThemeType themeType = ThemeType.values.firstWhere(
        (e) => e.name == savedTheme,
        orElse: () => ThemeType.system,
      );
      // 根据当前状态类型更新主题
      if (state is AuthInitial) {
        emit(AuthInitial(themeType: themeType));
      } else if (state is Authenticated) {
        emit(Authenticated(token: (state as Authenticated).token, themeType: themeType));
      } else if (state is Unauthenticated) {
        emit(Unauthenticated(token: (state as Unauthenticated).token, themeType: themeType));
      }
    });

    on<UpdateTheme>((event, emit) async {
      await _prefs.setString('theme_type', event.themeType.name);
      // 更新当前状态的主题
      if (state is AuthInitial) {
        emit(AuthInitial(themeType: event.themeType));
      } else if (state is Authenticated) {
        emit(Authenticated(token: (state as Authenticated).token, themeType: event.themeType));
      } else if (state is Unauthenticated) {
        emit(Unauthenticated(token: (state as Unauthenticated).token, themeType: event.themeType));
      }
    });
  }

  Future<void> _initPrefs() async {
    _prefs = await SharedPreferences.getInstance();
  }

  // 原有_checkAuthenticationStatus等方法保持不变...
}

步骤5:修改主题选择页面,触发主题更新事件

替换原setState逻辑,通过Bloc触发主题更新:

class ThemeSelectionPage extends StatefulWidget {
  const ThemeSelectionPage({super.key});

  @override
  State<ThemeSelectionPage> createState() => _ThemeSelectionPageState();
}

class _ThemeSelectionPageState extends State<ThemeSelectionPage> {
  late ThemeType _selectedTheme;

  @override
  void initState() {
    super.initState();
    // 从当前AuthState获取已选主题
    final authState = context.read<AuthBloc>().state;
    _selectedTheme = authState.themeType;
  }

  Row _buildThemeRow(String themeName, ThemeType themeType) {
    final isSelected = _selectedTheme == themeType;
    return Row(
      mainAxisAlignment: MainAxisAlignment.start,
      children: [
        SizedBox(width: 5.h),
        Text(themeName, style: TextStyles.bodyStyle),
        const Spacer(),
        if (isSelected)
          const Icon(
            Icons.check,
            color: Colors.grey,
            size: 15,
          ),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('主题选择'),
      ),
      body: Container(
        width: double.infinity,
        margin: REdgeInsets.all(8.0),
        decoration: BoxDecoration(
          color: Theme.of(context).colorScheme.secondary,
          borderRadius: BorderRadius.circular(8.0),
        ),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          mainAxisSize: MainAxisSize.min,
          children: [
            Container(
              width: double.infinity,
              padding: REdgeInsets.all(8.0),
              decoration: BoxDecoration(
                color: Theme.of(context).colorScheme.secondary,
                borderRadius: BorderRadius.circular(8.0),
              ),
              child: Column(
                children: [
                  GestureDetector(
                    onTap: () {
                      context.read<AuthBloc>().add(const UpdateTheme(themeType: ThemeType.system));
                      setState(() => _selectedTheme = ThemeType.system);
                    },
                    child: _buildThemeRow('系统主题', ThemeType.system),
                  ),
                  const Divider(),
                  GestureDetector(
                    onTap: () {
                      context.read<AuthBloc>().add(const UpdateTheme(themeType: ThemeType.light));
                      setState(() => _selectedTheme = ThemeType.light);
                    },
                    child: _buildThemeRow('亮色主题', ThemeType.light),
                  ),
                  const Divider(),
                  GestureDetector(
                    onTap: () {
                      context.read<AuthBloc>().add(const UpdateTheme(themeType: ThemeType.dark));
                      setState(() => _selectedTheme = ThemeType.dark);
                    },
                    child: _buildThemeRow('暗色主题', ThemeType.dark),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

步骤6:修改main.dart,根据AuthState设置主题模式

在MaterialApp中根据AuthState的themeType配置themeMode:

return BlocProvider(
  create: (context) => AuthBloc(authRepo: AuthRepo()),
  child: BlocBuilder<AuthBloc, AuthState>(
    builder: (context, state) {
      ThemeMode themeMode;
      switch (state.themeType) {
        case ThemeType.light:
          themeMode = ThemeMode.light;
          break;
        case ThemeType.dark:
          themeMode = ThemeMode.dark;
          break;
        default:
          themeMode = ThemeMode.system;
      }
      return MaterialApp(
        navigatorKey: navKey,
        title: 'YiwuMart',
        debugShowCheckedModeBanner: false,
        theme: lightTheme,
        darkTheme: darkTheme,
        themeMode: themeMode, // 应用主题模式
        home: const MainScreen(
          key: scakey,
        ),
      );
    },
  ),
);

内容的提问来源于stack exchange,提问作者st4tic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 04:12:03