feat: adjust button width instead of padding

This commit is contained in:
Tien Do Nam
2024-08-20 00:28:59 +02:00
parent 109cb4f066
commit b6c82e10c2
3 changed files with 94 additions and 52 deletions
+1 -1
View File
@@ -3,7 +3,7 @@
- feat: add button to retry a failed file transfer (@Tienisto)
- feat: show tooltip on the "Scan" button (@Tienisto)
- feat: treat any URI as link, so it becomes clickable on receiver (e.g. file://, obsidian://) (@Tienisto)
- feat(mobile): adjust padding between buttons in send tab to indicate that it's scrollable (@Tienisto)
- feat(mobile): adjust buttons width in send tab to indicate that it's scrollable (@Tienisto)
- feat(windows): title bar color should match the system theme (@FutoTan)
- fix: memory leak when sending files (regression in 1.15.0, 1.15.2 only fixed receiving files) (@Tienisto)
- fix(windows): LocalSend window is invisible at app start (@Tienisto)
+2 -2
View File
@@ -64,8 +64,8 @@ class SendTab extends StatelessWidget {
HorizontalClipListView(
outerHorizontalPadding: 15,
outerVerticalPadding: 10,
minPadding: 10,
childWidth: buttonWidth,
childPadding: 10,
minChildWidth: buttonWidth,
children: _options.map((option) {
return BigButton(
icon: option.icon,
+91 -49
View File
@@ -1,23 +1,21 @@
import 'dart:math';
import 'package:flutter/material.dart';
/// A horizontal list that adjusts the padding if the screen is too small.
/// In this case, the padding increases until half of the next button is visible.
/// A horizontal list that adjusts the width if the screen is too small.
/// In this case, the width increases until 10% - 50% of the next button is visible.
/// This is useful to communicate to the user that there are more buttons to the right.
class HorizontalClipListView extends StatelessWidget {
final double outerHorizontalPadding;
final double outerVerticalPadding;
final double minPadding;
final double childWidth;
final double childPadding;
final double minChildWidth;
final List<Widget> children;
const HorizontalClipListView({
super.key,
required this.outerHorizontalPadding,
required this.outerVerticalPadding,
required this.minPadding,
required this.childWidth,
required this.childPadding,
required this.minChildWidth,
required this.children,
});
@@ -25,21 +23,16 @@ class HorizontalClipListView extends StatelessWidget {
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
final availableWidth = constraints.maxWidth - outerHorizontalPadding;
final requiredWidth = childWidth * (children.length - 1) + childWidth * 0.2 + minPadding * (children.length - 1);
final padding = switch (requiredWidth <= availableWidth) {
true => minPadding,
false => _calcPadding(
availableWidth: availableWidth,
childWidth: childWidth,
childrenCount: children.length,
minPadding: minPadding,
),
};
final childWidth = _calcOptimalButtonWidth(
availableWidth: constraints.maxWidth,
paddingLeft: outerHorizontalPadding,
childrenCount: children.length,
minChildWidth: minChildWidth,
childPadding: childPadding,
);
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Padding(
key: ValueKey(padding),
padding: EdgeInsets.symmetric(
horizontal: outerHorizontalPadding,
vertical: outerVerticalPadding,
@@ -48,11 +41,16 @@ class HorizontalClipListView extends StatelessWidget {
children: [
for (int i = 0; i < children.length; i++)
i == children.length - 1
? children[i]
: Padding(
key: ValueKey(i),
padding: EdgeInsets.only(right: padding),
? SizedBox(
width: childWidth,
child: children[i],
)
: Padding(
padding: EdgeInsets.only(right: childPadding),
child: SizedBox(
width: childWidth,
child: children[i],
),
),
],
),
@@ -63,40 +61,84 @@ class HorizontalClipListView extends StatelessWidget {
}
}
double _calcPadding({
double _calcOptimalButtonWidth({
required double availableWidth,
required double childWidth,
required double paddingLeft,
required int childrenCount,
required double minPadding,
required double minChildWidth,
required double childPadding,
}) {
final possiblePaddings = const [0.15, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9].map((percentage) => _calcPaddingFormula(
availableWidth: availableWidth,
childWidth: childWidth,
childrenCount: childrenCount,
minPadding: minPadding,
clipPercentage: percentage,
));
int childWidth = minChildWidth.toInt();
while (true) {
if (_fitsOnScreen(
availableWidth: availableWidth,
paddingLeft: paddingLeft,
childrenCount: childrenCount,
childWidth: childWidth.toDouble(),
childPadding: childPadding,
) ||
_fitsPartially(
availableWidth: availableWidth,
paddingLeft: paddingLeft,
childrenCount: childrenCount,
childWidth: childWidth.toDouble(),
childPadding: childPadding,
)) {
return childWidth.toDouble();
}
return max(minPadding, possiblePaddings.reduce(min));
childWidth++;
}
}
double _calcPaddingFormula({
bool _fitsOnScreen({
required double availableWidth,
required double childWidth,
required double paddingLeft,
required int childrenCount,
required double minPadding,
required double clipPercentage,
required double childWidth,
required double childPadding,
}) {
int visibleChildren = 0;
for (int i = 1; i <= childrenCount; i++) {
if (childWidth * i + minPadding * (i - 1) <= availableWidth + childWidth * clipPercentage) {
visibleChildren++;
} else {
break;
return paddingLeft + childrenCount * childWidth + (childrenCount - 1) * childPadding <= availableWidth;
}
bool _fitsPartially({
required double availableWidth,
required double paddingLeft,
required int childrenCount,
required double childWidth,
required double childPadding,
}) {
for (int i = 2; i <= childrenCount; i++) {
final minWidth = _calcTotalWidthWithPartialLastItem(
paddingLeft: paddingLeft,
childrenCount: i,
childWidth: childWidth,
childPadding: childPadding,
lastItemPercentage: 0.1,
);
final maxWidth = _calcTotalWidthWithPartialLastItem(
paddingLeft: paddingLeft,
childrenCount: i,
childWidth: childWidth,
childPadding: childPadding,
lastItemPercentage: 0.5,
);
if (minWidth <= availableWidth && maxWidth > availableWidth) {
return true;
}
}
final padding = (availableWidth + (childWidth * clipPercentage) - childWidth * visibleChildren) / (visibleChildren - 1);
return padding;
return false;
}
@pragma('vm:prefer-inline')
@pragma('dart2js:tryInline')
double _calcTotalWidthWithPartialLastItem({
required double paddingLeft,
required int childrenCount,
required double childWidth,
required double childPadding,
required double lastItemPercentage,
}) {
return paddingLeft + (childrenCount - 1) * childWidth + childWidth * lastItemPercentage + (childrenCount - 1) * childPadding;
}