Flutter Directus API管理插件directus_api_manager的使用

Flutter Directus API管理插件directus_api_manager的使用

特性

此包可以为你的每个Directus集合生成模型类。

安装

在你的 pubspec.yaml 文件中添加该包作为依赖:

dependencies:
  flutter:
    sdk: flutter

  directus_api_manager:
    git: https://github.com/maxbritto/directus_api_manager.git

如果你想要使用特定版本,可以在 git 之后添加 version 字段:

dependencies:
  directus_api_manager:
    git: https://github.com/maxbritto/directus_api_manager.git
    version: ^1.2.0

开始使用

创建你的模型

为每个Directus模型创建一个新的类,继承自 DirectusItem 并使用注解指定 endpointName

[@DirectusCollection](/user/DirectusCollection)()
[@CollectionMetadata](/user/CollectionMetadata)(endpointName: "player")
class PlayerDirectusModel extends DirectusItem {
  PlayerDirectusModel.newItem({required String nickname}) : super.newItem() {
    setValue(nickname, forKey: "nickname");
  }

  PlayerDirectusModel(super.rawReceivedData);

  String get nickname => getValue(forKey: "nickname");
  int? get bestScore => getValue(forKey: "best_score");
  set bestScore(int? newBestScore) => setValue(newBestScore, forKey: "best_score");
}

重要提示:你必须包含一个初始化方法,该方法调用父类的初始化方法并传递接收到的原始数据,而不能添加其他参数:

PlayerDirectusModel(super.rawReceivedData);

如果需要创建新项目并发送到服务器,应该覆盖名为 newItem() 的初始化方法:

PlayerDirectusModel.newItem() : super.newItem();

你可以根据需要添加任何属性作为计算属性,以访问数据:

String get nickname => getValue(forKey: "nickname");

int get bestScore => getValue(forKey: "best_score");
set bestScore(int newBestScore) => setValue(newBestScore, forKey: "best_score");

注意:键名必须与Directus集合中的属性名称相同,并且类型也必须一致。

为你的模型生成代码

每次添加新的集合时,可以通过运行以下命令触发代码生成器:

dart run build_runner build lib

这将为你项目的目录添加新的 .reflectable.dart 文件。不要将这些文件包含在git仓库中。

建议:在 .gitignore 文件末尾添加以下行:

*.reflectable.dart

初始化库以使用生成的模型

main() 函数中初始化反射:

void main() {
  initializeReflectable();
  //...
  // 其余应用代码
}

创建你的DirectusApiManager

这个对象将处理所有事情,包括身份验证、令牌管理、发送请求、解析响应等。你应该只创建一个这种类型的对象,并且只需要提供你的Directus实例的URL:

DirectusApiManager _directusApiManager = DirectusApiManager(baseURL: "http://0.0.0.0:8055/");

管理用户和认证

在进行需要授权的请求之前,使用 loginDirectusUser 方法进行认证:

final apiManager = DirectusApiManager(baseURL: "http://0.0.0.0:8055/");
final result = await apiManager.loginDirectusUser("will@acn.com", "will-password");
if (result.type == DirectusLoginResultType.success) {
  print("User logged in");
} else if (result.type == DirectusLoginResultType.invalidCredentials) {
  print("Please verify entered credentials");
} else if (result.type == DirectusLoginResultType.invalidOTP) {
  print("Please provide OTP");
  // 保留邮箱和密码,进入下一个屏幕或额外字段并重新提交
  // await apiManager.loginDirectusUserWithOtp("will@acn.com", "will-password", "123456");
} else if (result.type == DirectusLoginResultType.error) {
  print("An unknown error occurred");
  final additionalMessage = result.message;
  if (additionalMessage != null) {
    print("More information: $additionalMessage");
  }
}

如果用户的登录需要MFA/OTP,你需要向用户提供额外的字段或页面来完成认证,并重新发送认证到 loginDirectusUserWithOtp 方法:

final result = await apiManager.loginDirectusUserWithOtp("will@acn.com", "will-password", "123456");

所有未来的请求都会包含此用户令牌。

CRUD操作

对于每个集合,你可以执行以下操作:

  • 获取一个或多个项目
  • 更新项目
  • 创建项目
  • 删除项目

创建新项目

final newPlayer = PlayerDirectusModel.newItem(nickname: "Sheldon");
final creationResult = await apiManager.createNewItem(objectToCreate: newPlayer);
if (creationResult.isSuccess) {
  print("Created player!");
  final createdPlayer = creationResult.createdItem;
  // 取决于你的Directus服务器授权,你可能无法访问创建的项目
  if (createdPlayer != null) {
    print("The id of this new player is ${createdPlayer.id}");
  }
} else {
  final error = creationResult.error;
  if (error != null) {
    print("Error while creating player: $error");
  }
}

获取现有项目

多个项目

final list = await apiManager.findListOfItems<PlayerDirectusModel>();
for (final player in list) {
  print("${player.nickname} - ${player.bestScore}");
}

单个特定项目

final PlayerDirectusModel onePlayer = await apiManager.getSpecificItem(id: "1");
print(onePlayer.nickname);

更新现有项目

final PlayerDirectusModel onePlayer = await apiManager.getSpecificItem(id: "1");
onePlayer.bestScore = 123;
final updatedPlayer = await apiManager.updateItem(objectToUpdate: onePlayer);

WebSocket支持

DirectusWebSocket

DirectusWebSocket 允许通过WebSocket从Directus获取数据。它会处理身份验证、刷新令牌过程,并保持连接活跃。每个 DirectusWebSocket 可以有多个 DirectusWebSocketSubscription

DirectusWebSocketSubscription

DirectusWebSocketSubscription 表示对你的Directus服务器的订阅。以下是使用它的必要属性:

  • uid 必须指定。当服务器发送消息时,将提供此uid。这允许我们知道消息来自哪个订阅。
  • onCreateonUpdateonDelete 回调会在订阅接收到消息时触发。它们都是可选的,但 DirectusWebSocketSubscription 至少需要有一个。
DirectusWebSocketSubscription<DirectusDataExtension>(
  uid: "directus_data_extension_uid",
  onCreate: onCreate,
  onUpdate: onUpdate,
  onDelete: onDelete,
  sort: const [SortProperty("id")],
  limit: 10,
  offset: 10,
  filter: const PropertyFilter(
    field: "folder",
    operator: FilterOperator.equals,
    value: "folder_id"
  )
);

缓存系统

启用和配置缓存系统

此API带有一个缓存系统,可以通过提供 ILocalDirectusCacheInterface 实例来启用,当创建 DirectusApiManager 实例时。

提供的一个现成实现是 JsonCacheEngine 类。它将使用json文件存储数据在一个你选择的文件夹中。例如:

import 'package:path_provider/path_provider.dart';

void main() async {
  final directory = await getApplicationCacheDirectory();
  final apiManager = DirectusApiManager(
    baseURL: "http://0.0.0.0:8055/",
    cacheEngine: JsonCacheEngine(cacheFolderPath: "${directory.path}/directus_api_cache")
  );
  // ...
}

你可以决定替换json文件实现,通过创建并提供你自己的实现,该实现实现了 ILocalDirectusCacheInterface 类。

使用缓存系统进行请求

所有读取请求(如 getfindcurrentUser 等)现在都有可选参数来配置缓存。默认情况下,大多数这些请求将保存响应,但如果下一次请求失败,则只会使用这些响应。如果你想替换未来响应为本地缓存读取,可以将 canUseCacheForResponse 参数设置为 true,并调整 maxCacheAge 参数来设置缓存的最大年龄(默认为1天)。这将防止直接调用Directus服务器,如果存在相同的请求的有效缓存。

