Flutterでのバーコードスキャンと、国会図書館APIを使った書籍情報取得する方法

2023-11-10-00-10-33.webp
目次

はじめに

実に10年ぶりぐらいにAndroidのアプリデベロッパーに再登録しました。 登録したからには、なにか作って公開したいなということで勧めていきます。 まずは小さいものを作って公開しまくるのがニッチ戦略としては正しいとようですので色々作ってみようかと思っています。

欲しい物を作るのが良いので、蔵書管理アプリでも作ろうかと思っています。 本屋でこの本持っていたかなーって調べることができるといいですよね。

本のバーコードを読み取り、書籍情報を取得し、データベースに溜め込むような蔵書管理アプリを作っていきます。

やることの整理

  • バーコードを読み取り、ISBNを取得
  • ISBNを国会図書館APIを通して書籍情報を取得

画面などは機能を追加するたびに都度追加していきます。 まずはバーコードを読み取り、ISBNの取得し、国会図書館APIを要求するところまでを行います。

main.dart

メイン画面にボタンをおいて、バーコードスキャンしてISBNコードを取得できるようにします。 スキャンに成功したら、ホーム画面にISBNが表示されるようにします。

このように書きました。

import 'package:flutter/material.dart';
import 'package:flutter_barcode_scanner/flutter_barcode_scanner.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Barcode Scanner Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String _scannedBarcode = 'Unknown';

  Future<void> scanBarcode() async {
    try {
      final barcodeScanRes = await FlutterBarcodeScanner.scanBarcode(
        '#ff6666',
        'Cancel',
        true,
        ScanMode.BARCODE,
      );
      if (!mounted) return;
      setState(() {
        _scannedBarcode =
            barcodeScanRes != '-1' ? barcodeScanRes : 'Failed to get ISBN';
      });
    } catch (e) {
      setState(() {
        _scannedBarcode = 'Failed to get ISBN';
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Scanned Barcode:',
              style: TextStyle(fontSize: 16),
            ),
            Text(
              _scannedBarcode,
              style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () => scanBarcode(),
              child: Text('Scan Barcode'),
            ),
          ],
        ),
      ),
    );
  }
}

実行するとこのような画面になります。

国会図書館APIを使用して書籍情報の取得

巷では、Google Books APIなどを使う例が多いのですが、書籍については国会図書館APIが使い勝手が良いです。 先程取得できたISBNを指定してクエリーを投げる関数を作成し、応答

書籍情報取得API

国会図書館APIのURLと、ISBNを指定して検索できるようにしました。 取得したデータを整形する部分は別途用意します。

class _MyHomePageState extends State<MyHomePage> {
  String _scannedBarcode = 'Unknown';

  Future<void> fetchBookInfo(String isbn) async {
    final String cql = 'isbn="$isbn" AND dpid=iss-ndl-opac';
    final uri = Uri.parse(
        'http://iss.ndl.go.jp/api/sru?operation=searchRetrieve&query=$cql');
    final response = await http.get(uri);

    // レスポンスを処理するコードをここに記述...
  }

省略

書籍情報を取得するためのCQL(Contextual Query Language)クエリを構築しているので、ISBNを使用して書籍の情報を検索し、取得します。

なのでSURのRequestを送りたい場合は上記のコードで取得できます。

取得したXMLの解析と画面への表示

flutter pub add xml

XML解析用のパッケージを追加します。

class _MyHomePageState extends State<MyHomePage> {
  String _scannedBarcode = 'Unknown';
  String _bookTitle = '';
  String _bookAuthor = '';
  String _bookPrice = '';
  String _catalogingStatus = '';
  String _catalogingRule = '';
  String _managementDescription = '';
  String _bibRecordCategory = '';
  String _bibRecordSubCategory = '';

