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

Flutter中Provider状态管理出现Null检查错误求助

问题排查:Provider状态管理中ProfilePage获取appUser为空的问题

之前采用Provider状态管理的实现方式曾正常运行,但目前持续触发错误:Exception has occurred. CastError (Null check operator used on a null value)。经调试发现,账户创建后LoginUserProvider类中的_userModel已成功赋值,但在ProfilePage页面通过Consumer获取的appUser却为空值。


LoginUserProvider类代码

class LoginUserProvider extends ChangeNotifier {
  UserModel? _userModel;
  SharedPreferences? preferences;
  final FirebaseAuth _auth = FirebaseAuth.instance;
  DatabaseService databaseService = DatabaseService();

  UserModel? get appUser => _userModel;

  set setLoginUser(UserModel user) {
    _userModel = user;
    notifyListeners();
  }

  Future<void> saveDataToFirestore(Map<String, dynamic> data) async {
    User? firebaseUser = _auth.currentUser;
    if (firebaseUser == null) return;

    await databaseService
        .saveUserDataToFirebase(firebaseUser.uid, data)
        .whenComplete(() {
      _userModel = UserModel.fromJson(data);
      // setLoginUser = UserModel.fromJson(data);
    });
    notifyListeners();
  }
}

首页标签页(MyStatefulWidget)代码

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

  @override
  State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> with 
              SingleTickerProviderStateMixin {
  int _selectedIndex = 0;
  TabController? _tabController;

  final List<Widget> _widgetOptions = <Widget>[
    const HomePage(),
    const BusinessPage(),
    const MySettingsPage()
  ];

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  @override
  void initState() {
    super.initState();
    _tabController =
        TabController(initialIndex: _selectedIndex, vsync: this, length: 3);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
      ),
      body: TabBarView(
            physics: const NeverScrollableScrollPhysics(),
            controller: _tabController,
            children: _widgetOptions),
      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'Home',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.business),
            label: 'Business',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.settings),
            label: 'Settings',
          ),
        ],
        currentIndex: _selectedIndex,
        selectedItemColor: Colors.amber[800],
        onTap: _onItemTapped,
      ),
    );
  }
}

设置页与个人资料页代码

class MySettingsPage extends StatelessWidget {
  const MySettingsPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Settings'), centerTitle: true,),
      body: SingleChildScrollView(
        child: Column(
          children:  [
            ListTile(
              leading: const Icon(Icons.person,),
              title: const Text('User profile'),
              trailing: const Icon(Icons.arrow_forward_ios),
              onTap: (){
                Navigator.push(context, MaterialPageRoute(builder: ((context) => const 
                 ProfilePage())));
              },
            )
          ],
        ),
      )
    );
  }
}

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

  @override
  State<ProfilePage> createState() => _ProfilePageState();
}

class _ProfilePageState extends State<ProfilePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Profile Page'), centerTitle: true,),
      body: Consumer<LoginUserProvider>(
        builder: (context, value, child) {
          return ListView(
            children:  [
              ListTile(
                title: const Text('E-mail'),
                subtitle: Text(value.appUser!.email!),
                // 错误发生在此处:appUser为空
              )
            ],
          );
        },
      )
    );
  }
}

问题原因与解决方案

1. Provider作用域不足

原因:如果LoginUserProvider没有在足够高的层级(如MaterialApp根节点)初始化,ProfilePage所在的导航栈可能不在Provider的作用域内,导致Consumer无法获取到正确的实例。
解决:在main.dart中确保Provider包裹在MaterialApp外层:

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => LoginUserProvider(),
      child: const MyApp(),
    ),
  );
}

2. 状态更新时机问题

原因:原代码中notifyListeners()在whenComplete外部调用,可能导致状态通知早于_userModel赋值完成。
解决:将notifyListeners()移到whenComplete内部,确保赋值完成后再通知监听:

Future<void> saveDataToFirestore(Map<String, dynamic> data) async {
  User? firebaseUser = _auth.currentUser;
  if (firebaseUser == null) return;

  await databaseService.saveUserDataToFirebase(firebaseUser.uid, data).whenComplete(() {
    _userModel = UserModel.fromJson(data);
    notifyListeners(); // 移至此处
  });
}

3. 空安全处理缺失

原因:直接使用value.appUser!.email!强制解空,即使appUser临时为空也会崩溃。
解决:添加空值判断,避免强制解空:

builder: (context, value, child) {
  if (value.appUser == null) {
    return const Center(child: CircularProgressIndicator());
  }
  return ListView(
    children:  [
      ListTile(
        title: const Text('E-mail'),
        subtitle: Text(value.appUser!.email ?? '未设置邮箱'),
      )
    ],
  );
},

4. 页面拼写错误修正

代码中MySettinsPage为拼写错误,应改为MySettingsPage,避免页面路由异常。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 15:40:21