Flutterでアプリを作る:水準器アプリ編①

現在開発中の別アプリ(お客様のあるお仕事ですので詳細は公開できないです)で、スマホジャイロセンサーや加速度センサーを使う必要がありました。その機能実装のテストフェーズとして、「水準器」アプリを作ってみよう、というのが今回のネタでございます。安直ですねぇ...。

開発の下準備

Android StudioだのVS Codeだので開発環境を整えてくださいね。 やり方なんてggrks。

参考にしているサイト

まずは今の時点で参考にしているサイトを紹介。 1. 「Sensorプラグイン」のgithub。サンプルコードが参考になります。 https://github.com/flutter/plugins/tree/master/packages/sensors 2. 「水準器アプリを作る」というサイト。コードは全然違うけど多分こんな感じでつくっていけばいいのかな、という雰囲気を見るため(完成形をこのサイトのアプリに近づけることが目標) https://codezine.jp/article/detail/8330?p=3

れっつびぎん

プラグインを導入

Flutterでは、必要な機能がプラグイン化されている*1ので、今回はセンサーの利用が可能になるプラグインを導入します。 手順は、 1. プロジェクトルートのpubspec.yamlに使用するプラグインを記述して、

...
dependencies:
  ...
  sensors:

以下のコマンドを実行。

$ flutter packages get
  1. コードの最初でインポート
import 'package:sensors/sensors.dart';
...

コードを書く

参考2を見ると、加速度センサー(Accelerometer)を使うとあります。 最初のサンプルに何箇所か追記して、まずは加速度センサーの値を見ます。

こんな感じ*2

import 'package:flutter/material.dart';
import 'package:sensors/sensors.dart';
import 'dart:async';

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

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

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {

  List<double> _accelerometerValues;
  List<StreamSubscription<dynamic>> _streamSubscriptions = <StreamSubscription<dynamic>>[];

  @override
  Widget build(BuildContext context) {
    final List<String> accelerometer = _accelerometerValues?.map((double v) => v.toStringAsFixed(1))?.toList();
    final double accel_x = _accelerometerValues[0];
    final String accel_y = _accelerometerValues[1].toStringAsFixed(2);

    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Stack(
        children: <Widget>[
          Material(
            color: Colors.yellow[100],
            child: Container(
              padding: EdgeInsets.all(4.0),
              child: FlutterLogo(
                size: 72.0,
              ),
            ),
          ),
          Column(
            children: <Widget>[
              Text('Accelerometer: $accelerometer',),
              Text('ACCEL_X: $accel_x'),
              Text("ACCEL_Y: $accel_y"),
            ],
          ),
        ],
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();
    for (StreamSubscription<dynamic> subscription in _streamSubscriptions) {
      subscription.cancel();
    }
  }

  @override
  void initState() {
    super.initState();
    _streamSubscriptions
      .add(accelerometerEvents.listen((AccelerometerEvent event) {
        setState(() {
          _accelerometerValues = <double>[event.x, event.y, event.z];
        });
    }));
  }
}

気付き

Accelerometerのの値(スマホを縦にしていることを前提)ですが、まずAndroid(LG isai Vivid)は、

  • X軸:スマホの「左右方向」の傾きに反応... 左に傾ける(左が下に向く)と数値がプラス、右に傾ける(左が上に向く)と数値がマイナス。※言い方が逆かな。右が上に向くと数値はプラスになる、という。
  • Y軸:スマホの「上下方向」の傾きに反応...スマホの上辺が高くなれば数値がプラスに、水平方向より上辺が下に向けばマイナス。最大値は10.0(またはマイナス10.0)、かなと思っていたらちょっと誤差が。これはPG側の問題かそれとも。
  • Z軸:Yと同じく「上下方向」だけど、基準になるのはスマホが縦かつ画面が地面と垂直になっている状態。地面と垂直なら0、水平になれば(縦でも横でも)最大値、という感じ。

うんうん。スマホという「板」で考えるからわかりにくいんだよね、多分。 球体で考えれば...、と思うんだけど、あとで絵にしてみよう。

iOS(iPhone)では確認をしていないのですが、まぁ似たような結果が出るんでしょうね。

このあとどうなるのか

一応軸各々の数値は取れることがわかったわけです。あとはひとつオブジェクトを中心に置き、各々の数値をオフセット値として動的にオブジェクトを動かしてあげればよいのかな、と考えていますが。

ちなみに、他にもいくつかやりたいこと(水準器機能とは全く別物)があるので、こっちも進めつついろんな開発(もちろん本業の受託開発も含め)を進めます。公開できるものはGithubで公開中です。

github.com

最後に*3

スマホでもタブレットでもいいんですが、「水準器」として使うのはすごく便利だと個人的には思っています。だって、板ですもん。必ず平らな板ですから...、と思っていたんです。 誰が流行らせたのかわかりませんが、スマホのカメラ、なんで飛び出てるの?*4

ヨメにこの愚痴を言うと、「そのためにスマホケースがあるんだよ。なぜ使わないの?」と反論されてしまいます。もっとも、スマホケースがあればスマホの背面が平らにはなりますが、必ずしも(あくまでもセンサー的に、ですが)水平を保てるとは限らないわけで、水準器としての利用を想定するならあまり良いアイデアではないのかな、とは思いますが、もしそういう使い方をするのであれば、キャリブレーション的な機能を最終的にもたせてもいいのかな、とか妄想が膨らみます*5

*1:この言い方が正しいのかどうかわからないけれど、少なくとも必要のない機能は最初からコンパイルなどされないのでアプリが軽い、とかそういうことなのかしら。よくわかんねぇや。

*2:サンプルへの追記なんでほぼサンプルコードですが...。

*3:どうしても言いたいことです。

*4:もちろんわかって書いています。iPhoneのカメラが背面から飛び出してしまったのはiPhone6からです。ジョブズがいなくなり、世間に迎合するようになったからiPhoneは巨大化し、カメラは飛び出て、更にはカメラレンズが複数になってしまったのです。美しくないなぁ...。

*5:一応スピンオフ企画なので、そこまでやるのか、という大きな議論は必要なのですが...。