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 Sign In #36

Closed
Jukez17 opened this issue Oct 7, 2019 · 9 comments
Closed

Google Sign In #36

Jukez17 opened this issue Oct 7, 2019 · 9 comments

Comments

@Jukez17
Copy link

Jukez17 commented Oct 7, 2019

I am the 27-29 days mark and my login is not working like it should. I'm not sure if my authentication block or the registration page has the problem. Logs are below. Here is also the link to the repository.

https://github.com/Jukez17/WeChat

I/flutter (10482): #1      MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:316:33)
I/flutter (10482): <asynchronous suspension>
I/flutter (10482): #2      MethodChannel.invokeMapMethod (package:flutter/src/services/platform_channel.dart:344:48)
I/flutter (10482): <asynchronous suspension>
I/flutter (10482): #3      GoogleSignIn._callMethod (package:google_sign_in/google_sign_in.dart:218:23)
I/flutter (10482): <asynchronous suspension>
I/flutter (10482): #4      GoogleSignIn._addMethodCall (package:google_sign_in/google_sign_in.dart:257:20)
I/flutter (10482): #5      GoogleSignIn.signIn (package:google_sign_in/google_sign_in.dart:324:48)
I/flutter (10482): #6      AuthenticationProvider.signInWithGoogle (package:wechat/providers/AuthenticationProvider.dart:18:28)
I/flutter (10482): <asynchronous suspension>
I/flutter (10482): #7      AuthenticationRepository.signInWithGoogle (package:wechat/repositories/AuthenticationRepository.dart:9:30)
I/flutter (10482): #8      AuthenticationBloc.mapClickedGoogleLoginToState (package:wechat/blocs/authentication/authentication_bloc.dart:77:42)
I/flutter (10482): <asynchronous suspension
I/flutter (10482): #0      StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:569:7)
I/flutter (10482): #1      MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:316:33)
I/flutter (10482): <asynchronous suspension>
I/flutter (10482): #2      MethodChannel.invokeMapMethod (package:flutter/src/services/platform_channel.dart:344:48)
I/flutter (10482): <asynchronous suspension>
I/flutter (10482): #3      GoogleSignIn._callMethod (package:google_sign_in/google_sign_in.dart:218:23)
I/flutter (10482): <asynchronous suspension>
I/flutter (10482): #4      GoogleSignIn._addMethodCall.<anonymous closure> (package:google_sign_in/google_sign_in.dart:270:28)
I/flutter (10482): #5      _rootRun (dart:async/zone.dart:1120:38)
I/flutter (10482): #6      _CustomZone.run (dart:async/zone.dart:1021:19)
I/flutter (10482): #7      _FutureListener.handleWhenComplete (dart:async/future_impl.dart:161:18)
I/flutter (10482): #8      Future._propagateToListeners.handleWhenCompleteCallback (dart:async/future_impl.dart:648:39)
I/flutter (10482): #9      Future._propagateToListeners (dart:async/future_impl.dart:704:37)
I/flutter (10482): #10     Future._addListener.<anonymous closure> (dart:async/future_impl.dart:387:9)
I/flutter (10482): #11     _rootRun
I/flutter (10482): #0      StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:569:7)
I/flutter (10482): #1      MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:316:33)
I/flutter (10482): <asynchronous suspension>
I/flutter (10482): #2      MethodChannel.invokeMapMethod (package:flutter/src/services/platform_channel.dart:344:48)
I/flutter (10482): <asynchronous suspension>
I/flutter (10482): #3      GoogleSignIn._callMethod (package:google_sign_in/google_sign_in.dart:218:23)
I/flutter (10482): <asynchronous suspension>
I/flutter (10482): #4      GoogleSignIn._addMethodCall.<anonymous closure> (package:google_sign_in/google_sign_in.dart:270:28)
I/flutter (10482): #5      _rootRun (dart:async/zone.dart:1120:38)
I/flutter (10482): #6      _CustomZone.run (dart:async/zone.dart:1021:19)
I/flutter (10482): #7      _FutureListener.handleWhenComplete (dart:async/future_impl.dart:161:18)
I/flutter (10482): #8      Future._propagateToListeners.handleWhenCompleteCallback (dart:async/future_impl.dart:648:39)
I/flutter (10482): #9      Future._propagateToListeners (dart:async/future_impl.dart:704:37)
I/flutter (10482): #10     Future._addListener.<anonymous closure> (dart:async/future_impl.dart:387:9)
I/flutter (10482): #11     _rootRun```
@adityadroid
Copy link
Owner

