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的大量运算…

fasterrcnn tensorflow代码详解_pytorch目标检测代码的一些bug调试

这几天一直在做调包侠&#xff0c;是时候来总结总结了。记录一些我所遇到的不常见的问题。faster rcnn:参考代码&#xff1a;jwyang/faster-rcnn.pytorch​github.compytorch代码调试&#xff0c;相较于tensorflow的版本要友好一些&#xff0c;不用创建软连接啥的&#xff0c;数…

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来比较文件夹并提取差异部分

在客户那边建库&#xff0c;需要把几百个G几十万个文件导入到Oracle里&#xff0c;好不容易导完了才发现中间缺了好几大块数据&#xff0c;约有四分之一吧&#xff0c;郁闷得很。 数据是客户从第三方买的&#xff0c;据客户分析是拷贝过来的时候有部分数据漏掉了&#xff0c;但…

mysql查询缓存到redis_php查询mysql并缓存到redis

首先安装redis&#xff0c;并在php环境中开启php_redis扩展。下面不多说了&#xff0c;直接上代码$redis new redis();$redis->connect(127.0.0.1, 6379);$blog $redis->get(redisrow);//如果$blog数组为空&#xff0c;则去数据库中查询&#xff0c;并加入到redis中if(…

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文章编写目的越…

python归并排序算法实现_python算法实现系列-归并排序

效率比较高的归并排序&#xff0c; 有两种方式。1.使用递归def merge(left, right):result []i,j 0, 0while i < len(left) and j < len(right):if left[i] < right[j]:result.append(left[i])i 1else:result.append(right[j])j 1result left[i:]result right[j…

python编程教程交互式联系_Python Koans交互式教程

Python Koans是一个学习Python编程语言的交互式教程&#xff0c;通过解决当中的问题来更深刻地理解Python。本系列文章为在此项目的学习中的问题解决方案及思考Lesson 2 String字符串是 Python 中最常用的数据类型。我们可以使用引号’或”来创建字符串。about_strings.py#!/us…

wxpython使用简介_wxpython简介

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

计算机科学与python编程导论_计算机科学入门和使用Python编程

你将学到什么A Notion of computationThe Python programming languageSome simple algorithmsTesting and debuggingAn informal introduction to algorithmic complexityData structures课程概况This course is the first of a two-course sequence: Introduction to Compute…

电脑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…

python脚本自动消除安卓版_python脚本中appium的自启动自关闭

前提&#xff1a;已安装appium命令行版本将appium的启动及其关闭直接写在脚本中&#xff0c;运行起来会方便很多创建startAppiumServer.bat 和 stopAppiumServer.bat文件&#xff0c;然后文件存放在运行的包下面注意点&#xff1a;调用startAppiumServer.bat 和stopAppiumServe…

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

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

python生成器 图片分类_Python内置类型(6)——生成器

上节内容说到Python的for语句循环本质上就是通过调用Iterable可迭代对象的__iter()__方法获得一个Iterator迭代器对象,然后不断调用Iterator迭代器对象__next()__方法实现的。Iterator迭代器对象则是一个需要实现__iter__()和__next__()两个迭代器协议方法的对象。python中生成…

java 把方法当参数传递_java 中 如何将“一个类的方法 ”作为参数传到“另一个类的方法”中...

展开全部在java中这是没有办法做到的&#xff0c;因e5a48de588b662616964757a686964616f31333337396336为java不是函数式编程语言&#xff0c;不过可以通过传递一个接口来实现。下面解说java如何模拟&#xff1a;先定义一个接口:interface MethodInterface{//这个方法假如就是你…

轮询数据库 java_谁做过定时任务,轮询查询数据。

一、增加所依赖的JAR包1、增加Spring的Maven依赖org.springframeworkspring-webmvc3.0.5.RELEASE2、增加Quartz的Maven依赖org.quartz-schedulerquartz1.8.4二、增加定时业务逻辑类public class ExpireJobTask {/** Logger */private static final Logger logger LoggerFactor…