Flutter原生传感器访问插件ios_native_sensors的使用

Flutter原生传感器访问插件ios_native_sensors的使用

fluttercommunity/plus_plugins 项目提交 8f132a2

平台支持

仅限iOS

使用

部分符号已更改,请参阅示例代码以获取更多详细信息。如果您可以帮助我维护此插件,请联系我 @normidar

TODO

  • iOS 姿态传感器

完整示例Demo

示例代码

// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// ignore_for_file: public_member_api_docs

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:ios_native_sensors/ios_native_sensors.dart';

import 'snake.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  SystemChrome.setPreferredOrientations(
    [
      DeviceOrientation.portraitUp,
      DeviceOrientation.portraitDown,
    ],
  );

  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sensors Demo',
      theme: ThemeData(
        useMaterial3: true,
        colorSchemeSeed: const Color(0x9f4376f8),
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final String? title;

  const MyHomePage({super.key, this.title});

  [@override](/user/override)
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  static const Duration _ignoreDuration = Duration(milliseconds: 20);

  static const int _snakeRows = 20;
  static const int _snakeColumns = 20;
  static const double _snakeCellSize = 10.0;

  MotionEvent? _motionEvent;
  GravityEvent? _accelerometerEvent;
  RotationRateEvent? _gyroscopeEvent;
  MagnetometerEvent? _magnetometerEvent;

  DateTime? _userAccelerometerUpdateTime;
  DateTime? _accelerometerUpdateTime;
  DateTime? _gyroscopeUpdateTime;
  DateTime? _magnetometerUpdateTime;

  int? _userAccelerometerLastInterval;
  int? _accelerometerLastInterval;
  int? _gyroscopeLastInterval;
  int? _magnetometerLastInterval;
  final _streamSubscriptions = <StreamSubscription<dynamic>>[];

  Duration sensorInterval = SensorInterval.normalInterval;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Sensors Plus Example'),
        elevation: 4,
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: <Widget>[
          Center(
            child: DecoratedBox(
              decoration: BoxDecoration(
                border: Border.all(width: 1.0, color: Colors.black38),
              ),
              child: SizedBox(
                height: _snakeRows * _snakeCellSize,
                width: _snakeColumns * _snakeCellSize,
                child: Snake(
                  rows: _snakeRows,
                  columns: _snakeColumns,
                  cellSize: _snakeCellSize,
                ),
              ),
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(20.0),
            child: Table(
              columnWidths: const {
                0: FlexColumnWidth(4),
                4: FlexColumnWidth(2),
              },
              children: [
                const TableRow(
                  children: [
                    SizedBox.shrink(),
                    Text('X'),
                    Text('Y'),
                    Text('Z'),
                    Text('Interval'),
                  ],
                ),
                TableRow(
                  children: [
                    const Padding(
                      padding: EdgeInsets.symmetric(vertical: 8.0),
                      child: Text('UserAccelerometer'),
                    ),
                    Text(_motionEvent?.userAcceleration.vector.x.toStringAsFixed(1) ?? '?'),
                    Text(_motionEvent?.userAcceleration.vector.y.toStringAsFixed(1) ?? '?'),
                    Text(_motionEvent?.userAcceleration.vector.z.toStringAsFixed(1) ?? '?'),
                    Text('${_userAccelerometerLastInterval?.toString() ?? '?'} ms'),
                  ],
                ),
                TableRow(
                  children: [
                    const Padding(
                      padding: EdgeInsets.symmetric(vertical: 8.0),
                      child: Text('Accelerometer'),
                    ),
                    Text(_accelerometerEvent?.vector.x.toStringAsFixed(1) ?? '?'),
                    Text(_accelerometerEvent?.vector.y.toStringAsFixed(1) ?? '?'),
                    Text(_accelerometerEvent?.vector.z.toStringAsFixed(1) ?? '?'),
                    Text('${_accelerometerLastInterval?.toString() ?? '?'} ms'),
                  ],
                ),
                TableRow(
                  children: [
                    const Padding(
                      padding: EdgeInsets.symmetric(vertical: 8.0),
                      child: Text('Gyroscope'),
                    ),
                    Text(_gyroscopeEvent?.vector.x.toStringAsFixed(1) ?? '?'),
                    Text(_gyroscopeEvent?.vector.y.toStringAsFixed(1) ?? '?'),
                    Text(_gyroscopeEvent?.vector.z.toStringAsFixed(1) ?? '?'),
                    Text('${_gyroscopeLastInterval?.toString() ?? '?'} ms'),
                  ],
                ),
                TableRow(
                  children: [
                    const Padding(
                      padding: EdgeInsets.symmetric(vertical: 8.0),
                      child: Text('Magnetometer'),
                    ),
                    Text(_magnetometerEvent?.vector.x.toStringAsFixed(1) ?? '?'),
                    Text(_magnetometerEvent?.vector.y.toStringAsFixed(1) ?? '?'),
                    Text(_magnetometerEvent?.vector.z.toStringAsFixed(1) ?? '?'),
                    Text('${_magnetometerLastInterval?.toString() ?? '?'} ms'),
                  ],
                ),
              ],
            ),
          ),
          Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              const Text('Update Interval:'),
              SegmentedButton(
                segments: [
                  ButtonSegment(
                    value: SensorInterval.gameInterval,
                    label: Text('Game\n(${SensorInterval.gameInterval.inMilliseconds}ms)'),
                  ),
                  ButtonSegment(
                    value: SensorInterval.uiInterval,
                    label: Text('UI\n(${SensorInterval.uiInterval.inMilliseconds}ms)'),
                  ),
                  ButtonSegment(
                    value: SensorInterval.normalInterval,
                    label: Text('Normal\n(${SensorInterval.normalInterval.inMilliseconds}ms)'),
                  ),
                  const ButtonSegment(
                    value: Duration(milliseconds: 500),
                    label: Text('500ms'),
                  ),
                  const ButtonSegment(
                    value: Duration(seconds: 1),
                    label: Text('1s'),
                  ),
                ],
                selected: {sensorInterval},
                showSelectedIcon: false,
                onSelectionChanged: (value) {
                  setState(() {
                    sensorInterval = value.first;
                    motionEventStream(samplingPeriod: sensorInterval);
                    accelerometerEventStream(samplingPeriod: sensorInterval);
                    gyroscopeEventStream(samplingPeriod: sensorInterval);
                    magnetometerEventStream(samplingPeriod: sensorInterval);
                  });
                },
              ),
            ],
          ),
        ],
      ),
    );
  }

  [@override](/user/override)
  void dispose() {
    super.dispose();
    for (final subscription in _streamSubscriptions) {
      subscription.cancel();
    }
  }

  [@override](/user/override)
  void initState() {
    super.initState();
    _streamSubscriptions.add(
      motionEventStream(samplingPeriod: sensorInterval).listen(
        (MotionEvent event) {
          final now = DateTime.now();
          setState(() {
            _motionEvent = event;
            if (_userAccelerometerUpdateTime != null) {
              final interval = now.difference(_userAccelerometerUpdateTime!);
              if (interval > _ignoreDuration) {
                _userAccelerometerLastInterval = interval.inMilliseconds;
              }
            }
          });
          _userAccelerometerUpdateTime = now;
        },
        onError: (e) {
          showDialog(
              context: context,
              builder: (context) {
                return AlertDialog(
                  title: Text("Sensor Not Found"),
                  content: Text("It seems that your device doesn't support User Accelerometer Sensor\n$e"),
                );
              });
        },
        cancelOnError: true,
      ),
    );
    _streamSubscriptions.add(
      accelerometerEventStream(samplingPeriod: sensorInterval).listen(
        (GravityEvent event) {
          final now = DateTime.now();
          setState(() {
            _accelerometerEvent = event;
            if (_accelerometerUpdateTime != null) {
              final interval = now.difference(_accelerometerUpdateTime!);
              if (interval > _ignoreDuration) {
                _accelerometerLastInterval = interval.inMilliseconds;
              }
            }
          });
          _accelerometerUpdateTime = now;
        },
        onError: (e) {
          showDialog(
              context: context,
              builder: (context) {
                return const AlertDialog(
                  title: Text("Sensor Not Found"),
                  content: Text("It seems that your device doesn't support Accelerometer Sensor"),
                );
              });
        },
        cancelOnError: true,
      ),
    );
    _streamSubscriptions.add(
      gyroscopeEventStream(samplingPeriod: sensorInterval).listen(
        (RotationRateEvent event) {
          final now = DateTime.now();
          setState(() {
            _gyroscopeEvent = event;
            if (_gyroscopeUpdateTime != null) {
              final interval = now.difference(_gyroscopeUpdateTime!);
              if (interval > _ignoreDuration) {
                _gyroscopeLastInterval = interval.inMilliseconds;
              }
            }
          });
          _gyroscopeUpdateTime = now;
        },
        onError: (e) {
          showDialog(
              context: context,
              builder: (context) {
                return const AlertDialog(
                  title: Text("Sensor Not Found"),
                  content: Text("It seems that your device doesn't support Gyroscope Sensor"),
                );
              });
        },
        cancelOnError: true,
      ),
    );
    _streamSubscriptions.add(
      magnetometerEventStream(samplingPeriod: sensorInterval).listen(
        (MagnetometerEvent event) {
          final now = DateTime.now();
          setState(() {
            _magnetometerEvent = event;
            if (_magnetometerUpdateTime != null) {
              final interval = now.difference(_magnetometerUpdateTime!);
              if (interval > _ignoreDuration) {
                _magnetometerLastInterval = interval.inMilliseconds;
              }
            }
          });
          _magnetometerUpdateTime = now;
        },
        onError: (e) {
          showDialog(
              context: context,
              builder: (context) {
                return const AlertDialog(
                  title: Text("Sensor Not Found"),
                  content: Text("It seems that your device doesn't support Magnetometer Sensor"),
                );
              });
        },
        cancelOnError: true,
      ),
    );
  }
}

