Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Google SignIn & Firebase Auth not working anymore after updating #46

Closed
kamami opened this issue Apr 9, 2019 · 11 comments
Closed

Google SignIn & Firebase Auth not working anymore after updating #46

kamami opened this issue Apr 9, 2019 · 11 comments

Comments

@kamami
Copy link

kamami commented Apr 9, 2019

After updating flutter and some packages the Google SignIn is not working anymore. It seems that the package changed its procedure with the newest version.

https://pub.dartlang.org/packages/firebase_auth/versions/0.8.4+2
flutter/flutter#27133

Should not be too difficult too update the code.

Thank you and btw great project and good job!

@mdanics
Copy link
Owner

mdanics commented Apr 10, 2019

I did an update of all the packages on a the branch update_dependencies but haven't done sufficient testing to merge it with master.

If you have a chance, can you check it out and let me know if it works for you on your end?

Thanks

@GustavoContreiras
Copy link
Contributor

GustavoContreiras commented Apr 14, 2019

There are too many bugs and errors on the first run (with fresh database, fresh account and fresh installation with clean cache).
I think you should try and see for yourself.
You did a great job with this project but this kind of thing can make people a little angry.

Also I think you shoud modularize and encapsulate some classes and methods and maybe change some variables name. Like a big revision. This is important, specially if you are using it as a portfolio for your curriculum and now flutter is getting bigger and there will be more people using yor project to learn.

I'll try to make pull request every error that I find and fix. Some are easy, you just need to assert that some variables are not null.

@GustavoContreiras
Copy link
Contributor

GustavoContreiras commented Apr 15, 2019

Something like that on main.dart:
(copy and paste wont work cause I am using circle_wave_progress package and because I updated firebase auth to 0.7.0, but take a look at the architecture, how the pages are loading and etc)

A little explaining:
If isLogged boolean is null, show a loading screen (will be shown for 1 sec) and then create Google and Firebase instances and then update isLogged boolean
If isLogged boolean is false, show login screen
If user press login button, check if is logged and then update isLogged boolean
If isLogged boolean is true, if is first login, create user from Google and update database, and show logged screen
If isLogged boolean is true, if is not first login, load user from database snapshot and show logged screen

import 'dart:async';
import 'dart:io' show Platform;
import 'dart:math';

import 'package:circle_wave_progress/circle_wave_progress.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:geocoder/geocoder.dart';
import 'package:google_sign_in/google_sign_in.dart';

import 'activity_feed.dart';
import 'circular_logo_progress.dart';
import 'feed.dart';
import 'profile_page.dart';
import 'search_page.dart';
import 'upload_page.dart';
import 'user.dart';

void main() {
  print('[Main] START');
  Firestore.instance.settings(timestampsInSnapshotsEnabled: true).then((_) {
    print('[Main] Firestore timestamps in snapshots set');},
    onError: (_) => print('[Main] Error')
  );
  runApp(Fluttergram());
  print('[Main] DONE');
}

final FirebaseAuth firebaseAuth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
PageController pageController = PageController();
User currentUserModel;
Address userLocation;

class Fluttergram extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Fluttergram',
      theme: ThemeData(
        backgroundColor: Colors.purple[300],
        primarySwatch: Colors.blue,
        buttonColor: Colors.pink,
        primaryIconTheme: IconThemeData(color: Colors.black)),
      home: RootPage(),
    );
  }
}

class RootPage extends StatefulWidget {
  RootPage({Key key}) : super(key: key);

  @override
  _RootPageState createState() => _RootPageState();
}

class _RootPageState extends State<RootPage> {
  int _page = 0;
  bool _isLogged;
  bool _loadingIcon = false;
  bool _notificationsAreSet = false;

  Future<Null> _setUpNotifications() async {
  print('[SetUpNotifications] START');

  if (Platform.isAndroid) {

    _firebaseMessaging.configure(
      onMessage: (Map<String, dynamic> message) async {
        print('[SetUpNotifications] on message $message');
      },
      onResume: (Map<String, dynamic> message) async {
        print('[SetUpNotifications] on resume $message');
      },
      onLaunch: (Map<String, dynamic> message) async {
        print('[SetUpNotifications] on launch $message');
      },
    );

    print('[SetUpNotifications] _firebaseMessaging configured');

    _firebaseMessaging.getToken().then((token) {
      print("[SetUpNotifications] _firebaseMessaging.getToken(): " + token);
      if (currentUserModel != null) {
        Firestore.instance.collection('insta_users').document(currentUserModel.id).updateData({"androidNotificationToken": token});
        print('[SetUpNotifications] androidNotificationToken updated for ${currentUserModel.displayName}');
        setState(() => _notificationsAreSet = true);
      } else {
        print('[SetUpNotifications] androidNotificationToken not updated because currentUserModel == null');
      }
    });
  }

  print('[SetUpNotifications] DONE');
}

