Flutter漫画阅读插件mangadex_library的使用
Flutter漫画阅读插件mangadex_library的使用
警告!正在进行中的库
该库目前处于开发中和逐步更改的状态,因此不包含许多功能,并且经常添加新功能和修复bug。当前状态下,该库能够实现以下功能:
- 获取登录、刷新和会话令牌
- 注销用户
- 搜索漫画
- 获取漫画缩略图/封面
- 获取漫画章节
- 获取漫画页面
- 获取已登录用户数据
- 获取用户详细信息
- 获取作者详细信息
- 管理自定义列表
- 关注/取消关注漫画/组/用户
- 设置/获取已登录用户的漫画阅读状态
- 获取、创建和删除扫描组
2.0.0版本的重大变更
mangadex_library
之前只是一组静态函数,虽然工作正常,但存在一些问题:
- 单个库文件变成了大量静态函数的堆栈,难以理解和维护。
- 刷新令牌需要用户手动处理,库只提供了刷新函数。
为了解决这些问题,引入了以下重大变更:
- 库现在更依赖于客户端实例而不是静态函数。你可以创建一个
MangadexClient
实例,使用login
函数后,它会自动每14分钟刷新一次令牌(可以根据需要更改)。还有一个onrefresh
回调函数,在成功刷新令牌时执行自定义任务。 - 所有函数都放在各自的仓库中,以便未来更容易添加更多功能。
toJson()模型方法不稳定
所有JSON模型类的 toJson()
方法不稳定,因为MangaDex在对象为空时返回的是空列表 []
而不是空对象 {}
。例如,如果描述为空,则返回一个空列表而不是空对象。
快速入门
以下是一个快速演示API的示例代码:
import 'package:mangadex_library/mangadex_client.dart';
import 'package:mangadex_library/mangadex_server_exception.dart';
import 'package:mangadex_library/src/client_types/personal_client.dart';
void main() {
printFilenames();
}
void printFilenames() async {
const clientId = 'YOUR_CLIENT_ID'; // 替换为你的Client ID
const clientSecret = 'YOUR_CLIENT_SECRET'; // 替换为你的Client Secret
// 请参考MangaDex官方文档获取Client ID和Client Secret
final client = MangadexPersonalClient(clientId: clientId, clientSecret: clientSecret);
var username = 'USERNAME'; // 替换为你的用户名
var password = 'PASSWORD'; // 替换为你的密码
try {
// 登录并设置登录令牌
await client.login(username, password);
// 搜索漫画,这里以 "oregairu" 为例
var searchData = await client.search(query: 'oregairu');
// 获取第一个搜索结果的漫画ID
var mangaID = searchData.data![0].id;
// 获取该漫画的所有章节
var chapterData = await client.getChapters(mangaID!);
// 获取第一个章节的ID
var chapterID = chapterData.data![0].id;
// 获取章节的基础URL
var baseUrl = await client.getBaseUrl(chapterID!);
// 打印该章节所有页面的URL
baseUrl.chapter!.dataSaver!.forEach((filename) {
print(client.constructPageUrl(baseUrl.baseUrl!, true, baseUrl.chapter!.hash!, filename));
});
} on MangadexServerException catch (e) {
e.info.errors!.forEach((error) {
print(error.title); // 打印错误标题
print(error.detail); // 打印错误详情
});
}
// 释放客户端资源,因为刷新定时器仍在运行
client.dispose();
}
文档
文档正在使用wiki编写,计划提供详细的文档,因此需要一些时间。目前,生成的HTML文档可以在这里找到:doc/api/
文件夹。
完整示例Demo
为了帮助你更好地理解如何使用 mangadex_library
,以下是一个完整的Flutter应用示例,展示了如何集成该库来搜索漫画并显示其章节页面。
import 'package:flutter/material.dart';
import 'package:mangadex_library/mangadex_client.dart';
import 'package:mangadex_library/mangadex_server_exception.dart';
import 'package:mangadex_library/src/client_types/personal_client.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'MangaDex Reader',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MangaSearchPage(),
);
}
}
class MangaSearchPage extends StatefulWidget {
[@override](/user/override)
_MangaSearchPageState createState() => _MangaSearchPageState();
}
class _MangaSearchPageState extends State<MangaSearchPage> {
final TextEditingController _searchController = TextEditingController();
List<String> _mangaTitles = [];
String? _selectedMangaId;
final client = MangadexPersonalClient(
clientId: 'YOUR_CLIENT_ID', // 替换为你的Client ID
clientSecret: 'YOUR_CLIENT_SECRET', // 替换为你的Client Secret
);
Future<void> _searchManga(String query) async {
try {
var searchData = await client.search(query: query);
setState(() {
_mangaTitles = searchData.data?.map((e) => e.attributes.title).toList() ?? [];
_selectedMangaId = null;
});
} on MangadexServerException catch (e) {
print('Error: ${e.info.errors?.first.title}');
}
}
Future<void> _selectManga(String mangaId) async {
try {
var chapterData = await client.getChapters(mangaId);
var chapterID = chapterData.data?[0].id;
var baseUrl = await client.getBaseUrl(chapterID!);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChapterPagesPage(
baseUrl: baseUrl,
chapterHash: baseUrl.chapter!.hash!,
pageUrls: baseUrl.chapter!.dataSaver!.map((filename) {
return client.constructPageUrl(baseUrl.baseUrl!, true, baseUrl.chapter!.hash!, filename);
}).toList(),
),
),
);
} on MangadexServerException catch (e) {
print('Error: ${e.info.errors?.first.title}');
}
}
[@override](/user/override)
void dispose() {
client.dispose();
super.dispose();
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('MangaDex Reader'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: _searchController,
decoration: InputDecoration(
labelText: 'Search for a manga',
suffixIcon: IconButton(
icon: Icon(Icons.search),
onPressed: () {
_searchManga(_searchController.text);
},
),
),
),
SizedBox(height: 16),
Expanded(
child: ListView.builder(
itemCount: _mangaTitles.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(_mangaTitles[index]),
onTap: () {
_selectManga(searchData.data![index].id!);
},
);
},
),
),
],
),
),
);
}
}
class ChapterPagesPage extends StatelessWidget {
final BaseUrl baseUrl;
final String chapterHash;
final List<String> pageUrls;
ChapterPagesPage({
required this.baseUrl,
required this.chapterHash,
required this.pageUrls,
});
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Chapter Pages'),
),
body: PageView.builder(
itemCount: pageUrls.length,
itemBuilder: (context, index) {
return Image.network(pageUrls[index]);
},
),
);
}
}
更多关于Flutter漫画阅读插件mangadex_library的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter漫画阅读插件mangadex_library的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中集成并使用mangadex_library
插件的一个基本示例。这个示例将展示如何初始化插件、获取漫画列表以及显示漫画详情。请注意,mangadex_library
插件是一个假设的插件名称,实际上可能需要根据真实的插件名称和API进行调整。
首先,确保你的Flutter项目中已经添加了mangadex_library
插件。在pubspec.yaml
文件中添加以下依赖项(假设插件的真实名称就是mangadex_library
):
dependencies:
flutter:
sdk: flutter
mangadex_library: ^latest_version # 替换为实际的最新版本号
然后,运行flutter pub get
来安装依赖项。
接下来,在你的Flutter应用中,你可以按照以下步骤使用mangadex_library
插件:
- 初始化插件并获取漫画列表
import 'package:flutter/material.dart';
import 'package:mangadex_library/mangadex_library.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
List<Manga> mangaList = [];
MangaDexClient? client;
@override
void initState() {
super.initState();
initMangaDexClient();
}
void initMangaDexClient() async {
// 初始化MangaDexClient,这里假设需要一些初始化参数,如API密钥等
client = MangaDexClient(apiKey: 'your_api_key_here');
// 获取漫画列表
try {
var response = await client!.getLatestManga();
if (response.success) {
setState(() {
mangaList = response.data;
});
} else {
print('Failed to fetch manga list: ${response.message}');
}
} catch (e) {
print('Error initializing MangaDexClient: $e');
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'MangaDex Reader',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('MangaDex Reader'),
),
body: mangaList.isEmpty
? Center(child: CircularProgressIndicator())
: ListView.builder(
itemCount: mangaList.length,
itemBuilder: (context, index) {
var manga = mangaList[index];
return ListTile(
title: Text(manga.title),
subtitle: Text(manga.author),
onTap: () {
// 导航到漫画详情页面
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MangaDetailPage(manga: manga)),
);
},
);
}),
),
);
}
}
// 假设MangaDexClient和相关的数据模型如下
class MangaDexClient {
String apiKey;
MangaDexClient({required this.apiKey});
Future<MangaResponse> getLatestManga() async {
// 这里应该是实际的网络请求代码,使用Dart的HttpClient或其他网络库
// 由于这是一个示例,我们直接返回一个模拟的响应
return MangaResponse(
success: true,
data: [
Manga(title: 'Comic 1', author: 'Author 1'),
Manga(title: 'Comic 2', author: 'Author 2'),
// ...更多漫画
],
);
}
}
class MangaResponse {
bool success;
List<Manga> data;
MangaResponse({required this.success, required this.data});
}
class Manga {
String title;
String author;
Manga({required this.title, required this.author});
}
// 漫画详情页面
class MangaDetailPage extends StatelessWidget {
final Manga manga;
MangaDetailPage({required this.manga});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(manga.title),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Title: ${manga.title}'),
Text('Author: ${manga.author}'),
// ...更多详情
],
),
),
);
}
}
这个示例展示了如何初始化MangaDexClient
,获取最新的漫画列表,并在列表项被点击时导航到漫画详情页面。请注意,MangaDexClient
和相关的数据模型(如MangaResponse
和Manga
)是假设的,你需要根据实际的插件API和返回的数据结构进行调整。
此外,实际的网络请求部分需要使用Dart的HttpClient
或其他网络库来实现,这里为了简化示例而直接返回了一个模拟的响应。你需要根据插件提供的API文档来实现实际的网络请求和数据处理逻辑。