flutterアプリからGoogleSpreadsheetにデータ保存(1.Googleログイン編)

  • URLをコピーしました!

こんにちは。

記録系のアプリで入力したデータは自分で自由に分析出来るように元データが欲しい、と思う人は少なくないと思います。どのような仕組みになっているのが楽かなぁと思った時に、自分のGoogleDriveにSpreadsheetでデータが保存出来たら、見やすいし加工もしやすいです。
という事で、ざっくり以下構成で解説します。今日はGoogleにログインするです。

  1. Googleにログインする
  2. GoogleDriveにSpreadsheetを作成してデータを入れる
目次

前提

  • Android Studioにてアプリを作成する前提で解説しております。
  • アプリに自前でGoogleログイン機能を追加するのはとても大変なので、Firebaseの認証機能などのプラグインを使っていきます。そのため、Firebaseの設定も必要になります。

プラグインインストール

必要なプラグインは次のとおりです。

firebase_coreFirebaseCoreAPIを使用するFlutterプラグイン
(Firebase初期化の使用)
firebase_authFirebaseAuthenticationAPIを使用するためのプラグイン
(Firebase認証機能の使用)
google_sign_inGoogleサインインを使用する

pubspec.yamlに以下を追加します。

dependencies:
 ・・・
  firebase_core: ^1.8.0
  firebase_auth: ^3.1.4
  google_sign_in: ^5.1.1

そして「flutter pub get」でインストールします。

設定(Android編)

基本的には、こちらに書いてある事をします。

