Flutter Laravel JSON API集成插件laravel_json_api的使用

Flutter Laravel JSON API 集成插件 laravel_json_api 的使用

Laravel Json Api

这个插件是为了消费基于 Laravel 并使用 cloudcreativity/laravel-json-api 包实现的符合 JSON:API 规范的 API 而创建的。

特性

  • 模式(Schemas)
  • 自定义头部(Accept, Content-Type, Authorization 等)
  • JSON:API 格式化器
  • HTTP 请求(GET, POST, PATCH, DELETE, PUT)
  • 过滤资源
  • 获取关联资源
  • 获取关系
  • 异常处理

安装

在 Dart 中安装:

dart pub add laravel_json_api

在 Flutter 中安装:

flutter pub add laravel_json_api

使用

连接到我们的服务器

import 'package:laravel_json_api/laravel_json_api.dart';

Adapter adapter = ApiController('www.host.com', '/api/v1');

(不需要添加 http 或 https 协议)

为了在整个应用程序中使用适配器,我们可以将其包装在一个状态处理器中。我们推荐使用 provider 包。

头部

默认情况下,以下头部的值如下:

Header Value
Accept application/vnd.api+json
Content-Type application/vnd.api+json

但它们可以被覆盖。

import 'package:laravel_json_api/laravel_json_api.dart';

ApiController controller = ApiController('www.host.com', '/api/v1');
print(controller.headers);
controller.addHeader('Authorization', 'Bearer token');
controller.addHeader('Accept', 'application/json');
controller.addHeader('Content-Type', 'application/json');
print(controller.headers);

Adapter adapter = controller;

创建模式

模式提供了获取器和设置器,帮助我们将响应转换为具有自身属性、关系、关联对象和错误的对象。

以一个简单的博客为例:

import 'package:laravel_json_api/laravel_json_api.dart';

class Article extends Schema {
  // 构造函数
  Article(ResourceObject resourceObject) : super(resourceObject);
  Article.init(String type) : super.init(type);

  // 属性

  String get title => getAttribute<String>('title');
  set title(String value) => setAttribute<String>('title', value);

  String get slug => getAttribute<String>('slug');
  set slug(String value) => setAttribute<String>('slug', value);

  String get content => getAttribute<String>('content');
  set content(String value) => setAttribute<String>('content', value);

  String get image => getAttribute<String>('image');
  set image(String value) => setAttribute<String>('image', value);

  // 关系
  String? get authorId => idFor('user');
  set author(User model) => setHasOne('author', model);
  Object? get relatedAuthor => includedDoc('users', 'user');

  String? get categoryId => idFor('category');
  set category(Category model) => setHasOne('category', model);
  Object? get relatedCategory => includedDoc('categories', 'category');
}

class Category extends Schema {
  // 构造函数
  Category(ResourceObject resourceObject) : super(resourceObject);
  Category.init(String type) : super.init(type);

  // 属性

  String get name => getAttribute<String>('name');
  set name(String value) => setAttribute<String>('name', value);

  String get slug => getAttribute<String>('slug');
  set slug(String value) => setAttribute<String>('slug', value);

  String get image => getAttribute<String>('image_cover');
  set image(String value) => setAttribute<String>('image_cover', value);

  // 关系
  Iterable<String> get articlesId => idsFor('articles');
  Iterable<Object> get articles => includedDocs('articles');
}

class User extends Schema {
  // 构造函数
  User(ResourceObject resourceObject) : super(resourceObject);
  User.init(String type) : super.init(type);

  // 属性

  String get firstName => getAttribute<String>('first_name');
  set firstName(String value) => setAttribute<String>('first_name', value);

  String get email => getAttribute<String>('email');
  set email(String value) => setAttribute<String>('email', value);

  // 关系
  Iterable<String> get articlesId => idsFor('articles');
  Iterable<Object> get articles => includedDocs('articles');
}

