mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-08-25 11:39:39 +00:00
Enhanced SpacesConnectionsArrowPainter and CommunityStructureCanvas to support dynamic card widths; enhance SpaceCell widget layout and shadow properties for improved UI consistency.
This commit is contained in:
@ -5,13 +5,14 @@ import 'package:syncrow_web/utils/color_manager.dart';
|
|||||||
class SpacesConnectionsArrowPainter extends CustomPainter {
|
class SpacesConnectionsArrowPainter extends CustomPainter {
|
||||||
final List<SpaceConnectionModel> connections;
|
final List<SpaceConnectionModel> connections;
|
||||||
final Map<String, Offset> positions;
|
final Map<String, Offset> positions;
|
||||||
final double cardWidth = 150.0;
|
final Map<String, double> cardWidths;
|
||||||
final double cardHeight = 90.0;
|
final double cardHeight = 90.0;
|
||||||
final Set<String> highlightedUuids;
|
final Set<String> highlightedUuids;
|
||||||
|
|
||||||
SpacesConnectionsArrowPainter({
|
SpacesConnectionsArrowPainter({
|
||||||
required this.connections,
|
required this.connections,
|
||||||
required this.positions,
|
required this.positions,
|
||||||
|
required this.cardWidths,
|
||||||
required this.highlightedUuids,
|
required this.highlightedUuids,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -29,19 +30,30 @@ class SpacesConnectionsArrowPainter extends CustomPainter {
|
|||||||
|
|
||||||
final from = positions[connection.from];
|
final from = positions[connection.from];
|
||||||
final to = positions[connection.to];
|
final to = positions[connection.to];
|
||||||
|
final fromWidth = cardWidths[connection.from] ?? 150.0;
|
||||||
|
final toWidth = cardWidths[connection.to] ?? 150.0;
|
||||||
|
|
||||||
if (from != null && to != null) {
|
if (from != null && to != null) {
|
||||||
final startPoint =
|
final startPoint =
|
||||||
Offset(from.dx + cardWidth / 2, from.dy + cardHeight - 10);
|
Offset(from.dx + fromWidth / 2, from.dy + cardHeight - 10);
|
||||||
final endPoint = Offset(to.dx + cardWidth / 2, to.dy);
|
final endPoint = Offset(to.dx + toWidth / 2, to.dy);
|
||||||
|
|
||||||
final path = Path()..moveTo(startPoint.dx, startPoint.dy);
|
final path = Path()..moveTo(startPoint.dx, startPoint.dy);
|
||||||
|
|
||||||
final controlPoint1 = Offset(startPoint.dx, startPoint.dy + 100);
|
if ((startPoint.dx - endPoint.dx).abs() < 1.0) {
|
||||||
final controlPoint2 = Offset(endPoint.dx, endPoint.dy - 100);
|
path.lineTo(endPoint.dx, endPoint.dy);
|
||||||
|
} else {
|
||||||
path.cubicTo(controlPoint1.dx, controlPoint1.dy, controlPoint2.dx,
|
final controlPoint1 = Offset(startPoint.dx, startPoint.dy + 100);
|
||||||
controlPoint2.dy, endPoint.dx, endPoint.dy);
|
final controlPoint2 = Offset(endPoint.dx, endPoint.dy - 100);
|
||||||
|
path.cubicTo(
|
||||||
|
controlPoint1.dx,
|
||||||
|
controlPoint1.dy,
|
||||||
|
controlPoint2.dx,
|
||||||
|
controlPoint2.dy,
|
||||||
|
endPoint.dx,
|
||||||
|
endPoint.dy,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
canvas.drawPath(path, paint);
|
canvas.drawPath(path, paint);
|
||||||
|
|
||||||
@ -51,7 +63,7 @@ class SpacesConnectionsArrowPainter extends CustomPainter {
|
|||||||
: ColorsManager.blackColor.withValues(alpha: 0.5)
|
: ColorsManager.blackColor.withValues(alpha: 0.5)
|
||||||
..style = PaintingStyle.fill
|
..style = PaintingStyle.fill
|
||||||
..blendMode = BlendMode.srcIn;
|
..blendMode = BlendMode.srcIn;
|
||||||
canvas.drawCircle(endPoint, 4, circlePaint);
|
canvas.drawCircle(endPoint, 6, circlePaint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,10 +30,11 @@ class CommunityStructureCanvas extends StatefulWidget {
|
|||||||
class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
||||||
with SingleTickerProviderStateMixin {
|
with SingleTickerProviderStateMixin {
|
||||||
final Map<String, Offset> _positions = {};
|
final Map<String, Offset> _positions = {};
|
||||||
final double _cardWidth = 150.0;
|
final Map<String, double> _cardWidths = {};
|
||||||
final double _cardHeight = 90.0;
|
final double _cardHeight = 90.0;
|
||||||
final double _horizontalSpacing = 150.0;
|
final double _horizontalSpacing = 150.0;
|
||||||
final double _verticalSpacing = 120.0;
|
final double _verticalSpacing = 120.0;
|
||||||
|
static const double _minCardWidth = 150.0;
|
||||||
|
|
||||||
late final TransformationController _transformationController;
|
late final TransformationController _transformationController;
|
||||||
late final AnimationController _animationController;
|
late final AnimationController _animationController;
|
||||||
@ -51,6 +52,7 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(covariant CommunityStructureCanvas oldWidget) {
|
void didUpdateWidget(covariant CommunityStructureCanvas oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
if (widget.selectedSpace?.uuid != oldWidget.selectedSpace?.uuid) {
|
if (widget.selectedSpace?.uuid != oldWidget.selectedSpace?.uuid) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
@ -58,7 +60,6 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
super.didUpdateWidget(oldWidget);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -68,6 +69,34 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double _calculateCardWidth(String text) {
|
||||||
|
final style = context.textTheme.bodyLarge?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
);
|
||||||
|
final textPainter = TextPainter(
|
||||||
|
text: TextSpan(text: text, style: style),
|
||||||
|
maxLines: 1,
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
)..layout();
|
||||||
|
|
||||||
|
const iconWidth = 40.0;
|
||||||
|
const horizontalPadding = 10.0;
|
||||||
|
const contentPadding = 10.0;
|
||||||
|
final calculatedWidth =
|
||||||
|
iconWidth + horizontalPadding + textPainter.width + contentPadding;
|
||||||
|
|
||||||
|
return calculatedWidth.clamp(_minCardWidth, double.infinity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _calculateAllCardWidths(List<SpaceModel> spaces) {
|
||||||
|
for (final space in spaces) {
|
||||||
|
_cardWidths[space.uuid] = _calculateCardWidth(space.spaceName);
|
||||||
|
if (space.children.isNotEmpty) {
|
||||||
|
_calculateAllCardWidths(space.children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Set<String> _getAllDescendantUuids(SpaceModel space) {
|
Set<String> _getAllDescendantUuids(SpaceModel space) {
|
||||||
final uuids = <String>{};
|
final uuids = <String>{};
|
||||||
for (final child in space.children) {
|
for (final child in space.children) {
|
||||||
@ -102,11 +131,12 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
final position = _positions[space.uuid];
|
final position = _positions[space.uuid];
|
||||||
if (position == null) return;
|
if (position == null) return;
|
||||||
|
|
||||||
|
final cardWidth = _cardWidths[space.uuid] ?? _minCardWidth;
|
||||||
const scale = 1;
|
const scale = 1;
|
||||||
final viewSize = context.size;
|
final viewSize = context.size;
|
||||||
if (viewSize == null) return;
|
if (viewSize == null) return;
|
||||||
|
|
||||||
final x = -position.dx * scale + (viewSize.width / 2) - (_cardWidth * scale / 2);
|
final x = -position.dx * scale + (viewSize.width / 2) - (cardWidth * scale / 2);
|
||||||
final y =
|
final y =
|
||||||
-position.dy * scale + (viewSize.height / 2) - (_cardHeight * scale / 2);
|
-position.dy * scale + (viewSize.height / 2) - (_cardHeight * scale / 2);
|
||||||
|
|
||||||
@ -155,13 +185,16 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
Map<int, double> levelXOffset,
|
Map<int, double> levelXOffset,
|
||||||
) {
|
) {
|
||||||
for (final space in spaces) {
|
for (final space in spaces) {
|
||||||
|
final cardWidth = _cardWidths[space.uuid] ?? _minCardWidth;
|
||||||
double childSubtreeWidth = 0;
|
double childSubtreeWidth = 0;
|
||||||
if (space.children.isNotEmpty) {
|
if (space.children.isNotEmpty) {
|
||||||
_calculateLayout(space.children, depth + 1, levelXOffset);
|
_calculateLayout(space.children, depth + 1, levelXOffset);
|
||||||
final firstChildPos = _positions[space.children.first.uuid];
|
final firstChildPos = _positions[space.children.first.uuid];
|
||||||
final lastChildPos = _positions[space.children.last.uuid];
|
final lastChildPos = _positions[space.children.last.uuid];
|
||||||
if (firstChildPos != null && lastChildPos != null) {
|
if (firstChildPos != null && lastChildPos != null) {
|
||||||
childSubtreeWidth = (lastChildPos.dx + _cardWidth) - firstChildPos.dx;
|
final lastChildWidth =
|
||||||
|
_cardWidths[space.children.last.uuid] ?? _minCardWidth;
|
||||||
|
childSubtreeWidth = (lastChildPos.dx + lastChildWidth) - firstChildPos.dx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,7 +203,7 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
|
|
||||||
if (space.children.isNotEmpty) {
|
if (space.children.isNotEmpty) {
|
||||||
final firstChildPos = _positions[space.children.first.uuid]!;
|
final firstChildPos = _positions[space.children.first.uuid]!;
|
||||||
x = firstChildPos.dx + (childSubtreeWidth - _cardWidth) / 2;
|
x = firstChildPos.dx + (childSubtreeWidth - cardWidth) / 2;
|
||||||
} else {
|
} else {
|
||||||
x = currentX;
|
x = currentX;
|
||||||
}
|
}
|
||||||
@ -187,7 +220,7 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
|
|
||||||
final y = depth * (_verticalSpacing + _cardHeight);
|
final y = depth * (_verticalSpacing + _cardHeight);
|
||||||
_positions[space.uuid] = Offset(x, y);
|
_positions[space.uuid] = Offset(x, y);
|
||||||
levelXOffset[depth] = x + _cardWidth + _horizontalSpacing;
|
levelXOffset[depth] = x + cardWidth + _horizontalSpacing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,8 +235,11 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
|
|
||||||
List<Widget> _buildTreeWidgets() {
|
List<Widget> _buildTreeWidgets() {
|
||||||
_positions.clear();
|
_positions.clear();
|
||||||
|
_cardWidths.clear();
|
||||||
final community = widget.community;
|
final community = widget.community;
|
||||||
|
|
||||||
|
_calculateAllCardWidths(community.spaces);
|
||||||
|
|
||||||
final levelXOffset = <int, double>{};
|
final levelXOffset = <int, double>{};
|
||||||
_calculateLayout(community.spaces, 0, levelXOffset);
|
_calculateLayout(community.spaces, 0, levelXOffset);
|
||||||
|
|
||||||
@ -240,6 +276,7 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
painter: SpacesConnectionsArrowPainter(
|
painter: SpacesConnectionsArrowPainter(
|
||||||
connections: connections,
|
connections: connections,
|
||||||
positions: _positions,
|
positions: _positions,
|
||||||
|
cardWidths: _cardWidths,
|
||||||
highlightedUuids: highlightedUuids,
|
highlightedUuids: highlightedUuids,
|
||||||
),
|
),
|
||||||
child: Stack(alignment: AlignmentDirectional.center, children: widgets),
|
child: Stack(alignment: AlignmentDirectional.center, children: widgets),
|
||||||
@ -271,6 +308,7 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final cardWidth = _cardWidths[space.uuid] ?? _minCardWidth;
|
||||||
final isHighlighted = highlightedUuids.contains(space.uuid);
|
final isHighlighted = highlightedUuids.contains(space.uuid);
|
||||||
final hasNoSelectedSpace = widget.selectedSpace == null;
|
final hasNoSelectedSpace = widget.selectedSpace == null;
|
||||||
|
|
||||||
@ -278,14 +316,10 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
buildSpaceContainer: () {
|
buildSpaceContainer: () {
|
||||||
return Opacity(
|
return Opacity(
|
||||||
opacity: hasNoSelectedSpace || isHighlighted ? 1.0 : 0.5,
|
opacity: hasNoSelectedSpace || isHighlighted ? 1.0 : 0.5,
|
||||||
child: Tooltip(
|
child: SpaceCell(
|
||||||
message: space.spaceName,
|
onTap: () => _onSpaceTapped(space),
|
||||||
preferBelow: false,
|
icon: space.icon,
|
||||||
child: SpaceCell(
|
name: space.spaceName,
|
||||||
onTap: () => _onSpaceTapped(space),
|
|
||||||
icon: space.icon,
|
|
||||||
name: space.spaceName,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -305,7 +339,7 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
Positioned(
|
Positioned(
|
||||||
left: position.dx,
|
left: position.dx,
|
||||||
top: position.dy,
|
top: position.dy,
|
||||||
width: _cardWidth,
|
width: cardWidth,
|
||||||
height: _cardHeight,
|
height: _cardHeight,
|
||||||
child: Draggable<SpaceReorderDataModel>(
|
child: Draggable<SpaceReorderDataModel>(
|
||||||
data: reorderData,
|
data: reorderData,
|
||||||
@ -314,7 +348,7 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
child: Opacity(
|
child: Opacity(
|
||||||
opacity: 0.2,
|
opacity: 0.2,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: _cardWidth,
|
width: cardWidth,
|
||||||
height: _cardHeight,
|
height: _cardHeight,
|
||||||
child: spaceCard,
|
child: spaceCard,
|
||||||
),
|
),
|
||||||
@ -330,7 +364,7 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
);
|
);
|
||||||
|
|
||||||
final targetPos = Offset(
|
final targetPos = Offset(
|
||||||
position.dx + _cardWidth + (_horizontalSpacing / 4) - 20,
|
position.dx + cardWidth + (_horizontalSpacing / 4) - 20,
|
||||||
position.dy,
|
position.dy,
|
||||||
);
|
);
|
||||||
widgets.add(_buildDropTarget(parent, community, i + 1, targetPos));
|
widgets.add(_buildDropTarget(parent, community, i + 1, targetPos));
|
||||||
|
@ -20,21 +20,19 @@ class SpaceCell extends StatelessWidget {
|
|||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 150,
|
padding: const EdgeInsetsDirectional.only(end: 10),
|
||||||
height: 70,
|
height: 70,
|
||||||
decoration: _containerDecoration(),
|
decoration: _containerDecoration(),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
spacing: 10,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
_buildIconContainer(),
|
_buildIconContainer(),
|
||||||
const SizedBox(width: 10),
|
Text(
|
||||||
Expanded(
|
name,
|
||||||
child: Text(
|
style: context.textTheme.bodyLarge?.copyWith(
|
||||||
name,
|
fontWeight: FontWeight.bold,
|
||||||
style: context.textTheme.bodyLarge?.copyWith(
|
color: ColorsManager.blackColor,
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: ColorsManager.blackColor,
|
|
||||||
),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -74,8 +72,9 @@ class SpaceCell extends StatelessWidget {
|
|||||||
borderRadius: BorderRadius.circular(15),
|
borderRadius: BorderRadius.circular(15),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: ColorsManager.lightGrayColor.withValues(alpha: 0.25),
|
color: ColorsManager.lightGrayColor.withValues(alpha: 0.5),
|
||||||
blurRadius: 7,
|
spreadRadius: 2,
|
||||||
|
blurRadius: 5,
|
||||||
offset: const Offset(0, 3),
|
offset: const Offset(0, 3),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
Reference in New Issue
Block a user