feat(mobile): adjust padding between buttons in send tab to indicate that it's scrollable (#1660)

This commit is contained in:
Tien Do Nam
2024-08-19 10:21:28 +02:00
committed by GitHub
parent 42a1036adf
commit 4dd4cde868
4 changed files with 130 additions and 31 deletions
+1
View File
@@ -1,6 +1,7 @@
## 1.15.4 (unreleased)
- feat: show tooltip on the "Scan" button (@Tienisto)
- feat(mobile): adjust padding between buttons in send tab to indicate that it's scrollable (@Tienisto)
- feat(windows): title bar color should match the system theme (@FutoTan)
- fix(desktop): multiple monitor setup may cause the window to be invisible at app start (@Tienisto)
- i18n: distinguish between "Exit" and "Quit" depending on the platform (@sergd88)
+20 -22
View File
@@ -24,9 +24,11 @@ import 'package:localsend_app/widget/custom_icon_button.dart';
import 'package:localsend_app/widget/dialogs/add_file_dialog.dart';
import 'package:localsend_app/widget/dialogs/send_mode_help_dialog.dart';
import 'package:localsend_app/widget/file_thumbnail.dart';
import 'package:localsend_app/widget/horizontal_clip_list_view.dart';
import 'package:localsend_app/widget/list_tile/device_list_tile.dart';
import 'package:localsend_app/widget/list_tile/device_placeholder_list_tile.dart';
import 'package:localsend_app/widget/opacity_slideshow.dart';
import 'package:localsend_app/widget/responsive_builder.dart';
import 'package:localsend_app/widget/responsive_list_view.dart';
import 'package:localsend_app/widget/rotating_widget.dart';
import 'package:refena_flutter/refena_flutter.dart';
@@ -44,6 +46,8 @@ class SendTab extends StatelessWidget {
provider: sendTabVmProvider,
init: (context) => context.global.dispatchAsync(SendTabInitAction(context)), // ignore: discarded_futures
builder: (context, vm) {
final sizingInformation = SizingInformation(MediaQuery.sizeOf(context).width);
final buttonWidth = sizingInformation.isDesktop ? BigButton.desktopWidth : BigButton.mobileWidth;
final ref = context.ref;
return ResponsiveListView(
padding: EdgeInsets.zero,
@@ -57,28 +61,22 @@ class SendTab extends StatelessWidget {
style: Theme.of(context).textTheme.titleMedium,
),
),
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
const SizedBox(width: 10),
..._options.map((option) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 5),
child: BigButton(
icon: option.icon,
label: option.label,
filled: false,
onTap: () async => ref.global.dispatchAsync(PickFileAction(
option: option,
context: context,
)),
),
);
}),
const SizedBox(width: 10),
],
),
HorizontalClipListView(
outerHorizontalPadding: 15,
outerVerticalPadding: 10,
minPadding: 10,
childWidth: buttonWidth,
children: _options.map((option) {
return BigButton(
icon: option.icon,
label: option.label,
filled: false,
onTap: () async => ref.global.dispatchAsync(PickFileAction(
option: option,
context: context,
)),
);
}).toList(),
),
] else ...[
Card(
+7 -9
View File
@@ -3,6 +3,9 @@ import 'package:localsend_app/config/theme.dart';
import 'package:localsend_app/widget/responsive_builder.dart';
class BigButton extends StatelessWidget {
static const double desktopWidth = 100.0;
static const double mobileWidth = 90.0;
final IconData icon;
final String label;
final bool filled;
@@ -19,15 +22,10 @@ class BigButton extends StatelessWidget {
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final sizingInformation = SizingInformation(MediaQuery.sizeOf(context).width);
final buttonWidth = sizingInformation.isDesktop ? 100.0 : 90.0;
const buttonHeight = 65.0;
return ConstrainedBox(
constraints: BoxConstraints(
maxWidth: buttonWidth,
minWidth: buttonWidth,
minHeight: buttonHeight,
maxHeight: buttonHeight,
),
final buttonWidth = sizingInformation.isDesktop ? desktopWidth : mobileWidth;
return SizedBox(
width: buttonWidth,
height: 65.0,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: filled ? colorScheme.primary : colorScheme.secondaryContainerIfDark,
@@ -0,0 +1,102 @@
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.
/// 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 List<Widget> children;
const HorizontalClipListView({
super.key,
required this.outerHorizontalPadding,
required this.outerVerticalPadding,
required this.minPadding,
required this.childWidth,
required this.children,
});
@override
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,
),
};
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Padding(
key: ValueKey(padding),
padding: EdgeInsets.symmetric(
horizontal: outerHorizontalPadding,
vertical: outerVerticalPadding,
),
child: Row(
children: [
for (int i = 0; i < children.length; i++)
i == children.length - 1
? children[i]
: Padding(
key: ValueKey(i),
padding: EdgeInsets.only(right: padding),
child: children[i],
),
],
),
),
);
},
);
}
}
double _calcPadding({
required double availableWidth,
required double childWidth,
required int childrenCount,
required double minPadding,
}) {
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,
));
return max(minPadding, possiblePaddings.reduce(min));
}
double _calcPaddingFormula({
required double availableWidth,
required double childWidth,
required int childrenCount,
required double minPadding,
required double clipPercentage,
}) {
int visibleChildren = 0;
for (int i = 1; i <= childrenCount; i++) {
if (childWidth * i + minPadding * (i - 1) <= availableWidth + childWidth * clipPercentage) {
visibleChildren++;
} else {
break;
}
}
final padding = (availableWidth + (childWidth * clipPercentage) - childWidth * visibleChildren) / (visibleChildren - 1);
return padding;
}