Firebase Console(https://console.developers.google.com/)からプロジェクトを新規作成します。

プロジェクトを作成したら、設定をしていきます。
[プロジェクトの設定]からアプリを追加します。

以下にて、Androidパッケージ名とデバッグ用の署名証明書SHA1を入力します。

SHA1はこちらのサイトに取得方法が記載されていますが、WindowsでAndroidStudioをお使いの方は以下のようにします。

コマンドプロンプトを起動して、以下を入力します。

>cd C:\Program Files\Android\Android Studio\jre\bin
>keytool -list -v -alias androiddebugkey -keystore %USERPROFILE%\.android\debug.keystore

「キーストアのパスワードを入力してください:」と言われるので「android」と入力します。

SHA1の部分をコピーして先ほどのデバッグ用の署名証明書SHA1に入力します。

google_service.jsonをダウンロードして所定の場所に置け、とあります。

こちらに置きます。

次に、google_service.jsonを読み込むためにソースコードを追記する必要があるそうです。

赤枠部分を追加(最初からあるものもある)します。

補足:現時点で以下がエラーになるので、「GrandleException」を「FileNotFoundException」に変更します。

そして、同期してね、という事なので、

Android Studioでは「Open for Editing in Android Studio」をクリックします。(これで同期)

設定(ios編)

※Macにて実施している前提で進めます。
Androidとiosの両方を設定する方はAndroidで作成したプロジェクトにアプリを追加します。もしFirebaseプロジェクトを作成されていない方はFirebase Console(https://console.developers.google.com/)からプロジェクトを新規作成します。

以下から追加します。

iosアイコンを選びます。

以下のような表示になります。

AppleハンドルIDはXcodeで調べます。
[Tools]-[Flutter]-[Open ios module in Xcode]

もしくはターミナルにてiosフォルダに移動し、「open Runner.xcworkspace」と入力するとXcodeで以下画面が立ち上がります。

Bundle IdentifierをAppleハンドルIDに入れて次に進みます。(Xcodeは後で使うのでそのままにしてください。)

GoogleService-info.plistをダウンロードします。

またXcodeに戻り、ダウンロードしたGoogleService-info.plistをios/Runnerに追加します。

以下のような画面が表示されるのでFinishします。

ログイン用ソースコード

ログイン画面に何もないと少し寂しいので、少しオシャレな感じにします。(センスの無さ如何ともし難い…)ログインボタンさえあればOKです。

Signin画像はこちらからダウンロードした画像です。画像はassetsフォルダに配置しpubspec.yamlに以下を追加します。

flutter:
  assets:
    - assets/

Signinボタンを押下すると、以下のようにFirebaseが勝手にしてくれます。ありがたや。

google_login.dartというファイルを作り、以下を書きます。

import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:google_sign_in/google_sign_in.dart';

import 'package:flutter_signin_button/flutter_signin_button.dart';
import 'package:tracer/routes/google_login_info.dart';

import '../baseappbar.dart';

class GoogleLogin extends StatefulWidget {
  const GoogleLogin({Key? key}) : super(key: key);

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

class _GoogleLoginState extends State<GoogleLogin> {
  bool _isSigningIn = false;

  @override
  Widget build(BuildContext context) {
    Size size = MediaQuery.of(context).size;
    return Scaffold(
      appBar: BaseAppBar(
        title: const Text('Upload',style: TextStyle(color: Colors.black54)),
        appBar: AppBar(),
      ),
      backgroundColor: Colors.white12,
      body:SafeArea(
        child: Column(
          mainAxisSize: MainAxisSize.max,
          children: <Widget>[
            ClipPath(
              clipper: MyClipper(),
              child: Container(
                margin: const EdgeInsets.only(bottom: 10.0 * 2.5),
                height: size.height * 0.6,
                width: double.infinity,
                decoration: const BoxDecoration(
                  color: Colors.pinkAccent,
                ),
                child: Column(
                  children: <Widget>[
                    Expanded(
                      child: Padding(
                        padding: const EdgeInsets.all(10.0),
                        child: SvgPicture.asset(
                          "assets/design_pc.svg",
                          fit: BoxFit.fitWidth,
                          // alignment: Alignment.bottomCenter,
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ),
            FutureBuilder(
              future: initFirebase(),
              builder: (context, snapshot) {
                if (snapshot.hasError) {
                  return const Text('Error initializing Firebase');
                } else if (snapshot.connectionState == ConnectionState.done) {
                  return Padding(
                    padding: const EdgeInsets.only(bottom: 16.0),
                    child: _isSigningIn
                        ? const CircularProgressIndicator()
                        : SignInButton(
                    Buttons.Google,
                    onPressed: () async {
                      setState(() {
                        _isSigningIn = true;
                      });
                      User? user = await signIn();
                      setState(() {
                        _isSigningIn = false;
                      });

                      if (user != null) {
                        Navigator.of(context).pushReplacement(
                          MaterialPageRoute(
                            builder: (context) => GoogleLoginInfo(
                              user: user,
                            ),
                          ),
                        );
                      } else {
                        AlertDialog(
                          title: const Text('Error'),
                          content: const Text('Google sign in failed.'),
                          actions: <Widget>[
                            TextButton(
                              child: const Text("OK"),
                              onPressed: () => Navigator.pop(context),
                            ),
                          ],
                        );
                      }
                    },
                    )
                  );
                }
                return const CircularProgressIndicator();
              },
            ),
          ],
        ),
      )
    );
  }

  Future<FirebaseApp> initFirebase() async {
    FirebaseApp firebaseApp = await Firebase.initializeApp();

    User? user = FirebaseAuth.instance.currentUser;

    if (user != null) {
        MaterialPageRoute(
          builder: (context) => GoogleLoginInfo(
            user: user,
          ),
        ),
      );
    }

    return firebaseApp;
  }


  Future<User?> signIn() async {
    FirebaseAuth auth = FirebaseAuth.instance;
    User? user;

    GoogleSignIn googleSignIn = GoogleSignIn();
    GoogleSignInAccount? googleSignInAccount = await googleSignIn.signIn();

    if (googleSignInAccount != null) {

      GoogleSignInAuthentication googleSignInAuthentication = await googleSignInAccount.authentication;
      Map<String, String> authHeaders = await googleSignInAccount.authHeaders;

      AuthCredential credential = GoogleAuthProvider.credential(
        accessToken: googleSignInAuthentication.accessToken,
        idToken: googleSignInAuthentication.idToken,
      );

      try {
        UserCredential userCredential = await auth.signInWithCredential(credential);
        user = userCredential.user;
      } on FirebaseAuthException catch (e) {
        user = null;
      }
    }
    return user;
  }
}




class MyClipper extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    var path = Path();
    path.lineTo(0, size.height - 80);
    path.quadraticBezierTo(
        size.width / 2, size.height, size.width, size.height - 80);
    path.lineTo(size.width, 0);
    path.close();
    return path;
  }

  @override
  bool shouldReclip(covariant CustomClipper<Path> oldClipper) {
    return false;
  }
}

ログアウト用ソースコード

ログアウトはGoogleSpreadSheetsの処理を追加したいので、今はシンプルにします。

google_login_info.dartというファイルを作り、以下を書きます。

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';

import 'package:tracer/routes/google_login.dart';

import '../baseappbar.dart';

class GoogleLoginInfo extends StatefulWidget {
  const GoogleLoginInfo({Key? key, required User user})
      : _user = user,
        super(key: key);

  final User _user;

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

class _GoogleLoginInfoState extends State {
  late User _user;
  bool _isSigningOut = false;

  @override
  void initState() {
    _user = widget._user;

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: TransAppBar(
        title: 'Google Sign In',
        appBar: AppBar(),
      ),
      body: SafeArea(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Row(),
              _user.photoURL != null
                  ? ClipOval(
                    child: Material(
                      color: Colors.white60,
                      child: Image.network(
                        _user.photoURL!,
                        fit: BoxFit.fitHeight,
                      ),
                    ),
                  )
                  : const ClipOval(
                      child: Material(
                        color: Colors.black12,
                        child: Padding(
                          padding: EdgeInsets.all(16.0),
                          child: Icon(
                            Icons.person,
                            size: 60,
                            color: Colors.deepOrange,
                          ),
                        ),
                      ),
                    ),
              const SizedBox(height: 16.0),
              Text(
                _user.displayName!,
                style: const TextStyle(
                  fontSize: 26,
                ),
              ),
              const SizedBox(height: 16.0),
              if (_isSigningOut) const CircularProgressIndicator(
                  valueColor: AlwaysStoppedAnimation(Colors.white),
                ) else OutlinedButton(
                  child: const Text('Sign Out'),
                  onPressed: () async {
                    setState(() {
                      _isSigningOut = true;
                    });
                    await signOut();
                    setState(() {
                      _isSigningOut = false;
                    });
                    Navigator.of(context).pushReplacement(
                      MaterialPageRoute(
                        builder: (context) => const GoogleLogin(),
                      ),
                    );
                  },
                )
            ],
          ),
        ),
    );
  }

  Future signOut() async {
    final GoogleSignIn googleSignIn = GoogleSignIn();
    await googleSignIn.signOut();
    await FirebaseAuth.instance.signOut();
  }
}

SignOutするとログイン用画面に戻るようにしてあります。

これで①Googleにログイン編は終わりです。

  • URLをコピーしました!

コメント

コメントする

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

目次