Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,37 @@
- refactor: Flutter 3.47.1 support ([#1353](https://github.com/bdlukaa/fluent_ui/pull/1353))
- migrate Material UI imports to the standalone `material_ui` package. [learn more](https://flutter.dev/blog/decoupling-material-cupertino)
- fix: skip hidden `TitleBar` children when collecting semantics ([#1354](https://github.com/bdlukaa/fluent_ui/pull/1354))
- fix: `TitleBar` sizing on minimal pane ([#1308](https://github.com/bdlukaa/fluent_ui/issues/1308))
- refactor: Rework `ScaffoldPage` ([#1358](https://github.com/bdlukaa/fluent_ui/pull/1358))
- `ScaffoldPage` is now a stateless page-content shell. Its `padding` applies consistently to the page's header, content, and footer, while the default horizontal padding follows Fluent adaptive margins (12 px in compact layouts and 24 px otherwise).
- `bottomBar` is now `footer`.
- `ScaffoldPage.withPadding` was removed. Use `ScaffoldPage(padding: ..., content: ...)` instead.
- `PageHeader.padding` and `PageHeader.horizontalPadding` were removed. Page-level spacing belongs to `ScaffoldPage`; remove calls to `PageHeader.horizontalPadding(context)` and configure `ScaffoldPage.padding` when custom spacing is needed.
- `PageHeader` no longer controls page geometry. At compact widths, its command bar moves below the title so long titles and large text scales can wrap without overflowing.

Migration:

```dart
// Before
ScaffoldPage(
header: PageHeader(
title: const Text('Settings'),
padding: const EdgeInsets.symmetric(horizontal: 32),
),
bottomBar: const CommandBar(items: []),
content: const SettingsView(),
);

// After
ScaffoldPage(
header: const PageHeader(title: Text('Settings')),
footer: const CommandBar(items: []),
padding: const EdgeInsets.symmetric(horizontal: 32),
content: const SettingsView(),
);
```

For scrollable pages, continue to use `ScaffoldPage.scrollable`; it now follows the same page-level padding semantics as the regular constructor.
- fix: `ComboBox` no longer throws a layout assertion when opened while fully aligned to the bottom edge of the viewport ([#1350](https://github.com/bdlukaa/fluent_ui/issues/1350))

## 4.16.1
Expand Down
2 changes: 1 addition & 1 deletion example/lib/screens/navigation/navigation_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ class _NavigationBodyItem extends StatelessWidget {

@override
Widget build(final BuildContext context) {
return ScaffoldPage.withPadding(
return ScaffoldPage(
header: PageHeader(title: Text(header ?? 'This is a header text')),
content:
content ??
Expand Down
2 changes: 1 addition & 1 deletion example/lib/screens/theming/colors.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class ColorsPage extends StatelessWidget {
);
return ScaffoldPage.scrollable(
header: const PageHeader(title: Text('Colors Showcase')),
bottomBar: const SizedBox(
footer: const SizedBox(
width: double.infinity,
child: InfoBar(
title: Text('Tip:'),
Expand Down
271 changes: 132 additions & 139 deletions example/lib/screens/theming/icons.dart
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ class _IconsPageState extends State<IconsPage> {
),
),
),
bottomBar: const SizedBox(
footer: const SizedBox(
width: double.infinity,
child: InfoBar(
title: Text('Tip:'),
Expand All @@ -126,167 +126,160 @@ class _IconsPageState extends State<IconsPage> {
),
),
),
content: Padding(
padding: EdgeInsetsDirectional.only(
start: PageHeader.horizontalPadding(context),
end: PageHeader.horizontalPadding(context),
),
child: CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsetsDirectional.only(bottom: 8),
child: CodeSnippetCard(
initiallyOpen: true,
codeSnippet:
'''const WindowsIcon(
content: CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsetsDirectional.only(bottom: 8),
child: CodeSnippetCard(
initiallyOpen: true,
codeSnippet:
'''const WindowsIcon(
$prefix.$iconName,
size: ${size!.toInt()},
color: Color(0x${color!.toARGB32().toRadixString(16).padLeft(8, '0')}),
),''',
child: Row(
spacing: 8,
children: [
WindowsIcon(icon, size: size, color: color),
const Spacer(),
IntrinsicWidth(
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
InfoLabel(
label: 'Icon Color',
child: ComboBox<Color>(
placeholder: const Text('Icon Color'),
onChanged: (final c) =>
setState(() => color = c),
value: color,
isExpanded: true,
items: [
ComboBoxItem(
value: Colors.white,
child: Row(
children: [
buildColorBox(Colors.white),
const SizedBox(width: 10),
const Text('White'),
],
),
child: Row(
spacing: 8,
children: [
WindowsIcon(icon, size: size, color: color),
const Spacer(),
IntrinsicWidth(
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
InfoLabel(
label: 'Icon Color',
child: ComboBox<Color>(
placeholder: const Text('Icon Color'),
onChanged: (final c) => setState(() => color = c),
value: color,
isExpanded: true,
items: [
ComboBoxItem(
value: Colors.white,
child: Row(
children: [
buildColorBox(Colors.white),
const SizedBox(width: 10),
const Text('White'),
],
),
),
ComboBoxItem(
value: const Color(0xE4000000),
child: Row(
children: [
buildColorBox(const Color(0xE4000000)),
const SizedBox(width: 10),
const Text('Black'),
],
),
ComboBoxItem(
value: const Color(0xE4000000),
),
...List.generate(Colors.accentColors.length, (
final index,
) {
final color = Colors.accentColors[index];
return ComboBoxItem(
value: color,
child: Row(
children: [
buildColorBox(const Color(0xE4000000)),
buildColorBox(color),
const SizedBox(width: 10),
const Text('Black'),
Text(accentColorNames[index + 1]),
],
),
),
...List.generate(Colors.accentColors.length, (
final index,
) {
final color = Colors.accentColors[index];
return ComboBoxItem(
value: color,
child: Row(
children: [
buildColorBox(color),
const SizedBox(width: 10),
Text(accentColorNames[index + 1]),
],
),
);
}),
],
),
),
const SizedBox(height: 8),
InfoLabel(
label: 'Icon Size',
child: Slider(
value: size!,
onChanged: (final v) => setState(() {
size = v;
);
}),
min: 8,
max: 56,
label: '${size!.toInt()}',
style: const SliderThemeData(
margin: EdgeInsetsDirectional.zero,
),
],
),
),
const SizedBox(height: 8),
InfoLabel(
label: 'Icon Size',
child: Slider(
value: size!,
onChanged: (final v) => setState(() {
size = v;
}),
min: 8,
max: 56,
label: '${size!.toInt()}',
style: const SliderThemeData(
margin: EdgeInsetsDirectional.zero,
),
),
],
),
),
],
),
],
),
),
],
),
),
),
SliverGrid.builder(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 150,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
),
itemCount: entries.length,
itemBuilder: (final context, final index) {
final e = entries.elementAt(index);
return HoverButton(
onPressed: () async {
final copyText = '$prefix.${e.key}';
await Clipboard.setData(ClipboardData(text: copyText));
),
SliverGrid.builder(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 150,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
),
itemCount: entries.length,
itemBuilder: (final context, final index) {
final e = entries.elementAt(index);
return HoverButton(
onPressed: () async {
final copyText = '$prefix.${e.key}';
await Clipboard.setData(ClipboardData(text: copyText));

if (context.mounted) showCopiedSnackbar(context, copyText);
},
cursor: SystemMouseCursors.copy,
builder: (final context, final states) {
return FocusBorder(
focused: states.isFocused,
renderOutside: false,
child: Tooltip(
useMousePosition: false,
message:
'\nWindowsIcons.${e.key}\n(tap to copy to clipboard)\n',
child: RepaintBoundary(
child: AnimatedContainer(
duration: theme.fasterAnimationDuration,
decoration: BoxDecoration(
color: ButtonThemeData.uncheckedInputColor(
theme,
states,
transparentWhenNone: true,
),
borderRadius: BorderRadius.circular(20),
if (context.mounted) showCopiedSnackbar(context, copyText);
},
cursor: SystemMouseCursors.copy,
builder: (final context, final states) {
return FocusBorder(
focused: states.isFocused,
renderOutside: false,
child: Tooltip(
useMousePosition: false,
message:
'\nWindowsIcons.${e.key}\n(tap to copy to clipboard)\n',
child: RepaintBoundary(
child: AnimatedContainer(
duration: theme.fasterAnimationDuration,
decoration: BoxDecoration(
color: ButtonThemeData.uncheckedInputColor(
theme,
states,
transparentWhenNone: true,
),
padding: const EdgeInsetsDirectional.all(6),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(e.value, size: 40),
Padding(
padding: const EdgeInsetsDirectional.only(
top: 8,
),
child: Text(
snakeCasetoSentenceCase(e.key),
textAlign: TextAlign.center,
overflow: TextOverflow.fade,
),
borderRadius: BorderRadius.circular(20),
),
padding: const EdgeInsetsDirectional.all(6),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(e.value, size: 40),
Padding(
padding: const EdgeInsetsDirectional.only(
top: 8,
),
],
),
child: Text(
snakeCasetoSentenceCase(e.key),
textAlign: TextAlign.center,
overflow: TextOverflow.fade,
),
),
],
),
),
),
);
},
);
},
),
],
),
),
);
},
);
},
),
],
),
);
}
Expand Down
2 changes: 1 addition & 1 deletion example/lib/screens/theming/reveal_focus.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class RevealFocusPage extends StatelessWidget with PageMixin {
@override
Widget build(final BuildContext context) {
final theme = FluentTheme.of(context);
return ScaffoldPage.withPadding(
return ScaffoldPage(
header: PageHeader(
title: const Text('Reveal Focus'),
commandBar: Button(
Expand Down
2 changes: 1 addition & 1 deletion example/lib/screens/theming/typography.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class _TypographyPageState extends State<TypographyPage> {
color ??= typography.display!.color;
typography = typography.apply(displayColor: color);
const Widget spacer = SizedBox(height: 4);
return ScaffoldPage.withPadding(
return ScaffoldPage(
header: PageHeader(
title: const Text('Typography showcase'),
commandBar: SizedBox(
Expand Down
Loading
Loading