  @override
  Widget build(BuildContext context) {

    if (_isLogged != null) {
      if (_isLogged) {
        if (!_notificationsAreSet)
          _setUpNotifications();
        
        return Scaffold(
          body: PageView(
            children: [
              Container(
                color: Colors.white,
                child: Feed(),
              ),
              Container(color: Colors.white, child: SearchPage()),
              Container(
                color: Colors.white,
                child: Uploader(),
              ),
              Container(
                color: Colors.white, child: ActivityFeedPage()),
              Container(
                color: Colors.white,
                child: ProfilePage(userId: googleSignIn.currentUser.id)
              ),
            ],
            controller: pageController,
            physics: NeverScrollableScrollPhysics(),
            onPageChanged: (page) => setState(() => this._page = page),
          ),

          bottomNavigationBar: CupertinoTabBar(
            activeColor: Colors.orange,
            items: <BottomNavigationBarItem>[
              BottomNavigationBarItem(
                icon: Icon(Icons.home,
                  color: (_page == 0) ? Colors.black : Colors.grey),
                title: Container(height: 0.0),
                backgroundColor: Colors.white),
              BottomNavigationBarItem(
                icon: Icon(Icons.search,
                  color: (_page == 1) ? Colors.black : Colors.grey),
                title: Container(height: 0.0),
                backgroundColor: Colors.white),
              BottomNavigationBarItem(
                icon: Icon(Icons.add_circle,
                  color: (_page == 2) ? Colors.black : Colors.grey),
                title: Container(height: 0.0),
                backgroundColor: Colors.white),
              BottomNavigationBarItem(
                icon: Icon(Icons.star,
                  color: (_page == 3) ? Colors.black : Colors.grey),
                title: Container(height: 0.0),
                backgroundColor: Colors.white),
              BottomNavigationBarItem(
                icon: Icon(Icons.person,
                  color: (_page == 4) ? Colors.black : Colors.grey),
                title: Container(height: 0.0),
                backgroundColor: Colors.white
              ),
            ],
            onTap: (page) => pageController.jumpToPage(page),
            currentIndex: _page,
          ),
        ); 
      }
      return _buildLoginScreen();
    }
    return _buildLoadingScreen();
  }

  @override
  void dispose() {
    pageController.dispose();
    super.dispose();
  }

  @override
  void initState() {

    print('[RootPage] [InitState] START');

    try{
      googleSignIn.isSignedIn().then((bool loginStatus) {
        if (loginStatus == true && currentUserModel.id == null) {
          googleSignIn.signIn().then((onValue) {
            GoogleSignInAccount googleSignInAcc = onValue;
            if (googleSignInAcc != null) {
              googleSignInAcc.authentication.then((GoogleSignInAuthentication googleSignInAuth) {            
               _signInWithGoogle(googleSignInAuth).then((_) {
                  print('[AppController] [InitState] currentUser.id: ${currentUserModel.id}, currentUser.username: ${currentUserModel.username}');
                });
              });
            }
          });
        }    
        print('[RootPage] [InitState] isLogged: $_isLogged');
        setState(() => _isLogged = loginStatus);
      });
    } catch(error) { print(error); }

    super.initState(); 

    print('[RootPage] [InitState] DONE');
  }

  Widget _buildLoginScreen() {

    return Scaffold(
      backgroundColor: Colors.blue[500],
      body: GestureDetector(
        onTap: _onClickLogin,
        child: Stack(
          children: <Widget>[
            
            Center(
              child: Container(  // Back wave
                child: CircleWaveProgress(
                  size: 256.0, 
                  backgroundColor: Colors.orange[300], 
                  waveColor: Colors.blue[900], 
                  borderColor: Colors.transparent, 
                  borderWidth: 0.0, 
                  progress: 24.0,
                )
              ),
            ),
          
            Center(
              child: Container(  // Front wave
                child: CircleWaveProgress(
                  size: 256.0, 
                  backgroundColor: Colors.transparent, 
                  waveColor: Colors.blue[600], 
                  borderColor: Colors.black, 
                  borderWidth: 3.0, 
                  progress: 14.0,
                )
              ),
            ),

            Center(
              child: Container(  // Title
                padding: EdgeInsets.only(bottom: 48.0),
                child: const Text('WaveCheck',
                  style: const TextStyle(
                    fontFamily: "Raustila-Regular", color: Colors.black, fontSize: 64.0, 
                    shadows: [
                      Shadow(offset: Offset(-0.5, -0.5), color: Colors.orange),
                      Shadow(offset: Offset(0.5, -0.5), color: Colors.orange),
                      Shadow(offset: Offset(0.5, 0.5), color: Colors.orange),
                      Shadow(offset: Offset(-0.5, 0.5), color: Colors.orange),
                    ]
                  )
                ),
              ),
            ),

            Center(
              child: Container(  // Subtitle
                padding: EdgeInsets.only(top: 34.0),
                child: const Text('Sua onda começa aqui!',
                  style: const TextStyle(
                    fontFamily: "Raustila-Regular", color: Colors.black, fontSize: 32.0, 
                    shadows: [
                      Shadow(offset: Offset(-0.5, -0.5), color: Colors.orange),
                      Shadow(offset: Offset(0.5, -0.5), color: Colors.orange),
                      Shadow(offset: Offset(0.5, 0.5), color: Colors.orange),
                      Shadow(offset: Offset(-0.5, 0.5), color: Colors.orange),
                    ]
                  )
                ),
              ),
            ),

            Center(
              child: Container(  // Tip
                padding: EdgeInsets.only(top: 285.0),
                child: const Text('Toque na tela para se conectar',
                  style: const TextStyle(
                    color: Colors.black, fontSize: 11.0, 
                  )
                ),
              ),
            ),

            Container(  // Progress indicator
              padding: EdgeInsets.only(top: 400.0),
              alignment: Alignment.bottomCenter,
              child: Visibility(
                visible: _loadingIcon, 
                child: CircularLogoProgress()
              ),
            ),
            
          ],
        ),
      ),
    );
  }