所有模式方法

  • String? idFor(String relationshipName)
  • String? typeFor(String relationshipName)
  • Map<String, dynamic> dataForHasOne(String relationshipName)
  • Iterable<dynamic>? dataForHasMany(String relationshipName)
  • Iterable<String> idsFor(String relationshipName)
  • Iterable<ResourceObject> includedDocs(String type, [Iterable<String>? ids])
  • ResourceObject? includedDoc(String type, String relationshipName)
  • void clearErrorsFor(String attributeName)
  • bool get hasErrors
  • bool attributeHasErrors(String attributeName)
  • Iterable<String> errorsFor(String attributeName)
  • void clearErrors()
  • void addErrorFor(String attributeName, String errorMessage)
  • void setHasOne(String relationshipName, LaravelJsonApiModel model)

获取数据

所有这些操作都是异步的,并返回特定值。

查找单个

Future<Article> getOneArticle(String id) async {
  Article article = Article(await adapter.find('articles', id) as ResourceObject);

  return article;
}
GET | https://www.host.com/api/v1/articles/1

可选地,我们可以发送 forceReload 参数来缓存此资源,并使用 queryParams 参数进行排序或包含关系。

Future<Article> getOneArticle(String id) async {
  Article article = Article(await adapter.find('articles', id,
      forceReload: true,
      queryParams: {'include': 'category,user'}) as ResourceObject);

  return article;
}
GET | https://www.host.com/api/v1/articles/1?include=category,user

查找所有

Future<Iterable<Article>> getAllArticles() async {
  Iterable<Article> articles = (await adapter.findAll('articles'))
      .map<Article>((article) => Article(article as ResourceObject))
      .toList();

  return articles;
}
GET | https://www.host.com/api/v1/articles

更多获取请求

  • Future<Iterable<Object>> findManyById(String endpoint, Iterable<String> ids, {Map<String, String> queryParams})
  • Future<Iterable<Object>> getRelated(String endpoint, String id, String relationshipName)
  • Future<Iterable<Object>> filter(String endpoint, String filterField, Iterable<String> values, {Map<String, String> queryParams})

写入数据

Article article = Article.init('articles');
article.title = 'Title';
article.content = 'Content';
article.user = user;
article.category = category;

创建资源

Future saveArticle(Article article) async {
  await adapter.save('articles', article.jsonApiDoc);
}

更新资源

Article article = Article(await adapter.find('articles', '1') as ResourceObject);
article.title = 'Title Update';

await adapter.save('articles', article.jsonApiDoc);

替换关系

Article article = Article(await adapter.find('articles', '1') as ResourceObject);
Category newCategory = Category(await adapter.find('categories', '2') as ResourceObject);

await adapter.replaceRelationship('articles', 'category', article, newCategory);

删除资源

Article article = Article(await adapter.find('articles', '1') as ResourceObject);

await adapter.delete('articles', article);

示例代码

示例代码

import 'package:laravel_json_api/laravel_json_api.dart';

// 主函数
void main() async {
  ApiController controller = ApiController('maeth.herokuapp.com', '/api/v1');

  controller.addHeader(
      'Authorization', 'Bearer 32|nEbOJRQUB4jSU9Zh2BcWLqKEpPWQ73hVPbKcZsFn');

  Adapter adapter = controller;

  // 获取用户
  User user = await _getUser(adapter);
  print(user);

  // 获取分类
  Iterable<Category> categories = await _getCategories(adapter);
  print(categories.first);

  // 创建文章
  Article newArticle = Article.init('articles');
  newArticle.title = 'Test 1';
  newArticle.slug = 'title-1';
  newArticle.content = 'Content test 1';
  newArticle.image =
      'https://res.cloudinary.com/maeth/image/upload/v1638752850/articles/cbogi4o5mny0iggmt1j4.jpg';
  newArticle.author = user;
  newArticle.category = categories.first;

  var response = await _saveArticle(adapter, newArticle.resourceObject);

  if (response == true) {
    print('Article created');
  } else {
    print('Article not created');
    print(response);
  }

  print(user.articles);
}

