Provider.of 总是返回 null Flutter

Posted

技术标签:

【中文标题】Provider.of 总是返回 null Flutter【英文标题】:Provider.of always returning null Flutter 【发布时间】:2021-02-21 06:44:22 【问题描述】:

我有一个提供程序对象,它上周一直在工作,直到我这样做了

flutter channel stable
flutter upgrade

现在,它总是返回 null。

问题出现在我的启动页面上。以下是页面内容:

import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:boots/helper/enum.dart';
import 'package:boots/helper/theme.dart';
import 'package:boots/page/Auth/selectAuthMethod.dart';
import 'package:boots/page/homePage.dart';
import 'package:boots/state/authState.dart';
import 'package:boots/widgets/customWidgets.dart';
import 'package:provider/provider.dart';

class SplashPage extends StatefulWidget 
  SplashPage(Key key) : super(key: key);

  @override
  _SplashPageState createState() => _SplashPageState();


class _SplashPageState extends State<SplashPage> 
  @override
  void initState() 
    WidgetsBinding.instance.addPostFrameCallback((_) 
      timer();
    );
    super.initState();
  

  void timer() async 
    Future.delayed(Duration(seconds: 1)).then((_) 
      var state = Provider.of<AuthState>(context, listen: false);
      state.getCurrentUser();
    );
  

  Widget _body() 
    var height = 150.0;
    return Container(
      height: fullHeight(context),
      width: fullWidth(context),
      child: Container(
        height: height,
        width: height,
        alignment: Alignment.center,
        child: Container(
          padding: EdgeInsets.all(50),
          decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.all(
              Radius.circular(10),
            ),
          ),
          child: Stack(
            alignment: Alignment.center,
            children: <Widget>[
              Platform.isios
                  ? CupertinoActivityIndicator(
                      radius: 35,
                    )
                  : CircularProgressIndicator(
                      strokeWidth: 2,
                    ),
              Image.asset(
                'assets/images/icon-480.png',
                height: 30,
                width: 30,
              )
            ],
          ),
        ),
      ),
    );
  

  @override
  Widget build(BuildContext context) 
    var state = Provider.of<AuthState>(context);
    return Scaffold(
      backgroundColor: TwitterColor.white,
      body: state.authStatus == AuthStatus.NOT_DETERMINED
          ? _body()
          : state.authStatus == AuthStatus.NOT_LOGGED_IN
              ? WelcomePage()
              : HomePage(),
    );
  

这里的罪魁祸首是函数:

  void timer() async 
    Future.delayed(Duration(seconds: 1)).then((_) 
      var state = Provider.of<AuthState>(context, listen: false);
      state.getCurrentUser();
    );
  

我的 main.dart 是我为 AuthState 设置 ChangeNotifierProvider 的地方,看起来像:

import 'package:boots/blocs/place_bloc.dart';
import 'package:boots/helper/locator.dart';
import 'package:boots/state/communitiesState.dart';
import 'package:boots/state/mapState.dart';
import 'package:flutter/material.dart';
import 'package:boots/helper/theme.dart';
import 'package:boots/state/searchState.dart';
import 'package:boots/state/nearbyState.dart';
import 'package:flutter/services.dart';
import 'helper/routes.dart';
import 'state/appState.dart';
import 'package:provider/provider.dart';
import 'state/authState.dart';
import 'state/chats/chatState.dart';
import 'state/feedState.dart';
import 'package:google_fonts/google_fonts.dart';
import 'state/notificationState.dart';

void main() 
  setupLocator();
  WidgetsFlutterBinding.ensureInitialized();

  runApp(MyApp());


class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    SystemChrome.setSystemUIOverlayStyle(
        SystemUiOverlayStyle(statusBarColor: AppColor.primary));
    return MultiProvider(
      providers: [
        ChangeNotifierProvider<AppState>(create: (_) => AppState()),
        ChangeNotifierProvider<AuthState>(create: (_) => AuthState()),
        ChangeNotifierProvider<FeedState>(create: (_) => FeedState()),
        ChangeNotifierProvider<ChatState>(create: (_) => ChatState()),
        ChangeNotifierProvider<SearchState>(create: (_) => SearchState()),
        ChangeNotifierProvider<NotificationState>(
            create: (_) => NotificationState()),
        ChangeNotifierProvider<MapState>(create: (_) => MapState()),
        ChangeNotifierProvider<CommunitiesState>(
            create: (_) => CommunitiesState()),
        ChangeNotifierProvider<NearbyState>(create: (_) => NearbyState()),
        ChangeNotifierProvider<PlaceBloc>(create: (_) => PlaceBloc()),
      ],
      child: MaterialApp(
        title: 'Boots',
        theme: AppTheme.apptheme.copyWith(
          textTheme: GoogleFonts.muliTextTheme(
            Theme.of(context).textTheme,
          ),
        ),
        debugShowCheckedModeBanner: false,
        routes: Routes.route(), // goes to splash.dart
        onGenerateRoute: (settings) => Routes.onGenerateRoute(settings),
        onUnknownRoute: (settings) => Routes.onUnknownRoute(settings),
      ),
    );
  

