269 lines
7.9 KiB
Dart
269 lines
7.9 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
class DashedDivider extends StatelessWidget {
|
|
final double? height;
|
|
final double? thickness;
|
|
final double? indent;
|
|
final double? endIndent;
|
|
final Color? color;
|
|
final double dashLength;
|
|
final double dashSpace;
|
|
final double mainAxisOffset;
|
|
|
|
/// If true, shows the advanced pill-style dashed divider
|
|
final bool isAdvanced;
|
|
|
|
const DashedDivider({
|
|
super.key,
|
|
this.height,
|
|
this.thickness,
|
|
this.color,
|
|
this.indent,
|
|
this.endIndent,
|
|
this.dashLength = 5,
|
|
this.dashSpace = 5,
|
|
this.mainAxisOffset = 0.0,
|
|
this.isAdvanced = false,
|
|
}) : assert(height == null || height >= 0.0),
|
|
assert(thickness == null || thickness >= 0.0),
|
|
assert(indent == null || indent >= 0.0),
|
|
assert(endIndent == null || endIndent >= 0.0);
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
// ── Advanced pill-style divider ──
|
|
if (isAdvanced) {
|
|
return _AdvancedDashedDivider(
|
|
color: color ?? const Color(0xFFBEBEBE),
|
|
height: height ?? 20,
|
|
indent: indent ?? 0,
|
|
endIndent: endIndent ?? 0,
|
|
);
|
|
}
|
|
|
|
// ── Original dashed divider ──
|
|
final theme = DividerThemeProvider.of(context).withDefaults(
|
|
height: height,
|
|
thickness: thickness,
|
|
indent: indent,
|
|
endIndent: endIndent,
|
|
color: color,
|
|
);
|
|
|
|
return Container(
|
|
margin: EdgeInsets.only(left: theme.indent, right: theme.endIndent),
|
|
height: theme.height,
|
|
width: double.infinity,
|
|
child: CustomPaint(
|
|
painter: DashedLinePainter(
|
|
color: theme.color,
|
|
thickness: theme.thickness,
|
|
dashLength: dashLength,
|
|
dashSpace: dashSpace,
|
|
mainAxisOffset: mainAxisOffset,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
// ─────────────────────────────────────────────
|
|
// Advanced Pill-Style Dashed Divider
|
|
// ─────────────────────────────────────────────
|
|
class _AdvancedDashedDivider extends StatelessWidget {
|
|
final Color color;
|
|
final double height;
|
|
final double indent;
|
|
final double endIndent;
|
|
|
|
const _AdvancedDashedDivider({
|
|
required this.color,
|
|
required this.height,
|
|
required this.indent,
|
|
required this.endIndent,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
margin: EdgeInsets.only(left: indent, right: endIndent),
|
|
height: height,
|
|
width: double.infinity,
|
|
child: CustomPaint(
|
|
painter: _PillDashedLinePainter(color: color),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _PillDashedLinePainter extends CustomPainter {
|
|
final Color color;
|
|
|
|
_PillDashedLinePainter({required this.color});
|
|
|
|
@override
|
|
void paint(Canvas canvas, Size size) {
|
|
final paint = Paint()
|
|
..color = color
|
|
..style = PaintingStyle.stroke
|
|
..strokeWidth = 1.5;
|
|
|
|
const pillWidth = 22.0;
|
|
const pillHeight = 10.0;
|
|
const gap = 6.0;
|
|
const radius = pillHeight / 2;
|
|
|
|
final centerY = size.height / 2;
|
|
double x = 0;
|
|
|
|
while (x + pillWidth <= size.width) {
|
|
final rect = RRect.fromRectAndRadius(
|
|
Rect.fromLTWH(x, centerY - radius, pillWidth, pillHeight),
|
|
const Radius.circular(radius),
|
|
);
|
|
canvas.drawRRect(rect, paint);
|
|
x += pillWidth + gap;
|
|
}
|
|
}
|
|
|
|
@override
|
|
bool shouldRepaint(CustomPainter oldDelegate) => false;
|
|
}
|
|
|
|
// ─────────────────────────────────────────────
|
|
// Original Painter
|
|
// ─────────────────────────────────────────────
|
|
class DashedLinePainter extends CustomPainter {
|
|
final Color color;
|
|
final double thickness;
|
|
final double dashLength;
|
|
final double dashSpace;
|
|
final bool isVertical;
|
|
final double mainAxisOffset;
|
|
|
|
DashedLinePainter({
|
|
required this.color,
|
|
required this.thickness,
|
|
required this.dashLength,
|
|
required this.dashSpace,
|
|
this.isVertical = false,
|
|
this.mainAxisOffset = 0.0,
|
|
});
|
|
|
|
@override
|
|
void paint(Canvas canvas, Size size) {
|
|
final paint = Paint()
|
|
..color = color
|
|
..strokeWidth = thickness <= 0 ? 1 : thickness;
|
|
|
|
final mainAxisSize = _getMainAxisSize(size);
|
|
final crossAxisPosition = _getCrossAxisPosition(size);
|
|
|
|
canvas.clipRect(Rect.fromLTWH(0, 0, size.width, size.height));
|
|
|
|
double currentPosition = mainAxisOffset;
|
|
|
|
while (currentPosition < mainAxisSize) {
|
|
double nextDashEnd = currentPosition + dashLength;
|
|
|
|
if (nextDashEnd > mainAxisSize) {
|
|
nextDashEnd = mainAxisSize;
|
|
}
|
|
|
|
final start = _calculateStartOffset(crossAxisPosition, currentPosition);
|
|
final end = _calculateEndOffset(crossAxisPosition, nextDashEnd);
|
|
|
|
canvas.drawLine(start, end, paint);
|
|
|
|
currentPosition += dashLength + dashSpace;
|
|
}
|
|
}
|
|
|
|
double _getMainAxisSize(Size size) =>
|
|
isVertical ? size.height : size.width;
|
|
|
|
double _getCrossAxisPosition(Size size) =>
|
|
isVertical ? size.width / 2 : size.height / 2;
|
|
|
|
Offset _calculateStartOffset(double crossAxisPosition, double currentPosition) =>
|
|
isVertical
|
|
? Offset(crossAxisPosition, currentPosition)
|
|
: Offset(currentPosition, crossAxisPosition);
|
|
|
|
Offset _calculateEndOffset(double crossAxisPosition, double nextDashEnd) =>
|
|
isVertical
|
|
? Offset(crossAxisPosition, nextDashEnd)
|
|
: Offset(nextDashEnd, crossAxisPosition);
|
|
|
|
@override
|
|
bool shouldRepaint(CustomPainter oldDelegate) => false;
|
|
}
|
|
|
|
// ─────────────────────────────────────────────
|
|
// Theme Provider (unchanged)
|
|
// ─────────────────────────────────────────────
|
|
class DividerThemeProvider {
|
|
final DividerThemeData _dividerTheme;
|
|
final ThemeData _theme;
|
|
final DividerThemeData _defaults;
|
|
|
|
double? _width;
|
|
double? _height;
|
|
double? _thickness;
|
|
double? _indent;
|
|
double? _endIndent;
|
|
Color? _color;
|
|
|
|
DividerThemeProvider._(BuildContext context)
|
|
: _dividerTheme = DividerTheme.of(context),
|
|
_theme = Theme.of(context),
|
|
_defaults = Theme.of(context).useMaterial3
|
|
? _DividerDefaultsM3(context)
|
|
: _DividerDefaultsM2(context);
|
|
|
|
static DividerThemeProvider of(BuildContext context) {
|
|
return DividerThemeProvider._(context);
|
|
}
|
|
|
|
DividerThemeProvider withDefaults({
|
|
double? width,
|
|
double? height,
|
|
double? thickness,
|
|
double? indent,
|
|
double? endIndent,
|
|
Color? color,
|
|
}) {
|
|
_width = width ?? _width;
|
|
_height = height ?? _height;
|
|
_thickness = thickness ?? _thickness;
|
|
_indent = indent ?? _indent;
|
|
_endIndent = endIndent ?? _endIndent;
|
|
_color = color ?? _color;
|
|
return this;
|
|
}
|
|
|
|
double get width => _width ?? _dividerTheme.space ?? _defaults.space!;
|
|
double get height => _height ?? _dividerTheme.space ?? _defaults.space!;
|
|
double get thickness => _thickness ?? _dividerTheme.thickness ?? _defaults.thickness!;
|
|
double get indent => _indent ?? _dividerTheme.indent ?? _defaults.indent!;
|
|
double get endIndent => _endIndent ?? _dividerTheme.endIndent ?? _defaults.endIndent!;
|
|
Color get color => _color ?? _dividerTheme.color ?? _defaults.color ?? _theme.dividerColor;
|
|
}
|
|
|
|
class _DividerDefaultsM3 extends DividerThemeData {
|
|
const _DividerDefaultsM3(this.context)
|
|
: super(space: 16, thickness: 1.0, indent: 0, endIndent: 0);
|
|
final BuildContext context;
|
|
|
|
@override
|
|
Color? get color => Theme.of(context).colorScheme.outlineVariant;
|
|
}
|
|
|
|
class _DividerDefaultsM2 extends DividerThemeData {
|
|
const _DividerDefaultsM2(this.context)
|
|
: super(space: 16, thickness: 0, indent: 0, endIndent: 0);
|
|
final BuildContext context;
|
|
|
|
@override
|
|
Color? get color => Theme.of(context).dividerColor;
|
|
} |