1 - AllocatedMadeModel
2 - RSegment widget
3 - buy in province
This commit is contained in:
2025-07-02 16:29:29 +03:30
parent e0ac676f0a
commit d6f7cb4930
19 changed files with 1129 additions and 2021 deletions

View File

@@ -14,9 +14,7 @@ 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,
);
const EdgeInsetsGeometry _kHorizontalItemPadding = EdgeInsets.symmetric(horizontal: 16.0);
// Minimum height of the segmented control.
const double _kMinSegmentedControlHeight = 28.0;
@@ -52,15 +50,17 @@ class NewCupertinoSegmentedControl<T extends Object> extends StatefulWidget {
this.unselectedColor,
this.selectedColor,
this.borderColor,
this.selectedBorderColor,
this.pressedColor,
this.disabledColor,
this.disabledTextColor,
this.padding,
this.unselectedItemStyle,
this.selectedItemStyle,
this.disabledChildren = const <Never>{},
}) : assert(children.length >= 2),
assert(
groupValue == null ||
children.keys.any((T child) => child == groupValue),
groupValue == null || children.keys.any((T child) => child == groupValue),
'The groupValue must be either null or one of the keys in the children map.',
);
@@ -101,6 +101,11 @@ class NewCupertinoSegmentedControl<T extends Object> extends StatefulWidget {
/// Defaults to [CupertinoTheme]'s `primaryColor` if null.
final Color? borderColor;
/// The color used as the border around selected widget.
///
/// Defaults to [CupertinoTheme]'s `primaryColor` if null.
final Color? selectedBorderColor;
/// The color used to fill the background of the widget the user is
/// temporarily interacting with through a long press or drag.
///
@@ -127,18 +132,25 @@ class NewCupertinoSegmentedControl<T extends Object> extends StatefulWidget {
/// All segments are enabled by default.
final Set<T> disabledChildren;
/// The text style for unselected items.
///
/// Defaults to null, which means it will use the default text style.
final TextStyle? unselectedItemStyle;
/// The text style for selected items.
///
/// Defaults to null, which means it will use the default text style.
final TextStyle? selectedItemStyle;
@override
State<NewCupertinoSegmentedControl<T>> createState() =>
_SegmentedControlState<T>();
State<NewCupertinoSegmentedControl<T>> createState() => _SegmentedControlState<T>();
}
class _SegmentedControlState<T extends Object>
extends State<NewCupertinoSegmentedControl<T>>
class _SegmentedControlState<T extends Object> extends State<NewCupertinoSegmentedControl<T>>
with TickerProviderStateMixin<NewCupertinoSegmentedControl<T>> {
T? _pressedKey;
final List<AnimationController> _selectionControllers =
<AnimationController>[];
final List<AnimationController> _selectionControllers = <AnimationController>[];
final List<ColorTween> _childTweens = <ColorTween>[];
late ColorTween _forwardBackgroundColorTween;
@@ -148,74 +160,66 @@ class _SegmentedControlState<T extends Object>
Color? _selectedColor;
Color? _unselectedColor;
Color? _borderColor;
Color? _selectedBorderColor;
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
});
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;
final Color disabledTextColor = widget.disabledTextColor ?? _kDisableTextColor;
if (_disabledTextColor != disabledTextColor) {
changed = true;
_disabledTextColor = disabledTextColor;
}
final Color selectedColor =
widget.selectedColor ?? CupertinoTheme.of(context).primaryColor;
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;
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;
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;
final Color borderColor = widget.borderColor ?? CupertinoTheme.of(context).primaryColor;
final Color selectedBorderColor = widget.selectedBorderColor ?? CupertinoTheme.of(context).primaryColor;
if (_borderColor != borderColor) {
changed = true;
_borderColor = borderColor;
}
if (_selectedBorderColor != selectedBorderColor) {
changed = true;
_selectedBorderColor = selectedBorderColor;
}
final Color pressedColor =
widget.pressedColor ??
CupertinoTheme.of(context).primaryColor.withOpacity(0.2);
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,
);
_forwardBackgroundColorTween = ColorTween(begin: _pressedColor, end: _selectedColor);
_reverseBackgroundColorTween = ColorTween(begin: _unselectedColor, end: _selectedColor);
_textColorTween = ColorTween(begin: _selectedColor, end: _unselectedColor);
return changed;
}
@@ -229,8 +233,7 @@ class _SegmentedControlState<T extends Object>
_childTweens.clear();
for (final T key in widget.children.keys) {
final AnimationController animationController =
createAnimationController();
final AnimationController animationController = createAnimationController();
if (widget.groupValue == key) {
_childTweens.add(_reverseBackgroundColorTween);
animationController.value = 1.0;
@@ -254,8 +257,7 @@ class _SegmentedControlState<T extends Object>
void didUpdateWidget(NewCupertinoSegmentedControl<T> oldWidget) {
super.didUpdateWidget(oldWidget);
if (_updateColors() ||
oldWidget.children.length != widget.children.length) {
if (_updateColors() || oldWidget.children.length != widget.children.length) {
_updateAnimationControllers();
}
@@ -276,8 +278,7 @@ class _SegmentedControlState<T extends Object>
@override
void dispose() {
for (final AnimationController animationController
in _selectionControllers) {
for (final AnimationController animationController in _selectionControllers) {
animationController.dispose();
}
super.dispose();
@@ -324,9 +325,7 @@ class _SegmentedControlState<T extends Object>
Color? getBackgroundColor(int index, T currentKey) {
if (widget.disabledChildren.contains(currentKey)) {
return widget.groupValue == currentKey
? _selectedDisabledColor
: _unselectedDisabledColor;
return widget.groupValue == currentKey ? _selectedDisabledColor : _unselectedDisabledColor;
}
if (_selectionControllers[index].isAnimating) {
return _childTweens[index].evaluate(_selectionControllers[index]);
@@ -350,13 +349,12 @@ class _SegmentedControlState<T extends Object>
for (final T currentKey in widget.children.keys) {
selectedIndex = (widget.groupValue == currentKey) ? index : selectedIndex;
pressedIndex = (_pressedKey == currentKey) ? index : pressedIndex;
final isSelected = widget.groupValue == currentKey;
final TextStyle textStyle = DefaultTextStyle.of(
context,
).style.copyWith(color: getTextColor(index, currentKey));
final IconThemeData iconTheme = IconThemeData(
color: getTextColor(index, currentKey),
);
final textStyle =
(isSelected ? widget.selectedItemStyle : widget.unselectedItemStyle) ??
DefaultTextStyle.of(context).style.copyWith(color: getTextColor(index, currentKey));
final IconThemeData iconTheme = IconThemeData(color: getTextColor(index, currentKey));
Widget child = Center(child: widget.children[currentKey]);
@@ -364,16 +362,12 @@ class _SegmentedControlState<T extends Object>
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,
onTapDown: widget.disabledChildren.contains(currentKey)
? null
: (TapDownDetails event) {
_onTapDown(currentKey);
},
onTapCancel: widget.disabledChildren.contains(currentKey) ? null : _onTapCancel,
onTap: () {
_onTap(currentKey);
},
@@ -402,6 +396,7 @@ class _SegmentedControlState<T extends Object>
pressedIndex: pressedIndex,
backgroundColors: backgroundColors,
borderColor: _borderColor!,
selectBorderColor: _selectedBorderColor ?? _borderColor!,
children: gestureChildren,
);
@@ -420,12 +415,14 @@ class _SegmentedControlRenderWidget<T> extends MultiChildRenderObjectWidget {
required this.pressedIndex,
required this.backgroundColors,
required this.borderColor,
required this.selectBorderColor,
});
final int? selectedIndex;
final int? pressedIndex;
final List<Color> backgroundColors;
final Color borderColor;
final Color selectBorderColor;
@override
RenderObject createRenderObject(BuildContext context) {
@@ -435,25 +432,23 @@ class _SegmentedControlRenderWidget<T> extends MultiChildRenderObjectWidget {
pressedIndex: pressedIndex,
backgroundColors: backgroundColors,
borderColor: borderColor,
selectBorderColor: selectBorderColor,
);
}
@override
void updateRenderObject(
BuildContext context,
_RenderSegmentedControl<T> renderObject,
) {
void updateRenderObject(BuildContext context, _RenderSegmentedControl<T> renderObject) {
renderObject
..textDirection = Directionality.of(context)
..selectedIndex = selectedIndex
..pressedIndex = pressedIndex
..backgroundColors = backgroundColors
..borderColor = borderColor;
..borderColor = borderColor
..selectedItemBorderColor = selectBorderColor;
}
}
class _SegmentedControlContainerBoxParentData
extends ContainerBoxParentData<RenderBox> {
class _SegmentedControlContainerBoxParentData extends ContainerBoxParentData<RenderBox> {
RRect? surroundingRect;
}
@@ -461,24 +456,20 @@ typedef _NextChild = RenderBox? Function(RenderBox child);
class _RenderSegmentedControl<T> extends RenderBox
with
ContainerRenderObjectMixin<
RenderBox,
ContainerBoxParentData<RenderBox>
>,
RenderBoxContainerDefaultsMixin<
RenderBox,
ContainerBoxParentData<RenderBox>
> {
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,
required Color selectBorderColor,
}) : _textDirection = textDirection,
_selectedIndex = selectedIndex,
_pressedIndex = pressedIndex,
_backgroundColors = backgroundColors,
_selectedBorderColor = selectBorderColor,
_borderColor = borderColor;
int? get selectedIndex => _selectedIndex;
@@ -536,6 +527,16 @@ class _RenderSegmentedControl<T> extends RenderBox
markNeedsPaint();
}
Color? _selectedBorderColor;
Color? get selectedItemBorderColor => _selectedBorderColor;
set selectedItemBorderColor(Color? value) {
if (_selectedBorderColor == value) return;
_selectedBorderColor = value;
markNeedsPaint();
}
@override
double computeMinIntrinsicWidth(double height) {
RenderBox? child = firstChild;
@@ -604,11 +605,7 @@ class _RenderSegmentedControl<T> extends RenderBox
}
}
void _layoutRects(
_NextChild nextChild,
RenderBox? leftChild,
RenderBox? rightChild,
) {
void _layoutRects(_NextChild nextChild, RenderBox? leftChild, RenderBox? rightChild) {
RenderBox? child = leftChild;
double start = 0.0;
while (child != null) {
@@ -616,12 +613,7 @@ class _RenderSegmentedControl<T> extends RenderBox
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 Rect childRect = Rect.fromLTWH(start, 0.0, child.size.width, child.size.height);
final RRect rChildRect;
if (child == leftChild) {
rChildRect = RRect.fromRectAndCorners(
@@ -649,10 +641,7 @@ class _RenderSegmentedControl<T> extends RenderBox
double childWidth = constraints.minWidth / childCount;
RenderBox? child = firstChild;
while (child != null) {
childWidth = math.max(
childWidth,
child.getMaxIntrinsicWidth(double.infinity),
);
childWidth = math.max(childWidth, child.getMaxIntrinsicWidth(double.infinity));
child = childAfter(child);
}
childWidth = math.min(childWidth, constraints.maxWidth / childCount);
@@ -666,25 +655,16 @@ class _RenderSegmentedControl<T> extends RenderBox
}
Size _computeOverallSizeFromChildSize(Size childSize) {
return constraints.constrain(
Size(childSize.width * childCount, childSize.height),
);
return constraints.constrain(Size(childSize.width * childCount, childSize.height));
}
@override
double? computeDryBaseline(
covariant BoxConstraints constraints,
TextBaseline baseline,
) {
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)
) {
for (RenderBox? child = firstChild; child != null; child = childAfter(child)) {
baselineOffset = baselineOffset.minOf(
BaselineOffset(child.getDryBaseline(childConstraints, baseline)),
);
@@ -735,28 +715,29 @@ class _RenderSegmentedControl<T> extends RenderBox
}
}
void _paintChild(
PaintingContext context,
Offset offset,
RenderBox child,
int childIndex,
) {
void _paintChild(PaintingContext context, Offset offset, RenderBox child, int childIndex) {
final _SegmentedControlContainerBoxParentData childParentData =
child.parentData! as _SegmentedControlContainerBoxParentData;
final RRect rect = childParentData.surroundingRect!.shift(offset);
context.canvas.drawRRect(
childParentData.surroundingRect!.shift(offset),
rect,
Paint()
..color = backgroundColors[childIndex]
..style = PaintingStyle.fill,
);
context.canvas.drawRRect(
childParentData.surroundingRect!.shift(offset),
Paint()
..color = borderColor
..strokeWidth = 1.0
..style = PaintingStyle.stroke,
);
final isSelected = selectedIndex == childIndex;
final borderPaint = Paint()
..color = isSelected && selectedItemBorderColor != null
? selectedItemBorderColor!
: borderColor
..strokeWidth = 1.0
..style = PaintingStyle.stroke;
context.canvas.drawRRect(rect, borderPaint);
context.paintChild(child, childParentData.offset + offset);
}

View File

@@ -0,0 +1,104 @@
import 'package:flutter/material.dart';
import 'package:rasadyar_core/core.dart';
const Duration _kFadeDuration = Duration(milliseconds: 165);
class RSegment extends StatefulWidget {
const RSegment({
super.key,
required this.children,
required this.selectedIndex,
required this.onSegmentSelected,
required this.backgroundColor,
required this.selectedBackgroundColor,
required this.borderColor,
required this.selectedBorderColor,
this.padding,
});
final List<String> children;
final int selectedIndex;
final Function(int index)? onSegmentSelected;
final EdgeInsetsGeometry? padding;
final Color backgroundColor;
final Color borderColor;
final Color selectedBackgroundColor;
final Color selectedBorderColor;
@override
State<RSegment> createState() => _RSegmentState();
}
class _RSegmentState extends State<RSegment> {
late int selectedIndex;
@override
void initState() {
super.initState();
selectedIndex = widget.selectedIndex;
}
@override
void didUpdateWidget(covariant RSegment oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.selectedIndex != widget.selectedIndex) {
widget.onSegmentSelected?.call(widget.selectedIndex);
}
}
@override
Widget build(BuildContext context) {
return Row(
children: List.generate(
widget.children.length,
(index) => Expanded(
child: _generateChild(
index: index,
hasNext: widget.children.length > index + 1,
hasPrevious: index > 0,
),
),
),
);
}
Widget _generateChild({required int index, required bool hasNext, required bool hasPrevious}) {
final bool isSelected = selectedIndex == index;
return GestureDetector(
onTap: () {
setState(() {
selectedIndex = index;
});
widget.onSegmentSelected?.call(index);
},
child: AnimatedContainer(
duration: _kFadeDuration,
padding: widget.padding??EdgeInsets.symmetric(vertical: 8),
margin: EdgeInsets.zero,
decoration: BoxDecoration(
color: isSelected ? widget.selectedBackgroundColor : widget.backgroundColor,
borderRadius: BorderRadius.only(
topRight: !hasPrevious && hasNext ? Radius.circular(8) : Radius.zero,
bottomRight: !hasPrevious && hasNext ? Radius.circular(8) : Radius.zero,
bottomLeft: hasPrevious && !hasNext ? Radius.circular(8) : Radius.zero,
topLeft: hasPrevious && !hasNext ? Radius.circular(8) : Radius.zero,
),
border: Border.all(
width: 1,
color: isSelected ? widget.selectedBorderColor : widget.borderColor,
),
),
child: Center(
child: Text(
widget.children[index],
textAlign: TextAlign.center,
style: isSelected
? AppFonts.yekan16Bold.copyWith(color: widget.selectedBorderColor)
: AppFonts.yekan16.copyWith(color: AppColor.mediumGreyDarkHover),
),
),
),
);
}
}

View File

@@ -24,5 +24,6 @@ export 'overlay_dropdown_widget/view.dart';
export 'pagination/pagination_from_until.dart';
export 'pagination/show_more.dart';
export 'tabs/new_tab.dart';
export 'tabs/r_segment.dart';
export 'tabs/tab.dart';
export 'vec_widget.dart';