import 'dart:async'; import 'dart:typed_data'; import 'package:chat/generated/i18n.dart'; import 'package:chat/photo/entity/options.dart'; import 'package:chat/photo/provider/asset_provider.dart'; import 'package:chat/photo/provider/config_provider.dart'; import 'package:chat/photo/provider/selected_provider.dart'; import 'package:chat/photo/ui/page/photo_main_page.dart'; import 'package:flutter/material.dart'; import 'package:photo_manager/photo_manager.dart'; class PhotoPreviewPage extends StatefulWidget { final SelectedProvider selectedProvider; final List list; final int initIndex; /// 这个参数是控制在内部点击check后是否实时修改provider状态 final bool changeProviderOnCheckChange; /// 是否通过预览进来的 final bool isPreview; /// 这里封装了结果 final PhotoPreviewResult result; final AssetProvider assetProvider; const PhotoPreviewPage({ Key key, @required this.selectedProvider, @required this.list, @required this.changeProviderOnCheckChange, @required this.result, @required this.assetProvider, this.initIndex = 0, this.isPreview = false, }) : super(key: key); @override _PhotoPreviewPageState createState() => _PhotoPreviewPageState(); } class _PhotoPreviewPageState extends State { PhotoPickerProvider get config => PhotoPickerProvider.of(context); AssetProvider get assetProvider => widget.assetProvider; Options get options => config.options; Color get themeColor => options.themeColor; Color get textColor => options.textColor; SelectedProvider get selectedProvider => widget.selectedProvider; List get list { if (!widget.isPreview) { return assetProvider.data; } return widget.list; } StreamController pageChangeController = StreamController.broadcast(); Stream get pageStream => pageChangeController.stream; bool get changeProviderOnCheckChange => widget.changeProviderOnCheckChange; PhotoPreviewResult get result => widget.result; /// 缩略图用的数据 /// /// 用于与provider数据联动 List get previewList { return selectedProvider.selectedList; } /// 选中的数据 List _selectedList = []; List get selectedList { if (changeProviderOnCheckChange) { return previewList; } return _selectedList; } PageController pageController; @override void initState() { super.initState(); pageChangeController.add(0); pageController = PageController( initialPage: widget.initIndex, ); _selectedList.clear(); _selectedList.addAll(selectedProvider.selectedList); result.previewSelectedList = _selectedList; } @override void dispose() { pageChangeController.close(); super.dispose(); } @override Widget build(BuildContext context) { int totalCount = 0; totalCount = assetProvider.current?.assetCount ?? 0; if (!widget.isPreview) { totalCount = assetProvider.current.assetCount; } else { totalCount = list.length; } var data = Theme.of(context); return Theme( data: data.copyWith( primaryColor: options.themeColor, ), child: Scaffold( appBar: AppBar( backgroundColor: config.options.themeColor, leading: BackButton( color: options.textColor, ), centerTitle: true, title: StreamBuilder( stream: pageStream, initialData: widget.initIndex, builder: (ctx, snap) { return Text( "${snap.data + 1}/$totalCount", style: TextStyle( color: options.textColor, ), ); }, ), actions: [ Container( width: 60, height: 60, padding: EdgeInsets.only(right: 20), alignment: Alignment.center, child: StreamBuilder( builder: (ctx, snapshot) { var index = snapshot.data; var data = list[index]; var checked = selectedList.contains(data); return Stack( alignment: Alignment.center, children: [ IgnorePointer( child: _buildCheckboxContent(checked, index), ), GestureDetector( onTap: () => _changeSelected(!checked, index), behavior: HitTestBehavior.translucent, child: Container(), ), ], ); }, initialData: widget.initIndex, stream: pageStream, )) ], ), body: PageView.builder( controller: pageController, itemBuilder: _buildItem, itemCount: totalCount, onPageChanged: _onPageChanged, ), bottomSheet: _buildThumb(), bottomNavigationBar: _buildBottom(), ), ); } Widget _buildBottom() { var textStyle = TextStyle( color: options.textColor, fontSize: 14.0, ); return StreamBuilder( stream: pageStream, builder: (ctx, s) => Row( mainAxisSize: MainAxisSize.min, children: [ Expanded(child: SizedBox()), InkWell( onTap: selectedList.length == 0 ? null : sure, child: Container( padding: EdgeInsets.symmetric(vertical: 5, horizontal: 15), margin: EdgeInsets.symmetric(vertical: 5, horizontal: 5), decoration: BoxDecoration( color: selectedList.length > 0 ? Color(0xFF2D81FF) : Color(0xFFC7C7C7), borderRadius: BorderRadius.circular(8)), child: Text( "${I18n.of(context).determine}(${selectedList.length})", style: textStyle.copyWith(color: Colors.white), ), )), Expanded(child: SizedBox()), ], )); } Widget _buildCheckboxContent(bool checked, int index) { Widget child; BoxDecoration decoration; if (checked) { child = Text( (index + 1).toString(), textAlign: TextAlign.center, style: TextStyle( fontSize: 12.0, color: Colors.white, ), ); decoration = BoxDecoration(color: Color(0xFF2D81FF), shape: BoxShape.circle); } else { child = Icon(Icons.check); decoration = BoxDecoration( shape: BoxShape.circle, border: Border.all( color: Colors.black, ), color: Colors.white); } return Padding( padding: const EdgeInsets.all(8.0), child: AnimatedContainer( duration: Duration(milliseconds: 300), decoration: decoration, alignment: Alignment.center, child: child, ), ); } void _changeSelected(bool isChecked, int index) { if (changeProviderOnCheckChange) { _onChangeProvider(isChecked, index); } else { _onCheckInOnlyPreview(isChecked, index); } } /// 仅仅修改预览时的状态,在退出时,再更新provider的顺序,这里无论添加与否不修改顺序 void _onCheckInOnlyPreview(bool check, int index) { var item = list[index]; if (check) { selectedList.add(item); } else { selectedList.remove(item); } pageChangeController.add(index); } /// 直接修改预览状态,会直接移除item void _onChangeProvider(bool check, int index) { var item = list[index]; if (check) { selectedProvider.addSelectEntity(item); } else { selectedProvider.removeSelectEntity(item); } pageChangeController.add(index); } Widget _buildItem(BuildContext context, int index) { if (!widget.isPreview && index >= list.length - 5) { _loadMore(); } var data = list[index]; return BigPhotoImage( assetEntity: data, loadingWidget: _buildLoadingWidget(data), ); } Future _loadMore() async { assetProvider.loadMore(); } Widget _buildLoadingWidget(AssetEntity entity) { return options.loadingDelegate .buildBigImageLoading(context, entity, themeColor); } void _onPageChanged(int value) { pageChangeController.add(value); } Widget _buildThumb() { return Container( height: 80.0, color: Color(0xFFF0F0F0), child: ListView.builder( padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), itemBuilder: _buildThumbItem, itemCount: previewList.length, scrollDirection: Axis.horizontal, ), ); } Widget _buildThumbItem(BuildContext context, int index) { var item = previewList[index]; return StreamBuilder( initialData: widget.initIndex, builder: (ctx, snapshot) => RepaintBoundary( child: GestureDetector( onTap: () => changeSelected(item, index), child: Container( width: 80.0, child: Container( margin: EdgeInsets.all(10), decoration: BoxDecoration( border: snapshot.data == index ? Border.all(color: Colors.blue, width: 2) : null), child: Stack( children: [ ImageItem( themeColor: themeColor, entity: item, size: options.thumbSize, loadingDelegate: options.loadingDelegate, ), IgnorePointer( child: selectedList.contains(item) ? Container() : Container( color: Colors.white.withOpacity(0.5), )), ], ), ), )), ), stream: pageStream, ); } void changeSelected(AssetEntity entity, int index) { var itemIndex = list.indexOf(entity); if (itemIndex != -1) pageController.jumpToPage(itemIndex); } void sure() { Navigator.pop(context, selectedList); } } class BigPhotoImage extends StatefulWidget { final AssetEntity assetEntity; final Widget loadingWidget; const BigPhotoImage({ Key key, this.assetEntity, this.loadingWidget, }) : super(key: key); @override _BigPhotoImageState createState() => _BigPhotoImageState(); } class _BigPhotoImageState extends State with AutomaticKeepAliveClientMixin { Widget get loadingWidget { return widget.loadingWidget ?? Container(); } @override Widget build(BuildContext context) { super.build(context); var width = MediaQuery.of(context).size.width; var height = MediaQuery.of(context).size.height; return FutureBuilder( future: widget.assetEntity.thumbDataWithSize(width.floor(), height.floor()), builder: (BuildContext context, AsyncSnapshot snapshot) { var file = snapshot.data; if (snapshot.connectionState == ConnectionState.done && file != null) { print(file.length); return Image.memory( file, fit: BoxFit.contain, width: double.infinity, height: double.infinity, ); } return loadingWidget; }, ); } @override bool get wantKeepAlive => true; } class PhotoPreviewResult { List previewSelectedList = []; }