Open13

Ubntsuローカルサーバーを用いてFlutter開発ログ

佃の備忘録佃の備忘録

環境

  • 自宅に眠ってたミニデスクトップにubtnsuOSをいれてSSH接続できるようにしている
  • WindowsPCがメイン機

目標

  • Flutterの開発環境をUbnstuに集約
  • QRコードを読み取りデータとして保存するアプリを作成

経緯

  • 貯まったアイコスのQRコードをなくしたいため
  • Flutterの勉強のため
佃の備忘録佃の備忘録

Fluuterの依存関係をインストール

sudo apt-get update
sudo apt-get install git curl wget unzip xz-utils zip libglu1-mesa

最新のSDKをダウンロード

https://docs.flutter.dev/release/archive
から最新のバージョンを確認し、wgetで取得

wget https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.24.0-stable.tar.xz

そのあと解凍

tar xf flutter_linux_3.24.0-stable.tar.xz

パスの追加

echo 'export PATH="$PATH:/home/tsukuda/flutter/bin"' >> ~/.bashrc
source ~/.bashrc

動作確認

flutter doctor

これでメッセージがただしくでてたのでインストール完了

佃の備忘録佃の備忘録

doctorのエラーを修正

いくつか失敗していたので修正する

tsukuda@homeserver:~/works/SaveQR$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.24.0, on Ubuntu 24.04 LTS 6.8.0-35-generic, locale en_US.UTF-8)
[✗] Android toolchain - develop for Android devices
    ✗ Unable to locate Android SDK.
      Install Android Studio from: https://developer.android.com/studio/index.html
      On first launch it will assist you in installing the Android SDK components.
      (or visit https://flutter.dev/to/linux-android-setup for detailed instructions).
      If the Android SDK has been installed to a custom location, please use
      `flutter config --android-sdk` to update to that location.

[✗] Chrome - develop for the web (Cannot find Chrome executable at google-chrome)
    ! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable.
[✗] Linux toolchain - develop for Linux desktop
    ✗ clang++ is required for Linux development.
      It is likely available from your distribution (e.g.: apt install clang), or can be downloaded from https://releases.llvm.org/
    ✗ CMake is required for Linux development.
      It is likely available from your distribution (e.g.: apt install cmake), or can be downloaded from https://cmake.org/download/
    ✗ ninja is required for Linux development.
      It is likely available from your distribution (e.g.: apt install ninja-build), or can be downloaded from https://github.com/ninja-build/ninja/releases
    ✗ pkg-config is required for Linux development.
      It is likely available from your distribution (e.g.: apt install pkg-config), or can be downloaded from https://www.freedesktop.org/wiki/Software/pkg-config/
[!] Android Studio (not installed)
[✓] Connected device (1 available)
[✓] Network resources

! Doctor found issues in 4 categories.
佃の備忘録佃の備忘録

Linux toolchain - develop for Linux desktopの対応

sudo apt-get install clang
sudo apt-get install cmake
sudo apt-get install ninja-build
sudo apt-get install pkg-config
sudo apt-get install libgtk-3-dev

これでエラーは消えた

佃の備忘録佃の備忘録

Android toolchain - develop for Android devices

https://qiita.com/taiyodayo/items/2067d276031c3cf42b55
を参考に対応

# SDKをいれる
sudo apt install android-sdk openjdk-17-jdk
# sudoで利用になるため、ルートに移動
sudo cp -rf /usr/lib/android-sdk ~/
sudo chown -R $(id -u):$(id -g) ~/android-sdk
# toolsをインストール
wget https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip
unzip commandlinetools-linux-11076708_latest.zip
mkdir -p ~/android-sdk/cmdline-tools/latest
mv ~/cmdline-tools/* ~/android-sdk/cmdline-tools/latest/
# 設定を変更する
vi  ~/.bashrc
source ~/.bashrc
# sdkマネージャーで必要なレベルを入れる
sdkmanager "platform-tools" "platforms;android-29" "build-tools;29.0.3"
# 最後に同意を行っておく
flutter doctor --android-licenses

クロームはなくていい、これでflutterの準備完了

tsukuda@homeserver:~$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.24.0, on Ubuntu 24.04 LTS 6.8.0-35-generic, locale en_US.UTF-8)
[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
[✗] Chrome - develop for the web (Cannot find Chrome executable at google-chrome)
    ! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable.
[✓] Linux toolchain - develop for Linux desktop
[!] Android Studio (not installed)
[✓] Connected device (1 available)
[✓] Network resources

! Doctor found issues in 2 categories.
佃の備忘録佃の備忘録

稼働するもリモートのため失敗

tsukuda@homeserver:~/works/saveqr$ flutter run
Launching lib/main.dart on Linux in debug mode...
Building Linux application...                                           
✓ Built build/linux/x64/debug/bundle/saveqr

(saveqr:750820): Gtk-WARNING **: 13:23:52.733: cannot open display: 
Error waiting for a debug connection: The log reader stopped unexpectedly, or never started.
Error launching application on Linux.

どうやらX11フォワーディング
にてlinuxで表示できないGUIをホスト機に送る必要がある。

http://sourceforge.net/projects/xming

にあるパブリックのXmingをインストールしてやってみたがうまくいかず
DISPLAYが空のままになる。
できた人おしえてほしい、、

佃の備忘録佃の備忘録

VcXsrvでやってみる

https://sourceforge.net/projects/vcxsrv/
からインストール
Select display settings: Multiple windows を選択

Select how to start clients: Start no client を選択

Disable access controlを選択

これでインストールしてxclockが表示されたけど
リモートからはだせなかった

佃の備忘録佃の備忘録

原因

  • windows側にいれていたESETが6000ポートをブロックしていた
  • export DISPLAY=192.168.68.63:0.0
    • にて、PCのIPを直接してしたらいけた
  • ただ普通にSSHしたら描画重かったので
    • ssh -YC ○○で接続したら軽くなった

https://dev.classmethod.jp/articles/wsl-x-window/

を参考

flutter run

で実行し

r

でホットリロード状態に

佃の備忘録佃の備忘録

QRコードの読み取り

パーミッションの設定

  • AndroidManifest.xml
<uses-permission android:name="android.permission.CAMERA"/>
  • Info.plist
<key>NSCameraUsageDescription</key>
<string>QRコードのスキャンのためにカメラにアクセスします。</string>

まずQRの読み取りに必要なパッケージ追加

flutter pub add qr_code_scanner

次にjava17を利用バージョンに

export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64

Gradleのバージョン指定

  • android/gradle/wrapper/gradle-wrapper.properties
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip

android端末をUSBで接続

  • ubntsuを入れたPCのほうにUSBでandroidをつないだ
  • 下記コマンドで端末を確認
adb devices

キャッシュをけして、実行

flutter clean
flutter pub get
flutter run

これでQRコードの開発準備ができた

佃の備忘録佃の備忘録

こぴぺ用のパッケージ

flutter pub add qr_code_scanner

読み取った値をコピペできるように

import 'package:flutter/material.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
import 'package:clipboard/clipboard.dart';
import 'dart:io'; // これを追加

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: QRViewExample(),
    );
  }
}

class QRViewExample extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _QRViewExampleState();
}

class _QRViewExampleState extends State<QRViewExample> {
  final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
  QRViewController? controller;
  String? qrText;

  @override
  void reassemble() {
    super.reassemble();
    if (Platform.isAndroid) {
      // Platform クラスを使用するために dart:io が必要
      controller!.pauseCamera();
    }
    controller!.resumeCamera();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: <Widget>[
          Expanded(
            flex: 4,
            child: QRView(
              key: qrKey,
              onQRViewCreated: _onQRViewCreated,
            ),
          ),
          Expanded(
            flex: 1,
            child: Center(
              child: Text(qrText ?? 'スキャンしてQRコードを表示'),
            ),
          ),
          Expanded(
            flex: 1,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(qrText ?? 'スキャンしてQRコードを表示'),
                ElevatedButton(
                  onPressed: () {
                    if (qrText != null) {
                      FlutterClipboard.copy(qrText!).then((value) =>
                          ScaffoldMessenger.of(context).showSnackBar(
                              SnackBar(content: Text('クリップボードにコピーしました'))));
                    }
                  },
                  child: Text('コピー'),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  void _onQRViewCreated(QRViewController controller) {
    this.controller = controller;
    controller.scannedDataStream.listen((scanData) {
      setState(() {
        qrText = scanData.code;
      });
    });
  }

  @override
  void dispose() {
    controller?.dispose();
    super.dispose();
  }
}

佃の備忘録佃の備忘録

firebaseとの連携

fireabaseのツールインストール

sudo npm install -g firebase-tools

firebaseにログイン

  • ブラウザがないので下記の方法
firebase login --no-localhost

cliパッケージを入れる

dart pub global activate flutterfire_cli
export PATH="$PATH":"$HOME/.pub-cache/bin"
source ~/.bashrc

プロジェクトの指定

  • androidを選択
flutterfire configure --project=ここにプロジェクトID
佃の備忘録佃の備忘録

パッケージ追加

flutter pub add firebase_core
flutter pub add cloud_firestore

保存を確認

  • いったんfirestoreのルールをなしにてテスト

import 'package:flutter/material.dart';
import 'package:qr_code_scanner/qr_code_scanner.dart';
import 'package:clipboard/clipboard.dart';
import 'package:cloud_firestore/cloud_firestore.dart'; // Firestore をインポート
import 'dart:io'; // これを追加
import 'package:firebase_core/firebase_core.dart'; // Firebase を初期化するために必要

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(); // Firebase を初期化
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: QRViewExample(),
    );
  }
}

class QRViewExample extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _QRViewExampleState();
}

class _QRViewExampleState extends State<QRViewExample> {
  final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
  QRViewController? controller;
  String? qrText;

  @override
  void reassemble() {
    super.reassemble();
    if (Platform.isAndroid) {
      controller!.pauseCamera();
    }
    controller!.resumeCamera();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: <Widget>[
          Expanded(
            flex: 4,
            child: QRView(
              key: qrKey,
              onQRViewCreated: _onQRViewCreated,
            ),
          ),
          Expanded(
            flex: 1,
            child: Center(
              child: Text(qrText ?? 'スキャンしてQRコードを表示'),
            ),
          ),
          Expanded(
            flex: 1,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(qrText ?? 'スキャンしてQRコードを表示'),
                ElevatedButton(
                  onPressed: () {
                    if (qrText != null) {
                      // クリップボードにコピー
                      FlutterClipboard.copy(qrText!).then((value) =>
                          ScaffoldMessenger.of(context).showSnackBar(
                              SnackBar(content: Text('クリップボードにコピーしました'))));

                      // Firebase Firestore に保存
                      _saveQRCodeToFirestore(qrText!);
                    }
                  },
                  child: Text('コピー & 保存'),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  void _onQRViewCreated(QRViewController controller) {
    this.controller = controller;
    controller.scannedDataStream.listen((scanData) {
      setState(() {
        qrText = scanData.code;
      });
    });
  }

  Future<void> _saveQRCodeToFirestore(String qrText) async {
    CollectionReference qrCodes =
        FirebaseFirestore.instance.collection('qr_codes');

    await qrCodes.add({
      'content': qrText,
      'timestamp': FieldValue.serverTimestamp(),
    }).then((value) {
      ScaffoldMessenger.of(context)
          .showSnackBar(SnackBar(content: Text('QRコードの内容がFirebaseに保存されました')));
    }).catchError((error) {
      ScaffoldMessenger.of(context)
          .showSnackBar(SnackBar(content: Text('保存に失敗しました: $error')));
    });
  }

  @override
  void dispose() {
    controller?.dispose();
    super.dispose();
  }
}