feat : button , outlined button
fab button , fab outlined button , input , pagination widget's
This commit is contained in:
784
lib/presentation/widget/tabs/new_tab.dart
Normal file
784
lib/presentation/widget/tabs/new_tab.dart
Normal file
@@ -0,0 +1,784 @@
|
||||
// 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.
|
||||
|
||||
/// @docImport 'switch.dart';
|
||||
library;
|
||||
|
||||
import 'dart:collection';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
|
||||
// Minimum padding from edges of the segmented control to edges of
|
||||
// encompassing widget.
|
||||
const EdgeInsetsGeometry _kHorizontalItemPadding = EdgeInsets.symmetric(
|
||||
horizontal: 16.0,
|
||||
);
|
||||
|
||||
// Minimum height of the segmented control.
|
||||
const double _kMinSegmentedControlHeight = 28.0;
|
||||
|
||||
// The default color used for the text of the disabled segment.
|
||||
const Color _kDisableTextColor = Color.fromARGB(115, 122, 122, 122);
|
||||
|
||||
// The duration of the fade animation used to transition when a new widget
|
||||
// is selected.
|
||||
const Duration _kFadeDuration = Duration(milliseconds: 165);
|
||||
|
||||
class NewCupertinoSegmentedControl<T extends Object> extends StatefulWidget {
|
||||
/// Creates an iOS-style segmented control bar.
|
||||
///
|
||||
/// The [children] argument must be an ordered [Map] such as a
|
||||
/// [LinkedHashMap]. Further, the length of the [children] list must be
|
||||
/// greater than one.
|
||||
///
|
||||
/// Each widget value in the map of [children] must have an associated key
|
||||
/// that uniquely identifies this widget. This key is what will be returned
|
||||
/// in the [onValueChanged] callback when a new value from the [children] map
|
||||
/// is selected.
|
||||
///
|
||||
/// The [groupValue] is the currently selected value for the segmented control.
|
||||
/// If no [groupValue] is provided, or the [groupValue] is null, no widget will
|
||||
/// appear as selected. The [groupValue] must be either null or one of the keys
|
||||
/// in the [children] map.
|
||||
NewCupertinoSegmentedControl({
|
||||
super.key,
|
||||
required this.children,
|
||||
required this.onValueChanged,
|
||||
this.groupValue,
|
||||
this.unselectedColor,
|
||||
this.selectedColor,
|
||||
this.borderColor,
|
||||
this.pressedColor,
|
||||
this.disabledColor,
|
||||
this.disabledTextColor,
|
||||
this.padding,
|
||||
this.disabledChildren = const <Never>{},
|
||||
}) : assert(children.length >= 2),
|
||||
assert(
|
||||
groupValue == null ||
|
||||
children.keys.any((T child) => child == groupValue),
|
||||
'The groupValue must be either null or one of the keys in the children map.',
|
||||
);
|
||||
|
||||
/// The identifying keys and corresponding widget values in the
|
||||
/// segmented control.
|
||||
///
|
||||
/// The map must have more than one entry.
|
||||
/// This attribute must be an ordered [Map] such as a [LinkedHashMap].
|
||||
final Map<T, Widget> children;
|
||||
|
||||
/// The identifier of the widget that is currently selected.
|
||||
///
|
||||
/// This must be one of the keys in the [Map] of [children].
|
||||
/// If this attribute is null, no widget will be initially selected.
|
||||
final T? groupValue;
|
||||
|
||||
/// The callback that is called when a new option is tapped.
|
||||
///
|
||||
/// The segmented control passes the newly selected widget's associated key
|
||||
/// to the callback but does not actually change state until the parent
|
||||
/// widget rebuilds the segmented control with the new [groupValue].
|
||||
final ValueChanged<T> onValueChanged;
|
||||
|
||||
/// The color used to fill the backgrounds of unselected widgets and as the
|
||||
/// text color of the selected widget.
|
||||
///
|
||||
/// Defaults to [CupertinoTheme]'s `primaryContrastingColor` if null.
|
||||
final Color? unselectedColor;
|
||||
|
||||
/// The color used to fill the background of the selected widget and as the text
|
||||
/// color of unselected widgets.
|
||||
///
|
||||
/// Defaults to [CupertinoTheme]'s `primaryColor` if null.
|
||||
final Color? selectedColor;
|
||||
|
||||
/// The color used as the border around each widget.
|
||||
///
|
||||
/// Defaults to [CupertinoTheme]'s `primaryColor` if null.
|
||||
final Color? borderColor;
|
||||
|
||||
/// The color used to fill the background of the widget the user is
|
||||
/// temporarily interacting with through a long press or drag.
|
||||
///
|
||||
/// Defaults to the selectedColor at 20% opacity if null.
|
||||
final Color? pressedColor;
|
||||
|
||||
/// The color used to fill the background of the segment when it is disabled.
|
||||
///
|
||||
/// If null, this color will be 50% opacity of the [selectedColor] when
|
||||
/// the segment is selected. If the segment is unselected, this color will be
|
||||
/// set to [unselectedColor].
|
||||
final Color? disabledColor;
|
||||
|
||||
/// The color used for the text of the segment when it is disabled.
|
||||
final Color? disabledTextColor;
|
||||
|
||||
/// The CupertinoSegmentedControl will be placed inside this padding.
|
||||
///
|
||||
/// Defaults to EdgeInsets.symmetric(horizontal: 16.0)
|
||||
final EdgeInsetsGeometry? padding;
|
||||
|
||||
/// The set of identifying keys that correspond to the segments that should be disabled.
|
||||
///
|
||||
/// All segments are enabled by default.
|
||||
final Set<T> disabledChildren;
|
||||
|
||||
@override
|
||||
State<NewCupertinoSegmentedControl<T>> createState() =>
|
||||
_SegmentedControlState<T>();
|
||||
}
|
||||
|
||||
class _SegmentedControlState<T extends Object>
|
||||
extends State<NewCupertinoSegmentedControl<T>>
|
||||
with TickerProviderStateMixin<NewCupertinoSegmentedControl<T>> {
|
||||
T? _pressedKey;
|
||||
|
||||
final List<AnimationController> _selectionControllers =
|
||||
<AnimationController>[];
|
||||
final List<ColorTween> _childTweens = <ColorTween>[];
|
||||
|
||||
late ColorTween _forwardBackgroundColorTween;
|
||||
late ColorTween _reverseBackgroundColorTween;
|
||||
late ColorTween _textColorTween;
|
||||
|
||||
Color? _selectedColor;
|
||||
Color? _unselectedColor;
|
||||
Color? _borderColor;
|
||||
Color? _pressedColor;
|
||||
Color? _selectedDisabledColor;
|
||||
Color? _unselectedDisabledColor;
|
||||
Color? _disabledTextColor;
|
||||
|
||||
AnimationController createAnimationController() {
|
||||
return AnimationController(duration: _kFadeDuration, vsync: this)
|
||||
..addListener(() {
|
||||
setState(() {
|
||||
// State of background/text colors has changed
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bool _updateColors() {
|
||||
assert(mounted, 'This should only be called after didUpdateDependencies');
|
||||
bool changed = false;
|
||||
final Color disabledTextColor =
|
||||
widget.disabledTextColor ?? _kDisableTextColor;
|
||||
if (_disabledTextColor != disabledTextColor) {
|
||||
changed = true;
|
||||
_disabledTextColor = disabledTextColor;
|
||||
}
|
||||
final Color selectedColor =
|
||||
widget.selectedColor ?? CupertinoTheme.of(context).primaryColor;
|
||||
if (_selectedColor != selectedColor) {
|
||||
changed = true;
|
||||
_selectedColor = selectedColor;
|
||||
}
|
||||
final Color unselectedColor =
|
||||
widget.unselectedColor ??
|
||||
CupertinoTheme.of(context).primaryContrastingColor;
|
||||
if (_unselectedColor != unselectedColor) {
|
||||
changed = true;
|
||||
_unselectedColor = unselectedColor;
|
||||
}
|
||||
final Color selectedDisabledColor =
|
||||
widget.disabledColor ?? selectedColor.withOpacity(0.5);
|
||||
final Color unselectedDisabledColor =
|
||||
widget.disabledColor ?? unselectedColor;
|
||||
if (_selectedDisabledColor != selectedDisabledColor ||
|
||||
_unselectedDisabledColor != unselectedDisabledColor) {
|
||||
changed = true;
|
||||
_selectedDisabledColor = selectedDisabledColor;
|
||||
_unselectedDisabledColor = unselectedDisabledColor;
|
||||
}
|
||||
final Color borderColor =
|
||||
widget.borderColor ?? CupertinoTheme.of(context).primaryColor;
|
||||
if (_borderColor != borderColor) {
|
||||
changed = true;
|
||||
_borderColor = borderColor;
|
||||
}
|
||||
final Color pressedColor =
|
||||
widget.pressedColor ??
|
||||
CupertinoTheme.of(context).primaryColor.withOpacity(0.2);
|
||||
if (_pressedColor != pressedColor) {
|
||||
changed = true;
|
||||
_pressedColor = pressedColor;
|
||||
}
|
||||
|
||||
_forwardBackgroundColorTween = ColorTween(
|
||||
begin: _pressedColor,
|
||||
end: _selectedColor,
|
||||
);
|
||||
_reverseBackgroundColorTween = ColorTween(
|
||||
begin: _unselectedColor,
|
||||
end: _selectedColor,
|
||||
);
|
||||
_textColorTween = ColorTween(begin: _selectedColor, end: _unselectedColor);
|
||||
return changed;
|
||||
}
|
||||
|
||||
void _updateAnimationControllers() {
|
||||
assert(mounted, 'This should only be called after didUpdateDependencies');
|
||||
for (final AnimationController controller in _selectionControllers) {
|
||||
controller.dispose();
|
||||
}
|
||||
_selectionControllers.clear();
|
||||
_childTweens.clear();
|
||||
|
||||
for (final T key in widget.children.keys) {
|
||||
final AnimationController animationController =
|
||||
createAnimationController();
|
||||
if (widget.groupValue == key) {
|
||||
_childTweens.add(_reverseBackgroundColorTween);
|
||||
animationController.value = 1.0;
|
||||
} else {
|
||||
_childTweens.add(_forwardBackgroundColorTween);
|
||||
}
|
||||
_selectionControllers.add(animationController);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
|
||||
if (_updateColors()) {
|
||||
_updateAnimationControllers();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(NewCupertinoSegmentedControl<T> oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
|
||||
if (_updateColors() ||
|
||||
oldWidget.children.length != widget.children.length) {
|
||||
_updateAnimationControllers();
|
||||
}
|
||||
|
||||
if (oldWidget.groupValue != widget.groupValue) {
|
||||
int index = 0;
|
||||
for (final T key in widget.children.keys) {
|
||||
if (widget.groupValue == key) {
|
||||
_childTweens[index] = _forwardBackgroundColorTween;
|
||||
_selectionControllers[index].forward();
|
||||
} else {
|
||||
_childTweens[index] = _reverseBackgroundColorTween;
|
||||
_selectionControllers[index].reverse();
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
for (final AnimationController animationController
|
||||
in _selectionControllers) {
|
||||
animationController.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onTapDown(T currentKey) {
|
||||
if (_pressedKey == null && currentKey != widget.groupValue) {
|
||||
setState(() {
|
||||
_pressedKey = currentKey;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _onTapCancel() {
|
||||
setState(() {
|
||||
_pressedKey = null;
|
||||
});
|
||||
}
|
||||
|
||||
void _onTap(T currentKey) {
|
||||
if (currentKey != _pressedKey) {
|
||||
return;
|
||||
}
|
||||
if (!widget.disabledChildren.contains(currentKey)) {
|
||||
if (currentKey != widget.groupValue) {
|
||||
widget.onValueChanged(currentKey);
|
||||
}
|
||||
}
|
||||
_pressedKey = null;
|
||||
}
|
||||
|
||||
Color? getTextColor(int index, T currentKey) {
|
||||
if (widget.disabledChildren.contains(currentKey)) {
|
||||
return _disabledTextColor;
|
||||
}
|
||||
if (_selectionControllers[index].isAnimating) {
|
||||
return _textColorTween.evaluate(_selectionControllers[index]);
|
||||
}
|
||||
if (widget.groupValue == currentKey) {
|
||||
return _unselectedColor;
|
||||
}
|
||||
return _selectedColor;
|
||||
}
|
||||
|
||||
Color? getBackgroundColor(int index, T currentKey) {
|
||||
if (widget.disabledChildren.contains(currentKey)) {
|
||||
return widget.groupValue == currentKey
|
||||
? _selectedDisabledColor
|
||||
: _unselectedDisabledColor;
|
||||
}
|
||||
if (_selectionControllers[index].isAnimating) {
|
||||
return _childTweens[index].evaluate(_selectionControllers[index]);
|
||||
}
|
||||
if (widget.groupValue == currentKey) {
|
||||
return _selectedColor;
|
||||
}
|
||||
if (_pressedKey == currentKey) {
|
||||
return _pressedColor;
|
||||
}
|
||||
return _unselectedColor;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final List<Widget> gestureChildren = <Widget>[];
|
||||
final List<Color> backgroundColors = <Color>[];
|
||||
int index = 0;
|
||||
int? selectedIndex;
|
||||
int? pressedIndex;
|
||||
for (final T currentKey in widget.children.keys) {
|
||||
selectedIndex = (widget.groupValue == currentKey) ? index : selectedIndex;
|
||||
pressedIndex = (_pressedKey == currentKey) ? index : pressedIndex;
|
||||
|
||||
final TextStyle textStyle = DefaultTextStyle.of(
|
||||
context,
|
||||
).style.copyWith(color: getTextColor(index, currentKey));
|
||||
final IconThemeData iconTheme = IconThemeData(
|
||||
color: getTextColor(index, currentKey),
|
||||
);
|
||||
|
||||
Widget child = Center(child: widget.children[currentKey]);
|
||||
|
||||
child = MouseRegion(
|
||||
cursor: kIsWeb ? SystemMouseCursors.click : MouseCursor.defer,
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTapDown:
|
||||
widget.disabledChildren.contains(currentKey)
|
||||
? null
|
||||
: (TapDownDetails event) {
|
||||
_onTapDown(currentKey);
|
||||
},
|
||||
onTapCancel:
|
||||
widget.disabledChildren.contains(currentKey)
|
||||
? null
|
||||
: _onTapCancel,
|
||||
onTap: () {
|
||||
_onTap(currentKey);
|
||||
},
|
||||
child: IconTheme(
|
||||
data: iconTheme,
|
||||
child: DefaultTextStyle(
|
||||
style: textStyle,
|
||||
child: Semantics(
|
||||
button: true,
|
||||
inMutuallyExclusiveGroup: true,
|
||||
selected: widget.groupValue == currentKey,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
backgroundColors.add(getBackgroundColor(index, currentKey)!);
|
||||
gestureChildren.add(child);
|
||||
index += 1;
|
||||
}
|
||||
|
||||
final Widget box = _SegmentedControlRenderWidget<T>(
|
||||
selectedIndex: selectedIndex,
|
||||
pressedIndex: pressedIndex,
|
||||
backgroundColors: backgroundColors,
|
||||
borderColor: _borderColor!,
|
||||
children: gestureChildren,
|
||||
);
|
||||
|
||||
return Padding(
|
||||
padding: widget.padding ?? _kHorizontalItemPadding,
|
||||
child: UnconstrainedBox(constrainedAxis: Axis.horizontal, child: box),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SegmentedControlRenderWidget<T> extends MultiChildRenderObjectWidget {
|
||||
const _SegmentedControlRenderWidget({
|
||||
super.key,
|
||||
super.children,
|
||||
required this.selectedIndex,
|
||||
required this.pressedIndex,
|
||||
required this.backgroundColors,
|
||||
required this.borderColor,
|
||||
});
|
||||
|
||||
final int? selectedIndex;
|
||||
final int? pressedIndex;
|
||||
final List<Color> backgroundColors;
|
||||
final Color borderColor;
|
||||
|
||||
@override
|
||||
RenderObject createRenderObject(BuildContext context) {
|
||||
return _RenderSegmentedControl<T>(
|
||||
textDirection: Directionality.of(context),
|
||||
selectedIndex: selectedIndex,
|
||||
pressedIndex: pressedIndex,
|
||||
backgroundColors: backgroundColors,
|
||||
borderColor: borderColor,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void updateRenderObject(
|
||||
BuildContext context,
|
||||
_RenderSegmentedControl<T> renderObject,
|
||||
) {
|
||||
renderObject
|
||||
..textDirection = Directionality.of(context)
|
||||
..selectedIndex = selectedIndex
|
||||
..pressedIndex = pressedIndex
|
||||
..backgroundColors = backgroundColors
|
||||
..borderColor = borderColor;
|
||||
}
|
||||
}
|
||||
|
||||
class _SegmentedControlContainerBoxParentData
|
||||
extends ContainerBoxParentData<RenderBox> {
|
||||
RRect? surroundingRect;
|
||||
}
|
||||
|
||||
typedef _NextChild = RenderBox? Function(RenderBox child);
|
||||
|
||||
class _RenderSegmentedControl<T> extends RenderBox
|
||||
with
|
||||
ContainerRenderObjectMixin<
|
||||
RenderBox,
|
||||
ContainerBoxParentData<RenderBox>
|
||||
>,
|
||||
RenderBoxContainerDefaultsMixin<
|
||||
RenderBox,
|
||||
ContainerBoxParentData<RenderBox>
|
||||
> {
|
||||
_RenderSegmentedControl({
|
||||
required int? selectedIndex,
|
||||
required int? pressedIndex,
|
||||
required TextDirection textDirection,
|
||||
required List<Color> backgroundColors,
|
||||
required Color borderColor,
|
||||
}) : _textDirection = textDirection,
|
||||
_selectedIndex = selectedIndex,
|
||||
_pressedIndex = pressedIndex,
|
||||
_backgroundColors = backgroundColors,
|
||||
_borderColor = borderColor;
|
||||
|
||||
int? get selectedIndex => _selectedIndex;
|
||||
int? _selectedIndex;
|
||||
|
||||
set selectedIndex(int? value) {
|
||||
if (_selectedIndex == value) {
|
||||
return;
|
||||
}
|
||||
_selectedIndex = value;
|
||||
markNeedsPaint();
|
||||
}
|
||||
|
||||
int? get pressedIndex => _pressedIndex;
|
||||
int? _pressedIndex;
|
||||
|
||||
set pressedIndex(int? value) {
|
||||
if (_pressedIndex == value) {
|
||||
return;
|
||||
}
|
||||
_pressedIndex = value;
|
||||
markNeedsPaint();
|
||||
}
|
||||
|
||||
TextDirection get textDirection => _textDirection;
|
||||
TextDirection _textDirection;
|
||||
|
||||
set textDirection(TextDirection value) {
|
||||
if (_textDirection == value) {
|
||||
return;
|
||||
}
|
||||
_textDirection = value;
|
||||
markNeedsLayout();
|
||||
}
|
||||
|
||||
List<Color> get backgroundColors => _backgroundColors;
|
||||
List<Color> _backgroundColors;
|
||||
|
||||
set backgroundColors(List<Color> value) {
|
||||
if (_backgroundColors == value) {
|
||||
return;
|
||||
}
|
||||
_backgroundColors = value;
|
||||
markNeedsPaint();
|
||||
}
|
||||
|
||||
Color get borderColor => _borderColor;
|
||||
Color _borderColor;
|
||||
|
||||
set borderColor(Color value) {
|
||||
if (_borderColor == value) {
|
||||
return;
|
||||
}
|
||||
_borderColor = value;
|
||||
markNeedsPaint();
|
||||
}
|
||||
|
||||
@override
|
||||
double computeMinIntrinsicWidth(double height) {
|
||||
RenderBox? child = firstChild;
|
||||
double minWidth = 0.0;
|
||||
while (child != null) {
|
||||
final _SegmentedControlContainerBoxParentData childParentData =
|
||||
child.parentData! as _SegmentedControlContainerBoxParentData;
|
||||
final double childWidth = child.getMinIntrinsicWidth(height);
|
||||
minWidth = math.max(minWidth, childWidth);
|
||||
child = childParentData.nextSibling;
|
||||
}
|
||||
return minWidth * childCount;
|
||||
}
|
||||
|
||||
@override
|
||||
double computeMaxIntrinsicWidth(double height) {
|
||||
RenderBox? child = firstChild;
|
||||
double maxWidth = 0.0;
|
||||
while (child != null) {
|
||||
final _SegmentedControlContainerBoxParentData childParentData =
|
||||
child.parentData! as _SegmentedControlContainerBoxParentData;
|
||||
final double childWidth = child.getMaxIntrinsicWidth(height);
|
||||
maxWidth = math.max(maxWidth, childWidth);
|
||||
child = childParentData.nextSibling;
|
||||
}
|
||||
return maxWidth * childCount;
|
||||
}
|
||||
|
||||
@override
|
||||
double computeMinIntrinsicHeight(double width) {
|
||||
RenderBox? child = firstChild;
|
||||
double minHeight = 0.0;
|
||||
while (child != null) {
|
||||
final _SegmentedControlContainerBoxParentData childParentData =
|
||||
child.parentData! as _SegmentedControlContainerBoxParentData;
|
||||
final double childHeight = child.getMinIntrinsicHeight(width);
|
||||
minHeight = math.max(minHeight, childHeight);
|
||||
child = childParentData.nextSibling;
|
||||
}
|
||||
return minHeight;
|
||||
}
|
||||
|
||||
@override
|
||||
double computeMaxIntrinsicHeight(double width) {
|
||||
RenderBox? child = firstChild;
|
||||
double maxHeight = 0.0;
|
||||
while (child != null) {
|
||||
final _SegmentedControlContainerBoxParentData childParentData =
|
||||
child.parentData! as _SegmentedControlContainerBoxParentData;
|
||||
final double childHeight = child.getMaxIntrinsicHeight(width);
|
||||
maxHeight = math.max(maxHeight, childHeight);
|
||||
child = childParentData.nextSibling;
|
||||
}
|
||||
return maxHeight;
|
||||
}
|
||||
|
||||
@override
|
||||
double? computeDistanceToActualBaseline(TextBaseline baseline) {
|
||||
return defaultComputeDistanceToHighestActualBaseline(baseline);
|
||||
}
|
||||
|
||||
@override
|
||||
void setupParentData(RenderBox child) {
|
||||
if (child.parentData is! _SegmentedControlContainerBoxParentData) {
|
||||
child.parentData = _SegmentedControlContainerBoxParentData();
|
||||
}
|
||||
}
|
||||
|
||||
void _layoutRects(
|
||||
_NextChild nextChild,
|
||||
RenderBox? leftChild,
|
||||
RenderBox? rightChild,
|
||||
) {
|
||||
RenderBox? child = leftChild;
|
||||
double start = 0.0;
|
||||
while (child != null) {
|
||||
final _SegmentedControlContainerBoxParentData childParentData =
|
||||
child.parentData! as _SegmentedControlContainerBoxParentData;
|
||||
final Offset childOffset = Offset(start, 0.0);
|
||||
childParentData.offset = childOffset;
|
||||
final Rect childRect = Rect.fromLTWH(
|
||||
start,
|
||||
0.0,
|
||||
child.size.width,
|
||||
child.size.height,
|
||||
);
|
||||
final RRect rChildRect;
|
||||
if (child == leftChild) {
|
||||
rChildRect = RRect.fromRectAndCorners(
|
||||
childRect,
|
||||
topLeft: const Radius.circular(10.0),
|
||||
bottomLeft: const Radius.circular(10.0),
|
||||
);
|
||||
} else if (child == rightChild) {
|
||||
rChildRect = RRect.fromRectAndCorners(
|
||||
childRect,
|
||||
topRight: const Radius.circular(10.0),
|
||||
bottomRight: const Radius.circular(10.0),
|
||||
);
|
||||
} else {
|
||||
rChildRect = RRect.fromRectAndCorners(childRect);
|
||||
}
|
||||
childParentData.surroundingRect = rChildRect;
|
||||
start += child.size.width;
|
||||
child = nextChild(child);
|
||||
}
|
||||
}
|
||||
|
||||
Size _calculateChildSize(BoxConstraints constraints) {
|
||||
double maxHeight = _kMinSegmentedControlHeight;
|
||||
double childWidth = constraints.minWidth / childCount;
|
||||
RenderBox? child = firstChild;
|
||||
while (child != null) {
|
||||
childWidth = math.max(
|
||||
childWidth,
|
||||
child.getMaxIntrinsicWidth(double.infinity),
|
||||
);
|
||||
child = childAfter(child);
|
||||
}
|
||||
childWidth = math.min(childWidth, constraints.maxWidth / childCount);
|
||||
child = firstChild;
|
||||
while (child != null) {
|
||||
final double boxHeight = child.getMaxIntrinsicHeight(childWidth);
|
||||
maxHeight = math.max(maxHeight, boxHeight);
|
||||
child = childAfter(child);
|
||||
}
|
||||
return Size(childWidth, maxHeight);
|
||||
}
|
||||
|
||||
Size _computeOverallSizeFromChildSize(Size childSize) {
|
||||
return constraints.constrain(
|
||||
Size(childSize.width * childCount, childSize.height),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
double? computeDryBaseline(
|
||||
covariant BoxConstraints constraints,
|
||||
TextBaseline baseline,
|
||||
) {
|
||||
final Size childSize = _calculateChildSize(constraints);
|
||||
final BoxConstraints childConstraints = BoxConstraints.tight(childSize);
|
||||
|
||||
BaselineOffset baselineOffset = BaselineOffset.noBaseline;
|
||||
for (
|
||||
RenderBox? child = firstChild;
|
||||
child != null;
|
||||
child = childAfter(child)
|
||||
) {
|
||||
baselineOffset = baselineOffset.minOf(
|
||||
BaselineOffset(child.getDryBaseline(childConstraints, baseline)),
|
||||
);
|
||||
}
|
||||
return baselineOffset.offset;
|
||||
}
|
||||
|
||||
@override
|
||||
Size computeDryLayout(BoxConstraints constraints) {
|
||||
final Size childSize = _calculateChildSize(constraints);
|
||||
return _computeOverallSizeFromChildSize(childSize);
|
||||
}
|
||||
|
||||
@override
|
||||
void performLayout() {
|
||||
final BoxConstraints constraints = this.constraints;
|
||||
final Size childSize = _calculateChildSize(constraints);
|
||||
|
||||
final BoxConstraints childConstraints = BoxConstraints.tightFor(
|
||||
width: childSize.width,
|
||||
height: childSize.height,
|
||||
);
|
||||
|
||||
RenderBox? child = firstChild;
|
||||
while (child != null) {
|
||||
child.layout(childConstraints, parentUsesSize: true);
|
||||
child = childAfter(child);
|
||||
}
|
||||
|
||||
switch (textDirection) {
|
||||
case TextDirection.rtl:
|
||||
_layoutRects(childBefore, lastChild, firstChild);
|
||||
case TextDirection.ltr:
|
||||
_layoutRects(childAfter, firstChild, lastChild);
|
||||
}
|
||||
|
||||
size = _computeOverallSizeFromChildSize(childSize);
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
RenderBox? child = firstChild;
|
||||
int index = 0;
|
||||
while (child != null) {
|
||||
_paintChild(context, offset, child, index);
|
||||
child = childAfter(child);
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void _paintChild(
|
||||
PaintingContext context,
|
||||
Offset offset,
|
||||
RenderBox child,
|
||||
int childIndex,
|
||||
) {
|
||||
final _SegmentedControlContainerBoxParentData childParentData =
|
||||
child.parentData! as _SegmentedControlContainerBoxParentData;
|
||||
|
||||
context.canvas.drawRRect(
|
||||
childParentData.surroundingRect!.shift(offset),
|
||||
Paint()
|
||||
..color = backgroundColors[childIndex]
|
||||
..style = PaintingStyle.fill,
|
||||
);
|
||||
context.canvas.drawRRect(
|
||||
childParentData.surroundingRect!.shift(offset),
|
||||
Paint()
|
||||
..color = borderColor
|
||||
..strokeWidth = 1.0
|
||||
..style = PaintingStyle.stroke,
|
||||
);
|
||||
|
||||
context.paintChild(child, childParentData.offset + offset);
|
||||
}
|
||||
|
||||
@override
|
||||
bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
|
||||
RenderBox? child = lastChild;
|
||||
while (child != null) {
|
||||
final _SegmentedControlContainerBoxParentData childParentData =
|
||||
child.parentData! as _SegmentedControlContainerBoxParentData;
|
||||
if (childParentData.surroundingRect!.contains(position)) {
|
||||
return result.addWithPaintOffset(
|
||||
offset: childParentData.offset,
|
||||
position: position,
|
||||
hitTest: (BoxHitTestResult result, Offset localOffset) {
|
||||
assert(localOffset == position - childParentData.offset);
|
||||
return child!.hitTest(result, position: localOffset);
|
||||
},
|
||||
);
|
||||
}
|
||||
child = childParentData.previousSibling;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user