|
|
|
@ -3,6 +3,7 @@
|
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:flutter/rendering.dart';
|
|
|
|
|
import 'package:flutter/services.dart';
|
|
|
|
|
|
|
|
|
|
const rowDivider = SizedBox(width: 20);
|
|
|
|
@ -26,26 +27,44 @@ class FirstComponentList extends StatelessWidget {
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
List<Widget> children = [
|
|
|
|
|
const Actions(),
|
|
|
|
|
colDivider,
|
|
|
|
|
const Communication(),
|
|
|
|
|
colDivider,
|
|
|
|
|
const Containment(),
|
|
|
|
|
if (!showSecondList) ...[
|
|
|
|
|
colDivider,
|
|
|
|
|
Navigation(scaffoldKey: scaffoldKey),
|
|
|
|
|
colDivider,
|
|
|
|
|
const Selection(),
|
|
|
|
|
colDivider,
|
|
|
|
|
const TextInputs()
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
List<double?> heights = List.filled(children.length, null);
|
|
|
|
|
|
|
|
|
|
// Fully traverse this list before moving on.
|
|
|
|
|
return FocusTraversalGroup(
|
|
|
|
|
child: ListView(
|
|
|
|
|
padding: showSecondList
|
|
|
|
|
? const EdgeInsetsDirectional.only(end: smallSpacing)
|
|
|
|
|
: EdgeInsets.zero,
|
|
|
|
|
children: [
|
|
|
|
|
const Actions(),
|
|
|
|
|
colDivider,
|
|
|
|
|
const Communication(),
|
|
|
|
|
colDivider,
|
|
|
|
|
const Containment(),
|
|
|
|
|
if (!showSecondList) ...[
|
|
|
|
|
colDivider,
|
|
|
|
|
Navigation(scaffoldKey: scaffoldKey),
|
|
|
|
|
colDivider,
|
|
|
|
|
const Selection(),
|
|
|
|
|
colDivider,
|
|
|
|
|
const TextInputs()
|
|
|
|
|
],
|
|
|
|
|
child: CustomScrollView(
|
|
|
|
|
slivers: [
|
|
|
|
|
SliverPadding(
|
|
|
|
|
padding: showSecondList
|
|
|
|
|
? const EdgeInsetsDirectional.only(end: smallSpacing)
|
|
|
|
|
: EdgeInsets.zero,
|
|
|
|
|
sliver: SliverList(
|
|
|
|
|
delegate: BuildSlivers(
|
|
|
|
|
heights: heights,
|
|
|
|
|
builder: (context, index) {
|
|
|
|
|
return _CacheHeight(
|
|
|
|
|
heights: heights,
|
|
|
|
|
index: index,
|
|
|
|
|
child: children[index],
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
@ -62,22 +81,128 @@ class SecondComponentList extends StatelessWidget {
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
|
List<Widget> children = [
|
|
|
|
|
Navigation(scaffoldKey: scaffoldKey),
|
|
|
|
|
colDivider,
|
|
|
|
|
const Selection(),
|
|
|
|
|
colDivider,
|
|
|
|
|
const TextInputs(),
|
|
|
|
|
];
|
|
|
|
|
List<double?> heights = List.filled(children.length, null);
|
|
|
|
|
|
|
|
|
|
// Fully traverse this list before moving on.
|
|
|
|
|
return FocusTraversalGroup(
|
|
|
|
|
child: ListView(
|
|
|
|
|
padding: const EdgeInsetsDirectional.only(end: smallSpacing),
|
|
|
|
|
children: <Widget>[
|
|
|
|
|
Navigation(scaffoldKey: scaffoldKey),
|
|
|
|
|
colDivider,
|
|
|
|
|
const Selection(),
|
|
|
|
|
colDivider,
|
|
|
|
|
const TextInputs(),
|
|
|
|
|
child: CustomScrollView(
|
|
|
|
|
slivers: [
|
|
|
|
|
SliverPadding(
|
|
|
|
|
padding: const EdgeInsetsDirectional.only(end: smallSpacing),
|
|
|
|
|
sliver: SliverList(
|
|
|
|
|
delegate: BuildSlivers(
|
|
|
|
|
heights: heights,
|
|
|
|
|
builder: (context, index) {
|
|
|
|
|
return _CacheHeight(
|
|
|
|
|
heights: heights,
|
|
|
|
|
index: index,
|
|
|
|
|
child: children[index],
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the content of a CustomScrollView does not change, then it's
|
|
|
|
|
// safe to cache the heights of each item as they are laid out. The
|
|
|
|
|
// sum of the cached heights are returned by an override of
|
|
|
|
|
// `SliverChildDelegate.estimateMaxScrollOffset`. The default version
|
|
|
|
|
// of this method bases its estimate on the average height of the
|
|
|
|
|
// visible items. The override ensures that the scrollbar thumb's
|
|
|
|
|
// size, which depends on the max scroll offset, will shrink smoothly
|
|
|
|
|
// as the contents of the list are exposed for the first time, and
|
|
|
|
|
// then remain fixed.
|
|
|
|
|
class _CacheHeight extends SingleChildRenderObjectWidget {
|
|
|
|
|
const _CacheHeight({
|
|
|
|
|
super.child,
|
|
|
|
|
required this.heights,
|
|
|
|
|
required this.index,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
final List<double?> heights;
|
|
|
|
|
final int index;
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
RenderObject createRenderObject(BuildContext context) {
|
|
|
|
|
return _RenderCacheHeight(
|
|
|
|
|
heights: heights,
|
|
|
|
|
index: index,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void updateRenderObject(
|
|
|
|
|
BuildContext context, _RenderCacheHeight renderObject) {
|
|
|
|
|
renderObject
|
|
|
|
|
..heights = heights
|
|
|
|
|
..index = index;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class _RenderCacheHeight extends RenderProxyBox {
|
|
|
|
|
_RenderCacheHeight({
|
|
|
|
|
required List<double?> heights,
|
|
|
|
|
required int index,
|
|
|
|
|
}) : _heights = heights,
|
|
|
|
|
_index = index,
|
|
|
|
|
super();
|
|
|
|
|
|
|
|
|
|
List<double?> _heights;
|
|
|
|
|
List<double?> get heights => _heights;
|
|
|
|
|
set heights(List<double?> value) {
|
|
|
|
|
if (value == _heights) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
_heights = value;
|
|
|
|
|
markNeedsLayout();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int _index;
|
|
|
|
|
int get index => _index;
|
|
|
|
|
set index(int value) {
|
|
|
|
|
if (value == index) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
_index = value;
|
|
|
|
|
markNeedsLayout();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void performLayout() {
|
|
|
|
|
super.performLayout();
|
|
|
|
|
heights[index] = size.height;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The heights information is used to override the `estimateMaxScrollOffset` and
|
|
|
|
|
// provide a more accurate estimation for the max scroll offset.
|
|
|
|
|
class BuildSlivers extends SliverChildBuilderDelegate {
|
|
|
|
|
BuildSlivers({
|
|
|
|
|
required NullableIndexedWidgetBuilder builder,
|
|
|
|
|
required this.heights,
|
|
|
|
|
}) : super(builder, childCount: heights.length);
|
|
|
|
|
|
|
|
|
|
final List<double?> heights;
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
double? estimateMaxScrollOffset(int firstIndex, int lastIndex,
|
|
|
|
|
double leadingScrollOffset, double trailingScrollOffset) {
|
|
|
|
|
return heights.reduce((sum, height) => (sum ?? 0) + (height ?? 0))!;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class Actions extends StatelessWidget {
|
|
|
|
|
const Actions({super.key});
|
|
|
|
|
|
|
|
|
|