Flutter云开发插件cloudbase_ce的使用

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

Flutter云开发插件cloudbase_ce的使用

介绍

cloudbase_ce 是腾讯云 CloudBase 的社区版 Flutter SDK。它完全兼容官方 SDK,并且支持 Dart 的空安全特性。

官方文档

特性对比

功能 官方 SDK cloudbase_ce
完全兼容官方 SDK 🚫
支持空安全
修复已知 Bug
支持手机认证 🔜
Dart3 兼容性
删除过时的 API 调用
使用最新依赖版本
GitHub Actions CI/CD

快速开始

1. 添加依赖

pubspec.yaml 文件中添加 cloudbase_ce 依赖:

dependencies:
  flutter:
    sdk: flutter
  cloudbase_ce: ^2.x.x

2. 导入包

在 Dart 文件中导入 cloudbase_ce 包:

import 'package:cloudbase_ce/cloudbase_ce.dart';

3. 初始化 CloudBase

void main() async {
  CloudBaseCore core = CloudBaseCore.init({
    'env': 'your-env-id', // 替换为您的环境 ID
    'appAccess': {
      'key': 'your-app-access-key', // 替换为您的应用访问密钥
      'version': 'your-app-access-version' // 替换为您的应用访问版本
    },
    'timeout': 3000 // 可选,请求超时时间(毫秒)
  });
}

已迁移的包

包名称 版本 状态
cloudbase_core Pub version
cloudbase_auth Pub version
cloudbase_database Pub version
cloudbase_function Pub version
cloudbase_storage Pub version

CI/CD

  • ✅ 自动构建与测试
  • ✅ 自动升级依赖(pub, github-actions)
  • ✅ 自动质量控制
  • ❌ 自动发布到 pub.dev

贡献者

GitHub contributors

示例代码

以下是一个完整的示例代码,展示了如何使用 cloudbase_ce 插件进行基本的操作。

import 'dart:developer' as dev;

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

import 'package:flutter/services.dart';
import 'case/interface_collection_p0.dart' as collection_p0;
import 'case/interface_command_p0.dart' as command_p0;
import 'case/intergace_geo_p0.dart' as geo_p0;

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MyApp());
}

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

class _MyAppState extends State<MyApp> {
  String _platformTest = 'Unknown';

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

  // 平台消息异步初始化
  Future<void> initPlatformState() async {
    String platformTest;
    try {
      platformTest = await TestMethodChannel.platformTest ?? 'Unknown platform test';
    } on PlatformException {
      platformTest = 'Failed to get platform test.';
    } on MissingPluginException {
      platformTest = 'iOS platform test.';
    }

    if (!mounted) return;

    setState(() {
      _platformTest = platformTest;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('插件示例应用'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              TextButton(
                onPressed: () async {
                  final CloudBaseCore core = await loginCloud();
                  await goTest(core);
                },
                child: Text('运行测试'),
              ),
              Text(
                _platformTest,
                style: Theme.of(context).textTheme.headlineSmall,
              )
            ],
          ),
        ),
      ),
    );
  }

  Future<CloudBaseCore> loginCloud() async {
    CloudBaseCore core = CloudBaseCore.init({
      'env': 'your-tencent-cloud-env-id', // 在腾讯云控制台获取环境ID
      'appAccess': {
        'key': 'your-tencent-cloud-app-access-key', // 在腾讯云控制台获取应用访问密钥
        'version': '1' // 应用访问版本
      }
    });

    CloudBaseAuth auth = CloudBaseAuth(core);
    CloudBaseAuthState? authState = await auth.getAuthState();
    if (authState == null) {
      authState = await auth.signInAnonymously();
    }
    return core;
  }

  Future<void> goTest(CloudBaseCore core) async {
    logIndex = 0;
    log("开始测试");

    CloudBaseDatabase db = CloudBaseDatabase(core);

    // collection p0用例
    await test(collection_p0.casesData['name']!, () async {
      List cases = collection_p0.casesData['cases'] as List;
      for (var i = 0; i < cases.length; i++) {
        await runCases(db, cases[i], collection_p0.casesData['name']!);
      }
    });

    // command p0用例
    await test(command_p0.casesData['name']!, () async {
      List cases = command_p0.casesData['cases'] as List;
      for (var i = 0; i < cases.length; i++) {
        await runCases(db, cases[i], command_p0.casesData['name']!);
      }
    });

    // geo command p0用例
    await test(geo_p0.casesData['name']!, () async {
      List cases = geo_p0.casesData['cases'] as List;
      for (var i = 0; i < cases.length; i++) {
        await runCases(db, cases[i], geo_p0.casesData['name']!);
      }
    });

    // server date p0用例
    await test('server_data p0用例', () async {
      var collection = db.collection('doc_wcc');
      var res = await collection
          .add({'description': 'eat an apple', 'createTime': db.serverDate()});
      testLog(res.code == null, 'server_data p0用例', res);
      var res2 = await collection.doc(res.id).get();
      testLog(res2.code == null, 'server_data p0用例', res2);
      testLog(res2.data[0]['createTime'] is DateTime, 'server_data p0用例', res2);
    });

    // 实时推送 p0用例
    await test(collection_p0.casesData['name']!, () async {
      var collection = db.collection('tcb_hello_world');
      var data = {
        '_id': "f3db088f5e84cd1300409145374590bc", // 指定ID
        'age': 18,
        'name': 'hhxxn',
      };
      var res = await collection.add(data);
      testLog(res.id == data['_id'], "添加一条数据 p0用例 - add测试", res);

      // 单doc测试
      RealtimeListener rl1 = collection
          .doc('f3db088f5e84cd1300409145374590ba')
          .watch(onChange: (Snapshot snapshot) {
        testLog(
            snapshot.docs.length == 1, "实时推送 p0用例 - 单doc测试 watch", snapshot);
      }, onError: (error) {
        testLog(error == null, "实时推送 p0用例 - 单doc测试 onError", error);
      });

      // query测试
      RealtimeListener rl2 =
          collection.where({'age': 18}).watch(onChange: (Snapshot snapshot) {
        testLog(
            snapshot.docs.length > 0, "实时推送 p0用例 - query测试 watch", snapshot);
      }, onError: (error) {
        testLog(error == null, "实时推送 p0用例 - query测试 onError", error);
      });

      // query + command 测试
      RealtimeListener rl3 = collection.where({'age': db.command.gt(15)}).watch(
          onChange: (Snapshot snapshot) {
        testLog(snapshot.docs.length > 0, "实时推送 p0用例 - query测试 + command watch",
            snapshot);
      }, onError: (error) {
        testLog(error == null, "实时推送 p0用例 - query + command 测试 onError", error);
      });

      // 等待10s, 让watch处理完
      await Future.delayed(Duration(seconds: 10), () {
        rl1.close();
        rl2.close();
        rl3.close();
      });
    });
  }

  int logIndex = 0;
  void log(String val) {
    dev.log("($logIndex) message: $val");
    logIndex++;
  }

  Future<void> test(dynamic teatName, Function invoke) async {
    dev.log("($logIndex) 测试: $teatName: ");
    await invoke();
  }

  Future<dynamic> collectionOp(CloudBaseDatabase db, String cmd, dynamic event) async {
    Query collection = db.collection(event['collection_name']);

    switch (cmd) {
      case 'collection_doc':
        return (collection as Collection).doc(event['record_id']).get();

      case 'collection_get':
        if (event['skip'] != null) {
          collection = collection.skip(event['skip']);
        }
        if (event['filter'] != null) {
          collection = collection.where(event['filter']);
        }
        if (event['limit'] != null) {
          collection = collection.limit(event['limit']);
        }
        if (event['order_key'] != null && event['order_type'] != null) {
          collection =
              collection.orderBy(event['order_key'], event['order_type']);
        }
        if (event['field'] != null) {
          collection = collection.field(event['field']);
        }
        return collection.get();

      case 'collection_search':
        return collection
            .where(event['filter'])
            .skip(event['skip'])
            .limit(event['limit'])
            .orderBy(event['order_key'], event['order_type'])
            .field(event['field'])
            .get();

      case 'collection_add':
        return (collection as Collection).add(event['data']);

      case 'collection_update':
        return collection.where(event['filter']).update(event['data']);

      case 'collection_remove':
        return collection.where(event['filter']).remove();

      case 'collection_count':
        return collection.where({'_id': db.command.neq('undefined')}).count();

      case 'collection_where':
        return collection.where(event['filter']).get();

      case 'collection_orderBy':
        return collection
            .orderBy(event['order_key'], event['order_type'])
            .get();

      case 'collection_limit':
        return collection.limit(event['limit']).get();

      case 'collection_skip':
        return collection.where(event['filter']).skip(event['skip']).get();

      case 'collection_field':
        return collection.field(event['field']).get();

      case 'collection_clean':
        DbQueryResponse res = await collection.get();
        List coll = res.data ?? [];
        return Future.wait(coll.map((doc) {
          return (collection as Collection).doc(doc['_id']).remove();
        }));

      default:
        return null;
    }
  }

  Future<dynamic> docOp(CloudBaseDatabase db, String cmd, dynamic event) async {
    var doc = db.collection(event['collection_name']).doc(event['doc_id']);

    switch (cmd) {
      case 'doc_get':
        return doc.get();

      case 'doc_set':
        return doc.set(event['data']);

      case 'doc_update':
        return doc.update(event['data']);

      case 'doc_remove':
        return doc.remove();

      default:
    }
  }

  Future<dynamic> commandOp(CloudBaseDatabase db, String cmd, dynamic event) async {
    var collection = db.collection(event['collection_name']);
    var _ = db.command;

    switch (cmd) {
      case 'command_eq':
        return collection
            .where({event['eq_key']: _.eq(event['eq_value'])}).get();

      case 'command_neq':
        return collection
            .where({event['neq_key']: _.neq(event['neq_value'])}).get();

      case 'command_lt':
        return collection
            .where({event['lt_key']: _.lt(event['lt_value'])}).get();

      case 'command_lte':
        return collection
            .where({event['lte_key']: _.lte(event['lte_value'])}).get();

      case 'command_gt':
        return collection
            .where({event['gt_key']: _.gt(event['gt_value'])}).get();

      case 'command_gte':
        return collection
            .where({event['gte_key']: _.gte(event['gte_value'])}).get();

      case 'command_in':
        return collection
            .where({event['in_key']: _.into(event['in_value'])}).get();

      case 'command_nin':
        return collection
            .where({event['nin_key']: _.nin(event['nin_value'])}).get();

      case 'command_and':
        return collection.where({
          event['and_key']:
              _.and([_.gt(event['and_value0']), _.lt(event['and_value1'])])
        }).get();

      case 'command_or':
        return collection.where({
          event['or_key']:
              _.or([_.gt(event['or_value0']), _.lt(event['or_value1'])])
        }).get();

      case 'command_set':
        return collection
            .doc(event['doc_id'])
            .update({event['set_key']: _.set(event['set_value'])});

      case 'command_remove':
        return collection
            .doc(event['doc_id'])
            .update({event['remove_key']: _.remove()});

      case 'command_inc':
        return collection
            .doc(event['doc_id'])
            .update({event['inc_key']: _.inc(event['inc_value'])});

      case 'command_mul':
        return collection
            .doc(event['doc_id'])
            .update({event['mul_key']: _.mul(event['mul_value'])});

      case 'command_push':
        return collection
            .doc(event['doc_id'])
            .update({event['push_key']: _.push(event['push_value'])});

      case 'command_pop':
        return collection
            .doc(event['doc_id'])
            .update({event['pop_key']: _.pop()});

      case 'command_shift':
        return collection
            .doc(event['doc_id'])
            .update({event['shift_key']: _.shift()});

      case 'command_unshift':
        return collection
            .doc(event['doc_id'])
            .update({event['unshift_key']: _.unshift(event['unshift_value'])});

      case 'command_geoNear':
        return collection.where({
          event['geoNear_key']: _.geoNear(
            event['geoNear_value']['geometry'],
            maxDistance: event['geoNear_value']['maxDistance'],
            minDistance: event['geoNear_value']['minDistance'],
          )
        }).get();

      case 'command_geoWithin':
        return collection.where({
          event['geoWithin_key']:
              _.geoWithin(event['geoWithin_value']['geometry'])
        }).get();

      case 'command_geoIntersects':
        return collection.where({
          event['geoIntersects_key']:
              _.geoIntersects(event['geoIntersects_value']['geometry'])
        }).get();

      default:
        return null;
    }
  }

  Future<void> runCases(db, element, testName) async {
    var event = element['request'];
    String cmd = event['cmd'];
    print(element['desc']);

    dynamic res;
    if (cmd.startsWith('collection')) {
      res = await collectionOp(db, cmd, event);
    } else if (cmd.startsWith('doc')) {
      res = await docOp(db, cmd, event);
    } else if (cmd.startsWith('command')) {
      res = await commandOp(db, cmd, event);
    }

    if (element['expect'] != null) {
      final value = element['expect'](res);
      testLog(value, element['desc'], res);
    }
  }

  void testLog(res, testName, data) {
    final result = "测试 [$testName] ${res ? 'SUCCESS' : 'FAIL'}";
    print(res ? "" : data);
    log("expect: [test result] -&gt; $result");
  }
}

