chore: adopt Dart 3.9 toolchain — honest floor + tall-style reformat (T-353)

Raise the declared minimums in pubspec.yaml to what our deps already
require: Flutter >=3.35.0 / Dart >=3.9.0 (was 3.19.0 / 3.5.0). alchemist
0.12 needs Flutter 3.32; Dart 3.9 first ships in Flutter 3.35, so 3.35 is
the binding floor. Pin the exact build toolchain in .fvmrc (Flutter
3.44.1).

Moving to the Dart 3.9 language level switches `dart format` to the new
"tall" style and enables two new lints. This commit is the resulting
mechanical churn, isolated from any behaviour change:
  - whole-tree `dart format` reformat (tall style)
  - `dart fix` for unnecessary_underscores + use_null_aware_elements

No runtime behaviour change; `make test` green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-11 12:11:53 +02:00
co-authored by Claude Opus 4.8
parent bcea5f15b7
commit 6d0ebab721
444 changed files with 7587 additions and 12849 deletions
+62 -116
View File
@@ -38,10 +38,7 @@ class _AppRoot extends StatelessWidget {
debugShowCheckedModeBanner: false,
title: clideName,
color: const Color(0xFF000000),
pageRouteBuilder: <T>(RouteSettings settings, WidgetBuilder builder) => PageRouteBuilder<T>(
settings: settings,
pageBuilder: (ctx, _, __) => builder(ctx),
),
pageRouteBuilder: <T>(RouteSettings settings, WidgetBuilder builder) => PageRouteBuilder<T>(settings: settings, pageBuilder: (ctx, _, _) => builder(ctx)),
home: _RootShell(services: services),
);
}
@@ -240,35 +237,19 @@ class RootLayout extends StatelessWidget {
child: Row(
children: [
if (sidebarVisible && sidebarCollapsed)
ClideSpine(
label: _sidebarSpineLabel(kernel),
side: SpineSide.left,
onExpand: () => a.setCollapsed(Slots.sidebar, false),
)
ClideSpine(label: _sidebarSpineLabel(kernel), side: SpineSide.left, onExpand: () => a.setCollapsed(Slots.sidebar, false))
else if (sidebarVisible) ...[
SizedBox(
width: sidebarSize,
child: SlotHost(slot: Slots.sidebar),
),
DragResizeHandle(
arrangement: a,
slot: Slots.sidebar,
axis: Axis.horizontal,
),
DragResizeHandle(arrangement: a, slot: Slots.sidebar, axis: Axis.horizontal),
],
const Expanded(child: SlotHost(slot: Slots.workspace)),
if (contextVisible && contextCollapsed)
ClideSpine(
label: 'context',
side: SpineSide.right,
onExpand: () => a.setCollapsed(Slots.contextPanel, false),
)
ClideSpine(label: 'context', side: SpineSide.right, onExpand: () => a.setCollapsed(Slots.contextPanel, false))
else if (contextVisible) ...[
DragResizeHandle(
arrangement: a,
slot: Slots.contextPanel,
axis: Axis.horizontal,
),
DragResizeHandle(arrangement: a, slot: Slots.contextPanel, axis: Axis.horizontal),
SizedBox(
width: contextSize,
child: SlotHost(slot: Slots.contextPanel),
@@ -281,14 +262,18 @@ class RootLayout extends StatelessWidget {
SizedBox(
height: dockHeight,
child: DecoratedBox(
decoration: BoxDecoration(border: Border(top: BorderSide(color: ClideTheme.of(ctx).surface.chromeBorder))),
decoration: BoxDecoration(
border: Border(top: BorderSide(color: ClideTheme.of(ctx).surface.chromeBorder)),
),
child: SlotHost(slot: Slots.dock),
),
),
if (statusVisible)
Container(
height: statusHeight,
decoration: BoxDecoration(border: Border(top: BorderSide(color: ClideTheme.of(ctx).surface.chromeBorder))),
decoration: BoxDecoration(
border: Border(top: BorderSide(color: ClideTheme.of(ctx).surface.chromeBorder)),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
@@ -296,12 +281,18 @@ class RootLayout extends StatelessWidget {
// children) so they never shift when a pane collapses (T-294).
StatusbarCollapseToggle(slot: Slots.sidebar, collapsed: sidebarCollapsed, visible: sidebarVisible),
if (sidebarVisible && !sidebarCollapsed)
SizedBox(width: sidebarSize, child: _BottomRail(slot: Slots.sidebar))
SizedBox(
width: sidebarSize,
child: _BottomRail(slot: Slots.sidebar),
)
else if (sidebarVisible && sidebarCollapsed)
const SizedBox(width: ClideSpine.width),
const Expanded(child: StatusbarHost()),
if (contextVisible && !contextCollapsed)
SizedBox(width: contextSize, child: _BottomRail(slot: Slots.contextPanel))
SizedBox(
width: contextSize,
child: _BottomRail(slot: Slots.contextPanel),
)
else if (contextVisible && contextCollapsed)
const SizedBox(width: ClideSpine.width),
StatusbarCollapseToggle(slot: Slots.contextPanel, collapsed: contextCollapsed, visible: contextVisible),
@@ -392,11 +383,13 @@ class _RightHatContent extends StatelessWidget {
Widget build(BuildContext context) {
if (kIsWeb) return const SizedBox.shrink();
if (!kIsWeb && Platform.isMacOS) return const SizedBox.shrink();
return Row(children: [
_WinBtn(icon: const PhosphorIconPainter(0xe32a), onTap: wc.minimize, tokens: tokens),
_WinBtn(icon: const PhosphorIconPainter(0xe45e), onTap: wc.toggleMaximize, tokens: tokens),
_WinBtn(icon: PhosphorIcons.byName('x'), onTap: wc.close, tokens: tokens, isClose: true),
]);
return Row(
children: [
_WinBtn(icon: const PhosphorIconPainter(0xe32a), onTap: wc.minimize, tokens: tokens),
_WinBtn(icon: const PhosphorIconPainter(0xe45e), onTap: wc.toggleMaximize, tokens: tokens),
_WinBtn(icon: PhosphorIcons.byName('x'), onTap: wc.close, tokens: tokens, isClose: true),
],
);
}
}
@@ -417,11 +410,7 @@ class _WinBtn extends StatelessWidget {
height: hatHeight,
color: hovered ? hoverBg : null,
alignment: Alignment.center,
child: ClideIcon(
icon,
size: 14,
color: hovered && isClose ? tokens.windowControlCloseHoverForeground : tokens.chromeForeground,
),
child: ClideIcon(icon, size: 14, color: hovered && isClose ? tokens.windowControlCloseHoverForeground : tokens.chromeForeground),
),
);
}
@@ -544,26 +533,29 @@ class _ProjectSwitcherDropdownState extends State<_ProjectSwitcherDropdown> {
shrinkWrap: true,
padding: EdgeInsets.zero,
itemCount: filtered.length,
itemBuilder: (ctx, i) => _RecentProjectRow(
project: filtered[i],
tokens: tokens,
onTap: () => _openProject(filtered[i].path),
),
itemBuilder: (ctx, i) => _RecentProjectRow(project: filtered[i], tokens: tokens, onTap: () => _openProject(filtered[i].path)),
),
),
] else
const Padding(padding: EdgeInsets.all(12), child: ClideText('No recent projects.', muted: true)),
Container(
decoration: BoxDecoration(border: Border(top: BorderSide(color: tokens.dividerColor))),
decoration: BoxDecoration(
border: Border(top: BorderSide(color: tokens.dividerColor)),
),
child: Column(
children: [
_ActionRow(
label: 'Open Local Project',
shortcut: Platform.isMacOS ? '⌘O' : 'Ctrl+O',
tokens: tokens,
onTap: () => _runFileCommand('file.openFolder')),
label: 'Open Local Project',
shortcut: Platform.isMacOS ? '⌘O' : 'Ctrl+O',
tokens: tokens,
onTap: () => _runFileCommand('file.openFolder'),
),
_ActionRow(
label: 'New Window', shortcut: Platform.isMacOS ? '⌘⇧N' : 'Ctrl+Shift+N', tokens: tokens, onTap: () => _runFileCommand('file.newWindow')),
label: 'New Window',
shortcut: Platform.isMacOS ? '⌘⇧N' : 'Ctrl+Shift+N',
tokens: tokens,
onTap: () => _runFileCommand('file.newWindow'),
),
if (widget.kernel.project.isOpen)
_ActionRow(label: 'Close Project', shortcut: '', tokens: tokens, onTap: () => _runFileCommand('file.closeWorkspace')),
],
@@ -604,8 +596,14 @@ class _RecentProjectRow extends StatelessWidget {
// Elide a long path instead of overflowing the row
// (matches the welcome recents row; T-160 discipline).
Flexible(
child: ClideText(project.relativePath,
muted: true, fontSize: 12, fontFamily: clideMonoFamily, maxLines: 1, overflow: TextOverflow.ellipsis),
child: ClideText(
project.relativePath,
muted: true,
fontSize: 12,
fontFamily: clideMonoFamily,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
ClideText(' · ', muted: true, fontSize: 12),
ClideIcon(PhosphorIcons.byName('git-branch'), size: 10, color: tokens.globalTextMuted),
@@ -706,10 +704,7 @@ class _SlotHostState extends State<SlotHost> {
return Container(color: tokens.panelBackground);
}
final activeId = kernel.panels.activeTabIn(widget.slot) ?? tabs.first.id;
final active = tabs.firstWhere(
(t) => t.id == activeId,
orElse: () => tabs.first,
);
final active = tabs.firstWhere((t) => t.id == activeId, orElse: () => tabs.first);
return _SlotBody(slot: widget.slot, tabs: tabs, active: active, activeId: activeId);
},
),
@@ -731,21 +726,11 @@ class _SlotBody extends StatelessWidget {
final tokens = ClideTheme.of(context).surface;
if (slot == Slots.sidebar) {
return _SidebarSlot(
tabs: tabs,
active: active,
activeId: activeId,
onSelect: (id) => kernel.panels.activateTab(slot, id),
);
return _SidebarSlot(tabs: tabs, active: active, activeId: activeId, onSelect: (id) => kernel.panels.activateTab(slot, id));
}
if (slot == Slots.contextPanel) {
return _ContextSlot(
tabs: tabs,
active: active,
activeId: activeId,
onSelect: (id) => kernel.panels.activateTab(slot, id),
);
return _ContextSlot(tabs: tabs, active: active, activeId: activeId, onSelect: (id) => kernel.panels.activateTab(slot, id));
}
if (slot == Slots.workspace) {
@@ -757,9 +742,7 @@ class _SlotBody extends StatelessWidget {
child: Column(
children: [
ClideTabBar(
items: [
for (final t in tabs) ClideTabItem(id: t.id, title: _resolveTitle(context, t)),
],
items: [for (final t in tabs) ClideTabItem(id: t.id, title: _resolveTitle(context, t))],
activeId: active.id,
onSelect: (id) => kernel.panels.activateTab(slot, id),
),
@@ -774,21 +757,12 @@ class _SlotBody extends StatelessWidget {
final key = t.titleKey;
final ns = t.i18nNamespace;
if (key == null || ns == null) return t.title;
return ClideKernel.of(context).i18n.string(
key,
namespace: ns,
placeholder: t.title,
);
return ClideKernel.of(context).i18n.string(key, namespace: ns, placeholder: t.title);
}
}
class _SidebarSlot extends StatelessWidget {
const _SidebarSlot({
required this.tabs,
required this.active,
required this.activeId,
required this.onSelect,
});
const _SidebarSlot({required this.tabs, required this.active, required this.activeId, required this.onSelect});
final List<TabContribution> tabs;
final TabContribution active;
@@ -863,10 +837,7 @@ class _WorkspaceSlot extends StatelessWidget {
SizedBox(
height: topHeight,
child: reveal != null
? _RevealedTab(
tab: reveal,
onClose: () => kernel.panels.activateTab(Slots.workspace, _claudeTabId),
)
? _RevealedTab(tab: reveal, onClose: () => kernel.panels.activateTab(Slots.workspace, _claudeTabId))
: topTab.build(ctx),
),
_EditorDragHandle(arrangement: kernel.arrangement, totalHeight: totalHeight),
@@ -903,12 +874,7 @@ class _RevealedTab extends StatelessWidget {
child: Row(
children: [
Expanded(
child: ClideText(
_SlotBody._resolveTitle(context, tab),
fontSize: clideFontCaption,
color: tokens.panelHeaderForeground,
maxLines: 1,
),
child: ClideText(_SlotBody._resolveTitle(context, tab), fontSize: clideFontCaption, color: tokens.panelHeaderForeground, maxLines: 1),
),
Semantics(
button: true,
@@ -918,7 +884,7 @@ class _RevealedTab extends StatelessWidget {
child: ClideTappable(
onTap: onClose,
tooltip: 'Close',
builder: (_, hovered, __) => Padding(
builder: (_, hovered, _) => Padding(
padding: const EdgeInsets.all(6),
child: ClideIcon(PhosphorIcons.byName('x'), size: 12, color: hovered ? tokens.globalForeground : tokens.globalTextMuted),
),
@@ -1027,12 +993,7 @@ class _EditorBumpIntent extends Intent {
}
class _ContextSlot extends StatelessWidget {
const _ContextSlot({
required this.tabs,
required this.active,
required this.activeId,
required this.onSelect,
});
const _ContextSlot({required this.tabs, required this.active, required this.activeId, required this.onSelect});
final List<TabContribution> tabs;
final TabContribution active;
@@ -1042,12 +1003,7 @@ class _ContextSlot extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = ClideTheme.of(context).surface;
return Container(
color: tokens.panelBackground,
alignment: Alignment.topLeft,
padding: const EdgeInsets.only(right: 2),
child: active.build(context),
);
return Container(color: tokens.panelBackground, alignment: Alignment.topLeft, padding: const EdgeInsets.only(right: 2), child: active.build(context));
}
}
@@ -1068,14 +1024,7 @@ class _BottomRail extends StatelessWidget {
return Container(
color: tokens.chromeBackground,
child: ClideIconRail(
items: [
for (final t in tabs)
ClideIconRailItem(
id: t.id,
icon: _iconFor(slot, t),
tooltip: _SlotBody._resolveTitle(ctx, t),
),
],
items: [for (final t in tabs) ClideIconRailItem(id: t.id, icon: _iconFor(slot, t), tooltip: _SlotBody._resolveTitle(ctx, t))],
activeId: activeId,
onSelect: (id) => kernel.panels.activateTab(slot, id),
),
@@ -1209,10 +1158,7 @@ class _WelcomeOverlay extends StatelessWidget {
builder: (ctx, _) {
if (kernel.project.isOpen) return const SizedBox.shrink();
final tokens = ClideTheme.of(ctx).surface;
return ColoredBox(
color: tokens.globalBackground,
child: const WelcomeView(),
);
return ColoredBox(color: tokens.globalBackground, child: const WelcomeView());
},
);
}