  Widget _buildLoadingScreen() {
    print('[RootPage] [BuildStartScreen]');
    return Scaffold(
      backgroundColor: Colors.blue[500],
      body: Center(
        child: Stack(
          children: <Widget>[

            Container(
              padding: EdgeInsets.only(top: 445.0),
              child: CircularLogoProgress()
            ),

            Center(
              child: Container(
                padding: EdgeInsets.only(top: 490.0),
                child: const Text('Aguarde a série',
                  style: const TextStyle(
                    color: Colors.black, fontSize: 11.0, fontWeight: FontWeight.w400 
                  )
                ),
              ),
            ),

          ],
        )
      ),
    );
  }

  User _createNewUser(DocumentSnapshot document, String username, Address userLocation) {

    print('[User] [createNewUser] START');

    return new User(
      username: username,
      id: document.documentID,
      photoUrl: document['photoUrl'] == null 
        ? '' 
        : document['photoUrl'],    
      bio: document['bio'] == null 
        ? '' 
        : document['bio'],
      displayName: document['displayName'] == null 
        ? '' 
        : document['displayName'],
      email: document['email'] == null 
        ? '' 
        : document['email'] ,
      followers: {},
      following: {},
      address: userLocation,
    );
  }

  void _onClickLogin() async {

    print('[RootPage] [OnClickLogin] START');

    setState(() => _loadingIcon = true);

    GoogleSignInAccount googleSignInAcc = await googleSignIn.signIn();
    GoogleSignInAuthentication googleSignInAuth = await googleSignInAcc.authentication;
    await _signInWithGoogle(googleSignInAuth);  
 
    setState(() => _loadingIcon = false);

    print('[RootPage] [OnClickLogin] currentUserModel.id: ${currentUserModel.id}, currentUserModel.username: ${currentUserModel.username}');

    currentUserModel != null ? _isLogged = true : _isLogged = false;

    print('[RootPage] [OnClickLogin] DONE');
  }

  Future<void> _signInWithGoogle(GoogleSignInAuthentication googleSignInAuth) async {
 
    FirebaseUser _firebaseUser;
    AuthCredential _authCredential;
    DocumentReference _firebaseUserDocRef;
    DocumentSnapshot _firebaseUserDocSnap;

    print('[Firebase] [SignInWithGoogle] START');

    try{
      
      if (googleSignInAuth != null)
        _authCredential = GoogleAuthProvider.getCredential(accessToken: googleSignInAuth.accessToken, idToken: googleSignInAuth.idToken,);

      _firebaseUser = await firebaseAuth.signInWithCredential(_authCredential);

      if (_firebaseUser != null)
        _updateUserOnFirestore(firebaseUser: _firebaseUser);

      if (_firebaseUser != null)
        _firebaseUserDocRef = Firestore.instance.collection('insta_users').document(_firebaseUser.uid);

      if (_firebaseUserDocRef != null)
        _firebaseUserDocSnap = await _firebaseUserDocRef.get();

      if (_firebaseUserDocSnap != null) {
        
        // First login
        if (_firebaseUserDocSnap.data['username'] == null) {
          final String _username = _firebaseUser.displayName.toString().replaceAll(RegExp(r"\s+\b|\b\s"), "").toLowerCase().trim() + Random().nextInt(1000000).toString();
          currentUserModel = _createNewUser(_firebaseUserDocSnap, _username, userLocation);
          _updateUserOnFirestore(firebaseUser: _firebaseUser, username: _username);
        }
        
        // Not first login
        else {
          User.createFromDocument(_firebaseUserDocSnap, userLocation);
        }
      }
    } catch(error) {print(error);}
  
    print('[Firebase] [SignInWithGoogle] DONE');
  }

