mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
WIP Commits separated as follows: - Update lints in analysis_options files - Run `dart fix --apply` - Clean up leftover analysis issues - Run `dart format .` in the right places. Local analysis and testing passes. Checking CI now. Part of https://github.com/flutter/flutter/issues/178827 - Adoption of flutter_lints in examples/api coming in a separate change (cc @loic-sharma) ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
575 lines
19 KiB
Dart
575 lines
19 KiB
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.
|
|
|
|
import 'dart:math' as math;
|
|
|
|
import 'package:flutter/foundation.dart' show clampDouble;
|
|
import 'package:flutter/gestures.dart' show DragStartBehavior;
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart' show CacheExtentStyle, ViewportOffset;
|
|
|
|
// BUILDER DELEGATE ---
|
|
|
|
final TwoDimensionalChildBuilderDelegate builderDelegate = TwoDimensionalChildBuilderDelegate(
|
|
maxXIndex: 5,
|
|
maxYIndex: 5,
|
|
builder: (BuildContext context, ChildVicinity vicinity) {
|
|
return Container(
|
|
key: ValueKey<ChildVicinity>(vicinity),
|
|
color: vicinity.xIndex.isEven && vicinity.yIndex.isEven
|
|
? Colors.amber[100]
|
|
: (vicinity.xIndex.isOdd && vicinity.yIndex.isOdd ? Colors.blueAccent[100] : null),
|
|
height: 200,
|
|
width: 200,
|
|
child: Center(child: Text('R${vicinity.xIndex}:C${vicinity.yIndex}')),
|
|
);
|
|
},
|
|
);
|
|
|
|
// Creates a simple 2D table of 200x200 squares with a builder delegate.
|
|
Widget simpleBuilderTest({
|
|
Axis mainAxis = Axis.vertical,
|
|
bool? primary,
|
|
ScrollableDetails? verticalDetails,
|
|
ScrollableDetails? horizontalDetails,
|
|
TwoDimensionalChildBuilderDelegate? delegate,
|
|
double? cacheExtent,
|
|
CacheExtentStyle? cacheExtentStyle,
|
|
DiagonalDragBehavior? diagonalDrag,
|
|
Clip? clipBehavior,
|
|
String? restorationID,
|
|
bool useCacheExtent = false,
|
|
bool applyDimensions = true,
|
|
bool forgetToLayoutChild = false,
|
|
bool setLayoutOffset = true,
|
|
}) {
|
|
return MaterialApp(
|
|
theme: ThemeData(useMaterial3: false),
|
|
restorationScopeId: restorationID,
|
|
home: Scaffold(
|
|
body: SimpleBuilderTableView(
|
|
mainAxis: mainAxis,
|
|
verticalDetails: verticalDetails ?? const ScrollableDetails.vertical(),
|
|
horizontalDetails: horizontalDetails ?? const ScrollableDetails.horizontal(),
|
|
cacheExtent: cacheExtent,
|
|
cacheExtentStyle: cacheExtentStyle,
|
|
useCacheExtent: useCacheExtent,
|
|
diagonalDragBehavior: diagonalDrag ?? DiagonalDragBehavior.none,
|
|
clipBehavior: clipBehavior ?? Clip.hardEdge,
|
|
delegate: delegate ?? builderDelegate,
|
|
applyDimensions: applyDimensions,
|
|
forgetToLayoutChild: forgetToLayoutChild,
|
|
setLayoutOffset: setLayoutOffset,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
class SimpleBuilderTableView extends TwoDimensionalScrollView {
|
|
const SimpleBuilderTableView({
|
|
super.key,
|
|
super.primary,
|
|
super.mainAxis = Axis.vertical,
|
|
super.verticalDetails = const ScrollableDetails.vertical(),
|
|
super.horizontalDetails = const ScrollableDetails.horizontal(),
|
|
required TwoDimensionalChildBuilderDelegate delegate,
|
|
super.cacheExtent,
|
|
super.cacheExtentStyle,
|
|
super.diagonalDragBehavior = DiagonalDragBehavior.none,
|
|
super.dragStartBehavior = DragStartBehavior.start,
|
|
super.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
|
|
super.clipBehavior = Clip.hardEdge,
|
|
this.useCacheExtent = false,
|
|
this.applyDimensions = true,
|
|
this.forgetToLayoutChild = false,
|
|
this.setLayoutOffset = true,
|
|
super.hitTestBehavior,
|
|
}) : super(delegate: delegate);
|
|
|
|
// Piped through for testing in RenderTwoDimensionalViewport
|
|
final bool useCacheExtent;
|
|
final bool applyDimensions;
|
|
final bool forgetToLayoutChild;
|
|
final bool setLayoutOffset;
|
|
|
|
@override
|
|
Widget buildViewport(
|
|
BuildContext context,
|
|
ViewportOffset verticalOffset,
|
|
ViewportOffset horizontalOffset,
|
|
) {
|
|
return SimpleBuilderTableViewport(
|
|
horizontalOffset: horizontalOffset,
|
|
horizontalAxisDirection: horizontalDetails.direction,
|
|
verticalOffset: verticalOffset,
|
|
verticalAxisDirection: verticalDetails.direction,
|
|
mainAxis: mainAxis,
|
|
delegate: delegate as TwoDimensionalChildBuilderDelegate,
|
|
cacheExtent: cacheExtent,
|
|
cacheExtentStyle: cacheExtentStyle,
|
|
clipBehavior: clipBehavior,
|
|
useCacheExtent: useCacheExtent,
|
|
applyDimensions: applyDimensions,
|
|
forgetToLayoutChild: forgetToLayoutChild,
|
|
setLayoutOffset: setLayoutOffset,
|
|
);
|
|
}
|
|
}
|
|
|
|
class SimpleBuilderTableViewport extends TwoDimensionalViewport {
|
|
const SimpleBuilderTableViewport({
|
|
super.key,
|
|
required super.verticalOffset,
|
|
required super.verticalAxisDirection,
|
|
required super.horizontalOffset,
|
|
required super.horizontalAxisDirection,
|
|
required TwoDimensionalChildBuilderDelegate delegate,
|
|
required super.mainAxis,
|
|
super.cacheExtent,
|
|
super.cacheExtentStyle,
|
|
super.clipBehavior = Clip.hardEdge,
|
|
this.useCacheExtent = false,
|
|
this.applyDimensions = true,
|
|
this.forgetToLayoutChild = false,
|
|
this.setLayoutOffset = true,
|
|
}) : super(delegate: delegate);
|
|
|
|
// Piped through for testing in RenderTwoDimensionalViewport
|
|
final bool useCacheExtent;
|
|
final bool applyDimensions;
|
|
final bool forgetToLayoutChild;
|
|
final bool setLayoutOffset;
|
|
|
|
@override
|
|
RenderTwoDimensionalViewport createRenderObject(BuildContext context) {
|
|
return RenderSimpleBuilderTableViewport(
|
|
horizontalOffset: horizontalOffset,
|
|
horizontalAxisDirection: horizontalAxisDirection,
|
|
verticalOffset: verticalOffset,
|
|
verticalAxisDirection: verticalAxisDirection,
|
|
mainAxis: mainAxis,
|
|
delegate: delegate as TwoDimensionalChildBuilderDelegate,
|
|
childManager: context as TwoDimensionalChildManager,
|
|
cacheExtent: cacheExtent,
|
|
cacheExtentStyle: cacheExtentStyle,
|
|
clipBehavior: clipBehavior,
|
|
useCacheExtent: useCacheExtent,
|
|
applyDimensions: applyDimensions,
|
|
forgetToLayoutChild: forgetToLayoutChild,
|
|
setLayoutOffset: setLayoutOffset,
|
|
);
|
|
}
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderSimpleBuilderTableViewport renderObject) {
|
|
renderObject
|
|
..horizontalOffset = horizontalOffset
|
|
..horizontalAxisDirection = horizontalAxisDirection
|
|
..verticalOffset = verticalOffset
|
|
..verticalAxisDirection = verticalAxisDirection
|
|
..mainAxis = mainAxis
|
|
..delegate = delegate
|
|
..cacheExtent = cacheExtent
|
|
..cacheExtentStyle = cacheExtentStyle
|
|
..clipBehavior = clipBehavior;
|
|
}
|
|
}
|
|
|
|
class RenderSimpleBuilderTableViewport extends RenderTwoDimensionalViewport {
|
|
RenderSimpleBuilderTableViewport({
|
|
required super.horizontalOffset,
|
|
required super.horizontalAxisDirection,
|
|
required super.verticalOffset,
|
|
required super.verticalAxisDirection,
|
|
required TwoDimensionalChildBuilderDelegate delegate,
|
|
required super.mainAxis,
|
|
required super.childManager,
|
|
super.cacheExtent,
|
|
super.cacheExtentStyle,
|
|
super.clipBehavior = Clip.hardEdge,
|
|
this.applyDimensions = true,
|
|
this.setLayoutOffset = true,
|
|
this.useCacheExtent = false,
|
|
this.forgetToLayoutChild = false,
|
|
}) : super(delegate: delegate);
|
|
|
|
// These are to test conditions to validate subclass implementations after
|
|
// layoutChildSequence
|
|
final bool applyDimensions;
|
|
final bool setLayoutOffset;
|
|
final bool useCacheExtent;
|
|
final bool forgetToLayoutChild;
|
|
|
|
RenderBox? testGetChildFor(ChildVicinity vicinity) => getChildFor(vicinity);
|
|
|
|
@override
|
|
TestExtendedParentData parentDataOf(RenderBox child) {
|
|
return super.parentDataOf(child) as TestExtendedParentData;
|
|
}
|
|
|
|
@override
|
|
void setupParentData(RenderBox child) {
|
|
if (child.parentData is! TestExtendedParentData) {
|
|
child.parentData = TestExtendedParentData();
|
|
}
|
|
}
|
|
|
|
@override
|
|
void layoutChildSequence() {
|
|
// Really simple table implementation for testing.
|
|
// Every child is 200x200 square
|
|
final double horizontalPixels = horizontalOffset.pixels;
|
|
final double verticalPixels = verticalOffset.pixels;
|
|
final double cacheExtentValue = useCacheExtent ? cacheExtent : 0.0;
|
|
final double horizontalCacheExtent;
|
|
final double verticalCacheExtent;
|
|
|
|
switch (cacheExtentStyle) {
|
|
case CacheExtentStyle.pixel:
|
|
horizontalCacheExtent = cacheExtentValue;
|
|
verticalCacheExtent = cacheExtentValue;
|
|
case CacheExtentStyle.viewport:
|
|
horizontalCacheExtent = viewportDimension.width * cacheExtentValue;
|
|
verticalCacheExtent = viewportDimension.height * cacheExtentValue;
|
|
}
|
|
|
|
final double viewportWidth = viewportDimension.width;
|
|
final double viewportHeight = viewportDimension.height;
|
|
final builderDelegate = delegate as TwoDimensionalChildBuilderDelegate;
|
|
|
|
final int maxRowIndex;
|
|
final int maxColumnIndex;
|
|
maxRowIndex = builderDelegate.maxYIndex ?? 5;
|
|
maxColumnIndex = builderDelegate.maxXIndex ?? 5;
|
|
|
|
final int leadingColumn = math.max(
|
|
((horizontalPixels - horizontalCacheExtent) / 200).floor(),
|
|
0,
|
|
);
|
|
final int leadingRow = math.max(((verticalPixels - verticalCacheExtent) / 200).floor(), 0);
|
|
final int trailingColumn = math.min(
|
|
((horizontalPixels + viewportWidth + horizontalCacheExtent) / 200).ceil(),
|
|
maxColumnIndex,
|
|
);
|
|
final int trailingRow = math.min(
|
|
((verticalPixels + viewportHeight + verticalCacheExtent) / 200).ceil(),
|
|
maxRowIndex,
|
|
);
|
|
|
|
double xLayoutOffset = (leadingColumn * 200) - horizontalOffset.pixels;
|
|
for (var column = leadingColumn; column <= trailingColumn; column++) {
|
|
double yLayoutOffset = (leadingRow * 200) - verticalOffset.pixels;
|
|
for (var row = leadingRow; row <= trailingRow; row++) {
|
|
final vicinity = ChildVicinity(xIndex: column, yIndex: row);
|
|
final RenderBox? child = buildOrObtainChildFor(vicinity);
|
|
if (!forgetToLayoutChild) {
|
|
child?.layout(constraints.tighten(width: 200.0, height: 200.0));
|
|
}
|
|
|
|
if (setLayoutOffset && child != null) {
|
|
parentDataOf(child).layoutOffset = Offset(xLayoutOffset, yLayoutOffset);
|
|
}
|
|
yLayoutOffset += 200;
|
|
}
|
|
xLayoutOffset += 200;
|
|
}
|
|
if (applyDimensions) {
|
|
final double verticalExtent = 200 * (maxRowIndex + 1);
|
|
verticalOffset.applyContentDimensions(
|
|
0.0,
|
|
clampDouble(verticalExtent - viewportDimension.height, 0.0, double.infinity),
|
|
);
|
|
final double horizontalExtent = 200 * (maxColumnIndex + 1);
|
|
horizontalOffset.applyContentDimensions(
|
|
0.0,
|
|
clampDouble(horizontalExtent - viewportDimension.width, 0.0, double.infinity),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// LIST DELEGATE ---
|
|
final List<List<Widget>> children = List<List<Widget>>.generate(100, (int xIndex) {
|
|
return List<Widget>.generate(100, (int yIndex) {
|
|
return Container(
|
|
color: xIndex.isEven && yIndex.isEven
|
|
? Colors.amber[100]
|
|
: (xIndex.isOdd && yIndex.isOdd ? Colors.blueAccent[100] : null),
|
|
height: 200,
|
|
width: 200,
|
|
child: Center(child: Text('R$xIndex:C$yIndex')),
|
|
);
|
|
});
|
|
});
|
|
|
|
// Builds a simple 2D table of 200x200 squares with a list delegate.
|
|
Widget simpleListTest({
|
|
Axis mainAxis = Axis.vertical,
|
|
bool? primary,
|
|
ScrollableDetails? verticalDetails,
|
|
ScrollableDetails? horizontalDetails,
|
|
TwoDimensionalChildListDelegate? delegate,
|
|
double? cacheExtent,
|
|
CacheExtentStyle? cacheExtentStyle,
|
|
DiagonalDragBehavior? diagonalDrag,
|
|
Clip? clipBehavior,
|
|
}) {
|
|
return MaterialApp(
|
|
home: Scaffold(
|
|
body: SimpleListTableView(
|
|
mainAxis: mainAxis,
|
|
verticalDetails: verticalDetails ?? const ScrollableDetails.vertical(),
|
|
horizontalDetails: horizontalDetails ?? const ScrollableDetails.horizontal(),
|
|
cacheExtent: cacheExtent,
|
|
cacheExtentStyle: cacheExtentStyle,
|
|
diagonalDragBehavior: diagonalDrag ?? DiagonalDragBehavior.none,
|
|
clipBehavior: clipBehavior ?? Clip.hardEdge,
|
|
delegate: delegate ?? TwoDimensionalChildListDelegate(children: children),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
class SimpleListTableView extends TwoDimensionalScrollView {
|
|
const SimpleListTableView({
|
|
super.key,
|
|
super.primary,
|
|
super.mainAxis = Axis.vertical,
|
|
super.verticalDetails = const ScrollableDetails.vertical(),
|
|
super.horizontalDetails = const ScrollableDetails.horizontal(),
|
|
required TwoDimensionalChildListDelegate delegate,
|
|
super.cacheExtent,
|
|
super.cacheExtentStyle,
|
|
super.diagonalDragBehavior = DiagonalDragBehavior.none,
|
|
super.dragStartBehavior = DragStartBehavior.start,
|
|
super.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
|
|
super.clipBehavior = Clip.hardEdge,
|
|
}) : super(delegate: delegate);
|
|
|
|
@override
|
|
Widget buildViewport(
|
|
BuildContext context,
|
|
ViewportOffset verticalOffset,
|
|
ViewportOffset horizontalOffset,
|
|
) {
|
|
return SimpleListTableViewport(
|
|
horizontalOffset: horizontalOffset,
|
|
horizontalAxisDirection: horizontalDetails.direction,
|
|
verticalOffset: verticalOffset,
|
|
verticalAxisDirection: verticalDetails.direction,
|
|
mainAxis: mainAxis,
|
|
delegate: delegate as TwoDimensionalChildListDelegate,
|
|
cacheExtent: cacheExtent,
|
|
cacheExtentStyle: cacheExtentStyle,
|
|
clipBehavior: clipBehavior,
|
|
);
|
|
}
|
|
}
|
|
|
|
class SimpleListTableViewport extends TwoDimensionalViewport {
|
|
const SimpleListTableViewport({
|
|
super.key,
|
|
required super.verticalOffset,
|
|
required super.verticalAxisDirection,
|
|
required super.horizontalOffset,
|
|
required super.horizontalAxisDirection,
|
|
required TwoDimensionalChildListDelegate delegate,
|
|
required super.mainAxis,
|
|
super.cacheExtent,
|
|
super.cacheExtentStyle,
|
|
super.clipBehavior = Clip.hardEdge,
|
|
}) : super(delegate: delegate);
|
|
|
|
@override
|
|
RenderTwoDimensionalViewport createRenderObject(BuildContext context) {
|
|
return RenderSimpleListTableViewport(
|
|
horizontalOffset: horizontalOffset,
|
|
horizontalAxisDirection: horizontalAxisDirection,
|
|
verticalOffset: verticalOffset,
|
|
verticalAxisDirection: verticalAxisDirection,
|
|
mainAxis: mainAxis,
|
|
delegate: delegate as TwoDimensionalChildListDelegate,
|
|
childManager: context as TwoDimensionalChildManager,
|
|
cacheExtent: cacheExtent,
|
|
cacheExtentStyle: cacheExtentStyle,
|
|
clipBehavior: clipBehavior,
|
|
);
|
|
}
|
|
|
|
@override
|
|
void updateRenderObject(BuildContext context, RenderSimpleListTableViewport renderObject) {
|
|
renderObject
|
|
..horizontalOffset = horizontalOffset
|
|
..horizontalAxisDirection = horizontalAxisDirection
|
|
..verticalOffset = verticalOffset
|
|
..verticalAxisDirection = verticalAxisDirection
|
|
..mainAxis = mainAxis
|
|
..delegate = delegate
|
|
..cacheExtent = cacheExtent
|
|
..cacheExtentStyle = cacheExtentStyle
|
|
..clipBehavior = clipBehavior;
|
|
}
|
|
}
|
|
|
|
class RenderSimpleListTableViewport extends RenderTwoDimensionalViewport {
|
|
RenderSimpleListTableViewport({
|
|
required super.horizontalOffset,
|
|
required super.horizontalAxisDirection,
|
|
required super.verticalOffset,
|
|
required super.verticalAxisDirection,
|
|
required TwoDimensionalChildListDelegate delegate,
|
|
required super.mainAxis,
|
|
required super.childManager,
|
|
super.cacheExtent,
|
|
super.cacheExtentStyle,
|
|
super.clipBehavior = Clip.hardEdge,
|
|
}) : super(delegate: delegate);
|
|
|
|
@override
|
|
void layoutChildSequence() {
|
|
// Really simple table implementation for testing.
|
|
// Every child is 200x200 square
|
|
final double horizontalPixels = horizontalOffset.pixels;
|
|
final double verticalPixels = verticalOffset.pixels;
|
|
final listDelegate = delegate as TwoDimensionalChildListDelegate;
|
|
final int rowCount;
|
|
final int columnCount;
|
|
rowCount = listDelegate.children.length;
|
|
columnCount = listDelegate.children[0].length;
|
|
|
|
final int leadingColumn = math.max((horizontalPixels / 200).floor(), 0);
|
|
final int leadingRow = math.max((verticalPixels / 200).floor(), 0);
|
|
final int trailingColumn = math.min(
|
|
((horizontalPixels + viewportDimension.width) / 200).ceil(),
|
|
columnCount - 1,
|
|
);
|
|
final int trailingRow = math.min(
|
|
((verticalPixels + viewportDimension.height) / 200).ceil(),
|
|
rowCount - 1,
|
|
);
|
|
|
|
double xLayoutOffset = (leadingColumn * 200) - horizontalOffset.pixels;
|
|
for (var column = leadingColumn; column <= trailingColumn; column++) {
|
|
double yLayoutOffset = (leadingRow * 200) - verticalOffset.pixels;
|
|
for (var row = leadingRow; row <= trailingRow; row++) {
|
|
final vicinity = ChildVicinity(xIndex: column, yIndex: row);
|
|
final RenderBox child = buildOrObtainChildFor(vicinity)!;
|
|
child.layout(constraints.tighten(width: 200.0, height: 200.0));
|
|
|
|
parentDataOf(child).layoutOffset = Offset(xLayoutOffset, yLayoutOffset);
|
|
yLayoutOffset += 200;
|
|
}
|
|
xLayoutOffset += 200;
|
|
}
|
|
|
|
verticalOffset.applyContentDimensions(
|
|
0.0,
|
|
math.max(200 * rowCount - viewportDimension.height, 0.0),
|
|
);
|
|
horizontalOffset.applyContentDimensions(
|
|
0,
|
|
math.max(200 * columnCount - viewportDimension.width, 0.0),
|
|
);
|
|
}
|
|
}
|
|
|
|
class KeepAliveCheckBox extends StatefulWidget {
|
|
const KeepAliveCheckBox({super.key});
|
|
|
|
@override
|
|
KeepAliveCheckBoxState createState() => KeepAliveCheckBoxState();
|
|
}
|
|
|
|
class KeepAliveCheckBoxState extends State<KeepAliveCheckBox> with AutomaticKeepAliveClientMixin {
|
|
bool checkValue = false;
|
|
|
|
@override
|
|
bool get wantKeepAlive => _wantKeepAlive;
|
|
bool _wantKeepAlive = false;
|
|
set wantKeepAlive(bool value) {
|
|
if (_wantKeepAlive != value) {
|
|
_wantKeepAlive = value;
|
|
updateKeepAlive();
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
super.build(context);
|
|
return Checkbox(
|
|
value: checkValue,
|
|
onChanged: (bool? value) {
|
|
if (checkValue != value) {
|
|
setState(() {
|
|
checkValue = value!;
|
|
wantKeepAlive = value;
|
|
});
|
|
}
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
// TwoDimensionalViewportParentData already mixes in KeepAliveParentDataMixin,
|
|
// and so should be compatible with both the KeepAlive and
|
|
// TestParentDataWidget ParentDataWidgets.
|
|
// This ParentData is set up above as part of the
|
|
// RenderSimpleBuilderTableViewport for testing.
|
|
class TestExtendedParentData extends TwoDimensionalViewportParentData {
|
|
int? testValue;
|
|
}
|
|
|
|
class TestParentDataWidget extends ParentDataWidget<TestExtendedParentData> {
|
|
const TestParentDataWidget({super.key, required super.child, this.testValue});
|
|
|
|
final int? testValue;
|
|
|
|
@override
|
|
void applyParentData(RenderObject renderObject) {
|
|
assert(renderObject.parentData is TestExtendedParentData);
|
|
final parentData = renderObject.parentData! as TestExtendedParentData;
|
|
parentData.testValue = testValue;
|
|
}
|
|
|
|
@override
|
|
Type get debugTypicalAncestorWidgetClass => SimpleBuilderTableViewport;
|
|
}
|
|
|
|
class KeepAliveOnlyWhenHovered extends StatefulWidget {
|
|
const KeepAliveOnlyWhenHovered({required this.child, super.key});
|
|
|
|
final Widget child;
|
|
|
|
@override
|
|
KeepAliveOnlyWhenHoveredState createState() => KeepAliveOnlyWhenHoveredState();
|
|
}
|
|
|
|
class KeepAliveOnlyWhenHoveredState extends State<KeepAliveOnlyWhenHovered>
|
|
with AutomaticKeepAliveClientMixin {
|
|
bool _hovered = false;
|
|
|
|
@override
|
|
bool get wantKeepAlive => _hovered;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
super.build(context);
|
|
return MouseRegion(
|
|
onEnter: (_) {
|
|
setState(() {
|
|
_hovered = true;
|
|
updateKeepAlive();
|
|
});
|
|
},
|
|
onExit: (_) {
|
|
setState(() {
|
|
_hovered = false;
|
|
updateKeepAlive();
|
|
});
|
|
},
|
|
child: widget.child,
|
|
);
|
|
}
|
|
}
|