如何通过使用flutter bloc从fire存储中使用依赖注入来处理错误`The getter was called on null`

Posted

技术标签:

【中文标题】如何通过使用flutter bloc从fire存储中使用依赖注入来处理错误`The getter was called on null`【英文标题】:How to handle error `The getter was called on null` by using dependency injection from fire store using flutter bloc 【发布时间】:2021-08-07 21:20:40 【问题描述】:

我在从FireStore 检索数据时遇到如下错误:

The following NoSuchMethodError was thrown building BlocBuilder<AppCubit, AppStates>(dirty, state: _BlocBuilderBaseState<AppCubit, AppStates>#783ea):
The getter 'cover' was called on null.
Receiver: null
Tried calling: cover

所以意识到这个错误是因为dependency injection 而我发现userModelnull,这是我拥有的userModel 类:

class UserModel 
  String name;
  String email;
  String phone;
  String uId;
  String image;
  String cover;
  String bio;
  bool isEmailVerified;

  UserModel(
    this.name,
    this.email,
    this.phone,
    this.uId,
    this.image,
    this.cover,
    this.bio,
    this.isEmailVerified,
  );

  UserModel.fromJson(Map<String, dynamic> json) 
    email = json['email'];
    name = json['name'];
    phone = json['phone'];
    uId = json['uId'];
    image = json['image'];
    cover = json['cover'];
    bio = json['bio'];
    isEmailVerified = json['isEmailVerified'];
  

  Map<String, dynamic> toMap() 
    return 
      'name': name,
      'email': email,
      'phone': phone,
      'uId': uId,
      'image': image,
      'cover': cover,
      'bio': bio,
      'isEmailVerified': isEmailVerified,
    ;
  

我在 Cubit 类中创建了一个 getUserData 方法,如下所示:

  UserModel userModel;

  void getUserData() 
    emit(AppGetUserLoadingState());
    FirebaseFirestore.instance.collection('users').doc(uId).get().then((value) 
      // print(value.data());
      userModel = UserModel.fromJson(value.data());
      emit(
        AppGetUserSuccessState(),
      );
    ).catchError((error) 
      print(error.toString());
      emit(
        AppGetUserErrorState(
          error.toString(),
        ),
      );
    );
  

当应用程序在 main 中启动时,我正在调用此方法,如下面的代码:

  Widget build(BuildContext context) 
    return BlocProvider(
      create: (BuildContext context) => AppCubit()..getUserData()..handleScroll(),
      child: BlocConsumer<AppCubit, AppStates>(
        listener: (context, state) ,
        builder: (context, state) ...,
      ),
    );
  

这是我有错误的多个部分之一:

DecorationImage(
                                  image: NetworkImage(
                                    '$userModel.cover',
                                  ),
                                  fit: BoxFit.cover,
                                ),

这是我正在使用的完整课程:

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:mysocially/shared/cubit/cubit.dart';
import 'package:mysocially/shared/cubit/states.dart';
import 'package:mysocially/styles/icon_broken.dart';

class SettingsScreen extends StatelessWidget 
  const SettingsScreen(Key key) : super(key: key);

  @override
  Widget build(BuildContext context) 
    return BlocConsumer<AppCubit, AppStates>(
      listener: (context, state) ,
      builder: (context, state) 
        var userModel = AppCubit.get(context).userModel;
        // print(userModel.cover);

        return Padding(
          padding: const EdgeInsets.all(8.0),
          child: Column(
            children: [
              Container(
                height: 190.0,
                child: Stack(
                  alignment: AlignmentDirectional.bottomCenter,
                  children: [
                    Align(
                      alignment: AlignmentDirectional.topCenter,
                      child: Container(
                        width: double.infinity,
                        height: 140.0,
                        decoration: BoxDecoration(
                          borderRadius: BorderRadius.only(
                            topLeft: Radius.circular(4.0),
                            topRight: Radius.circular(4.0),
                          ),
                          image: DecorationImage(
                                  image: NetworkImage(
                                    '$userModel.cover',
                                  ),
                                  fit: BoxFit.cover,
                                ),
                        ),
                      ),
                    ),
                    CircleAvatar(
                      radius: 51.0,
                      backgroundColor:
                          Theme.of(context).scaffoldBackgroundColor,
                      child: CircleAvatar(
                        // radius: responsive.height * 0.025,
                        radius: 50.0,
                        backgroundImage: NetworkImage(
                          '$userModel.image',
                        ),
                      ),
                    ),
                  ],
                ),
              ),
              SizedBox(
                height: 5.0,
              ),
              Text(
                '$userModel.name',
                style: Theme.of(context).textTheme.bodyText1,
              ),
              Text(
                '$userModel.bio',
                style: Theme.of(context).textTheme.caption,
              ),
              Padding(
                padding: const EdgeInsets.symmetric(vertical: 20.0),
                child: Row(
                  children: [
                    Expanded(
                      child: InkWell(
                        child: Column(
                          children: [
                            Text(
                              '100',
                              style: Theme.of(context).textTheme.subtitle2,
                            ),
                            Text(
                              'Posts',
                              style: Theme.of(context).textTheme.caption,
                            ),
                          ],
                        ),
                        onTap: () ,
                      ),
                    ),
                    Expanded(
                      child: InkWell(
                        child: Column(
                          children: [
                            Text(
                              '100',
                              style: Theme.of(context).textTheme.subtitle2,
                            ),
                            Text(
                              'Posts',
                              style: Theme.of(context).textTheme.caption,
                            ),
                          ],
                        ),
                        onTap: () ,
                      ),
                    ),
                    Expanded(
                      child: InkWell(
                        child: Column(
                          children: [
                            Text(
                              '62',
                              style: Theme.of(context).textTheme.subtitle2,
                            ),
                            Text(
                              'Photos',
                              style: Theme.of(context).textTheme.caption,
                            ),
                          ],
                        ),
                        onTap: () ,
                      ),
                    ),
                    Expanded(
                      child: InkWell(
                        child: Column(
                          children: [
                            Text(
                              '200',
                              style: Theme.of(context).textTheme.subtitle2,
                            ),
                            Text(
                              'Followers',
                              style: Theme.of(context).textTheme.caption,
                            ),
                          ],
                        ),
                        onTap: () ,
                      ),
                    ),
                    Expanded(
                      child: InkWell(
                        child: Column(
                          children: [
                            Text(
                              '600',
                              style: Theme.of(context).textTheme.subtitle2,
                            ),
                            Text(
                              'Followings',
                              style: Theme.of(context).textTheme.caption,
                            ),
                          ],
                        ),
                        onTap: () ,
                      ),
                    ),
                  ],
                ),
              ),
              Row(
                children: [
                  Expanded(
                    child: OutlinedButton(
                      onPressed: () ,
                      child: Text('Add Photos'),
                    ),
                  ),
                  SizedBox(
                    width: 10.0,
                  ),
                  OutlinedButton(
                    onPressed: () ,
                    child: Icon(
                      IconBroken.Edit,
                      size: 20.0,
                    ),
                  ),
                ],
              ),
            ],
          ),
        );
      ,
    );
  

我尝试通过使用init: Get.find(), 使用GetX 状态管理来处理此错误,但我未能使用flutter_bloc 处理此错误..

更多信息:第一次登录时未检索到数据,但在我重新启动应用程序时检索到了数据,但它给了我一秒钟的此错误并返回数据..

这里有什么建议吗?

我希望我提到了所有需要的信息..

#已编辑 所以我想到了一些东西,所以我在构建器的开头放置了一个条件,如下代码:

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:mysocially/modules/new_post/new_post_screen.dart';
import 'package:mysocially/shared/components/components.dart';
import 'package:mysocially/shared/cubit/cubit.dart';
import 'package:mysocially/shared/cubit/states.dart';
import 'package:mysocially/styles/icon_broken.dart';

class SocialLayout extends StatelessWidget 
  const SocialLayout(Key key) : super(key: key);

  @override
  Widget build(BuildContext context) 
    return BlocConsumer<AppCubit, AppStates>(
      listener: (context, state) ,
      builder: (context, state) 
        var cubit = AppCubit.get(context);
        if (state is AppGetUserSuccessState) 
          return Scaffold(
            extendBody: true,
            appBar: AppBar(
              title: Text(
                cubit.titles[cubit.currentIndex],
              ),
              actions: [
                IconButton(
                  onPressed: () ,
                  icon: Icon(IconBroken.Notification),
                ),
                IconButton(
                  onPressed: () ,
                  icon: Icon(IconBroken.Search),
                ),
              ],
            ),
            body: cubit.screens[cubit.currentIndex],
            bottomNavigationBar: AnimatedOpacity(
              opacity: cubit.show ? 1.0 : 0.0,
              duration: Duration(milliseconds: 500),
              child: BottomNavigationBar(
                currentIndex: cubit.currentIndex,
                onTap: (index) 
                  cubit.changeBottomNavIndex(index);
                ,
                items: [
                  BottomNavigationBarItem(
                      icon: Icon(IconBroken.Home), label: 'Home'),
                  BottomNavigationBarItem(
                      icon: Icon(IconBroken.Chat), label: 'Chat'),
                  // BottomNavigationBarItem(
                  //     icon: Icon(IconBroken.Paper_Upload,), label: 'Post'),
                  BottomNavigationBarItem(
                      icon: Icon(IconBroken.Location), label: 'Location'),
                  BottomNavigationBarItem(
                      icon: Icon(IconBroken.Setting), label: 'Settings'),
                ],
              ),
            ),
            floatingActionButtonLocation:
                FloatingActionButtonLocation.centerDocked,
            floatingActionButton: AnimatedOpacity(
              opacity: cubit.show ? 1.0 : 0.0,
              duration: Duration(milliseconds: 500),
              child: FloatingActionButton(
                onPressed: () 
                  navigateTo(
                    context,
                    NewPostScreen(),
                  );
                ,
                tooltip: 'Post',
                child: Icon(
                  IconBroken.Paper_Upload,
                  color: Colors.white,
                ),
                elevation: 2.0,
              ),
            ),
          );
         else 
          return Center(child: CircularProgressIndicator());
        
      ,
    );
  

现在应用程序在我重新启动应用程序后工作正常并且没有给我错误,但我第一次尝试登录它显示CircularProgressIndicator() 无限直到我重新启动应用程序并再次登录..

这是我所有的AppStates:

abstract class AppStates 

class AppInitialState extends AppStates 

class AppGetUserLoadingState extends AppStates 

class AppGetUserSuccessState extends AppStates 

class AppGetUserErrorState extends AppStates 
  final String error;

  AppGetUserErrorState(this.error);


class AppChangeBottomNavBarState extends AppStates 

class AppChangeBottomSheetState extends AppStates 

class AppChangeModeState extends AppStates 

class AppHandleReverseHideBottomWidgetState extends AppStates 

class AppHandleForwardHideBottomWidgetState extends AppStates 

class AppHideBottomWidgetState extends AppStates 

class AppShowBottomWidgetState extends AppStates 

class AppNewPostState extends AppStates 

这是LoginScreen

import 'package:conditional_builder/conditional_builder.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:mysocially/layout/social_layout.dart';
import 'package:mysocially/modules/register/register_screen.dart';
import 'package:mysocially/network/local/cache_helper.dart';
import 'package:mysocially/shared/components/components.dart';


import 'cubit/cubit.dart';
import 'cubit/states.dart';

class LoginScreen extends StatelessWidget 
  var formKey = GlobalKey<FormState>();
  var emailController = TextEditingController();
  var passwordController = TextEditingController();

  @override
  Widget build(BuildContext context) 
    return BlocProvider(
      create: (BuildContext context) => AppLoginCubit(),
      child: BlocConsumer<AppLoginCubit, AppLoginStates>(
        listener: (context, state) 
          if (state is AppLoginErrorState) 
            showToast(
              text: state.error,
              state: ToastStates.ERROR,
            );
          
          if(state is AppLoginSuccessState) 
            CacheHelper.saveData(
              key: 'uId',
              value: state.uId,
            ).then((value)
            
              navigateAndFinish(
                context,
                SocialLayout(),
              );
            );
          
        ,
        builder: (context, state) 
          return Scaffold(
            appBar: AppBar(),
            body: Center(
              child: SingleChildScrollView(
                child: Padding(
                  padding: const EdgeInsets.all(20.0),
                  child: Form(
                    key: formKey,
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(
                          'LOGIN',
                          style: Theme.of(context).textTheme.headline4.copyWith(
                                color: Colors.black,
                              ),
                        ),
                        Text(
                          'Login now to to contact with friends',
                          style: Theme.of(context).textTheme.bodyText1.copyWith(
                                color: Colors.grey,
                              ),
                        ),
                        SizedBox(
                          height: 30.0,
                        ),
                        defaultFormField(
                          controller: emailController,
                          type: TextInputType.emailAddress,
                          validate: (String value) 
                            if (value.isEmpty) 
                              return 'please enter your email address';
                            
                          ,
                          label: 'Email Address',
                          prefix: Icons.email_outlined,
                        ),
                        SizedBox(
                          height: 15.0,
                        ),
                        defaultFormField(
                          controller: passwordController,
                          type: TextInputType.visiblePassword,
                          suffix: AppLoginCubit.get(context).suffix,
                          onSubmit: (value) 
                            if (formKey.currentState.validate()) 
                              AppLoginCubit.get(context).userLogin(
                                email: emailController.text,
                                password: passwordController.text,
                              );
                            
                          ,
                          isPassword: AppLoginCubit.get(context).isPassword,
                          suffixPressed: () 
                            AppLoginCubit.get(context)
                                .changePasswordVisibility();
                          ,
                          validate: (String value) 
                            if (value.isEmpty) 
                              return 'password is too short';
                            
                          ,
                          label: 'Password',
                          prefix: Icons.lock_outline,
                        ),
                        SizedBox(
                          height: 30.0,
                        ),
                        ConditionalBuilder(
                          condition: state is! AppLoginLoadingState,
                          builder: (context) => defaultButton(
                            function: () 
                              if (formKey.currentState.validate()) 
                                AppLoginCubit.get(context).userLogin(
                                  email: emailController.text,
                                  password: passwordController.text,
                                );
                              
                            ,
                            text: 'login',
                            isUpperCase: true,
                          ),
                          fallback: (context) =>
                              Center(child: CircularProgressIndicator()),
                        ),
                        SizedBox(
                          height: 15.0,
                        ),
                        Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: [
                            Text(
                              'Don\'t have an account?',
                            ),
                            defaultTextButton(
                              function: () 
                                navigateTo(
                                  context,
                                  AppRegisterScreen(),
                                );
                              ,
                              text: 'register',
                            ),
                          ],
                        ),
                      ],
                    ),
                  ),
                ),
              ),
            ),
          );
        ,
      ),
    );
  

