Flutter连接管理插件vivanta_connect_flutter的使用

Flutter连接管理插件vivanta_connect_flutter的使用

插件用于在Flutter项目中集成Vivanta Connect。

如何实现Vivanta Connect流程

  1. 添加Vivanta Connect的导入:
import 'package:vivanta_connect_flutter/views/start_vivanta_connect.dart';
  1. 创建一个路由以打开Vivanta Connect。例如:
Navigator.of(context).push(
  MaterialPageRoute(
    builder: (context) => VivantaConnectFlutter(
      apiKey: apiKey,                   // 必填
      customerId: customerId,           // 必填
      externalUserId: externalUserId,   // 必填
      companyId: companyId,             // 可选
    ),
  ),
);

使用通过/users/auth端点获得的JWT Token:

Navigator.of(context).push(
  MaterialPageRoute(
    builder: (context) => VivantaConnectFlutter(
      token: token,                     // 必填
    ),
  ),
);
  1. Vivanta Connect将启动,并根据设备配置自动设置语言。

注意:Vivanta Connect需要有效的API密钥和关联的客户ID才能正常工作。

如何实现嵌入式图表

  1. 创建一个路由以打开嵌入式图表:
Navigator.of(context).push(
  MaterialPageRoute(
    builder: (context) => EmbeddedGraph(
      apiKey: apiKey,                    // 必填
      customerId: customerId,            // 必填
      externalUserId: externalUserId,    // 必填
      graphType: graphType,              // 必填(Sleep, Active Time, Activity)
      brandId: brandId,                  // 只有当用户连接了多个品牌时才需要,可选
    ),
  ),
);

仅适用于Apple Health集成(iOS)

  1. 在用户的会话开始时添加此代码以获取并同步Apple Health数据:
import 'package:vivanta_connect_flutter/helpers/vivanta_sync.dart';

final vivantaSyncData = VivantaSync(
  apiKey: apiKey,                   // 必填
  customerId: customerId,           // 必填
  externalUserId: externalUserId,   // 必填
);
vivantaSyncData.executeAll();       // 此过程从HealthKit获取数据并上传到Vivanta

使用通过/users/auth端点获得的JWT Token:

import 'package:vivanta_connect_flutter/helpers/vivanta_sync.dart';

final vivantaSyncData = VivantaSync(
  token: token,                       // 必填
);
vivantaSyncData.executeAll();       // 此过程从HealthKit获取数据并上传到Vivanta
  1. 在Info.plist文件中添加以下元素:
<key>NSHealthShareUsageDescription</key>
<string>[在此处添加读取Apple Health的目的]</string>
<key>NSHealthUpdateUsageDescription</key>
<string>[在此处添加写入Apple Health的目的]</string>

仅适用于Google Health Connect集成(Android)

  1. 在AndroidManifest.xml中,根节点内添加以下代码:
<queries>
  <package android:name="com.google.android.apps.healthdata" />
  <package android:name="com.google.android.apps.fitness" />
</queries>
  1. 在主<activity>节点内添加以下代码:
<intent-filter>
  <action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
</intent-filter>
  1. 在AndroidManifest.xml的根节点内至少添加一个权限,该权限是您的应用将要访问的。完整的权限列表在这个链接中:https://developer.android.com/health-and-fitness/guides/health-connect/plan/data-types#alpha10。例如:
<uses-permission android:name="android.permission.health.READ_STEPS"/>
  1. gradle.properties文件中替换这些值:
org.gradle.jvmargs=-Xmx1536M
android.enableJetifier=true
android.useAndroidX=true
  1. 在用户的会话开始时添加此代码以获取并同步Google Health Connect数据:
import 'package:vivanta_connect_flutter/helpers/vivanta_sync.dart';

final vivantaSyncData = VivantaSync(
  apiKey: apiKey,                   // 必填
  customerId: customerId,           // 必填
  externalUserId: externalUserId,   // 必填
);
vivantaSyncData.executeAll();       // 此过程从HealthKit获取数据并上传到Vivanta

使用通过/users/auth端点获得的JWT Token:

import 'package:vivanta_connect_flutter/helpers/vivanta_sync.dart';

