diff --git a/.gitignore b/.gitignore index ac5aa98..c18acde 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,7 @@ migrate_working_dir/ **/doc/api/ .dart_tool/ build/ + +# Don't check in golden failure output. +test/**/failures/** +test_goldens/**/failures/** \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..6788b48 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + "configurations": [ + { + "name": "Golden", + "request": "launch", + "type": "dart", + "codeLens": { + "for": ["run-test", "run-test-file"] + }, + "args": ["--update-goldens"] + } + ] +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ce2e25f..b3143b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +1,8 @@ +## 0.0.2 + +- Added `HStack` widget. +- Added `VStack` widget. +- Added `Rectangle` widget. + ## 0.0.1 - Sept, 2023 Setting up the project structure. This release is not usable. diff --git a/doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery.xcodeproj/project.pbxproj b/doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery.xcodeproj/project.pbxproj index 1be5564..4651f65 100644 --- a/doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery.xcodeproj/project.pbxproj +++ b/doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 714951452C0EB31C00818B06 /* ShapesPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 714951442C0EB31C00818B06 /* ShapesPage.swift */; }; + 714951472C0EB52400818B06 /* RectangleExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = 714951462C0EB52400818B06 /* RectangleExamples.swift */; }; 7179CF832B26DEB900C5927B /* TextTypographyPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7179CF822B26DEB900C5927B /* TextTypographyPage.swift */; }; 7179CF852B26DEDD00C5927B /* TextAccessibilityPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7179CF842B26DEDD00C5927B /* TextAccessibilityPage.swift */; }; 7179CF872B26DF0E00C5927B /* TextLocalizationPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7179CF862B26DF0E00C5927B /* TextLocalizationPage.swift */; }; @@ -51,6 +53,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 714951442C0EB31C00818B06 /* ShapesPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShapesPage.swift; sourceTree = ""; }; + 714951462C0EB52400818B06 /* RectangleExamples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RectangleExamples.swift; sourceTree = ""; }; 7179CF822B26DEB900C5927B /* TextTypographyPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextTypographyPage.swift; sourceTree = ""; }; 7179CF842B26DEDD00C5927B /* TextAccessibilityPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextAccessibilityPage.swift; sourceTree = ""; }; 7179CF862B26DF0E00C5927B /* TextLocalizationPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextLocalizationPage.swift; sourceTree = ""; }; @@ -105,6 +109,15 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 714951432C0EB2C200818B06 /* shapes */ = { + isa = PBXGroup; + children = ( + 714951442C0EB31C00818B06 /* ShapesPage.swift */, + 714951462C0EB52400818B06 /* RectangleExamples.swift */, + ); + path = shapes; + sourceTree = ""; + }; 7179CF882B26E2E900C5927B /* typography */ = { isa = PBXGroup; children = ( @@ -187,6 +200,7 @@ C9CAE2B62AE107D30042DBC7 /* scaffolds */, C9CAE2B52AE107C90042DBC7 /* collections */, C9CAE2B42AE107B60042DBC7 /* primitives */, + 714951432C0EB2C200818B06 /* shapes */, C9CAE2802AC23AB40042DBC7 /* swift_ui_galleryApp.swift */, C9CAE2822AC23AB40042DBC7 /* ContentView.swift */, C9CAE2842AC23AB50042DBC7 /* Assets.xcassets */, @@ -411,12 +425,14 @@ C9CAE2AD2AE106420042DBC7 /* CollectionsPage.swift in Sources */, 719116892B216D500007C4DE /* TextPage.swift in Sources */, 7179CF852B26DEDD00C5927B /* TextAccessibilityPage.swift in Sources */, + 714951472C0EB52400818B06 /* RectangleExamples.swift in Sources */, 7179CF832B26DEB900C5927B /* TextTypographyPage.swift in Sources */, C9CAE2832AC23AB40042DBC7 /* ContentView.swift in Sources */, C9CAE2BB2AE1089A0042DBC7 /* LayoutsPage.swift in Sources */, C9CAE2B12AE1065F0042DBC7 /* ControlsPage.swift in Sources */, C9CAE2AB2ADFB1480042DBC7 /* PrimitivesPage.swift in Sources */, C9FB17582C0C4D25004479AA /* ZStackExamples.swift in Sources */, + 714951452C0EB31C00818B06 /* ShapesPage.swift in Sources */, C902DDE72AE3904400242DBA /* TodoPage.swift in Sources */, C9CAE2B32AE1066B0042DBC7 /* MotionPage.swift in Sources */, B3DD96702B66513800F66E9F /* ImageLocalizationPage.swift in Sources */, diff --git a/doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/ContentView.swift b/doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/ContentView.swift index 74498a4..3c9cf27 100644 --- a/doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/ContentView.swift +++ b/doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/ContentView.swift @@ -26,6 +26,10 @@ struct ContentView: View { MotionPage().tabItem{ Label("Motion", systemImage: "move.3d") } + + ShapesPage().tabItem{ + Label("Shapes", systemImage: "square.on.circle") + } } } } diff --git a/doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/Localizable.xcstrings b/doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/Localizable.xcstrings index 71e829c..cafeb1b 100644 --- a/doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/Localizable.xcstrings +++ b/doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/Localizable.xcstrings @@ -196,12 +196,18 @@ }, "First" : { + }, + "First\nSecond" : { + }, "flight" : { }, "footnote" : { + }, + "Fourth\nFifth\nSixth" : { + }, "Frame" : { @@ -367,6 +373,9 @@ }, "Other initializers" : { + }, + "Overlay" : { + }, "Party LET" : { @@ -412,6 +421,9 @@ }, "Raised pitch" : { + }, + "Rectangle" : { + }, "red" : { @@ -455,6 +467,9 @@ }, "serif" : { + }, + "Shapes" : { + }, "size 6" : { diff --git a/doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/shapes/RectangleExamples.swift b/doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/shapes/RectangleExamples.swift new file mode 100644 index 0000000..baf49cb --- /dev/null +++ b/doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/shapes/RectangleExamples.swift @@ -0,0 +1,36 @@ +import SwiftUI + +struct RectanglePage: View { + var body: some View { + ScrollView { + VStack(spacing: 20) { + // Rectangle with solid fill + Rectangle() + .fill(Color.blue) + .frame(width: 200, height: 100) + + // Rectangle with gradient fill + Rectangle() + .fill(LinearGradient(gradient: Gradient(colors: [.yellow, .orange]), startPoint: .leading, endPoint: .trailing)) + .frame(width: 200, height: 100) + + // Rectangle with solid stroke + Rectangle() + .stroke(Color.red, lineWidth: 4) + .frame(width: 200, height: 100) + + // Rectangle with gradient stroke + Rectangle() + .stroke(LinearGradient(gradient: Gradient(colors: [.yellow, .blue]), startPoint: .leading, endPoint: .trailing), lineWidth: 14) + .frame(width: 200, height: 100) + } + } + } +} + +struct RectanglePage_Previews: PreviewProvider { + static var previews: some View { + RectanglePage() + } +} + diff --git a/doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/shapes/ShapesPage.swift b/doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/shapes/ShapesPage.swift new file mode 100644 index 0000000..4fbe5ca --- /dev/null +++ b/doc_swift_ui_gallery/swift_ui_gallery/swift_ui_gallery/shapes/ShapesPage.swift @@ -0,0 +1,21 @@ +import SwiftUI + +struct ShapesPage: View { + var body: some View { + NavigationStack { + List() { + NavigationLink { + RectanglePage() + } label: { + Text("Rectangle") + } + }.navigationTitle("Shapes") + } + } +} + +struct ShapesPage_Previews: PreviewProvider { + static var previews: some View { + ShapesPage() + } +} diff --git a/example/lib/home_screen.dart b/example/lib/home_screen.dart index d281944..4877ede 100644 --- a/example/lib/home_screen.dart +++ b/example/lib/home_screen.dart @@ -1,9 +1,11 @@ import 'package:example/collections/collection_examples.dart'; import 'package:example/controls/controls_examples.dart'; +import 'package:example/infrastructure/inventory_page.dart'; import 'package:example/layouts/layout_examples.dart'; import 'package:example/motion/motion_examples.dart'; import 'package:example/primitives/primitive_examples.dart'; import 'package:example/scaffolds/scaffold_examples.dart'; +import 'package:example/shapes/shapes_examples.dart'; import 'package:flutter/cupertino.dart'; class HomeScreen extends StatefulWidget { @@ -29,9 +31,7 @@ class _HomeScreenState extends State { case 3: return const ScaffoldsPage(); case 4: - return const ControlsPage(); - case 5: - return const MotionPage(); + return const OverflowPage(); default: throw Exception("Unknown tab index: $index"); } @@ -55,15 +55,41 @@ class _HomeScreenState extends State { label: "Scaffolds", ), BottomNavigationBarItem( - icon: Icon(CupertinoIcons.slider_horizontal_3), - label: "Controls", - ), - BottomNavigationBarItem( - icon: Icon(CupertinoIcons.move), - label: "Motion", + icon: Icon(CupertinoIcons.ellipsis), + label: "More", ), ], ), ); } } + +class OverflowPage extends StatelessWidget { + const OverflowPage({super.key}); + + @override + Widget build(BuildContext context) { + return InventoryPage( + title: "More", + groups: [ + InventoryGroup( + title: 'ADDITIONAL CATEGORIES', + items: [ + InventoryItem( + label: "Controls", + pageBuilder: (context) => const ControlsPage(), + ), + InventoryItem( + label: "Motion", + pageBuilder: (context) => const MotionPage(), + ), + InventoryItem( + label: "Shapes", + pageBuilder: (context) => const ShapesPage(), + ), + ], + ), + ], + ); + } +} diff --git a/example/lib/shapes/rectangle.dart b/example/lib/shapes/rectangle.dart new file mode 100644 index 0000000..fb3ab8c --- /dev/null +++ b/example/lib/shapes/rectangle.dart @@ -0,0 +1,57 @@ +import 'package:flutter/widgets.dart'; +import 'package:swift_ui/swift_ui.dart'; + +class RectangleDemo extends StatelessWidget { + const RectangleDemo({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return const VStack( + [ + // Rectangle with solid fill + Frame( + width: 200, + height: 100, + child: Rectangle( + fillColor: Colors.blue, + ), + ), + + // Rectangle with gradient fill + Frame( + width: 200, + height: 100, + child: Rectangle( + fillGradient: LinearGradient( + colors: [Colors.yellow, Colors.orange], + ), + ), + ), + + // Rectangle with solid stroke + Frame( + width: 200, + height: 100, + child: Rectangle( + strokeColor: Colors.red, + strokeLineWidth: 4.0, + ), + ), + + // Rectangle with gradient stroke + Frame( + width: 200, + height: 100, + child: Rectangle( + strokeGradient: LinearGradient( + colors: [Colors.yellow, Colors.blue], + ), + strokeLineWidth: 14.0, + ), + ), + ], + ); + } +} diff --git a/example/lib/shapes/shapes_examples.dart b/example/lib/shapes/shapes_examples.dart new file mode 100644 index 0000000..658ab10 --- /dev/null +++ b/example/lib/shapes/shapes_examples.dart @@ -0,0 +1,25 @@ +import 'package:example/demo_screen.dart'; +import 'package:example/infrastructure/inventory_page.dart'; +import 'package:example/shapes/rectangle.dart'; +import 'package:flutter/cupertino.dart'; + +class ShapesPage extends StatelessWidget { + const ShapesPage({super.key}); + + @override + Widget build(BuildContext context) { + return InventoryPage( + title: "Shapes", + groups: [ + InventoryGroup( + items: [ + InventoryItem( + label: "Rectangle", + pageBuilder: createDemo(const RectangleDemo()), + ), + ], + ), + ], + ); + } +} diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/example/macos/Runner.xcodeproj/project.pbxproj index 27e0f50..7d9ca67 100644 --- a/example/macos/Runner.xcodeproj/project.pbxproj +++ b/example/macos/Runner.xcodeproj/project.pbxproj @@ -227,7 +227,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 331C80D4294CF70F00263BE5 = { diff --git a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 397f3d3..15368ec 100644 --- a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ Frame >", () { + testGoldens("smoke test", (widgetTester) async { + final builder = GoldenBuilder.grid(columns: 4, widthToHeightRatio: 1) + ..addScenario( + 'Center', + Container( + decoration: BoxDecoration(border: Border.all()), + child: Frame( + width: 100, + height: 100, + alignment: Alignment.center, + child: Container( + width: 10, + height: 10, + color: Colors.blue, + ), + ), + ), + ) + ..addScenario( + 'Top', + Container( + decoration: BoxDecoration(border: Border.all()), + child: Frame( + width: 100, + height: 100, + alignment: Alignment.top, + child: Container( + width: 10, + height: 10, + color: Colors.blue, + ), + ), + ), + ) + ..addScenario( + 'Bottom', + Container( + decoration: BoxDecoration(border: Border.all()), + child: Frame( + width: 100, + height: 100, + alignment: Alignment.bottom, + child: Container( + width: 10, + height: 10, + color: Colors.blue, + ), + ), + ), + ) + ..addScenario( + 'Leading', + Container( + decoration: BoxDecoration(border: Border.all()), + child: Frame( + width: 100, + height: 100, + alignment: Alignment.leading, + child: Container( + width: 10, + height: 10, + color: Colors.blue, + ), + ), + ), + ) + ..addScenario( + 'Trailing', + Container( + decoration: BoxDecoration(border: Border.all()), + child: Frame( + width: 100, + height: 100, + alignment: Alignment.trailing, + child: Container( + width: 10, + height: 10, + color: Colors.blue, + ), + ), + ), + ) + ..addScenario( + 'topLeading', + Container( + decoration: BoxDecoration(border: Border.all()), + child: Frame( + width: 100, + height: 100, + alignment: Alignment.topLeading, + child: Container( + width: 10, + height: 10, + color: Colors.blue, + ), + ), + ), + ) + ..addScenario( + 'topTrailing', + Container( + decoration: BoxDecoration(border: Border.all()), + child: Frame( + width: 100, + height: 100, + alignment: Alignment.topTrailing, + child: Container( + width: 10, + height: 10, + color: Colors.blue, + ), + ), + ), + ) + ..addScenario( + 'bottomLeading', + Container( + decoration: BoxDecoration(border: Border.all()), + child: Frame( + width: 100, + height: 100, + alignment: Alignment.bottomLeading, + child: Container( + width: 10, + height: 10, + color: Colors.blue, + ), + ), + ), + ) + ..addScenario( + 'bottomTrailing', + Container( + decoration: BoxDecoration(border: Border.all()), + child: Frame( + width: 100, + height: 100, + alignment: Alignment.bottomTrailing, + child: Container( + width: 10, + height: 10, + color: Colors.blue, + ), + ), + ), + ); + + await widgetTester.pumpWidgetBuilder(builder.build()); + await screenMatchesGolden(widgetTester, 'frame_smoke-test'); + }); + }); +} diff --git a/test/layout/goldens/frame_smoke-test.png b/test/layout/goldens/frame_smoke-test.png new file mode 100644 index 0000000..6e76099 Binary files /dev/null and b/test/layout/goldens/frame_smoke-test.png differ diff --git a/test/shapes/goldens/rectangle_smoke-test.png b/test/shapes/goldens/rectangle_smoke-test.png new file mode 100644 index 0000000..8ece611 Binary files /dev/null and b/test/shapes/goldens/rectangle_smoke-test.png differ diff --git a/test/shapes/rectangle_golden_test.dart b/test/shapes/rectangle_golden_test.dart new file mode 100644 index 0000000..4fd1ae9 --- /dev/null +++ b/test/shapes/rectangle_golden_test.dart @@ -0,0 +1,58 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:golden_toolkit/golden_toolkit.dart'; +import 'package:swift_ui/swift_ui.dart'; + +void main() { + group("Shapes > Rectangle >", () { + testGoldens("smoke test", (widgetTester) async { + final builder = GoldenBuilder.grid(columns: 2, widthToHeightRatio: 1) + ..addScenario( + 'Solid fill', + const Frame( + width: 200, + height: 100, + child: Rectangle( + fillColor: Colors.blue, + ), + ), + ) + ..addScenario( + 'Gradient fill', + const Frame( + width: 200, + height: 100, + child: Rectangle( + fillGradient: LinearGradient( + colors: [Colors.yellow, Colors.orange], + ), + ), + ), + ) + ..addScenario( + 'Solid stroke', + const Frame( + width: 200, + height: 100, + child: Rectangle( + strokeColor: Colors.blue, + ), + ), + ) + ..addScenario( + 'Gradient stroke', + const Frame( + width: 200, + height: 100, + child: Rectangle( + strokeGradient: LinearGradient( + colors: [Colors.yellow, Colors.orange], + ), + ), + ), + ); + await widgetTester.pumpWidgetBuilder(builder.build()); + await screenMatchesGolden(widgetTester, 'rectangle_smoke-test'); + }); + }); +}