diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index af69bfd4..bb71af66 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -7,9 +7,7 @@ ## Pre-launch Checklist -- [ ] I have run `dartfmt` on all changed files - [ ] I have incremented the package version as appropriate and updated `CHANGELOG.md` with my changes - [ ] I have added/updated relevant documentation - [ ] I have run "optimize/organize imports" on all changed files -- [ ] I have addressed all analyzer warnings as best I could - \ No newline at end of file +- [ ] I have addressed all analyzer warnings as best I could \ No newline at end of file diff --git a/.github/workflows/flutter_analysis.yml b/.github/workflows/flutter_analysis.yml index 29e896de..0187a10d 100644 --- a/.github/workflows/flutter_analysis.yml +++ b/.github/workflows/flutter_analysis.yml @@ -10,13 +10,27 @@ jobs: - name: Install Flutter uses: subosito/flutter-action@v2 with: - channel: stable + channel: master - name: Install dependencies run: flutter pub get + - uses: actions/checkout@v3 + - name: Set up git + run: | + git config user.name "GitHub Actions Bot" + git config user.email "<>" + - name: Format code - run: dart format --set-exit-if-changed . + run: | + dart format . + if [ $? -eq 1 ]; then + git add . + git commit -m "chore: formatting corrections" + git push + echo "Code has been formatted and changes have been committed and pushed." + fi + echo "All code is properly formatted!" - name: Analyze code run: flutter analyze --fatal-infos . diff --git a/.github/workflows/pana_analysis.yml b/.github/workflows/pana_analysis.yml index a32d6cb3..9f90d17e 100644 --- a/.github/workflows/pana_analysis.yml +++ b/.github/workflows/pana_analysis.yml @@ -4,6 +4,7 @@ on: [pull_request, workflow_dispatch] jobs: package-analysis: runs-on: ubuntu-latest + if: github.base_ref != 'customer_testing' steps: - uses: actions/checkout@v3 diff --git a/.gitignore b/.gitignore index 70efb322..fcfa202c 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,7 @@ *.iws .idea/ -.vscode/launch.json +.vscode # The .vscode folder contains launch configuration and tasks you configure in # VS Code which you may wish to be included in version control, so this line diff --git a/CHANGELOG.md b/CHANGELOG.md index 994154c1..15c38001 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## [1.11.1] +* Fixed an issue where the `MacosSearchField` would not perform an action when an item was selected. + +## [1.11.0] +* 🚨 Breaking Changes 🚨 +* `ResizablePane` can now be vertically resized + * `ResizablePane.startWidth` has been changed to `ResizablePane.startSize` + * `ResizablePane.minWidth` has been changed to `ResizablePane.minSize` + * `ResizablePane.maxWidth` has been changed to `ResizablePane.maxSize` + ## [1.10.0] 🚨 Breaking Changes 🚨 * `MacosScrollbar` has been completely overhauled and now resembles the native macOS scrollbar in appearance and diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7b6b77d0..bed82a0e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,16 +18,12 @@ This repository uses [conventional commits](https://www.conventionalcommits.org/ As mentioned above, all pull requests should target `dev`. #### Pre-launch script -Before opening your pull request, run the `pr_prelaunch_tasks.sh` script to ensure that your changes meet the -following requirements: +Before opening your pull request, please ensure that the following +following requirements are met: * All code is properly formatted * There are no Dart analysis warnings * All tests pass -If the format step of the script results in changes, the script will make those change, commit them, and prompt you to push the commit. - -If the `dart fix` step results in changes, the script will make those changes, commit them, and prompt you to push the commit. - Pull requests should **always** be merged via GitHub and not via command-line. ### Versioning diff --git a/README.md b/README.md index 663d9d65..1a1a1451 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,10 @@ Guides, codelabs, and other documentation can be found at https://macosui.dev -## 🚨 Usage notes +## 🚨 Usage notes +### Flutter channel +`macos_ui` is developed against Flutter's `stable` channel. To ensure a smooth development experience with `macos_ui`, you should build your application on Flutter's `stable` channel. + ### Platform Compatibility pub.dev shows that `macos_ui` only supports macOS. This is because `macos_ui` calls some native code, and therefore @@ -50,6 +53,7 @@ should avoid allowing your application window to be resized below the height of - [Layout](#layout) - [MacosWindow](#macoswindow) + - [Sidebar](#sidebar) - [MacosScaffold](#macosscaffold) - [Modern Window Look](#modern-window-look) - [ToolBar](#toolbar) @@ -148,6 +152,56 @@ your `MacosScaffold` in a `Builder` widget in order for this to work properly. +## Sidebar +A sidebar enables app navigation and provides quick access to top-level collections of content in your app. + +Sidebars may be placed at the left or right of your app. To place a sidebar on the left, use the `MacosWindow.sidebar` property. To place a sidebar on the right, use the `MacosWindow.endSidebar` property. + + + +Example usage: + +```dart +int pageIndex = 0; + +... + +MacosWindow( + sidebar: Sidebar( + minWidth: 200, + builder: (context, scrollController) { + return SidebarItems( + currentIndex: pageIndex, + scrollController: scrollController, + itemSize: SidebarItemSize.large, + onChanged: (i) { + setState(() => pageIndex = i); + }, + items: const [ + SidebarItem( + label: Text('Page One'), + ), + SidebarItem( + label: Text('Page Two'), + ), + ], + ); + }, + ), + endSidebar: Sidebar( + startWidth: 200, + minWidth: 200, + maxWidth: 300, + shownByDefault: false, + builder: (context, _) { + return const Center( + child: Text('End Sidebar'), + ); + }, + ), +), +``` + ## MacosScaffold The `MacosScaffold` is what you might call a "page". @@ -355,7 +409,7 @@ CustomToolbarItem( ## MacosListTile -A widget that aims to approximate the [ListTile] widget found in +A widget that aims to approximate the [`ListTile`](https://api.flutter.dev/flutter/material/ListTile-class.html) widget found in Flutter's material library. ![MacosListTile](https://imgur.com/pQB99M2.png) @@ -862,10 +916,10 @@ You can set `discrete` to `true` to make it a discrete capacity indicator. A slider is a control that lets people select a value from a continuous or discrete range of values by moving the slider thumb. - Continuous | Discrete | -|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| ![Continuous Slider Example](https://i.imgur.com/dc4YjoX.png) | ![Discrete Slider Example](https://i.imgur.com/KckOTUf.png) | -| A horizontal slider where any value continuous value between a min and max can be selected | A horizontal slider where only discrete values between a min and max can be selected. Tick marks are often displayed to provide context. | + | Continuous | Discrete | + | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- | + | ![Continuous Slider Example](https://i.imgur.com/dc4YjoX.png) | ![Discrete Slider Example](https://i.imgur.com/KckOTUf.png) | + | A horizontal slider where any value continuous value between a min and max can be selected | A horizontal slider where only discrete values between a min and max can be selected. Tick marks are often displayed to provide context. | Here's an example of how to create an interactive continuous slider: diff --git a/example/.gitignore b/example/.gitignore index 8b52fde8..4e8c3b2b 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -47,3 +47,4 @@ app.*.map.json /android/app/release /windows/ +linux/ diff --git a/example/lib/pages/buttons_page.dart b/example/lib/pages/buttons_page.dart index dafc5ea2..6533484b 100644 --- a/example/lib/pages/buttons_page.dart +++ b/example/lib/pages/buttons_page.dart @@ -60,8 +60,8 @@ class _ButtonsPageState extends State { ), children: [ ResizablePane( - minWidth: 180, - startWidth: 200, + minSize: 180, + startSize: 200, windowBreakpoint: 700, resizableSide: ResizableSide.right, builder: (_, __) { @@ -72,366 +72,394 @@ class _ButtonsPageState extends State { ), ContentArea( builder: (context, scrollController) { - return SingleChildScrollView( - controller: scrollController, - padding: const EdgeInsets.all(20), - child: Column( - children: [ - const Text('MacosBackButton'), - const SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - MacosBackButton( - onPressed: () => debugPrint('click'), - fillColor: Colors.transparent, - ), - const SizedBox(width: 16.0), - MacosBackButton( - onPressed: () => debugPrint('click'), - ), - ], - ), - const SizedBox(height: 20), - const Text('MacosDisclosureButton'), - const SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - MacosDisclosureButton( - isPressed: isDisclosureButtonPressed, - onPressed: () { - debugPrint('click'); - setState(() { - isDisclosureButtonPressed = - !isDisclosureButtonPressed; - }); - }), - ], - ), - const SizedBox(height: 20), - const Text('MacosIconButton'), - const SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - MacosIconButton( - icon: const MacosIcon( - CupertinoIcons.star_fill, + return Column( + children: [ + Flexible( + fit: FlexFit.loose, + child: SingleChildScrollView( + controller: scrollController, + padding: const EdgeInsets.all(20), + child: Column( + children: [ + const Text('MacosBackButton'), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + MacosBackButton( + onPressed: () => debugPrint('click'), + fillColor: Colors.transparent, + ), + const SizedBox(width: 16.0), + MacosBackButton( + onPressed: () => debugPrint('click'), + ), + ], ), - shape: BoxShape.rectangle, - borderRadius: BorderRadius.circular(7), - onPressed: () {}, - ), - const SizedBox(width: 8), - const MacosIconButton( - icon: MacosIcon( - CupertinoIcons.plus_app, + const SizedBox(height: 20), + const Text('MacosDisclosureButton'), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + MacosDisclosureButton( + isPressed: isDisclosureButtonPressed, + onPressed: () { + debugPrint('click'); + setState(() { + isDisclosureButtonPressed = + !isDisclosureButtonPressed; + }); + }), + ], ), - shape: BoxShape.circle, - //onPressed: () {}, - ), - const SizedBox(width: 8), - MacosIconButton( - icon: const MacosIcon( - CupertinoIcons.minus_square, + const SizedBox(height: 20), + const Text('MacosIconButton'), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + MacosIconButton( + icon: const MacosIcon( + CupertinoIcons.star_fill, + ), + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(7), + onPressed: () {}, + ), + const SizedBox(width: 8), + const MacosIconButton( + icon: MacosIcon( + CupertinoIcons.plus_app, + ), + shape: BoxShape.circle, + //onPressed: () {}, + ), + const SizedBox(width: 8), + MacosIconButton( + icon: const MacosIcon( + CupertinoIcons.minus_square, + ), + backgroundColor: Colors.transparent, + onPressed: () {}, + ), + ], ), - backgroundColor: Colors.transparent, - onPressed: () {}, - ), - ], - ), - const SizedBox(height: 20), - const Text('PushButton'), - const SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - PushButton( - buttonSize: ButtonSize.large, - child: const Text('Large'), - onPressed: () { - MacosWindowScope.of(context).toggleSidebar(); - }, - ), - const SizedBox(width: 20), - PushButton( - buttonSize: ButtonSize.small, - child: const Text('Small'), - onPressed: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (_) { - return MacosScaffold( - toolBar: const ToolBar( - title: Text('New page'), - ), - children: [ - ContentArea( - builder: (context, _) { - return Center( - child: PushButton( - buttonSize: ButtonSize.large, - child: const Text('Go Back'), - onPressed: () { - Navigator.of(context).maybePop(); + const SizedBox(height: 20), + const Text('PushButton'), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + PushButton( + buttonSize: ButtonSize.large, + child: const Text('Large'), + onPressed: () { + MacosWindowScope.of(context).toggleSidebar(); + }, + ), + const SizedBox(width: 20), + PushButton( + buttonSize: ButtonSize.small, + child: const Text('Small'), + onPressed: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) { + return MacosScaffold( + toolBar: const ToolBar( + title: Text('New page'), + ), + children: [ + ContentArea( + builder: (context, _) { + return Center( + child: PushButton( + buttonSize: ButtonSize.large, + child: const Text('Go Back'), + onPressed: () { + Navigator.of(context) + .maybePop(); + }, + ), + ); }, ), - ); - }, - ), - ResizablePane( - minWidth: 180, - startWidth: 200, - windowBreakpoint: 700, - resizableSide: ResizableSide.left, - builder: (_, __) { - return const Center( - child: Text('Resizable Pane'), - ); - }, - ), - ], + ResizablePane( + minSize: 180, + startSize: 200, + windowBreakpoint: 700, + resizableSide: ResizableSide.left, + builder: (_, __) { + return const Center( + child: Text('Resizable Pane'), + ); + }, + ), + ], + ); + }, + ), ); }, ), - ); - }, - ), - const SizedBox(width: 20), - PushButton( - buttonSize: ButtonSize.large, - isSecondary: true, - child: const Text('Secondary'), - onPressed: () { - MacosWindowScope.of(context).toggleSidebar(); - }, - ), - ], - ), - const SizedBox(height: 20), - const Text('MacosSwitch'), - const SizedBox(height: 8), - MacosSwitch( - value: switchValue, - onChanged: (value) { - setState(() => switchValue = value); - }, - ), - const SizedBox(height: 20), - const Text('MacosPulldownButton'), - const SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - MacosPulldownButton( - title: "PDF", - items: [ - MacosPulldownMenuItem( - title: const Text('Open in Preview'), - onTap: () => debugPrint("Opening in preview..."), - ), - MacosPulldownMenuItem( - title: const Text('Save as PDF...'), - onTap: () => debugPrint("Saving as PDF..."), - ), - MacosPulldownMenuItem( - enabled: false, - title: const Text('Save as Postscript'), - onTap: () => debugPrint("Saving as Postscript..."), - ), - const MacosPulldownMenuDivider(), - MacosPulldownMenuItem( - enabled: false, - title: const Text('Save to iCloud Drive'), - onTap: () => debugPrint("Saving to iCloud..."), - ), - MacosPulldownMenuItem( - enabled: false, - title: const Text('Save to Web Receipts'), - onTap: () => - debugPrint("Saving to Web Receipts..."), - ), - MacosPulldownMenuItem( - title: const Text('Send in Mail...'), - onTap: () => debugPrint("Sending via Mail..."), - ), - const MacosPulldownMenuDivider(), - MacosPulldownMenuItem( - title: const Text('Edit Menu...'), - onTap: () => debugPrint("Editing menu..."), - ), - ], - ), - const SizedBox(width: 20), - const MacosPulldownButton( - title: "PDF", - disabledTitle: "Disabled", - items: [], - ), - ], - ), - const SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - MacosPulldownButton( - icon: CupertinoIcons.ellipsis_circle, - items: [ - MacosPulldownMenuItem( - title: const Text('New Folder'), - onTap: () => debugPrint("Creating new folder..."), - ), - MacosPulldownMenuItem( - title: const Text('Open'), - onTap: () => debugPrint("Opening..."), - ), - MacosPulldownMenuItem( - title: const Text('Open with...'), - onTap: () => debugPrint("Opening with..."), - ), - MacosPulldownMenuItem( - title: const Text('Import from iPhone...'), - onTap: () => debugPrint("Importing..."), - ), - const MacosPulldownMenuDivider(), - MacosPulldownMenuItem( - enabled: false, - title: const Text('Remove'), - onTap: () => debugPrint("Deleting..."), - ), - MacosPulldownMenuItem( - title: const Text('Move to Bin'), - onTap: () => debugPrint("Moving to Bin..."), - ), - const MacosPulldownMenuDivider(), - MacosPulldownMenuItem( - title: const Text('Tags...'), - onTap: () => debugPrint("Tags..."), - ), - ], - ), - const SizedBox(width: 20), - const MacosPulldownButton( - icon: CupertinoIcons.square_grid_3x2, - items: [], - ), - ], - ), - const SizedBox(height: 20), - const Text('MacosPopupButton'), - const SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - MacosPopupButton( - value: popupValue, - onChanged: (String? newValue) { - setState(() => popupValue = newValue!); - }, - items: ['One', 'Two', 'Three', 'Four'] - .map>((String value) { - return MacosPopupMenuItem( - value: value, - child: Text(value), - ); - }).toList(), - ), - const SizedBox(width: 20), - MacosPopupButton( - disabledHint: const Text("Disabled"), - onChanged: null, - items: null, - ), - ], - ), - const SizedBox(height: 20), - MacosPopupButton( - value: languagePopupValue, - onChanged: (String? newValue) { - setState(() => languagePopupValue = newValue!); - }, - items: languages - .map>((String value) { - return MacosPopupMenuItem( - value: value, - child: Text(value), - ); - }).toList(), - ), - const SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('System Theme'), - const SizedBox(width: 8), - MacosRadioButton( - groupValue: context.watch().mode, - value: ThemeMode.system, - onChanged: (value) { - context.read().mode = value!; - }, - ), - ], - ), - const SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('Light Theme'), - const SizedBox(width: 24), - MacosRadioButton( - groupValue: context.watch().mode, - value: ThemeMode.light, - onChanged: (value) { - context.read().mode = value!; - }, - ), - ], - ), - const SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('Dark Theme'), - const SizedBox(width: 26), - MacosRadioButton( - groupValue: context.watch().mode, - value: ThemeMode.dark, - onChanged: (value) { - context.read().mode = value!; - }, - ), - ], - ), - const SizedBox(height: 20), - const Text('MacosSegmentedControl'), - const SizedBox(height: 8), - MacosSegmentedControl( - controller: _tabController, - tabs: [ - MacosTab( - label: 'Tab 1', - active: _tabController.index == 0, - ), - MacosTab( - label: 'Tab 2', - active: _tabController.index == 1, - ), - MacosTab( - label: 'Tab 3', - active: _tabController.index == 2, - ), - ], + const SizedBox(width: 20), + PushButton( + buttonSize: ButtonSize.large, + isSecondary: true, + child: const Text('Secondary'), + onPressed: () { + MacosWindowScope.of(context).toggleSidebar(); + }, + ), + ], + ), + const SizedBox(height: 20), + const Text('MacosSwitch'), + const SizedBox(height: 8), + MacosSwitch( + value: switchValue, + onChanged: (value) { + setState(() => switchValue = value); + }, + ), + const SizedBox(height: 20), + const Text('MacosPulldownButton'), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + MacosPulldownButton( + title: "PDF", + items: [ + MacosPulldownMenuItem( + title: const Text('Open in Preview'), + onTap: () => + debugPrint("Opening in preview..."), + ), + MacosPulldownMenuItem( + title: const Text('Save as PDF...'), + onTap: () => debugPrint("Saving as PDF..."), + ), + MacosPulldownMenuItem( + enabled: false, + title: const Text('Save as Postscript'), + onTap: () => + debugPrint("Saving as Postscript..."), + ), + const MacosPulldownMenuDivider(), + MacosPulldownMenuItem( + enabled: false, + title: const Text('Save to iCloud Drive'), + onTap: () => + debugPrint("Saving to iCloud..."), + ), + MacosPulldownMenuItem( + enabled: false, + title: const Text('Save to Web Receipts'), + onTap: () => + debugPrint("Saving to Web Receipts..."), + ), + MacosPulldownMenuItem( + title: const Text('Send in Mail...'), + onTap: () => + debugPrint("Sending via Mail..."), + ), + const MacosPulldownMenuDivider(), + MacosPulldownMenuItem( + title: const Text('Edit Menu...'), + onTap: () => debugPrint("Editing menu..."), + ), + ], + ), + const SizedBox(width: 20), + const MacosPulldownButton( + title: "PDF", + disabledTitle: "Disabled", + items: [], + ), + ], + ), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + MacosPulldownButton( + icon: CupertinoIcons.ellipsis_circle, + items: [ + MacosPulldownMenuItem( + title: const Text('New Folder'), + onTap: () => + debugPrint("Creating new folder..."), + ), + MacosPulldownMenuItem( + title: const Text('Open'), + onTap: () => debugPrint("Opening..."), + ), + MacosPulldownMenuItem( + title: const Text('Open with...'), + onTap: () => debugPrint("Opening with..."), + ), + MacosPulldownMenuItem( + title: const Text('Import from iPhone...'), + onTap: () => debugPrint("Importing..."), + ), + const MacosPulldownMenuDivider(), + MacosPulldownMenuItem( + enabled: false, + title: const Text('Remove'), + onTap: () => debugPrint("Deleting..."), + ), + MacosPulldownMenuItem( + title: const Text('Move to Bin'), + onTap: () => debugPrint("Moving to Bin..."), + ), + const MacosPulldownMenuDivider(), + MacosPulldownMenuItem( + title: const Text('Tags...'), + onTap: () => debugPrint("Tags..."), + ), + ], + ), + const SizedBox(width: 20), + const MacosPulldownButton( + icon: CupertinoIcons.square_grid_3x2, + items: [], + ), + ], + ), + const SizedBox(height: 20), + const Text('MacosPopupButton'), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + MacosPopupButton( + value: popupValue, + onChanged: (String? newValue) { + setState(() => popupValue = newValue!); + }, + items: [ + 'One', + 'Two', + 'Three', + 'Four' + ].map>((String value) { + return MacosPopupMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + ), + const SizedBox(width: 20), + MacosPopupButton( + disabledHint: const Text("Disabled"), + onChanged: null, + items: null, + ), + ], + ), + const SizedBox(height: 20), + MacosPopupButton( + value: languagePopupValue, + onChanged: (String? newValue) { + setState(() => languagePopupValue = newValue!); + }, + items: languages + .map>((String value) { + return MacosPopupMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + ), + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('System Theme'), + const SizedBox(width: 8), + MacosRadioButton( + groupValue: context.watch().mode, + value: ThemeMode.system, + onChanged: (value) { + context.read().mode = value!; + }, + ), + ], + ), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('Light Theme'), + const SizedBox(width: 24), + MacosRadioButton( + groupValue: context.watch().mode, + value: ThemeMode.light, + onChanged: (value) { + context.read().mode = value!; + }, + ), + ], + ), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('Dark Theme'), + const SizedBox(width: 26), + MacosRadioButton( + groupValue: context.watch().mode, + value: ThemeMode.dark, + onChanged: (value) { + context.read().mode = value!; + }, + ), + ], + ), + const SizedBox(height: 20), + const Text('MacosSegmentedControl'), + const SizedBox(height: 8), + MacosSegmentedControl( + controller: _tabController, + tabs: [ + MacosTab( + label: 'Tab 1', + active: _tabController.index == 0, + ), + MacosTab( + label: 'Tab 2', + active: _tabController.index == 1, + ), + MacosTab( + label: 'Tab 3', + active: _tabController.index == 2, + ), + ], + ), + ], + ), ), - ], - ), + ), + ResizablePane( + minSize: 50, + startSize: 200, + //windowBreakpoint: 600, + builder: (_, __) { + return const Center( + child: Text('Resizable Pane'), + ); + }, + resizableSide: ResizableSide.top, + ) + ], ); }, ), ResizablePane( - minWidth: 180, - startWidth: 200, + minSize: 180, + startSize: 200, windowBreakpoint: 800, resizableSide: ResizableSide.left, builder: (_, __) { diff --git a/example/lib/pages/fields_page.dart b/example/lib/pages/fields_page.dart index c80ad815..c63e9f09 100644 --- a/example/lib/pages/fields_page.dart +++ b/example/lib/pages/fields_page.dart @@ -126,8 +126,8 @@ class _FieldsPageState extends State { }, ), ResizablePane( - minWidth: 180, - startWidth: 200, + minSize: 180, + startSize: 200, windowBreakpoint: 800, resizableSide: ResizableSide.left, builder: (_, __) { diff --git a/example/lib/pages/indicators_page.dart b/example/lib/pages/indicators_page.dart index 5daeaaf9..1dd4caf5 100644 --- a/example/lib/pages/indicators_page.dart +++ b/example/lib/pages/indicators_page.dart @@ -40,7 +40,7 @@ class _IndicatorsPageState extends State { children: [ CapacityIndicator( value: capacitorValue, - onChanged: (v) => setState(() => sliderValue = v), + onChanged: (v) => setState(() => capacitorValue = v), splits: 20, discrete: true, ), diff --git a/example/lib/pages/selectors_page.dart b/example/lib/pages/selectors_page.dart index e44a1936..8256a33e 100644 --- a/example/lib/pages/selectors_page.dart +++ b/example/lib/pages/selectors_page.dart @@ -30,6 +30,7 @@ class _SelectorsPageState extends State { ContentArea( builder: (context, scrollController) { return SingleChildScrollView( + controller: scrollController, padding: const EdgeInsets.all(20), child: Column( children: [ diff --git a/example/lib/pages/tabview_page.dart b/example/lib/pages/tabview_page.dart index 786b11b0..64d4d8ff 100644 --- a/example/lib/pages/tabview_page.dart +++ b/example/lib/pages/tabview_page.dart @@ -22,7 +22,7 @@ class _TabViewPageState extends State { ), children: [ ContentArea( - builder: (context, scrollController) { + builder: (context, _) { return Padding( padding: const EdgeInsets.all(24.0), child: MacosTabView( diff --git a/example/lib/pages/toolbar_page.dart b/example/lib/pages/toolbar_page.dart index af01f229..7492869b 100644 --- a/example/lib/pages/toolbar_page.dart +++ b/example/lib/pages/toolbar_page.dart @@ -158,11 +158,11 @@ class _ToolbarPageState extends State { children: [ ContentArea( builder: (context, scrollController) { - return SingleChildScrollView( - padding: const EdgeInsets.all(30), + return const SingleChildScrollView( + padding: EdgeInsets.all(30), child: Center( child: Column( - children: const [ + children: [ Text( "The toolbar appears below the title bar of the macOS app or integrates with it.", textAlign: TextAlign.center, diff --git a/example/pubspec.lock b/example/pubspec.lock index f6155c18..9d37f274 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -97,7 +97,7 @@ packages: path: ".." relative: true source: path - version: "1.10.0" + version: "1.11.1" matcher: dependency: transitive description: diff --git a/lib/src/fields/search_field.dart b/lib/src/fields/search_field.dart index d7eddb63..2064ef9a 100644 --- a/lib/src/fields/search_field.dart +++ b/lib/src/fields/search_field.dart @@ -326,39 +326,39 @@ class _MacosSearchFieldState extends State> { } height += _kResultsOverlayMargin; - return MacosOverlayFilter( - borderRadius: _kBorderRadius, - color: MacosSearchFieldTheme.of(context).resultsBackgroundColor, - child: SizedBox( - height: height, - child: ListView.builder( - reverse: showOverlayAbove, - padding: const EdgeInsets.all(6.0), - itemCount: snapshot.data!.length, - itemBuilder: (context, index) { - var selectedItem = snapshot.data![index]!; - return _SearchResultItemButton( - resultHeight: widget.resultHeight, - onPressed: () { - searchController!.text = selectedItem.searchKey; - searchController!.selection = TextSelection.fromPosition( - TextPosition( - offset: searchController!.text.length, - ), - ); - selectedItem.onSelected?.call(); - // Hide the results - suggestionStream.sink.add(null); - if (widget.onResultSelected != null) { - widget.onResultSelected!(selectedItem); - } - }, - child: selectedItem.child ?? - Text( - selectedItem.searchKey, - ), - ); - }, + return TextFieldTapRegion( + child: MacosOverlayFilter( + borderRadius: _kBorderRadius, + color: MacosSearchFieldTheme.of(context).resultsBackgroundColor, + child: SizedBox( + height: height, + child: ListView.builder( + reverse: showOverlayAbove, + padding: const EdgeInsets.all(6.0), + itemCount: snapshot.data!.length, + itemBuilder: (context, index) { + var selectedItem = snapshot.data![index]!; + return _SearchResultItemButton( + resultHeight: widget.resultHeight, + onPressed: () { + searchController!.text = selectedItem.searchKey; + searchController!.selection = + TextSelection.fromPosition( + TextPosition( + offset: searchController!.text.length, + ), + ); + selectedItem.onSelected?.call(); + // Hide the results + suggestionStream.sink.add(null); + if (widget.onResultSelected != null) { + widget.onResultSelected!(selectedItem); + } + }, + child: selectedItem.child ?? Text(selectedItem.searchKey), + ); + }, + ), ), ), ); diff --git a/lib/src/layout/resizable_pane.dart b/lib/src/layout/resizable_pane.dart index c71ff8c2..68ccfbed 100644 --- a/lib/src/layout/resizable_pane.dart +++ b/lib/src/layout/resizable_pane.dart @@ -15,16 +15,19 @@ enum ResizableSide { /// The right side of the [ResizablePane]. right, + + /// The top side of the [ResizablePane]. + top, } /// {@template resizablePane} -/// A widget that can be resized horizontally. +/// A widget that can be resized horizontally or vertically. /// -/// The [builder], [minWidth] and [resizableSide] can not be null. -/// The [maxWidth] and the [windowBreakpoint] default to `500.00`. +/// The [builder], [minSize] and [resizableSide] can not be null. +/// The [maxSize] and the [windowBreakpoint] default to `500.00`. /// [isResizable] defaults to `true`. /// -/// The [startWidth] is the initial width. +/// The [startSize] is the initial width or height depending on the orientation of the pane. /// {@endtemplate} class ResizablePane extends StatefulWidget { /// {@macro resizablePane} @@ -32,19 +35,19 @@ class ResizablePane extends StatefulWidget { super.key, required this.builder, this.decoration, - this.maxWidth = 500.0, - required this.minWidth, + this.maxSize = 500.0, + required this.minSize, this.isResizable = true, required this.resizableSide, this.windowBreakpoint, - required this.startWidth, + required this.startSize, }) : assert( - maxWidth >= minWidth, - 'minWidth should not be more than maxWidth.', + maxSize >= minSize, + 'minSize should not be more than maxSize.', ), assert( - (startWidth >= minWidth) && (startWidth <= maxWidth), - 'startWidth must not be less than minWidth or more than maxWidth', + (startSize >= minSize) && (startSize <= maxSize), + 'startSize must not be less than minSize or more than maxWidth', ); /// The builder that creates a child to display in this widget, which will @@ -61,19 +64,34 @@ class ResizablePane extends StatefulWidget { /// resizable side of this widget. final bool isResizable; - /// Specifies the maximum width that this [ResizablePane] can have. + /// Specifies the maximum width or height that this [ResizablePane] can have + /// according to its orientation. + /// + /// The orientation is horizontal if the [resizableSide] is + /// [ResizableSide.left] or [ResizableSide.right] and vertical if the + /// [resizableSide] is [ResizableSide.top]). /// - /// The value can be null and defaults to `500.0`. - final double maxWidth; + /// If this value is null, it defaults to `500.0`. + final double maxSize; - /// Specifies the minimum width that this [ResizablePane] can have. - final double minWidth; + /// Specifies the minimum width of height that this [ResizablePane] can have + /// according to its orientation. + /// + /// The orientation is horizontal if the [resizableSide] is + /// [ResizableSide.left] or [ResizableSide.right] and vertical if the + /// [resizableSide] is [ResizableSide.top]. + final double minSize; - /// Specifies the width that this [ResizablePane] first starts width. + /// Specifies the width or height that this [ResizablePane] first starts with + /// according to its orientation. + /// + /// The orientation is horizontal if the [resizableSide] is + /// [ResizableSide.left] or [ResizableSide.right] and vertical if the + /// [resizableSide] is [ResizableSide.top]). /// - /// The [startWidth] should not be more than the [maxWidth] or - /// less than the [minWidth]. - final double startWidth; + /// The [startSize] should not be more than the [maxSize] or + /// less than the [minSize]. + final double startSize; /// Indicates the draggable side of the [ResizablePane] for resizing final ResizableSide resizableSide; @@ -86,21 +104,26 @@ class ResizablePane extends StatefulWidget { } class _ResizablePaneState extends State { - SystemMouseCursor _cursor = SystemMouseCursors.resizeColumn; + late SystemMouseCursor _cursor; final _scrollController = ScrollController(); - late double _width; - late double _dragStartWidth; + late double _size; + late double _dragStartSize; late double _dragStartPosition; Color get _dividerColor => MacosTheme.of(context).dividerColor; bool get _resizeOnRight => widget.resizableSide == ResizableSide.right; + bool get _resizeOnTop => widget.resizableSide == ResizableSide.top; + BoxDecoration get _decoration { final borderSide = BorderSide(color: _dividerColor); final right = Border(right: borderSide); final left = Border(left: borderSide); - return BoxDecoration(border: _resizeOnRight ? right : left).copyWith( + final top = Border(top: borderSide); + return BoxDecoration( + border: _resizeOnTop ? top : (_resizeOnRight ? right : left), + ).copyWith( color: widget.decoration?.color, border: widget.decoration?.border, borderRadius: widget.decoration?.borderRadius, @@ -112,51 +135,99 @@ class _ResizablePaneState extends State { ); } + BoxConstraints get _boxConstraint { + if (_resizeOnTop) { + return BoxConstraints( + maxHeight: widget.maxSize, + minHeight: widget.minSize, + ).normalize(); + } + return BoxConstraints( + maxWidth: widget.maxSize, + minWidth: widget.minSize, + ).normalize(); + } + Widget get _resizeArea { - return GestureDetector( - behavior: HitTestBehavior.opaque, - child: MouseRegion( - cursor: _cursor, - child: const SizedBox(width: 5), - ), - onHorizontalDragStart: (details) { - _dragStartWidth = _width; - _dragStartPosition = details.globalPosition.dx; - }, - onHorizontalDragUpdate: (details) { - setState(() { - final newWidth = _resizeOnRight - ? _dragStartWidth - - (_dragStartPosition - details.globalPosition.dx) - : _dragStartWidth + - (_dragStartPosition - details.globalPosition.dx); - _width = math.max( - widget.minWidth, - math.min( - widget.maxWidth, - newWidth, + return _resizeOnTop + ? GestureDetector( + behavior: HitTestBehavior.opaque, + child: MouseRegion( + cursor: _cursor, + child: const SizedBox(width: 5), ), + onVerticalDragStart: (details) { + _dragStartSize = _size; + _dragStartPosition = details.globalPosition.dy; + }, + onVerticalDragUpdate: (details) { + setState(() { + final newHeight = _dragStartSize + + (_dragStartPosition - details.globalPosition.dy); + _size = math.max( + widget.minSize, + math.min( + widget.maxSize, + newHeight, + ), + ); + if (_size == widget.minSize) { + _cursor = SystemMouseCursors.resizeUp; + } else if (_size == widget.maxSize) { + _cursor = SystemMouseCursors.resizeDown; + } else { + _cursor = SystemMouseCursors.resizeRow; + } + }); + }, + ) + : GestureDetector( + behavior: HitTestBehavior.opaque, + child: MouseRegion( + cursor: _cursor, + child: const SizedBox(width: 5), + ), + onHorizontalDragStart: (details) { + _dragStartSize = _size; + _dragStartPosition = details.globalPosition.dx; + }, + onHorizontalDragUpdate: (details) { + setState(() { + final newWidth = _resizeOnRight + ? _dragStartSize - + (_dragStartPosition - details.globalPosition.dx) + : _dragStartSize + + (_dragStartPosition - details.globalPosition.dx); + _size = math.max( + widget.minSize, + math.min( + widget.maxSize, + newWidth, + ), + ); + if (_size == widget.minSize) { + _cursor = _resizeOnRight + ? SystemMouseCursors.resizeRight + : SystemMouseCursors.resizeLeft; + } else if (_size == widget.maxSize) { + _cursor = _resizeOnRight + ? SystemMouseCursors.resizeLeft + : SystemMouseCursors.resizeRight; + } else { + _cursor = SystemMouseCursors.resizeColumn; + } + }); + }, ); - if (_width == widget.minWidth) { - _cursor = _resizeOnRight - ? SystemMouseCursors.resizeRight - : SystemMouseCursors.resizeLeft; - } else if (_width == widget.maxWidth) { - _cursor = _resizeOnRight - ? SystemMouseCursors.resizeLeft - : SystemMouseCursors.resizeRight; - } else { - _cursor = SystemMouseCursors.resizeColumn; - } - }); - }, - ); } @override void initState() { super.initState(); - _width = widget.startWidth; + _cursor = _resizeOnTop + ? SystemMouseCursors.resizeRow + : SystemMouseCursors.resizeColumn; + _size = widget.startSize; _scrollController.addListener(() => setState(() {})); } @@ -164,12 +235,12 @@ class _ResizablePaneState extends State { void didUpdateWidget(covariant ResizablePane oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.windowBreakpoint != widget.windowBreakpoint || - oldWidget.minWidth != widget.minWidth || - oldWidget.maxWidth != widget.maxWidth || + oldWidget.minSize != widget.minSize || + oldWidget.maxSize != widget.maxSize || oldWidget.resizableSide != widget.resizableSide) { setState(() { - if (widget.minWidth > _width) _width = widget.minWidth; - if (widget.maxWidth < _width) _width = widget.maxWidth; + if (widget.minSize > _size) _size = widget.minSize; + if (widget.maxSize < _size) _size = widget.maxSize; }); } } @@ -186,19 +257,23 @@ class _ResizablePaneState extends State { final maxHeight = media.size.height; final maxWidth = media.size.width; - if (widget.windowBreakpoint != null && - maxWidth <= widget.windowBreakpoint!) { - return const SizedBox.shrink(); + if (_resizeOnTop) { + if (widget.windowBreakpoint != null && + maxHeight <= widget.windowBreakpoint!) { + return const SizedBox.shrink(); + } + } else { + if (widget.windowBreakpoint != null && + maxWidth <= widget.windowBreakpoint!) { + return const SizedBox.shrink(); + } } return Container( - width: _width, - height: maxHeight, + width: _resizeOnTop ? maxWidth : _size, + height: _resizeOnTop ? _size : maxHeight, decoration: _decoration, - constraints: BoxConstraints( - maxWidth: widget.maxWidth, - minWidth: widget.minWidth, - ).normalize(), + constraints: _boxConstraint, child: Stack( children: [ SafeArea( @@ -209,7 +284,7 @@ class _ResizablePaneState extends State { child: widget.builder(context, _scrollController), ), ), - if (widget.isResizable && !_resizeOnRight) + if (widget.isResizable && !_resizeOnRight && !_resizeOnTop) Positioned( left: 0, width: 5, @@ -223,6 +298,13 @@ class _ResizablePaneState extends State { height: maxHeight, child: _resizeArea, ), + if (widget.isResizable && _resizeOnTop) + Positioned( + top: 0, + width: maxWidth, + height: 5, + child: _resizeArea, + ), ], ), ); diff --git a/lib/src/layout/toolbar/toolbar.dart b/lib/src/layout/toolbar/toolbar.dart index 01a3c12d..294a24a6 100644 --- a/lib/src/layout/toolbar/toolbar.dart +++ b/lib/src/layout/toolbar/toolbar.dart @@ -89,15 +89,17 @@ class ToolBar extends StatefulWidget { /// 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. + /// Controls whether the toolbar should try to imply if the [leading] widget + /// is 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. + /// If `true` and [leading] are null, the toolbar will automatically try to + /// deduce what the leading widget should be. If `false` and [leading] is + /// null, leading space is given to [title]. If the [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. + /// 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. @@ -108,14 +110,14 @@ class ToolBar extends StatefulWidget { /// at the right edge of the toolbar. final List? actions; - /// Whether the title should be centered. + /// Whether the [title] should be centered. final bool centerTitle; /// The color of the divider below the toolbar. /// - /// Defaults to MacosTheme.of(context).dividerColor. + /// Defaults to `MacosTheme.of(context).dividerColor`. /// - /// Set it to MacosColors.transparent to remove. + /// Set this to `MacosColors.transparent` to remove. final Color? dividerColor; @override diff --git a/pr_prelaunch_tasks.sh b/pr_prelaunch_tasks.sh deleted file mode 100644 index b7d2ff83..00000000 --- a/pr_prelaunch_tasks.sh +++ /dev/null @@ -1,39 +0,0 @@ -dart format --set-exit-if-changed . -if [ $? -eq 1 ]; then - dart format lib - git add . - git commit -m "chore: run flutter format ." - echo "push changes? [y/n]" - read -r pushResponse - if [ "$pushResponse" = "y" ]; then - git push origin - fi -fi -echo "Run dart fix --dry-run? [y/n]" -read -r dryRunResponse -if [ "$dryRunResponse" = "y" ]; then - dart fix --dry-run -fi -echo "Run dart fix --apply? [y/n]" -read -r applyResponse -if [ "$applyResponse" = "y" ]; then - dart fix --apply - if [ -z "$(git status --porcelain)" ]; then - echo "No changes to commit" - else - git add . - git commit -m "chore: run dart fix --apply" - echo "push changes? [y/n]" - read -r pushResponse - if [ "$pushResponse" = "y" ]; then - git push origin - fi - fi -fi -echo "Run tests? [y/n]" -read -r testResponse -if [ "$testResponse" = "y" ]; then - flutter test -else - exit 0 -fi \ No newline at end of file diff --git a/publish_tasks.sh b/publish_tasks.sh deleted file mode 100644 index a6aa33e1..00000000 --- a/publish_tasks.sh +++ /dev/null @@ -1,16 +0,0 @@ -# MAINTAINER ONLY SCRIPT. DO NOT RUN THIS SCRIPT UNLESS YOU ARE THE MAINTAINER. -pana --no-warning -echo "Are you ready to dry-run publish macos_ui? [y/n]" -read -r dryRunResponse -if [ "$dryRunResponse" = "y" ]; then - flutter pub publish --dry-run -else - exit 0 -fi -echo "Are you ready to publish macos_ui to pub.dev? [y/n]" -read -r publishResponse -if [ "$publishResponse" = "y" ]; then - flutter pub publish -else - exit 0 -fi \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 7b6b263b..d1760775 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.10.0 +version: 1.11.1 homepage: "https://macosui.dev" repository: "https://github.com/GroovinChip/macos_ui" diff --git a/test/buttons/checkbox_test.dart b/test/buttons/checkbox_test.dart index 9ba9c1a5..09881007 100644 --- a/test/buttons/checkbox_test.dart +++ b/test/buttons/checkbox_test.dart @@ -12,7 +12,7 @@ void main() { child: MacosScaffold( children: [ ContentArea( - builder: (context, scrollController) { + builder: (context, _) { return StatefulBuilder( builder: (context, setState) { return MacosCheckbox( diff --git a/test/buttons/help_button_test.dart b/test/buttons/help_button_test.dart index 6124e5c0..2f11f430 100644 --- a/test/buttons/help_button_test.dart +++ b/test/buttons/help_button_test.dart @@ -24,7 +24,7 @@ void main() { child: MacosScaffold( children: [ ContentArea( - builder: (context, scrollController) { + builder: (context, _) { return HelpButton( onPressed: mockOnPressedFunction.handler, ); diff --git a/test/buttons/icon_button_test.dart b/test/buttons/icon_button_test.dart index a4c1e543..057f6a3b 100644 --- a/test/buttons/icon_button_test.dart +++ b/test/buttons/icon_button_test.dart @@ -19,7 +19,7 @@ void main() { child: MacosScaffold( children: [ ContentArea( - builder: (context, scrollController) { + builder: (context, _) { return MacosIconButton( icon: const Icon(CupertinoIcons.add), onPressed: mockOnPressedFunction.handler, diff --git a/test/buttons/popup_button_test.dart b/test/buttons/popup_button_test.dart index d685d3da..704501ef 100644 --- a/test/buttons/popup_button_test.dart +++ b/test/buttons/popup_button_test.dart @@ -17,7 +17,7 @@ void main() { child: MacosScaffold( children: [ ContentArea( - builder: (context, scrollController) { + builder: (context, _) { return StatefulBuilder( builder: (context, setState) { return MacosPopupButton( diff --git a/test/buttons/pulldown_button_test.dart b/test/buttons/pulldown_button_test.dart index aa42cc83..e22a355e 100644 --- a/test/buttons/pulldown_button_test.dart +++ b/test/buttons/pulldown_button_test.dart @@ -22,7 +22,7 @@ void main() { child: MacosScaffold( children: [ ContentArea( - builder: (context, scrollController) { + builder: (context, _) { return Center( child: MacosPulldownButton( title: "test", diff --git a/test/buttons/push_button_test.dart b/test/buttons/push_button_test.dart index 99fe95ae..96dc571e 100644 --- a/test/buttons/push_button_test.dart +++ b/test/buttons/push_button_test.dart @@ -25,7 +25,7 @@ void main() { child: MacosScaffold( children: [ ContentArea( - builder: (context, scrollController) { + builder: (context, _) { return PushButton( buttonSize: ButtonSize.small, onPressed: mockOnPressedFunction.handler, diff --git a/test/buttons/radio_button_test.dart b/test/buttons/radio_button_test.dart index cb51f3b3..d561b643 100644 --- a/test/buttons/radio_button_test.dart +++ b/test/buttons/radio_button_test.dart @@ -20,7 +20,7 @@ void main() { child: MacosScaffold( children: [ ContentArea( - builder: (context, scrollController) { + builder: (context, _) { return Center( child: MacosRadioButton( value: TestOptions.first, diff --git a/test/buttons/segmented_control_test.dart b/test/buttons/segmented_control_test.dart index 1ccf4a74..62d19796 100644 --- a/test/buttons/segmented_control_test.dart +++ b/test/buttons/segmented_control_test.dart @@ -14,7 +14,7 @@ void main() { child: MacosScaffold( children: [ ContentArea( - builder: (context, scrollController) { + builder: (context, _) { return Center( child: MacosSegmentedControl( controller: controller, diff --git a/test/buttons/switch_test.dart b/test/buttons/switch_test.dart index 75c3e863..642f0f11 100644 --- a/test/buttons/switch_test.dart +++ b/test/buttons/switch_test.dart @@ -12,7 +12,7 @@ void main() { child: MacosScaffold( children: [ ContentArea( - builder: (context, scrollController) { + builder: (context, _) { return Center( child: MacosSwitch( value: selected, diff --git a/test/fields/search_field_test.dart b/test/fields/search_field_test.dart index a6f1a750..4fdbc863 100644 --- a/test/fields/search_field_test.dart +++ b/test/fields/search_field_test.dart @@ -29,10 +29,11 @@ void main() { child: MacosScaffold( children: [ ContentArea( - builder: (context, scrollController) { + builder: (context, _) { return Center( child: SizedBox( width: 300.0, + height: 500.0, child: MacosSearchField( results: kOptions.map((e) => SearchResultItem(e)).toList(), @@ -64,6 +65,15 @@ void main() { ListView list = find.byType(ListView).evaluate().first.widget as ListView; // 'chameleon' and 'elephant' are displayed. expect(list.semanticChildCount, 2); + + await tester.ensureVisible(find.text('elephant')); + await tester.pump(); + + await tester.tap(find.text('elephant')); + await tester.pump(); + + expect(controller.text, 'elephant'); + expect(find.byType(ListView), findsNothing); }, ); } diff --git a/test/fields/text_field_test.dart b/test/fields/text_field_test.dart index d83ce27b..71b12568 100644 --- a/test/fields/text_field_test.dart +++ b/test/fields/text_field_test.dart @@ -12,7 +12,7 @@ void main() { child: MacosScaffold( children: [ ContentArea( - builder: (context, scrollController) { + builder: (context, _) { return Center( child: MacosTextField( controller: controller, diff --git a/test/layout/macos_list_tile_test.dart b/test/layout/macos_list_tile_test.dart index 6f86b952..e019a007 100644 --- a/test/layout/macos_list_tile_test.dart +++ b/test/layout/macos_list_tile_test.dart @@ -23,7 +23,7 @@ void main() { child: MacosScaffold( children: [ ContentArea( - builder: (context, scrollController) { + builder: (context, _) { return MacosListTile( title: const Text('List Tile'), onClick: mockOnPressedFunction.handler, diff --git a/test/layout/resizeable_pane_test.dart b/test/layout/resizeable_pane_test.dart index bae5ac4d..86638a6a 100644 --- a/test/layout/resizeable_pane_test.dart +++ b/test/layout/resizeable_pane_test.dart @@ -7,150 +7,226 @@ void main() { group('ResizablePane', () { for (var side in matrix) { - group(side == ResizableSide.left ? "left" : "right", () { - const double maxWidth = 300; - const double minWidth = 100; - const double startWidth = 200; - - final resizablePane = ResizablePane( - builder: (context, scrollController) => const Text('Hello there'), - minWidth: minWidth, - startWidth: startWidth, - maxWidth: maxWidth, - resizableSide: side, - ); - - final view = MacosApp( - home: MacosWindow( - child: MacosScaffold( - children: [ - resizablePane, - ContentArea( - builder: (context, scrollController) { - return const Text('Hello there'); - }, - ), - ], - ), - ), - ); - - final resizablePaneFinder = find.byWidget(resizablePane); - final dragFinder = find.descendant( - of: resizablePaneFinder, - matching: find.byType(GestureDetector), - ); - - final directionModifier = side == ResizableSide.right ? 1 : -1; - final double safeDelta = 50.0 * directionModifier; - final double overflowDelta = 500.0 * directionModifier; - - testWidgets('initial width equals startWidth', (tester) async { - await tester.pumpWidget(view); - - var resizablePaneRenderObject = - tester.renderObject(resizablePaneFinder); - expect(resizablePaneRenderObject.size.width, startWidth); - }); - - testWidgets('dragging wider works', (tester) async { - await tester.pumpWidget(view); - - await tester.drag( - dragFinder, - Offset(safeDelta, 0), + bool verticallyResizable = side == ResizableSide.top; + + group( + side == ResizableSide.top + ? 'top' + : (side == ResizableSide.left ? 'left' : 'right'), + () { + const double maxSize = 300; + const double minSize = 100; + const double startSize = 200; + + final resizablePane = ResizablePane( + builder: (context, scrollController) => const Text('Hello there'), + minSize: minSize, + startSize: startSize, + maxSize: maxSize, + resizableSide: side, ); - await tester.pump(); - var resizablePaneRenderObject = - tester.renderObject(resizablePaneFinder); - expect( - resizablePaneRenderObject.size.width, - startWidth + safeDelta * directionModifier, + final view = side == ResizableSide.top + ? MacosApp( + home: MacosWindow( + child: MacosScaffold( + children: [ + ContentArea( + builder: (context, scrollController) { + return Column( + children: [ + const Flexible( + fit: FlexFit.loose, + child: Center( + child: Text('Hello there'), + ), + ), + resizablePane, + ], + ); + }, + ), + ], + ), + ), + ) + : MacosApp( + home: MacosWindow( + child: MacosScaffold( + children: [ + resizablePane, + ContentArea( + builder: (context, scrollController) { + return const Text('Hello there'); + }, + ), + ], + ), + ), + ); + + final resizablePaneFinder = find.byWidget(resizablePane); + final dragFinder = find.descendant( + of: resizablePaneFinder, + matching: find.byType(GestureDetector), ); - }); - testWidgets('dragging wider respects maxWidth', (tester) async { - await tester.pumpWidget(view); + // No need to check if the resizable side is top because directionModifier + // would take -1 if it is the case + final directionModifier = side == ResizableSide.right ? 1 : -1; + final double safeDelta = 50.0 * directionModifier; + final double overflowDelta = 500.0 * directionModifier; - await tester.drag( - dragFinder, - Offset(overflowDelta, 0), - ); - await tester.pump(); + testWidgets('initial size equals startSize', (tester) async { + await tester.pumpWidget(view); + + var resizablePaneRenderObject = + tester.renderObject(resizablePaneFinder); + var initialSize = verticallyResizable + ? resizablePaneRenderObject.size.height + : resizablePaneRenderObject.size.width; - var resizablePaneRenderObject = - tester.renderObject(resizablePaneFinder); - expect(resizablePaneRenderObject.size.width, maxWidth); - }); + expect(initialSize, startSize); + }); - testWidgets( - 'drag events past maxWidth have no effect', - (tester) async { + testWidgets('dragging wider works $side', (tester) async { await tester.pumpWidget(view); - final dragStartLocation = tester.getCenter(dragFinder); - final drag = await tester.startGesture(dragStartLocation); - await drag.moveBy(Offset(overflowDelta, 0)); - await drag.moveBy(Offset(-10.0 * directionModifier, 0)); - await drag.up(); + await tester.drag( + dragFinder, + verticallyResizable ? Offset(0, safeDelta) : Offset(safeDelta, 0), + ); await tester.pump(); var resizablePaneRenderObject = tester.renderObject(resizablePaneFinder); - expect(resizablePaneRenderObject.size.width, maxWidth); - }, - ); - - testWidgets('dragging narrower works', (tester) async { - await tester.pumpWidget(view); + expect( + verticallyResizable + ? resizablePaneRenderObject.size.height + : resizablePaneRenderObject.size.width, + startSize + safeDelta * directionModifier, + ); + }); + + testWidgets('dragging wider respects maxSize', (tester) async { + await tester.pumpWidget(view); - await tester.drag( - dragFinder, - Offset(-safeDelta, 0), - ); - await tester.pump(); + await tester.drag( + dragFinder, + verticallyResizable + ? Offset(0, overflowDelta) + : Offset(overflowDelta, 0), + ); + await tester.pump(); - var resizablePaneRenderObject = - tester.renderObject(resizablePaneFinder); - expect( - resizablePaneRenderObject.size.width, - startWidth - safeDelta * directionModifier, + var resizablePaneRenderObject = + tester.renderObject(resizablePaneFinder); + var currentSize = verticallyResizable + ? resizablePaneRenderObject.size.height + : resizablePaneRenderObject.size.width; + expect(currentSize, maxSize); + }); + + testWidgets( + 'drag events past maxSize have no effect $side', + (tester) async { + await tester.pumpWidget(view); + + final dragStartLocation = tester.getCenter(dragFinder); + final drag = await tester.startGesture(dragStartLocation); + await drag.moveBy( + verticallyResizable + ? Offset(0, overflowDelta) + : Offset(overflowDelta, 0), + ); + await drag.moveBy( + verticallyResizable + ? Offset(0, -10.0 * directionModifier) + : Offset(-10.0 * directionModifier, 0), + ); + await drag.up(); + await tester.pump(); + + var resizablePaneRenderObject = + tester.renderObject(resizablePaneFinder); + var currentSize = verticallyResizable + ? resizablePaneRenderObject.size.height + : resizablePaneRenderObject.size.width; + expect(currentSize, maxSize); + }, ); - }); - - testWidgets('dragging narrower respects minWidth', (tester) async { - await tester.pumpWidget(view); - await tester.drag( - dragFinder, - Offset(-overflowDelta, 0), - ); - await tester.pump(); + testWidgets('dragging narrower works', (tester) async { + await tester.pumpWidget(view); - var resizablePaneRenderObject = - tester.renderObject(resizablePaneFinder); - expect(resizablePaneRenderObject.size.width, minWidth); - }); + await tester.drag( + dragFinder, + verticallyResizable + ? Offset(0, -safeDelta) + : Offset(-safeDelta, 0), + ); + await tester.pump(); - testWidgets( - 'drag events past minWidth have no effect', - (tester) async { + var resizablePaneRenderObject = + tester.renderObject(resizablePaneFinder); + var currentSize = verticallyResizable + ? resizablePaneRenderObject.size.height + : resizablePaneRenderObject.size.width; + expect( + currentSize, + startSize - safeDelta * directionModifier, + ); + }); + + testWidgets('dragging narrower respects minSize', (tester) async { await tester.pumpWidget(view); - final dragStartLocation = tester.getCenter(dragFinder); - final drag = await tester.startGesture(dragStartLocation); - await drag.moveBy(Offset(-overflowDelta, 0)); - await drag.moveBy(Offset(10.0 * directionModifier, 0)); - await drag.up(); + await tester.drag( + dragFinder, + verticallyResizable + ? Offset(0, -overflowDelta) + : Offset(-overflowDelta, 0), + ); await tester.pump(); var resizablePaneRenderObject = tester.renderObject(resizablePaneFinder); - expect(resizablePaneRenderObject.size.width, minWidth); - }, - ); - }); + var currentSize = verticallyResizable + ? resizablePaneRenderObject.size.height + : resizablePaneRenderObject.size.width; + expect(currentSize, minSize); + }); + + testWidgets( + 'drag events past minSize have no effect', + (tester) async { + await tester.pumpWidget(view); + + final dragStartLocation = tester.getCenter(dragFinder); + final drag = await tester.startGesture(dragStartLocation); + await drag.moveBy( + verticallyResizable + ? Offset(0, -overflowDelta) + : Offset(-overflowDelta, 0), + ); + await drag.moveBy( + verticallyResizable + ? Offset(0, 10.0 * directionModifier) + : Offset(10.0 * directionModifier, 0), + ); + await drag.up(); + await tester.pump(); + + var resizablePaneRenderObject = + tester.renderObject(resizablePaneFinder); + var currentSize = verticallyResizable + ? resizablePaneRenderObject.size.height + : resizablePaneRenderObject.size.width; + expect(currentSize, minSize); + }, + ); + }, + ); } }); } diff --git a/test/layout/tab_view_test.dart b/test/layout/tab_view_test.dart index 3673f4d1..2fb09c07 100644 --- a/test/layout/tab_view_test.dart +++ b/test/layout/tab_view_test.dart @@ -14,7 +14,7 @@ void main() { child: MacosScaffold( children: [ ContentArea( - builder: (context, scrollController) { + builder: (context, _) { return Padding( padding: const EdgeInsets.all(24.0), child: MacosTabView( diff --git a/test/selectors/date_picker_test.dart b/test/selectors/date_picker_test.dart index e6d36763..f0c5afbc 100644 --- a/test/selectors/date_picker_test.dart +++ b/test/selectors/date_picker_test.dart @@ -15,7 +15,7 @@ void main() { child: MacosScaffold( children: [ ContentArea( - builder: (context, scrollController) { + builder: (context, _) { return Center( child: MacosDatePicker( onDateChanged: (date) {}, @@ -262,7 +262,7 @@ void main() { child: MacosScaffold( children: [ ContentArea( - builder: (context, scrollController) { + builder: (context, _) { return Center( child: MacosDatePicker( onDateChanged: (date) { diff --git a/test/theme/help_button_theme_test.dart b/test/theme/help_button_theme_test.dart index cd93ac74..702a85ed 100644 --- a/test/theme/help_button_theme_test.dart +++ b/test/theme/help_button_theme_test.dart @@ -59,7 +59,7 @@ void main() { child: MacosScaffold( children: [ ContentArea( - builder: (context, scrollController) { + builder: (context, _) { capturedContext = context; return const HelpButton(); }, diff --git a/test/theme/icon_button_theme_test.dart b/test/theme/icon_button_theme_test.dart index 92832505..af7a40e6 100644 --- a/test/theme/icon_button_theme_test.dart +++ b/test/theme/icon_button_theme_test.dart @@ -66,7 +66,7 @@ void main() { child: MacosScaffold( children: [ ContentArea( - builder: (context, scrollController) { + builder: (context, _) { capturedContext = context; return MacosIconButton( icon: const Icon(CupertinoIcons.add), diff --git a/test/theme/icon_theme_test.dart b/test/theme/icon_theme_test.dart index 23182817..8e98f80a 100644 --- a/test/theme/icon_theme_test.dart +++ b/test/theme/icon_theme_test.dart @@ -66,7 +66,7 @@ void main() { child: MacosScaffold( children: [ ContentArea( - builder: (context, scrollController) { + builder: (context, _) { capturedContext = context; return const MacosIcon( CupertinoIcons.add, diff --git a/test/theme/popup_button_theme_test.dart b/test/theme/popup_button_theme_test.dart index 710f2f49..daa039de 100644 --- a/test/theme/popup_button_theme_test.dart +++ b/test/theme/popup_button_theme_test.dart @@ -70,7 +70,7 @@ void main() { child: MacosScaffold( children: [ ContentArea( - builder: (context, scrollController) { + builder: (context, _) { capturedContext = context; return MacosPopupButton( value: popupValue, diff --git a/test/theme/pulldown_button_theme_test.dart b/test/theme/pulldown_button_theme_test.dart index 44264874..373ac0c4 100644 --- a/test/theme/pulldown_button_theme_test.dart +++ b/test/theme/pulldown_button_theme_test.dart @@ -69,7 +69,7 @@ void main() { child: MacosScaffold( children: [ ContentArea( - builder: (context, scrollController) { + builder: (context, _) { capturedContext = context; return const Center( child: MacosPulldownButton( diff --git a/test/theme/push_button_theme_test.dart b/test/theme/push_button_theme_test.dart index 8ced6d85..5bcc4ef1 100644 --- a/test/theme/push_button_theme_test.dart +++ b/test/theme/push_button_theme_test.dart @@ -61,7 +61,7 @@ void main() { child: MacosScaffold( children: [ ContentArea( - builder: (context, scrollController) { + builder: (context, _) { capturedContext = context; return const PushButton( buttonSize: ButtonSize.small, diff --git a/test/theme/search_field_theme_test.dart b/test/theme/search_field_theme_test.dart index 866f83cc..da838f37 100644 --- a/test/theme/search_field_theme_test.dart +++ b/test/theme/search_field_theme_test.dart @@ -65,7 +65,7 @@ void main() { child: MacosScaffold( children: [ ContentArea( - builder: (context, scrollController) { + builder: (context, _) { capturedContext = context; return const Center( child: MacosSearchField(),