Future<User> _getUser(Adapter adapter) async {
  return User(await adapter.find(
      'users', '3c85faf7-eacb-4b6b-8547-4e5bd2b24c3f',
      queryParams: {'include': 'articles'}) as ResourceObject);
}

Future<Iterable<Category>> _getCategories(Adapter adapter) async {
  return (await adapter.findAll('categories'))
      .map<Category>((category) => Category(category as ResourceObject))
      .toList();
}

Future _saveArticle(Adapter adapter, Object model) async {
  try {
    Article(await adapter.save('articles', model) as ResourceObject);
    return true;
  } catch (e) {
    print(e);
    Article temp = Article(model as ResourceObject);
    return temp.errors.first['detail'];
  }
}

// 模型类

class Article extends Schema {
  // 构造函数
  Article(ResourceObject resourceObject) : super(resourceObject);
  Article.init(String type) : super.init(type);

  // 属性

  String get title => getAttribute<String>('title');
  set title(String value) => setAttribute<String>('title', value);

  String get slug => getAttribute<String>('slug');
  set slug(String value) => setAttribute<String>('slug', value);

  String get content => getAttribute<String>('content');
  set content(String value) => setAttribute<String>('content', value);

  String get image => getAttribute<String>('image');
  set image(String value) => setAttribute<String>('image', value);

  // 关系
  String? get authorId => idFor('user');
  set author(User model) => setHasOne('author', model);
  Object? get relatedAuthor => includedDoc('users', 'user');

  String? get categoryId => idFor('category');
  set category(Category model) => setHasOne('category', model);
  Object? get relatedCategory => includedDoc('categories', 'category');
}

class Category extends Schema {
  // 构造函数
  Category(ResourceObject resourceObject) : super(resourceObject);
  Category.init(String type) : super.init(type);

  // 属性

  String get name => getAttribute<String>('name');
  set name(String value) => setAttribute<String>('name', value);

  String get slug => getAttribute<String>('slug');
  set slug(String value) => setAttribute<String>('slug', value);

  String get image => getAttribute<String>('image_cover');
  set image(String value) => setAttribute<String>('image_cover', value);

  // 关系
  Iterable<String> get articlesId => idsFor('articles');
  Iterable<Object> get articles => includedDocs('articles');
}

class User extends Schema {
  // 构造函数
  User(ResourceObject resourceObject) : super(resourceObject);
  User.init(String type) : super.init(type);

  // 属性

  String get firstName => getAttribute<String>('first_name');
  set firstName(String value) => setAttribute<String>('first_name', value);

  String get email => getAttribute<String>('email');
  set email(String value) => setAttribute<String>('email', value);

  // 关系
  Iterable<String> get articlesId => idsFor('articles');
  Iterable<Object> get articles => includedDocs('articles');
}

更多关于Flutter Laravel JSON API集成插件laravel_json_api的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter Laravel JSON API集成插件laravel_json_api的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter应用中集成Laravel后端并使用laravel_json_api插件进行JSON API通信的示例代码。这个示例将涵盖基本的CRUD(创建、读取、更新、删除)操作。

前置条件

  1. Laravel后端:确保你的Laravel项目已经设置好,并且有一个资源(例如User)可以通过API进行访问。
  2. Flutter前端:确保你的Flutter开发环境已经搭建好。

步骤1:安装laravel_json_api插件

在Flutter项目的pubspec.yaml文件中添加laravel_json_api依赖:

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

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

步骤2:配置Laravel后端

确保你的Laravel后端已经安装了laravel-json-api/core包,并且配置了一个资源。例如,配置User资源:

composer require laravel-json-api/core

config/api.php中注册你的资源:

'resources' => [
    'users' => \App\Http\Api\V1\Models\User::class,
],