更多关于Flutter原生传感器访问插件ios_native_sensors的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter原生传感器访问插件ios_native_sensors的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


ios_native_sensors 是一个 Flutter 插件,用于在 iOS 设备上访问原生传感器数据。它允许开发者直接从 iOS 设备的硬件传感器(如加速度计、陀螺仪、磁力计等)获取数据,并将其集成到 Flutter 应用中。

以下是如何使用 ios_native_sensors 插件的步骤:

1. 添加依赖

首先,在 pubspec.yaml 文件中添加 ios_native_sensors 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  ios_native_sensors: ^1.0.0  # 请使用最新版本

然后运行 flutter pub get 来安装依赖。

2. 导入插件

在需要使用传感器的 Dart 文件中导入插件:

import 'package:ios_native_sensors/ios_native_sensors.dart';

3. 初始化传感器

在使用传感器之前,需要先初始化。通常,你可以在 initState 方法中进行初始化:

class SensorPage extends StatefulWidget {
  @override
  _SensorPageState createState() => _SensorPageState();
}

class _SensorPageState extends State<SensorPage> {
  IosNativeSensors _sensors = IosNativeSensors();
  StreamSubscription<SensorEvent>? _subscription;

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

  void _initializeSensors() async {
    bool isAvailable = await _sensors.checkSensorAvailability(SensorType.accelerometer);
    if (isAvailable) {
      _subscription = _sensors.sensorEvents.listen((event) {
        print("Sensor Data: ${event.data}");
      });
    } else {
      print("Accelerometer not available");
    }
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Sensor Example'),
      ),
      body: Center(
        child: Text('Check console for sensor data'),
      ),
    );
  }
}