注意事项

  • 在运行测试前,需要先在腾讯云控制台创建两个集合 doc_wcctcb_hello_world
  • doc_wcc 集合创建 6 个地理索引,具体步骤如下:
    • 打开 腾讯云数据库控制台
    • 选择 索引管理,点击 新建索引,依次创建如下 6 个索引:
      {'point_index': 'Point'}
      {'polygon_index': 'Polygon'}
      {'lineString_index': 'LineString'}
      {'multiPoint_index': 'MultiPoint'}
      {'multiPolygon_index': 'MultiPolygon'}
      {'multiLineString_index': 'MultiLineString'}
      

更多关于Flutter云开发插件cloudbase_ce的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter云开发插件cloudbase_ce的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter项目中使用cloudbase_ce插件进行云开发的代码示例。cloudbase_ce是腾讯云开发(CloudBase)的一个Flutter插件,允许你在Flutter应用中轻松集成云功能,如数据库、云函数和文件存储等。

1. 添加依赖

首先,在你的pubspec.yaml文件中添加cloudbase_ce依赖:

dependencies:
  flutter:
    sdk: flutter
  cloudbase_ce: ^最新版本号 # 请替换为实际的最新版本号

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

2. 初始化CloudBase实例

在你的Flutter应用中,你需要初始化一个CloudBase实例。这通常在你的应用入口文件(如main.dart)中进行。

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

