Flutter GraphQL请求插件custom_graphql_flutter的使用
Flutter GraphQL 请求插件 custom_graphql_flutter 的使用
在本指南中,我们将详细介绍如何在 Flutter 中使用 custom_graphql_flutter
插件来执行 GraphQL 请求。我们将涵盖安装、基本用法、查询(包括分页)、突变(包括乐观突变)以及订阅。
引言
custom_graphql_flutter
提供了一套用于 graphql/client.dart
的 Flutter API 和小部件。它们在 GitHub 上共同开发,您可以在那里找到更深入的例子。我们也有一个活跃的社区在 Discord 上。
本指南主要关注设置、小部件以及 Flutter 特定的注意事项。对于 graphql
API 的更多详细信息,请参阅 graphql
的 README。
有用的章节
- 深度链接指南
- 直接缓存访问
- 缓存写入严格性
- 策略
- 异常
- AWS AppSync 支持
- GraphQL 上传
- 构建时解析 ASTs
有用的 API 文档
GraphQLCache
GraphQLDataProxy
API 文档(直接缓存访问)
安装
首先,在 pubspec.yaml
文件中添加依赖:
$ flutter pub add custom_graphql_flutter
然后在 Dart 代码中导入:
import 'package:custom_graphql_flutter/graphql_flutter.dart';
使用
为了连接到 GraphQL 服务器,我们需要创建一个 GraphQLClient
。GraphQLClient
需要 cache
和 link
两个参数才能初始化。
在我们的示例中,我们将使用 Github 公共 API。我们将使用 HttpLink
并将其与 AuthLink
连接起来以附加我们的 GitHub 访问令牌。对于缓存,我们将使用 GraphQLCache
。
import 'package:custom_graphql_flutter/graphql_flutter.dart';
import 'package:hive/hive.dart';
import 'package:path_provider/path_provider.dart';
void main() async {
// 我们将使用 HiveStore 进行持久化,
// 因此我们需要初始化 Hive。
await initHiveForFlutter();
final HttpLink httpLink = HttpLink(
'https://api.github.com/graphql',
);
final AuthLink authLink = AuthLink(
getToken: () async => 'Bearer <YOUR_PERSONAL_ACCESS_TOKEN>',
// 或者
// getToken: () => 'Bearer <YOUR_PERSONAL_ACCESS_TOKEN>',
);
final Link link = authLink.concat(httpLink);
ValueNotifier<GraphQLClient> client = ValueNotifier(
GraphQLClient(
link: link,
// 默认存储是 InMemoryStore,它不会持久化到磁盘
cache: GraphQLCache(store: HiveStore()),
),
);
runApp(MyApp(client: client));
}
GraphQL Provider
为了使用客户端,您的 Query
和 Mutation
小部件必须包装在 GraphQLProvider
小部件中。
建议将 MaterialApp
包裹在 GraphQLProvider
小部件中。
import 'package:flutter/material.dart';
import 'package:custom_graphql_flutter/graphql_flutter.dart';
class MyApp extends StatelessWidget {
final ValueNotifier<GraphQLClient> client;
MyApp({required this.client});
@override
Widget build(BuildContext context) {
return GraphQLProvider(
client: client,
child: MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
),
);
}
}
查询
创建查询就像创建一个多行字符串一样简单:
String readRepositories = """
query ReadRepositories(\$nRepositories: Int!) {
viewer {
repositories(last: \$nRepositories) {
nodes {
id
name
viewerHasStarred
}
}
}
}
""";
在您的小部件中:
import 'package:flutter/material.dart';
import 'package:custom_graphql_flutter/graphql_flutter.dart';
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Query(
options: QueryOptions(
document: gql(readRepositories),
variables: {
'nRepositories': 50,
},
pollInterval: const Duration(seconds: 10),
),
builder: (QueryResult result, { VoidCallback? refetch, FetchMore? fetchMore }) {
if (result.hasException) {
return Text(result.exception.toString());
}
if (result.isLoading) {
return const Text('Loading');
}
List? repositories = result.data?['viewer']?['repositories']?['nodes'];
if (repositories == null) {
return const Text('No repositories');
}
return ListView.builder(
itemCount: repositories.length,
itemBuilder: (context, index) {
final repository = repositories[index];
return Text(repository['name'] ?? '');
},
);
},
);
}
}
或者,如果您更喜欢使用 flutter-hooks
,您可以这样写:
import 'package:flutter/material.dart';
import 'package:custom_graphql_flutter/graphql_flutter.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
class MyHomePage extends HookWidget {
@override
Widget build(BuildContext context) {
final readRepositoriesResult = useQuery(
QueryOptions(
document: gql(readRepositories),
variables: {
'nRepositories': 50,
},
pollInterval: const Duration(seconds: 10),
),
);
final result = readRepositoriesResult.result;
if (result.hasException) {
return Text(result.exception.toString());
}
if (result.isLoading) {
return const Text('Loading');
}
List? repositories = result.data?['viewer']?['repositories']?['nodes'];
if (repositories == null) {
return const Text('No repositories');
}
return ListView.builder(
itemCount: repositories.length,
itemBuilder: (context, index) {
final repository = repositories[index];
return Text(repository['name'] ?? '');
},
);
}
}
分页
您可以使用 fetchMore()
函数在 Query
构建器中执行分页。fetchMore()
函数允许您运行一个新的 GraphQL 操作并将新结果合并到原始结果中。此外,您可以重用原始查询的某些方面,例如查询或变量。
为了使用 fetchMore()
函数,您需要首先定义 FetchMoreOptions
变量用于新的查询。
final Map pageInfo = result.data['search']['pageInfo'];
final String fetchMoreCursor = pageInfo['endCursor'];
FetchMoreOptions opts = FetchMoreOptions(
variables: {'cursor': fetchMoreCursor},
updateQuery: (previousResultData, fetchMoreResultData) {
final List<dynamic> repos = [
...previousResultData['search']['nodes'] as List<dynamic>,
...fetchMoreResultData['search']['nodes'] as List<dynamic>
];
fetchMoreResultData['search']['nodes'] = repos;
return fetchMoreResultData;
},
);
然后,调用 fetchMore()
函数并传递您定义的 FetchMoreOptions
变量。
RaisedButton(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Load More"),
],
),
onPressed: () {
fetchMore(opts);
},
)
突变
同样,首先创建一个突变字符串:
String addStar = """
mutation AddStar(\$starrableId: ID!) {
addStar(input: {starrableId: \$starrableId}) {
starrable {
viewerHasStarred
}
}
}
""";
突变的语法与查询非常相似。唯一的区别是构建函数的第一个参数是一个突变函数。只需调用它即可触发突变。
Mutation(
options: MutationOptions(
document: gql(addStar),
update: (GraphQLDataProxy cache, QueryResult result) {
return cache;
},
onCompleted: (dynamic resultData) {
print(resultData);
},
),
builder: (
RunMutation runMutation,
QueryResult result,
) {
return FloatingActionButton(
onPressed: () => runMutation({
'starrableId': '<A_STARTABLE_REPOSITORY_ID>',
}),
tooltip: 'Star',
child: Icon(Icons.star),
);
},
);
对应的钩子实现如下:
import 'package:flutter/material.dart';
import 'package:custom_graphql_flutter/graphql_flutter.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
class MyHomePage extends HookWidget {
@override
Widget build(BuildContext context) {
final addStarMutation = useMutation(
MutationOptions(
document: gql(addStar),
update: (GraphQLDataProxy cache, QueryResult result) {
return cache;
},
onCompleted: (dynamic resultData) {
print(resultData);
},
),
);
return FloatingActionButton(
onPressed: () => addStarMutation.runMutation({
'starrableId': '<A_STARTABLE_REPOSITORY_ID>',
}),
tooltip: 'Star',
child: Icon(Icons.star),
);
}
}
graphql
还提供了文件上传支持。
乐观突变
通过向 RunMutation
传递 optimisticResult
,GraphQLCache
允许进行乐观突变。它将两次调用 update(GraphQLDataProxy cache, QueryResult result)
(一次急切地使用 optimisticResult
),并重新广播所有查询以使用乐观缓存状态。
有关如何与 update
提供的 proxy
进行接口的完整和注释详尽的说明,可以查看 GraphQLDataProxy
API 文档。
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
toggleStar(
{ 'starrableId': repository['id'] },
optimisticResult: {
'action': {
'starrable': {'viewerHasStarred': !starred}
}
},
);
},
);
}
}
订阅
订阅的语法再次类似于查询,但它利用了 WebSocket 和 dart Streams 来从服务器提供实时更新。
为了使用订阅,订阅消费链接必须从您的 HttpLink
或其他终止链接路由中分离出来:
link = Link.split((request) => request.isSubscription, websocketLink, link);
然后,您可以订阅服务器模式提供的任何 subscription
:
final subscriptionDocument = gql(
r'''
subscription reviewAdded {
reviewAdded {
stars, commentary, episode
}
}
''',
);
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Subscription(
options: SubscriptionOptions(
document: subscriptionDocument,
),
builder: (result) {
if (result.hasException) {
return Text(result.exception.toString());
}
if (result.isLoading) {
return Center(
child: const CircularProgressIndicator(),
);
}
return ResultAccumulator.appendUniqueEntries(
latest: result.data,
builder: (context, {results}) => DisplayReviews(
reviews: results.reversed.toList(),
),
);
}
),
)
);
}
}
对应的钩子实现如下:
import 'package:flutter/material.dart';
import 'package:custom_graphql_flutter/graphql_flutter.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
class MyHomePage extends HooksWidget {
@override
Widget build(BuildContext context) {
final subscriptionResult = useSubscription(
SubscriptionOptions(
document: subscriptionDocument,
),
);
Widget child;
if (subscriptionResult.hasException) {
child = Text(subscriptionResult.exception.toString());
} else if (subscriptionResult.isLoading) {
child = Center(
child: const CircularProgressIndicator(),
);
} else {
child = ResultAccumulator.appendUniqueEntries(
latest: subscriptionResult.data,
builder: (context, {results}) => DisplayReviews(
reviews: results.reversed.toList(),
),
);
}
return Scaffold(
body: Center(child: child)
);
}
}
其他钩子
除了 useMutation
、useQuery
和 useSubscription
,该包还包含以下钩子:
final client = useGraphQLClient(); // 获取当前客户端
final observableQuery = useWatchQuery(WatchQueryOptions(...)); // 监视查询
final mutationObservableQuery = useWatchMutation(WatchQueryOptions(...)); // 监视突变
GraphQL Consumer
如果您想直接使用 client
,比如其直接缓存更新方法,您可以使用 GraphQLConsumer
从任何 GraphQLProvider
下降的 context
中获取它:
import 'package:flutter/material.dart';
import 'package:custom_graphql_flutter/graphql_flutter.dart';
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GraphQLConsumer(
builder: (GraphQLClient client) {
// 对客户端做些什么
return Container(
child: Text('Hello world'),
);
},
);
}
}
代码生成
该包本身不支持代码生成,但 graphql_codegen
做到了!
该包生成钩子和选项,消除了序列化的麻烦,并通过类型安全给您信心。
例如,通过创建 .graphql
文件:
query ReadRepositories($nRepositories: Int!) {
viewer {
repositories(last: $nRepositories) {
nodes {
id
name
}
}
}
}
构建后,您可以通过钩子在代码中查询:
final queryResult = useQueryReadRepositories(
OptionsQueryReadRepositories(
variables: VariablesQueryReadRepositories(
nRepositories: 10
)
)
);
if (queryResult.result.hasException) {
return Text(queryResult.result.exception.toString());
}
if (queryResult.result.isLoading) {
return Text(text: "LOADING");
}
final data = queryResult.result.parsedData;
return Column(
children: data?.viewer.repositories.nodes.map((node) => Text(text: node.name)).toList() ?? [],
);
更多关于Flutter GraphQL请求插件custom_graphql_flutter的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter GraphQL请求插件custom_graphql_flutter的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
在Flutter中使用GraphQL进行网络请求时,custom_graphql_flutter
是一个非常灵活的插件。它允许你自定义GraphQL请求并处理响应。以下是使用 custom_graphql_flutter
插件的详细步骤:
1. 添加依赖
首先,你需要在 pubspec.yaml
文件中添加 custom_graphql_flutter
插件的依赖:
dependencies:
flutter:
sdk: flutter
custom_graphql_flutter: ^1.0.0 # 请使用最新版本
然后运行 flutter pub get
来获取依赖。
2. 初始化 GraphQL Client
在你的应用程序中,你需要初始化一个 GraphQL 客户端。通常,你可以在 main.dart
文件中完成这个步骤。
import 'package:custom_graphql_flutter/custom_graphql_flutter.dart';
import 'package:flutter/material.dart';
void main() {
final HttpLink httpLink = HttpLink(
'https://your-graphql-endpoint.com/graphql',
);
final GraphQLClient client = GraphQLClient(
link: httpLink,
cache: GraphQLCache(),
);
runApp(MyApp(client: client));
}
class MyApp extends StatelessWidget {
final GraphQLClient client;
MyApp({required this.client});
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter GraphQL Demo',
home: GraphQLProvider(
client: client,
child: MyHomePage(),
),
);
}
}
3. 使用 GraphQLProvider
GraphQLProvider
是一个 InheritedWidget
,它允许你在整个应用程序中访问 GraphQL 客户端。你可以在任何需要的地方使用 GraphQLProvider.of(context)
来获取客户端。
4. 发起 GraphQL 请求
你可以使用 Query
或 Mutation
组件来发起 GraphQL 请求。以下是一个使用 Query
组件的示例:
import 'package:custom_graphql_flutter/custom_graphql_flutter.dart';
import 'package:flutter/material.dart';
class MyHomePage extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
final client = GraphQLProvider.of(context).value;
return Scaffold(
appBar: AppBar(
title: Text('GraphQL Demo'),
),
body: Query(
options: QueryOptions(
document: gql('''
query GetPosts {
posts {
id
title
body
}
}
'''),
),
builder: (QueryResult result, { VoidCallback? refetch, FetchMore? fetchMore }) {
if (result.hasException) {
return Text('Error: ${result.exception.toString()}');
}
if (result.isLoading) {
return Center(child: CircularProgressIndicator());
}
final posts = result.data?['posts'] as List<dynamic>?;
return ListView.builder(
itemCount: posts?.length ?? 0,
itemBuilder: (context, index) {
final post = posts?[index];
return ListTile(
title: Text(post['title']),
subtitle: Text(post['body']),
);
},
);
},
),
);
}
}
5. 使用 Mutation
如果你需要执行一个 GraphQL 变更操作,你可以使用 Mutation
组件。以下是一个示例:
import 'package:custom_graphql_flutter/custom_graphql_flutter.dart';
import 'package:flutter/material.dart';
class AddPostPage extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add Post'),
),
body: Mutation(
options: MutationOptions(
document: gql('''
mutation AddPost($title: String!, $body: String!) {
addPost(title: $title, body: $body) {
id
title
body
}
}
'''),
),
builder: (RunMutation runMutation, QueryResult? result) {
return Column(
children: [
TextField(
decoration: InputDecoration(labelText: 'Title'),
onChanged: (value) {
// 处理标题输入
},
),
TextField(
decoration: InputDecoration(labelText: 'Body'),
onChanged: (value) {
// 处理内容输入
},
),
ElevatedButton(
onPressed: () {
runMutation({
'title': 'Example Title',
'body': 'Example Body',
});
},
child: Text('Add Post'),
),
if (result != null && result.hasException)
Text('Error: ${result.exception.toString()}'),
if (result != null && result.isLoading)
Center(child: CircularProgressIndicator()),
if (result != null && result.data != null)
Text('Post added successfully!'),
],
);
},
),
);
}
}
6. 处理错误和加载状态
在 Query
和 Mutation
组件中,你可以通过 QueryResult
对象来处理错误和加载状态。result.hasException
可以用于检查是否有错误发生,result.isLoading
可以用于检查请求是否仍在加载中。
7. 自定义请求
custom_graphql_flutter
允许你自定义请求的各个方面,包括请求头、缓存策略等。你可以通过 HttpLink
或其他 Link
实现来配置这些选项。
final AuthLink authLink = AuthLink(
getToken: () async => 'Bearer $yourToken',
);
final Link link = authLink.concat(httpLink);
final GraphQLClient client = GraphQLClient(
link: link,
cache: GraphQLCache(),
);
8. 缓存管理
GraphQLCache
是默认的缓存实现,你可以通过它来管理缓存。你也可以自定义缓存策略,或者使用其他缓存实现。
9. 使用 Subscription
如果你需要实时数据,你可以使用 Subscription
组件来订阅 GraphQL 数据。
import 'package:custom_graphql_flutter/custom_graphql_flutter.dart';
import 'package:flutter/material.dart';
class RealTimeDataPage extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Real Time Data'),
),
body: Subscription(
options: SubscriptionOptions(
document: gql('''
subscription OnPostAdded {
postAdded {
id
title
body
}
}
'''),
),
builder: (QueryResult result) {
if (result.hasException) {
return Text('Error: ${result.exception.toString()}');
}
if (result.isLoading) {
return Center(child: CircularProgressIndicator());
}
final post = result.data?['postAdded'];
return ListTile(
title: Text(post['title']),
subtitle: Text(post['body']),
);
},
),
);
}
}