首次使用GraphQL,如何在Flutter中集成提供的新闻API?
Flutter 对接 GraphQL 新闻接口实操指南
第一次接触GraphQL不用先啃完所有原理,先把接口跑通再慢慢补概念就行。你拿到的是Hasura引擎提供的GraphQL接口,鉴权靠请求头里的密钥,给定的查询语句就是要拉取新闻表的9个字段,按下面步骤走就能完成对接:
1. 添加项目依赖
打开项目根目录的pubspec.yaml,在dependencies下加入稳定版GraphQL客户端包,执行flutter pub get安装:
dependencies: flutter: sdk: flutter graphql_flutter: ^5.1.2
2. 初始化GraphQL客户端
和普通HTTP请求要配baseUrl、请求头逻辑一致,GraphQL客户端需要先配置接口地址和鉴权信息,注意把代码里的接口地址、密钥替换成给你的真实值:
import 'package:flutter/material.dart'; import 'package:graphql_flutter/graphql_flutter.dart'; void main() async { // 初始化本地缓存,不写这行会启动报错 await initHiveForFlutter(); runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { // 配置请求链接和鉴权头 final HttpLink httpLink = HttpLink( '替换成你拿到的Hasura接口完整地址,一般后缀为/v1/graphql', defaultHeaders: { "x-hasura-admin-secret": "替换成给你的真实secret值" }, ); // 初始化客户端 final ValueNotifier<GraphQLClient> client = ValueNotifier( GraphQLClient( link: httpLink, cache: GraphQLCache(store: HiveStore()), ), ); // 把客户端注入到整个应用上下文 return GraphQLProvider( client: client, child: MaterialApp( title: '新闻应用', theme: ThemeData(primarySwatch: Colors.blue), home: const NewsListPage(), ), ); } }
3. 编写查询逻辑渲染新闻列表
把给你的查询语句定义成字符串常量,用库提供的Query组件直接处理加载、报错、数据渲染三种状态,不用自己手写底层请求逻辑:
// 定义查询语句,和给你的内容完全一致就行,别改字段大小写 const String fetchNewsQuery = """ query getNews { news { title url publishedAt author content description h_id source urlToImage } } """; class NewsListPage extends StatelessWidget { const NewsListPage({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('新闻列表')), body: Query( options: QueryOptions(document: gql(fetchNewsQuery)), builder: (QueryResult result, {VoidCallback? refetch, FetchMore? fetchMore}) { // 加载中状态 if (result.isLoading) { return const Center(child: CircularProgressIndicator()); } // 请求报错状态 if (result.hasException) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: Text('加载失败:${result.exception.toString()}'), ), const SizedBox(height: 12), ElevatedButton( onPressed: refetch, child: const Text('重试'), ) ], ), ); } // 解析返回数据 final List newsData = result.data?['news'] ?? []; if (newsData.isEmpty) { return const Center(child: Text('暂无新闻内容')); } // 渲染列表 return RefreshIndicator( onRefresh: () async => refetch?.call(), child: ListView.builder( padding: const EdgeInsets.all(8), itemCount: newsData.length, itemBuilder: (context, index) { final singleNews = newsData[index]; return Card( margin: const EdgeInsets.only(bottom: 8), child: ListTile( contentPadding: const EdgeInsets.all(12), leading: singleNews['urlToImage'] != null ? Image.network( singleNews['urlToImage'], width: 90, fit: BoxFit.cover, errorBuilder: (_, __, ___) => const SizedBox(width: 90, child: Icon(Icons.broken_image)), ) : const SizedBox(width: 90, child: Icon(Icons.newspaper)), title: Text( singleNews['title'] ?? '无标题', maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle(fontWeight: FontWeight.w500), ), subtitle: Padding( padding: const EdgeInsets.only(top: 8), child: Text( '${singleNews['author'] ?? '未知作者'} · ${singleNews['publishedAt'] ?? ''}', maxLines: 1, overflow: TextOverflow.ellipsis, ), ), onTap: () { // 这里加跳新闻详情、打开原链接的逻辑即可 }, ), ); }, ), ); }, ), ); } }
实际开发注意点
- 不要把
x-hasura-admin-secret硬编码在客户端代码里,这个密钥拥有整个数据库的操作权限,正式上线必须换成后端签发的JWT做用户级鉴权,不然客户端被反编译后数据会被篡改 - Hasura的GraphQL接口固定后缀是
/v1/graphql,不要填成控制台的网页地址 - GraphQL对字段大小写敏感,查询语句里的字段名要和给你的完全一致,写错会直接报字段不存在的错误
- 调试时如果报跨域错误,找后端配置Hasura的CORS规则,允许你本地调试IP和正式上线的域名即可
- 要做下拉刷新直接调用
refetch方法就行,不用重新初始化请求
内容的提问来源于stack exchange,提问作者Chirag Maurya
相关产品推荐
相关产品推荐