  /// Should be called only when new user is created and whe bio, photo, email, display name or username is changed.
  void _updateUserOnFirestore({FirebaseUser firebaseUser, DocumentReference documentReference, String username, String email, String photoUrl, 
                      String displayName, String bio, bool merge = true}) async {

    FirebaseUser _firebaseUser;
    DocumentReference _documentReference;
    
    if (firebaseUser == null) {
      try{_firebaseUser = await firebaseAuth.currentUser();}
      catch(error){print(error);}
    }
    else {
      _firebaseUser = firebaseUser;
    }

    if (documentReference == null) {
      try{_documentReference = Firestore.instance.collection('insta_users').document(_firebaseUser.uid);}
      catch(error){print(error);}
    }
    else {
      _documentReference = documentReference;
    }

    if (_firebaseUser != null && _documentReference != null) {

      if (username != null) {_documentReference.setData({'username': username}, merge: merge);}
      if (displayName != null) {_documentReference.setData({'displayName': displayName}, merge: merge);}
      if (bio != null) {_documentReference.setData({'bio': bio}, merge: merge);}
      if (email != null) {_documentReference.setData({'email': email}, merge: merge);} 
      if (photoUrl != null) {_documentReference.setData({'photoUrl': photoUrl}, merge: merge);}

      try {
        _documentReference.setData({
          'uid': googleSignIn.currentUser.id,
          'email': googleSignIn.currentUser.email,
          'photoURL': googleSignIn.currentUser.photoUrl,
          'displayName': googleSignIn.currentUser.displayName,
        }, merge: merge);
      } 
      catch(error) {print(error);}
    }
  }
}

