deandrehaijiel
deandrehaijiel

Reputation: 153

Flutter Chat App Firebase Auth and Functions with getstream.io

i am currently following this tutorial (https://www.youtube.com/watch?v=y6OlrO3Bzag) on how to create a flutter chat app. the problem that i am facing is that whenever i sign up a new account, it will display an error has occurred and doesn't direct me to the home screen. however, if i sign in or hot restart, i will be able to go to the home screen. i have the logs below but am not sure what is going wrong. any help would be greatly appreciated. the source code is found here: https://github.com/HayesGordon/chatter

firebase_functions/unauthenticated] UNAUTHENTICATED<…>
flutter: \^[[38;5;196m│ \^[[0m\^[[39m\^[[48;5;196m<…>
flutter: \^[[38;5;196m│ \^[[0m\^[[39m\^[[48;5;196m#0      StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:607:7)<…>
flutter: \^[[38;5;196m│ \^[[0m\^[[39m\^[[48;5;196m#1      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:167:18)<…>
flutter: \^[[38;5;196m│ \^[[0m\^[[39m\^[[48;5;196m<asynchronous suspension><…>
flutter: \^[[38;5;196m│ \^[[0m\^[[39m\^[[48;5;196m#2      MethodChannelHttpsCallable.call (package:cloud_functions_platform_interface/src/method_channel/method_channel_https_callable.dart:23:24)<…>
flutter: \^[[38;5;196m│ \^[[0m\^[[39m\^[[48;5;196m<asynchronous suspension><…>
flutter: \^[[38;5;196m│ \^[[0m\^[[39m\^[[48;5;196m#3      HttpsCallable.call (package:cloud_functions/src/https_callable.dart:49:37)<…>
flutter: \^[[38;5;196m│ \^[[0m\^[[39m\^[[48;5;196m<asynchronous suspension><…>
flutter: \^[[38;5;196m│ \^[[0m\^[[39m\^[[48;5;196m#4      _SignUpScreenState._signUp (package:ibchat/screens/sign_up_screen.dart:368:25)<…>
flutter: \^[[38;5;196m│ \^[[0m\^[[39m\^[[48;5;196m<asynchronous suspension><…>
flutter: \^[[38;5;196m│ \^[[0m\^[[39m\^[[48;5;196m<…>
flutter: \^[[38;5;196m├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄<…>
flutter: \^[[38;5;196m│ #0   StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:607:7)<…>
flutter: \^[[38;5;196m│ #1   MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:167:18)<…>
flutter: \^[[38;5;196m│ #2   <asynchronous suspension><…>
flutter: \^[[38;5;196m│ #3   MethodChannelHttpsCallable.call (package:cloud_functions_platform_interface/src/method_channel/method_channel_https_callable.dart:23:24)<…>
flutter: \^[[38;5;196m│ #4   <asynchronous suspension><…>
flutter: \^[[38;5;196m│ #5   HttpsCallable.call (package:cloud_functions/src/https_callable.dart:49:37)<…>
flutter: \^[[38;5;196m│ #6   <asynchronous suspension><…>
flutter: \^[[38;5;196m│ #7   _SignUpScreenState._signUp (package:ibchat/screens/sign_up_screen.dart:368:25)<…>
flutter: \^[[38;5;196m├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄<…>
flutter: \^[[38;5;196m│ ⛔ Sign up error<…>
flutter: \^[[38;5;196m└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────<…>

Error: Do not use BuildContexts across async gaps. (with **)

Future<void> _signUp() async {
    if (_formKey.currentState!.validate()) {
      setState(() {
        _loading = true;
      });
      try {
        // Authenticate with Firebase
        final creds =
            await firebase.FirebaseAuth.instance.createUserWithEmailAndPassword(
          email: _emailController.text,
          password: _passwordController.text,
        );
        final user = creds.user;
        if (user == null) {
          **ScaffoldMessenger.of(context)**.showSnackBar(
            const SnackBar(content: Text('Member not found')),
          );
          return;
        }

        // Set Firebase display name and profile picture
        List<Future<void>> futures = [
          if (_profilePictureController.text.isNotEmpty)
            creds.user!.updatePhotoURL(_profilePictureController.text),
          creds.user!.updateDisplayName(_nameController.text),
        ];

        await Future.wait(futures);

        // Create Stream user and get token using Firebase Functions
        final callable = functions.httpsCallable('createStreamUserAndGetToken');
        final results = await callable();

        // Connect user to Stream and set user data
        final client = **StreamChatCore.of(context)**.client;
        await client.connectUser(
          User(
            image: _profilePictureController.text,
            id: creds.user!.uid,
            name: _nameController.text,
            /*designation: _designationController.text,
            company: _companyController.text,*/
          ),
          results.data,
        );

        // Navigate to home screen
        await **Navigator.of(context)**.pushReplacement(HomeScreen.route);
      } on firebase.FirebaseAuthException catch (e) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(backgroundColor: Colors.red, content: Text(e.message ?? 'Auth error')),
        );
      } catch (e, st) {
        logger.e('Sign up error', e, st);
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(backgroundColor: Colors.red, content: Text('An error occured')),
        );
      }
      setState(() {
        _loading = false;
      });
    }
  }
Future<void> _signIn() async {
    if (_formKey.currentState!.validate()) {
      setState(() {
        _loading = true;
      });
      try {
        // Authenticate with Firebase
        final creds =
            await firebase.FirebaseAuth.instance.signInWithEmailAndPassword(
          email: _emailController.text,
          password: _passwordController.text,
        );

        final user = creds.user;

        if (user == null) {
          **ScaffoldMessenger.of(context)**.showSnackBar(
            const SnackBar(content: Text('Member not found')),
          );
          return;
        }

        // Get Stream user token from Firebase Functions
        final callable = functions.httpsCallable('getStreamUserToken');
        final results = await callable();

        // Connnect stream user
        final client = **StreamChatCore.of(context)**.client;
        await client.connectUser(
          User(id: creds.user!.uid),
          results.data,
        );

        // Navigate to home screen
        await **Navigator.of(context)**.pushReplacement(HomeScreen.route);
      } on firebase.FirebaseAuthException catch (e) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
              backgroundColor: Colors.red,
              content: Text(e.message ?? 'Authentication error')),
        );
      } catch (e, st) {
        logger.e('Sign in error, ', e, st);
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(
              backgroundColor: Colors.red, content: Text('Error occured')),
        );
      }
      setState(() {
        _loading = false;
      });
    }
  }
Future<void> _handleAuthenticatedState() async {
    final auth = firebase.FirebaseAuth.instance;
    if (!mounted) {
      return;
    }
    listener = auth.authStateChanges().listen((user) async {
      if (user != null) {
        // get Stream user token
        final callable =
            FirebaseFunctions.instance.httpsCallable('getStreamUserToken');

        final results = await Future.wait([
          callable(),
          // delay to show loading indicator
          Future.delayed(const Duration(milliseconds: 700)),
        ]);

        // connect Stream user
        final client = **StreamChatCore.of(context)**.client;
        await client.connectUser(
          User(id: user.uid),
          results[0]!.data,
        );

        // authenticated
        **Navigator.of(context)**.pushReplacement(HomeScreen.route);
      } else {
        // delay to show loading indicator
        await Future.delayed(const Duration(milliseconds: 700));
        // not authenticated
        **Navigator.of(context)**.pushReplacement(SignInScreen.route);
      }
    });
  }
Future<void> _signOut() async {
    setState(() {
      _loading = true;
    });

    try {
      await StreamChatCore.of(context).client.disconnectUser();
      await firebase.FirebaseAuth.instance.signOut();

      **Navigator.of(context)**.pushReplacement(SplashScreen.route);
    } on Exception catch (e, st) {
      logger.e('Could not sign out', e, st);
      setState(() {
        _loading = false;
      });
    }
  }

Upvotes: 0

Views: 341

Answers (1)

Gordon Hayes
Gordon Hayes

Reputation: 271

I'm the author of the video tutorial you linked above.

For anyone reading this and who is interested in Firebase Auth + Stream, please see our updated Flutter guide: https://getstream.io/chat/docs/sdk/flutter/guides/token_generation_with_firebase/

Stream now has a Firebase extension to easily manage the heavy lifting for you. So the video above needs an updated version.

To answer your question:

It seems like the call to "createStreamUserAndGetToken" cloud function is happening even though the Firebase user is not authenticated. I see you created a ticket for this on the repo here: https://github.com/HayesGordon/chatter/issues/8

So let's discuss it further there.

With regards to your limiting issue for "BuildContexts across async gaps" see here: https://dart-lang.github.io/linter/lints/use_build_context_synchronously.html

Upvotes: 0

Related Questions