json阅读器_Flutter小说阅读器系列一:使用Bloc模式获取起点小说关键字提示

0c8e81182d7c6daba1bf388ef92d23fa.png

6ecbe07ec13c410151d2fa88f7a91f50.gif
Bloc模式下的小说关键字提示效果图

最近难得有些闲暇时间,所以我又打算做一个小说阅读器,以前倒是用RN+Golang写了一个,不过当时太过放飞自我导致自己看起来都很费力,这次我准备换成Flutter试一下。

先简单将小说阅读器分为以下几个部分:

  1. 书架
  2. 书库
  3. 搜索
  4. 阅读
  5. 缓存

其中书架和书库最容易却也最繁琐,所以将这两个放到后面,第一步的话先实现搜索中的关键字提示功能。

bfaaa956666eb70e24d48dd2c6a45606.png
百度关键字提示

b6a18d6ed33abf8156cd2cb279579191.png
起点关键字提示

对于小说关键字提示的数据来源,最开始的时候我选择的是百度,但通过对比后发现起点的关键字提示似乎更合适,通过上面的两张图片可以明显的看出来。

一:获取起点小说关键字接口并实现对应的Suggestion类

确定选择起点,那就只需要把起点的关键字提示接口扒出来,起点关键字提示接口如下:

链接:https://www.qidian.com/ajax/Search/AutoComplete
参数:query
方法:get
示例:https://www.qidian.com/ajax/Search/AutoComplete?query=惊悚

示例链接返回结果如下(因为JSON太长删掉了一部分):

{"query": "Unit","suggestions": [{"data": {"category": "书名"},"value": "惊悚乐园"}, {"data": {"category": "书名"},"value": "惊悚乐园:夜鸦"}],"code": 0
}

知道起点关键字接口的JSON数据格式后,我们就可以针对的写一个Suggestion类用于解析这串JSON数据,当然我建议直接通过在线转换工具进行转换。

/// 保存为 suggestion_model.dartclass Suggestion {String query;List<Suggestions> suggestions;int code;Suggestion({this.query, this.suggestions, this.code});Suggestion.fromJson(Map<String, dynamic> json) {query = json['query'];if (json['suggestions'] != null) {suggestions = new List<Suggestions>();json['suggestions'].forEach((v) {suggestions.add(new Suggestions.fromJson(v));});}code = json['code'];}Map<String, dynamic> toJson() {final Map<String, dynamic> data = new Map<String, dynamic>();data['query'] = this.query;if (this.suggestions != null) {data['suggestions'] = this.suggestions.map((v) => v.toJson()).toList();}data['code'] = this.code;return data;}
}class Suggestions {Data data;String value;Suggestions({this.data, this.value});Suggestions.fromJson(Map<String, dynamic> json) {data = json['data'] != null ? new Data.fromJson(json['data']) : null;value = json['value'];}Map<String, dynamic> toJson() {final Map<String, dynamic> data = new Map<String, dynamic>();if (this.data != null) {data['data'] = this.data.toJson();}data['value'] = this.value;return data;}
}class Data {String category;Data({this.category});Data.fromJson(Map<String, dynamic> json) {category = json['category'];}Map<String, dynamic> toJson() {final Map<String, dynamic> data = new Map<String, dynamic>();data['category'] = this.category;return data;}
}

上面的Suggestion模型用于解析获取到的JSON数据,接下来我们只需要实现一个用于发起请求获取数据的函数就行了,这里用到了官方的http库:dart-lang/http

/// 保存为 suggestion_api.dartimport 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;import './suggestion_model.dart';class Api {/// 关键字提示(起点)Future<List<String>> suggestion(String query) async {http.Response response = await http.get("https://www.qidian.com/ajax/Search/AutoComplete?siteid=1&query=$query");var data = Suggestion.fromJson(json.decode(response.body));List<String> suggestion = [];data.suggestions.forEach((k) {suggestion.add(k.value);});return suggestion;}
}Api api = Api();

为了演示方便所以只有一个简单的异步函数,获取到结果后返回一个包含了与关键字相关的小说列表。

正常情况下应该要将这一类的请求封装好,并与业务逻辑层分离开来。

对于一些会消耗大量资源可能会造成主线程卡顿的数据请求,比如缓存大量章节时就不要简单的使用异步了,这时应该要用isolate对数据进行处理。

二:使用Bloc模式实现业务逻辑

关于Flutter上的状态管理我就不多说了,各种各样的包都有,这里我使用的是所有Bloc包中star最多的包:felangel/bloc

选择好bloc包之后,现在可以开始实现业务逻辑中的event与state,因为只需要实现关键词提示,所以其中的事件暂时只需要一个就行。

首先要实现的是SuggestionEvent,思路与代码如下。

  • SuggestionFetch:获取关键字提示的事件,其中参数 [query] 用于接收传递过来的字符串
/// 保存为 suggestion_event.dartabstract class SuggestionEvent {}class SuggestionFetch extends SuggestionEvent {final String query;SuggestionFetch({this.query});@overrideString toString() => '获取关键字提示事件';
}

实现了SuggestionEvent之后再把SuggestionState实现,思路与代码如下。

  • SuggestionUninitialized:还未初始化时返回的状态。
  • SuggestionLoading:表示当前正处于加载中的状态。
  • SuggestionLoaded:获取关键字成功后返回的状态。
  • SuggestionError:获取关键字出错时返回的状态。
/// 保存为 suggestion_state.dartabstract class SuggestionState {}class SuggestionError extends SuggestionState {@overrideString toString() => 'SuggestionError:获取失败';
}class SuggestionUninitialized extends SuggestionState {@overrideString toString() => 'SuggestionUninitialized:未初始化';
}class SuggestionLoading extends SuggestionState {@overrideString toString() => 'SuggestionLoading :正在加载';
}class SuggestionLoaded extends SuggestionState {final List<String> res;SuggestionLoaded({this.res,});@overrideString toString() => 'SuggestionLoaded:加载完毕';
}

顺带一提,之所以要重写toString,仅仅是为了后续能够看到字符串提示,你也可以不重写。

现在已经实现了一个负责事件的SuggestionEvent和一个负责状态的SuggestionState,接下来就可以实现负责管理事件与状态的SuggestionBloc了。

先说下要注意的两点:

  1. initialState:也就是说未接收任何事件时返回的初始状态,上面写SuggestionState时已经添加了一个状态SuggestionUninitialized表示还未初始化,这样就可以针对不同的状态改变小部件的布局,比如处于未初始化状态时可以放一个加载指示器在屏幕上。
  2. mapEventToState:只要接收到事件就会触发,因为写SuggestionEvent的时候只写了SuggestionFetch这一个事件,因此只需要针对SuggestionFetch进行处理就行。当触发SuggestionFetch事件之后返回一个SuggestionLoading状态表示正在加载中,然后就开始获取关键字提示列表,获取成功则返回一个包含结果列表的SuggestionLoaded状态,否则返回一个表示错误的SuggestionError状态。
/// 保存为 suggestion_bloc.dartimport 'dart:async';
import 'package:bloc/bloc.dart';
import './suggestion_api.dart';
import './suggestion_event.dart';
import './suggestion_state.dart';class SuggestionBloc extends Bloc<SuggestionEvent, SuggestionState> {@overrideSuggestionState get initialState => SuggestionUninitialized();@overrideStream<SuggestionState> mapEventToState(SuggestionState currentState,SuggestionEvent event,) async* {if (event is SuggestionFetch) {try {yield SuggestionLoading();final res = await api.suggestion(event.query);yield SuggestionLoaded(res: res);} catch (_) {yield SuggestionError();}}}
}

到此,业务逻辑层都已经实现,接下来只需要实现一下界面就可以了。

// 替换 main.dartimport 'package:flutter/material.dart';
import 'package:bloc/bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import './suggestion_bloc.dart';
import './suggestion_event.dart';
import './suggestion_state.dart';void main() {runApp(App());
}class App extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(title: '关键字提示',home: Scaffold(appBar: AppBar(title: Text('关键字提示'),),body: AppHome(),),);}
}class AppHome extends StatefulWidget {@override_AppHomeState createState() => _AppHomeState();
}class _AppHomeState extends State<AppHome> {final SuggestionBloc _suggestion = SuggestionBloc();@overrideWidget build(BuildContext context) {return Material(child: Column(children: [TextField(autofocus: true,textAlign: TextAlign.center,onSubmitted: (text) {_suggestion.dispatch(SuggestionFetch(query: text));},),Expanded(child: BlocBuilder(bloc: _suggestion,builder: (BuildContext context, SuggestionState state) {if (state is SuggestionUninitialized) {return Center(child: Text('暂无内容'),);} else if (state is SuggestionLoading) {return Center(child: CircularProgressIndicator(),);} else if (state is SuggestionError) {return Center(child: Text('出现错误'),);} else if (state is SuggestionLoaded) {return ListView.builder(itemBuilder: (BuildContext context, int index) {return ListTile(title: Text(state.res[index]));},itemCount: state.res.length,);}},),),],),);}@overridevoid dispose() {_suggestion.dispose();super.dispose();}
}

界面很简单,就是一个文本输入框加上一个用于展示的列表,下面是效果图

6ecbe07ec13c410151d2fa88f7a91f50.gif
Bloc模式下的小说关键字提示效果图

上面的效果只是用于演示,搭配官方的搜索页面效果更好,只是懒得去弄了,后续的抽时间在写。

顺带一提,上面的目录结构只是为了演示,实际使用bloc模式的过程中最好规划好文件层次,比如使用如下目录结构:

libmain.dartblocs/suggestion/suggestion_bloc.dart

2019年3月23日 第二篇已更新

路过的冒险者:Flutter小说阅读器系列二:使用Bloc模式实现小说搜索的基本功能(略微有点长)​zhuanlan.zhihu.com
511695bfcaa7b4367d74b3512e5ba8bf.png

2019年4月6日 添加

我写文章的时候bloc版本还是0.10.0,这个版本的mapEventToState是有两个参数的,目前从bloc0.11.0开始只有一个参数了,因此只需要传入事件就行。

Stream<S> mapEventToState(S currentState, E event) -> Stream<S> mapEventToState(E event)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/559444.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python获取mysql中的数据供js调用_详解js文件通过python访问数据库方法

我来教你 js文件怎么通过python访问数据库&#xff0c;希望能够为你带来帮助。 1、如果是要提交表单内容给 服务器的 python 处理&#xff0c;那么只需要在表单 里面的 action 填写服务端的处理路由就可以了。或者用 指向服务器路由get querystring。前端方面不需要python啊。…

python 初始化一个4维向量_看图学NumPy:掌握n维数组基础知识点,看这一篇就够了...

摘要:NumPy是Python的最重要的扩展程序库之一,也是入门机器学习编程的必备工具。国外有位程序员讲NumPy的基本运算以图解的方式写下来,让学习过程变得轻松有趣。 NumPy是Python的最重要的扩展程序库之一,也是入门机器学习编程的必备工具。然而对初学者来说,NumPy的大量运算…

win8 mysql6_Win8系统 MySQL 6.0 安装图解

Win8系统 MySQL 6.0 安装图解(作者&#xff1a;wanghui2015/04/08)1、首先双击mysql-6.0.10-msi&#xff0c;会出现如下图所示界面。点击next&#xff1b;2、出现如下界面&#xff1a;这三个为选择安装类型&#xff0c;新手可以选择Typical或Complete就可以了。咱们为了看一下安…

python画锯齿波_用Python控制硬件35-自制二三十元成本的信号测量采集控制系统

如前篇所介绍&#xff0c;用Shell Lab测试台软件配合之前介绍的任意款实验板&#xff0c;都能方便地实现ADC电压测量&#xff0c;但遇到两个问题&#xff1a;示例代码虽然众多&#xff0c;但大都默认ShellLab类型的控制器&#xff0c;需要手动改为Mcush.Mcush类型才能运行&…

java连接hbase_HBase实战 | 05405.15.0Spark2使用HBaseSpark访问HBase

转载自微信公众号Hadoop实操温馨提示&#xff1a;如果使用电脑查看图片不清晰&#xff0c;可以使用手机打开文章单击文中的图片放大查看高清原图。Fayson的github&#xff1a;https://github.com/fayson/cdhproject提示&#xff1a;代码块部分可以左右滑动查看噢1文章编写目的越…

wxpython使用简介_wxpython简介

Python​ Python是一种成功的脚本语言&#xff0c;它最初是由Guido van Rossum开发的。它于1991年首次发布。Python的灵感来自于ABC和Haskell编程语言。Python是一种高级的、通用的、多平台的、解释型的语言。有人更喜欢称它为动态语言。它很容易学习。Python是一种极简主义语言…

电脑4次连续故障音_格力空调电子膨胀阀故障判定与“E6”处理方法

一、电子膨胀阀常见故障原因电器电子膨胀阀是一种利用电子控制器通过电缆向线圈发出脉冲控制信号&#xff0c;控制施加于膨胀阀上的电压或电流&#xff0c;从而控制阀针的动作实现阀口流通面积改变达到流量自动调节目的的节流器件&#xff0c;是空调器的重要部件之一&#xff0…

mysql+性能+计算列_MYSQL性能优化的最佳20+条经验

当你查询表的有些时候&#xff0c;你已经知道结果只会有一条结果&#xff0c;但因为你可能需要去fetch游标&#xff0c;或是你也许会去检查返回的记录数。在这种情况下&#xff0c;加上 LIMIT 1 可以增加性能。这样一样&#xff0c;MySQL数据库引擎会在找到一条数据后停止搜索&…

ClickHouse 入门与实战教程

目录 1. ClickHouse 简介 什么是 ClickHouse&#xff1f; ClickHouse 的优势和特点 适用场景 2. 安装 ClickHouse 3. ClickHouse 的基本概念 4. ClickHouse 的基本操作 创建数据库和表、插入和查询数据 使用 MergeTree 引擎处理时序数据 管理分区 创建带有分区的 Mer…

sid图像数据_实战材料信息学:使用skimage处理扫描电子显微镜(SEM)图像数据

算开个头&#xff0c;不算特别实战扫描电子显微镜&#xff0c;材料开发不可缺少的分析仪器。原理就不详述了。材料的形貌&#xff0c;具体长什么样的都可以看得很清楚。材料表面的形貌很多情况下会和材料的物性产生直接关系&#xff0c;比如说可以从SEM看出粒子的直径分布&…

arcgis制作空间变化图怎么做_听说如果做数据分析不用GIS,会被开?

Q你有没有遇到这样的情况&#xff1f;身边的同事或同学经常说什么高程图、坡向图、坡度图、生态水文图、热力密度图、大数据算法&#xff0c;而自己却一无所知&#xff0c;心里感到特别的慌啊。文末附ArcGIS最新版及超强数据神器 很多人会说&#xff0c;“设计师是感性的&#…

仅完成部分的readprocessmemory或write_王者荣耀:三种李小龙获取方式!无需完成任务、28号即可兑换...

哈喽大家好&#xff01;我是你们的小菜鸡“荣耀急先锋”王者荣耀五周年庆限定皮肤李小龙活动已经开始啦&#xff01;大家有参加了吗&#xff1f;或者说对这个活动不太理解&#xff0c;针对这些小伙伴菜鸡今天就简单的跟大家讲讲李小龙皮肤活动的规则&#xff0c;让我们一起看看…

优秀ppt作品范例_首次公开|锐普的内部PPT配色库

每次发锐普的作品&#xff0c;总会有小伙伴们问&#xff1a;这么漂亮的配色到底是怎么做的呢&#xff1f;今天我在锐普内部的资源库发现一个超级宝贝——锐普内部的PPT配色库&#xff0c;1008张超精彩PPT配色范例。我的天&#xff0c;原来设计部竟然还藏着这种宝贝&#xff01;…

linux rz sz 安装_Windows与Linux文件传输之lrzsz工具

请关注本头条号&#xff0c;每天坚持更新原创干货技术文章如需学习视频&#xff0c;请查看本头条号简介&#xff0c;免费在线观看学习视频1. lrzsz工具简介Windows向Linux传输文件&#xff0c;最简单的方法是使用rz和sz命令。当然用有其他的方法&#xff0c;比如通过ssh(winscp…

java游戏重新开始_问题1:java问题--某局小游戏结束后怎么重新开始?

有个问题&#xff0c;想了好久没解决&#xff0c;上网找了下&#xff0c;貌似也没找到好的解决方法(谷歌暂时没上去)&#xff0c;现在这里写下来吧&#xff1a;最近在写一个五子棋的小游戏&#xff0c;也算是刚刚开始java入门的一个作品了吧&#xff0c;刚刚完成到人人对战(当然…

lua pcall 返回值_Redis和Lua整合

前面我们已经介绍完了Redis相关的理论知识&#xff0c;从本篇开始我们介绍一些Redis相关的时机应用。比如&#xff1a;Redis整合LuaRedis的消息模式Redis实现分布式锁常见的缓存问题我们先从Redis整合Lua的使用开始。lua是一种轻量小巧的脚本语言&#xff0c;用标准C语言编写并…

java 异步调用webapi_Async Await异步调用WebApi

先铺垫一些基础知识 在 .net 4.5中出现了 Async Await关键字&#xff0c;配合之前版本的Task 来使得开发异步程序更为简单易控。 在使用它们之前 我们先关心下 为什么要使用它们。好比 一个人做几件事&#xff0c;那他得一件一件的做完&#xff0c;而如果添加几个人手一起帮着…

百度相关搜索软件_不太热门的办公神器软件篇搜索相关

这次介绍两个windows平台超好用的搜索神器——listary和everything全文阅读约需3分钟在工作中&#xff0c;虽然大部分文件都会分门别类地放在对应的文件夹中&#xff0c;但总有一些放完就忘了在哪或是层级过深打开不便的情况。这两个搜索神器完美地解决了找不到文件且windows自…

mysql 回滚段_MySQL和PostgreSQL比较

1、MySQL相对来说比较年轻&#xff0c;首度出现在1994年。它声称自己是最流行的开源数据库。MySQL就是LAMP(用于Web开发的软件包&#xff0c;包括 Linux、Apache及Perl/PHP/Python)中的M。构建在LAMP栈之上的大多数应用都会使用MySQL&#xff0c;包括那些知名的应用&#xff0c…

dbscan java_DBSCAN算法的Java,C++,Python实现

最近由于要实现‘基于网格的DBSCAN算法’&#xff0c;网上有没有找到现成的代码[如果您有代码&#xff0c;麻烦联系我]&#xff0c;只好参考已有的DBSCAN算法的实现。先从网上随便找了几篇放这儿&#xff0c;之后对比研究。DBSCAN简介&#xff1a;1.简介DBSCAN 算法是一种基于密…