But in my opinion Firebase functions should be encapsulated and use a interface.
And maybe a class for CurrentUser that extends User and is a singleton with non-final fields (this way you can change bio and username without acessing database, but I don't know if this is the best approach).

@mdanics
Copy link
Owner

mdanics commented Apr 15, 2019

Thank you I appreciate the feedback and help. I'm currently in the middle of exams so I will not be able to work on this until the summer, at which I will at a minimum fix these bugs + explore doing a full revision.

@kamami
Copy link
Author

kamami commented Apr 17, 2019

@GustavoContreiras could you please provide your User.dart file? I would like to update my version with your improvements. Would appreciate it! Thanks

@GustavoContreiras
Copy link
Contributor

@kamami I don't recommend copy and paste, but you can try.

user.dart:

import 'dart:math';

import 'package:Fluttergram/main.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:geocoder/geocoder.dart';
import 'package:google_sign_in/google_sign_in.dart';

final FirebaseAuth firebaseAuth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();

class User {

  final String email;
  final String id;
  final String photoUrl;
  final String username;
  final String displayName;
  final String bio;
  final Map followers;
  final Map following;
  
  const User({
    this.username,
    this.id,
    this.photoUrl,
    this.email,
    this.displayName,
    this.bio,
    this.followers,
    this.following,
  });

  /// Used to load other users
  factory User.loadFromDocument(DocumentSnapshot document) {
    //print ('[User] [Constructor] [LoadFromDocument] document.data: ${document.data}');
    return User(
      email: document.data['email'],
      username: document.data['username'],
      photoUrl: document.data['photoURL'],
      id: document.documentID,
      displayName: document.data['displayName'],
      bio: document.data['bio'] == null ? '' : document.data['bio'],
      followers: document.data['followers'],
      following: document.data['following'],
    );
  }
}

class CurrentUser {
  
  // Singleton
  static CurrentUser instance;
  
  // The CurrentUser has:
  String email;
  String id;
  String photoUrl;
  String username;
  String displayName;
  String bio;
  Map followers;
  Map following;
  Address address;

  CurrentUser({
    this.username,
    this.id,
    this.photoUrl,
    this.email,
    this.displayName,
    this.bio,
    this.followers,
    this.following,
    this.address
  });

  factory CurrentUser._singletonConstructor({DocumentSnapshot documentSnapshot, String username}) {

    // Creates from snapshot from document from database (any login that isnt the first)
    if (documentSnapshot != null) {
      print('[CurrentUser] [Constructor] creating from document snapshot');

      if (documentSnapshot.data != null) {
        print('[CurrentUser] [Constructor] documentSnapshot.data != null');
        return CurrentUser(
          bio: documentSnapshot.data['bio'] == null ? '' : documentSnapshot.data['bio'],
          displayName: documentSnapshot.data['displayName'],
          username: documentSnapshot.data['username'],
          id:  documentSnapshot.data['uid'],
          photoUrl: documentSnapshot.data['photoURL'],
          followers: documentSnapshot.data['followers'] == null ? new Map<dynamic, dynamic>() : documentSnapshot.data['followers'],
          following: documentSnapshot.data['following'] == null ? new Map<dynamic, dynamic>() : documentSnapshot.data['following'],
          address: userLocation
        );
      }
      else {
        print('[CurrentUser] [Constructor] documentSnapshot.data == null');
      }
    }

    // Creates from Google account
    if (googleSignIn.currentUser != null) {
      print('[CurrentUser] [Constructor] googleSignIn.currentUser != null');
      print('[CurrentUser] [Constructor] creating from Google');
      return CurrentUser(
        bio: '',
        displayName: googleSignIn.currentUser.displayName,
        username: username,
        id: googleSignIn.currentUser.id,
        photoUrl: googleSignIn.currentUser.photoUrl,
        followers: {},
        following: {googleSignIn.currentUser.id: true},
        address: userLocation
      );
    }

    return null; 
  }

  static signInWithGoogle() async {
 
    FirebaseUser _firebaseUser;
    AuthCredential _authCredential;
    DocumentReference _firebaseUserDocRef;
    DocumentSnapshot _firebaseUserDocSnap;
    GoogleSignInAccount _googleSignInAcc;

    print('[CurrentUser] [SignInWithGoogle] START');

    print('[CurrentUser] [SignInWithGoogle] googleSignIn.currentUser: ${googleSignIn.currentUser}');


    // Attempts to sign in a previously authenticated user without interaction
    if (isSigned) {
      print('[CurrentUser] [SignInWithGoogle] [SignInSilently] start');
      _googleSignInAcc = await googleSignIn.signInSilently();
      print('[CurrentUser] [SignInWithGoogle] [SignInSilently] done');
    }

    // Waits for user to choose account to sign
    else {
      print('[CurrentUser] [SignInWithGoogle] [SignIn] start');
      _googleSignInAcc = await googleSignIn.signIn();
      print('[CurrentUser] [SignInWithGoogle] [SignIn] done');
    }

    if (_googleSignInAcc != null) {
      print('[CurrentUser] [SignInWithGoogle] _googleSignInAcc.email: ${_googleSignInAcc.email}');

      // Waits for authentication with chosen account
      GoogleSignInAuthentication _googleSignInAuth = await _googleSignInAcc.authentication;
      print('[CurrentUser] [SignInWithGoogle] _googleSignInAuth.accessToken != null? ${_googleSignInAuth.accessToken != null}');
      
      // Gets credential to sign
      if (_googleSignInAuth != null) {
        _authCredential = GoogleAuthProvider.getCredential(accessToken: _googleSignInAuth.accessToken, idToken: _googleSignInAuth.idToken,);
        print('[CurrentUser] [SignInWithGoogle] _authCredential: $_authCredential');
      }
      
      // Sign with credential
      if (_authCredential != null) {
        _firebaseUser = await firebaseAuth.signInWithCredential(_authCredential);
        print('[CurrentUser] [SignInWithGoogle] _firebaseUser.displayName: ${_firebaseUser.displayName}, _firebaseUser.uid: ${_firebaseUser.uid}');
      }

      // Creates/gets a document for the firebase user signed (authenticated with Google)
      if (_firebaseUser != null) {
        _firebaseUserDocRef = Firestore.instance.collection('insta_users').document(googleSignIn.currentUser.id);
      }
           
      if (_firebaseUserDocRef != null) {

        // Updates basic data but don't update username
        _firebaseUserDocRef.setData({
          'uid': googleSignIn.currentUser.id,
          'email': googleSignIn.currentUser.email,
          'photoURL': googleSignIn.currentUser.photoUrl,
          'displayName': googleSignIn.currentUser.displayName,
        }, merge: true);

        print('[CurrentUser] [SignInWithGoogle] _firebaseUserDocRef.documentID: ${_firebaseUserDocRef.documentID}');

        // Gets a snapshot of the document
        _firebaseUserDocSnap = await _firebaseUserDocRef.get();
      }
      
      if (_firebaseUserDocSnap != null) {
        print('[CurrentUser] [SignInWithGoogle] _firebaseUserDocSnap.documentID: ${_firebaseUserDocSnap.documentID}');

        // First login (username not set)
        if (_firebaseUserDocSnap.data['username'] == null || _firebaseUserDocSnap.data['username'] == '') {
          print('[CurrentUser] [SignInWithGoogle] first login');
          final String _username = _firebaseUser.displayName.toString().replaceAll(RegExp(r"\s+\b|\b\s"), "").toLowerCase().trim() + Random().nextInt(1000000).toString();
          CurrentUser.instance = CurrentUser._singletonConstructor(username: _username);
          _firebaseUserDocRef.setData({
            'username': _username,
            'followers': {},
            'following': {googleSignIn.currentUser.id: true}
          }, merge: true);
        }
        
        // Not first login
        else {
          print('[CurrentUser] [SignInWithGoogle] not first login');
          CurrentUser.instance = CurrentUser._singletonConstructor(documentSnapshot: _firebaseUserDocSnap);
        }     
      }
    }

    else {
      print('[CurrentUser] [SignInWithGoogle] Error: _googleSignInAcc == null');
    }

    print('[CurrentUser] [SignInWithGoogle] CurrentUser.instance.id: ${CurrentUser.instance.id}, CurrentUser.instance.username: ${CurrentUser.instance.username}');
  
    print('[CurrentUser] [SignInWithGoogle] DONE');
  }
}

main.dart:

import 'dart:async';
import 'dart:io' show File, Platform;

import 'package:circle_wave_progress/circle_wave_progress.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:geocoder/geocoder.dart';
import 'package:image_picker/image_picker.dart';

import 'activity_feed.dart';
import 'circular_logo_progress.dart';
import 'feed.dart';
import 'profile_page.dart';
import 'search_page.dart';
import 'upload_page.dart';
import 'package:Fluttergram/user.dart';

void main() {
  print('[Main] START');
  Firestore.instance.settings(timestampsInSnapshotsEnabled: true).then((_) {
    print('[Main] Firestore timestamps in snapshots set');},
    onError: (_) => print('[Main] Error')
  );
  runApp(WaveCheck());
  print('[Main] DONE');
}


final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
PageController pageController = PageController();
Address userLocation;
bool isSigned;

class WaveCheck extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'WaveCheck',
      theme: ThemeData(primaryIconTheme: IconThemeData(color: Colors.black)),
      home: RootPage(),
    );
  }
}

