Flutter设备旋转传感器插件flutter_rotation_sensor的使用

发布于 1周前 作者 yuanlaile 来自 Flutter

Flutter设备旋转传感器插件flutter_rotation_sensor的使用

flutter_rotation_sensor 是一个用于访问设备物理方向的Flutter插件,支持多种表示方式(旋转矩阵、四元数和欧拉角)。本文将详细介绍如何在Flutter应用中使用该插件,并提供完整的示例代码。

特性

  • 实时旋转数据:访问实时旋转数据。
  • 多种格式支持:提供旋转矩阵、四元数和欧拉角(方位角、俯仰角、翻滚角)。
  • 自定义更新间隔:设置自定义间隔以获取传感器数据。
  • 坐标系重映射:支持方向坐标系重映射。

安装

步骤

  1. pubspec.yaml 文件中添加 flutter_rotation_sensor 依赖:

    dependencies:
      flutter_rotation_sensor: ^latest_version
    
  2. 运行以下命令安装插件:

    flutter pub get
    
  3. 在Dart代码中导入插件:

    import 'package:flutter_rotation_sensor/flutter_rotation_sensor.dart';
    

使用

StreamBuilder 示例

使用 StreamBuilder 来接收传感器数据:

@override
Widget build(BuildContext context) {
  return StreamBuilder(
    stream: RotationSensor.orientationStream,
    builder: (context, snapshot) {
      if (snapshot.hasData) {
        final data = snapshot.data!;
        print(data.quaternion);
        print(data.rotationMatrix);
        print(data.eulerAngles);
        // ...
      } else if (snapshot.hasError) {
        return Text('Error: ${snapshot.error}');
      } else {
        return const CircularProgressIndicator();
      }
    },
  );
}

直接订阅流

对于更精细的控制,可以直接订阅流并在 initState 中初始化传感器:

late final StreamSubscription<OrientationData> orientationSubscription;

@override
void initState() {
  super.initState();
  orientationSubscription = RotationSensor.orientationStream.listen((event) {
    final azimuth = event.eulerAngles.azimuth;
    // Print azimuth: 0 for North, π/2 for East, π for South, -π/2 for West
    print(azimuth);
  });
}

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

配置

初始化与配置

可以在 initState 方法中配置插件属性:

@override
void initState() {
  super.initState();
  // 设置采样周期
  RotationSensor.samplingPeriod = SensorInterval.uiInterval;

  // 设置坐标系
  RotationSensor.coordinateSystem = CoordinateSystem.transformed(Axis3.X, Axis3.Z);
}

采样周期

RotationSensor.samplingPeriod 决定传感器数据更新频率。可以使用预定义值或自定义 Duration

void config() {
  RotationSensor.samplingPeriod = Duration(seconds: 1);
}

预定义值包括:

  • SensorInterval.normal (200ms)
  • SensorInterval.ui (66ms)
  • SensorInterval.game (20ms)
  • SensorInterval.fastest (0ms)

坐标系

RotationSensor.coordinateSystem 允许重映射传感器使用的坐标系:

void config() {
  RotationSensor.coordinateSystem = CoordinateSystem.transformed(Axis3.X, -Axis3.Z);
}

预定义坐标系包括:

  • CoordinateSystem.device():相对于设备屏幕默认方向定义。
  • CoordinateSystem.display():适应设备当前方向。
  • CoordinateSystem.transformed():在基础坐标系上应用变换。

示例代码

