├── .github └── workflows │ └── pr_validation.yaml ├── .gitignore ├── .metadata ├── .vscode └── launch.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── dart_test.yaml ├── doc ├── branding │ ├── flutter-swift-ui_logo_128.png │ ├── flutter-swift-ui_logo_256.png │ └── flutter-swift-ui_logo_512.png └── ux_spec │ └── README_UX_SPEC.md ├── doc_swift_ui_gallery ├── README.md └── swift_ui_gallery │ ├── .gitignore │ ├── swift_ui_gallery.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ ├── IDETemplateMacros.plist │ │ └── xcschemes │ │ └── swift_ui_gallery.xcscheme │ ├── swift_ui_gallery │ ├── Assets.xcassets │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── dash_hello.imageset │ │ │ ├── Contents.json │ │ │ ├── dash_ciao.png │ │ │ ├── dash_hello.png │ │ │ └── dash_hello_default.png │ │ └── logo.imageset │ │ │ ├── Contents.json │ │ │ └── flutter-swift-ui_logo_256.png │ ├── ContentView.swift │ ├── Localizable.xcstrings │ ├── LocalizableAlternative.xcstrings │ ├── Preview Content │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ ├── TodoPage.swift │ ├── collections │ │ └── CollectionsPage.swift │ ├── controls │ │ └── ControlsPage.swift │ ├── layouts │ │ ├── HStackExamples.swift │ │ ├── LayoutsPage.swift │ │ ├── VStackExamples.swift │ │ └── ZStackExamples.swift │ ├── motion │ │ └── MotionPage.swift │ ├── primitives │ │ ├── ImageExamples.swift │ │ ├── PrimitivesPage.swift │ │ ├── image │ │ │ └── localization │ │ │ │ └── ImageLocalizationPage.swift │ │ └── text │ │ │ ├── TextPage.swift │ │ │ ├── accessibility │ │ │ └── TextAccessibilityPage.swift │ │ │ ├── localization │ │ │ └── TextLocalizationPage.swift │ │ │ └── typography │ │ │ └── TextTypographyPage.swift │ ├── scaffolds │ │ └── ScaffoldsPage.swift │ ├── shapes │ │ ├── EllipseExamples.swift │ │ ├── RectangleExamples.swift │ │ └── ShapesPage.swift │ └── swift_ui_galleryApp.swift │ ├── swift_ui_galleryTests │ └── swift_ui_galleryTests.swift │ └── swift_ui_galleryUITests │ ├── swift_ui_galleryUITests.swift │ └── swift_ui_galleryUITestsLaunchTests.swift ├── example ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ ├── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ │ └── LaunchImage.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ └── README.md │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h │ └── RunnerTests │ │ └── RunnerTests.swift ├── lib │ ├── app.dart │ ├── collections │ │ └── collection_examples.dart │ ├── controls │ │ └── controls_examples.dart │ ├── demo_screen.dart │ ├── home_screen.dart │ ├── infrastructure │ │ ├── inventory_page.dart │ │ └── not_built_yet_page.dart │ ├── layouts │ │ ├── hstack.dart │ │ ├── layout_examples.dart │ │ └── vstack.dart │ ├── main.dart │ ├── motion │ │ └── motion_examples.dart │ ├── primitives │ │ └── primitive_examples.dart │ ├── scaffolds │ │ └── scaffold_examples.dart │ └── shapes │ │ ├── ellipse.dart │ │ ├── rectangle.dart │ │ └── shapes_examples.dart ├── macos │ ├── .gitignore │ ├── Flutter │ │ ├── Flutter-Debug.xcconfig │ │ ├── Flutter-Release.xcconfig │ │ └── GeneratedPluginRegistrant.swift │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── app_icon_1024.png │ │ │ │ ├── app_icon_128.png │ │ │ │ ├── app_icon_16.png │ │ │ │ ├── app_icon_256.png │ │ │ │ ├── app_icon_32.png │ │ │ │ ├── app_icon_512.png │ │ │ │ └── app_icon_64.png │ │ ├── Base.lproj │ │ │ └── MainMenu.xib │ │ ├── Configs │ │ │ ├── AppInfo.xcconfig │ │ │ ├── Debug.xcconfig │ │ │ ├── Release.xcconfig │ │ │ └── Warnings.xcconfig │ │ ├── DebugProfile.entitlements │ │ ├── Info.plist │ │ ├── MainFlutterWindow.swift │ │ └── Release.entitlements │ └── RunnerTests │ │ └── RunnerTests.swift ├── pubspec.lock └── pubspec.yaml ├── lib ├── src │ ├── controls_and_indicators │ │ └── button.dart │ ├── drawing_and_graphics │ │ └── border.dart │ ├── infrastructure │ │ ├── colors.dart │ │ └── late_bound_build.dart │ ├── layout │ │ ├── alignment.dart │ │ ├── frame.dart │ │ ├── grid.dart │ │ ├── hstack.dart │ │ ├── list.dart │ │ ├── table.dart │ │ ├── vstack.dart │ │ └── zstack.dart │ └── shapes │ │ ├── ellipse.dart │ │ └── rectangle.dart └── swift_ui.dart ├── pubspec.yaml └── test ├── golden_tools.dart ├── infrastructure └── late_bound_build_test.dart ├── layout ├── frame_golden_test.dart ├── goldens │ ├── frame_smoke-test.png │ ├── hstack_smoke-test.png │ └── vstack_smoke-test.png ├── hstack_golden_test.dart ├── hstack_test.dart ├── vstack_golden_test.dart ├── vstack_test.dart └── zstack_test.dart └── shapes ├── ellipse_golden_test.dart ├── goldens ├── ellipse_smoke-test.png └── rectangle_smoke-test.png └── rectangle_golden_test.dart /.github/workflows/pr_validation.yaml: -------------------------------------------------------------------------------- 1 | name: Validate pull requests 2 | on: [pull_request] 3 | jobs: 4 | analyze: 5 | runs-on: ubuntu-latest 6 | defaults: 7 | run: 8 | working-directory: ./ 9 | steps: 10 | # Checkout the PR branch 11 | - uses: actions/checkout@v3 12 | 13 | # Setup Flutter environment 14 | - uses: subosito/flutter-action@v2 15 | with: 16 | channel: "stable" 17 | architecture: x64 18 | 19 | # Download all the packages that the app uses 20 | - run: flutter pub get 21 | 22 | # Enforce lint rules 23 | - run: flutter analyze 24 | 25 | test: 26 | runs-on: ubuntu-latest 27 | defaults: 28 | run: 29 | working-directory: ./ 30 | steps: 31 | # Checkout the PR branch 32 | - uses: actions/checkout@v3 33 | 34 | # Setup Flutter environment 35 | - uses: subosito/flutter-action@v2 36 | with: 37 | channel: "stable" 38 | architecture: x64 39 | 40 | # Download all the packages that the app uses 41 | - run: flutter pub get 42 | 43 | # Run all tests 44 | - run: flutter test --exclude-tags=golden 45 | 46 | test-goldens: 47 | runs-on: macos-latest 48 | defaults: 49 | run: 50 | working-directory: ./ 51 | steps: 52 | # Checkout the PR branch 53 | - uses: actions/checkout@v3 54 | 55 | # Setup Flutter environment 56 | - uses: subosito/flutter-action@v2 57 | with: 58 | channel: "stable" 59 | architecture: x64 60 | 61 | # Download all the packages that the app uses 62 | - run: flutter pub get 63 | 64 | # Run all tests 65 | - run: flutter test --tags=golden -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 26 | /pubspec.lock 27 | **/doc/api/ 28 | .dart_tool/ 29 | build/ 30 | 31 | # Don't check in golden failure output. 32 | test/**/failures/** 33 | test_goldens/**/failures/** -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "ef9befc9da9f358f301d052e900b36ea3e797565" 8 | channel: "master" 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Golden", 5 | "request": "launch", 6 | "type": "dart", 7 | "codeLens": { 8 | "for": ["run-test", "run-test-file"] 9 | }, 10 | "args": ["--update-goldens"] 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.0.2 2 | 3 | - Added `HStack` widget. 4 | - Added `VStack` widget. 5 | - Added `Rectangle` widget. 6 | 7 | ## 0.0.1 - Sept, 2023 8 | Setting up the project structure. This release is not usable. 9 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | This guide describes opportunities for contribution to this project, as well as contribution policies. 3 | 4 | ## Ways to Help 5 | There are both technical and non-technical opportunities to help the `swift_ui` package. 6 | 7 | ### Add to the UX Spec 8 | `swift_ui` is a clone of Apple UX. Apple doesn't publish a specification for its UX. Therefore, the quality of `swift_ui` is limited by the quality of observation of Apple's Swift UI implementation. Before `swift_ui` implements a new widget, this project needs to log a clear visual specification of the expected result. 9 | 10 | It would be a big help if you were to capture a clear specification for a given Swift UI widget, screen, or flow. See the existing specification to learn more. 11 | 12 | ### Implement Something 13 | Once a widget, screen, or flow is spec'd out, based on visible Apple behavior, that specification needs to be implemented in this package. 14 | 15 | ### Test Something 16 | Implementations are expected to include tests, but it's easy to miss things along the way. If a user-visible feature is un-tested, then it would be helpful to add appropriate tests to lock down that feature. 17 | 18 | ### Document Something 19 | If any contributions make it into this package without useful documentation, consider adding or improving that documentation. 20 | 21 | ### Post to Social Media 22 | If you think that some of the widgets in this toolkit are neat, or you build something interesting with these widgets, post a screenshot or video to social media to spread the word. 23 | 24 | ### Publish Videos 25 | Consider publishing videos to YouTube, Rumble, or Twitter, demonstrating your contributions to, or use of the `swift_ui` package. 26 | 27 | ## Repository Practices 28 | Please follow these rules when interacting with this project's repository. 29 | 30 | ### Filing Issues 31 | There's no extra time to figure out what an issue means. All issue tickets should be clear, concise, and actionable. Any issue that fails to meet this bar will be closed, so that contributors use their time effectively. 32 | 33 | A ticket to implement a new widget, screen, or flow, must include a complete specification for the desired layout, rendering, and user interactions. See [Add to the UX Spec] above. 34 | 35 | A ticket to fix a bug must include a minimal reproduction of the bug, a description of expected behavior, and a description of actual behavior. 36 | 37 | ### Submitting Pull Requests (PRs) 38 | Code review is a difficult and tedious task. All contributing developers are expected to make it as easy as possible to review their code, and also submit code that's clearly documented so that it may be extended or fixed in the future. 39 | 40 | Each PR should include: 41 | * A meaningful title. 42 | * A description of the problem being solved. 43 | * A description of how the problem was solved. 44 | * An image or video of what was implemented. 45 | 46 | ## Coding Practices 47 | The following coding practices should be followed unless there are significant and exceptional reasons to violate these policies. 48 | 49 | ### Style 50 | * Run `dartfmt` formatting with a line length of 120 characters. 51 | * Use the same artifact names as Swift UI, when possible, e.g., an `HStack` widget, `VStack` widget, `Table` widget, etc. 52 | * If a Swift UI name has a significant naming conflict, choose the least bad alternative, e.g., `ListView` instead of `List` because `List` is a core Dart data type. 53 | * Include Dart Docs for all non-trivial public classes, methods, properties, and functions. 54 | * When possible, consider using descriptions similar to those in the Swift UI documentation. 55 | * Include Dart Docs for tricky private classes, methods, properties, and functions. 56 | * Include Code comments for any tricky segment of code. 57 | * Use file and directory packaging that matches existing project practices. 58 | 59 | ### API Paradigm 60 | It's well known among Flutter and Swift UI developers that each toolkit uses a fundamentally different composition paradigm. 61 | 62 | Flutter explicitly composes a widget tree from the outside in. Swift UI implicitly composes a view tree, using modifier methods, from the inside out. 63 | 64 | The goal of the `swift_ui` package is to replicate the user experience (UX) of Swift UI, **NOT** the inside-out composition paradigm. Flutter's existing widget composition paradigm should be sufficient to reasonably accomplish all Swift UI cloning goals. 65 | 66 | Contributions that attempt to switch from Flutter's standard widget composition paradigm, to an extension method builder paradigm, will be rejected. 67 | 68 | ### New Concepts and Re-used Concepts 69 | Sometimes it makes sense to duplicate an existing Flutter concept in `swift_ui`, and other times it doesn't. 70 | 71 | As a guiding principle, any Swift UI view should be cloned by a widget in `swift_ui`, even if Flutter already has a similar widget. For example, Swift UI has a `VStack` which is conceptually similar to Flutter's `Column`. Despite the similarity, the `swift_ui` package should introduce a `VStack` widget. 72 | 73 | Some Swift UI types will be very similar to Flutter types, but slightly different. For example, Swift UI defines `VerticalAlignment` which is very similar to Flutter's `CrossAxisAlignment`. However, `VerticalAlignment` includes a `firstTextBaseline` and `lastTextBaseline`, while Flutter only offers a single `baseline`. As such, the `swift_ui` package should create `VerticalAlignment` to match Swift UI. 74 | 75 | In cases where a Swift UI type introduces nothing of value beyond an existing Flutter type, use the existing Flutter type instead of creating a new type. For example, a `CGFloat` should be represented by a Dart `double`, a Swift UI `EdgeInsets` should be represented by a Flutter `EdgeInsets`, etc. 76 | 77 | ### Don't Reference Material Design 78 | The goal of this package is to prove Flutter's ability to render Apple-style user interfaces, when needed. Ideally, this goal will ease the transition for Apple developers over to Flutter. 79 | 80 | Apple developers are notoriously tribal. If they see any reference to Material Design anywhere in the code, it might trigger them to run away, because they think that using Material Design means they're embracing Google and Android. 81 | 82 | Though it may take some extra work to pull it off, `swift_ui` should avoid importing any files from Material. If Material artifacts are required, consider copying them from Flutter into this package. 83 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 Declarative, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 | Swifty Flutter
4 |

A Flutter port of the Swift UI toolkit.

5 |

This package is not affiliated with Apple or any Apple technologies.

6 |
7 |

8 |

9 |

10 | 11 | # The Mission 12 | Provide a near pixel-perfect replica of Apple's Swift UI experience in Flutter. 13 | 14 | This mission serves multiple purposes: 15 | 16 | * Show developers that Flutter is a great choice for iOS development. 17 | * Remove the Cupertino burden from the Flutter framework team so they can focus on the framework. 18 | * Demonstrate that the Flutter community can come together to solve large problems. 19 |