class RootPage extends StatefulWidget {
  RootPage({Key key}) : super(key: key);

  @override
  _RootPageState createState() => _RootPageState();
}

class _RootPageState extends State<RootPage> {
  int _page = 0;
  bool _loadingIcon = false;
  bool _notificationsAreSet = false;

  Future<Null> _setUpNotifications() async {
  print('[SetUpNotifications] START');

  if (Platform.isAndroid) {

    _firebaseMessaging.configure(
      onMessage: (Map<String, dynamic> message) async {
        print('[SetUpNotifications] on message $message');
      },
      onResume: (Map<String, dynamic> message) async {
        print('[SetUpNotifications] on resume $message');
      },
      onLaunch: (Map<String, dynamic> message) async {
        print('[SetUpNotifications] on launch $message');
      },
    );

    print('[SetUpNotifications] _firebaseMessaging configured');

    try{
      _firebaseMessaging.getToken().then((token) {
        print("[SetUpNotifications] _firebaseMessaging.getToken(): " + token);
        if (CurrentUser.instance.id != null) {
          Firestore.instance.collection('insta_users').document(CurrentUser.instance.id).updateData({"androidNotificationToken": token});
          print('[SetUpNotifications] androidNotificationToken updated for ${CurrentUser.instance.displayName}');
          setState(() => _notificationsAreSet = true);
        } else {
          print('[SetUpNotifications] androidNotificationToken not updated because CurrentUser.instance.id == null');
        }
      });
    } catch(error) {print(error);}
  }

  print('[SetUpNotifications] DONE');
}

  @override
  Widget build(BuildContext context) {

    print('[RootPage] [Build] START');

    if (isSigned != null) {
      print('[RootPage] [Build] isSigned != null');
      if (isSigned && CurrentUser.instance != null) {

        if ( !_notificationsAreSet)
          _setUpNotifications();

        print('[RootPage] [Build] isSigned == true && CurrentUser.instance != null');
        print('[RootPage] [Build] building home screen');
        return Scaffold(
          backgroundColor: Colors.blue[100],
          body: PageView(
            controller: pageController,
            physics: NeverScrollableScrollPhysics(),
            onPageChanged: (pageTapped) async {
              if (pageTapped != 2) {
                setState(() => this._page = pageTapped);
              }
              else {
                File _filePicked = await ImagePicker.pickImage(source: ImageSource.camera);
                Navigator.of(context).push(MaterialPageRoute(
                  builder: (BuildContext context) => UploadPage(lastFilePicked: _filePicked, address: userLocation,))
                );
              }
            }, 
            children: [
              Container(
                color: Colors.white,
                child: Feed(),
              ),
              Container(
                color: Colors.white, 
                child: SearchPage()),
              
              Container(
                color: Colors.white,
                child: FutureBuilder(
                  future: this._page == 2 ? ImagePicker.pickImage(source: ImageSource.camera) : null,
                  initialData: Container(), // Called if the future is null
                  builder: (BuildContext context, AsyncSnapshot snapshot) {
  
                    if (snapshot.connectionState == ConnectionState.done) {
                      File _imageFile = snapshot.data;
                      print('[RootPage] [Camera] _imageFile: $_imageFile');
                      if (_imageFile != null)
                        return UploadPage(lastFilePicked: _imageFile, address: userLocation);
                    }

                    return Container(color: Colors.blue[100]);
                  },
                ),
              ),

              Container(
                color: Colors.white, child: ActivityFeedPage()),
              Container(
                color: Colors.white,
                child: ProfilePage(userId: CurrentUser.instance != null ? CurrentUser.instance.id : '')
              ),
            ],
          ),

          bottomNavigationBar: CupertinoTabBar(
            backgroundColor: Colors.blue[900],
            activeColor: Colors.orange,
            onTap: (page) => pageController.jumpToPage(page),
            currentIndex: _page,
            items: <BottomNavigationBarItem>[
              BottomNavigationBarItem(
                icon: Icon(Icons.library_books, color: (_page == 0) ? Colors.orange[400] : Colors.black),
                title: Text('Reports', style: TextStyle(color: (_page == 0) ? Colors.orange[400] : Colors.black),),
                backgroundColor: Colors.white),
              BottomNavigationBarItem(
                icon: Icon(Icons.flare, color: (_page == 1) ? Colors.orange[400] : Colors.black),
                title: Text('Forecast', style: TextStyle(color: (_page == 1) ? Colors.orange[400] : Colors.black),),
                backgroundColor: Colors.white),
              BottomNavigationBarItem(
                icon: Icon(Icons.camera, size: 48, color: (_page == 2) ? Colors.orange[400] : Colors.black),
                title: Container(height: 0.0),
                backgroundColor: Colors.white),
              BottomNavigationBarItem(
                icon: Icon(Icons.videocam, color: (_page == 3) ? Colors.orange[400] : Colors.black),
                title: Text('Livecams', style: TextStyle(color: (_page == 3) ? Colors.orange[400] : Colors.black),),
                backgroundColor: Colors.white),
              BottomNavigationBarItem(
                icon: Icon(Icons.chat, color: (_page == 4) ? Colors.orange[400] : Colors.black),
                title: Text('Chat', style: TextStyle(color: (_page == 4) ? Colors.orange[400] : Colors.black),),
                backgroundColor: Colors.white
              ),
            ],
          ),
        ); 
      }
      print('[RootPage] [Build] isSigned == false || CurrentUser.instance == null');
      print('[RootPage] [Build] building login screen');
      return _buildLoginScreen();
    }
    print('[RootPage] [Build] isSigned == null');
    print('[RootPage] [Build] building loading screen');
    return _buildLoadingScreen();
  }

