FlutterでiPhone(iOS)、Android対応のマルチコード認識アプリ作ってみた
2024-05-29
azblob://2024/05/29/eyecatch/2024-05-29-flutter-app-ios-android-000.jpg

はじめに


今回はFlutterでiPhone(iOS)、Android対応のQRコード認識アプリ作りました。

実装した機能として

  • アプリを作成
  • アプリ内にカメラ機能を実装
  • アプリ内にライト機能の実装、ライト機能の制御機能の実装を行いました。

今回iOSに対応するアプリを作成しなければいけないのに、私自身が使っているのはWindowsだったためAndroidemulatorでのテスト、もしくはandroid実機でテストする以外の手段がなく、iPhone実機でのテストができず、MacユーザーにXcodeなどを介し、テストしてもらう際にエラーが出たらどこを修正すればいいか検討もつかないため修正が期日までに追いつかない可能性が高いという絶望的状況でしたが、無事WindowsにインストールされているFlutterでiOSにもAndroidにも対応したQRコード認識アプリを作成できたので残しておこうと思います。

開発環境


  • Windows
  • vscode
  • Flutter3.22.0

フォルダ作成などの手順は他のブログなどでも紹介されているため省略します。

https://tech-blog.cloud-config.jp/2024-05-24-flutter-emulator-build
セットアップ手順のブログを書いたので良ければこちらも参考にしてください。

パッケージを追加


今回はこのパッケージをつかっていきます~

pubspec.yaml ファイルに mobile_scannerとpermission_handler を追加し、

flutter pub get (上記の2つのパッケージ名)コマンドを実行してインストールしてください
permission_handler は、Flutterアプリで必要な権限を管理するためのパッケージなので、カメラを起動させるときに必要です(permission_handlerのバージョンをミスで古いものを使ってしまったので、適宜変更などしていただければと思います)

カメラ権限を追加


android\app\src\main\AndroidManifest.xml

に以下の内容を必要に応じて追加してください
スマホのカメラを利用する権限を追加するなら以下のコードを追加

C    <uses-permission android:name="android.permission.CAMERA" />

ウェブサイトにアクセスしたければ以下のコードを追加

C <uses-permission android:name="android.permission.INTERNET"/>
   <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />

今回私は要件がカメラでQRコードを読み取れるかという内容でしたので必要なのはカメラだけでしたが

読み取れているか、挙動を確認するためにはテキストメッセージの変化がないとわからないな~ということで

3つ追加しました(スキャンした後の動作を指定している箇所がないためクリックしても飛べないURLのテキストが表示されているだけですが

カメラ使用の説明を追加



iPhoneで標準のカメラではないカメラアプリなどを使うときに「カメラの使用を許可しますか?」という旨の通知来ますよね。それを作ります。
以下のコード追加するとその通知が来るようになります。これを追加しないと、アクセス権の要求ができないのでカメラが動きません。

iOSで使用したかったので以下の文を追加しました。

Ios/Runner/Info.plistの中に

C
<key>NSCameraUsageDescription</key>
    <string>Can I use the camera please?</string>
   <key>NSMicrophoneUsageDescription</key>
   <string>Can I use the mic please?</string>

アプリのロジックを実装


lib/main.dartに

使用したコードを参考例として載せておきます。

import 'package:flutter/material.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:permission_handler/permission_handler.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title'Barcode Scanner',
      homeHomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  String? _scanResult;
  bool _isFlashOn = false;
  MobileScannerController _scannerController = MobileScannerController();

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

  Future<void_requestCameraPermission() async {
    final status = await Permission.camera.request();
    if (status.isGranted) {
      // カメラ使用許可が取得できた場合の処理else if (status.isPermanentlyDenied) {
      // カメラ使用許可が永久に拒否された場合の処理
      openAppSettings();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBarAppBar(
        titleText('Barcode Scanner'),
        actions: [
          IconButton(
            iconIcon(_isFlashOn ? Icons.flash_on : Icons.flash_off),
            onPressed: () {
              setState(() {
                _isFlashOn = !_isFlashOn;
              });
              _scannerController.toggleTorch();
            },
          ),
        ],
      ),
      body: Stack(
        children: [
          MobileScanner(
            controller: _scannerController,
            fit: BoxFit.cover,
            onDetect: (capture) {
              final List<Barcode> barcodes = capture.barcodes;
              for (final barcode in barcodes) {
                setState(() {
                  _scanResult = barcode.rawValue;
                });
              }
            },
          ),
          if (_scanResult != null)
            Positioned(
              bottom20,
              left20,
              childText(
                'Scan Result: $_scanResult',
                styleTextStyle(fontSize20, color: Colors.white),
              ),
            ),
        ],
      ),
    );
  }

<任意のファイル名>/ 

├── android/

│ └── app/

│ └── src/ 

│ └── main/ 

│ └── AndroidManifest.xml (カメラ権限を追加)

├── ios/ 

│ └── Runner/ 

│ └── Info.plist (カメラ使用の説明を追加)

├── lib/ 

│ └── main.dart (アプリのロジックを実装) 

└── pubspec.yaml (パッケージを追加)

階層はこんな感じ(o^―^o)

ビルドデバッグして終わり。

おわりに


今回初めて成果物ができたのでうれしかった(o^―^o)