20 | 21 | # Want to Help? 22 | Cloning Swift UI is a large and on-going task. The project can use all the help it can get. There are both technical and non-technical ways that you can help. Please see the [contributing guide](CONTRIBUTING.md) to learn more. 23 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | # Additional information about this file can be found at 4 | # https://dart.dev/guides/language/analysis-options 5 | -------------------------------------------------------------------------------- /dart_test.yaml: -------------------------------------------------------------------------------- 1 | tags: 2 | golden: 3 | -------------------------------------------------------------------------------- /doc/branding/flutter-swift-ui_logo_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/doc/branding/flutter-swift-ui_logo_128.png -------------------------------------------------------------------------------- /doc/branding/flutter-swift-ui_logo_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/doc/branding/flutter-swift-ui_logo_256.png -------------------------------------------------------------------------------- /doc/branding/flutter-swift-ui_logo_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/doc/branding/flutter-swift-ui_logo_512.png -------------------------------------------------------------------------------- /doc/ux_spec/README_UX_SPEC.md: -------------------------------------------------------------------------------- 1 | # UX Specification 2 | The `swift_ui` package is only as good as its UX specification. 3 | 4 | A UX specification describes the size, shapes, colors, fonts, gestures, text input, and accessibility requirements for every widget, screen, and flow. This directory holds the UX specification for the `swift_ui` package, which represents the best efforts of the contributors to observe and log what they see in real Swift UI interfaces. 5 | 6 | Carefully inspecting and logging UX specifications is one of the most time and effort-intensive tasks associated with this package. It would be a big help if you could contribute actionable UX specifications to this directory. Check the repository Issues to find out what this project needs. 7 | 8 | ## What to Inspect 9 | Even the simplest Swift UI views have a surprising number of behaviors that need to be inspected and logged. Here are some hints to get you started. 10 | 11 | Layout Specifications: 12 | * What happens when it has no children? 13 | * What happens if it has one child vs two children? 14 | * What happens if it has too many children to fit the available space? 15 | * Does it fill available space, size itself intrinsically, or both based on circumstance? 16 | * Does the layout apply any padding, margin, spacing? If so, how much? Do any of those values change based on the size of the layout? 17 | * What happens if the layout that you're inspecting is used inside of various other layouts? Does it behave as expected? 18 | * The OS can make content larger or smaller for easy viewing purposes - does changing that OS setting impact the layout? 19 | * Does it scroll/pan? If so, what are the rules for scrolling dynamics and overscroll? 20 | * Do gestures have any impact on that layout? If so, which gestures, and what impact? 21 | * What's the traversal behavior and audio announcement behavior in accessibility mode? 22 | 23 | Controls and Indicators: 24 | * Can it support arbitrary children (content), or does it only take specific types? 25 | * How does it respond when it has too much space, or too little space? 26 | * How does it respond when the aspect ratio of the available space is very different? 27 | * If it contains text, colors, shapes - where did those default configurations come from? How can they be changed? 28 | * Does the view include any margin, padding, or spacing? If so, what are the values? Do they change based on the size of the view? 29 | * The OS can make content larger or smaller for easy viewing purposes - does changing that OS setting impact the view? 30 | * Does Dark Mode and Light Mode impact the appearance? 31 | * Is there a hover effect? a press down effect? a release effect? 32 | * Does the appearance change depending on the parent or ancestor view? For example, buttons look very different depending on their container. 33 | * What's the traversal behavior and audio announcement behavior in accessibility mode? 34 | 35 | ## Quick Links 36 | [Apple's official Swift UI documentation](https://developer.apple.com/documentation/swiftui/) 37 | 38 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/README.md: -------------------------------------------------------------------------------- 1 | # Gallery of Real Swift UI Artifacts 2 | This directory includes a traditional iOS project, which includes many variations of Swift UI views so that the presentation and behavior of those views can be easily inspected. 3 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | # Package.pins 42 | # Package.resolved 43 | # *.xcodeproj 44 | # 45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 46 | # hence it is not needed unless you have added a package configuration file to your project 47 | # .swiftpm 48 | 49 | .build/ 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 56 | # 57 | # Pods/ 58 | # 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | # *.xcworkspace 61 | 62 | # Carthage 63 | # 64 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 65 | # Carthage/Checkouts 66 | 67 | Carthage/Build/ 68 | 69 | # Accio dependency management 70 | Dependencies/ 71 | .accio/ 72 | 73 | # fastlane 74 | # 75 | # It is recommended to not store the screenshots in the git repo. 76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 77 | # For more information about the recommended setup visit: 78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 79 | 80 | fastlane/report.xml 81 | fastlane/Preview.html 82 | fastlane/screenshots/**/*.png 83 | fastlane/test_output 84 | 85 | # Code Injection 86 | # 87 | # After new code Injection tools there's a generated folder /iOSInjectionProject 88 | # https://github.com/johnno1962/injectionforxcode 89 | 90 | iOSInjectionProject/ -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery.xcodeproj/xcshareddata/IDETemplateMacros.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | FILEHEADER 6 | 7 | 8 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery.xcodeproj/xcshareddata/xcschemes/swift_ui_gallery.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 35 | 41 | 42 | 43 | 46 | 52 | 53 | 54 | 55 | 56 | 66 | 68 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "platform" : "ios", 6 | "size" : "1024x1024" 7 | } 8 | ], 9 | "info" : { 10 | "author" : "xcode", 11 | "version" : 1 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/Assets.xcassets/dash_hello.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "dash_hello_default.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | }, 16 | { 17 | "filename" : "dash_hello.png", 18 | "idiom" : "universal", 19 | "locale" : "en", 20 | "scale" : "1x" 21 | }, 22 | { 23 | "idiom" : "universal", 24 | "locale" : "en", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "idiom" : "universal", 29 | "locale" : "en", 30 | "scale" : "3x" 31 | }, 32 | { 33 | "filename" : "dash_ciao.png", 34 | "idiom" : "universal", 35 | "locale" : "it", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "universal", 40 | "locale" : "it", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "universal", 45 | "locale" : "it", 46 | "scale" : "3x" 47 | } 48 | ], 49 | "info" : { 50 | "author" : "xcode", 51 | "version" : 1 52 | }, 53 | "properties" : { 54 | "localizable" : true 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/Assets.xcassets/dash_hello.imageset/dash_ciao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/Assets.xcassets/dash_hello.imageset/dash_ciao.png -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/Assets.xcassets/dash_hello.imageset/dash_hello.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/Assets.xcassets/dash_hello.imageset/dash_hello.png -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/Assets.xcassets/dash_hello.imageset/dash_hello_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/Assets.xcassets/dash_hello.imageset/dash_hello_default.png -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/Assets.xcassets/logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "flutter-swift-ui_logo_256.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/Assets.xcassets/logo.imageset/flutter-swift-ui_logo_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/Assets.xcassets/logo.imageset/flutter-swift-ui_logo_256.png -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/ContentView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct ContentView: View { 4 | var body: some View { 5 | TabView { 6 | PrimitivesPage().tabItem{ 7 | Label("Primitives", systemImage: "square.fill.on.circle.fill") 8 | } 9 | 10 | LayoutsPage().tabItem{ 11 | Label("Layouts", systemImage: "square.3.layers.3d") 12 | } 13 | 14 | CollectionsPage().tabItem{ 15 | Label("Collections", systemImage: "tablecells") 16 | } 17 | 18 | ScaffoldsPage().tabItem{ 19 | Label("Scaffolds", systemImage: "square.leftthird.inset.filled") 20 | } 21 | 22 | ControlsPage().tabItem{ 23 | Label("Controls", systemImage: "slider.horizontal.3") 24 | } 25 | 26 | MotionPage().tabItem{ 27 | Label("Motion", systemImage: "move.3d") 28 | } 29 | 30 | ShapesPage().tabItem{ 31 | Label("Shapes", systemImage: "square.on.circle") 32 | } 33 | } 34 | } 35 | } 36 | 37 | struct ContentView_Previews: PreviewProvider { 38 | static var previews: some View { 39 | ContentView() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/LocalizableAlternative.xcstrings: -------------------------------------------------------------------------------- 1 | { 2 | "sourceLanguage" : "en", 3 | "strings" : { 4 | "pencil" : { 5 | "localizations" : { 6 | "it" : { 7 | "stringUnit" : { 8 | "state" : "translated", 9 | "value" : "lapis" 10 | } 11 | } 12 | } 13 | } 14 | }, 15 | "version" : "1.0" 16 | } -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/TodoPage.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct TodoPage: View { 4 | var body: some View { 5 | Text("This page still needs to be spec'd and built. Wanna hepl?") 6 | .multilineTextAlignment(.center) 7 | } 8 | } 9 | 10 | struct TodoLink: View { 11 | let label: String 12 | 13 | var body: some View { 14 | NavigationLink { 15 | TodoPage() 16 | } label: { 17 | Text(label) 18 | } 19 | } 20 | } 21 | 22 | struct TodoPage_Previews: PreviewProvider { 23 | static var previews: some View { 24 | TodoPage() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/collections/CollectionsPage.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct CollectionsPage: View { 4 | var body: some View { 5 | NavigationStack { 6 | List() { 7 | Section() { 8 | TodoLink(label: "No Sections") 9 | 10 | TodoLink(label: "With Sections (plain)") 11 | 12 | TodoLink(label: "With Sections (grouped)") 13 | 14 | TodoLink(label: "Empty") 15 | } header: { 16 | Text("Lists") 17 | } 18 | 19 | Section() { 20 | TodoLink(label: "Static Rows") 21 | 22 | TodoLink(label: "Sortable") 23 | 24 | TodoLink(label: "Empty") 25 | } header: { 26 | Text("Tables") 27 | } 28 | }.navigationTitle("Collections") 29 | } 30 | } 31 | } 32 | 33 | struct CollectionsPage_Previews: PreviewProvider { 34 | static var previews: some View { 35 | CollectionsPage() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/controls/ControlsPage.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct ControlsPage: View { 4 | var body: some View { 5 | NavigationStack { 6 | List() { 7 | TodoLink(label: "Buttons") 8 | 9 | TodoLink(label: "Switches") 10 | }.navigationTitle("Controls") 11 | } 12 | } 13 | } 14 | 15 | struct ControlsPage_Previews: PreviewProvider { 16 | static var previews: some View { 17 | ControlsPage() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/layouts/HStackExamples.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct HStackLeadingAlignedPage : View { 4 | var body: some View { 5 | HStack(alignment: .top) { 6 | Text("First\nSecond") 7 | Text("Third") 8 | Text("Fourth\nFifth\nSixth") 9 | } 10 | } 11 | } 12 | 13 | struct HStackCenterAlignedPage : View { 14 | var body: some View { 15 | HStack(alignment: .center) { 16 | Text("First\nSecond") 17 | Text("Third") 18 | Text("Fourth\nFifth\nSixth") 19 | } 20 | } 21 | } 22 | 23 | struct HStackTrailingAlignedPage : View { 24 | var body: some View { 25 | HStack(alignment: .bottom) { 26 | Text("First\nSecond") 27 | Text("Third") 28 | Text("Fourth\nFifth\nSixth") 29 | } 30 | } 31 | } 32 | 33 | struct HStackExceedsAvailableSpacePage : View { 34 | var body: some View { 35 | HStack() { 36 | Rectangle() 37 | .foregroundColor(Color.red) 38 | .frame(width: 100, height: .infinity) 39 | Rectangle() 40 | .foregroundColor(Color.green) 41 | .frame(width: 100, height: .infinity) 42 | Rectangle() 43 | .foregroundColor(Color.blue) 44 | .frame(width: 100, height: .infinity) 45 | Rectangle() 46 | .foregroundColor(Color.purple) 47 | .frame(width: 100, height: .infinity) 48 | Rectangle() 49 | .foregroundColor(Color.orange) 50 | .frame(width: 100, height: .infinity) 51 | Rectangle() 52 | .foregroundColor(Color.cyan) 53 | .frame(width: 100, height: .infinity) 54 | } 55 | } 56 | } 57 | 58 | 59 | struct HStackTopAlignedPage_Previews: PreviewProvider { 60 | static var previews: some View { 61 | HStackLeadingAlignedPage() 62 | // HStackCenterAlignedPage() 63 | // HStackTrailingAlignedPage() 64 | // HStackExceedsAvailableSpacePage() 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/layouts/LayoutsPage.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct LayoutsPage: View { 4 | var body: some View { 5 | NavigationStack { 6 | List { 7 | Section() { 8 | NavigationLink { 9 | VStackLeadingAlignedPage() 10 | } label: { 11 | Text("Top Aligned") 12 | } 13 | 14 | NavigationLink { 15 | VStackCenterAlignedPage() 16 | } label: { 17 | Text("Middle Aligned") 18 | } 19 | 20 | NavigationLink { 21 | VStackTrailingAlignedPage() 22 | } label: { 23 | Text("Bottom Aligned") 24 | } 25 | 26 | NavigationLink { 27 | VStackExceedsAvailableSpacePage() 28 | } label: { 29 | Text("Exceeds available space") 30 | } 31 | } header: { 32 | Text("VStack") 33 | } 34 | 35 | Section() { 36 | TodoLink(label: "Left Aligned") 37 | 38 | TodoLink(label: "Center Aligned") 39 | 40 | TodoLink(label: "Right Aligned") 41 | 42 | TodoLink(label: "Exceeds available space") 43 | } header: { 44 | Text("HStack") 45 | } 46 | 47 | Section() { 48 | TodoLink(label: "Example") 49 | } header: { 50 | Text("ZStack") 51 | } 52 | 53 | Section() { 54 | TodoLink(label: "Fits available space") 55 | 56 | TodoLink(label: "Exceeds available space") 57 | } header: { 58 | Text("Grid") 59 | } 60 | }.navigationTitle("Layouts") 61 | } 62 | } 63 | } 64 | 65 | struct LayoutsPage_Previews: PreviewProvider { 66 | static var previews: some View { 67 | LayoutsPage() 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/layouts/VStackExamples.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct VStackLeadingAlignedPage : View { 4 | var body: some View { 5 | VStack(alignment: .leading) { 6 | Text("First") 7 | Text("Second") 8 | Text("Third") 9 | } 10 | } 11 | } 12 | 13 | struct VStackCenterAlignedPage : View { 14 | var body: some View { 15 | VStack(alignment: .center) { 16 | Text("First") 17 | Text("Second") 18 | Text("Third") 19 | } 20 | } 21 | } 22 | 23 | struct VStackTrailingAlignedPage : View { 24 | var body: some View { 25 | VStack(alignment: .trailing) { 26 | Text("First") 27 | Text("Second") 28 | Text("Third") 29 | } 30 | } 31 | } 32 | 33 | struct VStackExceedsAvailableSpacePage : View { 34 | var body: some View { 35 | VStack() { 36 | Rectangle() 37 | .foregroundColor(Color.red) 38 | .frame(width: .infinity, height: 200) 39 | Rectangle() 40 | .foregroundColor(Color.green) 41 | .frame(width: .infinity, height: 200) 42 | Rectangle() 43 | .foregroundColor(Color.blue) 44 | .frame(width: .infinity, height: 200) 45 | Rectangle() 46 | .foregroundColor(Color.purple) 47 | .frame(width: .infinity, height: 200) 48 | Rectangle() 49 | .foregroundColor(Color.orange) 50 | .frame(width: .infinity, height: 200) 51 | Rectangle() 52 | .foregroundColor(Color.cyan) 53 | .frame(width: .infinity, height: 200) 54 | } 55 | } 56 | } 57 | 58 | 59 | struct VStackTopAlignedPage_Previews: PreviewProvider { 60 | static var previews: some View { 61 | // VStackLeadingAlignedPage() 62 | // VStackCenterAlignedPage() 63 | // VStackTrailingAlignedPage() 64 | VStackExceedsAvailableSpacePage() 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/layouts/ZStackExamples.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct ZStackStaggeredSquares : View { 4 | let colors: [Color] = 5 | [.red, .orange, .yellow, .green, .blue, .purple] 6 | 7 | var body: some View { 8 | ZStack { 9 | ForEach(0.. arguments) { print('Default punctuation'); }") 49 | Text("void main(List arguments) { print('Include punctuation'); }").speechAlwaysIncludesPunctuation(true) 50 | Text("void main(List arguments) { print('Do not include punctuation'); }").speechAlwaysIncludesPunctuation(false) 51 | 52 | Divider() 53 | 54 | Text("Default Spell out characters") 55 | Text("APPL") 56 | Text("125") 57 | Text("Spell out characters") 58 | Text("APPL").speechSpellsOutCharacters(true) 59 | Text("125").speechSpellsOutCharacters(true) 60 | Text("Do not spell out characters") 61 | Text("APPL").speechSpellsOutCharacters(false) 62 | Text("125").speechSpellsOutCharacters(false) 63 | 64 | Divider() 65 | 66 | Text("Heading h1").accessibilityHeading(.h1).accessibilityAddTraits([.isHeader]) 67 | Text("Heading h2").accessibilityHeading(.h2).accessibilityAddTraits([.isHeader]) 68 | Text("Heading h3").accessibilityHeading(.h3).accessibilityAddTraits([.isHeader]) 69 | Text("Heading h4").accessibilityHeading(.h4).accessibilityAddTraits([.isHeader]) 70 | Text("Heading h5").accessibilityHeading(.h5).accessibilityAddTraits([.isHeader]) 71 | Text("Heading h6").accessibilityHeading(.h6).accessibilityAddTraits([.isHeader]) 72 | Text("Heading unspecified").accessibilityHeading(.unspecified) 73 | 74 | Divider() 75 | 76 | // TODO: I couldn't figure out how to jump to the next heading of the same level... 77 | // e.g. I want to jump from Apples to Pears with one gesture. 78 | Text("Access Rotor by two-finger rotation. Select Headings in the Rotor. Jump directly to the next or previous heading with swipe down and swipe up, respectively.") 79 | 80 | Text("Fruits (h1)").accessibilityHeading(.h1).accessibilityAddTraits([.isHeader]) 81 | Text("Etiam viverra eleifend elit ullamcorper aliquet. Nam sollicitudin lectus nisl, finibus volutpat augue posuere quis.") 82 | Text("Apples (h2)").accessibilityHeading(.h2).accessibilityAddTraits([.isHeader]) 83 | Text("Etiam viverra eleifend elit ullamcorper aliquet. Nam sollicitudin lectus nisl, finibus volutpat augue posuere quis.") 84 | Text("Golden Delicious (h3)").accessibilityHeading(.h3).accessibilityAddTraits([.isHeader]) 85 | Text("Etiam viverra eleifend elit ullamcorper aliquet. Nam sollicitudin lectus nisl, finibus volutpat augue posuere quis.") 86 | Text("Honeycrisp (h3)").accessibilityHeading(.h3).accessibilityAddTraits([.isHeader]) 87 | Text("Etiam viverra eleifend elit ullamcorper aliquet. Nam sollicitudin lectus nisl, finibus volutpat augue posuere quis.") 88 | Text("McIntosh (h3)").accessibilityHeading(.h3).accessibilityAddTraits([.isHeader]) 89 | Text("Etiam viverra eleifend elit ullamcorper aliquet. Nam sollicitudin lectus nisl, finibus volutpat augue posuere quis.") 90 | Text("Pears (h2)").accessibilityHeading(.h2).accessibilityAddTraits([.isHeader]) 91 | Text("Etiam viverra eleifend elit ullamcorper aliquet. Nam sollicitudin lectus nisl, finibus volutpat augue posuere quis.") 92 | Text("Concorde (h3)").accessibilityHeading(.h3).accessibilityAddTraits([.isHeader]) 93 | Text("Etiam viverra eleifend elit ullamcorper aliquet. Nam sollicitudin lectus nisl, finibus volutpat augue posuere quis.") 94 | Text("Green Anjou (h3)").accessibilityHeading(.h3).accessibilityAddTraits([.isHeader]) 95 | Text("Etiam viverra eleifend elit ullamcorper aliquet. Nam sollicitudin lectus nisl, finibus volutpat augue posuere quis.") 96 | Text("Red Bartlett (h3)").accessibilityHeading(.h3).accessibilityAddTraits([.isHeader]) 97 | Text("Etiam viverra eleifend elit ullamcorper aliquet. Nam sollicitudin lectus nisl, finibus volutpat augue posuere quis.") 98 | 99 | Divider() 100 | 101 | // This doesn't seem to be doing anything, but it might be a Swift UI bug? 102 | // https://swiftui-lab.com/bug-watch/ 103 | // > speechAnnouncementsQueued() does nothing - FB9313957 104 | // Asked for help on StackOverflow: 105 | // https://stackoverflow.com/q/77773505 106 | Text("Speech announcement is queued").speechAnnouncementsQueued(true) 107 | Text("Speech announcement is not queued").speechAnnouncementsQueued(false) 108 | 109 | Divider() 110 | 111 | // TODO: I didn't hear anything changed with the different text content types. 112 | // Found nothing on Google, asked on Twitter... 113 | // https://twitter.com/vincevargadev/status/1743302073259532324 114 | // Then asked for help on StackOverflow: 115 | // https://stackoverflow.com/q/77773708 116 | Text("console text content type").accessibilityTextContentType(.console) 117 | Text("file system text content type").accessibilityTextContentType(.fileSystem) 118 | Text("/home/user/documents/photos/2024/vacation/destination/").accessibilityTextContentType(.fileSystem) 119 | Text("/home/user/documents/photos/2024/vacation/destination/").accessibilityTextContentType(.messaging) 120 | Text("messaging text content type").accessibilityTextContentType(.messaging) 121 | Text("narrative text content type").accessibilityTextContentType(.narrative) 122 | Text("plain text content type").accessibilityTextContentType(.plain) 123 | Text("source code content type").accessibilityTextContentType(.sourceCode) 124 | Text("word processing text content type").accessibilityTextContentType(.wordProcessing) 125 | 126 | Divider() 127 | }.padding(.horizontal, 12) 128 | }.navigationTitle("Accessibility") 129 | .environment(\.locale, $localize.wrappedValue ? .init(identifier: "it") : .current) 130 | } 131 | } 132 | 133 | #Preview { 134 | TextAccessibilityPage() 135 | } 136 | 137 | #Preview("Italian") { 138 | TextAccessibilityPage(localize: true) 139 | } 140 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/primitives/text/localization/TextLocalizationPage.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct TextLocalizationPage: View { 4 | @State private var localize: Bool 5 | 6 | init(localize: Bool = false) { 7 | _localize = State(initialValue: localize) 8 | } 9 | 10 | var body: some View { 11 | Toggle(isOn: $localize, label: { 12 | Text("Localize") 13 | }).padding() 14 | 15 | ScrollView { 16 | VStack(spacing: 24) { 17 | /* Localizations are stored in "string catalogs" (.xcstrings files) which are bundled with the application at compile time. Once a catalog is created, Xcode can extract all localazible strings at compile time and place them in the catalog. 18 | In the following examples, "localization tables" refer to the string catalogs. 19 | */ 20 | 21 | Text("Text initialization from string + localization table specification").font(.title2) 22 | 23 | // Text's initializer accepts a LocalizedStringKey, which can be implicitly constructed from a string literal 24 | // The value of the string is used for the development language localization 25 | // Values for other languages are looked up in the default table using the string as key, if not found the value is used as is 26 | Text("pencil") 27 | 28 | // The whole key is used as the key in the localization table, even if it contains multiple paragraphs 29 | Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam efficitur, enim sit amet sodales rhoncus, nisl nisl varius urna, ut pulvinar odio nibh ac lorem. Etiam sit amet viverra erat, venenatis egestas tellus. Curabitur nec sodales lorem, nec tincidunt velit. Duis sed dolor pretium, viverra magna et, vehicula turpis. Cras suscipit venenatis felis, a dignissim nulla consequat sed. Sed non lacus nec velit vestibulum consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam dignissim, ligula id condimentum feugiat, odio mi dapibus risus, quis pulvinar risus elit non ex.\nDonec vitae nisl ornare, efficitur mi vitae, maximus mauris. Curabitur imperdiet commodo tellus, vel pellentesque purus bibendum a. Nunc molestie ut nulla eu accumsan. Proin placerat metus sagittis, elementum augue vel, posuere dui. Aenean viverra enim in leo gravida, vel mollis leo vehicula. Duis aliquet convallis volutpat. Vivamus a nulla velit. Fusce vestibulum libero in cursus feugiat.").font(.system(size: 10)) 30 | 31 | // Markdown-formatted strings can also be localized 32 | Text("_Please visit our [website](https://www.example.com)._") 33 | 34 | // The comment is shown in the localization table, useful for giving context to translators 35 | Text("another pencil", comment: "Translator comment") 36 | 37 | // This comment will be ignored, since the same key was declared earlier. This is valid even if no comment was specified on the previous key. 38 | Text("another pencil", comment: "A different comment") 39 | 40 | // Specify a different table for this localization, this must be the name of an existing string catalog. If the table is not found, the string will not be localized. 41 | Text("pencil", tableName: "LocalizableAlternative") 42 | 43 | // Specify a bundle that contains the localizations table (Bundle.main is the default) 44 | Text("pencil", bundle: .main) 45 | 46 | Divider() 47 | 48 | Text("Other initializers").font(.title2) 49 | 50 | // Variables are not localized by default 51 | let someString = "pen" 52 | Text(someString) 53 | 54 | // Make variables localizable by wrapping them in LocalizableStringKey 55 | Text(LocalizedStringKey(someString)) 56 | 57 | // Do not localize a string literal 58 | Text(verbatim: "pencil") 59 | 60 | // LocalizedStringResource initializer 61 | let resource = LocalizedStringResource("pencil") 62 | Text(resource) 63 | 64 | // String initializer 65 | let string = String(localized: "pencil") 66 | Text(LocalizedStringKey(string)) 67 | 68 | // AttributedString initializer 69 | // Warning: At present, localized AttributedString can only be displayed in the language currently set in the system and cannot be specified as a specific language. Run with the "swift_ui_gallery it" scheme to see it working 70 | let attributed = AttributedString(localized: "pencil") 71 | Text(attributed) 72 | 73 | Divider() 74 | 75 | Text("Automatic grammar agreement").font(.title2) 76 | 77 | // for supported languages, adding the `inflect: true` attribute automatically changes the localized strings to agree with amounts and genders. 78 | let quantity = 2 79 | let size = LocalizedStringResource("large") 80 | let food = LocalizedStringResource("salad") 81 | Text("Add ^[\(quantity) \(size) \(food)](inflect: true) to your order") 82 | } 83 | }.navigationTitle("Localization") 84 | .environment(\.locale, $localize.wrappedValue ? .init(identifier: "it") : .current) 85 | } 86 | } 87 | 88 | #Preview { 89 | TextLocalizationPage() 90 | } 91 | 92 | #Preview("Italian") { 93 | TextLocalizationPage(localize: true) 94 | } 95 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/scaffolds/ScaffoldsPage.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct ScaffoldsPage: View { 4 | var body: some View { 5 | NavigationStack { 6 | List() { 7 | TodoLink(label: "NavigationStack") 8 | }.navigationTitle("Scaffolds") 9 | } 10 | } 11 | } 12 | 13 | struct ScaffoldsPage_Previews: PreviewProvider { 14 | static var previews: some View { 15 | ScaffoldsPage() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/shapes/EllipseExamples.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct EllipsePage: View { 4 | var body: some View { 5 | ScrollView { 6 | VStack(spacing: 20) { 7 | // Ellipse with solid fill 8 | Ellipse() 9 | .fill(Color.blue) 10 | .frame(width: 200, height: 100) 11 | 12 | // Ellipse with gradient fill 13 | Ellipse() 14 | .fill(LinearGradient(gradient: Gradient(colors: [.yellow, .orange]), startPoint: .leading, endPoint: .trailing)) 15 | .frame(width: 200, height: 100) 16 | 17 | // Ellipse with solid stroke 18 | Ellipse() 19 | .stroke(Color.red, lineWidth: 4) 20 | .frame(width: 200, height: 100) 21 | 22 | // Ellipse with gradient stroke 23 | Ellipse() 24 | .stroke(LinearGradient(gradient: Gradient(colors: [.yellow, .blue]), startPoint: .leading, endPoint: .trailing), lineWidth: 14) 25 | .frame(width: 200, height: 100) 26 | }.frame(width: 300) 27 | } 28 | } 29 | } 30 | 31 | struct EllipsePage_Previews: PreviewProvider { 32 | static var previews: some View { 33 | EllipsePage() 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/shapes/RectangleExamples.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct RectanglePage: View { 4 | var body: some View { 5 | ScrollView { 6 | VStack(spacing: 20) { 7 | // Rectangle with solid fill 8 | Rectangle() 9 | .fill(Color.blue) 10 | .frame(width: 200, height: 100) 11 | 12 | // Rectangle with gradient fill 13 | Rectangle() 14 | .fill(LinearGradient(gradient: Gradient(colors: [.yellow, .orange]), startPoint: .leading, endPoint: .trailing)) 15 | .frame(width: 200, height: 100) 16 | 17 | // Rectangle with solid stroke 18 | Rectangle() 19 | .stroke(Color.red, lineWidth: 4) 20 | .frame(width: 200, height: 100) 21 | 22 | // Rectangle with gradient stroke 23 | Rectangle() 24 | .stroke(LinearGradient(gradient: Gradient(colors: [.yellow, .blue]), startPoint: .leading, endPoint: .trailing), lineWidth: 14) 25 | .frame(width: 200, height: 100) 26 | } 27 | } 28 | } 29 | } 30 | 31 | struct RectanglePage_Previews: PreviewProvider { 32 | static var previews: some View { 33 | RectanglePage() 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/shapes/ShapesPage.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | 3 | struct ShapesPage: View { 4 | var body: some View { 5 | NavigationStack { 6 | List() { 7 | NavigationLink { 8 | RectanglePage() 9 | } label: { 10 | Text("Rectangle") 11 | } 12 | 13 | NavigationLink { 14 | EllipsePage() 15 | } label: { 16 | Text("Ellipse") 17 | } 18 | }.navigationTitle("Shapes") 19 | } 20 | } 21 | } 22 | 23 | struct ShapesPage_Previews: PreviewProvider { 24 | static var previews: some View { 25 | ShapesPage() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/swift_ui_galleryApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // swift_ui_galleryApp.swift 3 | // swift_ui_gallery 4 | // 5 | // Created by Matt Carroll on 9/25/23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct swift_ui_galleryApp: App { 12 | var body: some Scene { 13 | WindowGroup { 14 | ContentView() 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_galleryTests/swift_ui_galleryTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // swift_ui_galleryTests.swift 3 | // swift_ui_galleryTests 4 | // 5 | // Created by Matt Carroll on 9/25/23. 6 | // 7 | 8 | import XCTest 9 | @testable import swift_ui_gallery 10 | 11 | final class swift_ui_galleryTests: XCTestCase { 12 | 13 | override func setUpWithError() throws { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDownWithError() throws { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | } 20 | 21 | func testExample() throws { 22 | // This is an example of a functional test case. 23 | // Use XCTAssert and related functions to verify your tests produce the correct results. 24 | // Any test you write for XCTest can be annotated as throws and async. 25 | // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. 26 | // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. 27 | } 28 | 29 | func testPerformanceExample() throws { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_galleryUITests/swift_ui_galleryUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // swift_ui_galleryUITests.swift 3 | // swift_ui_galleryUITests 4 | // 5 | // Created by Matt Carroll on 9/25/23. 6 | // 7 | 8 | import XCTest 9 | 10 | final class swift_ui_galleryUITests: XCTestCase { 11 | 12 | override func setUpWithError() throws { 13 | // Put setup code here. This method is called before the invocation of each test method in the class. 14 | 15 | // In UI tests it is usually best to stop immediately when a failure occurs. 16 | continueAfterFailure = false 17 | 18 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 19 | } 20 | 21 | override func tearDownWithError() throws { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | func testExample() throws { 26 | // UI tests must launch the application that they test. 27 | let app = XCUIApplication() 28 | app.launch() 29 | 30 | // Use XCTAssert and related functions to verify your tests produce the correct results. 31 | } 32 | 33 | func testLaunchPerformance() throws { 34 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { 35 | // This measures how long it takes to launch your application. 36 | measure(metrics: [XCTApplicationLaunchMetric()]) { 37 | XCUIApplication().launch() 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /doc_swift_ui_gallery/swift_ui_gallery/swift_ui_galleryUITests/swift_ui_galleryUITestsLaunchTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // swift_ui_galleryUITestsLaunchTests.swift 3 | // swift_ui_galleryUITests 4 | // 5 | // Created by Matt Carroll on 9/25/23. 6 | // 7 | 8 | import XCTest 9 | 10 | final class swift_ui_galleryUITestsLaunchTests: XCTestCase { 11 | 12 | override class var runsForEachTargetApplicationUIConfiguration: Bool { 13 | true 14 | } 15 | 16 | override func setUpWithError() throws { 17 | continueAfterFailure = false 18 | } 19 | 20 | func testLaunch() throws { 21 | let app = XCUIApplication() 22 | app.launch() 23 | 24 | // Insert steps here to perform after app launch but before taking a screenshot, 25 | // such as logging into a test account or navigating somewhere in the app 26 | 27 | let attachment = XCTAttachment(screenshot: app.screenshot()) 28 | attachment.name = "Launch Screen" 29 | attachment.lifetime = .keepAlways 30 | add(attachment) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Symbolication related 35 | app.*.symbols 36 | 37 | # Obfuscation related 38 | app.*.map.json 39 | 40 | # Android Studio will place build artifacts here 41 | /android/app/debug 42 | /android/app/profile 43 | /android/app/release 44 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "ef9befc9da9f358f301d052e900b36ea3e797565" 8 | channel: "master" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: ef9befc9da9f358f301d052e900b36ea3e797565 17 | base_revision: ef9befc9da9f358f301d052e900b36ea3e797565 18 | - platform: macos 19 | create_revision: ef9befc9da9f358f301d052e900b36ea3e797565 20 | base_revision: ef9befc9da9f358f301d052e900b36ea3e797565 21 | 22 | # User provided section 23 | 24 | # List of Local paths (relative to this file) that should be 25 | # ignored by the migrate tool. 26 | # 27 | # Files that are not part of the templates will be ignored by default. 28 | unmanaged_files: 29 | - 'lib/main.dart' 30 | - 'ios/Runner.xcodeproj/project.pbxproj' 31 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Example app for the Swift UI package 2 | Run this app to play with various pieces of the Swift UI Flutter package. -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at https://dart.dev/lints. 17 | # 18 | # Instead of disabling a lint rule for the entire project in the 19 | # section below, it can also be suppressed for a single line of code 20 | # or a specific dart file by using the `// ignore: name_of_lint` and 21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 22 | # producing the lint. 23 | rules: 24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 26 | 27 | # Additional information about this file can be found at 28 | # https://dart.dev/guides/language/analysis-options 29 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Example 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | example 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | CADisableMinimumFrameDurationOnPhone 45 | 46 | UIApplicationSupportsIndirectInputEvents 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /example/lib/app.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/home_screen.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | 4 | class SwiftUiGalleryApp extends StatelessWidget { 5 | const SwiftUiGalleryApp({super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | // TODO: Replace CupertinoApp with some combination of WidgetsApp and perhaps a SwiftUiScope 10 | return const CupertinoApp( 11 | home: HomeScreen(), 12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /example/lib/collections/collection_examples.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/infrastructure/inventory_page.dart'; 2 | import 'package:example/infrastructure/not_built_yet_page.dart'; 3 | import 'package:flutter/cupertino.dart'; 4 | 5 | class CollectionsPage extends StatelessWidget { 6 | const CollectionsPage({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return const InventoryPage( 11 | title: "Collections", 12 | groups: [ 13 | InventoryGroup( 14 | title: "LISTS", 15 | items: [ 16 | InventoryItem( 17 | label: "No Sections", 18 | pageBuilder: notBuiltYetPageBuilder, 19 | ), 20 | InventoryItem( 21 | label: "With Section (plain)", 22 | pageBuilder: notBuiltYetPageBuilder, 23 | ), 24 | InventoryItem( 25 | label: "With Sections (grouped)", 26 | pageBuilder: notBuiltYetPageBuilder, 27 | ), 28 | InventoryItem( 29 | label: "Empty", 30 | pageBuilder: notBuiltYetPageBuilder, 31 | ), 32 | ], 33 | ), 34 | InventoryGroup( 35 | title: "TABLES", 36 | items: [ 37 | InventoryItem( 38 | label: "Static Rows", 39 | pageBuilder: notBuiltYetPageBuilder, 40 | ), 41 | InventoryItem( 42 | label: "Sortable", 43 | pageBuilder: notBuiltYetPageBuilder, 44 | ), 45 | InventoryItem( 46 | label: "Empty", 47 | pageBuilder: notBuiltYetPageBuilder, 48 | ), 49 | ], 50 | ), 51 | ], 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /example/lib/controls/controls_examples.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/infrastructure/inventory_page.dart'; 2 | import 'package:example/infrastructure/not_built_yet_page.dart'; 3 | import 'package:flutter/cupertino.dart'; 4 | 5 | class ControlsPage extends StatelessWidget { 6 | const ControlsPage({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return const InventoryPage( 11 | title: "Controls", 12 | groups: [ 13 | InventoryGroup( 14 | items: [ 15 | InventoryItem( 16 | label: "Buttons", 17 | pageBuilder: notBuiltYetPageBuilder, 18 | ), 19 | InventoryItem( 20 | label: "Switches", 21 | pageBuilder: notBuiltYetPageBuilder, 22 | ), 23 | ], 24 | ), 25 | ], 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /example/lib/demo_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | 3 | WidgetBuilder createDemo(Widget child) { 4 | return (BuildContext context) { 5 | return DemoScreen(child: child); 6 | }; 7 | } 8 | 9 | class DemoScreen extends StatelessWidget { 10 | const DemoScreen({ 11 | super.key, 12 | required this.child, 13 | }); 14 | 15 | final Widget child; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return CupertinoPageScaffold( 20 | navigationBar: const CupertinoNavigationBar(), 21 | child: SafeArea( 22 | child: child, 23 | ), 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /example/lib/home_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/collections/collection_examples.dart'; 2 | import 'package:example/controls/controls_examples.dart'; 3 | import 'package:example/infrastructure/inventory_page.dart'; 4 | import 'package:example/layouts/layout_examples.dart'; 5 | import 'package:example/motion/motion_examples.dart'; 6 | import 'package:example/primitives/primitive_examples.dart'; 7 | import 'package:example/scaffolds/scaffold_examples.dart'; 8 | import 'package:example/shapes/shapes_examples.dart'; 9 | import 'package:flutter/cupertino.dart'; 10 | 11 | class HomeScreen extends StatefulWidget { 12 | const HomeScreen({super.key}); 13 | 14 | @override 15 | State createState() => _HomeScreenState(); 16 | } 17 | 18 | class _HomeScreenState extends State { 19 | @override 20 | Widget build(BuildContext context) { 21 | // TODO: replace CupertinoTabScaffold with our own Swift UI version 22 | return CupertinoTabScaffold( 23 | tabBuilder: (context, index) { 24 | switch (index) { 25 | case 0: 26 | return const PrimitivesPage(); 27 | case 1: 28 | return const LayoutsPage(); 29 | case 2: 30 | return const CollectionsPage(); 31 | case 3: 32 | return const ScaffoldsPage(); 33 | case 4: 34 | return const OverflowPage(); 35 | default: 36 | throw Exception("Unknown tab index: $index"); 37 | } 38 | }, 39 | tabBar: CupertinoTabBar( 40 | items: const [ 41 | BottomNavigationBarItem( 42 | icon: Icon(CupertinoIcons.square_fill_on_circle_fill), 43 | label: "Primitives", 44 | ), 45 | BottomNavigationBarItem( 46 | icon: Icon(CupertinoIcons.layers_alt), 47 | label: "Layouts", 48 | ), 49 | BottomNavigationBarItem( 50 | icon: Icon(CupertinoIcons.table_fill), 51 | label: "Collections", 52 | ), 53 | BottomNavigationBarItem( 54 | icon: Icon(CupertinoIcons.square_lefthalf_fill), 55 | label: "Scaffolds", 56 | ), 57 | BottomNavigationBarItem( 58 | icon: Icon(CupertinoIcons.ellipsis), 59 | label: "More", 60 | ), 61 | ], 62 | ), 63 | ); 64 | } 65 | } 66 | 67 | class OverflowPage extends StatelessWidget { 68 | const OverflowPage({super.key}); 69 | 70 | @override 71 | Widget build(BuildContext context) { 72 | return InventoryPage( 73 | title: "More", 74 | groups: [ 75 | InventoryGroup( 76 | title: 'ADDITIONAL CATEGORIES', 77 | items: [ 78 | InventoryItem( 79 | label: "Controls", 80 | pageBuilder: (context) => const ControlsPage(), 81 | ), 82 | InventoryItem( 83 | label: "Motion", 84 | pageBuilder: (context) => const MotionPage(), 85 | ), 86 | InventoryItem( 87 | label: "Shapes", 88 | pageBuilder: (context) => const ShapesPage(), 89 | ), 90 | ], 91 | ), 92 | ], 93 | ); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /example/lib/infrastructure/inventory_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | 3 | /// A page, which displays links to UI examples. 4 | /// 5 | /// The purpose of this page is to provide a uniform layout for every inventory page in this 6 | /// gallery app. 7 | /// 8 | /// The links are divided into groups. For example an inventory of "layouts" might include 9 | /// different sections for "VSTACK", "HSTACK", "ZSTACK", and "GRID". 10 | /// 11 | /// When the user taps on a link within one of the groups, the [InventoryItem] associated with 12 | /// that button pushes a new page onto the stack that's built by [InventoryItem.pageBuilder]. 13 | class InventoryPage extends StatelessWidget { 14 | const InventoryPage({ 15 | super.key, 16 | required this.title, 17 | required this.groups, 18 | }); 19 | 20 | final String title; 21 | final List groups; 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return CupertinoPageScaffold( 26 | navigationBar: CupertinoNavigationBar( 27 | middle: Text(title), 28 | ), 29 | child: SizedBox.expand( 30 | child: ColoredBox( 31 | color: CupertinoColors.systemGroupedBackground, 32 | child: SafeArea( 33 | child: SingleChildScrollView( 34 | child: Column( 35 | children: [ 36 | for (final group in groups) 37 | CupertinoListSection.insetGrouped( 38 | header: group.title != null ? Text(group.title!) : null, 39 | children: [ 40 | for (final item in group.items) // 41 | _buildCategoryButton(context, item), 42 | ], 43 | ), 44 | ], 45 | ), 46 | ), 47 | ), 48 | ), 49 | ), 50 | ); 51 | } 52 | 53 | Widget _buildCategoryButton(BuildContext context, InventoryItem item) { 54 | return CupertinoListTile( 55 | title: Text(item.label), 56 | trailing: const CupertinoListTileChevron(), 57 | onTap: () => Navigator.of(context).push(CupertinoPageRoute(builder: item.pageBuilder)), 58 | ); 59 | } 60 | } 61 | 62 | class InventoryGroup { 63 | const InventoryGroup({ 64 | this.title, 65 | required this.items, 66 | }); 67 | 68 | final String? title; 69 | final List items; 70 | } 71 | 72 | class InventoryItem { 73 | const InventoryItem({ 74 | required this.label, 75 | required this.pageBuilder, 76 | }); 77 | 78 | final String label; 79 | final WidgetBuilder pageBuilder; 80 | } 81 | -------------------------------------------------------------------------------- /example/lib/infrastructure/not_built_yet_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | 3 | /// A page, which informs the user that whatever content is supposed to appear on this 4 | /// page hasn't been built yet. 5 | /// 6 | /// This page is a placeholder for any examples or content that we know we need, but which 7 | /// hasn't been implemented yet. 8 | class NotBuiltYetPage extends StatelessWidget { 9 | const NotBuiltYetPage({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return CupertinoPageScaffold( 14 | navigationBar: const CupertinoNavigationBar(), 15 | child: Center( 16 | child: Container( 17 | width: double.infinity, 18 | padding: const EdgeInsets.symmetric(horizontal: 48), 19 | child: const Text(_contentText), 20 | ), 21 | ), 22 | ); 23 | } 24 | 25 | static const _contentText = ''' 26 | This page doesn't exist yet. Would you like to help build it? 27 | 28 | Our typical process is: 29 | 30 | 1. Investigate Swift UI API docs. 31 | 2. Create Swift UI examples in iOS app. 32 | 3. Implement Flutter version. 33 | 4. Add Flutter version to this app. 34 | 35 | Follow @FlutterBounties on Twitter to stay up to date. 36 | '''; 37 | } 38 | 39 | Widget notBuiltYetPageBuilder(BuildContext context) => const NotBuiltYetPage(); 40 | -------------------------------------------------------------------------------- /example/lib/layouts/hstack.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'package:swift_ui/swift_ui.dart'; 3 | 4 | class HStackTopAlignedDemo extends StatelessWidget { 5 | const HStackTopAlignedDemo({ 6 | super.key, 7 | }); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return const HStack( 12 | alignment: VerticalAlignment.top, 13 | [ 14 | Text("First\nSecond"), 15 | Text("Third"), 16 | Text("Fourth\nFifth\nSixth"), 17 | ], 18 | ); 19 | } 20 | } 21 | 22 | class HStackCenterAlignedDemo extends StatelessWidget { 23 | const HStackCenterAlignedDemo({ 24 | super.key, 25 | }); 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | return const HStack( 30 | alignment: VerticalAlignment.center, 31 | [ 32 | Text("First\nSecond"), 33 | Text("Third"), 34 | Text("Fourth\nFifth\nSixth"), 35 | ], 36 | ); 37 | } 38 | } 39 | 40 | class HStackBottomAlignedDemo extends StatelessWidget { 41 | const HStackBottomAlignedDemo({ 42 | super.key, 43 | }); 44 | 45 | @override 46 | Widget build(BuildContext context) { 47 | return const HStack( 48 | alignment: VerticalAlignment.bottom, 49 | [ 50 | Text("First\nSecond"), 51 | Text("Third"), 52 | Text("Fourth\nFifth\nSixth"), 53 | ], 54 | ); 55 | } 56 | } 57 | 58 | class HStackExceedsAvailableSpaceDemo extends StatelessWidget { 59 | const HStackExceedsAvailableSpaceDemo({ 60 | super.key, 61 | }); 62 | 63 | @override 64 | Widget build(BuildContext context) { 65 | return HStack([ 66 | // TODO: Replace these with Rectangle() widgets 67 | Container( 68 | width: 100, 69 | height: double.infinity, 70 | color: Colors.red, 71 | ), 72 | Container( 73 | width: 100, 74 | height: double.infinity, 75 | color: Colors.green, 76 | ), 77 | Container( 78 | width: 100, 79 | height: double.infinity, 80 | color: Colors.blue, 81 | ), 82 | Container( 83 | width: 100, 84 | height: double.infinity, 85 | color: Colors.purple, 86 | ), 87 | Container( 88 | width: 100, 89 | height: double.infinity, 90 | color: Colors.orange, 91 | ), 92 | Container( 93 | width: 100, 94 | height: double.infinity, 95 | color: Colors.cyan, 96 | ), 97 | ]); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /example/lib/layouts/layout_examples.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/demo_screen.dart'; 2 | import 'package:example/infrastructure/inventory_page.dart'; 3 | import 'package:example/infrastructure/not_built_yet_page.dart'; 4 | import 'package:example/layouts/hstack.dart'; 5 | import 'package:example/layouts/vstack.dart'; 6 | import 'package:flutter/cupertino.dart'; 7 | 8 | class LayoutsPage extends StatelessWidget { 9 | const LayoutsPage({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return InventoryPage( 14 | title: "Layouts", 15 | groups: [ 16 | InventoryGroup( 17 | title: "VSTACK", 18 | items: [ 19 | InventoryItem( 20 | label: "Leading Aligned", 21 | pageBuilder: createDemo( 22 | const VStackLeadingAlignedDemo(), 23 | ), 24 | ), 25 | InventoryItem( 26 | label: "Center Aligned", 27 | pageBuilder: createDemo( 28 | const VStackCenterAlignedDemo(), 29 | ), 30 | ), 31 | InventoryItem( 32 | label: "Trailing Aligned", 33 | pageBuilder: createDemo( 34 | const VStackTrailingAlignedDemo(), 35 | ), 36 | ), 37 | InventoryItem( 38 | label: "Exceeds Available Space", 39 | pageBuilder: createDemo( 40 | const VStackExceedsAvailableSpaceDemo(), 41 | ), 42 | ), 43 | ], 44 | ), 45 | InventoryGroup( 46 | title: "HSTACK", 47 | items: [ 48 | InventoryItem( 49 | label: "Top Aligned", 50 | pageBuilder: createDemo( 51 | const HStackTopAlignedDemo(), 52 | ), 53 | ), 54 | InventoryItem( 55 | label: "Center Aligned", 56 | pageBuilder: createDemo( 57 | const HStackCenterAlignedDemo(), 58 | ), 59 | ), 60 | InventoryItem( 61 | label: "Bottom Aligned", 62 | pageBuilder: createDemo( 63 | const HStackBottomAlignedDemo(), 64 | ), 65 | ), 66 | InventoryItem( 67 | label: "Exceeds Available Space", 68 | pageBuilder: createDemo( 69 | const HStackExceedsAvailableSpaceDemo(), 70 | ), 71 | ), 72 | ], 73 | ), 74 | const InventoryGroup( 75 | title: "ZSTACK", 76 | items: [ 77 | InventoryItem( 78 | label: "Example", 79 | pageBuilder: notBuiltYetPageBuilder, 80 | ), 81 | ], 82 | ), 83 | const InventoryGroup( 84 | title: "GRID", 85 | items: [ 86 | InventoryItem( 87 | label: "Fits Available Space", 88 | pageBuilder: notBuiltYetPageBuilder, 89 | ), 90 | InventoryItem( 91 | label: "Exceeds Available Space", 92 | pageBuilder: notBuiltYetPageBuilder, 93 | ), 94 | ], 95 | ), 96 | ], 97 | ); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /example/lib/layouts/vstack.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'package:swift_ui/swift_ui.dart'; 3 | 4 | class VStackLeadingAlignedDemo extends StatelessWidget { 5 | const VStackLeadingAlignedDemo({ 6 | super.key, 7 | }); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return const VStack( 12 | alignment: HorizontalAlignment.leading, 13 | [ 14 | Text("First"), 15 | Text("Second"), 16 | Text("Third"), 17 | ], 18 | ); 19 | } 20 | } 21 | 22 | class VStackCenterAlignedDemo extends StatelessWidget { 23 | const VStackCenterAlignedDemo({ 24 | super.key, 25 | }); 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | return const VStack( 30 | alignment: HorizontalAlignment.center, 31 | [ 32 | Text("First"), 33 | Text("Second"), 34 | Text("Third"), 35 | ], 36 | ); 37 | } 38 | } 39 | 40 | class VStackTrailingAlignedDemo extends StatelessWidget { 41 | const VStackTrailingAlignedDemo({ 42 | super.key, 43 | }); 44 | 45 | @override 46 | Widget build(BuildContext context) { 47 | return const VStack( 48 | alignment: HorizontalAlignment.trailing, 49 | [ 50 | Text("First"), 51 | Text("Second"), 52 | Text("Third"), 53 | ], 54 | ); 55 | } 56 | } 57 | 58 | class VStackExceedsAvailableSpaceDemo extends StatelessWidget { 59 | const VStackExceedsAvailableSpaceDemo({ 60 | super.key, 61 | }); 62 | 63 | @override 64 | Widget build(BuildContext context) { 65 | return VStack([ 66 | // TODO: Replace these with Rectangle() widgets 67 | Container( 68 | width: double.infinity, 69 | height: 200, 70 | color: Colors.red, 71 | ), 72 | Container( 73 | width: double.infinity, 74 | height: 200, 75 | color: Colors.green, 76 | ), 77 | Container( 78 | width: double.infinity, 79 | height: 200, 80 | color: Colors.blue, 81 | ), 82 | Container( 83 | width: double.infinity, 84 | height: 200, 85 | color: Colors.purple, 86 | ), 87 | Container( 88 | width: double.infinity, 89 | height: 200, 90 | color: Colors.orange, 91 | ), 92 | Container( 93 | width: double.infinity, 94 | height: 200, 95 | color: Colors.cyan, 96 | ), 97 | ]); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/app.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:swift_ui/swift_ui.dart'; 4 | 5 | void main() { 6 | runApp(const SwiftUiGalleryApp()); 7 | } 8 | 9 | class NavigationView extends StatefulWidget { 10 | static NavigationViewState of(BuildContext context) { 11 | return context.findAncestorStateOfType()!; 12 | } 13 | 14 | const NavigationView({ 15 | super.key, 16 | required this.child, 17 | }); 18 | 19 | final Widget child; 20 | 21 | @override 22 | State createState() => NavigationViewState(); 23 | } 24 | 25 | class NavigationViewState extends State { 26 | String? title; 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return Column( 31 | children: [ 32 | const SizedBox(height: 100), 33 | Container( 34 | height: 54, 35 | color: const Color(0xFFFFFF00), 36 | child: Center( 37 | // child: LateBoundBuilder( 38 | child: LateBoundBuild( 39 | builder: (context) { 40 | final title = NavigationView.of(context).title ?? "DEFAULT TILE"; 41 | return Text(title); 42 | }, 43 | ), 44 | ), 45 | ), 46 | Expanded( 47 | child: widget.child, 48 | ), 49 | ], 50 | ); 51 | } 52 | } 53 | 54 | class NavigationBarTitle extends StatefulWidget { 55 | const NavigationBarTitle( 56 | this.title, { 57 | super.key, 58 | this.child, 59 | }); 60 | 61 | final String title; 62 | final Widget? child; 63 | 64 | @override 65 | State createState() => _NavigationBarTitleState(); 66 | } 67 | 68 | class _NavigationBarTitleState extends State { 69 | @override 70 | void didChangeDependencies() { 71 | super.didChangeDependencies(); 72 | 73 | NavigationView.of(context).title = widget.title; 74 | } 75 | 76 | @override 77 | void didUpdateWidget(NavigationBarTitle oldWidget) { 78 | super.didUpdateWidget(oldWidget); 79 | 80 | NavigationView.of(context).title = widget.title; 81 | } 82 | 83 | @override 84 | Widget build(BuildContext context) { 85 | return widget.child ?? const SizedBox(); 86 | } 87 | } 88 | 89 | class LateBoundBuilder extends StatelessWidget { 90 | const LateBoundBuilder({ 91 | super.key, 92 | required this.builder, 93 | }); 94 | 95 | final WidgetBuilder builder; 96 | 97 | @override 98 | Widget build(BuildContext context) { 99 | return LayoutBuilder(builder: (context, _) => builder(context)); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /example/lib/motion/motion_examples.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/infrastructure/inventory_page.dart'; 2 | import 'package:example/infrastructure/not_built_yet_page.dart'; 3 | import 'package:flutter/cupertino.dart'; 4 | 5 | class MotionPage extends StatelessWidget { 6 | const MotionPage({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return const InventoryPage( 11 | title: "Motion", 12 | groups: [ 13 | InventoryGroup( 14 | items: [ 15 | InventoryItem( 16 | label: "Springs", 17 | pageBuilder: notBuiltYetPageBuilder, 18 | ), 19 | ], 20 | ), 21 | ], 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /example/lib/primitives/primitive_examples.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/infrastructure/inventory_page.dart'; 2 | import 'package:example/infrastructure/not_built_yet_page.dart'; 3 | import 'package:flutter/cupertino.dart'; 4 | 5 | class PrimitivesPage extends StatelessWidget { 6 | const PrimitivesPage({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return const InventoryPage( 11 | title: "Primitives", 12 | groups: [ 13 | InventoryGroup( 14 | title: "TEXT", 15 | items: [ 16 | InventoryItem( 17 | label: "All Styles", 18 | pageBuilder: notBuiltYetPageBuilder, 19 | ), 20 | ], 21 | ), 22 | InventoryGroup( 23 | title: "IMAGE", 24 | items: [ 25 | InventoryItem( 26 | label: "Natural Size", 27 | pageBuilder: notBuiltYetPageBuilder, 28 | ), 29 | InventoryItem( 30 | label: "Shrunk Smaller", 31 | pageBuilder: notBuiltYetPageBuilder, 32 | ), 33 | InventoryItem( 34 | label: "Expanded Larger", 35 | pageBuilder: notBuiltYetPageBuilder, 36 | ), 37 | InventoryItem( 38 | label: "Shape Clipped", 39 | pageBuilder: notBuiltYetPageBuilder, 40 | ), 41 | InventoryItem( 42 | label: "From Network", 43 | pageBuilder: notBuiltYetPageBuilder, 44 | ), 45 | ], 46 | ), 47 | InventoryGroup( 48 | title: "FRAME", 49 | items: [ 50 | InventoryItem( 51 | label: "Static Size", 52 | pageBuilder: notBuiltYetPageBuilder, 53 | ), 54 | ], 55 | ), 56 | ], 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /example/lib/scaffolds/scaffold_examples.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/infrastructure/inventory_page.dart'; 2 | import 'package:example/infrastructure/not_built_yet_page.dart'; 3 | import 'package:flutter/cupertino.dart'; 4 | 5 | class ScaffoldsPage extends StatelessWidget { 6 | const ScaffoldsPage({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return const InventoryPage( 11 | title: "Scaffolds", 12 | groups: [ 13 | InventoryGroup( 14 | title: "Navigation", 15 | items: [ 16 | InventoryItem( 17 | label: "NavigationStack", 18 | pageBuilder: notBuiltYetPageBuilder, 19 | ), 20 | ], 21 | ), 22 | ], 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /example/lib/shapes/ellipse.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'package:swift_ui/swift_ui.dart'; 3 | 4 | class EllipseDemo extends StatelessWidget { 5 | const EllipseDemo({ 6 | super.key, 7 | }); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return const VStack( 12 | [ 13 | // Ellipse with solid fill 14 | Frame( 15 | width: 200, 16 | height: 100, 17 | child: Ellipse( 18 | fillColor: Colors.blue, 19 | ), 20 | ), 21 | 22 | // Ellipse with gradient fill 23 | Frame( 24 | width: 200, 25 | height: 100, 26 | child: Ellipse( 27 | fillGradient: LinearGradient( 28 | colors: [Colors.yellow, Colors.orange], 29 | ), 30 | ), 31 | ), 32 | 33 | // Ellipse with solid stroke 34 | Frame( 35 | width: 200, 36 | height: 100, 37 | child: Ellipse( 38 | strokeColor: Colors.red, 39 | strokeLineWidth: 4.0, 40 | ), 41 | ), 42 | 43 | // Ellipse with gradient stroke 44 | Frame( 45 | width: 200, 46 | height: 100, 47 | child: Ellipse( 48 | strokeGradient: LinearGradient( 49 | colors: [Colors.yellow, Colors.blue], 50 | ), 51 | strokeLineWidth: 14.0, 52 | ), 53 | ), 54 | ], 55 | spacing: 20, 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /example/lib/shapes/rectangle.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'package:swift_ui/swift_ui.dart'; 3 | 4 | class RectangleDemo extends StatelessWidget { 5 | const RectangleDemo({ 6 | super.key, 7 | }); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return const VStack( 12 | [ 13 | // Rectangle with solid fill 14 | Frame( 15 | width: 200, 16 | height: 100, 17 | child: Rectangle( 18 | fillColor: Colors.blue, 19 | ), 20 | ), 21 | 22 | // Rectangle with gradient fill 23 | Frame( 24 | width: 200, 25 | height: 100, 26 | child: Rectangle( 27 | fillGradient: LinearGradient( 28 | colors: [Colors.yellow, Colors.orange], 29 | ), 30 | ), 31 | ), 32 | 33 | // Rectangle with solid stroke 34 | Frame( 35 | width: 200, 36 | height: 100, 37 | child: Rectangle( 38 | strokeColor: Colors.red, 39 | strokeLineWidth: 4.0, 40 | ), 41 | ), 42 | 43 | // Rectangle with gradient stroke 44 | Frame( 45 | width: 200, 46 | height: 100, 47 | child: Rectangle( 48 | strokeGradient: LinearGradient( 49 | colors: [Colors.yellow, Colors.blue], 50 | ), 51 | strokeLineWidth: 14.0, 52 | ), 53 | ), 54 | ], 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /example/lib/shapes/shapes_examples.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/demo_screen.dart'; 2 | import 'package:example/infrastructure/inventory_page.dart'; 3 | import 'package:example/shapes/rectangle.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | 6 | import 'ellipse.dart'; 7 | 8 | class ShapesPage extends StatelessWidget { 9 | const ShapesPage({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return InventoryPage( 14 | title: "Shapes", 15 | groups: [ 16 | InventoryGroup( 17 | items: [ 18 | InventoryItem( 19 | label: "Rectangle", 20 | pageBuilder: createDemo(const RectangleDemo()), 21 | ), 22 | InventoryItem( 23 | label: "Ellipse", 24 | pageBuilder: createDemo(const EllipseDemo()), 25 | ), 26 | ], 27 | ), 28 | ], 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /example/macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | 9 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 10 | } 11 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /example/macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = example 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2023 com.example. All rights reserved. 15 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /example/macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /example/macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /example/macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import FlutterMacOS 2 | import Cocoa 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "2.11.0" 12 | boolean_selector: 13 | dependency: transitive 14 | description: 15 | name: boolean_selector 16 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "2.1.1" 20 | characters: 21 | dependency: transitive 22 | description: 23 | name: characters 24 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "1.3.0" 28 | clock: 29 | dependency: transitive 30 | description: 31 | name: clock 32 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "1.1.1" 36 | collection: 37 | dependency: transitive 38 | description: 39 | name: collection 40 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "1.18.0" 44 | cupertino_icons: 45 | dependency: "direct main" 46 | description: 47 | name: cupertino_icons 48 | sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "1.0.6" 52 | fake_async: 53 | dependency: transitive 54 | description: 55 | name: fake_async 56 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 57 | url: "https://pub.dev" 58 | source: hosted 59 | version: "1.3.1" 60 | flutter: 61 | dependency: "direct main" 62 | description: flutter 63 | source: sdk 64 | version: "0.0.0" 65 | flutter_lints: 66 | dependency: "direct dev" 67 | description: 68 | name: flutter_lints 69 | sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 70 | url: "https://pub.dev" 71 | source: hosted 72 | version: "2.0.3" 73 | flutter_test: 74 | dependency: "direct dev" 75 | description: flutter 76 | source: sdk 77 | version: "0.0.0" 78 | leak_tracker: 79 | dependency: transitive 80 | description: 81 | name: leak_tracker 82 | sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" 83 | url: "https://pub.dev" 84 | source: hosted 85 | version: "10.0.4" 86 | leak_tracker_flutter_testing: 87 | dependency: transitive 88 | description: 89 | name: leak_tracker_flutter_testing 90 | sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" 91 | url: "https://pub.dev" 92 | source: hosted 93 | version: "3.0.3" 94 | leak_tracker_testing: 95 | dependency: transitive 96 | description: 97 | name: leak_tracker_testing 98 | sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" 99 | url: "https://pub.dev" 100 | source: hosted 101 | version: "3.0.1" 102 | lints: 103 | dependency: transitive 104 | description: 105 | name: lints 106 | sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" 107 | url: "https://pub.dev" 108 | source: hosted 109 | version: "2.1.1" 110 | matcher: 111 | dependency: transitive 112 | description: 113 | name: matcher 114 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb 115 | url: "https://pub.dev" 116 | source: hosted 117 | version: "0.12.16+1" 118 | material_color_utilities: 119 | dependency: transitive 120 | description: 121 | name: material_color_utilities 122 | sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" 123 | url: "https://pub.dev" 124 | source: hosted 125 | version: "0.8.0" 126 | meta: 127 | dependency: transitive 128 | description: 129 | name: meta 130 | sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" 131 | url: "https://pub.dev" 132 | source: hosted 133 | version: "1.12.0" 134 | path: 135 | dependency: transitive 136 | description: 137 | name: path 138 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" 139 | url: "https://pub.dev" 140 | source: hosted 141 | version: "1.9.0" 142 | sky_engine: 143 | dependency: transitive 144 | description: flutter 145 | source: sdk 146 | version: "0.0.99" 147 | source_span: 148 | dependency: transitive 149 | description: 150 | name: source_span 151 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 152 | url: "https://pub.dev" 153 | source: hosted 154 | version: "1.10.0" 155 | stack_trace: 156 | dependency: transitive 157 | description: 158 | name: stack_trace 159 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" 160 | url: "https://pub.dev" 161 | source: hosted 162 | version: "1.11.1" 163 | stream_channel: 164 | dependency: transitive 165 | description: 166 | name: stream_channel 167 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 168 | url: "https://pub.dev" 169 | source: hosted 170 | version: "2.1.2" 171 | string_scanner: 172 | dependency: transitive 173 | description: 174 | name: string_scanner 175 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 176 | url: "https://pub.dev" 177 | source: hosted 178 | version: "1.2.0" 179 | swift_ui: 180 | dependency: "direct main" 181 | description: 182 | path: ".." 183 | relative: true 184 | source: path 185 | version: "0.0.1" 186 | term_glyph: 187 | dependency: transitive 188 | description: 189 | name: term_glyph 190 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 191 | url: "https://pub.dev" 192 | source: hosted 193 | version: "1.2.1" 194 | test_api: 195 | dependency: transitive 196 | description: 197 | name: test_api 198 | sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" 199 | url: "https://pub.dev" 200 | source: hosted 201 | version: "0.7.0" 202 | vector_math: 203 | dependency: transitive 204 | description: 205 | name: vector_math 206 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 207 | url: "https://pub.dev" 208 | source: hosted 209 | version: "2.1.4" 210 | vm_service: 211 | dependency: transitive 212 | description: 213 | name: vm_service 214 | sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" 215 | url: "https://pub.dev" 216 | source: hosted 217 | version: "14.2.1" 218 | sdks: 219 | dart: ">=3.3.0 <4.0.0" 220 | flutter: ">=3.18.0-18.0.pre.54" 221 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: "Swift UI Example App" 3 | publish_to: 'none' 4 | 5 | version: 1.0.0+1 6 | 7 | environment: 8 | sdk: '>=3.2.0-131.0.dev <4.0.0' 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | 14 | swift_ui: 15 | path: ../ 16 | 17 | cupertino_icons: ^1.0.2 18 | 19 | dev_dependencies: 20 | flutter_lints: ^2.0.0 21 | flutter_test: 22 | sdk: flutter 23 | 24 | flutter: 25 | 26 | # The following line ensures that the Material Icons font is 27 | # included with your application, so that you can use the icons in 28 | # the material Icons class. 29 | uses-material-design: true 30 | 31 | # To add assets to your application, add an assets section, like this: 32 | # assets: 33 | # - images/a_dot_burr.jpeg 34 | # - images/a_dot_ham.jpeg 35 | 36 | # An image asset can refer to one or more resolution-specific "variants", see 37 | # https://flutter.dev/assets-and-images/#resolution-aware 38 | 39 | # For details regarding adding assets from package dependencies, see 40 | # https://flutter.dev/assets-and-images/#from-packages 41 | 42 | # To add custom fonts to your application, add a fonts section here, 43 | # in this "flutter" section. Each entry in this list should have a 44 | # "family" key with the font family name, and a "fonts" key with a 45 | # list giving the asset and other descriptors for the font. For 46 | # example: 47 | # fonts: 48 | # - family: Schyler 49 | # fonts: 50 | # - asset: fonts/Schyler-Regular.ttf 51 | # - asset: fonts/Schyler-Italic.ttf 52 | # style: italic 53 | # - family: Trajan Pro 54 | # fonts: 55 | # - asset: fonts/TrajanPro.ttf 56 | # - asset: fonts/TrajanPro_Bold.ttf 57 | # weight: 700 58 | # 59 | # For details regarding fonts from package dependencies, 60 | # see https://flutter.dev/custom-fonts/#from-packages 61 | -------------------------------------------------------------------------------- /lib/src/controls_and_indicators/button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | // TODO: 4 | class Button extends StatelessWidget { 5 | const Button({super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return const Placeholder(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/src/drawing_and_graphics/border.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart' as widgets; 2 | 3 | class Border extends widgets.StatelessWidget { 4 | const Border( 5 | this.color, { 6 | super.key, 7 | this.width = 1, 8 | required this.child, 9 | }); 10 | 11 | final widgets.Color color; 12 | final double width; 13 | final widgets.Widget child; 14 | 15 | @override 16 | widgets.Widget build(widgets.BuildContext context) { 17 | return widgets.DecoratedBox( 18 | decoration: widgets.BoxDecoration( 19 | border: widgets.Border.all(color: color, width: width), 20 | ), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/src/infrastructure/colors.dart: -------------------------------------------------------------------------------- 1 | export 'package:flutter/material.dart' show Colors, Color; 2 | -------------------------------------------------------------------------------- /lib/src/layout/alignment.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/painting.dart' as painting; 2 | 3 | enum HorizontalAlignment { 4 | /// Alignment with the "leading" or "starting" edge of a bounding space. 5 | /// 6 | /// In a left-to-right layout context, the leading edge is the left edge. 7 | leading, 8 | 9 | /// Alignment that's equidistant from the left and right edges of a bounding space. 10 | center, 11 | 12 | /// Alignment with the "trailing" or "ending" edge of a bounding space. 13 | /// 14 | /// In a left-to-right layout context, the trailing edge is the right edge. 15 | trailing, 16 | 17 | /// Alignment with the leading edge of a list separator - this alignment is only 18 | /// relevant when a widget is displayed within a list that has dividers. 19 | listRowSeparatorLeading, 20 | 21 | /// Alignment with the trailing edge of a list separator - this alignment is only 22 | /// relevant when a widget is displayed within a list that has dividers. 23 | listRowSeparatorTrailing, 24 | } 25 | 26 | enum VerticalAlignment { 27 | /// Alignment with the top of a bounding space. 28 | top, 29 | 30 | /// Alignment that's equidistant from the top and bottom of a bounding space. 31 | center, 32 | 33 | /// Alignment with the bottom of a bounding space. 34 | bottom, 35 | 36 | /// Alignment with the baseline of the first line of text within another widget. 37 | firstTextBaseline, 38 | 39 | /// Alignment with the baseline of the last line of text within another widget. 40 | lastTextBaseline, 41 | } 42 | 43 | enum Alignment { 44 | /// Alignment that's equidistant from all edges of the bounding space. 45 | center(horizontal: HorizontalAlignment.center, vertical: VerticalAlignment.center), 46 | 47 | /// Alignment with the "leading" (or "starting") edge of the bounding space. 48 | /// 49 | /// In a left-to-right layout context, this is the left-most edge. 50 | leading(horizontal: HorizontalAlignment.leading, vertical: VerticalAlignment.center), 51 | 52 | /// Alignment with the "trailing" (or "ending") edge of the bounding space. 53 | /// 54 | /// In a left-to-right layout context, this is the right-most edge. 55 | trailing(horizontal: HorizontalAlignment.trailing, vertical: VerticalAlignment.center), 56 | 57 | /// Alignment with the top edge of the bounding space. 58 | top(horizontal: HorizontalAlignment.center, vertical: VerticalAlignment.top), 59 | 60 | /// Alignment with the bottom edge of the bounding space. 61 | bottom(horizontal: HorizontalAlignment.center, vertical: VerticalAlignment.bottom), 62 | 63 | /// Alignment with the top and "leading" (or "starting") edges of the bounding space. 64 | /// 65 | /// In a left-to-right layout context, this is the top-left corner. 66 | topLeading(horizontal: HorizontalAlignment.leading, vertical: VerticalAlignment.top), 67 | 68 | /// Alignment with the top and "trailing" (or "ending") edges of the bounding space. 69 | /// 70 | /// In a left-to-right layout context, this is the top-right corner. 71 | topTrailing(horizontal: HorizontalAlignment.trailing, vertical: VerticalAlignment.top), 72 | 73 | /// Alignment with the bottom and "leading" (or "starting") edges of the bounding space. 74 | /// 75 | /// In a left-to-right layout context, this is the bottom-left corner. 76 | bottomLeading(horizontal: HorizontalAlignment.leading, vertical: VerticalAlignment.bottom), 77 | 78 | /// Alignment with the bottom and "trailing" (or "ending") edges of the bounding space. 79 | /// 80 | /// In a left-to-right layout context, this is the bottom-right corner. 81 | bottomTrailing(horizontal: HorizontalAlignment.trailing, vertical: VerticalAlignment.bottom), 82 | 83 | /// Alignment with the top-most baseline. 84 | centerFirstTextBaseline(horizontal: HorizontalAlignment.center, vertical: VerticalAlignment.firstTextBaseline), 85 | 86 | /// Alignment with the bottom-most baseline. 87 | centerLastTextBaseline(horizontal: HorizontalAlignment.center, vertical: VerticalAlignment.lastTextBaseline), 88 | 89 | /// Alignment with the "leading" (or "starting") edge of the top-most baseline. 90 | /// 91 | /// In a left-to-right layout context, this is the left edge of the first line of text. 92 | leadingFirstTextBaseline(horizontal: HorizontalAlignment.leading, vertical: VerticalAlignment.firstTextBaseline), 93 | 94 | /// Alignment with the "trailing" (or "ending") edge of the top-most baseline. 95 | /// 96 | /// In a left-to-right layout context, this is the right edge of the first line of text. 97 | trailingFirstTextBaseline(horizontal: HorizontalAlignment.trailing, vertical: VerticalAlignment.firstTextBaseline), 98 | 99 | /// Alignment with the "leading" (or "starting") edge of the bottom-most baseline. 100 | /// 101 | /// In a left-to-right layout context, this is the left edge of the last line of text. 102 | leadingLastTextBaseline(horizontal: HorizontalAlignment.leading, vertical: VerticalAlignment.lastTextBaseline), 103 | 104 | /// Alignment with the "trailing" (or "ending") edge of the bottom-most baseline. 105 | /// 106 | /// In a left-to-right layout context, this is the right edge of the last line of text. 107 | trailingLastTextBaseline(horizontal: HorizontalAlignment.trailing, vertical: VerticalAlignment.lastTextBaseline); 108 | 109 | const Alignment({ 110 | required this.horizontal, 111 | required this.vertical, 112 | }); 113 | 114 | /// The horizontal alignment of the widget within its bounding space. 115 | final HorizontalAlignment horizontal; 116 | 117 | /// The vertical alignment of the widget within its bounding space. 118 | final VerticalAlignment vertical; 119 | 120 | painting.AlignmentGeometry toFlutter(painting.TextDirection direction) { 121 | final isLtr = direction == painting.TextDirection.ltr; 122 | switch (this) { 123 | case Alignment.center: 124 | return painting.Alignment.center; 125 | case Alignment.leading: 126 | return isLtr ? painting.Alignment.centerLeft : painting.Alignment.centerRight; 127 | case Alignment.trailing: 128 | return isLtr ? painting.Alignment.centerRight : painting.Alignment.centerLeft; 129 | case Alignment.top: 130 | return painting.Alignment.topCenter; 131 | case Alignment.bottom: 132 | return painting.Alignment.bottomCenter; 133 | case Alignment.topLeading: 134 | return isLtr ? painting.Alignment.topLeft : painting.Alignment.topRight; 135 | case Alignment.topTrailing: 136 | return isLtr ? painting.Alignment.topRight : painting.Alignment.topLeft; 137 | case Alignment.bottomLeading: 138 | return isLtr ? painting.Alignment.bottomLeft : painting.Alignment.bottomRight; 139 | case Alignment.bottomTrailing: 140 | return isLtr ? painting.Alignment.bottomRight : painting.Alignment.bottomLeft; 141 | case Alignment.centerFirstTextBaseline: 142 | case Alignment.centerLastTextBaseline: 143 | case Alignment.leadingFirstTextBaseline: 144 | case Alignment.trailingFirstTextBaseline: 145 | case Alignment.leadingLastTextBaseline: 146 | case Alignment.trailingLastTextBaseline: 147 | throw Exception("swift_ui does not yet support this alignment: $this"); 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /lib/src/layout/frame.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart' hide Alignment; 2 | 3 | import 'alignment.dart'; 4 | 5 | /// A layout widget that aligns its child within a fixed-size rectangle. 6 | class Frame extends StatelessWidget { 7 | const Frame({ 8 | super.key, 9 | this.width, 10 | this.height, 11 | this.alignment = Alignment.center, 12 | required this.child, 13 | }); 14 | 15 | /// The width of the frame. 16 | /// 17 | /// If `null`, the intrinsic width of the child is used. 18 | final double? width; 19 | 20 | /// The height of the frame. 21 | /// 22 | /// If `null`, the intrinsic height of the child is used. 23 | final double? height; 24 | 25 | /// The alignment of the child within the frame. 26 | /// 27 | /// See [Alignment] for more info about the available alignments. 28 | final Alignment alignment; 29 | 30 | /// The child widget to be displayed within the frame. 31 | final Widget child; 32 | 33 | @override 34 | Widget build(BuildContext context) { 35 | if (width == null && height == null) { 36 | return child; 37 | } 38 | 39 | final textDirection = Directionality.of(context); 40 | 41 | if (width == null) { 42 | // Use the intrinsic width of the child. 43 | return SizedBox( 44 | height: height, 45 | child: Align( 46 | alignment: alignment.toFlutter(textDirection), 47 | child: IntrinsicWidth( 48 | child: child, 49 | ), 50 | ), 51 | ); 52 | } 53 | 54 | if (height == null) { 55 | // Use the intrinsic height of the child. 56 | return SizedBox( 57 | width: width, 58 | child: Align( 59 | alignment: alignment.toFlutter(textDirection), 60 | child: IntrinsicHeight( 61 | child: child, 62 | ), 63 | ), 64 | ); 65 | } 66 | 67 | return SizedBox( 68 | width: width, 69 | height: height, 70 | child: Align( 71 | alignment: alignment.toFlutter(textDirection), 72 | child: child, 73 | ), 74 | ); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/src/layout/grid.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | // TODO: 4 | class Grid extends StatelessWidget { 5 | const Grid({super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return const Placeholder(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/src/layout/hstack.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | import 'alignment.dart' hide Alignment; 4 | 5 | /// A layout widget that displays its children horizontally, like a row. 6 | /// 7 | /// An `HStack` tightly wraps its [children]. Each child must have an intrinsic width 8 | /// to be visible in an `HStack`. 9 | class HStack extends StatelessWidget { 10 | const HStack( 11 | this.children, { 12 | super.key, 13 | this.alignment = VerticalAlignment.center, 14 | this.spacing = 8, 15 | }); 16 | 17 | /// The vertical (cross-axis) alignment of the [children] within this [HStack]. 18 | /// 19 | /// See [VerticalAlignment] for more info about the available alignments. 20 | final VerticalAlignment alignment; 21 | 22 | /// The distance between each child, or `null` if you want the stack to choose 23 | /// a default spacing. 24 | final double spacing; 25 | 26 | /// Child widgets, which are displayed horizontally in a row-style layout. 27 | final List children; 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return OverflowBox( 32 | // Note: When a HStack overflows available bounds, the content horizontally 33 | // centers itself within the available height. Behavior verified as of 34 | // June 01, 2024. 35 | maxWidth: double.infinity, 36 | alignment: Alignment.center, 37 | // HStack content is horizontally centered when it doesn't take up all 38 | // available height. 39 | child: Center( 40 | child: Row( 41 | mainAxisSize: MainAxisSize.min, 42 | crossAxisAlignment: _rowAlignment, 43 | children: [ 44 | for (final child in children) ...[ 45 | child, 46 | if (spacing != 0.0) // 47 | SizedBox(width: spacing), 48 | ], 49 | ], 50 | ), 51 | ), 52 | ); 53 | } 54 | 55 | CrossAxisAlignment get _rowAlignment { 56 | switch (alignment) { 57 | case VerticalAlignment.top: 58 | return CrossAxisAlignment.start; 59 | case VerticalAlignment.center: 60 | return CrossAxisAlignment.center; 61 | case VerticalAlignment.bottom: 62 | return CrossAxisAlignment.end; 63 | case VerticalAlignment.firstTextBaseline: 64 | case VerticalAlignment.lastTextBaseline: 65 | throw Exception("HStack does not yet support this alignment: $alignment"); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/src/layout/list.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | // TODO: 4 | class ListView extends StatelessWidget { 5 | const ListView({super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return const Placeholder(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/src/layout/table.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | // TODO: 4 | class Table extends StatelessWidget { 5 | const Table({super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return const Placeholder(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/src/layout/vstack.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | import 'alignment.dart' hide Alignment; 4 | 5 | /// A layout widget that displays its children vertically, like a column. 6 | /// 7 | /// A `VStack` tightly wraps its [children]. Each child must have an intrinsic height 8 | /// to be visible in a `VStack`. 9 | class VStack extends StatelessWidget { 10 | const VStack( 11 | this.children, { 12 | super.key, 13 | this.alignment = HorizontalAlignment.center, 14 | this.spacing = 8, 15 | }); 16 | 17 | /// The horizontal (cross-axis) alignment of the [children] within this [VStack]. 18 | /// 19 | /// See [HorizontalAlignment] for more info about the available alignments. 20 | final HorizontalAlignment alignment; 21 | 22 | /// The distance between each child, or `nul` if you want the stack to choose a 23 | /// default spacing. 24 | final double spacing; 25 | 26 | /// Child widgets, which are displayed vertically in a column-style layout. 27 | final List children; 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return OverflowBox( 32 | // Note: When a VStack overflows available bounds, the content vertically 33 | // centers itself within the available height. Behavior verified as of 34 | // June 01, 2024. 35 | maxHeight: double.infinity, 36 | alignment: Alignment.center, 37 | // VStack content is vertically centered when it doesn't take up all 38 | // available height. 39 | child: Center( 40 | child: Column( 41 | mainAxisSize: MainAxisSize.min, 42 | crossAxisAlignment: _columnAlignment, 43 | children: [ 44 | for (final child in children) ...[ 45 | child, 46 | if (spacing != 0.0) // 47 | SizedBox(height: spacing), 48 | ], 49 | ], 50 | ), 51 | ), 52 | ); 53 | } 54 | 55 | CrossAxisAlignment get _columnAlignment { 56 | switch (alignment) { 57 | case HorizontalAlignment.leading: 58 | return CrossAxisAlignment.start; 59 | case HorizontalAlignment.center: 60 | return CrossAxisAlignment.center; 61 | case HorizontalAlignment.trailing: 62 | return CrossAxisAlignment.end; 63 | case HorizontalAlignment.listRowSeparatorLeading: 64 | case HorizontalAlignment.listRowSeparatorTrailing: 65 | throw Exception("VStack does not yet support this alignment: $alignment"); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/src/layout/zstack.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | // TODO: 4 | class ZStack extends StatelessWidget { 5 | const ZStack({super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return const Placeholder(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/src/shapes/ellipse.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | /// An elliptical shape whose width and height match size of the parent 4 | class Ellipse extends StatelessWidget { 5 | const Ellipse({ 6 | super.key, 7 | this.fillColor, 8 | this.fillGradient, 9 | this.strokeColor, 10 | this.strokeGradient, 11 | this.strokeLineWidth = 1.0, 12 | }); 13 | 14 | /// A color used for painting the interior of the ellipse. 15 | /// 16 | /// This is an alternative to [fillGradient]. 17 | final Color? fillColor; 18 | 19 | /// A gradient used for painting the interior of the ellipse. 20 | /// 21 | /// This is an alternative to [fillColor]. 22 | final Gradient? fillGradient; 23 | 24 | /// A color used for painting the outline of the ellipse. 25 | /// 26 | /// This is an alternative to [strokeGradient]. 27 | final Color? strokeColor; 28 | 29 | /// A gradient used for painting the outline of the ellipse. 30 | /// 31 | /// This is an alternative to [strokeColor]. 32 | final Gradient? strokeGradient; 33 | 34 | /// The width of the stroke used to paint the outline of the ellipse. 35 | /// 36 | /// The stroke line is centered along the edge of the ellipse. Half of 37 | /// [strokeLineWidth] is painted outside of the ellipse and half inside. 38 | /// 39 | /// The default is 1.0. 40 | final double strokeLineWidth; 41 | 42 | @override 43 | Widget build(BuildContext context) { 44 | return CustomPaint( 45 | size: Size.infinite, 46 | painter: _EllipsePainter( 47 | fillColor: fillColor, 48 | fillGradient: fillGradient, 49 | strokeColor: strokeColor, 50 | strokeGradient: strokeGradient, 51 | strokeLineWidth: strokeLineWidth, 52 | ), 53 | ); 54 | } 55 | } 56 | 57 | class _EllipsePainter extends CustomPainter { 58 | _EllipsePainter({ 59 | required this.fillColor, 60 | required this.fillGradient, 61 | required this.strokeColor, 62 | required this.strokeGradient, 63 | required this.strokeLineWidth, 64 | }); 65 | 66 | final Color? fillColor; 67 | final Gradient? fillGradient; 68 | final Color? strokeColor; 69 | final Gradient? strokeGradient; 70 | final double strokeLineWidth; 71 | 72 | @override 73 | void paint(Canvas canvas, Size size) { 74 | _paintFill(canvas, size); 75 | _paintStroke(canvas, size); 76 | } 77 | 78 | void _paintFill(Canvas canvas, Size size) { 79 | if (fillColor != null) { 80 | _paintSolidFill(canvas, size); 81 | } else if (fillGradient != null) { 82 | _paintGradientFill(canvas, size); 83 | } 84 | } 85 | 86 | void _paintSolidFill(Canvas canvas, Size size) { 87 | final rect = Rect.fromLTWH(0, 0, size.width, size.height); 88 | final paint = Paint() 89 | ..style = PaintingStyle.fill 90 | ..color = fillColor!; 91 | canvas.drawOval(rect, paint); 92 | } 93 | 94 | void _paintGradientFill(Canvas canvas, Size size) { 95 | final rect = Rect.fromLTWH(0, 0, size.width, size.height); 96 | final paint = Paint() 97 | ..style = PaintingStyle.fill 98 | ..shader = fillGradient!.createShader(rect); 99 | canvas.drawOval(rect, paint); 100 | } 101 | 102 | void _paintStroke(Canvas canvas, Size size) { 103 | if (strokeColor != null) { 104 | _paintSolidStroke(canvas, size); 105 | } else if (strokeGradient != null) { 106 | _paintGradientStroke(canvas, size); 107 | } 108 | } 109 | 110 | void _paintSolidStroke(Canvas canvas, Size size) { 111 | final rect = Rect.fromLTWH(0, 0, size.width, size.height); 112 | final paint = Paint() 113 | ..style = PaintingStyle.stroke 114 | ..color = strokeColor! 115 | ..strokeWidth = strokeLineWidth; 116 | canvas.drawOval(rect, paint); 117 | } 118 | 119 | void _paintGradientStroke(Canvas canvas, Size size) { 120 | final rect = Rect.fromLTWH(0, 0, size.width, size.height); 121 | final paint = Paint() 122 | ..style = PaintingStyle.stroke 123 | ..shader = strokeGradient!.createShader(rect) 124 | ..strokeWidth = strokeLineWidth; 125 | canvas.drawOval(rect, paint); 126 | } 127 | 128 | @override 129 | bool shouldRepaint(covariant _EllipsePainter oldDelegate) { 130 | return oldDelegate.fillColor != fillColor || 131 | oldDelegate.fillGradient != fillGradient || 132 | oldDelegate.strokeColor != strokeColor || 133 | oldDelegate.strokeGradient != strokeGradient || 134 | oldDelegate.strokeLineWidth != strokeLineWidth; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /lib/src/shapes/rectangle.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | /// A rectangular shape that takes the size of the parent 4 | class Rectangle extends StatelessWidget { 5 | const Rectangle({ 6 | super.key, 7 | this.fillColor, 8 | this.fillGradient, 9 | this.strokeColor, 10 | this.strokeGradient, 11 | this.strokeLineWidth = 1.0, 12 | }); 13 | 14 | /// A color used for painting the interior of the rectangle. 15 | /// 16 | /// This is an alternative to [fillGradient]. 17 | final Color? fillColor; 18 | 19 | /// A gradient used for painting the interior of the rectangle. 20 | /// 21 | /// This is an alternative to [fillColor]. 22 | final Gradient? fillGradient; 23 | 24 | /// A color used for painting the outline of the rectangle. 25 | /// 26 | /// The stroke line is drawn inside the frame of the rectangle, and its 27 | /// width is determined by [strokeLineWidth]. 28 | /// 29 | /// This is an alternative to [strokeGradient]. 30 | final Color? strokeColor; 31 | 32 | /// A gradient used for painting the outline of the rectangle. 33 | /// 34 | /// The stroke line is drawn inside the frame of the rectangle, and its 35 | /// width is determined by [strokeLineWidth]. 36 | /// 37 | /// This is an alternative to [strokeColor]. 38 | final Gradient? strokeGradient; 39 | 40 | /// The width of the stroke used to paint the outline of the rectangle. 41 | /// 42 | /// The default is 1.0. 43 | final double strokeLineWidth; 44 | 45 | @override 46 | Widget build(BuildContext context) { 47 | return CustomPaint( 48 | size: Size.infinite, 49 | painter: _RectanglePainter( 50 | fillColor: fillColor, 51 | fillGradient: fillGradient, 52 | strokeColor: strokeColor, 53 | strokeGradient: strokeGradient, 54 | strokeLineWidth: strokeLineWidth, 55 | ), 56 | ); 57 | } 58 | } 59 | 60 | class _RectanglePainter extends CustomPainter { 61 | _RectanglePainter({ 62 | required this.fillColor, 63 | required this.fillGradient, 64 | required this.strokeColor, 65 | required this.strokeGradient, 66 | required this.strokeLineWidth, 67 | }); 68 | 69 | final Color? fillColor; 70 | final Gradient? fillGradient; 71 | final Color? strokeColor; 72 | final Gradient? strokeGradient; 73 | final double strokeLineWidth; 74 | 75 | @override 76 | void paint(Canvas canvas, Size size) { 77 | _paintFill(canvas, size); 78 | _paintStroke(canvas, size); 79 | } 80 | 81 | void _paintFill(Canvas canvas, Size size) { 82 | if (fillColor != null) { 83 | _paintSolidFill(canvas, size); 84 | } else if (fillGradient != null) { 85 | _paintGradientFill(canvas, size); 86 | } 87 | } 88 | 89 | void _paintSolidFill(Canvas canvas, Size size) { 90 | final rect = Rect.fromLTWH(0, 0, size.width, size.height); 91 | final paint = Paint() 92 | ..style = PaintingStyle.fill 93 | ..color = fillColor!; 94 | canvas.drawRect(rect, paint); 95 | } 96 | 97 | void _paintGradientFill(Canvas canvas, Size size) { 98 | final rect = Rect.fromLTWH(0, 0, size.width, size.height); 99 | final paint = Paint() 100 | ..style = PaintingStyle.fill 101 | ..shader = fillGradient!.createShader(rect); 102 | canvas.drawRect(rect, paint); 103 | } 104 | 105 | void _paintStroke(Canvas canvas, Size size) { 106 | if (strokeColor != null) { 107 | _paintSolidStroke(canvas, size); 108 | } else if (strokeGradient != null) { 109 | _paintGradientStroke(canvas, size); 110 | } 111 | } 112 | 113 | void _paintSolidStroke(Canvas canvas, Size size) { 114 | final rect = _adjustRectForLineWidth(size); 115 | final paint = Paint() 116 | ..style = PaintingStyle.stroke 117 | ..color = strokeColor! 118 | ..strokeWidth = strokeLineWidth; 119 | canvas.drawRect(rect, paint); 120 | } 121 | 122 | Rect _adjustRectForLineWidth(Size size) { 123 | return Rect.fromLTWH( 124 | strokeLineWidth / 2, 125 | strokeLineWidth / 2, 126 | size.width - strokeLineWidth, 127 | size.height - strokeLineWidth, 128 | ); 129 | } 130 | 131 | void _paintGradientStroke(Canvas canvas, Size size) { 132 | final rect = _adjustRectForLineWidth(size); 133 | final paint = Paint() 134 | ..style = PaintingStyle.stroke 135 | ..shader = strokeGradient!.createShader(rect) 136 | ..strokeWidth = strokeLineWidth; 137 | canvas.drawRect(rect, paint); 138 | } 139 | 140 | @override 141 | bool shouldRepaint(covariant _RectanglePainter oldDelegate) { 142 | return oldDelegate.fillColor != fillColor || 143 | oldDelegate.fillGradient != fillGradient || 144 | oldDelegate.strokeColor != strokeColor || 145 | oldDelegate.strokeGradient != strokeGradient || 146 | oldDelegate.strokeLineWidth != strokeLineWidth; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /lib/swift_ui.dart: -------------------------------------------------------------------------------- 1 | library swift_ui; 2 | 3 | // Controls and Indicators 4 | export 'src/controls_and_indicators/button.dart'; 5 | 6 | // Drawing and Graphics 7 | export 'src/drawing_and_graphics/border.dart'; 8 | 9 | // Layout 10 | export 'src/layout/grid.dart'; 11 | export 'src/layout/hstack.dart'; 12 | export 'src/layout/list.dart'; 13 | export 'src/layout/table.dart'; 14 | export 'src/layout/vstack.dart'; 15 | export 'src/layout/zstack.dart'; 16 | 17 | // Layout Adjustments 18 | export 'src/layout/frame.dart'; 19 | export 'src/layout/alignment.dart'; 20 | 21 | // Shapes 22 | export 'src/shapes/ellipse.dart'; 23 | export 'src/shapes/rectangle.dart'; 24 | 25 | // Infrastructure 26 | export 'src/infrastructure/late_bound_build.dart'; 27 | export 'src/infrastructure/colors.dart'; 28 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: swift_ui 2 | description: "Swift UI in Flutter" 3 | version: 0.0.1 4 | repository: https://github.com/Flutter-Bounty-Hunters/swift_ui 5 | 6 | environment: 7 | sdk: '>=3.0.0 <4.0.0' 8 | flutter: ">=1.17.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | 14 | dev_dependencies: 15 | flutter_lints: ^2.0.0 16 | flutter_test: 17 | sdk: flutter 18 | golden_toolkit: ^0.15.0 19 | 20 | flutter: 21 | 22 | # To add assets to your package, add an assets section, like this: 23 | # assets: 24 | # - images/a_dot_burr.jpeg 25 | # - images/a_dot_ham.jpeg 26 | # 27 | # For details regarding assets in packages, see 28 | # https://flutter.dev/assets-and-images/#from-packages 29 | # 30 | # An image asset can refer to one or more resolution-specific "variants", see 31 | # https://flutter.dev/assets-and-images/#resolution-aware 32 | 33 | # To add custom fonts to your package, add a fonts section here, 34 | # in this "flutter" section. Each entry in this list should have a 35 | # "family" key with the font family name, and a "fonts" key with a 36 | # list giving the asset and other descriptors for the font. For 37 | # example: 38 | # fonts: 39 | # - family: Schyler 40 | # fonts: 41 | # - asset: fonts/Schyler-Regular.ttf 42 | # - asset: fonts/Schyler-Italic.ttf 43 | # style: italic 44 | # - family: Trajan Pro 45 | # fonts: 46 | # - asset: fonts/TrajanPro.ttf 47 | # - asset: fonts/TrajanPro_Bold.ttf 48 | # weight: 700 49 | # 50 | # For details regarding fonts in packages, see 51 | # https://flutter.dev/custom-fonts/#from-packages 52 | -------------------------------------------------------------------------------- /test/golden_tools.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | /// Surrounds [child] with a scaffold that helps fit a widget-under-test 4 | /// into a [GoldenBuilder.grid]. 5 | Widget createGoldenGridItemScaffold(Widget child) { 6 | // This scaffold structure is the result of how golden_toolkit seems 7 | // to layout widgets with GoldenBuilder. The GoldenBuilder is given 8 | // an aspect ratio to maintain, which should result in a bounded height, 9 | // but instead we get constraints with infinite height. 10 | // 11 | // To avoid layout exceptions, we constrain our height. In theory we 12 | // should be able to use an aspect ratio of 1.0, but golden_toolkit 13 | // includes some title text above each golden, which eats up some 14 | // height. Therefore, we make ourselves a bit shorter than square so 15 | // we don't overflow into the title text. 16 | // 17 | // Also, it appears that golden_toolkit doesn't clip the bounds of 18 | // individual widgets in the grid, so we clip ourselves explicitly. 19 | return AspectRatio( 20 | aspectRatio: 1.2, 21 | child: ClipRect( 22 | child: child, 23 | ), 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /test/infrastructure/late_bound_build_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:swift_ui/src/infrastructure/late_bound_build.dart'; 4 | 5 | void main() { 6 | group("LateBoundBuild", () { 7 | testWidgets("makes it possible to configure an ancestor from a descendant", (widgetTester) async { 8 | final ancestorTitle = ValueNotifier("TITLE ONE"); 9 | 10 | // Build a screen scaffold where we configure the scaffold title from 11 | // a descendant widget down in the tree. 12 | await widgetTester.pumpWidget( 13 | Directionality( 14 | textDirection: TextDirection.ltr, 15 | child: _SomeScaffold( 16 | child: Center( 17 | // We place a ValueListenableBuilder in this tree so that we can 18 | // test what happens when _SomeScaffoldTitle rebuilds, but 19 | // _SomeScaffold doesn't rebuild. 20 | child: ValueListenableBuilder( 21 | valueListenable: ancestorTitle, 22 | builder: (context, title, child) { 23 | return _SomeScaffoldTitle( 24 | title, // When this widget is mounted or updated, this title is set on AncestorScaffold 25 | child: const Text("Main Content"), 26 | ); 27 | }, 28 | ), 29 | ), 30 | ), 31 | ), 32 | ); 33 | 34 | // Ensure that the descendant _SomeScaffoldTitle widget set the title as expected. 35 | expect((_ancestorScaffoldTitleKey.currentWidget as Text).data, "TITLE ONE"); 36 | 37 | // Re-build just the descendant widget with a new title for the ancestor. 38 | ancestorTitle.value = "TITLE TWO"; 39 | // When rebuilding a subtree below the scaffold, it takes two frames to update the 40 | // title. First, the content subtree builds, and sets a new title value. Second, 41 | // the title subtree rebuilds with the new title value. 42 | await widgetTester.pump(); 43 | await widgetTester.pump(); 44 | 45 | // Ensure that the descendant _SomeScaffoldTitle widget set the title as expected. 46 | expect((_ancestorScaffoldTitleKey.currentWidget as Text).data, "TITLE TWO"); 47 | }); 48 | }); 49 | } 50 | 51 | /// A widget that represents an arbitrary screen scaffold, with a screen title and 52 | /// screen content. 53 | /// 54 | /// The title of [_SomeScaffold] is configured by placing [_SomeScaffoldTitle] 55 | /// somewhere within the [child] subtree. 56 | /// 57 | /// [_SomeScaffold] and [_SomeScaffoldTitle] represent a capability that's similar to 58 | /// how Swift UI configures the title in its navigation views. 59 | class _SomeScaffold extends StatefulWidget { 60 | static _SomeScaffoldState of(BuildContext context) => context.findAncestorStateOfType<_SomeScaffoldState>()!; 61 | 62 | const _SomeScaffold({ 63 | required this.child, 64 | }); 65 | 66 | final Widget child; 67 | 68 | @override 69 | State<_SomeScaffold> createState() => _SomeScaffoldState(); 70 | } 71 | 72 | class _SomeScaffoldState extends State<_SomeScaffold> { 73 | final _title = ValueNotifier(null); 74 | set title(String? title) => _title.value = title; 75 | 76 | @override 77 | Widget build(BuildContext context) { 78 | return Column( 79 | crossAxisAlignment: CrossAxisAlignment.stretch, 80 | children: [ 81 | _buildTitleBar(), 82 | Expanded( 83 | child: _buildContent(), 84 | ), 85 | ], 86 | ); 87 | } 88 | 89 | Widget _buildTitleBar() { 90 | return SizedBox( 91 | height: 54, 92 | child: Center( 93 | child: LateBoundBuild( 94 | // This builder runs during layout, instead of the build phase. 95 | builder: (lateBoundContext) => LateBoundValueListenableBuilder( 96 | listenable: _title, 97 | builder: (_, value, __) { 98 | return Text( 99 | value ?? "", 100 | key: _ancestorScaffoldTitleKey, 101 | ); 102 | }, 103 | ), 104 | ), 105 | ), 106 | ); 107 | } 108 | 109 | Widget _buildContent() { 110 | return widget.child; 111 | } 112 | } 113 | 114 | /// Displays a title within [_SomeScaffold]. 115 | class _SomeScaffoldTitle extends StatefulWidget { 116 | const _SomeScaffoldTitle( 117 | this.ancestorTitle, { 118 | this.child, 119 | }); 120 | 121 | final String ancestorTitle; 122 | final Widget? child; 123 | 124 | @override 125 | State<_SomeScaffoldTitle> createState() => _SomeScaffoldTitleState(); 126 | } 127 | 128 | class _SomeScaffoldTitleState extends State<_SomeScaffoldTitle> { 129 | @override 130 | void didChangeDependencies() { 131 | super.didChangeDependencies(); 132 | _SomeScaffold.of(context).title = widget.ancestorTitle; 133 | } 134 | 135 | @override 136 | void didUpdateWidget(_SomeScaffoldTitle oldWidget) { 137 | super.didUpdateWidget(oldWidget); 138 | _SomeScaffold.of(context).title = widget.ancestorTitle; 139 | } 140 | 141 | @override 142 | Widget build(BuildContext context) { 143 | return widget.child ?? const SizedBox(); 144 | } 145 | } 146 | 147 | final _ancestorScaffoldTitleKey = GlobalKey(); 148 | -------------------------------------------------------------------------------- /test/layout/frame_golden_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart' hide Alignment; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:golden_toolkit/golden_toolkit.dart'; 4 | import 'package:swift_ui/swift_ui.dart' hide Border; 5 | 6 | void main() { 7 | group("Layout adjustments > Frame >", () { 8 | testGoldens("smoke test", (widgetTester) async { 9 | final builder = GoldenBuilder.grid(columns: 4, widthToHeightRatio: 1) 10 | ..addScenario( 11 | 'Center', 12 | Container( 13 | decoration: BoxDecoration(border: Border.all()), 14 | child: Frame( 15 | width: 100, 16 | height: 100, 17 | alignment: Alignment.center, 18 | child: Container( 19 | width: 10, 20 | height: 10, 21 | color: Colors.blue, 22 | ), 23 | ), 24 | ), 25 | ) 26 | ..addScenario( 27 | 'Top', 28 | Container( 29 | decoration: BoxDecoration(border: Border.all()), 30 | child: Frame( 31 | width: 100, 32 | height: 100, 33 | alignment: Alignment.top, 34 | child: Container( 35 | width: 10, 36 | height: 10, 37 | color: Colors.blue, 38 | ), 39 | ), 40 | ), 41 | ) 42 | ..addScenario( 43 | 'Bottom', 44 | Container( 45 | decoration: BoxDecoration(border: Border.all()), 46 | child: Frame( 47 | width: 100, 48 | height: 100, 49 | alignment: Alignment.bottom, 50 | child: Container( 51 | width: 10, 52 | height: 10, 53 | color: Colors.blue, 54 | ), 55 | ), 56 | ), 57 | ) 58 | ..addScenario( 59 | 'Leading', 60 | Container( 61 | decoration: BoxDecoration(border: Border.all()), 62 | child: Frame( 63 | width: 100, 64 | height: 100, 65 | alignment: Alignment.leading, 66 | child: Container( 67 | width: 10, 68 | height: 10, 69 | color: Colors.blue, 70 | ), 71 | ), 72 | ), 73 | ) 74 | ..addScenario( 75 | 'Trailing', 76 | Container( 77 | decoration: BoxDecoration(border: Border.all()), 78 | child: Frame( 79 | width: 100, 80 | height: 100, 81 | alignment: Alignment.trailing, 82 | child: Container( 83 | width: 10, 84 | height: 10, 85 | color: Colors.blue, 86 | ), 87 | ), 88 | ), 89 | ) 90 | ..addScenario( 91 | 'topLeading', 92 | Container( 93 | decoration: BoxDecoration(border: Border.all()), 94 | child: Frame( 95 | width: 100, 96 | height: 100, 97 | alignment: Alignment.topLeading, 98 | child: Container( 99 | width: 10, 100 | height: 10, 101 | color: Colors.blue, 102 | ), 103 | ), 104 | ), 105 | ) 106 | ..addScenario( 107 | 'topTrailing', 108 | Container( 109 | decoration: BoxDecoration(border: Border.all()), 110 | child: Frame( 111 | width: 100, 112 | height: 100, 113 | alignment: Alignment.topTrailing, 114 | child: Container( 115 | width: 10, 116 | height: 10, 117 | color: Colors.blue, 118 | ), 119 | ), 120 | ), 121 | ) 122 | ..addScenario( 123 | 'bottomLeading', 124 | Container( 125 | decoration: BoxDecoration(border: Border.all()), 126 | child: Frame( 127 | width: 100, 128 | height: 100, 129 | alignment: Alignment.bottomLeading, 130 | child: Container( 131 | width: 10, 132 | height: 10, 133 | color: Colors.blue, 134 | ), 135 | ), 136 | ), 137 | ) 138 | ..addScenario( 139 | 'bottomTrailing', 140 | Container( 141 | decoration: BoxDecoration(border: Border.all()), 142 | child: Frame( 143 | width: 100, 144 | height: 100, 145 | alignment: Alignment.bottomTrailing, 146 | child: Container( 147 | width: 10, 148 | height: 10, 149 | color: Colors.blue, 150 | ), 151 | ), 152 | ), 153 | ); 154 | 155 | await widgetTester.pumpWidgetBuilder(builder.build()); 156 | await screenMatchesGolden(widgetTester, 'frame_smoke-test'); 157 | }); 158 | }); 159 | } 160 | -------------------------------------------------------------------------------- /test/layout/goldens/frame_smoke-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/test/layout/goldens/frame_smoke-test.png -------------------------------------------------------------------------------- /test/layout/goldens/hstack_smoke-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/test/layout/goldens/hstack_smoke-test.png -------------------------------------------------------------------------------- /test/layout/goldens/vstack_smoke-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/test/layout/goldens/vstack_smoke-test.png -------------------------------------------------------------------------------- /test/layout/hstack_golden_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:golden_toolkit/golden_toolkit.dart'; 4 | import 'package:swift_ui/swift_ui.dart'; 5 | 6 | import '../golden_tools.dart'; 7 | 8 | void main() { 9 | group("Layout > HStack >", () { 10 | testGoldens("smoke test", (widgetTester) async { 11 | final builder = GoldenBuilder.grid(columns: 2, widthToHeightRatio: 1) 12 | ..addScenario( 13 | "Top", 14 | createGoldenGridItemScaffold( 15 | const HStack( 16 | alignment: VerticalAlignment.top, 17 | [ 18 | Text("First\nSecond"), 19 | Text("Third"), 20 | Text("Fourth\nFifth\nSixth"), 21 | ], 22 | ), 23 | ), 24 | ) 25 | ..addScenario( 26 | "Center", 27 | createGoldenGridItemScaffold( 28 | const HStack( 29 | alignment: VerticalAlignment.center, 30 | [ 31 | Text("First\nSecond"), 32 | Text("Third"), 33 | Text("Fourth\nFifth\nSixth"), 34 | ], 35 | ), 36 | )) 37 | ..addScenario( 38 | "Bottom", 39 | createGoldenGridItemScaffold( 40 | const HStack( 41 | alignment: VerticalAlignment.bottom, 42 | [ 43 | Text("First\nSecond"), 44 | Text("Third"), 45 | Text("Fourth\nFifth\nSixth"), 46 | ], 47 | ), 48 | )) 49 | ..addScenario( 50 | "Overflow", 51 | createGoldenGridItemScaffold( 52 | HStack([ 53 | // TODO: Replace these with Rectangle() widgets 54 | Container( 55 | width: 100, 56 | height: double.infinity, 57 | color: Colors.red, 58 | ), 59 | Container( 60 | width: 100, 61 | height: double.infinity, 62 | color: Colors.green, 63 | ), 64 | Container( 65 | width: 100, 66 | height: double.infinity, 67 | color: Colors.blue, 68 | ), 69 | Container( 70 | width: 100, 71 | height: double.infinity, 72 | color: Colors.purple, 73 | ), 74 | Container( 75 | width: 100, 76 | height: double.infinity, 77 | color: Colors.orange, 78 | ), 79 | Container( 80 | width: 100, 81 | height: double.infinity, 82 | color: Colors.cyan, 83 | ), 84 | ]), 85 | )); 86 | 87 | await widgetTester.pumpWidgetBuilder(builder.build(), surfaceSize: const Size(800, 800)); 88 | 89 | await screenMatchesGolden(widgetTester, "hstack_smoke-test"); 90 | }); 91 | }); 92 | } 93 | -------------------------------------------------------------------------------- /test/layout/hstack_test.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | // TODO: 3 | } 4 | -------------------------------------------------------------------------------- /test/layout/vstack_golden_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:golden_toolkit/golden_toolkit.dart'; 4 | import 'package:swift_ui/swift_ui.dart'; 5 | 6 | import '../golden_tools.dart'; 7 | 8 | void main() { 9 | group("Layout > VStack >", () { 10 | testGoldens("smoke test", (widgetTester) async { 11 | final builder = GoldenBuilder.grid(columns: 2, widthToHeightRatio: 1) 12 | ..addScenario( 13 | "Leading", 14 | createGoldenGridItemScaffold( 15 | const VStack( 16 | alignment: HorizontalAlignment.leading, 17 | [ 18 | Text("First"), 19 | Text("Second"), 20 | Text("Third"), 21 | ], 22 | ), 23 | ), 24 | ) 25 | ..addScenario( 26 | "Center", 27 | createGoldenGridItemScaffold( 28 | const VStack( 29 | alignment: HorizontalAlignment.center, 30 | [ 31 | Text("First"), 32 | Text("Second"), 33 | Text("Third"), 34 | ], 35 | ), 36 | )) 37 | ..addScenario( 38 | "Trailing", 39 | createGoldenGridItemScaffold( 40 | const VStack( 41 | alignment: HorizontalAlignment.trailing, 42 | [ 43 | Text("First"), 44 | Text("Second"), 45 | Text("Third"), 46 | ], 47 | ), 48 | )) 49 | ..addScenario( 50 | "Overflow", 51 | createGoldenGridItemScaffold( 52 | VStack([ 53 | // TODO: Replace these with Rectangle() widgets 54 | Container( 55 | width: double.infinity, 56 | height: 100, 57 | color: Colors.red, 58 | ), 59 | Container( 60 | width: double.infinity, 61 | height: 100, 62 | color: Colors.green, 63 | ), 64 | Container( 65 | width: double.infinity, 66 | height: 100, 67 | color: Colors.blue, 68 | ), 69 | Container( 70 | width: double.infinity, 71 | height: 100, 72 | color: Colors.purple, 73 | ), 74 | Container( 75 | width: double.infinity, 76 | height: 100, 77 | color: Colors.orange, 78 | ), 79 | Container( 80 | width: double.infinity, 81 | height: 100, 82 | color: Colors.cyan, 83 | ), 84 | ]), 85 | )); 86 | 87 | await widgetTester.pumpWidgetBuilder(builder.build(), surfaceSize: const Size(800, 800)); 88 | 89 | await screenMatchesGolden(widgetTester, "vstack_smoke-test"); 90 | }); 91 | }); 92 | } 93 | -------------------------------------------------------------------------------- /test/layout/vstack_test.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | // TODO: 3 | } 4 | -------------------------------------------------------------------------------- /test/layout/zstack_test.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | // TODO: 3 | } 4 | -------------------------------------------------------------------------------- /test/shapes/ellipse_golden_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:golden_toolkit/golden_toolkit.dart'; 4 | import 'package:swift_ui/swift_ui.dart'; 5 | 6 | void main() { 7 | group("Shapes > Ellipse >", () { 8 | testGoldens("smoke test", (widgetTester) async { 9 | final builder = GoldenBuilder.grid(columns: 2, widthToHeightRatio: 1) 10 | ..addScenario( 11 | 'Solid fill', 12 | const Frame( 13 | width: 200, 14 | height: 100, 15 | child: Ellipse( 16 | fillColor: Colors.blue, 17 | ), 18 | ), 19 | ) 20 | ..addScenario( 21 | 'Gradient fill', 22 | const Frame( 23 | width: 200, 24 | height: 100, 25 | child: Ellipse( 26 | fillGradient: LinearGradient( 27 | colors: [Colors.yellow, Colors.orange], 28 | ), 29 | ), 30 | ), 31 | ) 32 | ..addScenario( 33 | 'Solid stroke', 34 | const Frame( 35 | width: 200, 36 | height: 100, 37 | child: Ellipse( 38 | strokeColor: Colors.blue, 39 | ), 40 | ), 41 | ) 42 | ..addScenario( 43 | 'Gradient stroke', 44 | const Frame( 45 | width: 200, 46 | height: 100, 47 | child: Ellipse( 48 | strokeGradient: LinearGradient( 49 | colors: [Colors.yellow, Colors.orange], 50 | ), 51 | ), 52 | ), 53 | ); 54 | await widgetTester.pumpWidgetBuilder(builder.build()); 55 | await screenMatchesGolden(widgetTester, 'ellipse_smoke-test'); 56 | }); 57 | }); 58 | } 59 | -------------------------------------------------------------------------------- /test/shapes/goldens/ellipse_smoke-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/test/shapes/goldens/ellipse_smoke-test.png -------------------------------------------------------------------------------- /test/shapes/goldens/rectangle_smoke-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flutter-Bounty-Hunters/swift_ui/92629983a90c327b84d3a2ac65c512e88120513b/test/shapes/goldens/rectangle_smoke-test.png -------------------------------------------------------------------------------- /test/shapes/rectangle_golden_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:golden_toolkit/golden_toolkit.dart'; 4 | import 'package:swift_ui/swift_ui.dart'; 5 | 6 | void main() { 7 | group("Shapes > Rectangle >", () { 8 | testGoldens("smoke test", (widgetTester) async { 9 | final builder = GoldenBuilder.grid(columns: 2, widthToHeightRatio: 1) 10 | ..addScenario( 11 | 'Solid fill', 12 | const Frame( 13 | width: 200, 14 | height: 100, 15 | child: Rectangle( 16 | fillColor: Colors.blue, 17 | ), 18 | ), 19 | ) 20 | ..addScenario( 21 | 'Gradient fill', 22 | const Frame( 23 | width: 200, 24 | height: 100, 25 | child: Rectangle( 26 | fillGradient: LinearGradient( 27 | colors: [Colors.yellow, Colors.orange], 28 | ), 29 | ), 30 | ), 31 | ) 32 | ..addScenario( 33 | 'Solid stroke', 34 | const Frame( 35 | width: 200, 36 | height: 100, 37 | child: Rectangle( 38 | strokeColor: Colors.blue, 39 | ), 40 | ), 41 | ) 42 | ..addScenario( 43 | 'Gradient stroke', 44 | const Frame( 45 | width: 200, 46 | height: 100, 47 | child: Rectangle( 48 | strokeGradient: LinearGradient( 49 | colors: [Colors.yellow, Colors.orange], 50 | ), 51 | ), 52 | ), 53 | ); 54 | await widgetTester.pumpWidgetBuilder(builder.build()); 55 | await screenMatchesGolden(widgetTester, 'rectangle_smoke-test'); 56 | }); 57 | }); 58 | } 59 | --------------------------------------------------------------------------------