文档简单用例
1.在 pubspec.yaml 中添加依赖
//pub 方式
dependencies:
flutter_easyrefresh: ^2.1.7
//导入方式
dependencies:
flutter_easyrefresh:
path: 项目路径
//git 方式
dependencies:
flutter_easyrefresh:
git:
url: git://github.com/xuelongqy/flutter_easyrefresh.git
import 'package:flutter_easyrefresh/easy_refresh.dart';
....
// 方式一
EasyRefresh(
child: ScrollView(),
onRefresh: () async{
....
},
onLoad: () async {
....
},
)
// 方式二
EasyRefresh.custom(
slivers: <Widget>[],
onRefresh: () async{
....
},
onLoad: () async {
....
},
)
// 方式三
EasyRefresh.builder(
builder: (context, physics, header, footer) {
return CustomScrollView(
physics: physics,
slivers: <Widget>[
...
header,
...
footer,
],
);
}
onRefresh: () async{
....
},
onLoad: () async {
....
},
)
EasyRefreshController _controller = EasyRefreshController();
....
EasyRefresh(
controller: _controller,
....
);
....
_controller.callRefresh();
_controller.callLoad();
EasyRefreshController _controller = EasyRefreshController();
....
EasyRefresh(
enableControlFinishRefresh: true,
enableControlFinishLoad: true,
....
);
....
_controller.finishRefresh(success: true);
_controller.finishLoad(success: true, noMore: false);
import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'package:flutter_easyrefresh/material_header.dart';
import 'package:flutter_easyrefresh/material_footer.dart';
....
new EasyRefresh(
header: MaterialHeader(),
footer: MaterialFooter(),
child: ScrollView(),
....
)
不提供自带国际化支持,请自行设置 ClassicalHeader 和 ClassicalFooter 中需要展示的文字。
流程:
只要在 onRefresh 方法里面,作数据下载的动作,下载完数据后,把值给 files.
上拉加载更多 onLoad
这个复杂一点,不过理清了就简单。在首次 onRefresh 加载数据给 files 时,先确定好每一次下载的数量。之后,每次调用 onLoad 下载追加到 files 后面。计算最后的数据。加载完后要显示的提示。
import 'dart:io';
import 'package:defensor/generated/l10n.dart';
import 'package:defensor/models/select.dart';
import 'package:defensor/service/device_service.dart';
import 'package:defensor/utils/tools.dart';
import 'package:defensor/views/common/gallery/photo_view.dart';
import 'package:defensor/views/common/gallery/video_view.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'package:path_provider/path_provider.dart';
import 'package:video_thumbnail/video_thumbnail.dart';
class GalleryList extends StatefulWidget {
/// 是否在线相册
bool isOnline;
/// 目录名
String dirName;
/// 目录数量
String dirsSum;
GalleryList(this.isOnline, this.dirName, this.dirsSum);
_GalleryListState createState() => _GalleryListState();
}
class _GalleryListState extends State<GalleryList> {
/// 视频文件列表
List<String> files;
/// 视频缩略图
List<Image> videoThumbnails;
/// 视频文件列表临时文件
List<String> filesTemp;
/// 视频缩略图临时文件
List<Image> videoThumbnailsTemp;
/// 文件总数
int sum;
/// 每次加截数量
int numOfPage = 24;
/// 加载更新记录次数
int tag = 0;
/// 是否显示删除
bool isShowDelete = false;
String selectText = '选择';
/// 删除文件下标
List<SelectModel> selectData;
void initState() {
super.initState();
}
void dispose() {
clear();
super.dispose();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(Tools.cameraDirToName(widget.dirName)),
actions: [
isShowDelete
? CupertinoButton(
child: Text(
'全删除',
style: TextStyle(color: Colors.white),
),
onPressed: () {
print('全删除');
})
: Container(),
CupertinoButton(
child: Text(
selectText,
style: TextStyle(color: Colors.white),
),
onPressed: () {
isShowDelete = !isShowDelete;
if (isShowDelete) {
selectText = '取消';
} else {
selectText = '选择';
}
setState(() {});
})
],
),
body: _body(),
);
}
Widget _body() {
return Stack(
alignment: AlignmentDirectional.center,
children: [
EasyRefresh(
onRefresh: () async {
if (widget.isOnline) {
loadOnlineFiles();
} else {
loadLocalFiles();
}
},
onLoad: () async {
if (widget.isOnline) {
loadMoreOnlineData();
} else {
loadMoreOfflineData();
}
},
firstRefresh: true,
child: _gridView()),
Positioned(
bottom: 20,
child: isShowDelete
? RaisedButton(
color: Colors.blue,
highlightColor: Colors.blue[700],
colorBrightness: Brightness.dark,
splashColor: Colors.grey,
child: Text('删除'),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0)),
onPressed: () => print('删除'),
)
: Container())
],
);
}
Widget _gridView() {
// 没有数据
if (files == null) {
return Center(child: Text(S.current.noVideos));
}
double boxWidth = 110;
// 取得屏幕 size
final size = MediaQuery.of(context).size;
int crossAxisCount = size.width ~/ boxWidth;
// 初始化
selectData = List<SelectModel>();
return GridView.builder(
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount, // 一行多少个
childAspectRatio: 1, // 宽高比
crossAxisSpacing: 1, // 左右间隔
mainAxisSpacing: 1, // 上下间隔
),
itemCount: files.length ?? 0,
itemBuilder: (context, index) {
return InkWell(
onTap: () => isShowDelete ? print('selected') : toView(files[index]),
child: Stack(
fit: StackFit.expand,
children: [
Container(
margin: EdgeInsets.all(1.0),
decoration: new BoxDecoration(
),
child: videoThumbnails.length == 0
? Image.asset('assets/images/gallery.png')
: videoThumbnails[index]),
],
),
);
},
);
}
/// 跳转浏览器
toView(String url) {
if (url.split('.').last != 'MP4') {
Navigator.push(context,
MaterialPageRoute(builder: (context) => PhotoViews(url: url)));
} else {
Navigator.push(context,
MaterialPageRoute(builder: (context) => VideoView(url: url)));
}
}
/// 判断是视频还是图片,并转换为图片
Future<List<Image>> videoToPhoto(List<String> filesPath) async {
var list = List<Image>();
var uint8list;
for (var file in filesPath) {
// specify the width of the thumbnail, let the height auto-scaled to keep the source aspect ratio
// 判断为图片
if (file.split('.').last != 'MP4') {
// 网络图片
if (file.split('/').first.contains('http')) {
list.add(Image.network(file, fit: BoxFit.cover));
} else {
// file 图片
list.add(Image.file(File(file), fit: BoxFit.cover));
}
} else {
// 判断为视频并转为图片
try {
if (widget.isOnline) {
// Generate a thumbnail file from video URL
print(file);
final thumbnailPath = await VideoThumbnail.thumbnailFile(
video: file,
thumbnailPath: (await getTemporaryDirectory()).path,
imageFormat: ImageFormat.JPEG,
maxHeight: 300,
quality: 75,
);
// print('thumbnailPath $thumbnailPath');
uint8list = File(thumbnailPath).readAsBytesSync();
} else {
// Generate a thumbnail in memory from video file
uint8list = await VideoThumbnail.thumbnailData(
video: file,
imageFormat: ImageFormat.JPEG,
maxWidth: 300,
quality: 75,
);
}
list.add(Image.memory(uint8list, fit: BoxFit.cover));
} catch (e) {
print(e);
}
}
}
return list;
}
/// 清空数据
clear() {
files = null;
filesTemp = null;
videoThumbnails = null;
videoThumbnailsTemp = null;
}
/******************** 在线相册 ********************/
/// 加载在线文件
loadOnlineFiles() async {
sum = int.parse(await DeviceService.getdirfilecount(dir: widget.dirName));
print('sum $sum');
var start = 0.toString();
var end = (numOfPage - 1).toString();
tag = 0;
List<String> list = await DeviceService.getdirfilelist(
dir: widget.dirName, start: start, end: end);
if (list == null) {
return;
}
// 判断是视频还是图片,并转换为图片
List<Image> photos = await videoToPhoto(list);
setState(() {
files = list;
videoThumbnails = photos;
});
tag++;
print('files: ${files.length}');
}
/// 加载更多数据
loadMoreOnlineData() async {
// 最大加载次数 36/5 = 8次
var sum = int.parse(widget.dirsSum);
var tagMax = (sum / numOfPage) > (sum ~/ numOfPage)
? sum ~/ numOfPage + 1
: sum ~/ numOfPage;
// print('tagMax $tagMax');
// 加载完, 返回
if (tag >= tagMax) {
return;
}
// 0-4 5-9 10-14, 15-19, 20-24, 25-29...
// 计算 start, end
var start = tag * numOfPage;
var end;
if (tag == tagMax - 1) {
// 最后一次
end = (tag * numOfPage) + (sum % numOfPage) - 1;
} else {
end = (tag + 1) * numOfPage - 1;
}
// print('start $start end $end');
List<String> list = await DeviceService.getdirfilelist(
dir: widget.dirName, start: start.toString(), end: end.toString());
if (list == null) {
return;
}
// 视频文件转图片
var photos = await videoToPhoto(list);
print('photos $photos');
setState(() {
files.addAll(list);
videoThumbnails.addAll(photos);
});
// print('files ${files.length}');
tag++;
}
/******************** 本地相册 ********************/
/// 加载本地文件
loadLocalFiles() async {
// 视频目录
Directory video;
try {
// Android
if (Platform.isAndroid) {
// 获取文档目录的路径 // /storage/emulated/0/Android/data/com.wpgholding.defensor/files
video = await getExternalStorageDirectory();
}
// iOS
if (Platform.isIOS) {
// 获取文档目录的路径
video = await getApplicationDocumentsDirectory();
// 拼接文档目录上的video地址
}
// 拼接 video 地址
video = Directory(video.path + '/video');
// print('本地视频目录: $video');
bool exists = await video.exists();
if (!exists) {
await video.create();
}
var videos = video.listSync();
var list = List<String>();
videos.forEach((file) {
list.add(file.path);
});
// 视频文件过滤
filesTemp = imageFilter(list);
// 视频文件转图片
videoThumbnailsTemp = await videoToPhoto(list);
/// 保存总数
sum = filesTemp.length;
tag = 0;
print('sum $sum');
setState(() {
files = filesTemp.sublist(0, numOfPage);
videoThumbnails = videoThumbnailsTemp.sublist(0, numOfPage);
});
// 记录一次
tag++;
// print('videoThumbnails $videoThumbnails');
} catch (err) {
print(err);
}
}
/// 加载更多数据
loadMoreOfflineData() {
// 最大加载次数 36/5 = 8次
var tagMax = (sum / numOfPage) > (sum ~/ numOfPage)
? sum ~/ numOfPage + 1
: sum ~/ numOfPage;
// print('tagMax $tagMax');
// 加载完, 返回
if (tag >= tagMax) {
return;
}
// 0-5 5-10 10-15, 15-20, 20-25, 25-30, 30-35, 35-36...
// 计算 start, end
var start = tag * numOfPage;
var end;
if (tag == tagMax - 1) {
// 最后一次
end = (tag * numOfPage) + (sum % numOfPage);
} else {
end = (tag + 1) * numOfPage;
}
// print('start $start end $end');
setState(() {
files.addAll(filesTemp.sublist(start, end));
videoThumbnails.addAll(videoThumbnailsTemp.sublist(start, end));
});
tag++;
// print('files ${files.length} videoThumbnails ${videoThumbnails.length}');
}
/// 视频文件过滤
List<String> imageFilter(List<String> files) {
var newFiles = List<String>();
files.forEach((element) {
// 保留视频格式后辍 mp4
var suffix = element.split('.').last.toLowerCase();
if (suffix == 'mp4') {
newFiles.add(element);
}
});
return newFiles;
}
}
参考资料:
flutter_easyrefresh (https://github.com/xuelongqy/flutter_easyrefresh)
评论