mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-10-21 15:02:21 +02:00
feat(settings): connect standard Material settings rows
This commit is contained in:
1 parent
5e6afc7a32
commit
8f6aa00152
2 files changed
+114
-3
No files matched your search
@@ -193,6 +193,23 @@ class _SettingsSearchTargetState extends State<SettingsSearchTarget> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether rows sit in an expressive connected [SettingsGroup], where the gaps
|
||||
/// between row surfaces replace dividers.
|
||||
bool _rowsConnected(BuildContext context) =>
|
||||
context.dependOnInheritedWidgetOfExactType<_ConnectedRowsScope>() != null;
|
||||
|
||||
BorderRadius _connectedRowRadius({
|
||||
required Radius top,
|
||||
required Radius bottom,
|
||||
}) => BorderRadius.vertical(top: top, bottom: bottom);
|
||||
|
||||
class _ConnectedRowsScope extends InheritedWidget {
|
||||
const _ConnectedRowsScope({required super.child});
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(_ConnectedRowsScope oldWidget) => false;
|
||||
}
|
||||
|
||||
class SettingsGroup extends StatelessWidget {
|
||||
final List<Widget> children;
|
||||
final EdgeInsetsGeometry? margin;
|
||||
@@ -244,7 +261,59 @@ class SettingsGroup extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
if (materialExpressiveEnabled(context)) {
|
||||
final expressive = materialExpressiveEnabled(context);
|
||||
// Only groups of standard rows split into connected tiles. Forms and
|
||||
// custom content keep one card, so spacers never become empty tiles.
|
||||
final standardRows =
|
||||
children.isNotEmpty &&
|
||||
children.every(
|
||||
(child) => child is SettingsItem || child is SettingsSwitchItem,
|
||||
);
|
||||
if (expressive && standardRows) {
|
||||
// Expressive connected list: every row is its own surface separated by
|
||||
// a 2dp gap. The group's ends keep the card radius, inner corners stay
|
||||
// tight, and the gaps replace row dividers.
|
||||
final outer = Radius.circular(context.tokens.radiusCard);
|
||||
const inner = Radius.circular(4);
|
||||
final last = children.length - 1;
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) => Padding(
|
||||
padding:
|
||||
margin ??
|
||||
EdgeInsets.symmetric(
|
||||
horizontal:
|
||||
16 +
|
||||
(constraints.hasBoundedWidth
|
||||
? wideInsetForWidth(constraints.maxWidth)
|
||||
: 0),
|
||||
vertical: 4,
|
||||
),
|
||||
child: _ConnectedRowsScope(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
for (final (index, row) in children.indexed) ...[
|
||||
if (index > 0) const SizedBox(height: 2),
|
||||
Material(
|
||||
color: cardColor,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: _connectedRowRadius(
|
||||
top: index == 0 ? outer : inner,
|
||||
bottom: index == last ? outer : inner,
|
||||
),
|
||||
),
|
||||
child: row,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (expressive) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) => Padding(
|
||||
padding:
|
||||
@@ -417,7 +486,7 @@ class SettingsItem extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (showDivider)
|
||||
if (showDivider && !_rowsConnected(context))
|
||||
Divider(
|
||||
height: 1,
|
||||
indent: showIcon
|
||||
@@ -538,7 +607,7 @@ class SettingsSwitchItem extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (showDivider)
|
||||
if (showDivider && !_rowsConnected(context))
|
||||
Divider(
|
||||
height: 1,
|
||||
indent: showIcon
|
||||
|
||||
@@ -454,4 +454,46 @@ void main() {
|
||||
expect(find.byType(CupertinoButton), findsAtLeast(2));
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
testWidgets('standard settings rows connect; custom content keeps a card', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.pumpWidget(
|
||||
_host(
|
||||
SettingsGroup(
|
||||
children: [
|
||||
SettingsItem(title: 'First', onTap: () {}),
|
||||
SettingsSwitchItem(title: 'Second', value: true, onChanged: (_) {}),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
final group = find.byType(SettingsGroup);
|
||||
expect(
|
||||
find.descendant(of: group, matching: find.byType(M3ECard)),
|
||||
findsNothing,
|
||||
);
|
||||
expect(
|
||||
find.descendant(of: group, matching: find.byType(Divider)),
|
||||
findsNothing,
|
||||
);
|
||||
final first = tester.getRect(find.text('First'));
|
||||
final second = tester.getRect(find.text('Second'));
|
||||
expect(second.top, greaterThan(first.bottom));
|
||||
|
||||
await tester.pumpWidget(
|
||||
_host(
|
||||
SettingsGroup(
|
||||
children: [
|
||||
SettingsItem(title: 'Field', onTap: () {}),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(M3ECard), findsOneWidget);
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user