diff --git a/lib/widget/home_photos.dart b/lib/widget/home_photos.dart index b6350185..76c42174 100644 --- a/lib/widget/home_photos.dart +++ b/lib/widget/home_photos.dart @@ -94,16 +94,6 @@ class _HomePhotosState extends State Widget _buildContent(BuildContext context, ScanDirBlocState state) { return LayoutBuilder(builder: (context, constraints) { - if (_prevListWidth == null) { - _prevListWidth = constraints.maxWidth; - } - if (constraints.maxWidth != _prevListWidth) { - _log.info( - "[_buildContent] updateListHeight: list viewport width changed"); - WidgetsBinding.instance.addPostFrameCallback((_) => updateListHeight()); - _prevListWidth = constraints.maxWidth; - } - final scrollExtent = _getScrollViewExtent(constraints); return Stack( children: [ @@ -583,7 +573,6 @@ class _HomePhotosState extends State final ScrollController _scrollController = ScrollController(); - double _prevListWidth; double _appBarExtent; static final _log = Logger("widget.home_photos._HomePhotosState"); diff --git a/lib/widget/measurable_item_list.dart b/lib/widget/measurable_item_list.dart new file mode 100644 index 00000000..dbcf1316 --- /dev/null +++ b/lib/widget/measurable_item_list.dart @@ -0,0 +1,134 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; +import 'package:logging/logging.dart'; +import 'package:nc_photos/widget/measureable_sliver_staggered_grid.dart'; +import 'package:uuid/uuid.dart'; + +abstract class MeasurableItemListState { + void updateListHeight(); +} + +class MeasurableItemList extends StatefulWidget { + MeasurableItemList({ + Key key, + @required this.maxCrossAxisExtent, + @required this.itemCount, + @required this.itemBuilder, + @required this.staggeredTileBuilder, + this.onMaxExtentChanged, + }) : super(key: key); + + @override + createState() => _MeasurableItemListState(); + + final double maxCrossAxisExtent; + final int itemCount; + final IndexedWidgetBuilder itemBuilder; + final IndexedStaggeredTileBuilder staggeredTileBuilder; + final ValueChanged onMaxExtentChanged; +} + +class _MeasurableItemListState extends State + with WidgetsBindingObserver + implements MeasurableItemListState { + @override + initState() { + super.initState(); + + WidgetsBinding.instance.addPostFrameCallback((_) { + _prevOrientation = MediaQuery.of(context).orientation; + WidgetsBinding.instance.addObserver(this); + }); + } + + @override + dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + @override + didChangeMetrics() { + WidgetsBinding.instance.addPostFrameCallback((_) { + final orientation = MediaQuery.of(context).orientation; + if (orientation != _prevOrientation) { + _log.info( + "[didChangeMetrics] updateListHeight: orientation changed: $orientation"); + _prevOrientation = orientation; + updateListHeight(); + } + }); + } + + @override + build(BuildContext context) { + return SliverLayoutBuilder(builder: (context, constraints) { + if (_prevListWidth == null) { + _prevListWidth = constraints.crossAxisExtent; + } + if (constraints.crossAxisExtent != _prevListWidth) { + _log.info("[build] updateListHeight: list viewport width changed"); + WidgetsBinding.instance.addPostFrameCallback((_) => updateListHeight()); + _prevListWidth = constraints.crossAxisExtent; + } + + // need to rebuild grid after cell size changed + final cellSize = widget.maxCrossAxisExtent; + if (cellSize != _prevCellSize) { + _log.info("[build] updateListHeight: cell size changed"); + WidgetsBinding.instance.addPostFrameCallback((_) => updateListHeight()); + _prevCellSize = cellSize; + } + _gridKey = _GridKey("$_uniqueToken $cellSize"); + return MeasurableSliverStaggeredGrid.extentBuilder( + key: _gridKey, + maxCrossAxisExtent: widget.maxCrossAxisExtent, + itemCount: widget.itemCount, + itemBuilder: widget.itemBuilder, + staggeredTileBuilder: widget.staggeredTileBuilder, + ); + }); + } + + @override + updateListHeight() { + double newMaxExtent; + try { + final renderObj = _gridKey.currentContext.findRenderObject() + as RenderMeasurableSliverStaggeredGrid; + final maxExtent = renderObj.calculateExtent(); + _log.info("[updateListHeight] Max extent: $maxExtent"); + if (maxExtent == 0) { + // ? + newMaxExtent = null; + } else { + newMaxExtent = maxExtent; + } + } catch (e, stacktrace) { + _log.shout("[updateListHeight] Failed while calculateMaxScrollExtent", e, + stacktrace); + newMaxExtent = null; + } + + if (newMaxExtent != _maxExtent) { + _maxExtent = newMaxExtent; + widget.onMaxExtentChanged?.call(newMaxExtent); + } + } + + double _prevListWidth; + double _prevCellSize; + double _maxExtent; + Orientation _prevOrientation; + + // this unique token is there to keep the global key unique + final _uniqueToken = Uuid().v4(); + GlobalObjectKey _gridKey; + + static final _log = + Logger("widget.measurable_item_list._MeasurableItemListState"); +} + +class _GridKey extends GlobalObjectKey { + const _GridKey(Object value) : super(value); +} diff --git a/lib/widget/selectable_item_stream_list_mixin.dart b/lib/widget/selectable_item_stream_list_mixin.dart index f3dff47c..90e8384c 100644 --- a/lib/widget/selectable_item_stream_list_mixin.dart +++ b/lib/widget/selectable_item_stream_list_mixin.dart @@ -12,8 +12,7 @@ import 'package:nc_photos/platform/k.dart' as platform_k; import 'package:nc_photos/session_storage.dart'; import 'package:nc_photos/snack_bar_manager.dart'; import 'package:nc_photos/theme.dart'; -import 'package:nc_photos/widget/measureable_sliver_staggered_grid.dart'; -import 'package:uuid/uuid.dart'; +import 'package:nc_photos/widget/measurable_item_list.dart'; abstract class SelectableItemStreamListItem { const SelectableItemStreamListItem({ @@ -29,36 +28,11 @@ abstract class SelectableItemStreamListItem { final StaggeredTile staggeredTile; } -mixin SelectableItemStreamListMixin - on State, WidgetsBindingObserver { +mixin SelectableItemStreamListMixin on State { @override initState() { super.initState(); _keyboardFocus.requestFocus(); - - WidgetsBinding.instance.addPostFrameCallback((_) { - _prevOrientation = MediaQuery.of(context).orientation; - WidgetsBinding.instance.addObserver(this); - }); - } - - @override - dispose() { - WidgetsBinding.instance.removeObserver(this); - super.dispose(); - } - - @override - didChangeMetrics() { - WidgetsBinding.instance.addPostFrameCallback((_) { - final orientation = MediaQuery.of(context).orientation; - if (orientation != _prevOrientation) { - _log.info( - "[didChangeMetrics] updateListHeight: orientation changed: $orientation"); - _prevOrientation = orientation; - updateListHeight(); - } - }); } @protected @@ -82,20 +56,16 @@ mixin SelectableItemStreamListMixin @protected Widget buildItemStreamList(BuildContext context) { - // need to rebuild grid after cell size changed - final cellSize = itemStreamListCellSize; - if (cellSize != _prevItemStreamListCellSize) { - _log.info("[buildItemStreamList] updateListHeight: cell size changed"); - WidgetsBinding.instance.addPostFrameCallback((_) => updateListHeight()); - _prevItemStreamListCellSize = cellSize; - } - _gridKey = _GridKey("$_uniqueToken $cellSize"); - return MeasurableSliverStaggeredGrid.extentBuilder( - key: _gridKey, + return MeasurableItemList( + key: _listKey, maxCrossAxisExtent: itemStreamListCellSize.toDouble(), itemCount: _items.length, itemBuilder: _buildItem, staggeredTileBuilder: (index) => _items[index].staggeredTile, + onMaxExtentChanged: (newExtent) { + _calculatedMaxExtent = newExtent; + onMaxExtentChanged(newExtent); + }, ); } @@ -107,27 +77,6 @@ mixin SelectableItemStreamListMixin _selectedItems.clear(); } - @protected - void updateListHeight() { - try { - final renderObj = _gridKey.currentContext.findRenderObject() - as RenderMeasurableSliverStaggeredGrid; - final maxExtent = renderObj.calculateExtent(); - _log.info("[updateListHeight] Max extent: $maxExtent"); - if (maxExtent == 0) { - // ? - _calculatedMaxExtent = null; - } else { - _calculatedMaxExtent = maxExtent; - onMaxExtentChanged(maxExtent); - } - } catch (e, stacktrace) { - _log.shout("[updateListHeight] Failed while calculateMaxScrollExtent", e, - stacktrace); - _calculatedMaxExtent = null; - } - } - @protected bool get isSelectionMode => _selectedItems.isNotEmpty; @@ -160,7 +109,8 @@ mixin SelectableItemStreamListMixin _lastSelectPosition = newLastSelectPosition; _log.info("[itemStreamListItems] updateListHeight: list item changed"); - WidgetsBinding.instance.addPostFrameCallback((_) => updateListHeight()); + WidgetsBinding.instance.addPostFrameCallback((_) => + (_listKey.currentState as MeasurableItemListState)?.updateListHeight()); } @protected @@ -281,16 +231,12 @@ mixin SelectableItemStreamListMixin int _lastSelectPosition; bool _isRangeSelectionMode = false; - int _prevItemStreamListCellSize; - double _calculatedMaxExtent; - Orientation _prevOrientation; final _items = []; final _selectedItems = {}; - // this unique token is there to keep the global key unique - final _uniqueToken = Uuid().v4(); - GlobalObjectKey _gridKey; + final _listKey = GlobalKey(); + double _calculatedMaxExtent; /// used to gain focus on web for keyboard support final _keyboardFocus = FocusNode(); @@ -299,10 +245,6 @@ mixin SelectableItemStreamListMixin "widget.selectable_item_stream_list_mixin.SelectableItemStreamListMixin"); } -class _GridKey extends GlobalObjectKey { - const _GridKey(Object value) : super(value); -} - class _SelectableItemWidget extends StatelessWidget { _SelectableItemWidget({ Key key,