fix performance issues
This commit is contained in:
+41
-12
@@ -18,6 +18,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'
|
||||
show MdiIcons;
|
||||
@@ -68,6 +69,44 @@ _TopSitesGridLayout _resolveGridLayout(double width) {
|
||||
);
|
||||
}
|
||||
|
||||
/// Responsive grid delegate that resolves the column count from the available
|
||||
/// cross-axis extent at the *render* layer (in [getLayout]), mirroring
|
||||
/// [_resolveGridLayout].
|
||||
///
|
||||
/// This replaces a `SliverLayoutBuilder`, whose builder re-runs (and rebuilds
|
||||
/// the entire grid subtree) on every [SliverConstraints] change — i.e. on every
|
||||
/// scroll and every keyboard-inset animation frame — which rebuilt all
|
||||
/// top-site tiles each frame. The delegate recomputes grid metrics on layout
|
||||
/// without rebuilding any widgets.
|
||||
class _TopSitesGridDelegate extends SliverGridDelegate {
|
||||
const _TopSitesGridDelegate();
|
||||
|
||||
@override
|
||||
SliverGridLayout getLayout(SliverConstraints constraints) {
|
||||
final layout = _resolveGridLayout(constraints.crossAxisExtent);
|
||||
final crossAxisCount = layout.crossAxisCount;
|
||||
|
||||
final usableCrossAxisExtent =
|
||||
(constraints.crossAxisExtent -
|
||||
_gridCrossAxisSpacing * (crossAxisCount - 1))
|
||||
.clamp(0.0, double.infinity);
|
||||
final childCrossAxisExtent = usableCrossAxisExtent / crossAxisCount;
|
||||
final childMainAxisExtent = childCrossAxisExtent / layout.childAspectRatio;
|
||||
|
||||
return SliverGridRegularTileLayout(
|
||||
crossAxisCount: crossAxisCount,
|
||||
mainAxisStride: childMainAxisExtent + _gridMainAxisSpacing,
|
||||
crossAxisStride: childCrossAxisExtent + _gridCrossAxisSpacing,
|
||||
childMainAxisExtent: childMainAxisExtent,
|
||||
childCrossAxisExtent: childCrossAxisExtent,
|
||||
reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRelayout(_TopSitesGridDelegate oldDelegate) => false;
|
||||
}
|
||||
|
||||
class TopSitesSection extends HookConsumerWidget {
|
||||
final void Function(Uri uri) onUriSelected;
|
||||
|
||||
@@ -156,16 +195,8 @@ class _TopSitesGrid extends ConsumerWidget {
|
||||
|
||||
return SliverPadding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 4.0),
|
||||
sliver: SliverLayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final layout = _resolveGridLayout(constraints.crossAxisExtent);
|
||||
return SliverGrid.builder(
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: layout.crossAxisCount,
|
||||
mainAxisSpacing: _gridMainAxisSpacing,
|
||||
crossAxisSpacing: _gridCrossAxisSpacing,
|
||||
childAspectRatio: layout.childAspectRatio,
|
||||
),
|
||||
sliver: SliverGrid.builder(
|
||||
gridDelegate: const _TopSitesGridDelegate(),
|
||||
itemCount: displayItems.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = displayItems[index];
|
||||
@@ -177,8 +208,6 @@ class _TopSitesGrid extends ConsumerWidget {
|
||||
onRemove: () => _removeItem(context, ref, item),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -84,6 +84,24 @@ class ContainerColors {
|
||||
static const double surfaceHighAlpha = 0.28;
|
||||
static const double outlineBorderAlpha = 0.5;
|
||||
|
||||
/// Cache of seed schemes keyed by `(seed color, brightness)`.
|
||||
///
|
||||
/// [ColorScheme.fromSeed] runs the full HCT tonal-palette solver, which is
|
||||
/// very expensive (the `material_color_utilities` `HctSolver`/`DynamicColor`
|
||||
/// math dominated CPU profiles at hundreds of ms). The result is a pure
|
||||
/// function of its seed colour and brightness, so generating it once per
|
||||
/// distinct `(color, brightness)` and reusing it across rebuilds removes the
|
||||
/// cost entirely — previously seed mode computed a *full* scheme up to four
|
||||
/// times per [palette] call, for every chip, on every rebuild.
|
||||
static final Map<(int, Brightness), ColorScheme> _seedSchemeCache = {};
|
||||
|
||||
static ColorScheme _seedScheme(Color seedColor, Brightness brightness) {
|
||||
return _seedSchemeCache.putIfAbsent(
|
||||
(seedColor.toARGB32(), brightness),
|
||||
() => ColorScheme.fromSeed(seedColor: seedColor, brightness: brightness),
|
||||
);
|
||||
}
|
||||
|
||||
static ContainerColorPalette palette(
|
||||
BuildContext context,
|
||||
Color color, {
|
||||
@@ -93,30 +111,22 @@ class ContainerColors {
|
||||
final appScheme = theme.colorScheme;
|
||||
final fullColor = fullOpacity(color);
|
||||
|
||||
final seedScheme = useCustomColor
|
||||
? null
|
||||
: _seedScheme(fullColor, theme.brightness);
|
||||
|
||||
final containerColor = useCustomColor
|
||||
? fullColor
|
||||
: ColorScheme.fromSeed(
|
||||
seedColor: fullColor,
|
||||
brightness: theme.brightness,
|
||||
).primaryContainer;
|
||||
: seedScheme!.primaryContainer;
|
||||
final accentColor = useCustomColor
|
||||
? _shiftTone(fullColor, theme.brightness)
|
||||
: ColorScheme.fromSeed(
|
||||
seedColor: fullColor,
|
||||
brightness: theme.brightness,
|
||||
).primary;
|
||||
: seedScheme!.primary;
|
||||
final onContainerColor = useCustomColor
|
||||
? _contrastingForeground(containerColor)
|
||||
: ColorScheme.fromSeed(
|
||||
seedColor: fullColor,
|
||||
brightness: theme.brightness,
|
||||
).onPrimaryContainer;
|
||||
: seedScheme!.onPrimaryContainer;
|
||||
final onAccentColor = useCustomColor
|
||||
? _contrastingForeground(accentColor)
|
||||
: ColorScheme.fromSeed(
|
||||
seedColor: fullColor,
|
||||
brightness: theme.brightness,
|
||||
).onPrimary;
|
||||
: seedScheme!.onPrimary;
|
||||
|
||||
final surfaceColor = Color.alphaBlend(
|
||||
containerColor.withValues(alpha: surfaceAlpha),
|
||||
|
||||
Reference in New Issue
Block a user