I am the 27-29 days mark and my login is not working like it should. I'm not sure if my authentication block or the registration page has the problem. Logs are below. Here is also the link to the repository.

https://github.com/Jukez17/WeChat

I/flutter (10482): #1      MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:316:33)
I/flutter (10482): <asynchronous suspension>
I/flutter (10482): #2      MethodChannel.invokeMapMethod (package:flutter/src/services/platform_channel.dart:344:48)
I/flutter (10482): <asynchronous suspension>
I/flutter (10482): #3      GoogleSignIn._callMethod (package:google_sign_in/google_sign_in.dart:218:23)
I/flutter (10482): <asynchronous suspension>
I/flutter (10482): #4      GoogleSignIn._addMethodCall (package:google_sign_in/google_sign_in.dart:257:20)
I/flutter (10482): #5      GoogleSignIn.signIn (package:google_sign_in/google_sign_in.dart:324:48)
I/flutter (10482): #6      AuthenticationProvider.signInWithGoogle (package:wechat/providers/AuthenticationProvider.dart:18:28)
I/flutter (10482): <asynchronous suspension>
I/flutter (10482): #7      AuthenticationRepository.signInWithGoogle (package:wechat/repositories/AuthenticationRepository.dart:9:30)
I/flutter (10482): #8      AuthenticationBloc.mapClickedGoogleLoginToState (package:wechat/blocs/authentication/authentication_bloc.dart:77:42)
I/flutter (10482): <asynchronous suspension
I/flutter (10482): #0      StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:569:7)
I/flutter (10482): #1      MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:316:33)
I/flutter (10482): <asynchronous suspension>
I/flutter (10482): #2      MethodChannel.invokeMapMethod (package:flutter/src/services/platform_channel.dart:344:48)
I/flutter (10482): <asynchronous suspension>
I/flutter (10482): #3      GoogleSignIn._callMethod (package:google_sign_in/google_sign_in.dart:218:23)
I/flutter (10482): <asynchronous suspension>
I/flutter (10482): #4      GoogleSignIn._addMethodCall.<anonymous closure> (package:google_sign_in/google_sign_in.dart:270:28)
I/flutter (10482): #5      _rootRun (dart:async/zone.dart:1120:38)
I/flutter (10482): #6      _CustomZone.run (dart:async/zone.dart:1021:19)
I/flutter (10482): #7      _FutureListener.handleWhenComplete (dart:async/future_impl.dart:161:18)
I/flutter (10482): #8      Future._propagateToListeners.handleWhenCompleteCallback (dart:async/future_impl.dart:648:39)
I/flutter (10482): #9      Future._propagateToListeners (dart:async/future_impl.dart:704:37)
I/flutter (10482): #10     Future._addListener.<anonymous closure> (dart:async/future_impl.dart:387:9)
I/flutter (10482): #11     _rootRun
I/flutter (10482): #0      StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:569:7)
I/flutter (10482): #1      MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:316:33)
I/flutter (10482): <asynchronous suspension>
I/flutter (10482): #2      MethodChannel.invokeMapMethod (package:flutter/src/services/platform_channel.dart:344:48)
I/flutter (10482): <asynchronous suspension>
I/flutter (10482): #3      GoogleSignIn._callMethod (package:google_sign_in/google_sign_in.dart:218:23)
I/flutter (10482): <asynchronous suspension>
I/flutter (10482): #4      GoogleSignIn._addMethodCall.<anonymous closure> (package:google_sign_in/google_sign_in.dart:270:28)
I/flutter (10482): #5      _rootRun (dart:async/zone.dart:1120:38)
I/flutter (10482): #6      _CustomZone.run (dart:async/zone.dart:1021:19)
I/flutter (10482): #7      _FutureListener.handleWhenComplete (dart:async/future_impl.dart:161:18)
I/flutter (10482): #8      Future._propagateToListeners.handleWhenCompleteCallback (dart:async/future_impl.dart:648:39)
I/flutter (10482): #9      Future._propagateToListeners (dart:async/future_impl.dart:704:37)
I/flutter (10482): #10     Future._addListener.<anonymous closure> (dart:async/future_impl.dart:387:9)
I/flutter (10482): #11     _rootRun```

