Flutter使用Provider实现颜色选择器时出现空值调用与初始化异常
报错根源定位
- 第一个报错
The method '&' was called on null直接来自Color构造函数的内部实现:Color(int value)的构造逻辑会执行value & 0xFFFFFFFF来提取32位色值,当传入的colorValue为null时,位运算操作就会在null对象上触发报错。 - 第二个报错
Provider创建时抛异常是连锁反应:
上述Color(null)的报错发生在ThemeProvider实例化的字段初始化阶段,导致ChangeNotifierProvider的create回调执行失败,因此后续读取Provider时提示创建过程抛错。
代码存在的其他逻辑问题
- 实例字段
light/dark的初始化时机错误:
Dart中实例字段的初始化逻辑会在构造函数方法体执行之前运行,你在字段初始化时引用的colorValue此时还没被从SharedPreferences读取赋值,必然为null。 - 构造函数参数逻辑写反:
你从prefs读取了primaryValue传入构造函数,但构造函数内写的是this.primaryValue = colorValue;,相当于直接把null的静态colorValue赋值给实例字段,完全浪费了传入的持久化值。 - 空值兜底逻辑完全失效:
你在primaryColor: Color(colorValue) ?? Colors.teal[700]写的??判断根本不会执行——Color构造函数不允许传入null,传null会在内部位运算阶段直接抛错,走不到兜底逻辑。 - 主题修改后不会更新:
light/dark是固定的实例字段,即使调用changeColor修改了colorValue,已经初始化好的主题实例不会重新生成,选颜色后主题不会生效。 - 颜色解析逻辑冗余:
你拆分字符串转hex的操作完全没必要,Color实例本身的.value属性就是对应的32位int值,拆字符串反而容易出现解析错误。
修正方案
第一步:重构ThemeProvider的初始化逻辑
移除不合理的静态变量,调整初始化顺序,封装主题生成逻辑:
import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; class ThemeProvider with ChangeNotifier { // 私有状态字段 ThemeData _selectedTheme; int _primaryColorValue; bool _isDarkMode; // 构造函数:接收持久化读取的初始值 ThemeProvider({ required bool isDarkMode, required int primaryColorValue, }) : _isDarkMode = isDarkMode, _primaryColorValue = primaryColorValue { // 基础字段赋值完成后再生成主题,避免空值问题 _selectedTheme = _isDarkMode ? _buildDarkTheme() : _buildLightTheme(); } // 封装亮色主题生成逻辑 ThemeData _buildLightTheme() { return ThemeData.light().copyWith( primaryColor: Color(_primaryColorValue), colorScheme: ColorScheme.fromSeed(seedColor: Color(_primaryColorValue)), ); } // 封装暗色主题生成逻辑 ThemeData _buildDarkTheme() { return ThemeData.dark().copyWith( primaryColor: Color(_primaryColorValue), colorScheme: ColorScheme.fromSeed( seedColor: Color(_primaryColorValue), brightness: Brightness.dark, ), ); } // 颜色修改方法 void changeColor(int colorValue) async { final prefs = await SharedPreferences.getInstance(); _primaryColorValue = colorValue; await prefs.setInt('PrimaryColor', _primaryColorValue); // 重新生成当前模式的主题 _selectedTheme = _isDarkMode ? _buildDarkTheme() : _buildLightTheme(); notifyListeners(); } // 暗色模式切换方法 void swapTheme() async { final prefs = await SharedPreferences.getInstance(); _isDarkMode = !_isDarkMode; await prefs.setBool('isDarkTheme', _isDarkMode); // 重新生成对应模式的主题 _selectedTheme = _isDarkMode ? _buildDarkTheme() : _buildLightTheme(); notifyListeners(); } // 暴露主题的getter ThemeData get getTheme => _selectedTheme; }
第二步:简化颜色选择器的回调逻辑
移除冗余的字符串拆分操作,直接使用Color内置的.value属性传值:
MaterialColorPicker( circleSize: 50, selectedColor: Color(themeProvider._primaryColorValue), onColorChange: (Color color) { setState(() { themeProvider.changeColor(color.value); }); }, )
第三步:确认main.dart的初始化逻辑
现有传入构造函数的逻辑仅需补充空安全校验,避免传入null:
ChangeNotifierProvider( create: (context) => ThemeProvider( isDarkMode: prefs.getBool('isDarkTheme') ?? false, primaryColorValue: prefs.getInt('PrimaryColor') ?? 4293467747, ), )
验证逻辑
- 首次启动时,构造函数先拿到从prefs读取的非空颜色值,再生成主题,不会出现
Color(null)的报错。 - 切换颜色或暗色模式时,会重新生成对应主题,通过
notifyListeners()触发MaterialApp重建,主题即时生效。 - 所有配置都通过SharedPreferences持久化,重启应用后会读取上次保存的配置。
内容的提问来源于stack exchange,提问作者Yusof Antar
相关产品推荐
相关产品推荐

