Unverified Commit e64d44ca authored by xster's avatar xster Committed by GitHub

CupertinoPicker part 1 - create a generic ListWheelScrollView (#13783)

* Create ListWheelScrollView

* fix missing doc

* tweak docs a bit

* fix imports

* Add some tests

* review

* review and transform tests

* fix test

* repatch lost https://github.com/flutter/flutter/commit/977701cf695ec106a52dde9f3659a7b132826748

* review

* Review
parent de023bc6
......@@ -44,6 +44,7 @@ export 'src/rendering/flow.dart';
export 'src/rendering/image.dart';
export 'src/rendering/layer.dart';
export 'src/rendering/list_body.dart';
export 'src/rendering/list_wheel_viewport.dart';
export 'src/rendering/object.dart';
export 'src/rendering/paragraph.dart';
export 'src/rendering/performance_overlay.dart';
......
This diff is collapsed.
// Copyright 2017 The Chromium 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/foundation.dart';
import 'package:flutter/rendering.dart';
import 'framework.dart';
import 'scroll_controller.dart';
import 'scroll_physics.dart';
import 'scrollable.dart';
/// A box in which children on a wheel can be scrolled.
///
/// This widget is similar to a [ListView] but with the restriction that all
/// children must be the same size along the scrolling axis.
///
/// When the list is at the zero scroll offset, the first child is aligned with
/// the middle of the viewport. When the list is at the final scroll offset,
/// the last child is aligned with the middle of the viewport
///
/// The children are rendered as if rotating on a wheel instead of scrolling on
/// a plane.
class ListWheelScrollView extends StatelessWidget {
/// Creates a box in which children are scrolled on a wheel.
const ListWheelScrollView({
Key key,
this.controller,
this.physics,
this.diameterRatio: RenderListWheelViewport.defaultDiameterRatio,
this.perspective: RenderListWheelViewport.defaultPerspective,
@required this.itemExtent,
this.clipToSize: true,
this.renderChildrenOutsideViewport: false,
@required this.children,
}) : assert(diameterRatio != null),
assert(diameterRatio > 0.0, RenderListWheelViewport.diameterRatioZeroMessage),
assert(perspective != null),
assert(perspective > 0),
assert(perspective <= 0.01, RenderListWheelViewport.perspectiveTooHighMessage),
assert(itemExtent != null),
assert(itemExtent > 0),
assert(clipToSize != null),
assert(renderChildrenOutsideViewport != null),
assert(
!renderChildrenOutsideViewport || !clipToSize,
RenderListWheelViewport.clipToSizeAndRenderChildrenOutsideViewportConflict,
),
super(key: key);
/// An object that can be used to control the position to which this scroll
/// view is scrolled.
///
/// A [ScrollController] serves several purposes. It can be used to control
/// the initial scroll position (see [ScrollController.initialScrollOffset]).
/// It can be used to control whether the scroll view should automatically
/// save and restore its scroll position in the [PageStorage] (see
/// [ScrollController.keepScrollOffset]). It can be used to read the current
/// scroll position (see [ScrollController.offset]), or change it (see
/// [ScrollController.animateTo]).
final ScrollController controller;
/// How the scroll view should respond to user input.
///
/// For example, determines how the scroll view continues to animate after the
/// user stops dragging the scroll view.
///
/// Defaults to matching platform conventions.
final ScrollPhysics physics;
/// {@macro flutter.rendering.wheelList.diameterRatio}
final double diameterRatio;
/// {@macro flutter.rendering.wheelList.perspective}
final double perspective;
/// Size of each child in the main axis. Must not be null and must be
/// positive.
final double itemExtent;
/// {@macro flutter.rendering.wheelList.clipToSize}
final bool clipToSize;
/// {@macro flutter.rendering.wheelList.renderChildrenOutsideViewport}
final bool renderChildrenOutsideViewport;
/// List of children to scroll on top of the cylinder.
final List<Widget> children;
@override
Widget build(BuildContext context) {
return new Scrollable(
controller: controller,
physics: physics,
viewportBuilder: (BuildContext context, ViewportOffset offset) {
return new ListWheelViewport(
diameterRatio: diameterRatio,
perspective: perspective,
itemExtent: itemExtent,
clipToSize: clipToSize,
renderChildrenOutsideViewport: renderChildrenOutsideViewport,
offset: offset,
children: children,
);
},
);
}
}
/// A viewport showing a subset of children on a wheel.
///
/// Typically used with [ListWheelScrollView], this viewport is similar to
/// [Viewport] in that it shows a subset of children in a scrollable based
/// on the scrolling offset and the childrens' dimensions. But uses
/// [RenderListWheelViewport] to display the children on a wheel.
///
/// See also:
///
/// * [ListWheelScrollView], widget that combines this viewport with a scrollable.
/// * [RenderListWheelViewport], the render object that renders the children
/// on a wheel.
class ListWheelViewport extends MultiChildRenderObjectWidget {
ListWheelViewport({
Key key,
this.diameterRatio: RenderListWheelViewport.defaultDiameterRatio,
this.perspective: RenderListWheelViewport.defaultPerspective,
@required this.itemExtent,
this.clipToSize: true,
this.renderChildrenOutsideViewport: false,
@required this.offset,
List<Widget> children,
}) : assert(offset != null),
assert(diameterRatio != null),
assert(diameterRatio > 0, RenderListWheelViewport.diameterRatioZeroMessage),
assert(perspective != null),
assert(perspective > 0),
assert(perspective <= 0.01, RenderListWheelViewport.perspectiveTooHighMessage),
assert(itemExtent != null),
assert(itemExtent > 0),
assert(clipToSize != null),
assert(renderChildrenOutsideViewport != null),
assert(
!renderChildrenOutsideViewport || !clipToSize,
RenderListWheelViewport.clipToSizeAndRenderChildrenOutsideViewportConflict,
),
super(key: key, children: children);
/// {@macro flutter.rendering.wheelList.diameterRatio}
final double diameterRatio;
/// {@macro flutter.rendering.wheelList.perspective}
final double perspective;
/// {@macro flutter.rendering.wheelList.itemExtent}
final double itemExtent;
/// {@macro flutter.rendering.wheelList.clipToSize}
final bool clipToSize;
/// {@macro flutter.rendering.wheelList.renderChildrenOutsideViewport}
final bool renderChildrenOutsideViewport;
/// [ViewportOffset] object describing the content that should be visible
/// in the viewport.
final ViewportOffset offset;
@override
RenderListWheelViewport createRenderObject(BuildContext context) {
return new RenderListWheelViewport(
diameterRatio: diameterRatio,
perspective: perspective,
itemExtent: itemExtent,
clipToSize: clipToSize,
renderChildrenOutsideViewport: renderChildrenOutsideViewport,
offset: offset,
);
}
@override
void updateRenderObject(BuildContext context, RenderListWheelViewport renderObject) {
renderObject
..diameterRatio = diameterRatio
..perspective = perspective
..itemExtent = itemExtent
..clipToSize = clipToSize
..renderChildrenOutsideViewport = renderChildrenOutsideViewport
..offset = offset;
}
}
......@@ -45,6 +45,7 @@ export 'src/widgets/image.dart';
export 'src/widgets/image_icon.dart';
export 'src/widgets/implicit_animations.dart';
export 'src/widgets/layout_builder.dart';
export 'src/widgets/list_wheel_scroll_view.dart';
export 'src/widgets/localizations.dart';
export 'src/widgets/media_query.dart';
export 'src/widgets/modal_barrier.dart';
......
......@@ -74,6 +74,21 @@ typedef void _CanvasPainterFunction(Canvas canvas);
/// Patterns are subset matches, meaning that any calls not described by the
/// pattern are ignored. This allows, for instance, transforms to be skipped.
abstract class PaintPattern {
/// Indicates that a transform is expected next.
///
/// Calls are skipped until a call to [Canvas.transform] is found. The call's
/// arguments are compared to those provided here. If any fail to match, or if
/// no call to [Canvas.transform] is found, then the matcher fails.
///
/// Dynamic so matchers can be more easily passed in.
///
/// The `matrix4` argument is dynamic so it can be either a [Matcher], or a
/// [Float64List] of [double]s. If it is a [Float64List] of [double]s then
/// each value in the matrix must match in the expected matrix. A deep
/// matching [Matcher] such as [equals] can be used to test each value in the
/// matrix with utilities such as [moreOrLessEquals].
void transform({ dynamic matrix4 });
/// Indicates that a translation transform is expected next.
///
/// Calls are skipped until a call to [Canvas.translate] is found. The call's
......@@ -574,6 +589,11 @@ class _TestRecordingCanvasPaintsAssertionMatcher extends Matcher {
class _TestRecordingCanvasPatternMatcher extends _TestRecordingCanvasMatcher implements PaintPattern {
final List<_PaintPredicate> _predicates = <_PaintPredicate>[];
@override
void transform({ dynamic matrix4 }) {
_predicates.add(new _FunctionPaintPredicate(#transform, <dynamic>[matrix4]));
}
@override
void translate({ double x, double y }) {
_predicates.add(new _FunctionPaintPredicate(#translate, <dynamic>[x, y]));
......
......@@ -110,6 +110,14 @@ class TestRecordingPaintingContext implements PaintingContext {
canvas.restore();
}
@override
void pushTransform(bool needsCompositing, Offset offset, Matrix4 transform, PaintingContextCallback painter) {
canvas.save();
canvas.transform(transform.storage);
painter(this, offset);
canvas.restore();
}
@override
void noSuchMethod(Invocation invocation) { }
}
......
This diff is collapsed.
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