await sut.getSpecificItem<DirectusItemTest>(
  id: "element1",
  canUseCacheForResponse: true,
  maxCacheAge: const Duration(days: 1)
);

如果你想完全禁用某个请求的缓存,可以将 canSaveResponseToCache 参数设置为 false

await sut.getSpecificItem<DirectusItemTest>(
  id: "element1",
  canSaveResponseToCache: false
);

默认情况下,即使缓存已过期,如果实际网络请求失败,也可以使用过期缓存。如果你想禁用此行为,可以将 canUseOldCachedResponseAsFallback 参数设置为 false

await sut.getSpecificItem<DirectusItemTest>(
  id: "element1",
  canUseOldCachedResponseAsFallback: false
);

这些参数适用于所有基于读取的请求。

额外信息

高级属性

如果你的集合有需要特定解析的高级属性,可以在计算属性中进行处理。例如,在Directus中,属性类型为“标签列表”,我们可以输入一些课程ID作为数字,但Directus将所有标签视为字符串。因此,我们在Dart代码中将其转换如下:

List<int> get requiredCourseIdList {
  final courseListJson = getValue(forKey: "required_course_id_list");
  if (courseListJson is List<dynamic>) {
    return courseListJson
        .map<int>((courseIdString) => int.parse(courseIdString))
        .toList();
  } else {
    return [];
  }
}

示例代码

import 'package:directus_api_manager/directus_api_manager.dart';
import 'directus_api_manager_example.reflectable.dart';

Future<void> main() async {
  initializeReflectable();
  final apiManager = DirectusApiManager(baseURL: "http://0.0.0.0:8055/");

  // 认证
  final loginResult = await apiManager.loginDirectusUser("admin@example.com", "d1r3ctu5");
  if (loginResult.type == DirectusLoginResultType.success) {
    print("User logged in");
  } else if (loginResult.type == DirectusLoginResultType.invalidCredentials) {
    print("Please verify entered credentials");
  } else if (loginResult.type == DirectusLoginResultType.invalidOTP) {
    print("You need to provide a valid OneTimePassword code");
  } else if (loginResult.type == DirectusLoginResultType.error) {
    print("An unknown error occurred");
    final additionalMessage = loginResult.message;
    if (additionalMessage != null) {
      print("More information: $additionalMessage");
    }
  }

  // 创建新项目
  final newPlayer = PlayerDirectusModel.newItem(nickname: "Leonard");
  final creationResult = await apiManager.createNewItem(objectToCreate: newPlayer);
  if (creationResult.isSuccess) {
    print("Created player!");
    final createdPlayer = creationResult.createdItem;
    // 取决于你的Directus服务器授权,你可能无法访问创建的项目
    if (createdPlayer != null) {
      print("The id of this new player is ${createdPlayer.id}");
    }
  } else {
    final error = creationResult.error;
    if (error != null) {
      print("Error while creating player: $error");
    }
  }

  // 多个项目
  final list = await apiManager.findListOfItems<PlayerDirectusModel>();
  for (final player in list) {
    print("${player.nickname} - ${player.bestScore}");
  }

  final playerId = list.first.id;
  if (playerId == null) {
    print("No player id found");
    return;
  }

  // 从ID获取单个特定项目
  final PlayerDirectusModel? fetchedPlayer = await apiManager.getSpecificItem(id: playerId);
  if (fetchedPlayer != null) {
    print(fetchedPlayer.nickname);

    // 更新项目
    fetchedPlayer.bestScore = 123;
    final updatedPlayer = await apiManager.updateItem(objectToUpdate: fetchedPlayer);
    print(updatedPlayer.bestScore);
  }
}

[@DirectusCollection](/user/DirectusCollection)()
[@CollectionMetadata](/user/CollectionMetadata)(endpointName: "player")
class PlayerDirectusModel extends DirectusItem {
  PlayerDirectusModel.newItem({required String nickname}) : super.newItem() {
    setValue(nickname, forKey: "nickname");
  }

