Flutter角度计算插件flutter_angle的使用

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

Flutter角度计算插件flutter_angle的使用

简介

flutter_angle 是一个基于 Dart 的图形库,允许用户查看更复杂的渲染项目,如3D对象和更复杂的着色器。它是一个将 flutter_web_gl 转换为 Dart 的项目,最初由 @escamoteur@kentcb 创建。

Gif of angle working.

特性

  • 支持3D模型和复杂着色器的渲染
  • 基于 ANGLE(ANGLE 是 Google 开发的一个跨平台的 OpenGL ES 模拟器)
  • 支持多种平台:MacOS、iOS、Android、Windows 和 Web

系统要求

MacOS

  • 最低操作系统版本:10.14
  • Xcode 13 或更新版本
  • Swift 5
  • 支持 Metal

iOS

  • 最低操作系统版本:12.0
  • Xcode 13 或更新版本
  • Swift 5
  • 支持 Metal

Android

  • compileSdkVersion: 34
  • 支持 OpenGL

Windows

  • 支持 Intel 和 AMD
  • 支持 Direct3D 11 和 OpenGL

Web

  • 支持 WebGL2

Linux

  • 不支持

入门指南

要开始使用 flutter_angle,请在 pubspec.yaml 文件中添加以下依赖项:

dependencies:
  flutter_angle: ^最新版本号

然后运行 flutter pub get 来安装插件。

使用示例

以下是一个完整的示例代码,展示了如何使用 flutter_angle 插件来渲染3D模型和复杂着色器。

import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:flutter_angle/flutter_angle.dart';
import 'learn_gl.dart'; // 假设这是一个自定义的着色器学习类

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

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

class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
  final textures = <FlutterAngleTexture>[];
  int textureId = -1;
  int textureId2 = -1;
  Lesson? lesson;
  Lesson? lesson2;
  static const textureWidth = 640;
  static const textureHeight = 320;
  static const aspect = textureWidth / textureHeight;

  double dpr = 1.0;
  late double width;
  late double height;
  Size? screenSize;

  [@override](/user/override)
  void initState() {
    super.initState();
    initPlatformState(); // 初始化平台状态
  }

  // 平台消息是异步的,因此我们在异步方法中初始化
  Future<void> initPlatformState() async {
    didInit = true;
    final mq = MediaQuery.of(context);
    screenSize = mq.size;
    dpr = mq.devicePixelRatio;

    width = screenSize!.width;
    height = width;

    await FlutterAngle.initOpenGL(true); // 初始化 OpenGL

    final options = AngleOptions(
      width: textureWidth, 
      height: textureHeight, 
      dpr: dpr,
    );

    try {
      textures.add(await FlutterAngle.createTexture(options)); // 创建纹理
      textures.add(await FlutterAngle.createTexture(options));
    } on PlatformException catch (e) {
      print("failed to get texture id $e");
      return;
    }

    lesson = Lesson3(textures[0].getContext()); // 初始化第一个 Lesson
    lesson2 = Lesson5(textures[1].getContext()); // 初始化第二个 Lesson

    if (!mounted) return;
    setState(() {
      textureId = textures[0].textureId; // 设置纹理 ID
      textureId2 = textures[1].textureId;
    });
    ticker = createTicker(updateTexture); // 创建并启动定时器
    ticker.start();
  }

  Stopwatch stopwatch = Stopwatch();

  late Ticker ticker;
  static bool updating = false;
  int animationCounter = 0;
  int totalTime = 0;
  int iterationCount = 60;
  int framesOver = 0;
  bool didInit = false;

  void updateTexture(_) async {
    if (textureId < 0) return;
    if (!updating) {
      updating = true;
      stopwatch.reset();
      stopwatch.start();
      textures[0].activate(); // 激活第一个纹理
      lesson?.handleKeys(); // 处理键盘事件
      lesson?.animate(animationCounter += 2); // 动画更新
      lesson?.drawScene(-1, 0, aspect); // 绘制场景
      await textures[0].signalNewFrameAvailable(); // 通知新帧可用
      stopwatch.stop();
      totalTime += stopwatch.elapsedMilliseconds;
      if (stopwatch.elapsedMilliseconds > 16) {
        framesOver++;
      }
      if (--iterationCount == 0) {
        totalTime = 0;
        iterationCount = 60;
        framesOver = 0;
      }
      textures[1].activate(); // 激活第二个纹理
      lesson2?.handleKeys(); // 处理键盘事件
      lesson2?.animate(animationCounter += 2); // 动画更新
      lesson2?.drawScene(-1, 0, aspect); // 绘制场景
      await textures[1].signalNewFrameAvailable(); // 通知新帧可用
      updating = false;
    } else {
      print('Too slow');
    }
  }

  void dispose() {
    ticker.dispose(); // 释放定时器
    lesson?.dispose(); // 释放 Lesson 资源
    lesson2?.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.red,
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: LayoutBuilder(builder: (context, constraints) {
          final useRow = constraints.maxWidth > constraints.maxHeight;
          if (!didInit) {
            initPlatformState(); // 确保平台状态已初始化
          }
          return GestureDetector(
            onVerticalDragStart: verticalDragStart, // 垂直拖动手势
            onVerticalDragUpdate: verticalDragUpdate,
            onHorizontalDragStart: horizontalDragStart, // 水平拖动手势
            onHorizontalDragUpdate: horizontalDragUpdate,
            child: Container(
              child: kIsWeb
                  ? useRow
                      ? Row(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            Expanded(
                              child: Transform.scale(
                                scaleY: -1,
                                child: HtmlElementView(viewType: textureId.toString()),
                              ),
                            ),
                            Expanded(
                              child: Transform.scale(
                                scaleY: -1,
                                child: HtmlElementView(viewType: textureId2.toString()),
                              ),
                            ),
                          ],
                        )
                      : Column(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            Expanded(
                              child: Transform.scale(
                                scaleY: -1,
                                child: HtmlElementView(viewType: textureId.toString()),
                              ),
                            ),
                            Expanded(
                              child: Transform.scale(
                                scaleY: -1,
                                child: HtmlElementView(viewType: textureId2.toString()),
                              ),
                            ),
                          ],
                        )
                  : useRow
                      ? Row(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            Expanded(child: Texture(textureId: textureId)),
                            Expanded(child: Texture(textureId: textureId2)),
                          ],
                        )
                      : Column(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            Expanded(child: Texture(textureId: textureId)),
                            Expanded(child: Texture(textureId: textureId2)),
                          ],
                        ),
            ),
          );
        }),
      ),
    );
  }

  double lastVertical = 0;
  double lastHorizontal = 0;

  void verticalDragStart(DragStartDetails details) {}

  void verticalDragUpdate(DragUpdateDetails details) {
    if (details.delta.dy < 0) {
      movement = Directions.up;
      print('up');
    } else if (details.delta.dy > 0) {
      print('down');
      movement = Directions.down;
    }
  }

  void horizontalDragStart(DragStartDetails details) {}

  void horizontalDragUpdate(DragUpdateDetails details) {
    if (details.delta.dx < 0) {
      movement = Directions.right;
      print('right');
    } else if (details.delta.dx > 0) {
      movement = Directions.left;
      print('left');
    }
  }
}