final vivantaSyncData = VivantaSync(
  token: token,                      // 必填
);
vivantaSyncData.executeAll();       // 此过程从HealthKit获取数据并上传到Vivanta

完整示例代码

import 'dart:io';

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

import 'package:vivanta_connect_example/helpers.dart';
import 'package:vivanta_connect_example/vivanta.dart';
import 'package:vivanta_connect_example/vivanta_sync_task_handler.dart';
import 'package:vivanta_connect_example/write_to_health.dart';
import 'package:vivanta_connect_flutter/helpers/vivanta_sync.dart';
import 'package:vivanta_connect_flutter/services/preferences.dart';
import 'package:vivanta_connect_flutter/styles/colors.dart';
import 'package:vivanta_connect_flutter/styles/fonts.dart';
import 'package:vivanta_connect_flutter/views/embedded_graph.dart';

// 回调函数应该始终是一个顶级函数。
[@pragma](/user/pragma)('vm:entry-point')
void startCallback() {
  // 必须调用setTaskHandler函数来处理后台任务。
  FlutterForegroundTask.setTaskHandler(VivantaSyncTaskHandler());
}

void main() => runApp(const Example());

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

  // 这个小部件是你的应用程序的根。
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Vivanta Connect 示例',
      home: Home(),
    );
  }
}

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

  [@override](/user/override)
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  String companyId = 'Vivanta';
  final Preferences preferences = Preferences();
  late TextEditingController _usernameController;
  late TextEditingController _brandController;
  late FocusNode _usernameFocusNode;
  late FocusNode _brandFocusNode;

  [@override](/user/override)
  void initState() {
    _usernameController = TextEditingController();
    _usernameFocusNode = FocusNode();
    _brandController = TextEditingController();
    _brandFocusNode = FocusNode();

    _usernameFocusNode.addListener(() {
      if (!_usernameFocusNode.hasFocus) {
        preferences.setExternalUserId(_usernameController.text);
      }
    });

    _brandFocusNode.addListener(() {
      if (!_brandFocusNode.hasFocus) {
        preferences.setBrandId(_brandController.text);
      }
    });
    initAsync();
    super.initState();

    _requestPermissionForAndroid();
  }

  Future<void> _requestPermissionForAndroid() async {
    if (!Platform.isAndroid) {
      return;
    }

    // "android.permission.SYSTEM_ALERT_WINDOW" 权限必须被授予,以便onNotificationPressed函数被调用。
    //
    // 当通知被按下且权限被拒绝时,onNotificationPressed函数不会被调用,应用程序会被打开。
    //
    // 如果你不使用onNotificationPressed或launchApp函数,你不需要写这段代码。
    if (!await FlutterForegroundTask.canDrawOverlays) {
      // 这个函数需要`android.permission.SYSTEM_ALERT_WINDOW`权限。
      await FlutterForegroundTask.openSystemAlertWindowSettings();
    }

    // Android 12及以上版本,对启动前台服务有一些限制。
    //
    // 为了在设备重启或意外问题时重新启动服务,你需要允许以下权限。
    if (!await FlutterForegroundTask.isIgnoringBatteryOptimizations) {
      // 这个函数需要`android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS`权限。
      await FlutterForegroundTask.requestIgnoreBatteryOptimization();
    }

    // Android 13及以上版本,你需要允许通知权限来暴露前台服务通知。
    final NotificationPermission notificationPermissionStatus =
        await FlutterForegroundTask.checkNotificationPermission();
    if (notificationPermissionStatus != NotificationPermission.granted) {
      await FlutterForegroundTask.requestNotificationPermission();
    }
  }

  void _onData(dynamic data) {
    if (data is int) {
    } else if (data is String) {
      if (data == 'onNotificationPressed') {
        Navigator.of(context).pushReplacementNamed('/');
      }
    }
  }

  [@override](/user/override)
  void dispose() {
    _usernameController.dispose();
    _usernameFocusNode.dispose();
    _brandController.dispose();
    _brandFocusNode.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return WillStartForegroundTask(
      onWillStart: () async {
        // 返回是否启动前台服务。
        return true;
      },
      androidNotificationOptions: AndroidNotificationOptions(
        channelId: 'vivanta_connect_demo_foreground_service',
        channelName: 'Vivanta Connect 示例前台服务',
        channelDescription:
            '此通知用于保持Vivanta Connect运行。',
        channelImportance: NotificationChannelImportance.DEFAULT,
        priority: NotificationPriority.MIN,
        isSticky: true, // 重要
        iconData: const NotificationIconData(
          resType: ResourceType.mipmap,
          resPrefix: ResourcePrefix.ic,
          name: 'launcher',
        ),
        buttons: [],
      ),
      iosNotificationOptions: const IOSNotificationOptions(
        showNotification: false,
        playSound: false,
      ),
      foregroundTaskOptions: const ForegroundTaskOptions(
        interval: 300000,
        isOnceEvent: false,
        allowWakeLock: false,
        allowWifiLock: false,
      ),
      notificationTitle: 'Vivanta Connect 正在运行',
      notificationText:
          '请不要关闭此通知以保持Vivanta Connect运行。',
      callback: startCallback,
      onData: _onData,
      child: Scaffold(
        appBar: AppBar(
          title: Text('Vivanta Connect 示例'),
        ),
        body: Container(
          width: MediaQuery.of(context).size.width,
          child: ListView(
            children: [
              _box(),
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 16),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.start,
                  children: [
                    Align(
                      alignment: Alignment.centerLeft,
                      child: Text(
                        '外部用户ID:',
                        textAlign: TextAlign.start,
                      ),
                    ),
                    TextField(
                      keyboardType: TextInputType.text,
                      controller: _usernameController,
                      focusNode: _usernameFocusNode,
                      onEditingComplete: () {
                        FocusScope.of(context).unfocus();
                      },
                    ),
                  ],
                ),
              ),
              _box(),
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 16),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.start,
                  children: [
                    Align(
                      alignment: Alignment.centerLeft,
                      child: Text(
                        '品牌ID:',
                        textAlign: TextAlign.start,
                      ),
                    ),
                    TextField(
                      keyboardType: TextInputType.text,
                      controller: _brandController,
                      focusNode: _brandFocusNode,
                      onEditingComplete: () {
                        FocusScope.of(context).unfocus();
                      },
                    ),
                  ],
                ),
              ),
              _box(),
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 16),
                child: Column(
                  children: [
                    TextButton(
                      onPressed: _usernameController.text.isEmpty
                          ? null
                          : _launchVivantaConnect,
                      style: _buttonStyle(),
                      child: Text(
                        '连接你的设备到Vivanta',
                        textAlign: TextAlign.center,
                        style: VivantaFonts.button,
                      ),
                    ),
                    if (Platform.isIOS) ...[
                      _box(),
                      TextButton(
                        onPressed: _usernameController.text.isEmpty
                            ? null
                            : _launchVivantaSync,
                        style: _buttonStyle(),
                        child: Text(
                          '同步Apple Health数据',
                          textAlign: TextAlign.center,
                          style: VivantaFonts.button,
                        ),
                      ),
                      _box(),
                      TextButton(
                        onPressed: () => writeAppleHealthParameter(context),
                        style: _buttonStyle(),
                        child: Text(
                          '写入Apple Health参数',
                          textAlign: TextAlign.center,
                          style: VivantaFonts.button,
                        ),
                      ),
                      _box(),
                      TextButton(
                        onPressed: () => writeAppleHealthActivity(context),
                        style: _buttonStyle(),
                        child: Text(
                          '写入Apple Health活动',
                          textAlign: TextAlign.center,
                          style: VivantaFonts.button,
                        ),
                      ),
                    ],
                    if (Platform.isAndroid) ...[
                      _box(),
                      TextButton(
                        onPressed: _usernameController.text.isEmpty
                            ? null
                            : _launchVivantaSync,
                        style: _buttonStyle(),
                        child: Text(
                          '同步Google Health Connect数据',
                          textAlign: TextAlign.center,
                          style: VivantaFonts.button,
                        ),
                      ),
                      _box(),
                      TextButton(
                        onPressed: () => writeGoogleHealthParameter(context),
                        style: _buttonStyle(),
                        child: Text(
                          '写入Google Health Connect参数',
                          textAlign: TextAlign.center,
                          style: VivantaFonts.button,
                        ),
                      ),
                      _box(),
                      TextButton(
                        onPressed: () => writeGoogleHealthActivity(context),
                        style: _buttonStyle(),
                        child: Text(
                          '写入Google Health Connect活动',
                          textAlign: TextAlign.center,
                          style: VivantaFonts.button,
                        ),
                      ),
                    ],
                    _box(),
                    _getButtonToGraph(
                      '打开活动图',
                      GraphType.activity,
                    ),
                    _box(),
                    _getButtonToGraph(
                      '打开活跃时间图',
                      GraphType.activeTime,
                    ),
                    _box(),
                    _getButtonToGraph(
                      '打开睡眠图',
                      GraphType.sleep,
                    ),
                    _box(),
                    _getButtonToGraph(
                      '打开卡路里图',
                      GraphType.calories,
                    ),
                    _box(),
                    _getButtonToGraph(
                      '打开平均心率图',
                      GraphType.avgHeartRate,
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Future<void> initAsync() async {
    final externalUserId = await preferences.getExternalUserId();
    final brandId = await preferences.getBrandId();
    if (externalUserId.isNotEmpty) {
      _usernameController.text = externalUserId;
      setState(() {});
    }
    if (brandId.isNotEmpty) {
      _brandController.text = brandId;
      setState(() {});
    }
  }

  ButtonStyle _buttonStyle({
    Color backgroundColor = VivantaColors.primary,
  }) =>
      ButtonStyle(
        backgroundColor: MaterialStateProperty.all<Color>(backgroundColor),
        fixedSize: MaterialStateProperty.all<Size>(
          Size(MediaQuery.of(context).size.width - 32, 32),
        ),
      );

  Widget _box() => SizedBox(height: 24);

  void _launchVivantaConnect() {
    showModalBottomSheet(
      context: context,
      builder: (context) {
        return Container(
          height: 100,
          child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                const Text('你想使用哪种类型的启动?'),
                SizedBox(
                  height: 8,
                ),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceAround,
                  children: [
                    ElevatedButton(
                      child: const Text('使用API密钥'),
                      onPressed: () async {
                        await launchVivantaConnectWithKeys(
                          context,
                          _usernameController.text,
                          companyId,
                        );
                        Navigator.pop(context);
                      },
                    ),
                    ElevatedButton(
                      child: const Text('使用令牌'),
                      onPressed: () async {
                        final token =
                            await getToken(_usernameController.text, companyId);
                        await launchVivantaConnectWithToken(context, token);
                        Navigator.pop(context);
                      },
                    ),
                  ],
                ),
                SizedBox(
                  height: 8,
                ),
              ],
            ),
          ),
        );
      },
    );
  }

  void _launchVivantaSync() {
    showModalBottomSheet(
      context: context,
      builder: (context) {
        return Container(
          height: 100,
          child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                const Text('你想使用哪种类型的同步过程?'),
                SizedBox(
                  height: 8,
                ),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceAround,
                  children: [
                    ElevatedButton(
                      child: const Text('使用API密钥'),
                      onPressed: () async {
                        await _sync(showSuccess: true);
                        Navigator.pop(context);
                      },
                    ),
                    ElevatedButton(
                      child: const Text('使用令牌'),
                      onPressed: () async {
                        final token =
                            await getToken(_usernameController.text, companyId);
                        await _syncWithToken(token: token, showSuccess: true);
                        Navigator.pop(context);
                      },
                    ),
                  ],
                ),
                SizedBox(
                  height: 8,
                ),
              ],
            ),
          ),
        );
      },
    );
  }

  Widget _getButtonToGraph(String text, GraphType graphType) {
    final brandId = int.tryParse(_brandController.text);
    return TextButton(
      onPressed: _usernameController.text.isEmpty
          ? null
          : () {
              Navigator.of(context).push(
                MaterialPageRoute(
                  builder: (context) => EmbeddedGraph(
                    apiKey: Vivanta.apiKey,
                    customerId: Vivanta.customerId,
                    externalUserId: _usernameController.text,
                    graphType: graphType,
                    brandId: brandId,
                  ),
                ),
              );
            },
      style: _buttonStyle(
        backgroundColor: VivantaColors.accent,
      ),
      child: Text(
        text,
        textAlign: TextAlign.center,
        style: VivantaFonts.button,
      ),
    );
  }

  Future<void> _sync({
    bool showSuccess = true,
  }) async {
    final sync = VivantaSync(
      apiKey: Vivanta.apiKey,
      customerId: Vivanta.customerId,
      externalUserId: _usernameController.text,
    );
    sync.executeAll().then((value) {
      if (showSuccess) {
        showSyncSuccess(context);
      }
    }).catchError(
      (onError) {},
    );
  }

  Future<void> _syncWithToken({
    bool showSuccess = true,
    String token = '',
  }) async {
    final sync = VivantaSync(
      token: token,
    );
    sync.executeAll().then((value) {
      if (showSuccess) {
        showSyncSuccess(context);
      }
    }).catchError(
      (onError) {},
    );
  }
}

