/* * @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 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?> pushRoute({ int maxCount = 9, }) => WNavUtil.instance!.push>( MaterialPageRoute( builder: (_) => WAlbumView( maxCount: maxCount, ), ), ); static _WAlbumViewState? _of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType<_Scope>()!.state; } @override _WAlbumViewState createState() => _WAlbumViewState(); } class _WAlbumViewState extends State { ///是否多选 bool get multi => widget.maxCount > 1; ///目录 List paths = []; ///当前选择的目录 AssetPathEntity? currentPaths; ///获取目录 Future getPaths() async { paths = await PhotoManager.getAssetPathList( type: RequestType.image, ); if (paths.length > 0) { currentPaths = paths[0]; setState(() {}); } else { WToastUtil.show('手机相册暂无照片呢'); } } var albums = []; 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 list = []; int page = 0; ///请求列表 Future getList() async { var _list = await currentPaths!.getAssetListPaged( page: page, size: 50, ); if (_list.length > 0) { page = page + 1; list.addAll(_list); } setState(() {}); } ///缓存 Map cache = {}; Future 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 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 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( 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; } }