4. 监听传感器数据

在上面的代码中,我们使用 _sensors.sensorEvents.listen 来监听传感器数据。当传感器数据发生变化时,回调函数会被触发,你可以在回调函数中处理传感器数据。

5. 处理传感器数据

listen 回调中,你可以获取到 SensorEvent 对象,它包含了传感器类型和数据:

_subscription = _sensors.sensorEvents.listen((event) {
  if (event.type == SensorType.accelerometer) {
    print("Accelerometer Data: ${event.data}");
  } else if (event.type == SensorType.gyroscope) {
    print("Gyroscope Data: ${event.data}");
  }
});

6. 释放资源

dispose 方法中,记得取消订阅以释放资源:

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

7. 支持的传感器类型

ios_native_sensors 插件支持多种传感器类型,包括:

  • SensorType.accelerometer:加速度计
  • SensorType.gyroscope:陀螺仪
  • SensorType.magnetometer:磁力计
  • SensorType.deviceMotion:设备运动传感器

你可以通过 checkSensorAvailability 方法来检查设备是否支持某个传感器。

8. 处理权限

在 iOS 上,访问某些传感器可能需要用户授权。你需要在 Info.plist 文件中添加相应的权限描述:

<key>NSMotionUsageDescription</key>
<string>We need access to the motion sensors to provide certain features.</string>
回到顶部