更多关于Flutter连接管理插件vivanta_connect_flutter的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter连接管理插件vivanta_connect_flutter的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何使用vivanta_connect_flutter插件的示例代码,这个插件通常用于管理Flutter应用中的网络连接。请注意,由于这是一个假设的插件名称(因为在实际生态系统中可能不存在名为vivanta_connect_flutter的官方插件),以下代码示例将基于一个典型的网络连接管理插件的功能来构建。

首先,确保在pubspec.yaml文件中添加了对该插件的依赖(假设该插件真实存在):

dependencies:
  flutter:
    sdk: flutter
  vivanta_connect_flutter: ^x.y.z  # 替换为实际版本号

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

接下来,在你的Flutter应用中,你可以这样使用vivanta_connect_flutter插件来管理网络连接:

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

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

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

class ConnectivityScreen extends StatefulWidget {
  @override
  _ConnectivityScreenState createState() => _ConnectivityScreenState();
}

class _ConnectivityScreenState extends State<ConnectivityScreen> {
  VivantaConnect _connectivity = VivantaConnect();
  bool _isConnected = false;

  @override
  void initState() {
    super.initState();
    initConnectivity();
    _connectivity.onConnectivityChanged.listen((VivantaConnectivityResult result) {
      if (result == VivantaConnectivityResult.connected) {
        setState(() {
          _isConnected = true;
        });
      } else {
        setState(() {
          _isConnected = false;
        });
      }
    });
  }

