Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 29 additions & 20 deletions lib/src/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,28 @@ class Utils {
required int totalSwitches,
List<double>? customWidths,
required double minWidth}) {
/// Extra width to prevent overflow and add padding
/// Extra width to prevent overflow and add padding — same slack used for both paths.
double extraWidth = 0.10 * totalSwitches;

/// Max screen width
double screenWidth = MediaQuery.of(context).size.width;

/// Returns width per label
///
/// Returns passed minWidth per label if total requested width plus extra width is less than max screen width.
/// Returns calculated width to fit within the max screen width if total requested width plus extra width is more than max screen width.
return customWidths != null
? customWidths[index]
: ((totalSwitches + extraWidth) * minWidth < screenWidth
? minWidth
: screenWidth / (totalSwitches + extraWidth));
if (customWidths != null) {
/// Compute the available space the same way the non-custom path does,
/// subtracting the same slack so dividers/borders are accounted for.
final double available = screenWidth / (1 + extraWidth / totalSwitches);
final double total =
customWidths.fold<double>(0, (sum, v) => sum + v);

/// Scale factor is computed once and applied to a single index — O(n) over the build pass.
final double scale = total > available ? available / total : 1.0;
return customWidths[index] * scale;
}

/// Returns passed minWidth per label if total fits; otherwise scales to fit.
return (totalSwitches + extraWidth) * minWidth < screenWidth
? minWidth
: screenWidth / (totalSwitches + extraWidth);
}

/// Ignores customHeights if toggle switch is horizontal
Expand All @@ -33,20 +40,22 @@ class Utils {
required int totalSwitches,
List<double>? customHeights,
required double minHeight}) {
/// Extra height to prevent overflow and add padding
/// Extra height to prevent overflow and add padding — same slack used for both paths.
double extraHeight = 0.10 * totalSwitches;

/// Max screen height
double screenHeight = MediaQuery.of(context).size.height;

/// Returns width per label
///
/// Returns passed minHeight per label if total requested width plus extra height is less than max screen height.
/// Returns calculated width to fit within the max screen width if total requested width plus extra height is more than max screen height.
return customHeights != null
? customHeights[index]
: ((totalSwitches + extraHeight) * minHeight < screenHeight
? minHeight
: screenHeight / (totalSwitches + extraHeight));
if (customHeights != null) {
final double available = screenHeight / (1 + extraHeight / totalSwitches);
final double total =
customHeights.fold<double>(0, (sum, v) => sum + v);
final double scale = total > available ? available / total : 1.0;
return customHeights[index] * scale;
}

return (totalSwitches + extraHeight) * minHeight < screenHeight
? minHeight
: screenHeight / (totalSwitches + extraHeight);
}
}
43 changes: 43 additions & 0 deletions test/toggle_switch_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -283,4 +283,47 @@ void main() {
expect(helloTextFinder, findsOneWidget);
expect(flutterTextFinder, findsOneWidget);
});

// customWidths that exceed screen width should be scaled down proportionally,
// preserving relative ratios without causing overflow.
testWidgets('customWidths exceeding screen width are scaled proportionally',
(WidgetTester tester) async {
// Screen width: 400. customWidths total: 65 + 300 + 65 + 50 = 480,
// so the widths should be scaled proportionally.
await tester.pumpWidget(
MediaQuery(
data: const MediaQueryData(size: Size(400, 800)),
child: MaterialApp(
home: Scaffold(
body: ToggleSwitch(
totalSwitches: 4,
labels: const ['A', 'B', 'C', 'D'],
customWidths: [65.0, 300.0, 65.0, 50.0], // total 480 > 400
minWidth: double.maxFinite,
onToggle: (_) {},
),
),
),
),
);

// Widget should render without overflow errors.
expect(tester.takeException(), isNull);

// All labels should be visible.
expect(find.text('A'), findsOneWidget);
expect(find.text('B'), findsOneWidget);
expect(find.text('C'), findsOneWidget);
expect(find.text('D'), findsOneWidget);
Comment thread
Vasusn marked this conversation as resolved.

// 'B' container (customWidth 300) should be wider than 'A' container (customWidth 65),
// confirming proportional ratios are preserved after scaling.
final containerA = find.ancestor(
of: find.text('A'), matching: find.byType(AnimatedContainer));
final containerB = find.ancestor(
of: find.text('B'), matching: find.byType(AnimatedContainer));
final widthA = tester.getSize(containerA.first).width;
final widthB = tester.getSize(containerB.first).width;
expect(widthB, greaterThan(widthA));
});
}