├── .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 |
--------------------------------------------------------------------------------