Flutter动画效果插件flutter_motion的使用

Flutter动画效果插件flutter_motion的使用

flutter_motion 是一个用于访问iOS平台上的CMMotionActivityManager和CMPedometer类的插件。该插件可以获取实时运动数据,从你开始监听时计算。

权限设置(Android)

对于Android 10及以上版本,请在Android清单文件中添加以下权限:

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

权限设置(iOS)

在你的Info.plist文件中添加以下条目:

<key>NSMotionUsageDescription</key>
<string>此应用程序将跟踪您的运动数据。</string>
<key>UIBackgroundModes</key>
<array>
    <string>processing</string>
</array>

步数统计

步数统计表示自上次系统启动以来走过的步数。在Android上,安装应用前的步数不会被计入。

行人状态

行人状态可以是 walkingstopped。如果发生错误,则状态为 unknown

传感器可用性

步数统计和行人状态可能在某些手机上不可用:

  • 一些三星手机不支持步数统计或行人状态。
  • 较旧的iPhone不支持行人状态。

如果某个传感器不可用,会抛出错误。你需要自己处理这些错误。

示例代码

下面是一个更通用的例子。请确保已设置上述所需的权限。

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

import 'package:flutter_motion/flutter_motion.dart';
import 'package:provider/provider.dart';

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

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

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

class _MyAppState extends State<MyApp> {
  StreamSubscription<Activity>? _pedestrianStatusSubscription;
  StreamSubscription<Motion>? _motionDetectorSubscription;
  bool isRunning = false;
  String? _motionError, _activityError;

  late ValueNotifier<Activity> _activityNotifier;
  late ValueNotifier<Motion> _motionNotifier;

  @override
  void initState() {
    _activityNotifier = ValueNotifier(Activity.unknown);
    _motionNotifier = ValueNotifier(const Motion());
    super.initState();
  }

  void onMotion(Motion event) {
    // 打印运动事件
    _motionNotifier.value = event;
  }

  void onPedestrianStatusChanged(Activity event) {
    // 打印行人状态变化事件
    _activityNotifier.value = event;
  }

  void onPedestrianStatusError(error) {
    // 打印行人状态错误
    setState(() {
      _motionError = error.toString();
    });
  }

  void onMotionError(error) {
    // 打印运动检测错误
    setState(() {
      _activityError = error.toString();
    });
  }

  _listenToActivity() {
    _pedestrianStatusSubscription = FlutterMotion.pedestrianStatusStream
        .listen(onPedestrianStatusChanged)
      ..onError(onPedestrianStatusError);
  }

  _listenToMotion() {
    _motionDetectorSubscription = FlutterMotion.motionDetectorStream
        .listen(onMotion)
      ..onError(onMotionError);
  }

  _start() {
    _listenToActivity();
    _listenToMotion();
    setState(() {
      isRunning = true;
    });
  }