【问题讨论】:

image: NetworkImage( '$userModel.cover',), 。我认为您的错误是因为该代码。 userModel.cover 的可能性为零吗? @GilangPratama 感谢您的支持,不,这不是问题,我面临同样的错误:( 您是否尝试过使用 BlocConsumer 的 builder 函数的 state 参数。例如,在您检查 state 参数的值的构建器函数中使用 if 语句。 @edgeboy7 你能解释一下吗,我真的没有尝试过:) 类似这样的:builder: (context, state) if(state is InitialState) if(state is LoadingState) if(state is LoadedState) 基本上,使用状态来确定构建器函数返回什么以及屏幕上显示什么 【参考方案1】:

我认为您的主要困惑源于您使用(至少在名称上)CubitCubitBloc 的高度简化类型,可用于单个值。例如颜色或数字或枚举值。

你没有那个。您有复杂数据的列表。您应该使用完整的Bloc。 flutter_bloc page 上有非常好的教程。请不要停留在简单的教程上,阅读复杂数据的教程,因为这就是你所拥有的。

主要区别在于您不再直接访问Bloc 获取数据,所有数据都通过状态进行通信。例如,您的Bloc 没有属性User,只有您的UserSuccessfullyLoaded 状态具有该属性。这样,您的编译器将指导您,何时该数据可用,何时不可用。

【讨论】:

以上是关于如何通过使用flutter bloc从fire存储中使用依赖注入来处理错误`The getter was called on null`的主要内容,如果未能解决你的问题,请参考以下文章

flutter_bloc - 每个 Bloc 有多个存储库,还是只有一个?

如何使用flutter bloc模式通过分页加载数据列表?

Flutter Bloc 如何从 Widget 本身更新 BlocBuilder 中的 Widget?

flutter_bloc - 为啥在 UI 而不是后端声明存储库?

没有 BLoC 的 Flutter 状态管理

如何在 Hot Reload 上使用 Provider 维护 Flutter Global BloC 状态?