以下是完整示例代码,展示了如何结合三维渲染库展示旋转数据:

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_rotation_sensor/flutter_rotation_sensor.dart';
import 'package:simple_3d/simple_3d.dart';
import 'package:simple_3d_renderer/simple_3d_renderer.dart';
import 'package:util_simple_3d/util_simple_3d.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  SystemChrome.setPreferredOrientations([
    DeviceOrientation.portraitUp,
    DeviceOrientation.portraitDown,
    DeviceOrientation.landscapeLeft,
    DeviceOrientation.landscapeRight,
  ]);
  runApp(const MyApp());
}

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late final Sp3dWorld world;

  @override
  void initState() {
    super.initState();
    const black = Color(0xFF000000);
    final obj = UtilSp3dGeometry.cube(60, 200, 40, 1, 1, 1)
      ..move(Sp3dV3D(0, 0, -20))
      ..materials = [
        Sp3dMaterial(black, true, 0, black, imageIndex: 0),
        Sp3dMaterial(black, true, 0, black, imageIndex: 1),
        FSp3dMaterial.red,
        FSp3dMaterial.blue,
      ]
      ..fragments[0].faces[0].materialIndex = 1
      ..fragments[0].faces[2].materialIndex = 2
      ..fragments[0].faces[4].materialIndex = 3;

    world = Sp3dWorld([obj]);

    loadImages(world);

    RotationSensor.samplingPeriod = SensorInterval.uiInterval;
  }

  @override
  Widget build(BuildContext context) => MaterialApp(
        home: Scaffold(
          appBar: AppBar(
            title: const Text('Rotation Sensor Example'),
          ),
          body: OrientationBuilder(
            builder: (context, orientation) => StreamBuilder(
              stream: RotationSensor.orientationStream,
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  final data = snapshot.data!;
                  final axisAngle = data.quaternion.invert().toAxisAngle();
                  final axis = axisAngle.axis;
                  return Center(
                    child: Flex(
                      direction: orientation == Orientation.portrait
                          ? Axis.vertical
                          : Axis.horizontal,
                      children: [
                        SizedBox(
                          width: 240,
                          height: 240,
                          child: Sp3dRenderer(
                            const Size(240, 240),
                            const Sp3dV2D(120, 120),
                            world,
                            Sp3dCamera(
                              Sp3dV3D(0, 0, 3000),
                              3000,
                              rotateAxis: Sp3dV3D(axis.x, axis.y, axis.z),
                              radian: axisAngle.angle,
                            ),
                            Sp3dLight(Sp3dV3D(0, 0, 1)),
                            useUserGesture: false,
                          ),
                        ),
                        Expanded(
                          child: Column(
                            mainAxisAlignment: MainAxisAlignment.spaceAround,
                            children: [
                              Text(
                                'Euler:\n'
                                '${formatEulerAngles(data.eulerAngles)}',
                                textAlign: TextAlign.center,
                              ),
                              Text(
                                'Quaternion:\n'
                                '${formatQuaternion(data.quaternion)}',
                                textAlign: TextAlign.center,
                              ),
                              Text(
                                'Matrix:\n'
                                '${formatMatrix(data.rotationMatrix)}',
                                textAlign: TextAlign.center,
                              ),
                              Text(
                                'Accuracy:\n'
                                '${formatDouble(data.accuracy)}',
                                textAlign: TextAlign.center,
                              ),
                              Text(
                                'Timestamp:\n'
                                '${data.timestamp}',
                                textAlign: TextAlign.center,
                              ),
                            ],
                          ),
                        ),
                      ],
                    ),
                  );
                } else if (snapshot.hasError) {
                  return Text('Error: ${snapshot.error}');
                } else {
                  return const CircularProgressIndicator();
                }
              },
            ),
          ),
        ),
      );

  Future<void> loadImages(Sp3dWorld world) async {
    world.objs[0].images = await Future.wait([
      readImageFile('./assets/images/other.png'),
      readImageFile('./assets/images/top.png'),
    ]);
    await world.initImages();
  }

  Future<Uint8List> readImageFile(String filePath) async {
    final byteData = await rootBundle.load(filePath);
    return byteData.buffer.asUint8List();
  }

  String formatQuaternion(Quaternion q) {
    final f = formatDouble;
    return '(${f(q.x)}, ${f(q.y)}, ${f(q.z)} @ ${f(q.w)})';
  }

  String formatMatrix(Matrix3 m) {
    final f = formatDouble;
    return '/${f(m[0])}, ${f(m[3])}, ${f(m[6])}\\\n'
        '| ${f(m[1])}, ${f(m[4])}, ${f(m[7])} |\n'
        '\\${f(m[2])}, ${f(m[5])}, ${f(m[8])}/';
  }

  String formatEulerAngles(EulerAngles e) {
    final f = formatDouble;
    return '(${f(e.azimuth)}, ${f(e.pitch)}, ${f(e.roll)})';
  }

  String formatDouble(double d) => d.toStringAsFixed(2).padLeft(5);

  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.add(DiagnosticsProperty<Sp3dWorld>('world', world));
  }
}

