如何在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
相关产品推荐
相关产品推荐

