最近有空关注了下flutter空安全升级,我也跟着升级了下,重构一下自己写的dart-cms-flutter项目。主要使用flutter + getx, 《getx yyds》
我不会放出采集那些数据,自己处理就好。这里只是说下如何做的
项目地址:Dart-Cms-Flutter
项目截图
从首页开始
由于使用了getx,所以,首页的部分需要改一些东西,这里需要多提一嘴的是,getx提供了全局注入控制器,相当于全局状态,非常好用,使用GetxService,下面是例子
这是一个收藏记录的例子,
import 'package:get/get.dart';
import 'package:dart_cms_flutter/utils/storage.dart';
import 'package:dart_cms_flutter/interface/videoDetaill.dart';
// 全局响应数据
class StoreService extends GetxService {
// 历史记录
RxList<dynamic> storeList = [].obs;
Future<StoreService> init() async {
List<Map<String, dynamic>> storeData =
List.from(StorageUtil().getJSON("store") ?? []);
storeList.addAll(storeData);
return this;
}
Future<bool> add<T extends VideoDetaillInterFace>(
T obj,
) async {
// 插入一条新的
String newKey = obj.Id!;
// 检查是否存在
bool isExist = storeList.any((el) => el["Id"] == newKey);
// 是否超出限制50个存储配额, 并且当前历史记录中没有这个视频
if (storeList.length >= 50 && !isExist) {
storeList.removeLast();
}
// 当前的id是否已经存在
if (isExist) {
// 存在就删除,重新插入,变化位置,插入到最前
storeList.removeWhere((el) => el["Id"] == newKey);
}
// 当前视频的数据 formant
Map<String, dynamic> curVideoMap = _formantVideoDetaill(
obj,
);
// 插入新的
storeList.insert(0, curVideoMap);
// 存入
// ignore: invalid_use_of_protected_member
return StorageUtil().setJSON('store', storeList.value);
}
Future<bool> removeKey(String keyName) async {
storeList.removeWhere((element) => true);
return StorageUtil().remove(keyName);
}
Map<String, dynamic> _formantVideoDetaill<T extends VideoDetaillInterFace>(
T obj,
) {
return {
"Id": obj.Id,
"videoTitle": obj.videoTitle,
"director": obj.director,
"poster": obj.performer,
"videoImage": obj.videoImage,
"video_type": obj.videoType!.name,
"video_rate": obj.videoRate,
"update_time": obj.updateTime,
"language": obj.language,
"sub_region": obj.subRegion,
"rel_time": obj.relTime,
"introduce": obj.introduce,
"remind_tip": obj.remindTip,
"popular": obj.popular,
"allow_reply": obj.allowReply,
"display": obj.display,
"scource_sort": obj.scourceSort,
};
}
}
全局注入,使得控制器不会被回收,下面是注入时的例子
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// debugPaintSizeEnabled = true;
await initStore();
runApp(MyApp());
}
Future<void> initStore() async {
// 初始化http请求
HttpUtils().init(baseUrl: hostUrl);
// 初始化单例模式
await StorageUtil().init();
// 这里注入历史记录模块
await Get.putAsync(() => HistoryService().init());
// 这里注入收藏记录模块
await Get.putAsync(() => StoreService().init());
print("全局注入");
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ScreenUtilInit(
// designSize: Size(375, 812),
builder: () => GetMaterialApp(
debugShowCheckedModeBanner: false,
initialRoute: PageName.HOME,
getPages: PageRoutes.routes,
),
);
}
}
在runapp之前注入这些需要的依赖,以及单例对象,
首页,文章页,分类页,页面部分
页面主要还是statefullwdiget, 但是getx给我们提供了一个简便操作,那就是getview,使用getview我们一些一次性请求的页面(指的是数据出现之后不会有下拉刷新这种情况下)使用getview就很方便,比如我上面的文章页面,下面是例子:
// view部分
class AppBarIndexView extends GetView<AppbarIndexViewStore>{
@override
Widget build(BuildContext context) {
// 这里注入控制器
AppbarIndexViewStore controller = Get.put(AppbarIndexViewStore());
return controller.obx(
// 这里是成功时候展现的内容
(state) => Widget,
// 这里是loading时候的内容
onLoading: widget,
// 这里是为空数据时候的内容
onEmpty: widget,
// 这里是失败时候的内
onError: widget,
)
}
}
// 控制器部分
class AppbarIndexViewStore extends GetxController with SingleGetTickerProviderMixin,StateMixin {
initEvent(){
// 改变状态,调用change方法
change("失败", status: RxStatus.error());
change("成功", status: RxStatus.success());
change("加载中", status: RxStatus.loading());
}
@override
void onInit() {
super.onInit();
// 初始化控制要运行啥
initEvent();
}
}
关键部分,视频播放器
flutter的视频播放器,我发过一篇水贴,各位可以看下,flutter这边目前你能够找到的播放器,比如chewie,betterplayer,这些都没有滑动快进上下滑动改变音量和屏幕亮度。所以对于你想开箱即用,还能满足一下下自己,很难。据我观察许久。如果你和我一样是一个垃圾小前端(大废物)。那么你最好是基于fijkplayer。
说到fijkplayer,必须得提一嘴,我基于fijkplayer开发的皮肤,在fijkplayer作者皮肤的基础之上开发的,加入了手势上下滑动改变音量和屏幕亮度,左右滑动快进快退。项目地址:fijkplayer_skin
效果就是上面截图的效果
这里着重说下给播放皮肤加功能,
快进快退实现
快进快退,说白了就是检测滑动的时候判断当前点和屏幕按下时候的点的距离,最简单的方法就是>大于就+1,小于就-1,以下是我的实现。这里只是函数部分,ui部分,你需要自己写布局,不想写可以抄袭下我的皮肤
_onHorizontalDragStart(detills) {
setState(() {
// 在按下的时候现存一下当前的点的位置
updatePrevDx = detills.globalPosition.dx;
updatePosX = _currentPos.inSeconds;
});
}
_onHorizontalDragUpdate(detills) {
double curDragDx = detills.globalPosition.dx;
// 确定当前是前进或者后退
int cdx = curDragDx.toInt();
int pdx = updatePrevDx!.toInt();
bool isBefore = cdx > pdx;
// + -, 不满足, 左右滑动合法滑动值,> 1
if (isBefore && cdx - pdx < 1 || !isBefore && pdx - cdx < 1) return null;
int dragRange = isBefore ? updatePosX! + 1 : updatePosX! - 1;
// 是否溢出 最大
int lastSecond = _duration.inSeconds;
if (dragRange >= _duration.inSeconds) {
dragRange = lastSecond;
}
// 是否溢出 最小
if (dragRange <= 0) {
dragRange = 0;
}
//
this.setState(() {
_isHorizontalMove = true;
_hideStuff = false;
_isTouch = true;
// 更新下上一次存的滑动位置
updatePrevDx = curDragDx;
// 更新时间
updatePosX = dragRange.toInt();
_dargPos = Duration(seconds: updatePosX!.toInt());
});
}
// 在松手的时候,说明就不打架了,所以就各回各家,各找各妈
_onHorizontalDragEnd(detills) {
// 这里就给播放器设置更新后的位置,就是时间
player.seekTo(_dargPos.inMilliseconds);
this.setState(() {
_isHorizontalMove = false;
_isTouch = false;
_hideStuff = true;
_currentPos = _dargPos;
});
}
上下滑动也是同理,稍作修改便可以完成。至此一些主要的点结束。
|