  Future<void> fetchBookInfo(String isbn) async {
    // final String cql = 'isbn="$isbn" AND dpid=iss-ndl-opac';
    final uri = Uri.parse(
        'https://iss.ndl.go.jp/api/sru?operation=searchRetrieve&version=1.2&recordSchema=dcndl&onlyBib=true&recordPacking=xml&query=isbn="$isbn"%20AND%20dpid=iss-ndl-opac');
    // 'http://iss.ndl.go.jp/api/sru?operation=searchRetrieve&query=$cql');
    final response = await http.get(uri);

    if (response.statusCode == 200) {
      final document = XmlDocument.parse(response.body);

      _bookTitle = document
          .findAllElements('dcterms:title')
          .map((node) => node.text)
          .join(', ');

      // 作者
      _bookAuthor = document
          .findAllElements('dc:creator')
          .map((node) => node.text)
          .join(', ');

      // 価格
      _bookPrice = document
          .findAllElements('dcndl:price')
          .map((node) => node.text)
          .join(', ');

      // 管理情報
      _catalogingStatus = document
          .findAllElements('dcndl:catalogingStatus')
          .map((node) => node.text)
          .join(', ');
      _catalogingRule = document
          .findAllElements('dcndl:catalogingRule')
          .map((node) => node.text)
          .join(', ');
      _managementDescription = document
          .findAllElements('dcterms:description')
          .where((element) =>
              element.parentElement?.name.local == 'BibAdminResource')
          .map((node) => node.text)
          .join(', ');

      // メタデータの提供者
      _bibRecordCategory = document
          .findAllElements('dcndl:bibRecordCategory')
          .map((node) => node.text)
          .join(', ');
      _bibRecordSubCategory = document
          .findAllElements('dcndl:bibRecordSubCategory')
          .map((node) => node.text)
          .join(', ');

      // UIを更新するためにsetStateを呼ぶ
      setState(() {
        _scannedBarcode = isbn;
        // 抽出した情報をウィジェットで表示できるようにセット
      });
    } else {
      throw Exception('Failed to load book data');
    }
  }

  Future<void> scanBarcode() async {
    try {
      final barcodeScanRes = await FlutterBarcodeScanner.scanBarcode(
        '#ff6666',
        'Cancel',
        true,
        ScanMode.BARCODE,
      );
      if (!mounted) return;
      setState(() {
        _scannedBarcode =
            barcodeScanRes != '-1' ? barcodeScanRes : 'Failed to get ISBN';
      });

      // バーコードスキャン後に本の情報をフェッチ
      if (barcodeScanRes != '-1') {
        fetchBookInfo(barcodeScanRes);
      }
    } catch (e) {
      setState(() {
        _scannedBarcode = 'Failed to get ISBN';
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Scanned Barcode:',
              style: TextStyle(fontSize: 16),
            ),
            Text(
              _scannedBarcode,
              style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () => scanBarcode(),
              child: Text('Scan Barcode'),
            ),
            // 取得した情報を表示
            Text(
              'Book Title: $_bookTitle',
              style: TextStyle(fontSize: 16),
            ),
            Text(
              'Author: $_bookAuthor',
              style: TextStyle(fontSize: 16),
            ),
            Text(
              'Price: $_bookPrice',
              style: TextStyle(fontSize: 16),
            ),
          ],
        ),
      ),
    );
  }
}

XML解析して、データを表示するようにしました。

これで大枠は完成です。 あとは表示方法をいじるなどしていい感じに整えてあげれば良いです。

まとめ

FlutterからバーコードスキャンしてISBNを取得する方法と、国会図書館APIにISBNを指定したリクエストを投げてデータ取得する方法をまとめました。

Related Post

> Flutterでのバーコードスキャンと、国会図書館APIを使った書籍情報取得する方法
Flutterで実行時オプションを使って処理内容を切り替える方法
> Flutterでのバーコードスキャンと、国会図書館APIを使った書籍情報取得する方法
FlutterからTerminalコマンドを実行する方法
> Flutterでのバーコードスキャンと、国会図書館APIを使った書籍情報取得する方法
Flutter でQRコードを生成と読み取りをやってみよう
> Flutterでのバーコードスキャンと、国会図書館APIを使った書籍情報取得する方法
Flutter + flutter_hooksで始めるUI分離パターン
> Flutterでのバーコードスキャンと、国会図書館APIを使った書籍情報取得する方法
FlutterでWebRTCを使ったビデオ通話アプリを試してみる
> Flutterでのバーコードスキャンと、国会図書館APIを使った書籍情報取得する方法
Android EmulatorでWebカメラの映像を出力する方法【Flutter】

おすすめの商品

>