flutterアプリからGoogleSpreadsheetにデータ保存(2.Spreadsheet作成編)

こんにちは。

前回の記事でFlutterアプリでGoogleログインをしましたので、今日はSpreadsheet作成して適当なデータを入れたいと思います。今日は②Spreadsheet作成編するです。

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

前回の記事はこちらになります。

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

2021.11.06

今回の記事ではUpload用のボタンを用意した画面を表示し、Uploadボタンで押下GoogleDriveにSpreadsheetを作成します。

作成したSpreadsheetです。

手順

使用するGoogleCloudAPIの設定をする必要があります。以下手順で実施します。

手順
  1. プラグインインストール
  2. GoogleCloudAPIの設定
  3. GoogleCloudAPIのOAuth2.0スコープ設定
  4. ログイン用ソースコード(修正)
  5. Spreadsheet作成ソースコード

①プラグインインストール

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

googleapisGoogleCloudAPIを使用するFlutterプラグイン

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

dependencies:
 ・・・
  googleapis: ^6.0.0

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

②GoogleCloudAPIの設定

「Google Drive API」と「Google Sheets API」を利用出来るようにします。
Google Cloud Consoleで[APIとサービス]から[ライブラリ]を選択します。

「Google Drive」などのキーワードを入れて検索します。

「Google Drive API」を有効にします。

同じように「Google Sheets API」も有効にします。

③GoogleAPIのOAuth2.0スコープ設定

アプリがAPIを使用してGoogleユーザーのデータにアクセスする場合、そのスコープ(範囲)をユーザに同意してもらう必要があります。その認証にOAuth2.0認証を使用します。

先ほど有効にした「Google Drive API」と「Google Sheets API」のスコープを決めます。今回はGoogleDriveにSheetsファイルを作成し、Sheetsファイルのデータに書き込みを行うのが目的です。

GoogleAPIのOAuth2.0スコープを参照します。

上記赤枠のスコープをOAuth同意画面に登録します。ここに登録する事でアプリにスコープに対する同意画面がユーザーに表示されるようになります。
[OAuth同意画面]から[アプリを編集]を選択します。

①OAuth同意画面から表示されるので適切に入力し、②スコープまで進めます。

[スコープを追加または削除]を選択します。

手動追加にて、以下を追加します。
https://www.googleapis.com/auth/drive.file
https://www.googleapis.com/auth/spreadsheets

③④は必要に応じて入力します。

④ログイン用ソースコード(修正)

前回の記事のログイン用ソースコードを少し修正します。

google_login.dartのsignIn()の中を変更します。

変更前

GoogleSignIn googleSignIn = GoogleSignIn();

変更後

GoogleSignIn googleSignIn = GoogleSignIn(scopes: [
  'https://www.googleapis.com/auth/drive.file',
  'https://www.googleapis.com/auth/spreadsheets',
]);

もう一か所、GoogleLoginInfoクラスを次の章で作成するGoogleSheetsクラスに変更します。
変更前

MaterialPageRoute(
  builder: (context) => GoogleLoginInfo(
    user: user,
  ),
),

変更後

MaterialPageRoute(
  builder: (context) => GoogleSheets(
    user: user,
  ),
),

⑤Spreadsheet作成ソースコード

google_sheets.dartを作成します。

import 'package:flutter/material.dart';
import 'package:http/io_client.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:googleapis/drive/v3.dart' as drive;
import 'package:googleapis/sheets/v4.dart' as sheets;
import 'package:http/http.dart';
import 'package:google_sign_in/google_sign_in.dart';

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

import '../baseappbar.dart';

class Category {
  late int id;
  late String title;

  Category(
      this.id,
      this.title,
      );
}


class GoogleHttpClient extends IOClient {
  Map<String, String> _headers;

  GoogleHttpClient(this._headers) : super();

  @override
  Future<IOStreamedResponse> send(BaseRequest request) =>
      super.send(request..headers.addAll(_headers));