  PlayerDirectusModel(super.rawReceivedData);

  String get nickname => getValue(forKey: "nickname");
  int? get bestScore => getValue(forKey: "best_score");
  set bestScore(int? newBestScore) => setValue(newBestScore, forKey: "best_score");
}

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

1 回复

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


当然,下面是一个关于如何在Flutter项目中使用directus_api_manager插件来管理Directus API的示例代码。这个示例展示了如何初始化插件、进行身份验证以及执行一些基本的CRUD操作。

首先,确保你已经在pubspec.yaml文件中添加了directus_api_manager依赖:

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

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

接下来,你可以在你的Flutter项目中按照以下步骤使用directus_api_manager插件:

1. 导入插件并初始化

在你的Dart文件中导入插件,并初始化Directus API客户端:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: DirectusExample(),
    );
  }
}

class DirectusExample extends StatefulWidget {
  @override
  _DirectusExampleState createState() => _DirectusExampleState();
}

class _DirectusExampleState extends State<DirectusExample> {
  DirectusClient? _directusClient;

  @override
  void initState() {
    super.initState();
    // 初始化Directus客户端
    _initDirectusClient();
  }

  void _initDirectusClient() async {
    // 替换为你的Directus实例的URL、项目名称、管理密钥等信息
    String directusUrl = 'https://your-directus-instance.com';
    String projectName = 'your-project-name';
    String adminKey = 'your-admin-key';

    DirectusClient client = DirectusClient(
      baseUrl: directusUrl,
      projectName: projectName,
      adminKey: adminKey,
    );

    setState(() {
      _directusClient = client;
    });

    // 可以在这里添加登录逻辑,如果需要的话
    // await _login(client);
  }

  // Future<void> _login(DirectusClient client) async {
  //   // 登录逻辑(如果需要的话)
  // }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Directus API Manager Example'),
      ),
      body: Center(
        child: _directusClient == null
            ? CircularProgressIndicator()
            : Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  ElevatedButton(
                    onPressed: () => _fetchItems(_directusClient!),
                    child: Text('Fetch Items'),
                  ),
                  // 添加其他按钮以执行CRUD操作
                ],
              ),
      ),
    );
  }

  void _fetchItems(DirectusClient client) async {
    try {
      // 假设我们有一个名为'items'的集合
      var response = await client.getItems('items');
      print(response.data);
      // 处理响应数据
    } catch (e) {
      print('Error fetching items: $e');
    }
  }
}

2. 执行CRUD操作

在上面的代码中,我们展示了如何初始化Directus客户端并获取项目中的项。下面是如何执行创建、读取、更新和删除(CRUD)操作的示例:

void _createItem(DirectusClient client, Map<String, dynamic> itemData) async {
  try {
    var response = await client.createItem('items', itemData);
    print('Created item: ${response.data}');
  } catch (e) {
    print('Error creating item: $e');
  }
}

void _updateItem(DirectusClient client, String itemId, Map<String, dynamic> updatedData) async {
  try {
    var response = await client.updateItem('items', itemId, updatedData);
    print('Updated item: ${response.data}');
  } catch (e) {
    print('Error updating item: $e');
  }
}

void _deleteItem(DirectusClient client, String itemId) async {
  try {
    var response = await client.deleteItem('items', itemId);
    print('Deleted item: ${response.data}');
  } catch (e) {
    print('Error deleting item: $e');
  }
}

你可以将这些函数添加到你的_DirectusExampleState类中,并在相应的按钮点击事件中调用它们。例如:

ElevatedButton(
  onPressed: () {
    Map<String, dynamic> itemData = {
      'name': 'New Item',
      // 其他字段
    };
    _createItem(_directusClient!, itemData);
  },
  child: Text('Create Item'),
),

这样,你就可以在Flutter应用中通过directus_api_manager插件与Directus API进行交互了。请根据你的实际需求调整代码中的集合名称、字段和数据。

回到顶部