collection building review
This commit is contained in:
parent
1ac13796da
commit
0c202ac185
6 changed files with 205 additions and 173 deletions
|
@ -2,7 +2,6 @@ import 'dart:math';
|
||||||
|
|
||||||
import 'package:aves/model/collection_lens.dart';
|
import 'package:aves/model/collection_lens.dart';
|
||||||
import 'package:aves/model/collection_source.dart';
|
import 'package:aves/model/collection_source.dart';
|
||||||
import 'package:aves/model/image_entry.dart';
|
|
||||||
import 'package:aves/utils/android_file_utils.dart';
|
import 'package:aves/utils/android_file_utils.dart';
|
||||||
import 'package:aves/utils/constants.dart';
|
import 'package:aves/utils/constants.dart';
|
||||||
import 'package:aves/widgets/album/grid/header_album.dart';
|
import 'package:aves/widgets/album/grid/header_album.dart';
|
||||||
|
@ -14,13 +13,11 @@ import 'package:flutter/rendering.dart';
|
||||||
|
|
||||||
class SectionHeader extends StatelessWidget {
|
class SectionHeader extends StatelessWidget {
|
||||||
final CollectionLens collection;
|
final CollectionLens collection;
|
||||||
final Map<dynamic, List<ImageEntry>> sections;
|
|
||||||
final dynamic sectionKey;
|
final dynamic sectionKey;
|
||||||
|
|
||||||
const SectionHeader({
|
const SectionHeader({
|
||||||
Key key,
|
Key key,
|
||||||
@required this.collection,
|
@required this.collection,
|
||||||
@required this.sections,
|
|
||||||
@required this.sectionKey,
|
@required this.sectionKey,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,109 @@
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:aves/model/collection_lens.dart';
|
||||||
|
import 'package:aves/widgets/album/grid/header_generic.dart';
|
||||||
|
import 'package:aves/widgets/album/grid/list_sliver.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class SectionedListLayoutProvider extends StatelessWidget {
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
final CollectionLens collection;
|
||||||
|
final int columnCount;
|
||||||
|
final double scrollableWidth;
|
||||||
|
final double tileExtent;
|
||||||
|
|
||||||
|
SectionedListLayoutProvider({
|
||||||
|
@required this.collection,
|
||||||
|
@required this.scrollableWidth,
|
||||||
|
@required this.tileExtent,
|
||||||
|
@required this.child,
|
||||||
|
}) : columnCount = (scrollableWidth / tileExtent).round();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ProxyProvider0<SectionedListLayout>(
|
||||||
|
update: (context, __) => _updateLayouts(context),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
SectionedListLayout _updateLayouts(BuildContext context) {
|
||||||
|
debugPrint('$runtimeType _updateLayouts entries=${collection.entryCount} columnCount=$columnCount tileExtent=$tileExtent');
|
||||||
|
final sectionLayouts = <SectionLayout>[];
|
||||||
|
final showHeaders = collection.showHeaders;
|
||||||
|
final source = collection.source;
|
||||||
|
final sections = collection.sections;
|
||||||
|
final sectionKeys = sections.keys.toList();
|
||||||
|
var currentIndex = 0, currentOffset = 0.0;
|
||||||
|
sectionKeys.forEach((sectionKey) {
|
||||||
|
final sectionEntryCount = sections[sectionKey].length;
|
||||||
|
final sectionChildCount = 1 + (sectionEntryCount / columnCount).ceil();
|
||||||
|
|
||||||
|
final headerExtent = showHeaders ? SectionHeader.computeHeaderHeight(source, sectionKey, scrollableWidth) : 0.0;
|
||||||
|
|
||||||
|
final sectionFirstIndex = currentIndex;
|
||||||
|
currentIndex += sectionChildCount;
|
||||||
|
final sectionLastIndex = currentIndex - 1;
|
||||||
|
|
||||||
|
final sectionMinOffset = currentOffset;
|
||||||
|
currentOffset += headerExtent + tileExtent * (sectionChildCount - 1);
|
||||||
|
final sectionMaxOffset = currentOffset;
|
||||||
|
|
||||||
|
sectionLayouts.add(
|
||||||
|
SectionLayout(
|
||||||
|
sectionKey: sectionKey,
|
||||||
|
firstIndex: sectionFirstIndex,
|
||||||
|
lastIndex: sectionLastIndex,
|
||||||
|
minOffset: sectionMinOffset,
|
||||||
|
maxOffset: sectionMaxOffset,
|
||||||
|
headerExtent: headerExtent,
|
||||||
|
tileExtent: tileExtent,
|
||||||
|
builder: (context, listIndex) => _buildInSection(listIndex - sectionFirstIndex, collection, sectionKey),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return SectionedListLayout(sectionLayouts);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildInSection(int sectionChildIndex, CollectionLens collection, dynamic sectionKey) {
|
||||||
|
if (sectionChildIndex == 0) {
|
||||||
|
return collection.showHeaders
|
||||||
|
? SectionHeader(
|
||||||
|
collection: collection,
|
||||||
|
sectionKey: sectionKey,
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
sectionChildIndex--;
|
||||||
|
|
||||||
|
final section = collection.sections[sectionKey];
|
||||||
|
final sectionEntryCount = section.length;
|
||||||
|
|
||||||
|
final minEntryIndex = sectionChildIndex * columnCount;
|
||||||
|
final maxEntryIndex = min(sectionEntryCount, minEntryIndex + columnCount);
|
||||||
|
final children = <Widget>[];
|
||||||
|
for (var i = minEntryIndex; i < maxEntryIndex; i++) {
|
||||||
|
children.add(GridThumbnail(
|
||||||
|
collection: collection,
|
||||||
|
index: i,
|
||||||
|
entry: section[i],
|
||||||
|
tileExtent: tileExtent,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: children,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SectionedListLayout {
|
||||||
|
final List<SectionLayout> sectionLayouts;
|
||||||
|
|
||||||
|
const SectionedListLayout(this.sectionLayouts);
|
||||||
|
}
|
||||||
|
|
||||||
class SectionLayout {
|
class SectionLayout {
|
||||||
final dynamic sectionKey;
|
final dynamic sectionKey;
|
||||||
|
|
|
@ -1,111 +1,36 @@
|
||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:aves/model/collection_lens.dart';
|
import 'package:aves/model/collection_lens.dart';
|
||||||
import 'package:aves/model/image_entry.dart';
|
import 'package:aves/model/image_entry.dart';
|
||||||
import 'package:aves/widgets/album/grid/header_generic.dart';
|
|
||||||
import 'package:aves/widgets/album/grid/list_known_extent.dart';
|
import 'package:aves/widgets/album/grid/list_known_extent.dart';
|
||||||
import 'package:aves/widgets/album/grid/list_section_layout.dart';
|
import 'package:aves/widgets/album/grid/list_section_layout.dart';
|
||||||
import 'package:aves/widgets/album/thumbnail.dart';
|
import 'package:aves/widgets/album/thumbnail.dart';
|
||||||
import 'package:aves/widgets/album/transparent_material_page_route.dart';
|
import 'package:aves/widgets/album/transparent_material_page_route.dart';
|
||||||
import 'package:aves/widgets/fullscreen/fullscreen_page.dart';
|
import 'package:aves/widgets/fullscreen/fullscreen_page.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
// use a `SliverList` instead of multiple `SliverGrid` because having one `SliverGrid` per section does not scale up
|
// use a `SliverList` instead of multiple `SliverGrid` because having one `SliverGrid` per section does not scale up
|
||||||
// with the multiple `SliverGrid` solution, thumbnails at the beginning of each sections are built even though they are offscreen
|
// with the multiple `SliverGrid` solution, thumbnails at the beginning of each sections are built even though they are offscreen
|
||||||
// because of `RenderSliverMultiBoxAdaptor.addInitialChild` called by `RenderSliverGrid.performLayout` (line 547), as of Flutter v1.17.0
|
// because of `RenderSliverMultiBoxAdaptor.addInitialChild` called by `RenderSliverGrid.performLayout` (line 547), as of Flutter v1.17.0
|
||||||
class CollectionListSliver extends StatelessWidget {
|
class CollectionListSliver extends StatelessWidget {
|
||||||
final CollectionLens collection;
|
|
||||||
final bool showHeader;
|
|
||||||
final double scrollableWidth;
|
|
||||||
final int columnCount;
|
|
||||||
final double tileExtent;
|
|
||||||
|
|
||||||
CollectionListSliver({
|
|
||||||
Key key,
|
|
||||||
@required this.collection,
|
|
||||||
@required this.showHeader,
|
|
||||||
@required this.scrollableWidth,
|
|
||||||
@required this.tileExtent,
|
|
||||||
}) : columnCount = (scrollableWidth / tileExtent).round(),
|
|
||||||
super(key: key);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final sectionLayouts = <SectionLayout>[];
|
return Consumer<SectionedListLayout>(
|
||||||
final source = collection.source;
|
builder: (context, sectionedListLayout, child) {
|
||||||
final sections = collection.sections;
|
final sectionLayouts = sectionedListLayout.sectionLayouts;
|
||||||
final sectionKeys = sections.keys.toList();
|
final childCount = sectionLayouts.last.lastIndex + 1;
|
||||||
var currentIndex = 0, currentOffset = 0.0;
|
return SliverKnownExtentList(
|
||||||
sectionKeys.forEach((sectionKey) {
|
sectionLayouts: sectionLayouts,
|
||||||
final sectionEntryCount = sections[sectionKey].length;
|
delegate: SliverChildBuilderDelegate(
|
||||||
final sectionChildCount = 1 + (sectionEntryCount / columnCount).ceil();
|
(context, index) {
|
||||||
|
if (index >= childCount) return null;
|
||||||
final headerExtent = showHeader ? SectionHeader.computeHeaderHeight(source, sectionKey, scrollableWidth) : 0.0;
|
final sectionLayout = sectionLayouts.firstWhere((section) => section.hasChild(index), orElse: () => null);
|
||||||
|
return sectionLayout?.builder(context, index) ?? const SizedBox.shrink();
|
||||||
final sectionFirstIndex = currentIndex;
|
},
|
||||||
currentIndex += sectionChildCount;
|
childCount: childCount,
|
||||||
final sectionLastIndex = currentIndex - 1;
|
addAutomaticKeepAlives: false,
|
||||||
|
),
|
||||||
final sectionMinOffset = currentOffset;
|
);
|
||||||
currentOffset += headerExtent + tileExtent * (sectionChildCount - 1);
|
},
|
||||||
final sectionMaxOffset = currentOffset;
|
|
||||||
|
|
||||||
sectionLayouts.add(
|
|
||||||
SectionLayout(
|
|
||||||
sectionKey: sectionKey,
|
|
||||||
firstIndex: sectionFirstIndex,
|
|
||||||
lastIndex: sectionLastIndex,
|
|
||||||
minOffset: sectionMinOffset,
|
|
||||||
maxOffset: sectionMaxOffset,
|
|
||||||
headerExtent: headerExtent,
|
|
||||||
tileExtent: tileExtent,
|
|
||||||
builder: (context, listIndex) {
|
|
||||||
listIndex -= sectionFirstIndex;
|
|
||||||
if (listIndex == 0) {
|
|
||||||
return showHeader
|
|
||||||
? SectionHeader(
|
|
||||||
collection: collection,
|
|
||||||
sections: sections,
|
|
||||||
sectionKey: sectionKey,
|
|
||||||
)
|
|
||||||
: const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
listIndex--;
|
|
||||||
|
|
||||||
final section = sections[sectionKey];
|
|
||||||
final minEntryIndex = listIndex * columnCount;
|
|
||||||
final maxEntryIndex = min(sectionEntryCount, minEntryIndex + columnCount);
|
|
||||||
final children = <Widget>[];
|
|
||||||
for (var i = minEntryIndex; i < maxEntryIndex; i++) {
|
|
||||||
children.add(GridThumbnail(
|
|
||||||
collection: collection,
|
|
||||||
index: i,
|
|
||||||
entry: section[i],
|
|
||||||
tileExtent: tileExtent,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: children,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
final childCount = currentIndex;
|
|
||||||
|
|
||||||
return SliverKnownExtentList(
|
|
||||||
sectionLayouts: sectionLayouts,
|
|
||||||
delegate: SliverChildBuilderDelegate(
|
|
||||||
(context, index) {
|
|
||||||
if (index >= childCount) return null;
|
|
||||||
final sectionLayout = sectionLayouts.firstWhere((section) => section.hasChild(index), orElse: () => null);
|
|
||||||
return sectionLayout?.builder(context, index) ?? const SizedBox.shrink();
|
|
||||||
},
|
|
||||||
childCount: childCount,
|
|
||||||
addAutomaticKeepAlives: false,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,14 +13,14 @@ class GridScaleGestureDetector extends StatefulWidget {
|
||||||
final GlobalKey scrollableKey;
|
final GlobalKey scrollableKey;
|
||||||
final ValueNotifier<double> extentNotifier;
|
final ValueNotifier<double> extentNotifier;
|
||||||
final Size mqSize;
|
final Size mqSize;
|
||||||
final EdgeInsets mqPadding;
|
final double mqHorizontalPadding;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
const GridScaleGestureDetector({
|
const GridScaleGestureDetector({
|
||||||
this.scrollableKey,
|
this.scrollableKey,
|
||||||
@required this.extentNotifier,
|
@required this.extentNotifier,
|
||||||
@required this.mqSize,
|
@required this.mqSize,
|
||||||
@required this.mqPadding,
|
@required this.mqHorizontalPadding,
|
||||||
@required this.child,
|
@required this.child,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ class _GridScaleGestureDetectorState extends State<GridScaleGestureDetector> {
|
||||||
// sanitize and update grid layout if necessary
|
// sanitize and update grid layout if necessary
|
||||||
final newExtent = TileExtentManager.applyTileExtent(
|
final newExtent = TileExtentManager.applyTileExtent(
|
||||||
widget.mqSize,
|
widget.mqSize,
|
||||||
widget.mqPadding,
|
widget.mqHorizontalPadding,
|
||||||
tileExtentNotifier,
|
tileExtentNotifier,
|
||||||
newExtent: _scaledExtentNotifier.value,
|
newExtent: _scaledExtentNotifier.value,
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,6 +5,7 @@ import 'package:aves/model/mime_types.dart';
|
||||||
import 'package:aves/widgets/album/app_bar.dart';
|
import 'package:aves/widgets/album/app_bar.dart';
|
||||||
import 'package:aves/widgets/album/collection_page.dart';
|
import 'package:aves/widgets/album/collection_page.dart';
|
||||||
import 'package:aves/widgets/album/empty.dart';
|
import 'package:aves/widgets/album/empty.dart';
|
||||||
|
import 'package:aves/widgets/album/grid/list_section_layout.dart';
|
||||||
import 'package:aves/widgets/album/grid/list_sliver.dart';
|
import 'package:aves/widgets/album/grid/list_sliver.dart';
|
||||||
import 'package:aves/widgets/album/grid/scaling.dart';
|
import 'package:aves/widgets/album/grid/scaling.dart';
|
||||||
import 'package:aves/widgets/album/tile_extent_manager.dart';
|
import 'package:aves/widgets/album/tile_extent_manager.dart';
|
||||||
|
@ -30,84 +31,33 @@ class ThumbnailCollection extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: Selector<MediaQueryData, Tuple3<Size, EdgeInsets, double>>(
|
child: Selector<MediaQueryData, Tuple2<Size, double>>(
|
||||||
selector: (c, mq) => Tuple3(mq.size, mq.padding, mq.viewInsets.bottom),
|
selector: (context, mq) => Tuple2(mq.size, mq.padding.horizontal),
|
||||||
builder: (c, mq, child) {
|
builder: (context, mq, child) {
|
||||||
final mqSize = mq.item1;
|
final mqSize = mq.item1;
|
||||||
final mqPadding = mq.item2;
|
final mqHorizontalPadding = mq.item2;
|
||||||
final mqViewInsetsBottom = mq.item3;
|
TileExtentManager.applyTileExtent(mqSize, mqHorizontalPadding, _tileExtentNotifier);
|
||||||
TileExtentManager.applyTileExtent(mqSize, mqPadding, _tileExtentNotifier);
|
|
||||||
return Consumer<CollectionLens>(
|
return Consumer<CollectionLens>(
|
||||||
builder: (context, collection, child) {
|
builder: (context, collection, child) {
|
||||||
// debugPrint('$runtimeType collection builder entries=${collection.entryCount}');
|
final scrollView = _buildScrollView(collection);
|
||||||
final showHeaders = collection.showHeaders;
|
final draggable = _buildDraggableScrollView(scrollView);
|
||||||
return GridScaleGestureDetector(
|
final sectionedListLayoutProvider = ValueListenableBuilder<double>(
|
||||||
|
valueListenable: _tileExtentNotifier,
|
||||||
|
builder: (context, tileExtent, child) => SectionedListLayoutProvider(
|
||||||
|
collection: collection,
|
||||||
|
scrollableWidth: mqSize.width - mqHorizontalPadding,
|
||||||
|
tileExtent: tileExtent,
|
||||||
|
child: draggable,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final scaler = GridScaleGestureDetector(
|
||||||
scrollableKey: _scrollableKey,
|
scrollableKey: _scrollableKey,
|
||||||
extentNotifier: _tileExtentNotifier,
|
extentNotifier: _tileExtentNotifier,
|
||||||
mqSize: mqSize,
|
mqSize: mqSize,
|
||||||
mqPadding: mqPadding,
|
mqHorizontalPadding: mqHorizontalPadding,
|
||||||
child: ValueListenableBuilder<double>(
|
child: sectionedListLayoutProvider,
|
||||||
valueListenable: _tileExtentNotifier,
|
|
||||||
builder: (context, tileExtent, child) {
|
|
||||||
debugPrint('$runtimeType tileExtent builder entries=${collection.entryCount} tileExtent=$tileExtent');
|
|
||||||
final scrollView = CustomScrollView(
|
|
||||||
key: _scrollableKey,
|
|
||||||
primary: true,
|
|
||||||
// workaround to prevent scrolling the app bar away
|
|
||||||
// when there is no content and we use `SliverFillRemaining`
|
|
||||||
physics: collection.isEmpty ? const NeverScrollableScrollPhysics() : null,
|
|
||||||
slivers: [
|
|
||||||
CollectionAppBar(
|
|
||||||
stateNotifier: stateNotifier,
|
|
||||||
appBarHeightNotifier: _appBarHeightNotifier,
|
|
||||||
collection: collection,
|
|
||||||
),
|
|
||||||
collection.isEmpty
|
|
||||||
? SliverFillRemaining(
|
|
||||||
child: _buildEmptyCollectionPlaceholder(collection),
|
|
||||||
hasScrollBody: false,
|
|
||||||
)
|
|
||||||
: CollectionListSliver(
|
|
||||||
collection: collection,
|
|
||||||
showHeader: showHeaders,
|
|
||||||
scrollableWidth: mqSize.width - mqPadding.horizontal,
|
|
||||||
tileExtent: tileExtent,
|
|
||||||
),
|
|
||||||
SliverToBoxAdapter(
|
|
||||||
child: Selector<MediaQueryData, double>(
|
|
||||||
selector: (c, mq) => mq.viewInsets.bottom,
|
|
||||||
builder: (c, mqViewInsetsBottom, child) {
|
|
||||||
return SizedBox(height: mqViewInsetsBottom);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
return ValueListenableBuilder<double>(
|
|
||||||
valueListenable: _appBarHeightNotifier,
|
|
||||||
builder: (context, appBarHeight, child) {
|
|
||||||
return DraggableScrollbar(
|
|
||||||
heightScrollThumb: avesScrollThumbHeight,
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
scrollThumbBuilder: avesScrollThumbBuilder(
|
|
||||||
height: avesScrollThumbHeight,
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
),
|
|
||||||
controller: PrimaryScrollController.of(context),
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
// padding to keep scroll thumb between app bar above and nav bar below
|
|
||||||
top: appBarHeight,
|
|
||||||
bottom: mqViewInsetsBottom,
|
|
||||||
),
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: scrollView,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
return scaler;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -115,6 +65,62 @@ class ThumbnailCollection extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ScrollView _buildScrollView(CollectionLens collection) {
|
||||||
|
return CustomScrollView(
|
||||||
|
key: _scrollableKey,
|
||||||
|
primary: true,
|
||||||
|
// workaround to prevent scrolling the app bar away
|
||||||
|
// when there is no content and we use `SliverFillRemaining`
|
||||||
|
physics: collection.isEmpty ? const NeverScrollableScrollPhysics() : null,
|
||||||
|
slivers: [
|
||||||
|
CollectionAppBar(
|
||||||
|
stateNotifier: stateNotifier,
|
||||||
|
appBarHeightNotifier: _appBarHeightNotifier,
|
||||||
|
collection: collection,
|
||||||
|
),
|
||||||
|
collection.isEmpty
|
||||||
|
? SliverFillRemaining(
|
||||||
|
child: _buildEmptyCollectionPlaceholder(collection),
|
||||||
|
hasScrollBody: false,
|
||||||
|
)
|
||||||
|
: CollectionListSliver(),
|
||||||
|
SliverToBoxAdapter(
|
||||||
|
child: Selector<MediaQueryData, double>(
|
||||||
|
selector: (context, mq) => mq.viewInsets.bottom,
|
||||||
|
builder: (context, mqViewInsetsBottom, child) {
|
||||||
|
return SizedBox(height: mqViewInsetsBottom);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDraggableScrollView(ScrollView scrollView) {
|
||||||
|
return ValueListenableBuilder<double>(
|
||||||
|
valueListenable: _appBarHeightNotifier,
|
||||||
|
builder: (context, appBarHeight, child) => Selector<MediaQueryData, double>(
|
||||||
|
selector: (context, mq) => mq.viewInsets.bottom,
|
||||||
|
builder: (context, mqViewInsetsBottom, child) => DraggableScrollbar(
|
||||||
|
heightScrollThumb: avesScrollThumbHeight,
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
scrollThumbBuilder: avesScrollThumbBuilder(
|
||||||
|
height: avesScrollThumbHeight,
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
),
|
||||||
|
controller: PrimaryScrollController.of(context),
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
// padding to keep scroll thumb between app bar above and nav bar below
|
||||||
|
top: appBarHeight,
|
||||||
|
bottom: mqViewInsetsBottom,
|
||||||
|
),
|
||||||
|
child: scrollView,
|
||||||
|
),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildEmptyCollectionPlaceholder(CollectionLens collection) {
|
Widget _buildEmptyCollectionPlaceholder(CollectionLens collection) {
|
||||||
return collection.filters.any((filter) => filter is FavouriteFilter)
|
return collection.filters.any((filter) => filter is FavouriteFilter)
|
||||||
? const EmptyContent(
|
? const EmptyContent(
|
||||||
|
|
|
@ -8,8 +8,8 @@ class TileExtentManager {
|
||||||
static const int columnCountDefault = 4;
|
static const int columnCountDefault = 4;
|
||||||
static const double tileExtentMin = 46.0;
|
static const double tileExtentMin = 46.0;
|
||||||
|
|
||||||
static double applyTileExtent(Size mqSize, EdgeInsets mqPadding, ValueNotifier<double> extentNotifier, {double newExtent}) {
|
static double applyTileExtent(Size mqSize, double mqHorizontalPadding, ValueNotifier<double> extentNotifier, {double newExtent}) {
|
||||||
final availableWidth = mqSize.width - mqPadding.horizontal;
|
final availableWidth = mqSize.width - mqHorizontalPadding;
|
||||||
var numColumns;
|
var numColumns;
|
||||||
if ((newExtent ?? 0) == 0) {
|
if ((newExtent ?? 0) == 0) {
|
||||||
newExtent = extentNotifier.value;
|
newExtent = extentNotifier.value;
|
||||||
|
@ -20,7 +20,6 @@ class TileExtentManager {
|
||||||
if ((newExtent ?? 0) == 0) {
|
if ((newExtent ?? 0) == 0) {
|
||||||
numColumns = columnCountDefault;
|
numColumns = columnCountDefault;
|
||||||
} else {
|
} else {
|
||||||
debugPrint('TODO TLAD tileExtentMin=$tileExtentMin mqSize=$mqSize');
|
|
||||||
newExtent = newExtent.clamp(tileExtentMin, extentMaxForSize(mqSize));
|
newExtent = newExtent.clamp(tileExtentMin, extentMaxForSize(mqSize));
|
||||||
numColumns = max(columnCountMin, (availableWidth / newExtent).round());
|
numColumns = max(columnCountMin, (availableWidth / newExtent).round());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue