本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!」前端
以前几篇都是关于 Dio
网络请求相关的内容,咱们的动态模块也就差详情页了,可是每次添加和编辑成功后,返回到列表页仍是须要手动刷新,没有达到“所见即所得”的效果。本篇将介绍使用 GetIt 容器插件完成页面间的数据同步。 本篇涉及到的知识点以下:数据库
详情页面咱们显示动态的标题、查看次数、图片和内容。最简单的方式是使用 Column 组件将全部内容依次包裹。可是,考虑内容实际会很长(也多是富文本),所以使用滚动组件包裹更合适,这里仍是使用 CustomerScrollView
来,相比普通的 ScrollView
来讲,CustomerScrollView
使用 Sliver
子组件,滑动性能会更高也更顺畅,就如同某巧克力广告说的同样——纵享丝滑。编程
页面自己比较简单,就很少介绍了,具体的页面层级以下所示。关于 CustomerScrollView
能够参考以前的文章:Flutter 入门与实战(十二):利用CustomScrollView实现更有趣的滑动效果。后端
CustomScrollView
slivers
Container
包裹以便调整布局。以上的 slivers
的子组件的内容都使用SliverToBoxAdapter转换为
Sliver
。最终界面看后面的动图便可。api
当咱们进入详情页面时,须要向后端提交更新查看次数的接口。注意,有些应用处理是后端直接在获取详情接口时往数据库增长查看数。虽然这样能够减小请求次数,可是后端处理存在缺陷是会把调用详情接口直接当作查看详情页面进行统计,结果在其余地方获取详情时(好比编辑接口)可能致使多统计,行业黑话称之为“刷流量”。markdown
使用前端更新查看次数能够作到更精准的控制,好比咱们能够设置在页面停留多长时间才是有效查看,或者滑动到页面底部才算有效查看等等。网络
更新查看次数的接口地址为:http://localhost:3900/api/dynamics/view/:id
,记得拉取最新的代码运行。咱们在获取详情接口成功后再进行查看次数更新。更新查看次数不是很重要的逻辑,出现错误时无需给予提醒(避免给用户形成困惑),这里只是打印异常信息,用于开发过程当中排查问题。实际生产过程能够将异常信息上传到异常监控后台。async
if (response.statusCode == 200) {
setState(() {
_dynamicEntity = DynamicEntity.fromJson(response.data);
});
_updateViewCount();
}
//...
void _updateViewCount() async {
try {
var response = await DynamicService.updateViewCount(_dynamicEntity.id);
if (response.statusCode == 200) {
setState(() {
_dynamicEntity.viewCount = response.data['viewCount'];
GetIt.instance.get<DynamicListener>().dynamicUpdated(
_dynamicEntity.id,
_dynamicEntity,
);
});
}
} catch (e) {
print(e.toString());
}
}
复制代码
GetIt
自己是一个容器管理插件,其最初的设计是用于完成依赖注入DI 和 IOC 容器的功能,有点相似Java Spring 的Bean容器。因为容器中的对象是全局的,所以能够用来作数据同步,也是 Flutter 官方推荐的状态管理容器之一。另外一个经常使用的状态管理插件是 Provider
,后面咱们涉及到状态管理的时候再来说述。ide
所谓的容器,本质上就是一个全局Map
对象,能够往里面存入对象后,在须要用的时候直接取出,而不须要每一个使用者都本身建立对象,也实现了对象之间的解耦。oop
GetIt 的基础用法很简单,以下所示。若是考虑启动时避免占用太多资源,也可使用 lazy懒加载的方式,懒加载时传入的是一个构建对象的方法,在取出对象的时候,若是容器中没有该对象,则使用构建对象的方法建立一个,若是已经有了就直接返回。
注意,GetIt 有不少个版本,对 Flutter 的 最低SDK 版本有要求,咱们当前使用的 SDK 版本是2.0.6,所以最高只能选择4.0.3版本(最新版本是7.1.2,须要2.12.x 以上版本)。
// 注册对象:通常是单例
GetIt.instance.registerSingleton<T>(T object);
// 懒加载方式注册
GetIt.instance.registerLazySingleton<T>(FactoryFunc<T> func)
// 获取容器中的对象
GetIt.instance.get<T>();
复制代码
当动态新增,或者动态内容发生改变时,咱们须要更新列表。最简单的方式是通知列表刷新,可是那样的增长了网络请求。咱们能够直接修改列表的数据来完成列表的更新。考虑到不只仅是动态列表页须要更新(好比动态嵌入到其余页面中),咱们把动态更新的方法抽象为接口,只要是实现了对应接口的对象均可以在动态发生变化时调用对应的接口更新——即所谓的面向接口编程。
新增一个 dynamic_listener.dart
文件,定义一个接口抽象类DynamicListener
。在列表页面的_DynamicPageState
中实现对应的接口。
import 'package:home_framework/models/dynamic_entity.dart';
abstract class DynamicListener {
void dynamicUpdated(String id, DynamicEntity updatedDynamic);
void dynamicAdded(DynamicEntity newDynamic);
}
复制代码
_DynamicPageState使用 implements 关键字(也可使用 with 关键字)实现DynamicListener接口的两个方法:
同时,在initState
方法中注册自身到 GetIt 容器。
class _DynamicPageState extends State<DynamicPage> implements DynamicListener {
// ...
@override
void initState() {
super.initState();
// 注册到 GetIt容器
GetIt.instance.registerSingleton<DynamicListener>(this);
}
void dynamicUpdated(String id, DynamicEntity updatedDynamic) {
int index = _listItems.indexWhere((element) => element.id == id);
if (index != -1) {
setState(() {
_listItems[index] = updatedDynamic;
});
}
}
void dynamicAdded(DynamicEntity newDynamic) {
setState(() {
_listItems.insert(0, newDynamic);
});
}
// ...
}
复制代码
有了 GetIt 容器,由于能够直接从容器中获取动态列表状态管理对象,在其余页面处理就比较简单了,逻辑分别以下:
dynamicAdded
方法更新列表页面;dynamicUpdated
方法更新列表页面;dynamicUpdated
方法更新列表页面。三个页面的代码分别以下:
//新增页面
var response = await DynamicService.post(newFormData);
if (response.statusCode == 200) {
Dialogs.showInfo(context, '添加成功');
GetIt.instance
.get<DynamicListener>()
.dynamicAdded(DynamicEntity.fromJson(response.data));
Navigator.of(context).pop();
}
//-------------------------------------
//编辑页面
if (response.statusCode == 200) {
Dialogs.showInfo(context, '保存成功');
//处理成功更新后的业务
_handleUpdated(newFormData);
Navigator.of(context).pop();
}
// 处理更新,若是图片更新了才更新动态图片内容
void _handleUpdated(Map<String, String> newFormData) {
_dynamicEntity.title = newFormData['title'];
_dynamicEntity.content = newFormData['content'];
if (newFormData.containsKey('imageUrl')) {
_dynamicEntity.imageUrl = newFormData['imageUrl'];
}
GetIt.instance.get<DynamicListener>().dynamicUpdated(
_dynamicEntity.id,
_dynamicEntity,
);
}
//-------------------------------------
//详情页面
void _updateViewCount() async {
try {
var response = await DynamicService.updateViewCount(_dynamicEntity.id);
if (response.statusCode == 200) {
setState(() {
_dynamicEntity.viewCount = response.data['viewCount'];
GetIt.instance.get<DynamicListener>().dynamicUpdated(
_dynamicEntity.id,
_dynamicEntity,
);
});
}
} catch (e) {
print(e.toString());
}
}
复制代码
本篇完成了整个动态管理的业务逻辑,包括了新增、删除、编辑、查看次数等功能。经过 GetIt 容器管理插件及接口定义,能够很简单快速地完成页面之间的数据同步。从整个系列也能够看到,咱们在网络请求这块的代码存在以下问题:
try...catch
代码块;接下来的篇章咱们将逐步完成对 Dio的封装和网络请求部分代码的重构。