please add your google-services.json to android/app folder.

@Jukez17
Copy link
Author

Jukez17 commented Oct 8, 2019

I added it but still the same result. I noticed that might be the sha1 that is causing the problem. How do I fix that. I generated the keystore and debug one and took the certificate fingerprint for the Google-services.json but didn't work. How did u do this part?

@adityadroid
Copy link
Owner

adityadroid commented Oct 8, 2019

I added it but still the same result. I noticed that might be the sha1 that is causing the problem. How do I fix that. I generated the keystore and debug one and took the certificate fingerprint for the Google-services.json but didn't work. How did u do this part?

  1. If you're using android studio you can use signingReport
    image

    Or from the terminal
    ./gradlew app:signingreport

  2. Use the generated SHA1 while you're creating the google-services.json. Make sure the package name is the same as you chose for android. (Check applicationId in your build.gradle )

@Jukez17
Copy link
Author

Jukez17 commented Oct 8, 2019

I have to try that, thank you :)

@Jukez17
Copy link
Author

Jukez17 commented Oct 8, 2019

Okey, progress was made but after sign in I get this error.

package:flutter/src/painting/_network_image_dart.io failed assertion line 23 pos 14 url != null : is not true

@adityadroid
Copy link
Owner

Okey, progress was made but after sign in I get this error.

package:flutter/src/painting/_network_image_dart.io failed assertion line 23 pos 14 url != null : is not true

Can you post the code from your register screen? Looks like you forgot to add a placeholder.

@Jukez17
Copy link
Author

Jukez17 commented Oct 8, 2019

Here,


import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:image_picker/image_picker.dart';
import 'package:wechat/config/Assets.dart';
import 'package:wechat/config/Decorations.dart';
import 'package:wechat/config/Palette.dart';
import 'package:wechat/config/Styles.dart';
import 'package:wechat/config/Transitions.dart';
import 'package:wechat/pages/ContactListPage.dart';
import 'package:wechat/widgets/CircleIndicator.dart';
import 'package:wechat/widgets/NumberPicker.dart';
import 'package:wechat/blocs/authentication/Bloc.dart';

class RegisterPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _RegisterPageState();
  }
}