  @override
  void dispose() {
    pageController.dispose();
    super.dispose();
  }

  @override
  void initState() {

    print('[RootPage] [InitState] START');

    googleSignIn.isSignedIn().then((onValue) {
      print('[RootPage] [InitState] FUTURE 1 DONE');
      isSigned = onValue;
      print('[RootPage] [InitState] isSigned: $isSigned');
      
      if (isSigned) {
        setState(() => _loadingIcon = true);
        CurrentUser.signInWithGoogle().then((_) {
          print('[RootPage] [InitState] FUTURE 2 DONE');
          print('[RootPage] [InitState] CurrentUser.instance.id: ${CurrentUser.instance.id}, CurrentUser.instance.username: ${CurrentUser.instance.username}');
          setState(() => isSigned = onValue);
        });
      }

      if (!isSigned)
        setState(() => isSigned = onValue);

    }); 

    super.initState(); 

    print('[RootPage] [InitState] DONE');  
  }

  Widget _buildLoginScreen() {
    print('[RootPage] [BuildLoginScreen]');
    return Scaffold(
      backgroundColor: Colors.blue[500],
      body: GestureDetector(
        onTap: _onClickLogin,
        child: Stack(
          children: <Widget>[
            
            Center(
              child: Container(  // Back wave
                child: CircleWaveProgress(
                  size: 256.0, 
                  backgroundColor: Colors.orange[300], 
                  waveColor: Colors.blue[900], 
                  borderColor: Colors.transparent, 
                  borderWidth: 0.0, 
                  progress: 24.0,
                )
              ),
            ),
          
            Center(
              child: Container(  // Front wave
                child: CircleWaveProgress(
                  size: 256.0, 
                  backgroundColor: Colors.transparent, 
                  waveColor: Colors.blue[600], 
                  borderColor: Colors.black, 
                  borderWidth: 3.0, 
                  progress: 14.0,
                )
              ),
            ),

            Center(
              child: Container(  // Title
                padding: EdgeInsets.only(bottom: 48.0),
                child: const Text('WaveCheck',
                  style: const TextStyle(
                    fontFamily: "Raustila-Regular", color: Colors.black, fontSize: 64.0, 
                    shadows: [
                      Shadow(offset: Offset(-0.5, -0.5), color: Colors.orange),
                      Shadow(offset: Offset(0.5, -0.5), color: Colors.orange),
                      Shadow(offset: Offset(0.5, 0.5), color: Colors.orange),
                      Shadow(offset: Offset(-0.5, 0.5), color: Colors.orange),
                    ]
                  )
                ),
              ),
            ),

            Center(
              child: Container(  // Subtitle
                padding: EdgeInsets.only(top: 34.0),
                child: const Text('Sua onda começa aqui!',
                  style: const TextStyle(
                    fontFamily: "Raustila-Regular", color: Colors.black, fontSize: 32.0, 
                    shadows: [
                      Shadow(offset: Offset(-0.5, -0.5), color: Colors.orange),
                      Shadow(offset: Offset(0.5, -0.5), color: Colors.orange),
                      Shadow(offset: Offset(0.5, 0.5), color: Colors.orange),
                      Shadow(offset: Offset(-0.5, 0.5), color: Colors.orange),
                    ]
                  )
                ),
              ),
            ),

            Center(
              child: Container(  // Tip
                padding: EdgeInsets.only(top: 285.0),
                child: const Text('Toque na tela para se conectar',
                  style: const TextStyle(
                    color: Colors.black, fontSize: 11.0, 
                  )
                ),
              ),
            ),

            Center(
              child: Container(  // Progress indicator
                padding: EdgeInsets.only(top: 480.0),
                alignment: Alignment.bottomCenter,
                child: Visibility(
                  visible: _loadingIcon, 
                  child: CircularLogoProgress()
                ),
              ),
            ),

            Center( // Text
              child: Container(
                padding: EdgeInsets.only(top: 525.0),
                child: Visibility(
                  visible: _loadingIcon,
                  child: const Text('Aguarde a série',
                    style: const TextStyle(
                      color: Colors.black, fontSize: 11.0, fontWeight: FontWeight.w400 
                    )
                  ),
                ),
              ),
            ),
            
          ],
        ),
      ),
    );
  }