  _stop() {
    _pedestrianStatusSubscription?.cancel();
    _motionDetectorSubscription?.cancel();
    setState(() {
      isRunning = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
        lazy: false,
        create: (context) => _motionNotifier,
        builder: (c, child) => ChangeNotifierProvider(
              lazy: false,
              create: (context) => _activityNotifier,
              builder: (c, child) => MaterialApp(
                home: Scaffold(
                  appBar: AppBar(
                    title: const Text('FlutterMotion 示例应用'),
                  ),
                  floatingActionButton: FloatingActionButton(
                    onPressed: isRunning ? _stop : _start,
                    child: isRunning
                        ? const Icon(Icons.pause)
                        : const Icon(Icons.play_arrow),
                  ),
                  body: Column(
                    mainAxisAlignment: MainAxisAlignment.start,
                    children: <Widget>[
                      if (_activityError != null) Text(_activityError ?? ''),
                      if (_motionError != null) Text(_motionError ?? ''),
                      Builder(builder: (context) {
                        return ListTile(
                          title: Text(
                              context
                                  .watch<ValueNotifier<Activity>>()
                                  .value
                                  .name,
                              style: Theme.of(context).textTheme.headline4),
                          subtitle: const Text('Activity'),
                        );
                      }),
                      Builder(builder: (context) {
                        var motion = context.watch<ValueNotifier<Motion>>();
                        return Column(
                          children: [
                            ListTile(
                              title: Text(
                                  motion.value.averageActivePace
                                      .toStringAsFixed(2),
                                  style: Theme.of(context).textTheme.headline4),
                              subtitle: const Text('平均活跃步速'),
                            ),
                            ListTile(
                              title: Text(
                                  motion.value.currentPace.toStringAsFixed(2),
                                  style: Theme.of(context).textTheme.headline4),
                              subtitle: const Text('当前步速'),
                            ),
                            ListTile(
                              title: Text(
                                  motion.value.distance.toStringAsFixed(2),
                                  style: Theme.of(context).textTheme.headline4),
                              subtitle: const Text('距离'),
                            ),
                            ListTile(
                              title: Text(
                                  motion.value.floorsAscended
                                      .toStringAsFixed(2),
                                  style: Theme.of(context).textTheme.headline4),
                              subtitle: const Text('爬升楼层'),
                            ),
                            ListTile(
                              title: Text('${motion.value.numberOfSteps}',
                                  style: Theme.of(context).textTheme.headline4),
                              subtitle: const Text('步数'),
                            ),
                            ElevatedButton(
                                onPressed: () {
                                  FlutterMotion.queryData(
                                      DateTime.now().subtract(
                                          const Duration(minutes: 10)),
                                      DateTime.now());
                                  // .then((value) => print(value));
                                },
                                child: const Text('查询运动数据'))
                          ],
                        );
                      }),
                    ],
                  ),
                ),
              ),
            ));
  }
}

查询特定日期范围内的运动数据

你可以使用以下方法来查询特定日期范围内的运动数据:

FlutterMotion.queryData(
  DateTime.now().subtract(const Duration(minutes: 10)),
  DateTime.now(),
).then((value) => print(value));

更多关于Flutter动画效果插件flutter_motion的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter动画效果插件flutter_motion的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


flutter_motion 是一个用于在 Flutter 应用中创建复杂动画效果的插件。它提供了一种简单且灵活的方式来处理动画,使得开发者可以轻松地创建复杂的动画序列和交互效果。以下是如何使用 flutter_motion 插件的基本指南。

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 flutter_motion 依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_motion: ^latest_version

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

2. 基本使用

flutter_motion 提供了多种动画效果,包括平移、缩放、旋转等。以下是一个简单的示例,展示如何使用 flutter_motion 来创建一个平移动画。

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

class MotionExample extends StatefulWidget {
  [@override](/user/override)
  _MotionExampleState createState() => _MotionExampleState();
}

class _MotionExampleState extends State<MotionExample> {
  double _offsetX = 0.0;
  double _offsetY = 0.0;

  void _moveBox() {
    setState(() {
      _offsetX = 100.0;
      _offsetY = 100.0;
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Motion Example'),
      ),
      body: Center(
        child: Motion(
          duration: Duration(seconds: 1),
          curve: Curves.easeInOut,
          translation: Offset(_offsetX, _offsetY),
          child: Container(
            width: 100.0,
            height: 100.0,
            color: Colors.blue,
            child: Center(
              child: Text(
                'Move Me!',
                style: TextStyle(color: Colors.white),
              ),
            ),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _moveBox,
        child: Icon(Icons.play_arrow),
      ),
    );
  }
}

void main() => runApp(MaterialApp(
  home: MotionExample(),
));

3. 解释代码

  • Motion Widget: Motionflutter_motion 提供的核心组件,用于包裹你想要应用动画的子组件。
  • Duration: 指定动画的持续时间。
  • Curve: 指定动画的曲线,例如 Curves.easeInOut 表示动画开始和结束时较慢。
  • Translation: 指定动画的平移距离,Offset(_offsetX, _offsetY) 表示在 X 和 Y 轴上的平移距离。
  • Child: 你想要应用动画的组件。

4. 其他动画效果

flutter_motion 还支持其他动画效果,例如缩放、旋转等。你可以通过设置 scalerotation 属性来实现这些效果。

Motion(
  duration: Duration(seconds: 1),
  curve: Curves.easeInOut,
  scale: 2.0, // 缩放
  rotation: 0.5, // 旋转
  child: Container(
    width: 100.0,
    height: 100.0,
    color: Colors.red,
  ),
);

5. 动画控制器

如果你需要更精细的控制,可以使用 AnimationController 来手动控制动画的开始、暂停、反转等操作。

AnimationController _controller;

[@override](/user/override)
void initState() {
  super.initState();
  _controller = AnimationController(
    duration: Duration(seconds: 1),
    vsync: this,
  );
}

void _startAnimation() {
  _controller.forward();
}

[@override](/user/override)
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Flutter Motion Example'),
    ),
    body: Center(
      child: Motion(
        controller: _controller,
        translation: Offset(100.0, 100.0),
        child: Container(
          width: 100.0,
          height: 100.0,
          color: Colors.green,
        ),
      ),
    ),
    floatingActionButton: FloatingActionButton(
      onPressed: _startAnimation,
      child: Icon(Icons.play_arrow),
    ),
  );
}
回到顶部