创建对应的API控制器和路由,通常这些可以通过laravel-json-api包自动生成。

步骤3:Flutter前端代码示例

以下是一个简单的Flutter应用,它使用laravel_json_api插件与Laravel后端进行通信。

main.dart

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Laravel JSON API Integration',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final LaravelJsonApiClient client = LaravelJsonApiClient(
    baseUrl: 'http://your-laravel-api-url.com/api/v1',
    headers: {'Accept': 'application/vnd.api+json'},
  );

  List<dynamic> users = [];

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

  void fetchUsers() async {
    try {
      final response = await client.get('users');
      setState(() {
        users = response.data.data;
      });
    } catch (e) {
      print(e);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Users List'),
      ),
      body: ListView.builder(
        itemCount: users.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(users[index]['attributes']['name']),
            trailing: IconButton(
              icon: Icon(Icons.delete),
              onPressed: () {
                deleteUser(users[index]['id']);
              },
            ),
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => UserDetailPage(user: users[index])),
              );
            },
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Navigator.push(
            context,
            MaterialPageRoute(builder: (context) => UserCreatePage(client: client)),
          );
        },
        tooltip: 'Add User',
        child: Icon(Icons.add),
      ),
    );
  }

  void deleteUser(String id) async {
    try {
      await client.delete('users/$id');
      setState(() {
        users.removeWhere((user) => user['id'] == id);
      });
    } catch (e) {
      print(e);
    }
  }
}

class UserDetailPage extends StatefulWidget {
  final dynamic user;

  UserDetailPage({required this.user});

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

class _UserDetailPageState extends State<UserDetailPage> {
  final TextEditingController nameController = TextEditingController();

  @override
  void initState() {
    super.initState();
    nameController.value = TextEditingValue(text: widget.user['attributes']['name']);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('User Detail'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            TextField(
              controller: nameController,
              decoration: InputDecoration(labelText: 'Name'),
            ),
            SizedBox(height: 16),
            ElevatedButton(
              onPressed: () async {
                try {
                  final response = await client.patch(
                    'users/${widget.user['id']}',
                    data: {
                      'data': {
                        'type': 'users',
                        'id': widget.user['id'],
                        'attributes': {'name': nameController.text},
                      },
                    },
                  );
                  Navigator.pop(context);
                } catch (e) {
                  print(e);
                }
              },
              child: Text('Update'),
            ),
          ],
        ),
      ),
    );
  }
}

class UserCreatePage extends StatefulWidget {
  final LaravelJsonApiClient client;

  UserCreatePage({required this.client});

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

class _UserCreatePageState extends State<UserCreatePage> {
  final TextEditingController nameController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Create User'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            TextField(
              controller: nameController,
              decoration: InputDecoration(labelText: 'Name'),
            ),
            SizedBox(height: 16),
            ElevatedButton(
              onPressed: () async {
                try {
                  final response = await widget.client.post(
                    'users',
                    data: {
                      'data': {
                        'type': 'users',
                        'attributes': {'name': nameController.text},
                      },
                    },
                  );
                  Navigator.popAndPushNamed(context, '/');
                } catch (e) {
                  print(e);
                }
              },
              child: Text('Create'),
            ),
          ],
        ),
      ),
    );
  }
}

解释

  1. LaravelJsonApiClient初始化:在_MyHomePageState类中初始化LaravelJsonApiClient,设置基础URL和请求头。
  2. 获取用户列表fetchUsers方法通过GET请求获取用户列表,并更新UI。
  3. 创建用户UserCreatePage类允许用户输入名称并提交,通过POST请求创建新用户。
  4. 查看/编辑用户详情UserDetailPage类显示用户详情,并允许用户更新名称,通过PATCH请求更新用户信息。
  5. 删除用户:在用户列表中,点击删除按钮通过DELETE请求删除用户。

这个示例提供了一个基础框架,你可以根据实际需求进行扩展和修改。

回到顶部