class _RegisterPageState extends State<RegisterPage>
    with SingleTickerProviderStateMixin, WidgetsBindingObserver {
  int currentPage = 0;

  //fields for the form
  File profileImageFile;
  ImageProvider profileImage;
  int age = 18;
  final TextEditingController usernameController = TextEditingController();

  var isKeyboardOpen =
      false; //this variable keeps track of the keyboard, when its shown and when its hidden

  PageController pageController =
      PageController(); // this is the controller of the page. This is used to navigate back and forth between the pages

  //Fields related to animation of the gradient
  Alignment begin = Alignment.center;
  Alignment end = Alignment.bottomRight;

  //Fields related to animating the layout and pushing widgets up when the focus is on the username field
  AnimationController usernameFieldAnimationController;
  Animation profilePicHeightAnimation, usernameAnimation, ageAnimation;
  FocusNode usernameFocusNode = FocusNode();

  AuthenticationBloc authenticationBloc;

  @override
  void initState() {
    initApp();
    super.initState();
  }

  void initApp() async {
    WidgetsBinding.instance.addObserver(this);
    usernameFieldAnimationController =
        AnimationController(vsync: this, duration: Duration(milliseconds: 300));
    profilePicHeightAnimation =
        Tween(begin: 100.0, end: 0.0).animate(usernameFieldAnimationController)
          ..addListener(() {
            setState(() {});
          });
    usernameAnimation =
        Tween(begin: 50.0, end: 10.0).animate(usernameFieldAnimationController)
          ..addListener(() {
            setState(() {});
          });
    ageAnimation =
        Tween(begin: 80.0, end: 10.0).animate(usernameFieldAnimationController)
          ..addListener(() {
            setState(() {});
          });
    usernameFocusNode.addListener(() {
      if (usernameFocusNode.hasFocus) {
        usernameFieldAnimationController.forward();
      } else {
        usernameFieldAnimationController.reverse();
      }
    });
    pageController.addListener(() {
      setState(() {
        begin = Alignment(pageController.page, pageController.page);
        end = Alignment(1 - pageController.page, 1 - pageController.page);
      });
    });

    authenticationBloc = BlocProvider.of<AuthenticationBloc>(context);
    authenticationBloc.state.listen((state) {
      if (state is Authenticated) {
        updatePageState(1);
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
        onWillPop: onWillPop, //user to override the back button press
        child: Scaffold(
          resizeToAvoidBottomPadding: false,
          //  avoids the bottom overflow warning when keyboard is shown
          body: SafeArea(
              child: Stack(
            children: <Widget>[
              buildHome(),
              BlocBuilder<AuthenticationBloc, AuthenticationState>(
                builder: (context, state) {
                  if (state is AuthInProgress ||
                      state is ProfileUpdateInProgress) {
                    return buildCircularProgressBarWidget();
                  }
                  return SizedBox();
                },
              )
            ],
          )),
        ));
  }

  buildHome() {
    return Container(
        decoration: BoxDecoration(
            gradient: LinearGradient(begin: begin, end: end, colors: [
          Palette.gradientStartColor,
          Palette.gradientEndColor
        ])),
        child: Stack(
            alignment: AlignmentDirectional.bottomCenter,
            children: <Widget>[
              PageView(
                  controller: pageController,
                  physics: NeverScrollableScrollPhysics(),
                  onPageChanged: (int page) => updatePageState(page),
                  children: <Widget>[buildPageOne(), buildPageTwo()]),
              Container(
                margin: EdgeInsets.only(bottom: 30),
                child: Row(
                  mainAxisSize: MainAxisSize.min,
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    for (int i = 0; i < 2; i++)
                      CircleIndicator(i == currentPage),
                  ],
                ),
              ),
              buildUpdateProfileButtonWidget()
            ]));
  }

  buildCircularProgressBarWidget() {
    return Container(
        decoration: BoxDecoration(
            gradient: LinearGradient(begin: begin, end: end, colors: [
          Palette.gradientStartColor,
          Palette.gradientEndColor
        ])),
        child: Container(
            child: Center(
          child: Column(children: <Widget>[
            buildHeaderSectionWidget(),
            Container(
              margin: EdgeInsets.only(top: 100),
              child: CircularProgressIndicator(
                  valueColor:
                      AlwaysStoppedAnimation<Color>(Palette.primaryColor)),
            )
          ]),
        )));
  }

  buildPageOne() {
    return Column(
      children: <Widget>[buildHeaderSectionWidget(), buildGoogleButtonWidget()],
    );
  }

  buildHeaderSectionWidget() {
    return Column(children: <Widget>[
      Container(
          margin: EdgeInsets.only(top: 250),
          child: Image.asset(Assets.app_icon_fg, height: 100)),
      Container(
          margin: EdgeInsets.only(top: 30),
          child: Text('WeChat Messenger',
              style: TextStyle(
                  color: Colors.white,
                  fontWeight: FontWeight.bold,
                  fontSize: 22)))
    ]);
  }

  buildGoogleButtonWidget() {
    return Container(
        margin: EdgeInsets.only(top: 100),
        child: FlatButton.icon(
            onPressed: () => BlocProvider.of<AuthenticationBloc>(context)
                .dispatch(ClickedGoogleLogin()),
            color: Colors.transparent,
            icon: Image.asset(
              Assets.google_button,
              height: 25,
            ),
            label: Text(
              'Sign In with Google',
              style: TextStyle(
                  color: Palette.primaryTextColorLight,
                  fontWeight: FontWeight.w800),
            )));
  }

  buildPageTwo() {
    return InkWell(
        // to dismiss the keyboard when the user tabs out of the TextField
        onTap: () {
      FocusScope.of(context).requestFocus(FocusNode());
    }, child: Container(
      child: BlocBuilder<AuthenticationBloc, AuthenticationState>(
        builder: (context, state) {
          profileImage = Image.asset(Assets.user).image;
          if (state is PreFillData) {
            age = state.user.age != null ? state.user.age : 18;
            profileImage = Image.network(state.user.photoUrl).image;
          } else if (state is ReceivedProfilePicture) {
            profileImageFile = state.file;
            profileImage = Image.file(profileImageFile).image;
          }

          return Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              SizedBox(height: profilePicHeightAnimation.value),
              buildProfilePictureWidget(),
              SizedBox(
                height: ageAnimation.value,
              ),
              Text(
                'How old are you?',
                style: Styles.questionLight,
              ),
              buildAgePickerWidget(),
              SizedBox(
                height: usernameAnimation.value,
              ),
              Text(
                'Choose a username',
                style: Styles.questionLight,
              ),
              buildUsernameWidget()
            ],
          );
        },
      ),
    ));
  }

  buildProfilePictureWidget() {
    return GestureDetector(
      onTap: pickImage,
      child: CircleAvatar(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Icon(
              Icons.camera,
              color: Colors.white,
              size: 15,
            ),
            Text(
              'Set Profile Picture',
              textAlign: TextAlign.center,
              style: TextStyle(
                color: Colors.white,
                fontSize: 10,
              ),
            )
          ],
        ),
        backgroundImage: profileImage,
        radius: 60,
      ),
    );
  }

  buildAgePickerWidget() {
    return Row(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        NumberPicker.horizontal(
            initialValue: age,
            minValue: 15,
            maxValue: 100,
            highlightSelectedValue: true,
            onChanged: (num value) {
              setState(() {
                age = value;
              });
            }),
        Text('Years', style: Styles.textLight)
      ],
    );
  }

  buildUsernameWidget() {
    return Container(
        margin: EdgeInsets.only(top: 20),
        width: 120,
        child: TextField(
          textAlign: TextAlign.center,
          style: Styles.subHeadingLight,
          focusNode: usernameFocusNode,
          controller: usernameController,
          decoration: Decorations.getInputDecoration(
              hint: '@username', isPrimary: false),
        ));
  }

  updatePageState(index) {
    if (currentPage == index) return;
    if (index == 1)
      pageController.nextPage(
          duration: Duration(milliseconds: 300), curve: Curves.easeIn);

    setState(() {
      currentPage = index;
    });
  }

  Future pickImage() async {
    profileImageFile = await ImagePicker.pickImage(source: ImageSource.gallery);
    authenticationBloc.dispatch(PickedProfilePicture(profileImageFile));
  }

  Future<bool> onWillPop() async {
    if (currentPage == 1) {
      //go to first page if currently on second page
      pageController.previousPage(
        duration: Duration(milliseconds: 300),
        curve: Curves.easeOut,
      );
      return false;
    }
    return true;
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    usernameFieldAnimationController.dispose();
    usernameFocusNode.dispose();
    super.dispose();
  }

  ///
  /// This routine is invoked when the window metrics have changed.
  ///
  @override
  void didChangeMetrics() {
    final value = MediaQuery.of(context).viewInsets.bottom;
    if (value > 0) {
      if (isKeyboardOpen) {
        onKeyboardChanged(false);
      }
      isKeyboardOpen = false;
    } else {
      isKeyboardOpen = true;
      onKeyboardChanged(true);
    }
  }

  onKeyboardChanged(bool isVisible) {
    if (!isVisible) {
      FocusScope.of(context).requestFocus(FocusNode());
      usernameFieldAnimationController.reverse();
    }
  }

  navigateToHome() {
    Navigator.push(
      context,
      SlideLeftRoute(page: ContactListPage()),
    );
  }

  buildUpdateProfileButtonWidget() {
    return AnimatedOpacity(
        opacity: currentPage == 1 ? 1.0 : 0.0,
        //shows only on page 1
        duration: Duration(milliseconds: 500),
        child: Container(
            margin: EdgeInsets.only(right: 20, bottom: 20),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.end,
              mainAxisSize: MainAxisSize.max,
              children: <Widget>[
                FloatingActionButton(
                  onPressed: () => authenticationBloc.dispatch(SaveProfile(
                      profileImageFile, age, usernameController.text)),
                  elevation: 0,
                  backgroundColor: Palette.primaryColor,
                  child: Icon(
                    Icons.done,
                    color: Palette.accentColor,
                  ),
                )
              ],
            )));
  }
}```

@adityadroid
Copy link
Owner

adityadroid commented Oct 8, 2019

This should fix the issue. Added a null check for profile picture url. @Jukez17


import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:image_picker/image_picker.dart';
import 'package:wechat/config/Assets.dart';
import 'package:wechat/config/Decorations.dart';
import 'package:wechat/config/Palette.dart';
import 'package:wechat/config/Styles.dart';
import 'package:wechat/config/Transitions.dart';
import 'package:wechat/pages/ContactListPage.dart';
import 'package:wechat/widgets/CircleIndicator.dart';
import 'package:wechat/widgets/NumberPicker.dart';
import 'package:wechat/blocs/authentication/Bloc.dart';

class RegisterPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _RegisterPageState();
  }
}

class _RegisterPageState extends State<RegisterPage>
    with SingleTickerProviderStateMixin, WidgetsBindingObserver {
  int currentPage = 0;

  //fields for the form
  File profileImageFile;
  ImageProvider profileImage;


  int age = 18;
  final TextEditingController usernameController = TextEditingController();

  var isKeyboardOpen =
      false; //this variable keeps track of the keyboard, when its shown and when its hidden

  PageController pageController =
      PageController(); // this is the controller of the page. This is used to navigate back and forth between the pages

  //Fields related to animation of the gradient
  Alignment begin = Alignment.center;
  Alignment end = Alignment.bottomRight;

  //Fields related to animating the layout and pushing widgets up when the focus is on the username field
  AnimationController usernameFieldAnimationController;
  Animation profilePicHeightAnimation, usernameAnimation, ageAnimation;
  FocusNode usernameFocusNode = FocusNode();

  AuthenticationBloc authenticationBloc;

  @override
  void initState() {
    initApp();
    super.initState();
  }

  void initApp() async {
    WidgetsBinding.instance.addObserver(this);
    usernameFieldAnimationController =
        AnimationController(vsync: this, duration: Duration(milliseconds: 300));
    profilePicHeightAnimation =
        Tween(begin: 100.0, end: 0.0).animate(usernameFieldAnimationController)
          ..addListener(() {
            setState(() {});
          });
    usernameAnimation =
        Tween(begin: 50.0, end: 10.0).animate(usernameFieldAnimationController)
          ..addListener(() {
            setState(() {});
          });
    ageAnimation =
        Tween(begin: 80.0, end: 10.0).animate(usernameFieldAnimationController)
          ..addListener(() {
            setState(() {});
          });
    usernameFocusNode.addListener(() {
      if (usernameFocusNode.hasFocus) {
        usernameFieldAnimationController.forward();
      } else {
        usernameFieldAnimationController.reverse();
      }
    });
    pageController.addListener(() {
      setState(() {
        begin = Alignment(pageController.page, pageController.page);
        end = Alignment(1 - pageController.page, 1 - pageController.page);
      });
    });

    authenticationBloc = BlocProvider.of<AuthenticationBloc>(context);
    authenticationBloc.state.listen((state) {
      if (state is Authenticated) {
        updatePageState(1);
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
        onWillPop: onWillPop, //user to override the back button press
        child: Scaffold(
          resizeToAvoidBottomPadding: false,
          //  avoids the bottom overflow warning when keyboard is shown
          body: SafeArea(
              child: Stack(
            children: <Widget>[
              buildHome(),
              BlocBuilder<AuthenticationBloc, AuthenticationState>(
                builder: (context, state) {
                  if (state is AuthInProgress ||
                      state is ProfileUpdateInProgress) {
                    return buildCircularProgressBarWidget();
                  }
                  return SizedBox();
                },
              )
            ],
          )),
        ));
  }

  buildHome() {
    return Container(
        decoration: BoxDecoration(
            gradient: LinearGradient(begin: begin, end: end, colors: [
          Palette.gradientStartColor,
          Palette.gradientEndColor
        ])),
        child: Stack(
            alignment: AlignmentDirectional.bottomCenter,
            children: <Widget>[
              PageView(
                  controller: pageController,
                  physics: NeverScrollableScrollPhysics(),
                  onPageChanged: (int page) => updatePageState(page),
                  children: <Widget>[buildPageOne(), buildPageTwo()]),
              Container(
                margin: EdgeInsets.only(bottom: 30),
                child: Row(
                  mainAxisSize: MainAxisSize.min,
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    for (int i = 0; i < 2; i++)
                      CircleIndicator(i == currentPage),
                  ],
                ),
              ),
              buildUpdateProfileButtonWidget()
            ]));
  }

  buildCircularProgressBarWidget() {
    return Container(
        decoration: BoxDecoration(
            gradient: LinearGradient(begin: begin, end: end, colors: [
          Palette.gradientStartColor,
          Palette.gradientEndColor
        ])),
        child: Container(
            child: Center(
          child: Column(children: <Widget>[
            buildHeaderSectionWidget(),
            Container(
              margin: EdgeInsets.only(top: 100),
              child: CircularProgressIndicator(
                  valueColor:
                      AlwaysStoppedAnimation<Color>(Palette.primaryColor)),
            )
          ]),
        )));
  }

  buildPageOne() {
    return Column(
      children: <Widget>[buildHeaderSectionWidget(), buildGoogleButtonWidget()],
    );
  }

  buildHeaderSectionWidget() {
    return Column(children: <Widget>[
      Container(
          margin: EdgeInsets.only(top: 250),
          child: Image.asset(Assets.app_icon_fg, height: 100)),
      Container(
          margin: EdgeInsets.only(top: 30),
          child: Text('WeChat Messenger',
              style: TextStyle(
                  color: Colors.white,
                  fontWeight: FontWeight.bold,
                  fontSize: 22)))
    ]);
  }

  buildGoogleButtonWidget() {
    return Container(
        margin: EdgeInsets.only(top: 100),
        child: FlatButton.icon(
            onPressed: () => BlocProvider.of<AuthenticationBloc>(context)
                .dispatch(ClickedGoogleLogin()),
            color: Colors.transparent,
            icon: Image.asset(
              Assets.google_button,
              height: 25,
            ),
            label: Text(
              'Sign In with Google',
              style: TextStyle(
                  color: Palette.primaryTextColorLight,
                  fontWeight: FontWeight.w800),
            )));
  }

  buildPageTwo() {
    return InkWell(
        // to dismiss the keyboard when the user tabs out of the TextField
        onTap: () {
      FocusScope.of(context).requestFocus(FocusNode());
    }, child: Container(
      child: BlocBuilder<AuthenticationBloc, AuthenticationState>(
        builder: (context, state) {
          profileImage = Image.asset(Assets.user).image;
          if (state is PreFillData) {
            age = state.user.age != null ? state.user.age : 18;
            if (state.user.photoUrl != null) {
            profileImage = Image.network(state.user.photoUrl).image;
		}          
	  } else if (state is ReceivedProfilePicture) {
            profileImageFile = state.file;
            profileImage = Image.file(profileImageFile).image;
          }

          return Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              SizedBox(height: profilePicHeightAnimation.value),
              buildProfilePictureWidget(),
              SizedBox(
                height: ageAnimation.value,
              ),
              Text(
                'How old are you?',
                style: Styles.questionLight,
              ),
              buildAgePickerWidget(),
              SizedBox(
                height: usernameAnimation.value,
              ),
              Text(
                'Choose a username',
                style: Styles.questionLight,
              ),
              buildUsernameWidget()
            ],
          );
        },
      ),
    ));
  }

  buildProfilePictureWidget() {
    return GestureDetector(
      onTap: pickImage,
      child: CircleAvatar(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Icon(
              Icons.camera,
              color: Colors.white,
              size: 15,
            ),
            Text(
              'Set Profile Picture',
              textAlign: TextAlign.center,
              style: TextStyle(
                color: Colors.white,
                fontSize: 10,
              ),
            )
          ],
        ),
        backgroundImage: profileImage,
        radius: 60,
      ),
    );
  }

  buildAgePickerWidget() {
    return Row(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        NumberPicker.horizontal(
            initialValue: age,
            minValue: 15,
            maxValue: 100,
            highlightSelectedValue: true,
            onChanged: (num value) {
              setState(() {
                age = value;
              });
            }),
        Text('Years', style: Styles.textLight)
      ],
    );
  }

  buildUsernameWidget() {
    return Container(
        margin: EdgeInsets.only(top: 20),
        width: 120,
        child: TextField(
          textAlign: TextAlign.center,
          style: Styles.subHeadingLight,
          focusNode: usernameFocusNode,
          controller: usernameController,
          decoration: Decorations.getInputDecoration(
              hint: '@username', isPrimary: false),
        ));
  }

  updatePageState(index) {
    if (currentPage == index) return;
    if (index == 1)
      pageController.nextPage(
          duration: Duration(milliseconds: 300), curve: Curves.easeIn);

    setState(() {
      currentPage = index;
    });
  }

  Future pickImage() async {
    profileImageFile = await ImagePicker.pickImage(source: ImageSource.gallery);
    authenticationBloc.dispatch(PickedProfilePicture(profileImageFile));
  }

  Future<bool> onWillPop() async {
    if (currentPage == 1) {
      //go to first page if currently on second page
      pageController.previousPage(
        duration: Duration(milliseconds: 300),
        curve: Curves.easeOut,
      );
      return false;
    }
    return true;
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    usernameFieldAnimationController.dispose();
    usernameFocusNode.dispose();
    super.dispose();
  }

  ///
  /// This routine is invoked when the window metrics have changed.
  ///
  @override
  void didChangeMetrics() {
    final value = MediaQuery.of(context).viewInsets.bottom;
    if (value > 0) {
      if (isKeyboardOpen) {
        onKeyboardChanged(false);
      }
      isKeyboardOpen = false;
    } else {
      isKeyboardOpen = true;
      onKeyboardChanged(true);
    }
  }

  onKeyboardChanged(bool isVisible) {
    if (!isVisible) {
      FocusScope.of(context).requestFocus(FocusNode());
      usernameFieldAnimationController.reverse();
    }
  }

  navigateToHome() {
    Navigator.push(
      context,
      SlideLeftRoute(page: ContactListPage()),
    );
  }

  buildUpdateProfileButtonWidget() {
    return AnimatedOpacity(
        opacity: currentPage == 1 ? 1.0 : 0.0,
        //shows only on page 1
        duration: Duration(milliseconds: 500),
        child: Container(
            margin: EdgeInsets.only(right: 20, bottom: 20),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.end,
              mainAxisSize: MainAxisSize.max,
              children: <Widget>[
                FloatingActionButton(
                  onPressed: () => authenticationBloc.dispatch(SaveProfile(
                      profileImageFile, age, usernameController.text)),
                  elevation: 0,
                  backgroundColor: Palette.primaryColor,
                  child: Icon(
                    Icons.done,
                    color: Palette.accentColor,
                  ),
                )
              ],
            )));
  }
}```

@Jukez17
Copy link
Author

Jukez17 commented Oct 8, 2019

Thank you it's working now :)

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

2 participants