  Widget _buildLoadingScreen() {
    print('[RootPage] [BuildLoadingScreen]');
    return Scaffold(
      backgroundColor: Colors.blue[400],
      body: GestureDetector(
        onTap: _onClickLogin,
        child: Stack(
          children: <Widget>[
            
            Center(
              child: Container(  // Back wave
                child: CircleWaveProgress(
                  size: 256.0, 
                  backgroundColor: Colors.orange[300], 
                  waveColor: Colors.blue[900], 
                  borderColor: Colors.transparent, 
                  borderWidth: 0.0, 
                  progress: 24.0,
                )
              ),
            ),
          
            Center(
              child: Container(  // Front wave
                child: CircleWaveProgress(
                  size: 256.0, 
                  backgroundColor: Colors.transparent, 
                  waveColor: Colors.blue[600], 
                  borderColor: Colors.black, 
                  borderWidth: 3.0, 
                  progress: 14.0,
                )
              ),
            ),

            Center(
              child: Container(  // Title
                padding: EdgeInsets.only(bottom: 48.0),
                child: const Text('WaveCheck',
                  style: const TextStyle(
                    fontFamily: "Raustila-Regular", color: Colors.black, fontSize: 64.0, 
                    shadows: [
                      Shadow(offset: Offset(-0.5, -0.5), color: Colors.orange),
                      Shadow(offset: Offset(0.5, -0.5), color: Colors.orange),
                      Shadow(offset: Offset(0.5, 0.5), color: Colors.orange),
                      Shadow(offset: Offset(-0.5, 0.5), color: Colors.orange),
                    ]
                  )
                ),
              ),
            ),

            Center(
              child: Container(  // Subtitle
                padding: EdgeInsets.only(top: 34.0),
                child: const Text('Sua onda começa aqui!',
                  style: const TextStyle(
                    fontFamily: "Raustila-Regular", color: Colors.black, fontSize: 32.0, 
                    shadows: [
                      Shadow(offset: Offset(-0.5, -0.5), color: Colors.orange),
                      Shadow(offset: Offset(0.5, -0.5), color: Colors.orange),
                      Shadow(offset: Offset(0.5, 0.5), color: Colors.orange),
                      Shadow(offset: Offset(-0.5, 0.5), color: Colors.orange),
                    ]
                  )
                ),
              ),
            ),

            Center(
              child: Container(  // Progress indicator
                padding: EdgeInsets.only(top: 480.0),
                alignment: Alignment.bottomCenter,
                child: Visibility(
                  visible: _loadingIcon, 
                  child: CircularLogoProgress()
                ),
              ),
            ),

            Center( // Text
              child: Container(
                padding: EdgeInsets.only(top: 525.0),
                child: Visibility(
                  visible: _loadingIcon,
                  child: const Text('Aguarde a série',
                    style: const TextStyle(
                      color: Colors.black, fontSize: 11.0, fontWeight: FontWeight.w400 
                    )
                  ),
                ),
              ),
            ),
  
          ],
        ),
      ),
    );
  }

  void _onClickLogin() async {
    print('[RootPage] [OnClickLogin] START');
    setState(() => _loadingIcon = true);
    
    print('[RootPage] [OnClickLogin] signInWithGoogle start');
    await CurrentUser.signInWithGoogle();  
    print('[RootPage] [OnClickLogin] signInWithGoogle done');
    
    if (CurrentUser.instance != null) {
      print('[RootPage] [OnClickLogin] CurrentUser.instance.id: ${CurrentUser.instance.id}, CurrentUser.instance.username: ${CurrentUser.instance.username}');
      setState(() {
        isSigned = true;
        _loadingIcon = false;
      });
    } 
    else {
      print('[RootPage] [OnClickLogin] CurrentUser.instance == null');
      setState(() {
        isSigned = false;
        _loadingIcon = false;
      });
    }
    print('[RootPage] [OnClickLogin] DONE');
  }

}

@kamami
Copy link
Author

kamami commented Apr 17, 2019

@GustavoContreiras Thanks but there are too many other files missing. Nevermind, I will stick to the repository.

@GustavoContreiras
Copy link
Contributor

GustavoContreiras commented Apr 17, 2019

Thats because thats for my own project. I'm using a circle_wave_progress package for a custom CircularProgressIndicator and maybe other dependencies version (check here #48).

Later I'll try to make a fresh download from the repository and make a clean pull request with these changes in case anybody want to do that.

Edit:
@kamami maybe that's what is missing (and some code corrections, like every time you want to get something from the currentUser you have to call CurrentUser.instance (cause its a singleton), for example: CurrentUser.instance.displayName; and the CurrentUser address is a global variable called userLocation).
https://pub.dartlang.org/packages/circle_wave_progress
https://pub.dartlang.org/packages/circular_profile_avatar

example:
var url = 'https://us-central1-myfluttergramapp.cloudfunctions.net/getFeed?uid=' + CurrentUser.instance.id;

@kamami
Copy link
Author

kamami commented Apr 17, 2019

@GustavoContreiras that would be really cool if you could provide your changes here! They seem like a good improvement and I would love to try them out! Thanks

@mdanics
Copy link
Owner

mdanics commented Apr 17, 2019

@kamami did you checkout the branch with the updated dependencies?

@kamami
Copy link
Author

kamami commented Apr 18, 2019

@mdanics I tried it yesterday evening and the auth works fine with the updated main file!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants