一、需求来源
NFileUploadBox 实现功能之后要同步支持到多个app,但是多个app的设计风格差异很大,必须将样式可以二次自定义;于是就有此篇文章,思路是通过基类实现功能,重写父类方法实现样式覆盖,状态复用。好处是共用逻辑,维护成本低。
二、重构调整
1、NFileUploadBox 修改
将选择按钮,点击跳转,子项显示抽离到 NFileUploadHandle类,通过继承重写方法可自定义界面,达到逻辑复用的目的;
...
/// 从文件存储系统选择文件
class NFileUploadBox extends StatefulWidget {
const NFileUploadBox({
...
this.fileUpload,
});
...
final NFileUploadHandle? fileUpload;
@override
State<NFileUploadBox> createState() => _NFileUploadBoxState();
}
class _NFileUploadBoxState extends State<NFileUploadBox> {
...
@override
Widget build(BuildContext context) {
void urlBlock(String url) {
final isAllFinished = selectedModels.where((e) => e.url == null).isEmpty;
// debugPrint("isAllFinished: ${isAllFinished}");
if (isAllFinished) {
final urls = selectedModels.map((e) => e.url).toList();
debugPrint("isAllFinished urls: $urls");
widget.onChanged(selectedModels);
isAllUploadFinished.value = true;
}
}
onPick() async {
await onPickFile(
maxMB: widget.maxMB,
maxCount: widget.maxCount,
type: widget.type,
allowMultiple: widget.allowMultiple,
allowedExtensions: widget.allowedExtensions,
onPermission: () async {
//todo: 权限
return true;
},
);
}
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
widget.header ??
Padding(
padding: EdgeInsets.symmetric(vertical: 5),
child: NText(
widget.title,
fontSize: 14,
color: fontColor737373,
),
),
...selectedModels.map((e) {
final index = selectedModels.indexOf(e);
void deleteItem() {
debugPrint(
"onDelete: $index, length: ${selectedModels[index].assetFile?.path}");
selectedModels.remove(e);
setState(() {});
widget.onChanged(selectedModels);
}
// final fileName = (e.assetFile?.path ?? "").split("/").last;
return GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
if (widget.fileUpload != null) {
widget.fileUpload?.onTapItem(e);
return;
}
onTapItem(e);
},
child: widget.fileUpload?.buildItem(
itemWidth: widget.itemWidth,
itemHeight: widget.itemHeight,
e: e,
urlBlock: urlBlock,
deleteItem: deleteItem,
canEdit: widget.canEdit,
showFileSize: widget.showFileSize) ??
NFileUploadItem(
model: e,
canEdit: widget.canEdit,
urlBlock: urlBlock,
onDelete: widget.canEdit == false ? null : deleteItem,
showFileSize: widget.showFileSize,
),
);
}).toList(),
if (selectedModels.length < widget.maxCount)
widget.fileUpload?.buildUploadButton(onPressed: onPick) ??
buildUploadButton(onPressed: onPick),
...
],
);
}
...
}
2、NFileUploadItem 修改
...
typedef NFileUploadItemBuilder = Widget Function(
NFileUploadModel model,
VoidCallback? onDelete,
VoidCallback? onRefresh,
ValueNotifier<bool> successVN,
ValueNotifier<double> percentVN,
);
/// 上传图片单元(基于 wechat_assets_picker)
class NFileUploadItem extends StatefulWidget {
const NFileUploadItem({
...
this.builder,
});
...
final NFileUploadItemBuilder? builder;
@override
NFileUploadItemState createState() => NFileUploadItemState();
}
class NFileUploadItemState extends State<NFileUploadItem>
with AutomaticKeepAliveClientMixin {
...
@override
Widget build(BuildContext context) {
super.build(context);
final fileName = widget.model.fileName ?? "--";
final fileNameNew = fileName.split(".").firstOrNull ?? "--";
final ext = fileName.split(".").lastOrNull ?? "";
if (widget.builder != null) {
return widget.builder!(
widget.model,
widget.onDelete,
onRefresh,
_successVN,
_percentVN,
);
}
...
}
...
}
3、NFileUploadHandle 源码
/// 文件上传基类
class NFileUploadHandle {
const NFileUploadHandle();
/// 子项展示
NFileUploadItem buildItem({
double? itemWidth,
double? itemHeight,
required NFileUploadModel e,
required ValueChanged<String> urlBlock,
required VoidCallback deleteItem,
required bool canEdit,
required bool showFileSize,
}) {
...
}
/// 子项点击
void onTapItem(NFileUploadModel model) {
...
}
/// 发起选择按钮
Widget buildUploadButton({required VoidCallback onPressed}) {
...
}
}
最后
1、使用:比如第一个 app:OneApp,通过继承 NFileUploadHandle 重写 UI 即可:
class NFileUploadOneApp extends NFileUploadHandle {
/// 子项
@override
NFileUploadItem buildItem({
double? itemWidth,
double? itemHeight,
required NFileUploadModel e,
required ValueChanged<String> urlBlock,
required VoidCallback deleteItem,
required bool canEdit,
required bool showFileSize,
}) {
return NFileUploadItem(
model: e,
canEdit: canEdit,
urlBlock: urlBlock,
onDelete: canEdit == false ? null : deleteItem,
showFileSize: showFileSize,
builder: (NFileUploadModel model,
VoidCallback? onDelete,
VoidCallback? onRefresh,
ValueNotifier<bool> successVN,
ValueNotifier<double> percentVN) {
final validUrl = model.url?.startsWith("http") == true;
String name = model.fileName ?? "--";
String? fileDesc = model.fileDesc;
//重写子项样式(builder已返回必须的逻辑参数)
},
);
}
@override
void onTapItem(NFileUploadModel model) {
super.onTapItem(model);
//重写点击事件
}
/// 发起选择按钮
@override
Widget buildUploadButton({required VoidCallback onPressed}) {
//重写上传按钮
}
2、本文只是根据前端公式 UI = F(state) 进行的一个文件上传组件的简单重构。