通过以上步骤和示例代码,您可以轻松集成 flutter_rotation_sensor 插件到您的Flutter项目中,实现对设备旋转传感器的访问和处理。


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

1 回复

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


当然,下面是一个关于如何使用 flutter_rotation_sensor 插件来获取设备旋转数据的示例代码。这个插件允许你访问设备的旋转传感器数据,如欧拉角(Euler angles)和四元数(quaternions)。

首先,确保你已经在 pubspec.yaml 文件中添加了 flutter_rotation_sensor 依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_rotation_sensor: ^latest_version  # 请替换为最新版本号

然后,运行 flutter pub get 来获取依赖。

接下来,在你的 Dart 文件中,你可以按照以下步骤使用 flutter_rotation_sensor

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

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  RotationData? _rotationData;

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

  void _initRotationSensor() {
    RotationSensor.rotationEvents.listen((RotationData event) {
      setState(() {
        _rotationData = event;
      });
    });

    // 可选:启动传感器(某些设备可能需要显式启动)
    RotationSensor.startSensor();
  }

  @override
  void dispose() {
    // 停止传感器以节省电池
    RotationSensor.stopSensor();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Rotation Sensor Example'),
        ),
        body: Center(
          child: _rotationData == null
              ? Text('No rotation data available.')
              : Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    Text('X: ${_rotationData!.x.toStringAsFixed(2)}'),
                    Text('Y: ${_rotationData!.y.toStringAsFixed(2)}'),
                    Text('Z: ${_rotationData!.z.toStringAsFixed(2)}'),
                    Text('W (Quaternion): ${_rotationData!.w.toStringAsFixed(2)}'),
                  ],
                ),
        ),
      ),
    );
  }
}

class RotationData {
  final double x;
  final double y;
  final double z;
  final double w; // Quaternion's w component (if available)

  RotationData({required this.x, required this.y, required this.z, required this.w});

  @override
  String toString() {
    return 'RotationData{x: $x, y: $y, z: $z, w: $w}';
  }
}

// 注意:这里的RotationData类是为了示例而创建的,实际使用时应该使用flutter_rotation_sensor包中的RotationData类
// 导入flutter_rotation_sensor包后,直接使用RotationSensorEvent类代替上面的RotationData类
// 例如:RotationSensorEvent event 中的 event.x, event.y, event.z, 和 event.quaternion(如果可用)

注意:上面的代码示例中,我创建了一个自定义的 RotationData 类来模拟从传感器获取的数据。然而,在实际使用中,你应该直接使用 flutter_rotation_sensor 包提供的 RotationSensorEvent 类。因此,你应该将 RotationData 替换为 RotationSensorEvent,并访问 event.x, event.y, event.z, 以及 event.quaternion(如果可用)来获取旋转数据。

实际使用时,你应该这样处理事件数据:

RotationSensor.rotationEvents.listen((RotationSensorEvent event) {
  setState(() {
    // 使用 event.x, event.y, event.z, 和 event.quaternion
  });
});

确保你查阅了 flutter_rotation_sensor 的最新文档,以获取关于如何使用该插件的最新和最准确的信息。

回到顶部