最后,我的 AuthState 类看起来像:

import 'dart:io';

import 'package:firebase_auth/firebase_auth.dart' as firebaseauth;
import 'package:firebase_database/firebase_database.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:boots/helper/enum.dart';
import 'package:boots/helper/utility.dart';
import 'package:boots/model/user.dart';
import 'package:boots/widgets/customWidgets.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:path/path.dart' as Path;
import 'appState.dart';
import 'package:firebase_database/firebase_database.dart' as dabase;

class AuthState extends AppState 
  AuthStatus authStatus = AuthStatus.NOT_DETERMINED;
  bool isSignInWithGoogle = false;
  firebaseauth.User user;
  String userId;
  final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
  final firebaseauth.FirebaseAuth _firebaseAuth =
      firebaseauth.FirebaseAuth.instance;
  final GoogleSignIn _googleSignIn = GoogleSignIn();
  dabase.Query _profileQuery;
  List<User> _profileUserModelList;
  User _userModel;

  User get userModel => _userModel;

  User get profileUserModel 
    if (_profileUserModelList != null && _profileUserModelList.length > 0) 
      return _profileUserModelList.last;
     else 
      return null;
    
  

  void removeLastUser() 
    _profileUserModelList.removeLast();
  

  /// Logout from device
  void logoutCallback() 
    authStatus = AuthStatus.NOT_LOGGED_IN;
    userId = '';
    _userModel = null;
    user = null;
    _profileUserModelList = null;
    if (isSignInWithGoogle) 
      _googleSignIn.signOut();
      logEvent('google_logout');
    
    _firebaseAuth.signOut();
    notifyListeners();
  

  /// Alter select auth method, login and sign up page
  void openSignUpPage() 
    authStatus = AuthStatus.NOT_LOGGED_IN;
    userId = '';
    notifyListeners();
  

  databaseInit() 
    try 
      if (_profileQuery == null) 
        _profileQuery = kDatabase.child("profile").child(user.uid);
        _profileQuery.onValue.listen(_onProfileChanged);
      
     catch (error) 
      cprint(error, errorIn: 'databaseInit');
    
  

  /// Verify user's credentials for login
  Future<String> signIn(String email, String password,
      GlobalKey<ScaffoldState> scaffoldKey) async 
    try 
      loading = true;
      var result = await _firebaseAuth.signInWithEmailAndPassword(
          email: email, password: password);
      user = result.user;
      userId = user.uid;
      return user.uid;
     catch (error) 
      loading = false;
      cprint(error, errorIn: 'signIn');
      kAnalytics.logLogin(loginMethod: 'email_login');
      customSnackBar(scaffoldKey, error.message);
      // logoutCallback();
      return null;
    
  

  /// Create user from `google login`
  /// If user is new then it create a new user
  /// If user is old then it just `authenticate` user and return firebase user data
  Future<firebaseauth.User> handleGoogleSignIn() async 
    try 
      /// Record log in firebase kAnalytics about Google login
      kAnalytics.logLogin(loginMethod: 'google_login');
      final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
      if (googleUser == null) 
        throw Exception('Google login cancelled by user');
      
      final GoogleSignInAuthentication googleAuth =
          await googleUser.authentication;

      final firebaseauth.AuthCredential credential =
          firebaseauth.GoogleAuthProvider.credential(
        accessToken: googleAuth.accessToken,
        idToken: googleAuth.idToken,
      );
      user = (await _firebaseAuth.signInWithCredential(credential)).user;
      authStatus = AuthStatus.LOGGED_IN;
      userId = user.uid;
      isSignInWithGoogle = true;
      createUserFromGoogleSignIn(user);
      notifyListeners();
      return user;
     on PlatformException catch (error) 
      user = null;
      authStatus = AuthStatus.NOT_LOGGED_IN;
      cprint(error, errorIn: 'handleGoogleSignIn');
      return null;
     on Exception catch (error) 
      user = null;
      authStatus = AuthStatus.NOT_LOGGED_IN;
      cprint(error, errorIn: 'handleGoogleSignIn');
      return null;
     catch (error) 
      user = null;
      authStatus = AuthStatus.NOT_LOGGED_IN;
      cprint(error, errorIn: 'handleGoogleSignIn');
      return null;
    
  

  /// Create user profile from google login
  createUserFromGoogleSignIn(firebaseauth.User user) 
    var diff = DateTime.now().difference(user.metadata.creationTime);
    // Check if user is new or old
    // If user is new then add new user to firebase realtime kDatabase
    if (diff < Duration(seconds: 15)) 
      User model = User(
        bio: 'Edit profile to update bio',
        dob: DateTime(1950, DateTime.now().month, DateTime.now().day + 3)
            .toString(),
        location: 'Somewhere in universe',
        profilePic: user.photoURL,
        displayName: user.displayName,
        email: user.email,
        key: user.uid,
        userId: user.uid,
        contact: user.phoneNumber,
        isVerified: user.emailVerified,
      );
      createUser(model, newUser: true);
     else 
      cprint('Last login at: $user.metadata.lastSignInTime');
    
  

  /// Create new user's profile in db
  Future<String> signUp(User userModel,
      GlobalKey<ScaffoldState> scaffoldKey, String password) async 
    try 
      loading = true;
      var result = await _firebaseAuth.createUserWithEmailAndPassword(
        email: userModel.email,
        password: password,
      );

      user = result.user;
      authStatus = AuthStatus.LOGGED_IN;
      kAnalytics.logSignUp(signUpMethod: 'register');

      await result.user.updateProfile(
        displayName: userModel.displayName,
        photoURL: userModel.profilePic,
      );
      _userModel = userModel;
      _userModel.key = user.uid;
      _userModel.userId = user.uid;
      createUser(_userModel, newUser: true);
      return user.uid;
     catch (error) 
      loading = false;
      cprint(error, errorIn: 'signUp');
      customSnackBar(scaffoldKey, error.message);
      return null;
    
  

  /// `Create` and `Update` user
  /// IF `newUser` is true new user is created
  /// Else existing user will update with new values
  createUser(User user, bool newUser = false) 
    if (newUser) 
      // Create username by the combination of name and id
      user.userName = getUserName(email: user.email);
      kAnalytics.logEvent(name: 'create_newUser');

      // Time at which user is created
      user.createdAt = DateTime.now().toUtc().toString();
    

    kDatabase.child('profile').child(user.userId).set(user.toJsonUpdate());
    _userModel = user;
    if (_profileUserModelList != null) 
      _profileUserModelList.last = _userModel;
    
    loading = false;
  

  /// Fetch current user profile
  Future<firebaseauth.User> getCurrentUser() async 
    try 
      loading = true;
      logEvent('get_currentUSer');
      user = _firebaseAuth != null ? _firebaseAuth.currentUser : null;
      if (user != null) 
        authStatus = AuthStatus.LOGGED_IN;
        userId = user.uid;
        getProfileUser();
       else 
        authStatus = AuthStatus.NOT_LOGGED_IN;
      
      loading = false;
      return user;
     catch (error) 
      loading = false;
      cprint(error, errorIn: 'getCurrentUser');
      authStatus = AuthStatus.NOT_LOGGED_IN;
      return null;
    
  

  /// Reload user to get refresh user data
  reloadUser() async 
    await user.reload();
    user = _firebaseAuth.currentUser;
    if (user.emailVerified) 
      userModel.isVerified = true;
      // If user verifed his email
      // Update user in firebase realtime kDatabase
      createUser(userModel);
      cprint('User email verification complete');
      logEvent('email_verification_complete',
          parameter: userModel.userName: user.email);
    
  

  /// Send email verification link to email2
  Future<void> sendEmailVerification(
      GlobalKey<ScaffoldState> scaffoldKey) async 
    firebaseauth.User user = _firebaseAuth.currentUser;
    user.sendEmailVerification().then((_) 
      logEvent('email_verifcation_sent',
          parameter: userModel.displayName: user.email);
      customSnackBar(
        scaffoldKey,
        'An email verification link is send to your email.',
      );
    ).catchError((error) 
      cprint(error.message, errorIn: 'sendEmailVerification');
      logEvent('email_verifcation_block',
          parameter: userModel.displayName: user.email);
      customSnackBar(
        scaffoldKey,
        error.message,
      );
    );
  

  /// Check if user's email is verified
  Future<bool> isEmailVerified() async 
    firebaseauth.User user = _firebaseAuth.currentUser;
    return user.emailVerified;
  

  /// Send password reset link to email
  Future<void> forgetPassword(String email,
      GlobalKey<ScaffoldState> scaffoldKey) async 
    try 
      await _firebaseAuth.sendPasswordResetEmail(email: email).then((value) 
        customSnackBar(scaffoldKey,
            'A reset password link is sent yo your mail.You can reset your password from there');
        logEvent('forgot+password');
      ).catchError((error) 
        cprint(error.message);
        return false;
      );
     catch (error) 
      customSnackBar(scaffoldKey, error.message);
      return Future.value(false);
    
  

  /// `Update user` profile
  Future<void> updateUserProfile(User userModel, File image) async 
    try 
      if (image == null) 
        createUser(userModel);
       else 
        Reference storageReference = FirebaseStorage.instance
            .ref()
            .child('user/profile/$Path.basename(image.path)');
        UploadTask uploadTask = storageReference.putFile(image);
        await uploadTask.then((value) 
          storageReference.getDownloadURL().then((fileURL) async 
            print(fileURL);
            await user.updateProfile(
              displayName: userModel?.displayName ?? user.displayName,
              photoURL: fileURL,
            );
            if (userModel != null) 
              userModel.profilePic = fileURL;
              createUser(userModel);
             else 
              _userModel.profilePic = fileURL;
              createUser(_userModel);
            
          );
        );
      
      logEvent('update_user');
     catch (error) 
      cprint(error, errorIn: 'updateUserProfile');
    
  

  /// `Fetch` user `detail` whoose userId is passed
  Future<User> getuserDetail(String userId) async 
    User user;
    var snapshot = await kDatabase.child('profile').child(userId).once();
    if (snapshot.value != null) 
      var map = snapshot.value;
      user = User.fromJson(map);
      user.key = snapshot.key;
      return user;
     else 
      return null;
    
  

  /// Fetch user profile
  /// If `userProfileId` is null then logged in user's profile will fetched
  getProfileUser(String userProfileId) 
    try 
      loading = true;
      if (_profileUserModelList == null) 
        _profileUserModelList = [];
      

      userProfileId = userProfileId == null ? user.uid : userProfileId;
      kDatabase
          .child("profile")
          .child(userProfileId)
          .once()
          .then((DataSnapshot snapshot) 
        if (snapshot.value != null) 
          var map = snapshot.value;
          if (map != null) 
            _profileUserModelList.add(User.fromJson(map));
            if (userProfileId == user.uid) 
              _userModel = _profileUserModelList.last;
              _userModel.isVerified = user.emailVerified;
              if (!user.emailVerified) 
                // Check if logged in user verified his email address or not
                reloadUser();
              
              if (_userModel.fcmToken == null) 
                updateFCMToken();
              
            

            logEvent('get_profile');
          
        
        loading = false;
      );
     catch (error) 
      loading = false;
      cprint(error, errorIn: 'getProfileUser');
    
  

  /// if firebase token not available in profile
  /// Then get token from firebase and save it to profile
  /// When someone sends you a message FCM token is used
  void updateFCMToken() 
    if (_userModel == null) 
      return;
    
    getProfileUser();
    _firebaseMessaging.getToken().then((String token) 
      assert(token != null);
      _userModel.fcmToken = token;
      createUser(_userModel);
    );
  

  /// Follow / Unfollow user
  ///
  /// If `removeFollower` is true then remove user from follower list
  ///
  /// If `removeFollower` is false then add user to follower list
  followUser(bool removeFollower = false) 
    /// `userModel` is user who is looged-in app.
    /// `profileUserModel` is user whoose profile is open in app.
    try 
      if (removeFollower) 
        /// If logged-in user `alredy follow `profile user then
        /// 1.Remove logged-in user from profile user's `follower` list
        /// 2.Remove profile user from logged-in user's `following` list
        profileUserModel.followersList.remove(userModel.userId);

        /// Remove profile user from logged-in user's following list
        userModel.followingList.remove(profileUserModel.userId);
        cprint('user removed from following list', event: 'remove_follow');
       else 
        /// if logged in user is `not following` profile user then
        /// 1.Add logged in user to profile user's `follower` list
        /// 2. Add profile user to logged in user's `following` list
        if (profileUserModel.followersList == null) 
          profileUserModel.followersList = [];
        
        profileUserModel.followersList.add(userModel.userId);
        // Adding profile user to logged-in user's following list
        if (userModel.followingList == null) 
          userModel.followingList = [];
        
        userModel.followingList.add(profileUserModel.userId);
      
      // update profile user's user follower count
      profileUserModel.followers = profileUserModel.followersList.length;
      // update logged-in user's following count
      userModel.following = userModel.followingList.length;
      kDatabase
          .child('profile')
          .child(profileUserModel.userId)
          .child('followerList')
          .set(profileUserModel.followersList);
      kDatabase
          .child('profile')
          .child(userModel.userId)
          .child('followingList')
          .set(userModel.followingList);
      cprint('user added to following list', event: 'add_follow');
      notifyListeners();
     catch (error) 
      cprint(error, errorIn: 'followUser');
    
  

  /// Trigger when logged-in user's profile change or updated
  /// Firebase event callback for profile update
  void _onProfileChanged(Event event) 
    if (event.snapshot != null) 
      final updatedUser = User.fromJson(event.snapshot.value);
      if (updatedUser.userId == user.uid) 
        _userModel = updatedUser;
      
      cprint('User Updated');
      notifyListeners();
    
  


