Unverified Commit a02cd540 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Revert "[framework] SliverDecoration" (#107561)

parent 95a5a8d7
......@@ -68,8 +68,6 @@ import 'image_provider.dart';
///
/// * [DecoratedBox] and [Container], widgets that can be configured with
/// [BoxDecoration] objects.
/// * [SliverDecoration],a widget that can be configured with [BoxDecoration]
/// objects that is specialized for rendering with slivers.
/// * [CustomPaint], a widget that lets you draw arbitrary graphics.
/// * [Decoration], the base class which lets you define other decorations.
class BoxDecoration extends Decoration {
......
......@@ -52,7 +52,6 @@ import 'image.dart';
/// * [Decoration], which you can extend to provide other effects with
/// [DecoratedBox].
/// * [CustomPaint], another way to draw custom effects from the widget layer.
/// * [SliverDecoration], which paints a [Decoration] around a sliver.
class DecoratedBox extends SingleChildRenderObjectWidget {
/// Creates a widget that paints a [Decoration].
///
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/rendering.dart';
import 'basic.dart';
import 'framework.dart';
import 'image.dart';
/// A sliver widget that paints a [Decoration] either before or after its child
/// paints.
///
/// Unlike [DecoratedBox], this widget expects its child to be a sliver, and
/// must be placed in a widget that expects a sliver. This can allow decorating the
/// background of a [SliverList] without resorting to a shrink-wrapped list view or
/// column layout.
///
/// Commonly used with [BoxDecoration].
///
/// The [child] is not clipped. To clip a child to the shape of a particular
/// [ShapeDecoration], consider using a [ClipPath] widget.
///
/// {@tool snippet}
///
/// This sample shows a radial gradient that draws a moon on a night sky:
///
/// ```dart
/// SliverDecoration(
/// decoration: const BoxDecoration(
/// gradient: RadialGradient(
/// center: Alignment(-0.5, -0.6),
/// radius: 0.15,
/// colors: <Color>[
/// Color(0xFFEEEEEE),
/// Color(0xFF111133),
/// ],
/// stops: <double>[0.9, 1.0],
/// ),
/// ),
/// sliver: SliverList(
/// delegate: SliverChildListDelegate(<Widget>[
/// const Text('Goodnight Moon'),
/// ]),
/// ),
/// )
/// ```
/// {@end-tool}
///
/// See also:
///
/// * [DecoratedBox], the version of this class that works with regular widgets.
/// * [Decoration], which you can extend to provide other effects with
/// [SliverDecoration].
/// * [CustomPaint], another way to draw custom effects from the widget layer.
class SliverDecoration extends SingleChildRenderObjectWidget {
/// Creates a widget that paints a [Decoration].
///
/// The [decoration] and [position] arguments must not be null. By default the
/// decoration paints behind the child.
const SliverDecoration({
super.key,
required this.decoration,
this.position = DecorationPosition.background,
Widget? sliver,
}) : super(child: sliver);
/// What decoration to paint.
///
/// Commonly a [BoxDecoration].
final Decoration decoration;
/// Whether to paint the box decoration behind or in front of the child.
final DecorationPosition position;
@override
RenderSliverDecoration createRenderObject(BuildContext context) {
return RenderSliverDecoration(
decoration: decoration,
position: position,
configuration: createLocalImageConfiguration(context),
);
}
@override
void updateRenderObject(BuildContext context, RenderSliverDecoration renderObject) {
renderObject
..decoration = decoration
..position = position
..configuration = createLocalImageConfiguration(context);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
final String label;
switch (position) {
case DecorationPosition.background:
label = 'bg';
break;
case DecorationPosition.foreground:
label = 'fg';
break;
}
properties.add(EnumProperty<DecorationPosition>('position', position, level: DiagnosticLevel.hidden));
properties.add(DiagnosticsProperty<Decoration>(label, decoration));
}
}
/// Paints a [Decoration] either before or after its child paints.
class RenderSliverDecoration extends RenderProxySliver {
/// Creates a decorated sliver.
///
/// The [decoration], [position], and [configuration] arguments must not be
/// null. By default the decoration paints behind the child.
///
/// The [ImageConfiguration] will be passed to the decoration (with the size
/// filled in) to let it resolve images.
RenderSliverDecoration({
required Decoration decoration,
DecorationPosition position = DecorationPosition.background,
ImageConfiguration configuration = ImageConfiguration.empty,
}) : _decoration = decoration,
_position = position,
_configuration = configuration;
/// What decoration to paint.
///
/// Commonly a [BoxDecoration].
Decoration get decoration => _decoration;
Decoration _decoration;
set decoration(Decoration value) {
if (value == decoration) {
return;
}
_decoration = value;
_painter?.dispose();
_painter = decoration.createBoxPainter(markNeedsPaint);
markNeedsPaint();
}
/// Whether to paint the box decoration behind or in front of the child.
DecorationPosition get position => _position;
DecorationPosition _position;
set position(DecorationPosition value) {
if (value == position) {
return;
}
_position = value;
markNeedsPaint();
}
/// The settings to pass to the decoration when painting, so that it can
/// resolve images appropriately. See [ImageProvider.resolve] and
/// [BoxPainter.paint].
///
/// The [ImageConfiguration.textDirection] field is also used by
/// direction-sensitive [Decoration]s for painting and hit-testing.
ImageConfiguration get configuration => _configuration;
ImageConfiguration _configuration;
set configuration(ImageConfiguration value) {
if (value == configuration) {
return;
}
_configuration = value;
markNeedsPaint();
}
BoxPainter? _painter;
@override
void attach(covariant PipelineOwner owner) {
_painter = decoration.createBoxPainter(markNeedsPaint);
super.attach(owner);
}
@override
void detach() {
_painter?.dispose();
_painter = null;
super.detach();
}
@override
void dispose() {
_painter?.dispose();
_painter = null;
super.dispose();
}
@override
void paint(PaintingContext context, Offset offset) {
if (child != null && child!.geometry!.visible) {
final SliverPhysicalParentData childParentData = child!.parentData! as SliverPhysicalParentData;
final Size childSize = Size(constraints.crossAxisExtent, child!.geometry!.paintExtent);
final Offset childOffset = offset + childParentData.paintOffset;
if (position == DecorationPosition.background) {
_painter!.paint(context.canvas, childOffset, configuration.copyWith(size: childSize));
}
context.paintChild(child!, offset + childParentData.paintOffset);
if (position == DecorationPosition.foreground) {
_painter!.paint(context.canvas, childOffset, configuration.copyWith(size: childSize));
}
}
}
}
......@@ -119,7 +119,6 @@ export 'src/widgets/shortcuts.dart';
export 'src/widgets/single_child_scroll_view.dart';
export 'src/widgets/size_changed_layout_notifier.dart';
export 'src/widgets/sliver.dart';
export 'src/widgets/sliver_decoration.dart';
export 'src/widgets/sliver_fill.dart';
export 'src/widgets/sliver_layout_builder.dart';
export 'src/widgets/sliver_persistent_header.dart';
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file is run as part of a reduced test set in CI on Mac and Windows
// machines.
@Tags(<String>['reduced-test-set'])
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('SliverDecoration creates, paints, and disposes BoxPainter', (WidgetTester tester) async {
final TestDecoration decoration = TestDecoration();
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverDecoration(
decoration: decoration,
sliver: const SliverToBoxAdapter(
child: SizedBox(width: 100, height: 100),
),
)
],
)
)
));
expect(decoration.painters, hasLength(1));
expect(decoration.painters.last.lastConfiguration!.size, const Size(800, 100));
expect(decoration.painters.last.lastOffset, Offset.zero);
expect(decoration.painters.last.disposed, false);
await tester.pumpWidget(const SizedBox());
expect(decoration.painters, hasLength(1));
expect(decoration.painters.last.disposed, true);
});
testWidgets('SliverDecoration can update box painter', (WidgetTester tester) async {
final TestDecoration decorationA = TestDecoration();
final TestDecoration decorationB = TestDecoration();
Decoration activateDecoration = decorationA;
late void Function(void Function()) localSetState;
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: StatefulBuilder(
builder:(BuildContext context, void Function(void Function()) setState) {
localSetState = setState;
return CustomScrollView(
slivers: <Widget>[
SliverDecoration(
decoration: activateDecoration,
sliver: const SliverToBoxAdapter(
child: SizedBox(width: 100, height: 100),
),
)
],
);
},
)
)
));
expect(decorationA.painters, hasLength(1));
expect(decorationA.painters.last.paintCount, 1);
expect(decorationB.painters, hasLength(0));
localSetState(() {
activateDecoration = decorationB;
});
await tester.pump();
expect(decorationA.painters, hasLength(1));
expect(decorationB.painters, hasLength(1));
expect(decorationB.painters.last.paintCount, 1);
});
testWidgets('SliverDecoration can update DecorationPosition', (WidgetTester tester) async {
final TestDecoration decoration = TestDecoration();
DecorationPosition activePosition = DecorationPosition.foreground;
late void Function(void Function()) localSetState;
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: StatefulBuilder(
builder:(BuildContext context, void Function(void Function()) setState) {
localSetState = setState;
return CustomScrollView(
slivers: <Widget>[
SliverDecoration(
decoration: decoration,
position: activePosition,
sliver: const SliverToBoxAdapter(
child: SizedBox(width: 100, height: 100),
),
)
],
);
},
)
)
));
expect(decoration.painters, hasLength(1));
expect(decoration.painters.last.paintCount, 1);
localSetState(() {
activePosition = DecorationPosition.background;
});
await tester.pump();
expect(decoration.painters, hasLength(1));
expect(decoration.painters.last.paintCount, 2);
});
testWidgets('SliverDecoration golden test', (WidgetTester tester) async {
const BoxDecoration decoration = BoxDecoration(
color: Colors.blue,
);
final Key backgroundKey = UniqueKey();
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: RepaintBoundary(
key: backgroundKey,
child: CustomScrollView(
slivers: <Widget>[
SliverDecoration(
decoration: decoration,
sliver: SliverPadding(
padding: const EdgeInsets.all(16),
sliver: SliverList(
delegate: SliverChildListDelegate.fixed(<Widget>[
Container(
height: 100,
color: Colors.red,
),
Container(
height: 100,
color: Colors.yellow,
),
Container(
height: 100,
color: Colors.red,
),
]),
),
),
),
],
),
),
)
));
await expectLater(find.byKey(backgroundKey), matchesGoldenFile('decorated_sliver.moon.background.png'));
final Key foregroundKey = UniqueKey();
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: RepaintBoundary(
key: foregroundKey,
child: CustomScrollView(
slivers: <Widget>[
SliverDecoration(
decoration: decoration,
position: DecorationPosition.foreground,
sliver: SliverPadding(
padding: const EdgeInsets.all(16),
sliver: SliverList(
delegate: SliverChildListDelegate.fixed(<Widget>[
Container(
height: 100,
color: Colors.red,
),
Container(
height: 100,
color: Colors.yellow,
),
Container(
height: 100,
color: Colors.red,
),
]),
),
),
),
],
),
),
)
));
await expectLater(find.byKey(foregroundKey), matchesGoldenFile('decorated_sliver.moon.foreground.png'));
});
}
class TestDecoration extends Decoration {
final List<TestBoxPainter> painters = <TestBoxPainter>[];
@override
BoxPainter createBoxPainter([VoidCallback? onChanged]) {
final TestBoxPainter painter = TestBoxPainter();
painters.add(painter);
return painter;
}
}
class TestBoxPainter extends BoxPainter {
Offset? lastOffset;
ImageConfiguration? lastConfiguration;
bool disposed = false;
int paintCount = 0;
@override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
lastOffset = offset;
lastConfiguration = configuration;
paintCount += 1;
}
@override
void dispose() {
assert(!disposed);
disposed = true;
super.dispose();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment