diff --git a/CHANGELOG.md b/CHANGELOG.md
index 15c38001..cefe920e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,6 @@
+## [1.12.0]
+✨ New widget: `SliverToolBar`
+
## [1.11.1]
* Fixed an issue where the `MacosSearchField` would not perform an action when an item was selected.
diff --git a/README.md b/README.md
index 1a1a1451..cd74c79c 100644
--- a/README.md
+++ b/README.md
@@ -57,6 +57,7 @@ should avoid allowing your application window to be resized below the height of
- [MacosScaffold](#macosscaffold)
- [Modern Window Look](#modern-window-look)
- [ToolBar](#toolbar)
+ - [SliverToolBar](#SliverToolBar)
- [MacosListTile](#MacosListTile)
- [MacosTabView](#MacosTabView)
@@ -407,6 +408,33 @@ CustomToolbarItem(
),
```
+## `SliverToolBar`
+
+
+`SliverToolbar` is a variant of the standard `ToolBar`, with the key difference being that (as the name implies), it
+is compatible with scrollable widgets like `CustomScrollView` and `NestedScrollView`. There are three additional
+properties on `SliverToolBar`:
+* `pinned`, which determines if the toolbar should remain visible while scrolling
+* `floating`, which determines if the toolbar should become visible as soon as the use starts scrolling upwards
+* `opacity`, which manages the translucency effect of the toolbar
+
+This widget enables developers to achieve the toolbar behaviors seen in Apple's App Store.
+
+Sample usage:
+```dart
+return CustomScrollView(
+ controller: scrollController,
+ slivers: [
+ SliverToolBar(
+ title: const Text('SliverToolbar'),
+ pinned: true,
+ toolbarOpacity: 0.75,
+ ),
+ // Other slivers below
+ ],
+);
+```
+
## MacosListTile
A widget that aims to approximate the [`ListTile`](https://api.flutter.dev/flutter/material/ListTile-class.html) widget found in
@@ -414,7 +442,7 @@ Flutter's material library.
![MacosListTile](https://imgur.com/pQB99M2.png)
-Usage:
+Sample usage:
```dart
MacosListTile(
leading: const Icon(CupertinoIcons.lightbulb),
diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml
index 9c4b285c..2d96245c 100644
--- a/example/analysis_options.yaml
+++ b/example/analysis_options.yaml
@@ -23,6 +23,7 @@ linter:
# producing the lint.
rules:
- use_super_parameters
+ - prefer_single_quotes
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
diff --git a/example/assets/sf_symbols/macwindow.on.rectangle_2x.png b/example/assets/sf_symbols/macwindow.on.rectangle_2x.png
new file mode 100644
index 00000000..e17fbdb7
Binary files /dev/null and b/example/assets/sf_symbols/macwindow.on.rectangle_2x.png differ
diff --git a/example/assets/sf_symbols/menubar.arrow.down.rectangle_2x.png b/example/assets/sf_symbols/menubar.arrow.down.rectangle_2x.png
new file mode 100644
index 00000000..9c9fe7a2
Binary files /dev/null and b/example/assets/sf_symbols/menubar.arrow.down.rectangle_2x.png differ
diff --git a/example/assets/sf_symbols/menubar.arrow.up.rectangle_2x.png b/example/assets/sf_symbols/menubar.arrow.up.rectangle_2x.png
new file mode 100644
index 00000000..5621fab0
Binary files /dev/null and b/example/assets/sf_symbols/menubar.arrow.up.rectangle_2x.png differ
diff --git a/example/assets/sf_symbols/menubar.rectangle_2x.png b/example/assets/sf_symbols/menubar.rectangle_2x.png
new file mode 100644
index 00000000..487446ff
Binary files /dev/null and b/example/assets/sf_symbols/menubar.rectangle_2x.png differ
diff --git a/example/lib/main.dart b/example/lib/main.dart
index 447f1fcd..3d1a24e7 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -4,6 +4,7 @@ import 'package:example/pages/dialogs_page.dart';
import 'package:example/pages/fields_page.dart';
import 'package:example/pages/indicators_page.dart';
import 'package:example/pages/selectors_page.dart';
+import 'package:example/pages/sliver_toolbar_page.dart';
import 'package:example/pages/tabview_page.dart';
import 'package:example/pages/toolbar_page.dart';
import 'package:flutter/cupertino.dart';
@@ -68,8 +69,9 @@ class _WidgetGalleryState extends State {
),
const DialogsPage(),
const ToolbarPage(),
- const SelectorsPage(),
+ const SliverToolbarPage(),
const TabViewPage(),
+ const SelectorsPage(),
];
@override
@@ -234,8 +236,30 @@ class _WidgetGalleryState extends State {
label: Text('Dialogs & Sheets'),
),
const SidebarItem(
- leading: MacosIcon(CupertinoIcons.macwindow),
- label: Text('Toolbar'),
+ leading: MacosImageIcon(
+ AssetImage(
+ 'assets/sf_symbols/macwindow.on.rectangle_2x.png',
+ ),
+ ),
+ label: Text('Layout'),
+ disclosureItems: [
+ SidebarItem(
+ leading: MacosIcon(CupertinoIcons.macwindow),
+ label: Text('Toolbar'),
+ ),
+ SidebarItem(
+ leading: MacosImageIcon(
+ AssetImage(
+ 'assets/sf_symbols/menubar.rectangle_2x.png',
+ ),
+ ),
+ label: Text('SliverToolbar'),
+ ),
+ SidebarItem(
+ leading: MacosIcon(CupertinoIcons.uiwindow_split_2x1),
+ label: Text('TabView'),
+ ),
+ ],
),
const SidebarItem(
leading: MacosImageIcon(
@@ -245,10 +269,6 @@ class _WidgetGalleryState extends State {
),
label: Text('Selectors'),
),
- const SidebarItem(
- leading: MacosIcon(CupertinoIcons.uiwindow_split_2x1),
- label: Text('TabView'),
- ),
],
);
},
diff --git a/example/lib/pages/buttons_page.dart b/example/lib/pages/buttons_page.dart
index 6533484b..6c373f5f 100644
--- a/example/lib/pages/buttons_page.dart
+++ b/example/lib/pages/buttons_page.dart
@@ -230,52 +230,52 @@ class _ButtonsPageState extends State {
mainAxisAlignment: MainAxisAlignment.center,
children: [
MacosPulldownButton(
- title: "PDF",
+ title: 'PDF',
items: [
MacosPulldownMenuItem(
title: const Text('Open in Preview'),
onTap: () =>
- debugPrint("Opening in preview..."),
+ debugPrint('Opening in preview...'),
),
MacosPulldownMenuItem(
title: const Text('Save as PDF...'),
- onTap: () => debugPrint("Saving as PDF..."),
+ onTap: () => debugPrint('Saving as PDF...'),
),
MacosPulldownMenuItem(
enabled: false,
title: const Text('Save as Postscript'),
onTap: () =>
- debugPrint("Saving as Postscript..."),
+ debugPrint('Saving as Postscript...'),
),
const MacosPulldownMenuDivider(),
MacosPulldownMenuItem(
enabled: false,
title: const Text('Save to iCloud Drive'),
onTap: () =>
- debugPrint("Saving to iCloud..."),
+ debugPrint('Saving to iCloud...'),
),
MacosPulldownMenuItem(
enabled: false,
title: const Text('Save to Web Receipts'),
onTap: () =>
- debugPrint("Saving to Web Receipts..."),
+ debugPrint('Saving to Web Receipts...'),
),
MacosPulldownMenuItem(
title: const Text('Send in Mail...'),
onTap: () =>
- debugPrint("Sending via Mail..."),
+ debugPrint('Sending via Mail...'),
),
const MacosPulldownMenuDivider(),
MacosPulldownMenuItem(
title: const Text('Edit Menu...'),
- onTap: () => debugPrint("Editing menu..."),
+ onTap: () => debugPrint('Editing menu...'),
),
],
),
const SizedBox(width: 20),
const MacosPulldownButton(
- title: "PDF",
- disabledTitle: "Disabled",
+ title: 'PDF',
+ disabledTitle: 'Disabled',
items: [],
),
],
@@ -290,34 +290,34 @@ class _ButtonsPageState extends State {
MacosPulldownMenuItem(
title: const Text('New Folder'),
onTap: () =>
- debugPrint("Creating new folder..."),
+ debugPrint('Creating new folder...'),
),
MacosPulldownMenuItem(
title: const Text('Open'),
- onTap: () => debugPrint("Opening..."),
+ onTap: () => debugPrint('Opening...'),
),
MacosPulldownMenuItem(
title: const Text('Open with...'),
- onTap: () => debugPrint("Opening with..."),
+ onTap: () => debugPrint('Opening with...'),
),
MacosPulldownMenuItem(
title: const Text('Import from iPhone...'),
- onTap: () => debugPrint("Importing..."),
+ onTap: () => debugPrint('Importing...'),
),
const MacosPulldownMenuDivider(),
MacosPulldownMenuItem(
enabled: false,
title: const Text('Remove'),
- onTap: () => debugPrint("Deleting..."),
+ onTap: () => debugPrint('Deleting...'),
),
MacosPulldownMenuItem(
title: const Text('Move to Bin'),
- onTap: () => debugPrint("Moving to Bin..."),
+ onTap: () => debugPrint('Moving to Bin...'),
),
const MacosPulldownMenuDivider(),
MacosPulldownMenuItem(
title: const Text('Tags...'),
- onTap: () => debugPrint("Tags..."),
+ onTap: () => debugPrint('Tags...'),
),
],
),
@@ -353,7 +353,7 @@ class _ButtonsPageState extends State {
),
const SizedBox(width: 20),
MacosPopupButton(
- disabledHint: const Text("Disabled"),
+ disabledHint: const Text('Disabled'),
onChanged: null,
items: null,
),
@@ -474,38 +474,38 @@ class _ButtonsPageState extends State {
}
const languages = [
- "Mandarin Chinese",
- "Spanish",
- "English",
- "Hindi/Urdu",
- "Arabic",
- "Bengali",
- "Portuguese",
- "Russian",
- "Japanese",
- "German",
- "Thai",
- "Greek",
- "Nepali",
- "Punjabi",
- "Wu",
- "French",
- "Telugu",
- "Vietnamese",
- "Marathi",
- "Korean",
- "Tamil",
- "Italian",
- "Turkish",
- "Cantonese/Yue",
- "Urdu",
- "Javanese",
- "Egyptian Arabic",
- "Gujarati",
- "Iranian Persian",
- "Indonesian",
- "Polish",
- "Ukrainian",
- "Romanian",
- "Dutch"
+ 'Mandarin Chinese',
+ 'Spanish',
+ 'English',
+ 'Hindi/Urdu',
+ 'Arabic',
+ 'Bengali',
+ 'Portuguese',
+ 'Russian',
+ 'Japanese',
+ 'German',
+ 'Thai',
+ 'Greek',
+ 'Nepali',
+ 'Punjabi',
+ 'Wu',
+ 'French',
+ 'Telugu',
+ 'Vietnamese',
+ 'Marathi',
+ 'Korean',
+ 'Tamil',
+ 'Italian',
+ 'Turkish',
+ 'Cantonese/Yue',
+ 'Urdu',
+ 'Javanese',
+ 'Egyptian Arabic',
+ 'Gujarati',
+ 'Iranian Persian',
+ 'Indonesian',
+ 'Polish',
+ 'Ukrainian',
+ 'Romanian',
+ 'Dutch'
];
diff --git a/example/lib/pages/fields_page.dart b/example/lib/pages/fields_page.dart
index 2172019e..94785708 100644
--- a/example/lib/pages/fields_page.dart
+++ b/example/lib/pages/fields_page.dart
@@ -109,7 +109,7 @@ class _FieldsPageState extends State {
emptyWidget: const Center(
child: Padding(
padding: EdgeInsets.all(8.0),
- child: Text("No action found!"),
+ child: Text('No action found!'),
)),
placeholder: 'Search for an action...',
onResultSelected: (resultItem) {
@@ -142,251 +142,251 @@ class _FieldsPageState extends State {
}
const countries = [
- "Afghanistan",
- "Albania",
- "Algeria",
- "Andorra",
- "Angola",
- "Anguilla",
- "Antigua and Barbuda",
- "Argentina",
- "Armenia",
- "Aruba",
- "Australia",
- "Austria",
- "Azerbaijan",
- "Bahamas",
- "Bahrain",
- "Bangladesh",
- "Barbados",
- "Belarus",
- "Belgium",
- "Belize",
- "Benin",
- "Bermuda",
- "Bhutan",
- "Bolivia",
- "Bosnia and Herzegovina",
- "Botswana",
- "Brazil",
- "British Virgin Islands",
- "Brunei",
- "Bulgaria",
- "Burkina Faso",
- "Burundi",
- "Cambodia",
- "Cameroon",
- "Cape Verde",
- "Cayman Islands",
- "Chad",
- "Chile",
- "China",
- "Colombia",
- "Congo",
- "Cook Islands",
- "Costa Rica",
- "Cote D Ivoire",
- "Croatia",
- "Cruise Ship",
- "Cuba",
- "Cyprus",
- "Czech Republic",
- "Denmark",
- "Djibouti",
- "Dominica",
- "Dominican Republic",
- "Ecuador",
- "Egypt",
- "El Salvador",
- "Equatorial Guinea",
- "Estonia",
- "Ethiopia",
- "Falkland Islands",
- "Faroe Islands",
- "Fiji",
- "Finland",
- "France",
- "French Polynesia",
- "French West Indies",
- "Gabon",
- "Gambia",
- "Georgia",
- "Germany",
- "Ghana",
- "Gibraltar",
- "Greece",
- "Greenland",
- "Grenada",
- "Guam",
- "Guatemala",
- "Guernsey",
- "Guinea",
- "Guinea Bissau",
- "Guyana",
- "Haiti",
- "Honduras",
- "Hong Kong",
- "Hungary",
- "Iceland",
- "India",
- "Indonesia",
- "Iran",
- "Iraq",
- "Ireland",
- "Isle of Man",
- "Israel",
- "Italy",
- "Jamaica",
- "Japan",
- "Jersey",
- "Jordan",
- "Kazakhstan",
- "Kenya",
- "Kuwait",
- "Kyrgyz Republic",
- "Laos",
- "Latvia",
- "Lebanon",
- "Lesotho",
- "Liberia",
- "Libya",
- "Liechtenstein",
- "Lithuania",
- "Luxembourg",
- "Macau",
- "Macedonia",
- "Madagascar",
- "Malawi",
- "Malaysia",
- "Maldives",
- "Mali",
- "Malta",
- "Mauritania",
- "Mauritius",
- "Mexico",
- "Moldova",
- "Monaco",
- "Mongolia",
- "Montenegro",
- "Montserrat",
- "Morocco",
- "Mozambique",
- "Namibia",
- "Nepal",
- "Netherlands",
- "Netherlands Antilles",
- "New Caledonia",
- "New Zealand",
- "Nicaragua",
- "Niger",
- "Nigeria",
- "Norway",
- "Oman",
- "Pakistan",
- "Palestine",
- "Panama",
- "Papua New Guinea",
- "Paraguay",
- "Peru",
- "Philippines",
- "Poland",
- "Portugal",
- "Puerto Rico",
- "Qatar",
- "Reunion",
- "Romania",
- "Russia",
- "Rwanda",
- "Saint Pierre and Miquelon",
- "Samoa",
- "San Marino",
- "Satellite",
- "Saudi Arabia",
- "Senegal",
- "Serbia",
- "Seychelles",
- "Sierra Leone",
- "Singapore",
- "Slovakia",
- "Slovenia",
- "South Africa",
- "South Korea",
- "Spain",
- "Sri Lanka",
- "St Kitts and Nevis",
- "St Lucia",
- "St Vincent",
- "St. Lucia",
- "Sudan",
- "Suriname",
- "Swaziland",
- "Sweden",
- "Switzerland",
- "Syria",
- "Taiwan",
- "Tajikistan",
- "Tanzania",
- "Thailand",
+ 'Afghanistan',
+ 'Albania',
+ 'Algeria',
+ 'Andorra',
+ 'Angola',
+ 'Anguilla',
+ 'Antigua and Barbuda',
+ 'Argentina',
+ 'Armenia',
+ 'Aruba',
+ 'Australia',
+ 'Austria',
+ 'Azerbaijan',
+ 'Bahamas',
+ 'Bahrain',
+ 'Bangladesh',
+ 'Barbados',
+ 'Belarus',
+ 'Belgium',
+ 'Belize',
+ 'Benin',
+ 'Bermuda',
+ 'Bhutan',
+ 'Bolivia',
+ 'Bosnia and Herzegovina',
+ 'Botswana',
+ 'Brazil',
+ 'British Virgin Islands',
+ 'Brunei',
+ 'Bulgaria',
+ 'Burkina Faso',
+ 'Burundi',
+ 'Cambodia',
+ 'Cameroon',
+ 'Cape Verde',
+ 'Cayman Islands',
+ 'Chad',
+ 'Chile',
+ 'China',
+ 'Colombia',
+ 'Congo',
+ 'Cook Islands',
+ 'Costa Rica',
+ 'Cote D Ivoire',
+ 'Croatia',
+ 'Cruise Ship',
+ 'Cuba',
+ 'Cyprus',
+ 'Czech Republic',
+ 'Denmark',
+ 'Djibouti',
+ 'Dominica',
+ 'Dominican Republic',
+ 'Ecuador',
+ 'Egypt',
+ 'El Salvador',
+ 'Equatorial Guinea',
+ 'Estonia',
+ 'Ethiopia',
+ 'Falkland Islands',
+ 'Faroe Islands',
+ 'Fiji',
+ 'Finland',
+ 'France',
+ 'French Polynesia',
+ 'French West Indies',
+ 'Gabon',
+ 'Gambia',
+ 'Georgia',
+ 'Germany',
+ 'Ghana',
+ 'Gibraltar',
+ 'Greece',
+ 'Greenland',
+ 'Grenada',
+ 'Guam',
+ 'Guatemala',
+ 'Guernsey',
+ 'Guinea',
+ 'Guinea Bissau',
+ 'Guyana',
+ 'Haiti',
+ 'Honduras',
+ 'Hong Kong',
+ 'Hungary',
+ 'Iceland',
+ 'India',
+ 'Indonesia',
+ 'Iran',
+ 'Iraq',
+ 'Ireland',
+ 'Isle of Man',
+ 'Israel',
+ 'Italy',
+ 'Jamaica',
+ 'Japan',
+ 'Jersey',
+ 'Jordan',
+ 'Kazakhstan',
+ 'Kenya',
+ 'Kuwait',
+ 'Kyrgyz Republic',
+ 'Laos',
+ 'Latvia',
+ 'Lebanon',
+ 'Lesotho',
+ 'Liberia',
+ 'Libya',
+ 'Liechtenstein',
+ 'Lithuania',
+ 'Luxembourg',
+ 'Macau',
+ 'Macedonia',
+ 'Madagascar',
+ 'Malawi',
+ 'Malaysia',
+ 'Maldives',
+ 'Mali',
+ 'Malta',
+ 'Mauritania',
+ 'Mauritius',
+ 'Mexico',
+ 'Moldova',
+ 'Monaco',
+ 'Mongolia',
+ 'Montenegro',
+ 'Montserrat',
+ 'Morocco',
+ 'Mozambique',
+ 'Namibia',
+ 'Nepal',
+ 'Netherlands',
+ 'Netherlands Antilles',
+ 'New Caledonia',
+ 'New Zealand',
+ 'Nicaragua',
+ 'Niger',
+ 'Nigeria',
+ 'Norway',
+ 'Oman',
+ 'Pakistan',
+ 'Palestine',
+ 'Panama',
+ 'Papua New Guinea',
+ 'Paraguay',
+ 'Peru',
+ 'Philippines',
+ 'Poland',
+ 'Portugal',
+ 'Puerto Rico',
+ 'Qatar',
+ 'Reunion',
+ 'Romania',
+ 'Russia',
+ 'Rwanda',
+ 'Saint Pierre and Miquelon',
+ 'Samoa',
+ 'San Marino',
+ 'Satellite',
+ 'Saudi Arabia',
+ 'Senegal',
+ 'Serbia',
+ 'Seychelles',
+ 'Sierra Leone',
+ 'Singapore',
+ 'Slovakia',
+ 'Slovenia',
+ 'South Africa',
+ 'South Korea',
+ 'Spain',
+ 'Sri Lanka',
+ 'St Kitts and Nevis',
+ 'St Lucia',
+ 'St Vincent',
+ 'St. Lucia',
+ 'Sudan',
+ 'Suriname',
+ 'Swaziland',
+ 'Sweden',
+ 'Switzerland',
+ 'Syria',
+ 'Taiwan',
+ 'Tajikistan',
+ 'Tanzania',
+ 'Thailand',
"Timor L'Este",
- "Togo",
- "Tonga",
- "Trinidad and Tobago",
- "Tunisia",
- "Turkey",
- "Turkmenistan",
- "Turks and Caicos",
- "Uganda",
- "Ukraine",
- "United Arab Emirates",
- "United Kingdom",
- "Uruguay",
- "Uzbekistan",
- "Venezuela",
- "Vietnam",
- "Virgin Islands (US)",
- "Yemen",
- "Zambia",
- "Zimbabwe"
+ 'Togo',
+ 'Tonga',
+ 'Trinidad and Tobago',
+ 'Tunisia',
+ 'Turkey',
+ 'Turkmenistan',
+ 'Turks and Caicos',
+ 'Uganda',
+ 'Ukraine',
+ 'United Arab Emirates',
+ 'United Kingdom',
+ 'Uruguay',
+ 'Uzbekistan',
+ 'Venezuela',
+ 'Vietnam',
+ 'Virgin Islands (US)',
+ 'Yemen',
+ 'Zambia',
+ 'Zimbabwe'
];
var actionResults = [
SearchResultItem(
- "Build project",
+ 'Build project',
child: Row(
children: const [
Padding(
padding: EdgeInsets.symmetric(horizontal: 8.0),
child: MacosIcon(CupertinoIcons.hammer),
),
- Text("Build project"),
+ Text('Build project'),
],
),
- onSelected: () => debugPrint("Will build the project"),
+ onSelected: () => debugPrint('Will build the project'),
),
SearchResultItem(
- "Debug project",
+ 'Debug project',
child: Row(
children: const [
Padding(
padding: EdgeInsets.symmetric(horizontal: 8.0),
child: MacosIcon(CupertinoIcons.tickets),
),
- Text("Debug project"),
+ Text('Debug project'),
],
),
- onSelected: () => debugPrint("Will debug the project"),
+ onSelected: () => debugPrint('Will debug the project'),
),
SearchResultItem(
- "Open containing folder",
+ 'Open containing folder',
child: Row(
children: const [
Padding(
padding: EdgeInsets.symmetric(horizontal: 8.0),
child: MacosIcon(CupertinoIcons.folder),
),
- Text("Open containing folder"),
+ Text('Open containing folder'),
],
),
- onSelected: () => debugPrint("Will open containing folder"),
+ onSelected: () => debugPrint('Will open containing folder'),
),
];
diff --git a/example/lib/pages/sliver_toolbar_page.dart b/example/lib/pages/sliver_toolbar_page.dart
new file mode 100644
index 00000000..8c2039a5
--- /dev/null
+++ b/example/lib/pages/sliver_toolbar_page.dart
@@ -0,0 +1,137 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:macos_ui/macos_ui.dart';
+
+class SliverToolbarPage extends StatefulWidget {
+ const SliverToolbarPage({super.key});
+
+ @override
+ State createState() => _SliverToolbarPageState();
+}
+
+class _SliverToolbarPageState extends State {
+ bool pinned = true;
+ bool floating = false;
+ double opacity = .9;
+
+ @override
+ Widget build(BuildContext context) {
+ return MacosScaffold(
+ children: [
+ ContentArea(
+ builder: (context, scrollController) {
+ return CustomScrollView(
+ slivers: [
+ SliverToolBar(
+ title: const Text('SliverToolbar'),
+ floating: floating,
+ pinned: pinned,
+ toolbarOpacity: opacity,
+ actions: [
+ ToolBarIconButton(
+ label: 'Pinned',
+ icon: MacosIcon(
+ pinned ? CupertinoIcons.pin_fill : CupertinoIcons.pin,
+ ),
+ tooltipMessage: pinned ? 'Unpin' : 'Pin',
+ showLabel: false,
+ onPressed: () {
+ setState(() => pinned = !pinned);
+ },
+ ),
+ ToolBarIconButton(
+ label: 'Floating',
+ icon: MacosImageIcon(
+ AssetImage(
+ floating
+ ? 'assets/sf_symbols/menubar.arrow.down.rectangle_2x.png'
+ : 'assets/sf_symbols/menubar.arrow.up.rectangle_2x.png',
+ ),
+ ),
+ tooltipMessage: floating ? 'Unfloat' : 'Float',
+ showLabel: false,
+ onPressed: () {
+ setState(() => floating = !floating);
+ },
+ ),
+ CustomToolbarItem(
+ inToolbarBuilder: (context) {
+ return MacosTooltip(
+ message: 'Toolbar opacity',
+ child: MacosPopupButton(
+ value: opacity,
+ items: const [
+ MacosPopupMenuItem(
+ value: 0.25,
+ child: Text('25%'),
+ ),
+ MacosPopupMenuItem(
+ value: 0.5,
+ child: Text('50%'),
+ ),
+ MacosPopupMenuItem(
+ value: 0.75,
+ child: Text('75%'),
+ ),
+ MacosPopupMenuItem(
+ value: 0.9,
+ child: Text('90% (Default)'),
+ ),
+ ],
+ onChanged: (opacity) {
+ if (opacity == 0.25) {
+ setState(() => this.opacity = 0.25);
+ } else if (opacity == 0.5) {
+ setState(() => this.opacity = 0.5);
+ } else if (opacity == 0.75) {
+ setState(() => this.opacity = 0.75);
+ } else if (opacity == 0.9) {
+ setState(() => this.opacity = 0.9);
+ }
+ },
+ ),
+ );
+ },
+ ),
+ ],
+ ),
+ const SliverPadding(
+ padding: EdgeInsets.all(16),
+ sliver: SliverToBoxAdapter(
+ child: Text(
+ 'SliverToolbar is nearly identical to the standard '
+ 'Toolbar widget, except that it can be used in a '
+ 'CustomScrollView. It can be pinned, floating, or '
+ 'neither.',
+ ),
+ ),
+ ),
+ SliverList(
+ delegate: SliverChildListDelegate(
+ [
+ Row(
+ children: [
+ ...List.generate(
+ 3,
+ (index) => const FlutterLogo(size: 150),
+ )
+ ],
+ ),
+ ...List.generate(
+ 100,
+ (index) => Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16.0),
+ child: Text('Item ${index + 1}'),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
+ );
+ },
+ ),
+ ],
+ );
+ }
+}
diff --git a/example/lib/pages/toolbar_page.dart b/example/lib/pages/toolbar_page.dart
index af01f229..6e1754ce 100644
--- a/example/lib/pages/toolbar_page.dart
+++ b/example/lib/pages/toolbar_page.dart
@@ -1,6 +1,5 @@
+import 'package:flutter/cupertino.dart';
import 'package:macos_ui/macos_ui.dart';
-// ignore: implementation_imports
-import 'package:macos_ui/src/library.dart';
class ToolbarPage extends StatefulWidget {
const ToolbarPage({super.key});
@@ -21,92 +20,92 @@ class _ToolbarPageState extends State {
icon: const MacosIcon(
CupertinoIcons.folder_badge_plus,
),
- onPressed: () => debugPrint("New Folder..."),
- label: "New Folder",
+ onPressed: () => debugPrint('New Folder...'),
+ label: 'New Folder',
showLabel: true,
- tooltipMessage: "This is a beautiful tooltip",
+ tooltipMessage: 'This is a beautiful tooltip',
),
ToolBarIconButton(
icon: const MacosIcon(
CupertinoIcons.add_circled,
),
- onPressed: () => debugPrint("Add..."),
- label: "Add",
+ onPressed: () => debugPrint('Add...'),
+ label: 'Add',
showLabel: true,
- tooltipMessage: "This is another beautiful tooltip",
+ tooltipMessage: 'This is another beautiful tooltip',
),
const ToolBarSpacer(),
ToolBarIconButton(
- label: "Delete",
+ label: 'Delete',
icon: const MacosIcon(
CupertinoIcons.trash,
),
- onPressed: () => debugPrint("pressed"),
+ onPressed: () => debugPrint('pressed'),
showLabel: false,
),
const ToolBarIconButton(
- label: "Change View",
+ label: 'Change View',
icon: MacosIcon(
CupertinoIcons.list_bullet,
),
showLabel: false,
),
ToolBarPullDownButton(
- label: "Actions",
+ label: 'Actions',
icon: CupertinoIcons.ellipsis_circle,
- tooltipMessage: "Perform tasks with the selected items",
+ tooltipMessage: 'Perform tasks with the selected items',
items: [
MacosPulldownMenuItem(
- label: "New Folder",
- title: const Text("New Folder"),
- onTap: () => debugPrint("Creating new folder..."),
+ label: 'New Folder',
+ title: const Text('New Folder'),
+ onTap: () => debugPrint('Creating new folder...'),
),
MacosPulldownMenuItem(
- label: "Open",
- title: const Text("Open"),
- onTap: () => debugPrint("Opening..."),
+ label: 'Open',
+ title: const Text('Open'),
+ onTap: () => debugPrint('Opening...'),
),
MacosPulldownMenuItem(
- label: "Open with...",
+ label: 'Open with...',
title: const Text('Open with...'),
- onTap: () => debugPrint("Opening with..."),
+ onTap: () => debugPrint('Opening with...'),
),
MacosPulldownMenuItem(
- label: "Import from iPhone...",
+ label: 'Import from iPhone...',
title: const Text('Import from iPhone...'),
- onTap: () => debugPrint("Importing..."),
+ onTap: () => debugPrint('Importing...'),
),
const MacosPulldownMenuDivider(),
MacosPulldownMenuItem(
- label: "Remove",
+ label: 'Remove',
enabled: false,
title: const Text('Remove'),
- onTap: () => debugPrint("Deleting..."),
+ onTap: () => debugPrint('Deleting...'),
),
MacosPulldownMenuItem(
- label: "Move to Bin",
+ label: 'Move to Bin',
title: const Text('Move to Bin'),
- onTap: () => debugPrint("Moving to Bin..."),
+ onTap: () => debugPrint('Moving to Bin...'),
),
const MacosPulldownMenuDivider(),
MacosPulldownMenuItem(
- label: "Tags...",
+ label: 'Tags...',
title: const Text('Tags...'),
- onTap: () => debugPrint("Tags..."),
+ onTap: () => debugPrint('Tags...'),
),
],
),
const ToolBarDivider(),
ToolBarIconButton(
- label: "Table",
+ label: 'Table',
icon: const MacosIcon(
CupertinoIcons.square_grid_3x2,
),
- onPressed: () => debugPrint("Table..."),
+ onPressed: () => debugPrint('Table...'),
showLabel: false,
),
ToolBarIconButton(
- label: "Toggle Sidebar",
+ label: 'Toggle Sidebar',
icon: const MacosIcon(
CupertinoIcons.sidebar_left,
),
@@ -114,43 +113,43 @@ class _ToolbarPageState extends State {
showLabel: false,
),
ToolBarPullDownButton(
- label: "Group",
+ label: 'Group',
icon: CupertinoIcons.rectangle_grid_3x2,
items: [
MacosPulldownMenuItem(
- label: "None",
+ label: 'None',
title: const Text('None'),
- onTap: () => debugPrint("Remove sorting"),
+ onTap: () => debugPrint('Remove sorting'),
),
const MacosPulldownMenuDivider(),
MacosPulldownMenuItem(
- label: "Name",
+ label: 'Name',
title: const Text('Name'),
- onTap: () => debugPrint("Sorting by name"),
+ onTap: () => debugPrint('Sorting by name'),
),
MacosPulldownMenuItem(
- label: "Kind",
+ label: 'Kind',
title: const Text('Kind'),
- onTap: () => debugPrint("Sorting by kind"),
+ onTap: () => debugPrint('Sorting by kind'),
),
MacosPulldownMenuItem(
- label: "Size",
+ label: 'Size',
title: const Text('Size'),
- onTap: () => debugPrint("Sorting by size"),
+ onTap: () => debugPrint('Sorting by size'),
),
MacosPulldownMenuItem(
- label: "Date Added",
+ label: 'Date Added',
title: const Text('Date Added'),
- onTap: () => debugPrint("Sorting by date"),
+ onTap: () => debugPrint('Sorting by date'),
),
],
),
ToolBarIconButton(
- label: "Share",
+ label: 'Share',
icon: const MacosIcon(
CupertinoIcons.share,
),
- onPressed: () => debugPrint("pressed"),
+ onPressed: () => debugPrint('pressed'),
showLabel: false,
),
],
@@ -159,19 +158,16 @@ class _ToolbarPageState extends State {
ContentArea(
builder: (context, scrollController) {
return SingleChildScrollView(
+ controller: scrollController,
padding: const EdgeInsets.all(30),
child: Center(
child: Column(
children: const [
Text(
- "The toolbar appears below the title bar of the macOS app or integrates with it.",
+ 'A toolbar provides convenient access to frequently used commands and controls that perform actions relevant to the current view.',
textAlign: TextAlign.center,
),
SizedBox(height: 20.0),
- Text(
- "It provides convenient access to frequently used commands and features.",
- textAlign: TextAlign.center,
- ),
],
),
),
diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock
index 90ae1621..67acca3c 100644
--- a/example/macos/Podfile.lock
+++ b/example/macos/Podfile.lock
@@ -15,7 +15,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
- macos_ui: 125c911559d646194386d84c017ad6819122e2db
+ macos_ui: 6229a8922cd97bafb7d9636c8eb8dfb0744183ca
PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7
diff --git a/example/pubspec.lock b/example/pubspec.lock
index abfba059..8e15ffe7 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -97,7 +97,7 @@ packages:
path: ".."
relative: true
source: path
- version: "1.11.1"
+ version: "1.12.0"
matcher:
dependency: transitive
description:
diff --git a/lib/macos_ui.dart b/lib/macos_ui.dart
index 4bd80c30..6a71ceec 100644
--- a/lib/macos_ui.dart
+++ b/lib/macos_ui.dart
@@ -53,6 +53,7 @@ export 'src/layout/tab_view/tab_controller.dart';
export 'src/layout/tab_view/tab_view.dart';
export 'src/layout/title_bar.dart';
export 'src/layout/toolbar/custom_toolbar_item.dart';
+export 'src/layout/toolbar/sliver_toolbar.dart';
export 'src/layout/toolbar/toolbar.dart';
export 'src/layout/toolbar/toolbar_divider.dart';
export 'src/layout/toolbar/toolbar_overflow_menu.dart';
diff --git a/lib/src/layout/toolbar/custom_toolbar_item.dart b/lib/src/layout/toolbar/custom_toolbar_item.dart
index 257866ca..bae382fe 100644
--- a/lib/src/layout/toolbar/custom_toolbar_item.dart
+++ b/lib/src/layout/toolbar/custom_toolbar_item.dart
@@ -10,17 +10,26 @@ class CustomToolbarItem extends ToolbarItem {
/// ```dart
/// // Add a grey vertical line as a custom toolbar item:
/// CustomToolbarItem(
- /// inToolbarBuilder: (context) => Padding(
+ /// inToolbarBuilder: (context) => Padding(
/// padding: const EdgeInsets.all(8.0),
- /// child: Container(color: Colors.grey, width: 1, height: 30),
+ /// child: Container(
+ /// color: Colors.grey,
+ /// width: 1,
+ /// height: 30,
+ /// ),
+ /// ),
+ /// inOverflowedBuilder: (context) => Container(
+ /// color: Colors.grey,
+ /// width: 30,
+ /// height: 1,
/// ),
- /// inOverflowedBuilder: (context) =>
- /// Container(color: Colors.grey, width: 30, height: 1),
/// ),
/// // Add a search field as a custom toolbar item:
/// CustomToolbarItem(
- /// inToolbarBuilder: (context) =>
- /// const SizedBox(width: 200, child: MacosSearchField()),
+ /// inToolbarBuilder: (context) => const SizedBox(
+ /// width: 200,
+ /// child: MacosSearchField(),
+ /// ),
/// ),
/// ```
///
diff --git a/lib/src/layout/toolbar/sliver_toolbar.dart b/lib/src/layout/toolbar/sliver_toolbar.dart
new file mode 100644
index 00000000..8ec1b087
--- /dev/null
+++ b/lib/src/layout/toolbar/sliver_toolbar.dart
@@ -0,0 +1,317 @@
+import 'dart:math' as math;
+
+import 'package:flutter/foundation.dart';
+import 'package:macos_ui/macos_ui.dart';
+import 'package:macos_ui/src/library.dart';
+
+/// Defines the height of a regular-sized [SliverToolBar]
+const _kToolbarHeight = 52.0;
+
+/// Defines the width of the [SliverToolBar]'s title.
+const _kTitleWidth = 150.0;
+
+/// {@template sliverToolBar}
+/// A variant of [ToolBar] that is compatible with slivers.
+///
+/// It is nearly identical to [ToolBar], with the exception that this widget
+/// must only be used [ScrollView]s that allow slivers, such as
+/// [CustomScrollView] and [NestedScrollView]. It contains three additional
+/// properties that are relevant to its usage in such [ScrollView]s: [pinned],
+/// [floating], and [toolbarOpacity].
+///
+/// See also:
+/// * [SliverAppBar] (package:material)
+/// {@endtemplate}
+class SliverToolBar extends StatefulWidget with Diagnosticable {
+ /// {@macro sliverToolBar}
+ const SliverToolBar({
+ super.key,
+ this.height = _kToolbarHeight,
+ this.alignment = Alignment.center,
+ this.title,
+ this.titleWidth = _kTitleWidth,
+ this.padding = const EdgeInsets.symmetric(horizontal: 8, vertical: 4.0),
+ this.decoration,
+ this.leading,
+ this.automaticallyImplyLeading = true,
+ this.actions,
+ this.centerTitle = false,
+ this.dividerColor,
+ this.pinned = true,
+ this.floating = false,
+ this.toolbarOpacity = 0.9,
+ });
+
+ /// Specifies the height of this [ToolBar].
+ ///
+ /// Defaults to [_kToolbarHeight] which is 52.0.
+ final double height;
+
+ /// Aligns the [title] within the [ToolBar].
+ ///
+ /// Defaults to [Alignment.center].
+ ///
+ /// The [ToolBar] will expand to fill its parent and position its
+ /// child within itself according to the given value.
+ ///
+ /// See also:
+ ///
+ /// * [Alignment], a class with convenient constants typically used to
+ /// specify an [AlignmentGeometry].
+ /// * [AlignmentDirectional], like [Alignment] for specifying alignments
+ /// relative to text direction.
+ final Alignment alignment;
+
+ /// The [title] of the toolbar.
+ ///
+ /// Typically, a [Text] widget.
+ final Widget? title;
+
+ /// Specifies the width of the title of the [ToolBar].
+ ///
+ /// Defaults to [_kTitleWidth] which is 150.0.
+ final double titleWidth;
+
+ /// The decoration to paint behind the [title].
+ final BoxDecoration? decoration;
+
+ /// Empty space to inscribe inside the toolbar. The [title], if any, is
+ /// placed inside this padding.
+ ///
+ /// Defaults to ` EdgeInsets.symmetric(horizontal: 8, vertical: 4.0)`.
+ final EdgeInsets padding;
+
+ /// A widget to display before the toolbar's [title].
+ ///
+ /// Typically the [leading] widget is a [MacosIcon] or a [MacosIconButton].
+ final Widget? leading;
+
+ /// Controls whether we should try to imply the leading widget if null.
+ ///
+ /// If `true` and [leading] is null, automatically try to deduce what the leading
+ /// widget should be. If `false` and [leading] is null, leading space is given to [title].
+ /// If leading widget is not null, this parameter has no effect.
+ final bool automaticallyImplyLeading;
+
+ /// A list of [ToolbarItem] widgets to display in a row after the [title] widget,
+ /// as the toolbar actions.
+ ///
+ /// Toolbar items include [ToolBarIconButton], [ToolBarPulldownButton],
+ /// [ToolBarSpacer], and [CustomToolbarItem] widgets.
+ ///
+ /// If the toolbar actions exceed the available toolbar width (e.g. when the
+ /// window is resized), the overflowed actions are displayed via a
+ /// [ToolbarOverflowMenu], that can be opened from the [ToolbarOverflowButton]
+ /// at the right edge of the toolbar.
+ final List? actions;
+
+ /// Whether the title should be centered.
+ final bool centerTitle;
+
+ /// The color of the divider below the toolbar.
+ ///
+ /// Defaults to MacosTheme.of(context).dividerColor.
+ ///
+ /// Set it to MacosColors.transparent to remove.
+ final Color? dividerColor;
+
+ /// Whether the toolbar should remain visible at the start of the scroll view.
+ ///
+ /// Defaults to `true`.
+ final bool pinned;
+
+ /// Whether the toolbar should become visible as soon as the user scrolls
+ /// upwards.
+ ///
+ /// Otherwise, the user will need to scroll near the top of the scroll view
+ /// to reveal the toolbar.
+ ///
+ /// Defaults to `false`.
+ final bool floating;
+
+ /// The opacity of the toolbar when content is scrolled underneath it.
+ ///
+ /// Adjust this value to tweak the blur effect the toolbar creates. Note that
+ /// the blur is only applied when content is being scrolled underneath the
+ /// toolbar.
+ ///
+ /// Defaults to `0.9`.
+ final double toolbarOpacity;
+
+ @override
+ void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+ super.debugFillProperties(properties);
+ properties.add(DoubleProperty('height', height));
+ properties.add(DiagnosticsProperty('alignment', alignment));
+ properties.add(DiagnosticsProperty('title', title));
+ properties.add(DoubleProperty('titleWidth', titleWidth));
+ properties
+ .add(DiagnosticsProperty('decoration', decoration));
+ properties.add(DiagnosticsProperty('padding', padding));
+ properties.add(DiagnosticsProperty('leading', leading));
+ properties.add(FlagProperty(
+ 'automaticallyImplyLeading',
+ value: automaticallyImplyLeading,
+ ifTrue: 'automatically imply leading',
+ ));
+ properties.add(DiagnosticsProperty>('actions', actions));
+ properties.add(
+ FlagProperty('centerTitle', value: centerTitle, ifTrue: 'center title'),
+ );
+ properties.add(DiagnosticsProperty('dividerColor', dividerColor));
+ properties.add(FlagProperty('pinned', value: pinned, ifTrue: 'pinned'));
+ properties
+ .add(FlagProperty('floating', value: floating, ifTrue: 'floating'));
+ }
+
+ @override
+ State createState() => _SliverToolBarState();
+}
+
+class _SliverToolBarState extends State
+ with TickerProviderStateMixin {
+ int overflowedActionsCount = 0;
+
+ @override
+ void didUpdateWidget(SliverToolBar oldWidget) {
+ super.didUpdateWidget(oldWidget);
+ if (widget.actions != null &&
+ widget.actions!.length != oldWidget.actions!.length) {
+ overflowedActionsCount = 0;
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return MediaQuery.removePadding(
+ context: context,
+ removeBottom: true,
+ child: SliverPersistentHeader(
+ floating: widget.floating,
+ pinned: widget.pinned,
+ delegate: _SliverToolBarDelegate(
+ height: widget.height,
+ alignment: widget.alignment,
+ title: widget.title,
+ titleWidth: widget.titleWidth,
+ decoration: widget.decoration,
+ padding: widget.padding,
+ leading: widget.leading,
+ automaticallyImplyLeading: widget.automaticallyImplyLeading,
+ actions: widget.actions,
+ centerTitle: widget.centerTitle,
+ dividerColor: widget.dividerColor,
+ floating: widget.floating,
+ pinned: widget.pinned,
+ toolbarOpacity: widget.toolbarOpacity,
+ vsync: this,
+ ),
+ ),
+ );
+ }
+}
+
+class _SliverToolBarDelegate extends SliverPersistentHeaderDelegate {
+ _SliverToolBarDelegate({
+ required this.height,
+ required this.alignment,
+ required this.title,
+ required this.titleWidth,
+ required this.decoration,
+ required this.padding,
+ required this.leading,
+ required this.automaticallyImplyLeading,
+ required this.actions,
+ required this.centerTitle,
+ required this.dividerColor,
+ required this.vsync,
+ required this.floating,
+ required this.pinned,
+ required this.toolbarOpacity,
+ });
+
+ final double height;
+ final Alignment alignment;
+ final Widget? title;
+ final double titleWidth;
+ final BoxDecoration? decoration;
+ final EdgeInsets padding;
+ final Widget? leading;
+ final bool automaticallyImplyLeading;
+ final List? actions;
+ final bool centerTitle;
+ final Color? dividerColor;
+ final bool floating;
+ final bool pinned;
+ final double toolbarOpacity;
+
+ @override
+ double get minExtent => _kToolbarHeight;
+
+ @override
+ double get maxExtent => _kToolbarHeight;
+
+ @override
+ final TickerProvider vsync;
+
+ @override
+ Widget build(
+ BuildContext context,
+ double shrinkOffset,
+ bool overlapsContent,
+ ) {
+ final bool isScrolledUnder = overlapsContent ||
+ (pinned || floating && shrinkOffset > maxExtent - minExtent);
+ final double opacity =
+ pinned || floating && isScrolledUnder ? toolbarOpacity : 1.0;
+
+ BoxDecoration? effectiveDecoration;
+ if (isScrolledUnder) {
+ effectiveDecoration = decoration?.copyWith(
+ color: decoration?.color?.withOpacity(opacity),
+ ) ??
+ BoxDecoration(
+ color: MacosTheme.of(context).canvasColor.withOpacity(opacity),
+ );
+ }
+
+ final Widget toolBar = FlexibleSpaceBar.createSettings(
+ minExtent: minExtent,
+ maxExtent: maxExtent,
+ currentExtent: math.max(minExtent, maxExtent - shrinkOffset),
+ toolbarOpacity: opacity,
+ isScrolledUnder: isScrolledUnder,
+ child: ToolBar(
+ automaticallyImplyLeading: automaticallyImplyLeading,
+ leading: leading,
+ title: title,
+ titleWidth: titleWidth,
+ decoration: effectiveDecoration,
+ padding: padding,
+ actions: actions,
+ centerTitle: centerTitle,
+ dividerColor: dividerColor,
+ alignment: alignment,
+ height: height,
+ ),
+ );
+ return toolBar;
+ }
+
+ @override
+ bool shouldRebuild(covariant _SliverToolBarDelegate oldDelegate) {
+ return leading != oldDelegate.leading ||
+ automaticallyImplyLeading != oldDelegate.automaticallyImplyLeading ||
+ alignment != oldDelegate.alignment ||
+ title != oldDelegate.title ||
+ titleWidth != oldDelegate.titleWidth ||
+ decoration != oldDelegate.decoration ||
+ padding != oldDelegate.padding ||
+ leading != oldDelegate.leading ||
+ actions != oldDelegate.actions ||
+ centerTitle != oldDelegate.centerTitle ||
+ dividerColor != oldDelegate.dividerColor ||
+ floating != oldDelegate.floating ||
+ pinned != oldDelegate.pinned;
+ }
+}
diff --git a/lib/src/layout/toolbar/toolbar.dart b/lib/src/layout/toolbar/toolbar.dart
index 294a24a6..a9685f6a 100644
--- a/lib/src/layout/toolbar/toolbar.dart
+++ b/lib/src/layout/toolbar/toolbar.dart
@@ -15,7 +15,7 @@ const _kLeadingWidth = 20.0;
const _kTitleWidth = 150.0;
/// A toolbar to use in a [MacosScaffold].
-class ToolBar extends StatefulWidget {
+class ToolBar extends StatefulWidget with Diagnosticable {
/// Creates a toolbar in the [MacosScaffold]. The toolbar appears below the
/// title bar (if present) of the macOS app or integrates with it.
///
@@ -120,6 +120,31 @@ class ToolBar extends StatefulWidget {
/// Set this to `MacosColors.transparent` to remove.
final Color? dividerColor;
+ @override
+ void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+ super.debugFillProperties(properties);
+ properties.add(DoubleProperty('height', height));
+ properties.add(DiagnosticsProperty('alignment', alignment));
+ properties.add(DiagnosticsProperty('title', title));
+ properties.add(DoubleProperty('titleWidth', titleWidth));
+ properties
+ .add(DiagnosticsProperty('decoration', decoration));
+ properties.add(DiagnosticsProperty('padding', padding));
+ properties.add(DiagnosticsProperty('leading', leading));
+ properties.add(FlagProperty(
+ 'automaticallyImplyLeading',
+ value: automaticallyImplyLeading,
+ ifTrue: 'automatically imply leading',
+ ));
+ properties.add(DiagnosticsProperty>('actions', actions));
+ properties.add(FlagProperty(
+ 'centerTitle',
+ value: centerTitle,
+ ifTrue: 'center title',
+ ));
+ properties.add(DiagnosticsProperty('dividerColor', dividerColor));
+ }
+
@override
State createState() => _ToolBarState();
}
diff --git a/lib/src/library.dart b/lib/src/library.dart
index 9d05ec7e..37fb1228 100644
--- a/lib/src/library.dart
+++ b/lib/src/library.dart
@@ -28,6 +28,7 @@ export 'package:flutter/material.dart'
DateUtils,
TimeOfDay,
DayPeriod,
+ FlexibleSpaceBar,
MaterialState;
export 'package:flutter/widgets.dart';
diff --git a/macos/macos_ui.podspec b/macos/macos_ui.podspec
index 4f45d04e..947c74a3 100644
--- a/macos/macos_ui.podspec
+++ b/macos/macos_ui.podspec
@@ -9,7 +9,7 @@ Pod::Spec.new do |s|
s.description = <<-DESC
A new flutter plugin project.
DESC
- s.homepage = 'https://github.com/GroovinChip/macos_ui'
+ s.homepage = 'https://github.com/macosui/macos_ui'
s.license = { :file => '../LICENSE' }
s.author = { 'GroovinChip' => 'groovinchip@gmail.com' }
s.source = { :path => '.' }
diff --git a/pubspec.lock b/pubspec.lock
index 75773850..6cfa9f14 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -420,10 +420,10 @@ packages:
dependency: transitive
description:
name: source_maps
- sha256: "490098075234dcedb83c5d949b4c93dad5e6b7702748de000be2b57b8e6b2427"
+ sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703"
url: "https://pub.dev"
source: hosted
- version: "0.10.11"
+ version: "0.10.12"
source_span:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index d1760775..9604bd3f 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
name: macos_ui
description: Flutter widgets and themes implementing the current macOS design language.
-version: 1.11.1
+version: 1.12.0
homepage: "https://macosui.dev"
repository: "https://github.com/GroovinChip/macos_ui"
@@ -23,5 +23,5 @@ flutter:
plugin:
platforms:
macos:
- package: dev.groovinchip.macos_ui
+ package: dev.macosui.macos_ui
pluginClass: MacOSUiPlugin
diff --git a/test/layout/sliver_toolbar_test.dart b/test/layout/sliver_toolbar_test.dart
new file mode 100644
index 00000000..bcfeb9d4
--- /dev/null
+++ b/test/layout/sliver_toolbar_test.dart
@@ -0,0 +1,182 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/gestures.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:macos_ui/macos_ui.dart';
+
+void main() {
+ Future pumpScrollableWithSliverToolbar(
+ WidgetTester tester,
+ ScrollController controller, {
+ bool pinned = true,
+ bool floating = false,
+ double opacity = 0.9,
+ }) async {
+ await tester.pumpWidget(
+ MacosApp(
+ home: MacosScaffold(
+ children: [
+ ContentArea(
+ builder: (context, _) {
+ return CustomScrollView(
+ controller: controller,
+ slivers: [
+ SliverToolBar(
+ title: const Text('Title'),
+ pinned: pinned,
+ floating: floating,
+ toolbarOpacity: 0.9,
+ ),
+ const SliverToBoxAdapter(
+ child: SizedBox(height: 1200),
+ ),
+ ],
+ );
+ },
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ testWidgets(
+ 'MediaQuery bottom padding is removed as expected',
+ (tester) async {
+ final Key leadingKey = GlobalKey();
+ final Key titleKey = GlobalKey();
+ await tester.pumpWidget(
+ MacosApp(
+ home: MediaQuery(
+ data: const MediaQueryData(
+ padding: EdgeInsets.only(
+ bottom: 30.0,
+ ),
+ ),
+ child: MacosScaffold(
+ children: [
+ ContentArea(
+ builder: (context, scrollController) {
+ return CustomScrollView(
+ controller: scrollController,
+ slivers: [
+ SliverToolBar(
+ leading: Placeholder(key: leadingKey),
+ title: Text('Title', key: titleKey),
+ ),
+ const SliverToBoxAdapter(
+ child: SizedBox(height: 1200),
+ ),
+ ],
+ );
+ },
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+
+ // location of bottomLeft is unchanged by applying padding
+ expect(
+ tester.getBottomLeft(find.byKey(leadingKey)),
+ const Offset(8.0, 47.0),
+ );
+ },
+ );
+
+ testWidgets(
+ 'Y-Offsets and sizes are correct',
+ (tester) async {
+ final ScrollController controller = ScrollController();
+ await pumpScrollableWithSliverToolbar(tester, controller);
+
+ final toolbar = find.byType(ToolBar);
+ final navToolbar = find.byType(NavigationToolbar);
+
+ expect(tester.getTopLeft(toolbar).dy, 0.0);
+ expect(tester.getTopLeft(navToolbar).dy, 4.0);
+ expect(tester.getSize(toolbar).height, 52.0);
+ expect(tester.getSize(navToolbar).height, 43.0);
+ },
+ );
+
+ testWidgets(
+ 'Scrolling down while pinned=true keeps the toolbar in view',
+ (tester) async {
+ final ScrollController controller = ScrollController();
+ await pumpScrollableWithSliverToolbar(tester, controller);
+
+ final toolbar = find.byType(ToolBar);
+ final navToolbar = find.byType(NavigationToolbar);
+
+ expect(controller.offset, 0.0);
+
+ controller.jumpTo(600.0);
+ await tester.pump();
+
+ expect(tester.getTopLeft(toolbar).dy, 0.0);
+ expect(tester.getTopLeft(navToolbar).dy, 4.0);
+ },
+ );
+
+ testWidgets(
+ 'Scrolling down while pinned=false does not keep the toolbar in view',
+ (tester) async {
+ final ScrollController controller = ScrollController();
+ await pumpScrollableWithSliverToolbar(tester, controller, pinned: false);
+
+ final toolbar = find.byType(ToolBar);
+ final navToolbar = find.byType(NavigationToolbar);
+
+ expect(controller.offset, 0.0);
+ expect(tester.getTopLeft(toolbar).dy, 0.0);
+ expect(tester.getTopLeft(navToolbar).dy, 4.0);
+
+ controller.jumpTo(600.0);
+ await tester.pump();
+
+ expect(toolbar, findsNothing);
+ expect(navToolbar, findsNothing);
+ },
+ );
+
+ testWidgets(
+ 'Scrolling down and back up while pinned=false and floating=true brings the toolbar back into view',
+ (tester) async {
+ final ScrollController controller = ScrollController();
+ await pumpScrollableWithSliverToolbar(
+ tester,
+ controller,
+ pinned: false,
+ floating: true,
+ );
+
+ final toolbar = find.byType(ToolBar);
+ final navToolbar = find.byType(NavigationToolbar);
+
+ expect(controller.offset, 0.0);
+ expect(tester.getTopLeft(toolbar).dy, 0.0);
+ expect(tester.getTopLeft(navToolbar).dy, 4.0);
+
+ controller.jumpTo(600.0);
+ await tester.pump();
+
+ expect(toolbar, findsNothing);
+ expect(navToolbar, findsNothing);
+
+ final Offset scrollEventLocation =
+ tester.getCenter(find.byType(CustomScrollView));
+ final TestPointer testPointer = TestPointer(1, PointerDeviceKind.mouse);
+ testPointer.hover(scrollEventLocation);
+ await tester
+ .sendEventToBinding(testPointer.scroll(const Offset(0.0, -52.0)));
+ await tester.pumpAndSettle();
+
+ expect(controller.offset, 548.0);
+ expect(toolbar, findsOneWidget);
+ expect(tester.getTopLeft(toolbar).dy, 0.0);
+ expect(navToolbar, findsOneWidget);
+ expect(tester.getTopLeft(navToolbar).dy, 4.0);
+ },
+ );
+}