这些是我做的时候得到的日志

flutter clean

紧随其后

flutter run --release

日志:

I/flutter (13941): 
E/flutter (13941): [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: NoSuchMethodError: The method 'getCurrentUser' was called on null.
E/flutter (13941): Receiver: null
E/flutter (13941): Tried calling: getCurrentUser()
E/flutter (13941): #0      _SplashPageState.timer.<anonymous closure> (package:boots/page/common/splash.dart:31)
E/flutter (13941): #1      _rootRunUnary (dart:async/zone.dart:1198)
E/flutter (13941): #2      _CustomZone.runUnary (dart:async/zone.dart:1100)
E/flutter (13941): #3      _FutureListener.handleValue (dart:async/future_impl.dart:143)
E/flutter (13941): #4      Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:696)
E/flutter (13941): #5      Future._propagateToListeners (dart:async/future_impl.dart:725)
E/flutter (13941): #6      Future._complete (dart:async/future_impl.dart:519)
E/flutter (13941): #7      new Future.delayed.<anonymous closure> (dart:async/future.dart:323)
E/flutter (13941): #8      _rootRun (dart:async/zone.dart:1182)
E/flutter (13941): #9      _CustomZone.run (dart:async/zone.dart:1093)
E/flutter (13941): #10     _CustomZone.runGuarded (dart:async/zone.dart:997)
E/flutter (13941): #11     _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1037)
E/flutter (13941): #12     _rootRun (dart:async/zone.dart:1190)
E/flutter (13941): #13     _CustomZone.run (dart:async/zone.dart:1093)
E/flutter (13941): #14     _CustomZone.bindCallback.<anonymous closure> (dart:async/zone.dart:1021)
E/flutter (13941): #15     TickerFuture.whenCompleteOrCancel.thunk (package:flutter/src/scheduler/ticker.dart:399)
E/flutter (13941): #16     _Timer._runTimers (dart:isolate-patch/timer_impl.dart:397)
E/flutter (13941): #17     _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:428)
E/flutter (13941): #18     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168)
E/flutter (13941):

我想纠正这个问题,因为我的应用程序因此根本无法启动。非常感谢所有帮助!

【问题讨论】:

把这个放到队列中 【参考方案1】:

原来我的更新导致需要添加新的 firebase 依赖项。

在 pubspec.yaml 中,不得不添加:

firebase_core: ^0.5.2

而且主要是添加

await Firebase.initializeApp();

就在之前

runApp(MyApp());

【讨论】:

以上是关于Provider.of 总是返回 null Flutter的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 Provider.of(context) 调用类方法

电池状态总是返回 Null

GraphQL 总是返回 null

Android getSupportedVideoSizes 总是返回 null

为啥 findFragmentById 总是返回 null

Graphql 查询总是返回 null