  @override
  Future<Response> head(Uri url, {Map<String, String>? headers}) =>
      super.head(url, headers: headers!..addAll(_headers));
}


class GoogleSheets extends StatefulWidget {

  final User user;

  const GoogleSheets({
    Key? key,
    required this.user,
  }) : super(key: key);

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

class _GoogleSheetsState extends State<GoogleSheets> {
  late User _user;
  GoogleHttpClient? authenticateClient;
  Future? _future;

  bool _isSigningOut = false;
  String _sheetId = '';

  List<Category> list= [
    Category(0,'sentiment'),
    Category(1,'food'),
    Category(2,'sports'),
    Category(3,'hardware'),
    Category(4,'vehicle'),
  ];


  @override
  void initState() {
    _user = widget.user;
    _future = initAuth();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        resizeToAvoidBottomInset : false,
        appBar: TransAppBar(
          title: 'GoogleSpreadSheets',
          appBar: AppBar(),
        ),
        body: FutureBuilder(
            future: _future,
            builder: (context, snapshot) {
              switch (snapshot.connectionState) {
                case ConnectionState.waiting:
                  return const Center(
                    child: CircularProgressIndicator(),
                  );
                default:
                  if (snapshot.hasError) {
                    print(snapshot.error);
                    return Text('Error: ${snapshot.error}');
                  } else {
                    return createGSheets();
                  }
              }
            }
        )
    );
  }

  createGSheets() {
    return 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),
            OutlinedButton(
              child: const Text('Upload'),
              onPressed: () async {
                if (authenticateClient == null) {
                  Navigator.of(context).pushReplacement(
                    MaterialPageRoute(
                      builder: (context) => const GoogleLogin(),
                    ),
                  );
                }
                String? sheetId = await createSheet();
                if(sheetId != null) {
                  await insertSheet(sheetId);

                  showDialog(
                    context: context,
                    builder: (context) {
                      return AlertDialog(
                        content: const Text('Googleスプレッドシートを作成しました。'),
                        actions: [
                          ElevatedButton(
                            onPressed: () => Navigator.pop(context, 'OK'),
                            child: const Text('OK'),
                          ),
                        ],
                      );
                    },
                  );
                }
              },
            ),
            _isSigningOut
              ? const CircularProgressIndicator()
              : 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<GoogleHttpClient?> initAuth() async {

    GoogleSignInAccount? googleSignInAccount = await GoogleSignIn().signInSilently();
    if (googleSignInAccount == null) {
      return null;
    }

    authenticateClient = GoogleHttpClient(await googleSignInAccount.authHeaders);
    return authenticateClient;
  }

  Future<String?> createSheet() async {

    GoogleSignInAccount? googleSignInAccount = await GoogleSignIn().signInSilently();
    if (googleSignInAccount == null) {
      return null;
    }

    authenticateClient = GoogleHttpClient(await googleSignInAccount.authHeaders);
    final driveApi = drive.DriveApi(authenticateClient!);

    var driveFile = drive.File();
    driveFile.name = 'FlutterApp';
    driveFile.mimeType = 'application/vnd.google-apps.spreadsheet';
    final result = await driveApi.files.create(driveFile);
    String sheetId = result.id!;

    return sheetId;
  }

  Future<void> insertSheet(String sheetId) async {

    final sheetsApi = sheets.SheetsApi(authenticateClient!);

    List<dynamic> inputData = [];
    for (int i = 0; i < list.length; i++) {
      List<dynamic> row = [];
      row.add(list[i].id);
      row.add(list[i].title);
      inputData.add(row);
    }
    sheets.ValueRange vr = sheets.ValueRange.fromJson({ 'values': inputData });

    await sheetsApi.spreadsheets.values
        .append(vr, sheetId, 'A:B', valueInputOption: 'USER_ENTERED')
        .then((sheets.AppendValuesResponse r) {
    });

    return;
  }

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

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

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