Extract photo item list

This commit is contained in:
Ming Ming 2021-07-05 23:18:43 +08:00
parent d729834185
commit f191317627
3 changed files with 146 additions and 81 deletions

View file

@ -94,16 +94,6 @@ class _HomePhotosState extends State<HomePhotos>
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<HomePhotos>
final ScrollController _scrollController = ScrollController();
double _prevListWidth;
double _appBarExtent;
static final _log = Logger("widget.home_photos._HomePhotosState");

View file

@ -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<double> onMaxExtentChanged;
}
class _MeasurableItemListState extends State<MeasurableItemList>
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);
}

View file

@ -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<T extends StatefulWidget>
on State<T>, WidgetsBindingObserver {
mixin SelectableItemStreamListMixin<T extends StatefulWidget> on State<T> {
@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<T extends StatefulWidget>
@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<T extends StatefulWidget>
_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<T extends StatefulWidget>
_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<T extends StatefulWidget>
int _lastSelectPosition;
bool _isRangeSelectionMode = false;
int _prevItemStreamListCellSize;
double _calculatedMaxExtent;
Orientation _prevOrientation;
final _items = <SelectableItemStreamListItem>[];
final _selectedItems = <SelectableItemStreamListItem>{};
// 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<T extends StatefulWidget>
"widget.selectable_item_stream_list_mixin.SelectableItemStreamListMixin");
}
class _GridKey extends GlobalObjectKey {
const _GridKey(Object value) : super(value);
}
class _SelectableItemWidget extends StatelessWidget {
_SelectableItemWidget({
Key key,