import 'package:flutter/material.dart'; import 'package:hgdj/assets_tool/images.dart'; import 'package:hgdj/hj_model/video_model.dart'; import 'package:hgdj/hj_utils/const.dart'; import 'package:hgdj/hj_utils/screen.dart'; import 'package:hgdj/tools_base/video_download/media_download_manager.dart'; /// 视频下载按钮,短视频 / 长视频 / 短剧 / 动漫共用。 /// 只管画状态和转发点击;权限、扣次、落记录、进度回调全在 [MediaDownloadManager] class DownloadButton extends StatefulWidget { final VideoModel? video; /// true 竖排(短视频那套图标),false 横排(长视频),只影响外观 final bool isShort; /// 缓存记录落哪个桶。不传时按 [isShort] 推断(短视频 / 影视 / 动漫); /// 短剧在操作台里排版同短视频,但要单独归类,所以显式传 [MediaStyle.Drama] final MediaStyle? style; const DownloadButton( {super.key, this.video, this.isShort = true, this.style}); @override State createState() => _DownloadButtonState(); } class _DownloadButtonState extends State { late DownloadTask _task; /// 免费下载一套图标,短视频/长视频各一套 String get _icon => (widget.video?.isFreeDownload == true ? "download_free.png" : widget.isShort ? "download_short.png" : "download.png") .videoPath; /// 保存到缓存库时的媒体类型 MediaStyle get _mediaStyle { final style = widget.style; if (style != null) return style; if (widget.isShort) return MediaStyle.ShortVideo; return widget.video?.videoType == 1 ? MediaStyle.Cartoon : MediaStyle.Video; } @override void initState() { super.initState(); _task = MediaDownloadManager.instance .attach(video: widget.video, style: _mediaStyle, onChanged: _refresh); } @override void didUpdateWidget(covariant DownloadButton oldWidget) { super.didUpdateWidget(oldWidget); //列表/PageView 复用 State 时会换 model,会话得跟着换:不换的话按钮画的是上一条的状态, //点下载下的也是上一条。同一个对象(父级只是重建)就别白折腾 if (identical(oldWidget.video, widget.video)) return; _task.detach(); _task = MediaDownloadManager.instance .attach(video: widget.video, style: _mediaStyle, onChanged: _refresh); } @override void dispose() { _task.detach(); super.dispose(); } /// 本按钮在短视频列表里随滑动大量创建/销毁,下载回调由单例持有、dispose 后仍可能被调到,统一判 mounted void _refresh() { if (mounted) setState(() {}); } @override Widget build(BuildContext context) { final isShort = widget.isShort; final icon = SizedBox( width: isShort ? 30 : 24, height: isShort ? 30 : 24, child: Image.asset(_icon), ); final label = Text( _task.state.desc, style: isShort ? const TextStyle( color: Color(0xffdcdcdc), fontSize: 12, fontWeight: FontWeight.w500) : const TextStyle(color: Color(0xff989898), fontSize: 12), ); return InkWell( enableFeedback: false, onTap: _task.start, child: isShort ? Column(mainAxisSize: MainAxisSize.min, children: [icon, label]) : Row( mainAxisSize: MainAxisSize.min, children: [icon, 2.sizeBoxW, label]), ); } }