You need to enable JavaScript to run this app. Flutter使用Provider实现颜色选择器时出现空值调用与初始化异常
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.30 06:45:03