Flutter多平台支持插件hycop_multi_platform的使用

Flutter多平台支持插件hycop_multi_platform的使用

特性

Hycop允许你使用以下由Firebase或AppWrite提供的6个服务:

  • 数据库
  • 实时
  • 无服务器函数
  • 账户
  • 存储
  • SocketIO

(SocketIO不是Firebase或AppWrite提供的服务,而是我们自己创建的服务。)

开始使用

示例程序将连接我们的演示服务器(Firebase或AppWrite)。你可以在example文件夹中运行演示程序,例如:

% cd example
% flutter run -d chrome

使用说明

请参考以下示例页面:

  1. 数据库
    • example/app/database_example_page.dart
  2. 实时
    • example/app/real_example_page.dart
  3. 存储
    • example/app/storage_example_page.dart
  4. 无服务器函数
    • example/app/function_example_page.dart
  5. 账户
    • example/app/login_page.dart
    • example/app/register_page.dart
    • example/app/reset_password_confirm_page.dart
    • example/app/user_example_page.dart
  6. SocketIO
    • example/app/socketio_example_page.dart
  7. 配置
    • example/assets/hycop_config_example.json

示例程序将连接我们的演示服务器(Firebase或AppWrite)。如果你想要拥有自己的Firebase或AppWrite服务器,请按照以下步骤操作。

使用Firebase服务器的情况

在Firebase控制台中:

  1. 创建项目
  2. 注册你的应用
  3. 创建Firestore数据库
  4. 创建实时数据库
  5. 创建存储
  6. 创建函数

在源代码中:

  1. 在你的资源文件夹下创建一个hycop_config.json文件,并填充相应的值。
  2. 你可以参考example/assets/hycop_config_example.json文件,并在你的pubspec.yaml文件中指定hycop_config.json

使用AppWrite服务器的情况

安装Docker

根据常见的方法安装Docker。Docker必须安装在至少有4GB内存的服务器上。只有具有域名的服务器才能使用HTTPS。

安装AppWrite

按照appwrite.io主页上的描述安装AppWrite。

AppWrite设置

如果你通过浏览器连接到AppWrite服务器的地址,可以访问AppWrite控制台。

  1. 第一次连接时,输入你的ID和密码。
  2. 创建项目
  3. 创建数据库

与Firebase不同,这里需要创建必要的集合。创建集合时,不要忘记给予适当的读写权限。 要使用实时服务,必须创建名为hycop_delta的集合。 要使用账户服务,必须创建名为hycop_user的集合。 hycop_userhycop_delta的模式在example/assets/collection_hycop_delta.jsonexample/assets/collection_hycop_user.json文件中。

  1. 创建API密钥
  2. 创建函数 如果需要,可以按照AppWrite.io页面上的说明创建无服务器函数。

创建hycop_config.json文件

你需要创建一个包含Firebase或AppWrite服务器信息的hycop_config.json文件,并将其放在你的资源文件夹中。 hycop_config.json文件可以通过参考example/assets/hycop_config_example.json文件来创建。当然,你还需要在你的pubspec.yaml文件中的assets部分添加hycop_config.json文件。

运行示例程序

cd example
flutter run -d chrome --web-renderer html

或者

flutter run -d chrome --web-renderer canvaskit

示例代码

以下是example/lib/main.dart文件中的完整示例代码:

import 'package:creta_studio_model/model/frame_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:logging/logging.dart';
import 'package:hycop_multi_platform/hycop.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Hycop Multi Platform',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Hycop Multi Platform'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String _hycopInitResult = 'not initialize';
  String _createAccountResult = '[empty]';
  final String _loginAccountResult = '[empty]';
  String _deleteAccountResult = '[empty]';
  String _setDataResult = '[empty]';
  String _getDataResult = '[empty]';
  String _deleteDataResult = '[empty]';
  String _uploadStorageResult = '[empty]';
  String _downloadStorageResult = '[empty]';
  Uint8List? _downloadUint8List;
  FrameModel realtimeModel = FrameModel('', '');
  bool _uploadToRealtime = false;
  bool _downloadFromRealtime = false;

  double _mouseX = 0;
  double _mouseY = 0;
  int _lastRealtime = DateTime.now().millisecondsSinceEpoch;

  String _getNowString() {
    return DateTime.now().toString();
  }

  Future<void> _hycopInit() async {
    setupLogger();
    Logger.root.level = Level.INFO;
    HycopFactory.serverType = ServerType.firebase;
    setState(() {
      _hycopInitResult = '[${_getNowString()}] initializing...';
    });
    await HycopFactory.initAll();
    setState(() {
      _hycopInitResult = '[${_getNowString()}] initialized';
    });
  }

  Future<void> _createAccount() async {
    Map<String, dynamic> userData = {
      'accountSignUpType': AccountSignUpType.hycop.index,
      'password': '-507263a',
      'email': 'HycopTestUser@nomail.com',
    };
    AccountManager.createAccount(userData).then((value) {
      // 成功
      setState(() {
        _createAccountResult = '[${_getNowString()}] ${userData['email']} account created';
      });
    }).catchError(
      (error, stackTrace) {
        // 错误
        setState(() {
          _createAccountResult = '[${_getNowString()}] fail to create account !!!\n${error.toString()}';
        });
      },
    );
  }

  Future<void> _loginAccount() async {
    String password = '-507263a';
    String email = 'HycopTestUser@nomail.com';
    AccountManager.login(email, password).then((value) {
      // 成功
      setState(() {
        _createAccountResult = '[${_getNowString()}] $email logined';
      });
    }).catchError(
      (error, stackTrace) {
        // 错误
        setState(() {
          _createAccountResult = '[${_getNowString()}] fail to login !!!\n${error.toString()}';
        });
      },
    );
  }

  Future<void> _deleteAccount() async {
    String email = AccountManager.currentLoginUser.email;
    HycopFactory.account!.deleteAccount(deleteNow: true).then((value) {
      // 成功
      setState(() {
        _deleteAccountResult = '[${_getNowString()}] $email account deleted';
      });
    }).catchError(
      (error, stackTrace) {
        // 错误
        setState(() {
          _deleteAccountResult = '[${_getNowString()}] fail to delete account !!!\n${error.toString()}';
        });
      },
    );
  }

  Future<void> _getDataFromDB() async {
    var userDataList = await HycopFactory.dataBase
        ?.queryData('hycop_users', where: {'email': 'test'}, orderBy: 'email');
    if (userDataList?.isEmpty ?? true) {
      setState(() {
        _getDataResult = '[${_getNowString()}] no data';
      });
      return;
    }
    setState(() {
      _getDataResult = '[${_getNowString()}] ';
      for (var userData in userDataList!) {
        _getDataResult += 'users=${userData['email']?.toString()},';
      }
    });
  }

  Future<void> _setDataToDB() async {
    Map<String, dynamic> userData = {'userId': 'test', 'email': 'test', 'accountSignUpType': 0};
    await HycopFactory.dataBase!.createData('hycop_users', 'user=test', userData).then((value) {
      // 成功
      setState(() {
        _setDataResult = '[${_getNowString()}] create successfully';
      });
    }).catchError(
      (error, stackTrace) {
        // 错误
        setState(() {
          _setDataResult = '[${_getNowString()}] fail to create !!!\n${error.toString()}';
        });
      },
    );
  }

  Future<void> _deleteDataFromDB() async {
    await HycopFactory.dataBase!.removeData('hycop_users', 'user=test').then((value) {
      // 成功
      setState(() {
        _deleteDataResult = '[${_getNowString()}] delete successfully';
      });
    }).catchError(
      (error, stackTrace) {
        // 错误
        setState(() {
          _deleteDataResult = '[${_getNowString()}] fail to delete !!!\n${error.toString()}';
        });
      },
    );
  }

  Future<void> _downloadDataFromStorage() async {
    HycopFactory.storage!.setBucket();
    String fileId = 'HycopTest/content/image/a6de38b7f004ea361acfbc9ed98a447eHycopTestImage.png';
    setState(() {
      _downloadUint8List = null;
      _downloadStorageResult = 'downloading...';
    });

    HycopFactory.storage!.getFileBytes(fileId).then((value) {
      // 成功
      setState(() {
        _downloadUint8List = value;
        if (_downloadUint8List == null) {
          _downloadStorageResult = '[${_getNowString()}] fail to download !!!';
        }
      });
    }).catchError(
      (error, stackTrace) {
        // 错误
        setState(() {
          _deleteDataResult = '[${_getNowString()}] fail to delete !!!\n${error.toString()}';
        });
      },
    );
  }

  Future<void> _uploadDataToStorage() async {
    setState(() {
      _uploadStorageResult = 'uploading...';
    });
    HycopFactory.storage!.setBucket();
    String fileName = 'HycopTestImage.png';
    String fileType = 'image';
    final ByteData bytes = await rootBundle.load('assets/logo.png');
    final Uint8List fileBytes = bytes.buffer.asUint8List();
    FileModel? fileModel = await HycopFactory.storage!.uploadFile(
      fileName,
      fileType,
      fileBytes,
      makeThumbnail: true,
      bucketId: 'HycopTest',
    );
    setState(() {
      _uploadStorageResult =
          (fileModel == null) ? '[${_getNowString()}] fail to upload !!!' : fileModel.id;
    });
  }

  void _updateLocation(PointerEvent details) {
    if (_uploadToRealtime) {
      int nowMilliseconds = DateTime.now().millisecondsSinceEpoch;
      setState(() {
        _mouseX = details.position.dx;
        _mouseY = details.position.dy;
      });
      if (nowMilliseconds - _lastRealtime > 1000) {
        _lastRealtime = nowMilliseconds;
        realtimeModel.posX.set(details.position.dx,
            save: false, noUndo: true, dontChangeBookTime: true, dontRealTime: true);
        realtimeModel.posY.set(details.position.dy,
            save: false, noUndo: true, dontChangeBookTime: true, dontRealTime: true);
        HycopFactory.realtime!
            .setDelta(directive: 'set', mid: realtimeModel.mid, delta: realtimeModel.toMap());
      }
    }
  }

  Future<void> _uploadDataToRealtime() async {
    if (_uploadToRealtime == false && _downloadFromRealtime == false) {
      _uploadToRealtime = true;
      FrameModel tmpModel = FrameModel('page=hycoptest', 'book=hycoptest');
      realtimeModel.copyFrom(tmpModel, newMid: 'frame=hycoptest', pMid: 'page=hycoptest');
      HycopFactory.realtime!.startTemp(realtimeModel.mid);
      //HycopFactory.realtime!.setPrefix('hycop'); // 不需要
    }
  }

  void realTimeCallback(
      String listenerId, String directive, String userId, Map<String, dynamic> dataMap) {
    // logger.finest('realTimeCallback invoker($directive, $userId)');
    if (directive == 'set') {
      String mid = dataMap["mid"] ?? '';
      if (mid.isEmpty) {
        return;
      }
      if (realtimeModel.mid == mid) {
        realtimeModel.fromMap(dataMap);
        setState(() {
          _mouseX = realtimeModel.posX.value;
          _mouseY = realtimeModel.posY.value;
        });
      }
    }
  }

  Future<void> _downloadDataFromRealtime() async {
    if (_uploadToRealtime == false && _downloadFromRealtime == false) {
      _downloadFromRealtime = true;
      FrameModel tmpModel = FrameModel('page=hycoptest', 'book=hycoptest');
      realtimeModel.copyFrom(tmpModel, newMid: 'frame=hycoptest', pMid: 'page=hycoptest');
      HycopFactory.realtime!.start();
      HycopFactory.realtime!.addListener("page=hycoptest", "hycop_frame", realTimeCallback);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: SingleChildScrollView(
        child: MouseRegion(
          onHover: _updateLocation,
          child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                // 初始化Hycop
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: TextButton(
                    style: ButtonStyle(backgroundColor: WidgetStateProperty.all(Colors.grey[300])),
                    onPressed: () async {
                      await _hycopInit();
                    },
                    child: const Padding(
                      padding: EdgeInsets.all(8.0),
                      child: Text('初始化Hycop'),
                    ),
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: Text(_hycopInitResult),
                ),

                // 创建账户
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: TextButton(
                    style: ButtonStyle(backgroundColor: WidgetStateProperty.all(Colors.grey[300])),
                    onPressed: () async {
                      await _createAccount();
                    },
                    child: const Padding(
                      padding: EdgeInsets.all(8.0),
                      child: Text('创建账户'),
                    ),
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: Text(_createAccountResult),
                ),

                // 登录账户
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: TextButton(
                    style: ButtonStyle(backgroundColor: WidgetStateProperty.all(Colors.grey[300])),
                    onPressed: () async {
                      await _loginAccount();
                    },
                    child: const Padding(
                      padding: EdgeInsets.all(8.0),
                      child: Text('登录账户'),
                    ),
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: Text(_loginAccountResult),
                ),

                // 删除账户
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: TextButton(
                    style: ButtonStyle(backgroundColor: WidgetStateProperty.all(Colors.grey[300])),
                    onPressed: () async {
                      await _deleteAccount();
                    },
                    child: const Padding(
                      padding: EdgeInsets.all(8.0),
                      child: Text('删除账户'),
                    ),
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: Text(_deleteAccountResult),
                ),

                // 从数据库获取数据
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: TextButton(
                    style: ButtonStyle(backgroundColor: WidgetStateProperty.all(Colors.grey[300])),
                    onPressed: () async {
                      await _getDataFromDB();
                    },
                    child: const Padding(
                      padding: EdgeInsets.all(8.0),
                      child: Text('从数据库获取数据'),
                    ),
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: Text(_getDataResult),
                ),
                // 设置数据库数据
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: TextButton(
                    style: ButtonStyle(backgroundColor: WidgetStateProperty.all(Colors.grey[300])),
                    onPressed: () async {
                      await _setDataToDB();
                    },
                    child: const Padding(
                      padding: EdgeInsets.all(8.0),
                      child: Text('设置数据库数据'),
                    ),
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: Text(_setDataResult),
                ),
                // 从数据库删除数据
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: TextButton(
                    style: ButtonStyle(backgroundColor: WidgetStateProperty.all(Colors.grey[300])),
                    onPressed: () async {
                      await _deleteDataFromDB();
                    },
                    child: const Padding(
                      padding: EdgeInsets.all(8.0),
                      child: Text('从数据库删除数据'),
                    ),
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: Text(_deleteDataResult),
                ),
                // 从存储下载数据
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: TextButton(
                    style: ButtonStyle(backgroundColor: WidgetStateProperty.all(Colors.grey[300])),
                    onPressed: () async {
                      await _downloadDataFromStorage();
                    },
                    child: const Padding(
                      padding: EdgeInsets.all(8.0),
                      child: Text('从存储下载数据'),
                    ),
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: _downloadUint8List == null
                      ? Text(_downloadStorageResult)
                      : Container(
                          color: Colors.black,
                          child: Image.memory(
                            _downloadUint8List!,
                            width: 200,
                          )),
                ),
                // 上传数据到存储
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: TextButton(
                    style: ButtonStyle(backgroundColor: WidgetStateProperty.all(Colors.grey[300])),
                    onPressed: () async {
                      await _uploadDataToStorage();
                    },
                    child: const Padding(
                      padding: EdgeInsets.all(8.0),
                      child: Text('上传数据到存储'),
                    ),
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: Text(_uploadStorageResult),
                ),
                // 上传数据到实时
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: TextButton(
                    style: ButtonStyle(backgroundColor: WidgetStateProperty.all(Colors.grey[300])),
                    onPressed: () async {
                      await _uploadDataToRealtime();
                    },
                    child: const Padding(
                      padding: EdgeInsets.all(8.0),
                      child: Text('上传数据到实时'),
                    ),
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: Text('X:$_mouseX, Y:$_mouseY'),
                ),
                Padding(
                  padding: const EdgeInsets.all(4.0),
                  child: TextButton(
                    style: ButtonStyle(backgroundColor: WidgetStateProperty.all(Colors.grey[300])),
                    onPressed: () async {
                      await _downloadDataFromRealtime();
                    },
                    child: const Padding(
                      padding: EdgeInsets.all(8.0),
                      child: Text('从实时下载数据'),
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

更多关于Flutter多平台支持插件hycop_multi_platform的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter多平台支持插件hycop_multi_platform的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


hycop_multi_platform 是一个 Flutter 插件,旨在简化跨平台开发,允许开发者使用一套代码在多个平台上运行应用。以下是如何使用 hycop_multi_platform 插件的基本步骤:

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 hycop_multi_platform 插件的依赖。

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

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

2. 导入插件

在需要使用插件的 Dart 文件中导入 hycop_multi_platform

import 'package:hycop_multi_platform/hycop_multi_platform.dart';

3. 使用插件功能

hycop_multi_platform 插件通常提供了一些跨平台的 API,你可以根据需求调用这些 API。

例如,假设插件提供了一个方法来获取当前平台的名称:

void getPlatformInfo() {
  String platform = HycopMultiPlatform.getPlatformName();
  print('Running on: $platform');
}

4. 平台特定代码

如果插件支持平台特定的代码,你可以在 androidiosweb 等目录下添加特定平台的实现。

例如,如果你需要在 Android 平台上进行一些特定的操作,你可以在 android/src/main/kotlin/com/example/your_app 目录下添加相应的 Kotlin 代码。

5. 运行和测试

在添加了插件代码后,你可以在不同的平台上运行和测试你的应用。

flutter run -d chrome  # 在 Web 上运行
flutter run -d android # 在 Android 上运行
flutter run -d ios     # 在 iOS 上运行

6. 调试和优化

根据不同的平台特性,你可能需要对代码进行一些调试和优化,以确保在所有平台上都能正常运行。

7. 发布

当你完成了开发和测试后,可以使用 Flutter 提供的命令来打包和发布你的应用。

flutter build apk   # 打包 Android APK
flutter build ios   # 打包 iOS
flutter build web   # 打包 Web 应用
回到顶部