nc-photos/app/lib/widget/measurable_item_list.dart

150 lines
4.4 KiB
Dart
Raw Normal View History

2021-07-05 17:18:43 +02:00
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';
2022-12-16 16:01:04 +01:00
import 'package:np_codegen/np_codegen.dart';
2021-07-05 17:18:43 +02:00
import 'package:uuid/uuid.dart';
2022-12-16 16:01:04 +01:00
part 'measurable_item_list.g.dart';
2021-07-05 17:18:43 +02:00
abstract class MeasurableItemListState {
void updateListHeight();
}
class MeasurableItemList extends StatefulWidget {
2021-09-15 08:58:06 +02:00
const MeasurableItemList({
2021-07-23 22:05:57 +02:00
Key? key,
required this.maxCrossAxisExtent,
required this.itemCount,
required this.itemBuilder,
required this.staggeredTileBuilder,
2021-08-22 22:03:13 +02:00
this.mainAxisSpacing = 0,
2021-07-05 17:18:43 +02:00
this.onMaxExtentChanged,
}) : super(key: key);
@override
createState() => _MeasurableItemListState();
final double maxCrossAxisExtent;
final int itemCount;
final IndexedWidgetBuilder itemBuilder;
final IndexedStaggeredTileBuilder staggeredTileBuilder;
2021-08-22 22:03:13 +02:00
final double mainAxisSpacing;
2021-07-23 22:05:57 +02:00
final ValueChanged<double?>? onMaxExtentChanged;
2021-07-05 17:18:43 +02:00
}
2022-12-16 16:01:04 +01:00
@npLog
2021-07-05 17:18:43 +02:00
class _MeasurableItemListState extends State<MeasurableItemList>
with WidgetsBindingObserver
implements MeasurableItemListState {
@override
initState() {
super.initState();
2022-06-20 13:49:58 +02:00
WidgetsBinding.instance.addPostFrameCallback((_) {
2021-07-05 17:18:43 +02:00
_prevOrientation = MediaQuery.of(context).orientation;
2022-06-20 13:49:58 +02:00
WidgetsBinding.instance.addObserver(this);
2021-07-05 17:18:43 +02:00
});
}
@override
dispose() {
2022-06-20 13:49:58 +02:00
WidgetsBinding.instance.removeObserver(this);
2021-07-05 17:18:43 +02:00
super.dispose();
}
@override
didChangeMetrics() {
2022-06-20 13:49:58 +02:00
WidgetsBinding.instance.addPostFrameCallback((_) {
2022-12-03 03:02:04 +01:00
if (mounted) {
final orientation = MediaQuery.of(context).orientation;
if (orientation != _prevOrientation) {
_log.info(
"[didChangeMetrics] updateListHeight: orientation changed: $orientation");
_prevOrientation = orientation;
updateListHeight();
}
2021-07-05 17:18:43 +02:00
}
});
}
@override
build(BuildContext context) {
2021-07-09 09:31:30 +02:00
// on mobile, LayoutBuilder conflicts with TextFields.
// See https://github.com/flutter/flutter/issues/63919
2021-07-05 17:18:43 +02:00
return SliverLayoutBuilder(builder: (context, constraints) {
2021-09-15 08:58:06 +02:00
_prevListWidth ??= constraints.crossAxisExtent;
2021-07-05 17:18:43 +02:00
if (constraints.crossAxisExtent != _prevListWidth) {
_log.info("[build] updateListHeight: list viewport width changed");
2022-06-20 13:49:58 +02:00
WidgetsBinding.instance.addPostFrameCallback((_) => updateListHeight());
2021-07-05 17:18:43 +02:00
_prevListWidth = constraints.crossAxisExtent;
}
// need to rebuild grid after cell size changed
final cellSize = widget.maxCrossAxisExtent;
2021-09-15 08:58:06 +02:00
_prevCellSize ??= cellSize;
2021-07-05 17:18:43 +02:00
if (cellSize != _prevCellSize) {
_log.info("[build] updateListHeight: cell size changed");
2022-06-20 13:49:58 +02:00
WidgetsBinding.instance.addPostFrameCallback((_) => updateListHeight());
2021-07-05 17:18:43 +02:00
_prevCellSize = cellSize;
}
_gridKey = _GridKey("$_uniqueToken $cellSize");
return MeasurableSliverStaggeredGrid.extentBuilder(
key: _gridKey,
maxCrossAxisExtent: widget.maxCrossAxisExtent,
itemCount: widget.itemCount,
itemBuilder: widget.itemBuilder,
staggeredTileBuilder: widget.staggeredTileBuilder,
2021-08-22 22:03:13 +02:00
mainAxisSpacing: widget.mainAxisSpacing,
2021-07-05 17:18:43 +02:00
);
});
}
@override
updateListHeight() {
2021-07-23 22:05:57 +02:00
double? newMaxExtent;
2021-07-05 17:18:43 +02:00
try {
2021-07-23 22:05:57 +02:00
final renderObj = _gridKey.currentContext!.findRenderObject()
2021-07-05 17:18:43 +02:00
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);
}
}
2021-07-23 22:05:57 +02:00
double? _prevListWidth;
double? _prevCellSize;
double? _maxExtent;
Orientation? _prevOrientation;
2021-07-05 17:18:43 +02:00
// this unique token is there to keep the global key unique
2021-09-15 08:58:06 +02:00
final _uniqueToken = const Uuid().v4();
late _GridKey _gridKey;
2021-07-05 17:18:43 +02:00
}
class _GridKey extends GlobalObjectKey {
const _GridKey(Object value) : super(value);
@override
operator ==(Object other) {
return other is _GridKey && value == other.value;
}
@override
get hashCode => value.hashCode;
2021-07-05 17:18:43 +02:00
}