更多关于Flutter角度计算插件flutter_angle的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter角度计算插件flutter_angle的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,flutter_angle 是一个用于角度计算的插件,虽然这个插件在Flutter社区中可能不是非常常见,但假设它提供了一些基本的功能来计算和处理角度,我们可以根据假设来展示如何使用它。由于我无法直接访问具体的 flutter_angle 插件的实现细节,我将提供一个假设的代码案例来展示如何在Flutter项目中使用一个类似的角度计算插件。

首先,你需要确保在 pubspec.yaml 文件中添加了对 flutter_angle(或类似的插件)的依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_angle: ^x.y.z  # 假设的版本号

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

接下来,在你的 Dart 文件中,你可以这样使用假设的 flutter_angle 插件:

import 'package:flutter/material.dart';
import 'package:flutter_angle/flutter_angle.dart'; // 假设的导入路径

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Angle Calculation'),
        ),
        body: Center(
          child: AngleCalculationExample(),
        ),
      ),
    );
  }
}

class AngleCalculationExample extends StatefulWidget {
  @override
  _AngleCalculationExampleState createState() => _AngleCalculationExampleState();
}

class _AngleCalculationExampleState extends State<AngleCalculationExample> {
  double _angle1 = 30.0;
  double _angle2 = 45.0;
  double _sumOfAngles = 0.0;
  double _differenceOfAngles = 0.0;

  void _calculateAngles() {
    // 假设 AngleUtils 是 flutter_angle 插件提供的一个工具类
    // 这里我们假设它有两个静态方法 addAngles 和 subtractAngles
    setState(() {
      _sumOfAngles = AngleUtils.addAngles(_angle1, _angle2);
      _differenceOfAngles = AngleUtils.subtractAngles(_angle1, _angle2);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        TextField(
          decoration: InputDecoration(labelText: 'Angle 1 (degrees)'),
          keyboardType: TextInputType.number,
          inputFormatters: <TextInputFormatter>[
            FilteringTextInputFormatter.digitsOnly,
          ],
          onChanged: (value) {
            _angle1 = double.tryParse(value) ?? 0.0;
            _calculateAngles();
          },
        ),
        SizedBox(height: 16.0),
        TextField(
          decoration: InputDecoration(labelText: 'Angle 2 (degrees)'),
          keyboardType: TextInputType.number,
          inputFormatters: <TextInputFormatter>[
            FilteringTextInputFormatter.digitsOnly,
          ],
          onChanged: (value) {
            _angle2 = double.tryParse(value) ?? 0.0;
            _calculateAngles();
          },
        ),
        SizedBox(height: 32.0),
        Text('Sum of Angles: $_sumOfAngles°'),
        SizedBox(height: 16.0),
        Text('Difference of Angles: $_differenceOfAngles°'),
      ],
    );
  }
}

// 假设的 AngleUtils 类,实际中你应该从 flutter_angle 插件中导入
// class AngleUtils {
//   static double addAngles(double a, double b) => a + b;
//   static double subtractAngles(double a, double b) => a - b;
// }

注意:上面的代码中的 AngleUtils 类是假设存在的,实际上你需要根据 flutter_angle 插件提供的API文档来替换这部分内容。如果 flutter_angle 插件提供了不同的方法或类来进行角度计算,你需要相应地调整代码。

由于我无法确认 flutter_angle 插件的具体实现,上面的代码只是一个基于假设的示例。在实际使用中,请查阅该插件的官方文档以获取正确的使用方法和API。

回到顶部