  Future<void> initConnectivity() async {
    VivantaConnectivityResult result = await _connectivity.checkConnectivity();
    if (result == VivantaConnectivityResult.connected) {
      setState(() {
        _isConnected = true;
      });
    } else {
      setState(() {
        _isConnected = false;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Connectivity Status'),
      ),
      body: Center(
        child: Text(
          _isConnected ? 'Connected to the internet' : 'Not connected to the internet',
          style: TextStyle(fontSize: 24),
        ),
      ),
    );
  }
}

// 假设的VivantaConnect类和VivantaConnectivityResult枚举,用于模拟插件功能
enum VivantaConnectivityResult { connected, disconnected }

class VivantaConnect {
  StreamController<VivantaConnectivityResult> _controller = StreamController<VivantaConnectivityResult>();

  Stream<VivantaConnectivityResult> get onConnectivityChanged => _controller.stream;

  Future<VivantaConnectivityResult> checkConnectivity() async {
    // 模拟一个网络检查
    await Future.delayed(Duration(seconds: 2));
    return VivantaConnectivityResult.connected; // 在这里可以返回连接状态
  }

  // 模拟连接状态改变的方法(在实际插件中可能由原生代码触发)
  void simulateConnectivityChange(VivantaConnectivityResult result) {
    _controller.add(result);
  }
}

请注意,上述代码中的VivantaConnect类和VivantaConnectivityResult枚举是假设的,用于模拟vivanta_connect_flutter插件的功能。在实际使用中,你将根据插件提供的API来编写代码。

此外,由于vivanta_connect_flutter可能是一个虚构的插件名称,你需要查找实际的插件文档来了解其具体的API和使用方法。通常,插件的README文件会包含足够的示例代码和说明来帮助你开始使用。

回到顶部