Polish floating bottom bar: elevation, height, overlap, and FAB shape
- Give the floating bar and search satellite the app's one shadow (nooDialogShadow) plus a smaller, tighter height on both platforms. - Let every tab's scrollable list compute its own bottom clearance (bottomBarClearance) instead of a flat 100px, so the floating bar no longer overlaps the last row/card. - Square the Android upload FAB off to a rounded-square radius when the bottom bar is attached, keeping it fully round only when floating. - Let a swipe on a file row trigger its action directly once dragged past 1.8x the reveal width, instead of requiring a second tap. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ import 'package:noo/widgets/noo/core/noo_progress_bar.dart';
|
||||
import 'package:noo/widgets/noo/core/noo_search_field.dart';
|
||||
import 'package:noo/widgets/noo/core/noo_segmented_control.dart';
|
||||
import 'package:noo/widgets/noo/core/noo_toggle.dart';
|
||||
import 'package:noo/widgets/noo/nav/noo_nav_style.dart';
|
||||
|
||||
import 'noo_test_utils.dart';
|
||||
|
||||
@@ -247,7 +248,11 @@ void main() {
|
||||
group('NooFab', () {
|
||||
testNooWidgets('shows label and fires onTap', (tester, theme, c) async {
|
||||
var taps = 0;
|
||||
await pumpNoo(tester, NooFab(onTap: () => taps++), theme: theme);
|
||||
await pumpNoo(
|
||||
tester,
|
||||
NooFab(barStyle: NooBottomBarStyle.floating, onTap: () => taps++),
|
||||
theme: theme,
|
||||
);
|
||||
expect(find.text('Upload'), findsOneWidget);
|
||||
expect(tester.getSize(find.byType(NooFab)).height, 56);
|
||||
expect(tester.getSize(find.byType(NooFab)).width, lessThan(200));
|
||||
@@ -263,7 +268,11 @@ void main() {
|
||||
var taps = 0;
|
||||
await pumpNoo(
|
||||
tester,
|
||||
NooFab(collapsed: true, onTap: () => taps++),
|
||||
NooFab(
|
||||
collapsed: true,
|
||||
barStyle: NooBottomBarStyle.floating,
|
||||
onTap: () => taps++,
|
||||
),
|
||||
theme: theme,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
@@ -274,6 +283,44 @@ void main() {
|
||||
await tester.tap(find.byType(NooFab));
|
||||
expect(taps, 1);
|
||||
});
|
||||
|
||||
testNooWidgets('floating bar style stays fully round', (
|
||||
tester,
|
||||
theme,
|
||||
c,
|
||||
) async {
|
||||
await pumpNoo(
|
||||
tester,
|
||||
const NooFab(barStyle: NooBottomBarStyle.floating),
|
||||
theme: theme,
|
||||
);
|
||||
final material = tester.widget<Material>(
|
||||
find.descendant(
|
||||
of: find.byType(NooFab),
|
||||
matching: find.byType(Material),
|
||||
),
|
||||
);
|
||||
expect((material.borderRadius as BorderRadius).topLeft.x, NooRadii.pill);
|
||||
});
|
||||
|
||||
testNooWidgets('attached bar style squares off to a rounded square', (
|
||||
tester,
|
||||
theme,
|
||||
c,
|
||||
) async {
|
||||
await pumpNoo(
|
||||
tester,
|
||||
const NooFab(barStyle: NooBottomBarStyle.attached),
|
||||
theme: theme,
|
||||
);
|
||||
final material = tester.widget<Material>(
|
||||
find.descendant(
|
||||
of: find.byType(NooFab),
|
||||
matching: find.byType(Material),
|
||||
),
|
||||
);
|
||||
expect((material.borderRadius as BorderRadius).topLeft.x, NooRadii.card);
|
||||
});
|
||||
});
|
||||
|
||||
group('NooProgressBar', () {
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:noo/widgets/noo/files/noo_swipe_action.dart';
|
||||
|
||||
import 'noo_test_utils.dart';
|
||||
|
||||
void main() {
|
||||
setUpNooTests();
|
||||
|
||||
group('NooSwipeAction', () {
|
||||
testNooWidgets('a moderate swipe opens the action without triggering it', (
|
||||
tester,
|
||||
theme,
|
||||
c,
|
||||
) async {
|
||||
var triggered = 0;
|
||||
await pumpNoo(
|
||||
tester,
|
||||
SizedBox(
|
||||
width: 360,
|
||||
height: 64,
|
||||
child: NooSwipeAction(
|
||||
endAction: NooSwipeActionSpec(
|
||||
kind: NooSwipeActionKind.delete,
|
||||
onTriggered: () => triggered++,
|
||||
),
|
||||
child: const ColoredBox(
|
||||
color: Colors.white,
|
||||
child: SizedBox(width: 360, height: 64),
|
||||
),
|
||||
),
|
||||
),
|
||||
theme: theme,
|
||||
);
|
||||
// Half the block's own width (96) is well past the "snap open"
|
||||
// threshold but nowhere near the 1.8x trigger extent.
|
||||
await tester.drag(find.byType(NooSwipeAction), const Offset(-60, 0));
|
||||
await tester.pumpAndSettle();
|
||||
expect(triggered, 0);
|
||||
expect(find.text('Delete'), findsOneWidget);
|
||||
});
|
||||
|
||||
testNooWidgets(
|
||||
'swiping past the trigger extent fires the action on release - no '
|
||||
'second tap needed',
|
||||
(tester, theme, c) async {
|
||||
var triggered = 0;
|
||||
await pumpNoo(
|
||||
tester,
|
||||
SizedBox(
|
||||
width: 360,
|
||||
height: 64,
|
||||
child: NooSwipeAction(
|
||||
endAction: NooSwipeActionSpec(
|
||||
kind: NooSwipeActionKind.delete,
|
||||
onTriggered: () => triggered++,
|
||||
),
|
||||
child: const ColoredBox(
|
||||
color: Colors.white,
|
||||
child: SizedBox(width: 360, height: 64),
|
||||
),
|
||||
),
|
||||
),
|
||||
theme: theme,
|
||||
);
|
||||
// 300px of drag on a 96px-wide block clears the 1.8x (~173px)
|
||||
// trigger extent well past the clamp ceiling.
|
||||
await tester.drag(find.byType(NooSwipeAction), const Offset(-300, 0));
|
||||
await tester.pump();
|
||||
expect(triggered, 1);
|
||||
},
|
||||
);
|
||||
|
||||
testNooWidgets('tapping the revealed block still triggers the action too', (
|
||||
tester,
|
||||
theme,
|
||||
c,
|
||||
) async {
|
||||
var triggered = 0;
|
||||
await pumpNoo(
|
||||
tester,
|
||||
SizedBox(
|
||||
width: 360,
|
||||
height: 64,
|
||||
child: NooSwipeAction(
|
||||
startAction: NooSwipeActionSpec(
|
||||
kind: NooSwipeActionKind.favorite,
|
||||
onTriggered: () => triggered++,
|
||||
),
|
||||
child: const ColoredBox(
|
||||
color: Colors.white,
|
||||
child: SizedBox(width: 360, height: 64),
|
||||
),
|
||||
),
|
||||
),
|
||||
theme: theme,
|
||||
);
|
||||
await tester.drag(find.byType(NooSwipeAction), const Offset(60, 0));
|
||||
await tester.pumpAndSettle();
|
||||
expect(triggered, 0);
|
||||
await tester.tap(find.text('Favorite'));
|
||||
await tester.pump();
|
||||
expect(triggered, 1);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user