void main() async {
  // 初始化CloudBase实例
  CloudBase cloudBase = CloudBase(
    env: 'your-env-id', // 替换为你的云开发环境ID
  );

  // 将CloudBase实例传递给MaterialApp(或你的应用的其他部分)
  runApp(MyApp(cloudBase: cloudBase));
}

class MyApp extends StatelessWidget {
  final CloudBase cloudBase;

  MyApp({required this.cloudBase});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter CloudBase Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(cloudBase: cloudBase),
    );
  }
}

3. 使用CloudBase功能

数据库操作

下面是一个简单的示例,展示如何使用CloudBase的数据库功能:

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

class MyHomePage extends StatefulWidget {
  final CloudBase cloudBase;

  MyHomePage({required this.cloudBase});

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

class _MyHomePageState extends State<MyHomePage> {
  late Database _database;

  @override
  void initState() {
    super.initState();
    // 获取数据库实例
    _database = widget.cloudBase.database();
  }

  Future<void> addRecord() async {
    try {
      await _database
          .collection('your-collection-name')
          .add({
        'name': 'John Doe',
        'age': 30,
      });
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Record added successfully')),
      );
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Failed to add record: $e')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('CloudBase Demo'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: addRecord,
          child: Text('Add Record'),
        ),
      ),
    );
  }
}

云函数调用

下面是一个调用云函数的示例:

Future<void> callCloudFunction() async {
  try {
    var result = await widget.cloudBase.callFunction({
      'name': 'your-cloud-function-name',
      'data': {
        'key': 'value',
      },
    });
    print('Cloud function result: $result');
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Cloud function called successfully')),
    );
  } catch (e) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Failed to call cloud function: $e')),
    );
  }
}

你可以在按钮的onPressed回调中使用这个函数来调用云函数。

文件存储操作

下面是一个上传文件的示例:

Future<void> uploadFile() async {
  var filePickerResult = await FilePicker.platform.pickFiles();
  if (filePickerResult != null && filePickerResult.files.isNotEmpty) {
    var file = File(filePickerResult.files.first.path!);
    try {
      var cloudPath = 'your-directory/${file.path.split('/').last}';
      await widget.cloudBase.storage().uploadFile(cloudPath, file);
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('File uploaded successfully')),
      );
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Failed to upload file: $e')),
      );
    }
  }
}

同样,你可以在按钮的onPressed回调中使用这个函数来上传文件。

注意事项

  1. 确保你已经正确配置了腾讯云开发环境,并获取了环境ID。
  2. 在实际使用中,请处理异常和错误,以提供更好的用户体验。
  3. 根据你的应用需求,调整数据库集合名称、云函数名称和文件存储路径等参数。

希望这些代码示例能帮助你在Flutter项目中成功使用cloudbase_ce插件进行云开发!

回到顶部