123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 |
- /*
- * @Author: XianKaiQun
- * @Date: 2020-08-31 11:44:00
- * @LastEditors: ChenYaJin
- * @LastEditTime: 2022-11-16 17:31:43
- * @Description:
- */
- import 'dart:io';
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- import 'package:photo_manager/photo_manager.dart';
- import 'package:wisdom_cli/wisdom_cli.dart';
- ///File实体类
- ///
- ///之后本地文件的选择有很多不可预料的东西,
- ///创建一个实体类之后能够达到兼容的效果。
- class FileEntity {
- final File? file;
- FileEntity({this.file});
- static Future<FileEntity> formAssetFile(AssetEntity assetFile) async {
- return FileEntity(file: await assetFile.file);
- }
- }
- ///让子组件获取到父级State
- class _Scope extends InheritedWidget {
- _Scope({
- Key? key,
- required this.child,
- this.state,
- }) : super(key: key, child: child);
- final Widget child;
- final _WAlbumViewState? state;
- @override
- bool updateShouldNotify(_Scope oldWidget) {
- return oldWidget.state!.values != state!.values;
- }
- }
- ///相册视图
- class WAlbumView extends StatefulWidget {
- WAlbumView({
- Key? key,
- this.maxCount = 9,
- }) : assert(maxCount > 0),
- super(key: key);
- final int maxCount;
- ///推进路由栈
- static Future<List<FileEntity>?> pushRoute({
- int maxCount = 9,
- }) =>
- WNavUtil.instance!.push<List<FileEntity>>(
- MaterialPageRoute(
- builder: (_) => WAlbumView(
- maxCount: maxCount,
- ),
- ),
- );
- static _WAlbumViewState? _of(BuildContext context) {
- return context.dependOnInheritedWidgetOfExactType<_Scope>()!.state;
- }
- @override
- _WAlbumViewState createState() => _WAlbumViewState();
- }
- class _WAlbumViewState extends State<WAlbumView> {
- ///是否多选
- bool get multi => widget.maxCount > 1;
- ///目录
- List<AssetPathEntity> paths = [];
- ///当前选择的目录
- AssetPathEntity? currentPaths;
- ///获取目录
- Future<void> getPaths() async {
- paths = await PhotoManager.getAssetPathList(
- type: RequestType.image,
- );
- if (paths.length > 0) {
- currentPaths = paths[0];
- setState(() {});
- } else {
- WToastUtil.show('手机相册暂无照片呢');
- }
- }
- var albums = <AssetPathEntity>[];
- GlobalKey globalKey = GlobalKey();
- OverlayEntry? entry;
- onChangeCurrentPath() {
- if (entry != null) {
- entry?.remove();
- entry = null;
- setState(() {});
- return;
- }
- final RenderBox renderBox =
- globalKey.currentContext?.findRenderObject() as RenderBox;
- final offset = renderBox.localToGlobal(Offset.zero);
- final size = renderBox.size;
- entry = OverlayEntry(
- builder: (context) {
- return Positioned.fill(
- top: offset.dy + size.height,
- child: GestureDetector(
- onTap: () {
- entry?.remove();
- entry = null;
- },
- child: Material(
- color: Colors.black26,
- child: Wisdom(
- margin: EdgeInsets.only(
- bottom: MediaQuery.of(context).size.height / 2,
- ),
- color: Colors.white,
- child: ListView.builder(
- itemCount: paths.length,
- itemBuilder: (_, i) {
- return Wisdom(
- padding: EdgeInsets.symmetric(horizontal: 20.pt),
- height: 44.pt,
- onTap: () => changeCurrentPath(paths[i]),
- child: Text(
- paths[i].isAll ? '全部' : paths[i].name,
- maxLines: 1,
- overflow: TextOverflow.ellipsis,
- ),
- );
- },
- ),
- ),
- ),
- ),
- );
- },
- );
- Overlay.of(context).insert(entry!);
- setState(() {});
- }
- ///切换目录
- void changeCurrentPath(AssetPathEntity path) {
- entry?.remove();
- entry = null;
- currentPaths = path;
- list = [];
- page = 0;
- setState(() {});
- getList();
- }
- ///当前目录的列表
- List<AssetEntity> list = [];
- int page = 0;
- ///请求列表
- Future<void> getList() async {
- var _list = await currentPaths!.getAssetListPaged(
- page: page,
- size: 50,
- );
- if (_list.length > 0) {
- page = page + 1;
- list.addAll(_list);
- }
- setState(() {});
- }
- ///缓存
- Map<String, Uint8List?> cache = {};
- Future<Uint8List?> onCache(AssetEntity data) async {
- if (cache[data.id] == null) {
- cache[data.id] =
- await data.thumbnailDataWithSize(ThumbnailSize(100, 100));
- }
- return cache[data.id];
- }
- @override
- void dispose() {
- cache = {};
- entry?.remove();
- super.dispose();
- }
- @override
- void initState() {
- super.initState();
- }
- onInit() async {
- page = 0;
- setState(() {});
- getPaths().then((value) {
- ///稍微延迟一下,防止低端机在路由动画期间卡顿
- Future.delayed(Duration(milliseconds: 200)).then((value) {
- getList();
- });
- });
- }
- ///当前选中
- List<AssetEntity?> values = [];
- ///选中事件
- void onPick(AssetEntity? data) {
- if (widget.maxCount == 1) {
- values = [data];
- } else if (values.indexOf(data) == -1) {
- if (values.length >= widget.maxCount && widget.maxCount != 1) {
- WToastUtil.show('已超出最大选择数目');
- return;
- } else {
- values.add(data);
- }
- } else {
- values.remove(data);
- }
- setState(() {});
- }
- ///确定 ,
- ///循环转换实体类
- Future<void> pop() async {
- final futures = values.map((e) => FileEntity.formAssetFile(e!)).toList();
- final _values = await Future.wait(futures);
- WNavUtil.instance!.pop(_values);
- }
- @override
- Widget build(BuildContext context) {
- final color = Theme.of(context).primaryColor;
- return _Scope(
- state: this,
- child: Scaffold(
- appBar: AppBar(
- key: globalKey,
- centerTitle: true,
- backgroundColor: Colors.white,
- leading: CloseButton(),
- iconTheme: IconThemeData(color: Colors.black),
- title: TextButton(
- onPressed: () => onChangeCurrentPath(),
- style: ButtonStyle(
- foregroundColor: MaterialStateProperty.all(
- Colors.black,
- ),
- ),
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- if (currentPaths != null)
- Text(
- currentPaths!.isAll ? '全部' : (currentPaths?.name ?? '未知'),
- ),
- Icon(entry == null ? FaIcons.angle_down : FaIcons.angle_up)
- ],
- ),
- ),
- systemOverlayStyle: SystemUiOverlayStyle.dark,
- ),
- body: WisScrollView(
- onInit: () => onInit(),
- onLoad: () => getList(),
- slivers: [
- WisSliverGrid(
- count: list.length,
- crossAxisCount: 4,
- padding: EdgeInsets.all(2.pt),
- mainAxisSpacing: 2.pt,
- crossAxisSpacing: 2.pt,
- builder: (_, i) => _GridItem(
- data: list[i],
- color: color,
- ),
- ),
- ],
- ),
- bottomNavigationBar: _BottomBar(),
- ),
- );
- }
- }
- class _BottomBar extends StatelessWidget {
- const _BottomBar({Key? key}) : super(key: key);
- @override
- Widget build(BuildContext context) {
- final state = WAlbumView._of(context)!;
- final countText =
- state.multi ? ' ${state.values.length}/${state.widget.maxCount}' : '';
- return BottomAppBar(
- child: Wisdom.row(
- padding: EdgeInsets.symmetric(horizontal: 15),
- children: [
- // Text('预览'),
- // Text('原图'),
- Spacer(),
- WLoadingButton(
- remain: true,
- onPressed: state.values.length == 0 ? null : () => state.pop(),
- padding: EdgeInsets.symmetric(horizontal: 10),
- height: 30.pt,
- minWidth: 50.pt,
- child: Text(
- '确定' + countText,
- ),
- ),
- ],
- ),
- );
- }
- }
- class _GridItem extends StatelessWidget {
- _GridItem({
- Key? key,
- this.data,
- this.color,
- }) : super(key: key);
- final AssetEntity? data;
- final Color? color;
- @override
- Widget build(BuildContext context) {
- final state = WAlbumView._of(context)!;
- final active = state.values.indexOf(data) != -1;
- // final title = data.title ?? '';
- // final formatList = title.split('.');
- // final format = formatList.length > 0 ? formatList.last.toUpperCase() : '';
- Widget current;
- if (state.cache[data!.id] != null)
- current = WImage.memory(
- state.cache[data!.id]!,
- fit: BoxFit.cover,
- );
- else
- current = FutureBuilder<Uint8List?>(
- future: state.onCache(data!),
- builder: (_, snapshot) {
- if (snapshot.connectionState == ConnectionState.done) {
- switch (data?.type) {
- case AssetType.image:
- return WImage.memory(
- snapshot.data ?? Uint8List(0),
- fit: BoxFit.cover,
- );
- default:
- return ColoredBox(color: Colors.white);
- }
- } else {
- return ColoredBox(color: Colors.white);
- }
- },
- );
- Widget? dot;
- if (active)
- dot = state.widget.maxCount != 1
- ? Text(
- '${state.values.indexOf(data) + 1}',
- style: TextStyle(
- fontSize: 11,
- fontWeight: FontWeight.bold,
- color: Colors.white,
- ),
- )
- : Icon(
- Icons.check,
- color: Colors.white,
- size: 15,
- );
- dot = Wisdom(
- alignment: Alignment.center,
- minWidth: 22,
- minHeight: 22,
- color: active ? color : Colors.black.withOpacity(0.1),
- border: active ? null : Border.all(color: Colors.white, width: 2),
- borderRadius: BorderRadius.circular(11),
- child: dot,
- );
- current = GestureDetector(
- child: Stack(
- alignment: Alignment.center,
- fit: StackFit.expand,
- children: [
- current,
- if (active)
- Positioned.fill(
- child: ColoredBox(
- color: Colors.black.withOpacity(0.4),
- ),
- ),
- Positioned(
- right: 0,
- top: 0,
- child: Wisdom(
- onTap: () => state.onPick(data),
- padding: EdgeInsets.all(4),
- child: dot,
- ),
- ),
- ],
- ),
- );
- return current;
- }
- }
|