├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── easy_dsl ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── lib │ ├── annotations.dart │ ├── easy_dsl.dart │ └── src │ │ ├── defaults.dart │ │ ├── div_impl.dart │ │ ├── options.dart │ │ └── utils.dart └── pubspec.yaml ├── easy_dsl_gen ├── .gitignore ├── .pubignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── build.yaml ├── lib │ ├── builder │ │ ├── combine_builder.dart │ │ └── part_builder.dart │ ├── easy_dsl_gen.dart │ ├── generator │ │ ├── cls_generator.dart │ │ ├── cls_item.dart │ │ ├── code │ │ │ └── code_gen.dart │ │ ├── iter │ │ │ ├── aspect.dart │ │ │ ├── attr.dart │ │ │ ├── background.dart │ │ │ ├── border.dart │ │ │ ├── box.dart │ │ │ ├── iter.dart │ │ │ ├── margin.dart │ │ │ ├── opacity.dart │ │ │ ├── padding.dart │ │ │ ├── rounded.dart │ │ │ ├── safearea.dart │ │ │ ├── size.dart │ │ │ └── utils.dart │ │ └── widget_generator.dart │ └── visitor │ │ └── classname_visitor.dart ├── pubspec.yaml └── test │ ├── test1.dart │ ├── test2.dart │ └── test3.dart └── example ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── example │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── build.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 ├── gen │ └── demo1.dart ├── main.dart └── pages │ ├── demo1.dart │ ├── demo2.dart │ ├── div.dart │ └── home.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.yaml ├── test └── widget_test.dart ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── index.html └── manifest.json └── windows ├── .gitignore ├── CMakeLists.txt ├── flutter ├── CMakeLists.txt ├── generated_plugin_registrant.cc ├── generated_plugin_registrant.h └── generated_plugins.cmake └── runner ├── CMakeLists.txt ├── Runner.rc ├── flutter_window.cpp ├── flutter_window.h ├── main.cpp ├── resource.h ├── resources └── app_icon.ico ├── runner.exe.manifest ├── utils.cpp ├── utils.h ├── win32_window.cpp └── win32_window.h /.gitignore: -------------------------------------------------------------------------------- 1 | .dart_tool 2 | .packages 3 | .pub 4 | packages 5 | pubspec.lock 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 zzzgydi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build prepare test 2 | 3 | prepare: 4 | cd easy_dsl && dart pub get 5 | cd easy_dsl_gen && dart pub get 6 | cd example && flutter pub get 7 | 8 | test: 9 | cd example && dart run build_runner clean 10 | cd example && dart run build_runner build -d 11 | 12 | test1: 13 | cd example && dart run build_runner build -d --no-track-performance 14 | 15 | watch: 16 | cd example && dart run build_runner watch -d -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EasyDSL: Streamlining Flutter UI Development (WIP) 2 | 3 | ## Introduction 4 | 5 | EasyDSL is a Dart package that enhances Flutter UI development by introducing the concept of Domain-Specific Language (DSL). Drawing inspiration from the simplicity of TailwindCSS, EasyDSL enables Flutter developers to use DSL strings to generate Flutter widgets, greatly simplifying the creation of complex user interfaces. 6 | 7 | The project is currently under heavy development. Many Tailwind properties are yet to be adapted. Due to the differences between Flutter and the web, not all Tailwind classes will be supported. Additionally, some unique classes will be incorporated. 8 | 9 | ## Features 10 | 11 | - Offers a development experience akin to Tailwind CSS 12 | - Relies on code generation, ensuring zero runtime cost 13 | - Continuous development to expand DSL features 14 | 15 | ## Installation 16 | 17 | Add these dependencies to `pubspec.yaml`. 18 | 19 | ```yaml 20 | dependencies: 21 | easy_dsl: latest 22 | 23 | dev_dependencies: 24 | build_runner: ^2.4.0 25 | easy_dsl_gen: latest 26 | ``` 27 | 28 | ## Usage 29 | 30 | You must initially set the Widget's name as `Div`. 31 | 32 | ```dart 33 | // div.dart 34 | import 'package:flutter/foundation.dart'; 35 | import 'package:flutter/material.dart'; 36 | import 'package:easy_dsl/easy_dsl.dart'; 37 | 38 | part 'div.easy.g.dart'; 39 | 40 | @EasyDSL() 41 | class Div extends $Div { 42 | const Div({super.key, required super.className, super.children}) 43 | : super(option: const EasyOption.empty()); 44 | } 45 | ``` 46 | 47 | Next, include the following in your `build.yaml` file: 48 | 49 | ```yaml 50 | targets: 51 | $default: 52 | builders: 53 | easy_dsl_gen|easy_gen: 54 | generate_for: 55 | - lib/**.dart 56 | ``` 57 | 58 | Finally, you can use the code in your project as illustrated below: 59 | 60 | ```dart 61 | @override 62 | Widget build(BuildContext context) { 63 | const className = "items-center bg-black"; 64 | 65 | return const Div( 66 | className: className, 67 | children: [ 68 | Div( 69 | className: "bg-red-500 pt-[10] px-[100] py-[20]", 70 | children: [ 71 | Div( 72 | className: "p-10 bg-gray-400", 73 | children: [Text('This is'), Text('EasyDSL')], 74 | ), 75 | SizedBox(height: 20), 76 | Div( 77 | className: "flex items-center p-[10] bg-gray-500", 78 | children: [Text('Hello World')], 79 | ), 80 | ], 81 | ), 82 | ], 83 | ); 84 | } 85 | ``` 86 | 87 | **Note:** The value of `className` must be a string literal or a constant to be resolved at compile time. 88 | 89 | ## Running the code generator 90 | 91 | Once you have modified the code related to EasyDSL, you should execute the following command to generate the missing `.easy.g.dart` generated dart files. 92 | 93 | ```bash 94 | dart run build_runner build 95 | ``` 96 | 97 | Alternatively, you can execute the following command to continuously monitor file changes. 98 | 99 | ```bash 100 | dart run build_runner watch 101 | ``` 102 | 103 | ## Contributing 104 | 105 | Contributions to EasyDSL are highly encouraged! If you have suggestions or code contributions, please feel free to open an issue or submit a pull request. 106 | 107 | ## License 108 | 109 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 110 | -------------------------------------------------------------------------------- /easy_dsl/.gitignore: -------------------------------------------------------------------------------- 1 | # https://dart.dev/guides/libraries/private-files 2 | # Created by `dart pub` 3 | .dart_tool/ 4 | 5 | # Avoid committing pubspec.lock for library packages; see 6 | # https://dart.dev/guides/libraries/private-files#pubspeclock. 7 | pubspec.lock 8 | -------------------------------------------------------------------------------- /easy_dsl/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.0.4 2 | 3 | - support `SafeArea` 4 | - support children is null 5 | - optimize hot reload 6 | 7 | # 0.0.3 8 | 9 | - fix hot reload not works 10 | 11 | # 0.0.2 12 | 13 | - support `Row`, `Column` and `Stack` 14 | - support `width`, `height`, `maxWidth`, `maxHeight` and so on 15 | - support `background color` 16 | - support `border` and `borderRadius` 17 | - support `margin` and `padding` 18 | - support `aspectRatio` and `opacity` 19 | 20 | # 0.0.1 21 | 22 | - Initial version. 23 | -------------------------------------------------------------------------------- /easy_dsl/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 zzzgydi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /easy_dsl/README.md: -------------------------------------------------------------------------------- 1 | # EasyDSL: Streamlining Flutter UI Development (WIP) 2 | 3 | ## Introduction 4 | 5 | EasyDSL is a Dart package that enhances Flutter UI development by introducing the concept of Domain-Specific Language (DSL). Drawing inspiration from the simplicity of TailwindCSS, EasyDSL enables Flutter developers to use DSL strings to generate Flutter widgets, greatly simplifying the creation of complex user interfaces. 6 | 7 | The project is currently under heavy development. Many Tailwind properties are yet to be adapted. Due to the differences between Flutter and the web, not all Tailwind classes will be supported. Additionally, some unique classes will be incorporated. 8 | 9 | ## Features 10 | 11 | - Offers a development experience akin to Tailwind CSS 12 | - Relies on code generation, ensuring zero runtime cost 13 | - Continuous development to expand DSL features 14 | 15 | ## Installation 16 | 17 | Add these dependencies to `pubspec.yaml`. 18 | 19 | ```yaml 20 | dependencies: 21 | easy_dsl: latest 22 | 23 | dev_dependencies: 24 | build_runner: ^2.4.0 25 | easy_dsl_gen: latest 26 | ``` 27 | 28 | ## Usage 29 | 30 | You must initially set the Widget's name as `Div`. 31 | 32 | ```dart 33 | // div.dart 34 | import 'package:flutter/foundation.dart'; 35 | import 'package:flutter/material.dart'; 36 | import 'package:easy_dsl/easy_dsl.dart'; 37 | 38 | part 'div.easy.g.dart'; 39 | 40 | @EasyDSL() 41 | class Div extends $Div { 42 | const Div({super.key, required super.className, super.children}) 43 | : super(option: const EasyOption.empty()); 44 | } 45 | ``` 46 | 47 | Next, include the following in your `build.yaml` file: 48 | 49 | ```yaml 50 | targets: 51 | $default: 52 | builders: 53 | easy_dsl_gen|easy_gen: 54 | generate_for: 55 | - lib/**.dart 56 | ``` 57 | 58 | Finally, you can use the code in your project as illustrated below: 59 | 60 | ```dart 61 | @override 62 | Widget build(BuildContext context) { 63 | const className = "items-center bg-black"; 64 | 65 | return const Div( 66 | className: className, 67 | children: [ 68 | Div( 69 | className: "bg-red-500 pt-[10] px-[100] py-[20]", 70 | children: [ 71 | Div( 72 | className: "p-10 bg-gray-400", 73 | children: [Text('This is'), Text('EasyDSL')], 74 | ), 75 | SizedBox(height: 20), 76 | Div( 77 | className: "flex items-center p-[10] bg-gray-500", 78 | children: [Text('Hello World')], 79 | ), 80 | ], 81 | ), 82 | ], 83 | ); 84 | } 85 | ``` 86 | 87 | **Note:** The value of `className` must be a string literal or a constant to be resolved at compile time. 88 | 89 | ## Running the code generator 90 | 91 | Once you have modified the code related to EasyDSL, you should execute the following command to generate the missing `.easy.g.dart` generated dart files. 92 | 93 | ```bash 94 | dart run build_runner build 95 | ``` 96 | 97 | Alternatively, you can execute the following command to continuously monitor file changes. 98 | 99 | ```bash 100 | dart run build_runner watch 101 | ``` 102 | 103 | ## Contributing 104 | 105 | Contributions to EasyDSL are highly encouraged! If you have suggestions or code contributions, please feel free to open an issue or submit a pull request. 106 | 107 | ## License 108 | 109 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 110 | -------------------------------------------------------------------------------- /easy_dsl/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:lints/recommended.yaml 2 | -------------------------------------------------------------------------------- /easy_dsl/lib/annotations.dart: -------------------------------------------------------------------------------- 1 | class EasyDSL { 2 | final String? name; 3 | 4 | const EasyDSL({this.name}); 5 | } 6 | -------------------------------------------------------------------------------- /easy_dsl/lib/easy_dsl.dart: -------------------------------------------------------------------------------- 1 | /// Support for doing something awesome. 2 | /// 3 | /// More dartdocs go here. 4 | library easy_dsl; 5 | 6 | export 'annotations.dart'; 7 | export 'src/options.dart'; 8 | export 'src/defaults.dart'; 9 | export 'src/div_impl.dart'; 10 | export 'src/utils.dart'; 11 | -------------------------------------------------------------------------------- /easy_dsl/lib/src/defaults.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | const defaultSpacing = { 4 | "px": 1, 5 | "0": 0, 6 | "0.5": 0.5, 7 | "1": 4, 8 | "1.5": 6, 9 | "2": 8, 10 | "2.5": 10, 11 | "3": 12, 12 | "3.5": 14, 13 | "4": 16, 14 | "5": 20, 15 | "6": 24, 16 | "7": 28, 17 | "8": 32, 18 | "9": 36, 19 | "10": 40, 20 | "11": 44, 21 | "12": 48, 22 | "14": 56, 23 | "16": 64, 24 | "20": 80, 25 | "24": 96, 26 | "28": 112, 27 | "32": 128, 28 | "36": 144, 29 | "40": 160, 30 | "44": 176, 31 | "48": 192, 32 | "52": 208, 33 | "56": 224, 34 | "60": 240, 35 | "64": 256, 36 | "72": 288, 37 | "80": 320, 38 | "96": 384, 39 | }; 40 | 41 | const defaultBorderRadius = { 42 | "none": 0, 43 | "sm": 2, 44 | "md": 4, 45 | "lg": 8, 46 | "xl": 16, 47 | "2xl": 32, 48 | "3xl": 48, 49 | "full": 9999, 50 | "default": 4, 51 | }; 52 | 53 | const defaultAspectRatio = { 54 | "square": 1, 55 | "video": 16 / 9, 56 | }; 57 | 58 | // default color from https://github.com/tailwindlabs/tailwindcss/blob/master/src/public/colors.js 59 | const defaultColor = { 60 | "transparent": Color(0x00000000), 61 | "current": Color(0xFF000000), 62 | "black": Color(0xFF000000), 63 | "white": Color(0xFFFFFFFF), 64 | "slate-50": Color(0xFFf8fafc), 65 | "slate-100": Color(0xFFf1f5f9), 66 | "slate-200": Color(0xFFe2e8f0), 67 | "slate-300": Color(0xFFcbd5e1), 68 | "slate-400": Color(0xFF94a3b8), 69 | "slate-500": Color(0xFF64748b), 70 | "slate-600": Color(0xFF475569), 71 | "slate-700": Color(0xFF334155), 72 | "slate-800": Color(0xFF1e293b), 73 | "slate-900": Color(0xFF0f172a), 74 | "slate-950": Color(0xFF020617), 75 | "gray-50": Color(0xFFf9fafb), 76 | "gray-100": Color(0xFFf3f4f6), 77 | "gray-200": Color(0xFFe5e7eb), 78 | "gray-300": Color(0xFFd1d5db), 79 | "gray-400": Color(0xFF9ca3af), 80 | "gray-500": Color(0xFF6b7280), 81 | "gray-600": Color(0xFF4b5563), 82 | "gray-700": Color(0xFF374151), 83 | "gray-800": Color(0xFF1f2937), 84 | "gray-900": Color(0xFF111827), 85 | "gray-950": Color(0xFF030712), 86 | "zinc-50": Color(0xFFfafafa), 87 | "zinc-100": Color(0xFFf4f4f5), 88 | "zinc-200": Color(0xFFe4e4e7), 89 | "zinc-300": Color(0xFFd4d4d8), 90 | "zinc-400": Color(0xFFa1a1aa), 91 | "zinc-500": Color(0xFF71717a), 92 | "zinc-600": Color(0xFF52525b), 93 | "zinc-700": Color(0xFF3f3f46), 94 | "zinc-800": Color(0xFF27272a), 95 | "zinc-900": Color(0xFF18181b), 96 | "zinc-950": Color(0xFF09090b), 97 | "neutral-50": Color(0xFFfafafa), 98 | "neutral-100": Color(0xFFf5f5f5), 99 | "neutral-200": Color(0xFFe5e5e5), 100 | "neutral-300": Color(0xFFd4d4d4), 101 | "neutral-400": Color(0xFFa3a3a3), 102 | "neutral-500": Color(0xFF737373), 103 | "neutral-600": Color(0xFF525252), 104 | "neutral-700": Color(0xFF404040), 105 | "neutral-800": Color(0xFF262626), 106 | "neutral-900": Color(0xFF171717), 107 | "neutral-950": Color(0xFF0a0a0a), 108 | "stone-50": Color(0xFFfafaf9), 109 | "stone-100": Color(0xFFf5f5f4), 110 | "stone-200": Color(0xFFe7e5e4), 111 | "stone-300": Color(0xFFd6d3d1), 112 | "stone-400": Color(0xFFa8a29e), 113 | "stone-500": Color(0xFF78716c), 114 | "stone-600": Color(0xFF57534e), 115 | "stone-700": Color(0xFF44403c), 116 | "stone-800": Color(0xFF292524), 117 | "stone-900": Color(0xFF1c1917), 118 | "stone-950": Color(0xFF0c0a09), 119 | "red-50": Color(0xFFfef2f2), 120 | "red-100": Color(0xFFfee2e2), 121 | "red-200": Color(0xFFfecaca), 122 | "red-300": Color(0xFFfca5a5), 123 | "red-400": Color(0xFFf87171), 124 | "red-500": Color(0xFFef4444), 125 | "red-600": Color(0xFFdc2626), 126 | "red-700": Color(0xFFb91c1c), 127 | "red-800": Color(0xFF991b1b), 128 | "red-900": Color(0xFF7f1d1d), 129 | "red-950": Color(0xFF450a0a), 130 | "orange-50": Color(0xFFfff7ed), 131 | "orange-100": Color(0xFFffedd5), 132 | "orange-200": Color(0xFFfed7aa), 133 | "orange-300": Color(0xFFfdba74), 134 | "orange-400": Color(0xFFfb923c), 135 | "orange-500": Color(0xFFf97316), 136 | "orange-600": Color(0xFFea580c), 137 | "orange-700": Color(0xFFc2410c), 138 | "orange-800": Color(0xFF9a3412), 139 | "orange-900": Color(0xFF7c2d12), 140 | "orange-950": Color(0xFF431407), 141 | "amber-50": Color(0xFFfffbeb), 142 | "amber-100": Color(0xFFfef3c7), 143 | "amber-200": Color(0xFFfde68a), 144 | "amber-300": Color(0xFFfcd34d), 145 | "amber-400": Color(0xFFfbbf24), 146 | "amber-500": Color(0xFFf59e0b), 147 | "amber-600": Color(0xFFd97706), 148 | "amber-700": Color(0xFFb45309), 149 | "amber-800": Color(0xFF92400e), 150 | "amber-900": Color(0xFF78350f), 151 | "amber-950": Color(0xFF451a03), 152 | "yellow-50": Color(0xFFfefce8), 153 | "yellow-100": Color(0xFFfef9c3), 154 | "yellow-200": Color(0xFFfef08a), 155 | "yellow-300": Color(0xFFfde047), 156 | "yellow-400": Color(0xFFfacc15), 157 | "yellow-500": Color(0xFFeab308), 158 | "yellow-600": Color(0xFFca8a04), 159 | "yellow-700": Color(0xFFa16207), 160 | "yellow-800": Color(0xFF854d0e), 161 | "yellow-900": Color(0xFF713f12), 162 | "yellow-950": Color(0xFF422006), 163 | "lime-50": Color(0xFFf7fee7), 164 | "lime-100": Color(0xFFecfccb), 165 | "lime-200": Color(0xFFd9f99d), 166 | "lime-300": Color(0xFFbef264), 167 | "lime-400": Color(0xFFa3e635), 168 | "lime-500": Color(0xFF84cc16), 169 | "lime-600": Color(0xFF65a30d), 170 | "lime-700": Color(0xFF4d7c0f), 171 | "lime-800": Color(0xFF3f6212), 172 | "lime-900": Color(0xFF365314), 173 | "lime-950": Color(0xFF1a2e05), 174 | "green-50": Color(0xFFf0fdf4), 175 | "green-100": Color(0xFFdcfce7), 176 | "green-200": Color(0xFFbbf7d0), 177 | "green-300": Color(0xFF86efac), 178 | "green-400": Color(0xFF4ade80), 179 | "green-500": Color(0xFF22c55e), 180 | "green-600": Color(0xFF16a34a), 181 | "green-700": Color(0xFF15803d), 182 | "green-800": Color(0xFF166534), 183 | "green-900": Color(0xFF14532d), 184 | "green-950": Color(0xFF052e16), 185 | "emerald-50": Color(0xFFecfdf5), 186 | "emerald-100": Color(0xFFd1fae5), 187 | "emerald-200": Color(0xFFa7f3d0), 188 | "emerald-300": Color(0xFF6ee7b7), 189 | "emerald-400": Color(0xFF34d399), 190 | "emerald-500": Color(0xFF10b981), 191 | "emerald-600": Color(0xFF059669), 192 | "emerald-700": Color(0xFF047857), 193 | "emerald-800": Color(0xFF065f46), 194 | "emerald-900": Color(0xFF064e3b), 195 | "emerald-950": Color(0xFF022c22), 196 | "teal-50": Color(0xFFf0fdfa), 197 | "teal-100": Color(0xFFccfbf1), 198 | "teal-200": Color(0xFF99f6e4), 199 | "teal-300": Color(0xFF5eead4), 200 | "teal-400": Color(0xFF2dd4bf), 201 | "teal-500": Color(0xFF14b8a6), 202 | "teal-600": Color(0xFF0d9488), 203 | "teal-700": Color(0xFF0f766e), 204 | "teal-800": Color(0xFF115e59), 205 | "teal-900": Color(0xFF134e4a), 206 | "teal-950": Color(0xFF042f2e), 207 | "cyan-50": Color(0xFFecfeff), 208 | "cyan-100": Color(0xFFcffafe), 209 | "cyan-200": Color(0xFFa5f3fc), 210 | "cyan-300": Color(0xFF67e8f9), 211 | "cyan-400": Color(0xFF22d3ee), 212 | "cyan-500": Color(0xFF06b6d4), 213 | "cyan-600": Color(0xFF0891b2), 214 | "cyan-700": Color(0xFF0e7490), 215 | "cyan-800": Color(0xFF155e75), 216 | "cyan-900": Color(0xFF164e63), 217 | "cyan-950": Color(0xFF083344), 218 | "sky-50": Color(0xFFf0f9ff), 219 | "sky-100": Color(0xFFe0f2fe), 220 | "sky-200": Color(0xFFbae6fd), 221 | "sky-300": Color(0xFF7dd3fc), 222 | "sky-400": Color(0xFF38bdf8), 223 | "sky-500": Color(0xFF0ea5e9), 224 | "sky-600": Color(0xFF0284c7), 225 | "sky-700": Color(0xFF0369a1), 226 | "sky-800": Color(0xFF075985), 227 | "sky-900": Color(0xFF0c4a6e), 228 | "sky-950": Color(0xFF082f49), 229 | "blue-50": Color(0xFFeff6ff), 230 | "blue-100": Color(0xFFdbeafe), 231 | "blue-200": Color(0xFFbfdbfe), 232 | "blue-300": Color(0xFF93c5fd), 233 | "blue-400": Color(0xFF60a5fa), 234 | "blue-500": Color(0xFF3b82f6), 235 | "blue-600": Color(0xFF2563eb), 236 | "blue-700": Color(0xFF1d4ed8), 237 | "blue-800": Color(0xFF1e40af), 238 | "blue-900": Color(0xFF1e3a8a), 239 | "blue-950": Color(0xFF172554), 240 | "indigo-50": Color(0xFFeef2ff), 241 | "indigo-100": Color(0xFFe0e7ff), 242 | "indigo-200": Color(0xFFc7d2fe), 243 | "indigo-300": Color(0xFFa5b4fc), 244 | "indigo-400": Color(0xFF818cf8), 245 | "indigo-500": Color(0xFF6366f1), 246 | "indigo-600": Color(0xFF4f46e5), 247 | "indigo-700": Color(0xFF4338ca), 248 | "indigo-800": Color(0xFF3730a3), 249 | "indigo-900": Color(0xFF312e81), 250 | "indigo-950": Color(0xFF1e1b4b), 251 | "violet-50": Color(0xFFf5f3ff), 252 | "violet-100": Color(0xFFede9fe), 253 | "violet-200": Color(0xFFddd6fe), 254 | "violet-300": Color(0xFFc4b5fd), 255 | "violet-400": Color(0xFFa78bfa), 256 | "violet-500": Color(0xFF8b5cf6), 257 | "violet-600": Color(0xFF7c3aed), 258 | "violet-700": Color(0xFF6d28d9), 259 | "violet-800": Color(0xFF5b21b6), 260 | "violet-900": Color(0xFF4c1d95), 261 | "violet-950": Color(0xFF2e1065), 262 | "purple-50": Color(0xFFfaf5ff), 263 | "purple-100": Color(0xFFf3e8ff), 264 | "purple-200": Color(0xFFe9d5ff), 265 | "purple-300": Color(0xFFd8b4fe), 266 | "purple-400": Color(0xFFc084fc), 267 | "purple-500": Color(0xFFa855f7), 268 | "purple-600": Color(0xFF9333ea), 269 | "purple-700": Color(0xFF7e22ce), 270 | "purple-800": Color(0xFF6b21a8), 271 | "purple-900": Color(0xFF581c87), 272 | "purple-950": Color(0xFF3b0764), 273 | "fuchsia-50": Color(0xFFfdf4ff), 274 | "fuchsia-100": Color(0xFFfae8ff), 275 | "fuchsia-200": Color(0xFFf5d0fe), 276 | "fuchsia-300": Color(0xFFf0abfc), 277 | "fuchsia-400": Color(0xFFe879f9), 278 | "fuchsia-500": Color(0xFFd946ef), 279 | "fuchsia-600": Color(0xFFc026d3), 280 | "fuchsia-700": Color(0xFFa21caf), 281 | "fuchsia-800": Color(0xFF86198f), 282 | "fuchsia-900": Color(0xFF701a75), 283 | "fuchsia-950": Color(0xFF4a044e), 284 | "pink-50": Color(0xFFfdf2f8), 285 | "pink-100": Color(0xFFfce7f3), 286 | "pink-200": Color(0xFFfbcfe8), 287 | "pink-300": Color(0xFFf9a8d4), 288 | "pink-400": Color(0xFFf472b6), 289 | "pink-500": Color(0xFFec4899), 290 | "pink-600": Color(0xFFdb2777), 291 | "pink-700": Color(0xFFbe185d), 292 | "pink-800": Color(0xFF9d174d), 293 | "pink-900": Color(0xFF831843), 294 | "pink-950": Color(0xFF500724), 295 | "rose-50": Color(0xFFfff1f2), 296 | "rose-100": Color(0xFFffe4e6), 297 | "rose-200": Color(0xFFfecdd3), 298 | "rose-300": Color(0xFFfda4af), 299 | "rose-400": Color(0xFFfb7185), 300 | "rose-500": Color(0xFFf43f5e), 301 | "rose-600": Color(0xFFe11d48), 302 | "rose-700": Color(0xFFbe123c), 303 | "rose-800": Color(0xFF9f1239), 304 | "rose-900": Color(0xFF881337), 305 | "rose-950": Color(0xFF4c0519), 306 | }; 307 | -------------------------------------------------------------------------------- /easy_dsl/lib/src/div_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import 'options.dart'; 5 | 6 | class EasyDivImpl extends StatelessWidget { 7 | const EasyDivImpl({ 8 | super.key, 9 | required this.className, 10 | required this.option, 11 | this.children, 12 | }); 13 | final String className; 14 | final EasyOption option; 15 | final List? children; 16 | 17 | double spacing(String key) { 18 | return option.spacing[key] ?? 0; 19 | } 20 | 21 | double? spacingOr(String key) { 22 | return option.spacing[key]; 23 | } 24 | 25 | Color color(String key) { 26 | return option.color[key] ?? Colors.transparent; 27 | } 28 | 29 | double rounded(String key) { 30 | return option.borderRadius[key] ?? 0; 31 | } 32 | 33 | double aspectRatio(String key) { 34 | final v = option.aspectRatio[key]; 35 | if (kDebugMode && v == null) { 36 | print( 37 | "[EasyDSL] Warning: `aspect-$key` not set, please check your option."); 38 | } 39 | return v ?? 1; 40 | } 41 | 42 | List joinSpacer(List? children, Widget spacer) { 43 | if (children == null || children.isEmpty) { 44 | return const []; 45 | } 46 | final result = []; 47 | for (var i = 0; i < children.length; i++) { 48 | result.add(children[i]); 49 | if (i < children.length - 1) { 50 | result.add(spacer); 51 | } 52 | } 53 | return result; 54 | } 55 | 56 | @override 57 | Widget build(BuildContext context) { 58 | return const Placeholder(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /easy_dsl/lib/src/options.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | import 'defaults.dart'; 3 | 4 | // TODO 5 | class EasyOption { 6 | final Map spacing; 7 | 8 | final Map borderRadius; 9 | 10 | final Map aspectRatio; 11 | 12 | final Map color; 13 | 14 | const EasyOption({ 15 | this.spacing = defaultSpacing, 16 | this.color = defaultColor, 17 | this.aspectRatio = defaultAspectRatio, 18 | this.borderRadius = defaultBorderRadius, 19 | }); 20 | 21 | const EasyOption.empty() 22 | : color = defaultColor, 23 | spacing = defaultSpacing, 24 | aspectRatio = defaultAspectRatio, 25 | borderRadius = defaultBorderRadius; 26 | 27 | EasyOption.extend({ 28 | Map? extendSpacing, 29 | Map? extendBorderRadius, 30 | Map? extendAspectRatio, 31 | Map? extendColor, 32 | }) : spacing = {...defaultSpacing, ...?extendSpacing}, 33 | borderRadius = {...defaultBorderRadius, ...?extendBorderRadius}, 34 | aspectRatio = {...defaultAspectRatio, ...?extendAspectRatio}, 35 | color = {...defaultColor, ...?extendColor}; 36 | } 37 | -------------------------------------------------------------------------------- /easy_dsl/lib/src/utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | int easyEditDistance(List str1, List str2) { 4 | int len1 = str1.length; 5 | int len2 = str2.length; 6 | 7 | List> dp = List.generate( 8 | len1 + 1, (i) => List.generate(len2 + 1, (j) => 0, growable: false), 9 | growable: false); 10 | 11 | for (int i = 0; i <= len1; i++) { 12 | for (int j = 0; j <= len2; j++) { 13 | if (i == 0) { 14 | dp[i][j] = j; 15 | } else if (j == 0) { 16 | dp[i][j] = i; 17 | } else if (str1[i - 1] == str2[j - 1]) { 18 | dp[i][j] = dp[i - 1][j - 1]; 19 | } else { 20 | dp[i][j] = 1 + min(dp[i - 1][j], min(dp[i][j - 1], dp[i - 1][j - 1])); 21 | } 22 | } 23 | } 24 | 25 | return dp[len1][len2]; 26 | } 27 | -------------------------------------------------------------------------------- /easy_dsl/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: easy_dsl 2 | version: 0.0.4 3 | description: Streamlining Flutter UI Development with DSL-Based Code Generation 4 | repository: https://github.com/zzzgydi/easy_dsl 5 | homepage: https://github.com/zzzgydi/easy_dsl 6 | topics: 7 | - dsl 8 | - tailwind 9 | - build-runner 10 | - codegen 11 | 12 | environment: 13 | sdk: ^3.2.3 14 | 15 | dependencies: 16 | flutter: 17 | sdk: flutter 18 | 19 | dev_dependencies: 20 | lints: ^2.1.0 21 | test: ^1.24.0 22 | -------------------------------------------------------------------------------- /easy_dsl_gen/.gitignore: -------------------------------------------------------------------------------- 1 | # https://dart.dev/guides/libraries/private-files 2 | # Created by `dart pub` 3 | .dart_tool/ 4 | 5 | # Avoid committing pubspec.lock for library packages; see 6 | # https://dart.dev/guides/libraries/private-files#pubspeclock. 7 | pubspec.lock 8 | -------------------------------------------------------------------------------- /easy_dsl_gen/.pubignore: -------------------------------------------------------------------------------- 1 | 2 | test 3 | -------------------------------------------------------------------------------- /easy_dsl_gen/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.0.4 2 | 3 | - support `SafeArea` 4 | - support children is null 5 | - optimize hot reload 6 | 7 | # 0.0.3 8 | 9 | - fix hot reload not works 10 | 11 | # 0.0.2 12 | 13 | - support `Row`, `Column` and `Stack` 14 | - support `width`, `height`, `maxWidth`, `maxHeight` and so on 15 | - support `background color` 16 | - support `border` and `borderRadius` 17 | - support `margin` and `padding` 18 | - support `aspectRatio` and `opacity` 19 | 20 | # 0.0.1 21 | 22 | - Initial version. 23 | -------------------------------------------------------------------------------- /easy_dsl_gen/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 zzzgydi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /easy_dsl_gen/README.md: -------------------------------------------------------------------------------- 1 | # EasyDSL: Streamlining Flutter UI Development (WIP) 2 | 3 | ## Introduction 4 | 5 | EasyDSL is a Dart package that enhances Flutter UI development by introducing the concept of Domain-Specific Language (DSL). Drawing inspiration from the simplicity of TailwindCSS, EasyDSL enables Flutter developers to use DSL strings to generate Flutter widgets, greatly simplifying the creation of complex user interfaces. 6 | 7 | The project is currently under heavy development. Many Tailwind properties are yet to be adapted. Due to the differences between Flutter and the web, not all Tailwind classes will be supported. Additionally, some unique classes will be incorporated. 8 | 9 | ## Features 10 | 11 | - Offers a development experience akin to Tailwind CSS 12 | - Relies on code generation, ensuring zero runtime cost 13 | - Continuous development to expand DSL features 14 | 15 | ## Installation 16 | 17 | Add these dependencies to `pubspec.yaml`. 18 | 19 | ```yaml 20 | dependencies: 21 | easy_dsl: latest 22 | 23 | dev_dependencies: 24 | build_runner: ^2.4.0 25 | easy_dsl_gen: latest 26 | ``` 27 | 28 | ## Usage 29 | 30 | You must initially set the Widget's name as `Div`. 31 | 32 | ```dart 33 | // div.dart 34 | import 'package:flutter/foundation.dart'; 35 | import 'package:flutter/material.dart'; 36 | import 'package:easy_dsl/easy_dsl.dart'; 37 | 38 | part 'div.easy.g.dart'; 39 | 40 | @EasyDSL() 41 | class Div extends $Div { 42 | const Div({super.key, required super.className, super.children}) 43 | : super(option: const EasyOption.empty()); 44 | } 45 | ``` 46 | 47 | Next, include the following in your `build.yaml` file: 48 | 49 | ```yaml 50 | targets: 51 | $default: 52 | builders: 53 | easy_dsl_gen|easy_gen: 54 | generate_for: 55 | - lib/**.dart 56 | ``` 57 | 58 | Finally, you can use the code in your project as illustrated below: 59 | 60 | ```dart 61 | @override 62 | Widget build(BuildContext context) { 63 | const className = "items-center bg-black"; 64 | 65 | return const Div( 66 | className: className, 67 | children: [ 68 | Div( 69 | className: "bg-red-500 pt-[10] px-[100] py-[20]", 70 | children: [ 71 | Div( 72 | className: "p-10 bg-gray-400", 73 | children: [Text('This is'), Text('EasyDSL')], 74 | ), 75 | SizedBox(height: 20), 76 | Div( 77 | className: "flex items-center p-[10] bg-gray-500", 78 | children: [Text('Hello World')], 79 | ), 80 | ], 81 | ), 82 | ], 83 | ); 84 | } 85 | ``` 86 | 87 | **Note:** The value of `className` must be a string literal or a constant to be resolved at compile time. 88 | 89 | ## Running the code generator 90 | 91 | Once you have modified the code related to EasyDSL, you should execute the following command to generate the missing `.easy.g.dart` generated dart files. 92 | 93 | ```bash 94 | dart run build_runner build 95 | ``` 96 | 97 | Alternatively, you can execute the following command to continuously monitor file changes. 98 | 99 | ```bash 100 | dart run build_runner watch 101 | ``` 102 | 103 | ## Contributing 104 | 105 | Contributions to EasyDSL are highly encouraged! If you have suggestions or code contributions, please feel free to open an issue or submit a pull request. 106 | 107 | ## License 108 | 109 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 110 | -------------------------------------------------------------------------------- /easy_dsl_gen/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:lints/recommended.yaml 2 | -------------------------------------------------------------------------------- /easy_dsl_gen/build.yaml: -------------------------------------------------------------------------------- 1 | builders: 2 | easy_gen: 3 | import: "package:easy_dsl_gen/easy_dsl_gen.dart" 4 | builder_factories: ["easyPartBuilder"] 5 | build_extensions: { ".dart": [".easy"] } 6 | auto_apply: dependents 7 | build_to: cache 8 | applies_builders: ["|easy_combine"] 9 | 10 | easy_combine: 11 | import: "package:easy_dsl_gen/easy_dsl_gen.dart" 12 | builder_factories: ["easyCombineBuilder"] 13 | build_extensions: { ".easy": [".easy.g.dart"] } 14 | build_to: source 15 | # applies_builders: ["easy_dsl_gen|custom_cleanup"] 16 | # post_process_builders: 17 | # custom_cleanup: 18 | # import: "package:easy_dsl_gen/easy_dsl_gen.dart" 19 | # builder_factory: "customCleanup" 20 | -------------------------------------------------------------------------------- /easy_dsl_gen/lib/builder/combine_builder.dart: -------------------------------------------------------------------------------- 1 | import 'package:build/build.dart'; 2 | import 'package:glob/glob.dart'; 3 | 4 | import '../generator/cls_generator.dart'; 5 | import '../generator/cls_item.dart'; 6 | 7 | class EasyCombineBuilder implements Builder { 8 | @override 9 | final buildExtensions = const { 10 | '.easy': ['.easy.g.dart'] 11 | }; 12 | 13 | @override 14 | Future build(BuildStep buildStep) async { 15 | var inputId = buildStep.inputId; 16 | final outputId = buildStep.allowedOutputs.single; 17 | 18 | var contents = await buildStep.readAsString(inputId); 19 | 20 | if (!contents.contains("// [element]: EasyDSL")) { 21 | return; 22 | } 23 | 24 | log.warning( 25 | "[Easy Combine] build: ${inputId.path}, outputId: ${outputId.path}"); 26 | 27 | final assetIds = await buildStep.findAssets(Glob("**/*.easy")).toList() 28 | ..sort(); 29 | 30 | final clsSet = {}; 31 | 32 | await Future.wait( 33 | assetIds.map((e) async { 34 | final content = await buildStep.readAsString(e); 35 | final classNames = _getClassNames(content); 36 | clsSet.addAll(classNames); 37 | }), 38 | ); 39 | 40 | final items = clsSet.map((e) => ClsItem.fromSrc(e)).toList(); 41 | 42 | final generator = ClsGenerator( 43 | partOfUri: inputId.changeExtension('.dart').uri.toString(), 44 | items: items, 45 | ); 46 | 47 | await buildStep.writeAsString(outputId, generator.generate()); 48 | } 49 | 50 | List _getClassNames(String content) { 51 | final lines = content.split("\n"); 52 | return lines 53 | .map((e) => e.trim()) 54 | .where((e) => e.startsWith("// [className]:")) 55 | .map((e) => e.replaceFirst("// [className]:", "").trim()) 56 | .where((e) => e.isNotEmpty) 57 | .toList(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /easy_dsl_gen/lib/builder/part_builder.dart: -------------------------------------------------------------------------------- 1 | import 'package:build/build.dart'; 2 | import 'package:easy_dsl/annotations.dart'; 3 | import 'package:source_gen/source_gen.dart'; 4 | 5 | import '../visitor/classname_visitor.dart'; 6 | 7 | class EasyPartBuilder implements Builder { 8 | @override 9 | final buildExtensions = const { 10 | '.dart': ['.easy'] 11 | }; 12 | 13 | @override 14 | Future build(BuildStep buildStep) async { 15 | final inputId = buildStep.inputId; 16 | final outputId = buildStep.allowedOutputs.single; 17 | 18 | if (inputId.path.endsWith(".g.dart")) { 19 | return; 20 | } 21 | 22 | final StringBuffer output = StringBuffer(); 23 | 24 | // log.warning("build: ${inputId.path}, outputId: ${outputId.path}"); 25 | 26 | final ele = await buildStep.resolver.libraryFor(buildStep.inputId); 27 | final visitor = ClassNameVisitor(nodeName: "Div"); 28 | 29 | await Future.wait( 30 | ele.topLevelElements.map((e) async { 31 | final ast = await buildStep.resolver.astNodeFor(e, resolve: true); 32 | if (ast == null) { 33 | return; 34 | } 35 | ast.visitChildren(visitor); 36 | }), 37 | ); 38 | 39 | // Find all className constants 40 | for (var className in visitor.foundClassNames) { 41 | output.writeln("// [className]: $className"); 42 | } 43 | 44 | /// Find the EasyDSL annotation 45 | final allElements = [ 46 | ele, 47 | ...ele.topLevelElements, 48 | ...ele.libraryImports, 49 | ...ele.libraryExports, 50 | ...ele.parts, 51 | ]; 52 | 53 | TypeChecker typeChecker = TypeChecker.fromRuntime(EasyDSL); 54 | 55 | for (final element in allElements) { 56 | final anno = typeChecker.firstAnnotationOf(element); 57 | if (anno != null) { 58 | output.writeln("// [element]: ${anno.type}"); 59 | } 60 | } 61 | 62 | final out = output.toString(); 63 | if (out.isNotEmpty) { 64 | await buildStep.writeAsString(outputId, out); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /easy_dsl_gen/lib/easy_dsl_gen.dart: -------------------------------------------------------------------------------- 1 | library easy_dsl_gen; 2 | 3 | import 'package:build/build.dart'; 4 | 5 | import 'builder/combine_builder.dart'; 6 | import 'builder/part_builder.dart'; 7 | 8 | Builder easyPartBuilder(BuilderOptions options) => EasyPartBuilder(); 9 | 10 | Builder easyCombineBuilder(BuilderOptions options) => EasyCombineBuilder(); 11 | -------------------------------------------------------------------------------- /easy_dsl_gen/lib/generator/cls_generator.dart: -------------------------------------------------------------------------------- 1 | import 'package:dart_style/dart_style.dart'; 2 | 3 | import 'cls_item.dart'; 4 | import 'widget_generator.dart'; 5 | 6 | class ClsGenerator { 7 | ClsGenerator({ 8 | required this.partOfUri, 9 | required this.items, 10 | }); 11 | 12 | final String partOfUri; 13 | final List items; 14 | 15 | final formatter = DartFormatter(); 16 | 17 | String generate() { 18 | final taskMap = >{}; 19 | for (final item in items) { 20 | final hashCls = item.hashCls; 21 | if (taskMap.containsKey(hashCls)) { 22 | taskMap[hashCls]!.add(item); 23 | } else { 24 | taskMap[hashCls] = [item]; 25 | } 26 | } 27 | 28 | final outMap = {}; 29 | List widgetList = []; 30 | List constructorList = []; 31 | 32 | var index = 1; 33 | taskMap.forEach((key, value) { 34 | final constructor = "\$Div${index++}"; 35 | final item = value.first; 36 | widgetList.add(WidgetGenerator(item, constructor).generate()); 37 | constructorList.add(constructor); 38 | 39 | for (final item in value) { 40 | outMap[item.srcCls] = constructor; 41 | } 42 | }); 43 | 44 | final widgetCode = widgetList.join("\n\n"); 45 | 46 | final output = StringBuffer(); 47 | output.writeln("// Generated by EasyDSL"); 48 | output.writeln("// Do Not Modify By Hand\n"); 49 | output.writeln( 50 | "// ignore_for_file: unused_element, prefer_const_constructors"); 51 | 52 | output.writeln("part of '$partOfUri';\n"); 53 | 54 | // output.writeln(_genMapCode(outMap)); 55 | output.writeln(_genDivCode(constructorList, outMap)); 56 | // output.writeln(_genConstCode()); 57 | output.writeln(widgetCode); 58 | return formatter.format(output.toString()); 59 | } 60 | 61 | String _genMapCode(Map map) { 62 | final buffer = StringBuffer(); 63 | buffer.writeln( 64 | "final divMap = ?)>{"); 65 | map.forEach((key, value) { 66 | buffer.writeln( 67 | " \"$key\": (cls, o, c) => $value(className: cls, option: o, children: c),"); 68 | }); 69 | buffer.writeln("};"); 70 | return buffer.toString(); 71 | } 72 | 73 | String _genDivCode(List constructorList, Map map) { 74 | return "class \$Div extends StatelessWidget {\n" 75 | " const \$Div({\n" 76 | " super.key, required this.className, this.children,\n" 77 | " this.option = const EasyOption.empty(),\n" 78 | " });\n" 79 | " final String className;\n" 80 | " final EasyOption option;\n" 81 | " final List? children;\n\n" 82 | " @override\n" 83 | " Widget build(BuildContext context) {\n" 84 | " ${_genMapCode(map)}\n" 85 | " var creator = divMap[className.trim()];\n" 86 | " ${_genDebugCode()}\n" 87 | " return creator != null ? creator(className, option, children) : (children?.length ?? 0) > 0 ? Column(children: children!) : const SizedBox();\n" 88 | " }\n" 89 | "}\n"; 90 | } 91 | 92 | String _genDebugCode() { 93 | return """ 94 | if (kDebugMode) { 95 | var clsList = className.split(" ").map((e) => e.trim()).toList(); 96 | for (var e in divMap.entries) { 97 | var l = e.key.split(" ").map((e) => e.trim()).toList(); 98 | var d = easyEditDistance(clsList, l); 99 | if ((d <= 1 && l.length > 2) || (d <= 3 && l.length > 8)) { 100 | creator = e.value; 101 | break; 102 | } 103 | } 104 | } 105 | """; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /easy_dsl_gen/lib/generator/cls_item.dart: -------------------------------------------------------------------------------- 1 | class ClsItem { 2 | const ClsItem({ 3 | required this.srcCls, 4 | required this.hashCls, 5 | required this.clsSet, 6 | }); 7 | 8 | final String srcCls; // source className 9 | final String hashCls; // hash className 10 | final Set clsSet; 11 | 12 | static ClsItem fromSrc(String srcCls) { 13 | final clsSet = srcCls 14 | .split(" ") 15 | .map((e) => e.trim()) 16 | .where((e) => e.isNotEmpty) 17 | .toSet(); 18 | 19 | return ClsItem( 20 | srcCls: srcCls, 21 | hashCls: clsSet.join(" "), 22 | clsSet: clsSet, 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /easy_dsl_gen/lib/generator/code/code_gen.dart: -------------------------------------------------------------------------------- 1 | class CodeConstrutor { 2 | final String name; 3 | final map = {}; 4 | 5 | CodeConstrutor(this.name); 6 | 7 | void add(String key, String? value) { 8 | if (value != null) map[key] = value; 9 | } 10 | 11 | bool hasFields() { 12 | return map.isNotEmpty; 13 | } 14 | 15 | /// must return a value 16 | String generate() { 17 | final field = map.entries.map((e) => "${e.key}: ${e.value}").join(","); 18 | return "$name($field)"; 19 | } 20 | 21 | /// return null if there is no value 22 | String? maybeGenerate() { 23 | if (map.isEmpty) return null; 24 | return generate(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /easy_dsl_gen/lib/generator/iter/aspect.dart: -------------------------------------------------------------------------------- 1 | import 'attr.dart'; 2 | 3 | final aspectPattern = RegExp(r'^aspect-(\[\d+\/\d+\]|.+)$'); 4 | 5 | class AspectIter extends AttrIter { 6 | String? _ratio; 7 | 8 | @override 9 | void iter(String cls) { 10 | if (aspectPattern.hasMatch(cls)) { 11 | var value = aspectPattern.firstMatch(cls)?.group(1); 12 | if (value == null) return; 13 | if (value.startsWith('[') && value.endsWith(']')) { 14 | value = value.substring(1, value.length - 1); 15 | } else if (value == 'none' || value == "auto") { 16 | value = null; 17 | } else { 18 | value = "aspectRatio('$value')"; 19 | } 20 | _ratio = value; 21 | } 22 | } 23 | 24 | @override 25 | String? wrapper(String? child) { 26 | if (_ratio == null) return child; 27 | return "AspectRatio(aspectRatio: $_ratio, child: $child)"; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /easy_dsl_gen/lib/generator/iter/attr.dart: -------------------------------------------------------------------------------- 1 | abstract class AttrIter { 2 | void iter(String cls); 3 | 4 | String? generate() { 5 | throw UnimplementedError(); 6 | } 7 | 8 | String? wrapper(String? child) { 9 | throw UnimplementedError(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /easy_dsl_gen/lib/generator/iter/background.dart: -------------------------------------------------------------------------------- 1 | import 'attr.dart'; 2 | import 'utils.dart'; 3 | 4 | final bgColorPattern = RegExp(r'^bg-(\[.+?\]|.+?)(?:/(\[\.\d+\]|\d+))?$'); 5 | 6 | class BackgroundIter extends AttrIter { 7 | String? color; 8 | 9 | @override 10 | void iter(String cls) { 11 | if (bgColorPattern.hasMatch(cls)) { 12 | final match = bgColorPattern.firstMatch(cls)!; 13 | final color = match.group(1); 14 | final alpha = match.group(2); 15 | 16 | this.color = parseColor(color, alpha); 17 | } 18 | } 19 | 20 | @override 21 | String? generate() { 22 | return color; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /easy_dsl_gen/lib/generator/iter/border.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_dsl_gen/generator/code/code_gen.dart'; 2 | 3 | import 'attr.dart'; 4 | import 'utils.dart'; 5 | 6 | final borderWidthPattern = 7 | RegExp(r'^border(-[tlrbxy])?(?:-(\d+|\[\d+(?:\.\d+)?\]))?$'); 8 | final borderStylePattern = 9 | RegExp(r'^border(-[tlrbxy])?-(solid|none|dashed|dotted|double)$'); 10 | final borderColorPattern = 11 | RegExp(r'^border(-[tlrbxy])?-(\[.+?\]|.+?)(?:/(\[\.\d+\]|\d+))?$'); 12 | 13 | class BorderIter extends AttrIter { 14 | String? tColor, tWidth, tStyle; 15 | String? lColor, lWidth, lStyle; 16 | String? rColor, rWidth, rStyle; 17 | String? bColor, bWidth, bStyle; 18 | 19 | @override 20 | void iter(String cls) { 21 | if (borderWidthPattern.hasMatch(cls)) { 22 | final match = borderWidthPattern.firstMatch(cls)!; 23 | final side = match.group(1); 24 | var width = (match.group(2) ?? "1"); 25 | if (width.startsWith('[') && width.endsWith(']')) { 26 | width = width.substring(1, width.length - 1); 27 | } 28 | switch (side) { 29 | case "-t": 30 | tWidth = width; 31 | break; 32 | case "-l": 33 | lWidth = width; 34 | break; 35 | case "-r": 36 | rWidth = width; 37 | break; 38 | case "-b": 39 | bWidth = width; 40 | break; 41 | case "-x": 42 | lWidth = width; 43 | rWidth = width; 44 | break; 45 | case "-y": 46 | tWidth = width; 47 | bWidth = width; 48 | break; 49 | default: 50 | tWidth = width; 51 | lWidth = width; 52 | rWidth = width; 53 | bWidth = width; 54 | break; 55 | } 56 | return; 57 | } 58 | 59 | if (borderStylePattern.hasMatch(cls)) { 60 | final match = borderStylePattern.firstMatch(cls)!; 61 | final side = match.group(1); 62 | var style = match.group(2) ?? "solid"; 63 | if (style != "none") { 64 | style = "solid"; 65 | } 66 | switch (side) { 67 | case "-t": 68 | tStyle = "BorderStyle.$style"; 69 | break; 70 | case "-l": 71 | lStyle = "BorderStyle.$style"; 72 | break; 73 | case "-r": 74 | rStyle = "BorderStyle.$style"; 75 | break; 76 | case "-b": 77 | bStyle = "BorderStyle.$style"; 78 | break; 79 | case "-x": 80 | lStyle = "BorderStyle.$style"; 81 | rStyle = "BorderStyle.$style"; 82 | break; 83 | case "-y": 84 | tStyle = "BorderStyle.$style"; 85 | bStyle = "BorderStyle.$style"; 86 | break; 87 | default: 88 | tStyle = "BorderStyle.$style"; 89 | lStyle = "BorderStyle.$style"; 90 | rStyle = "BorderStyle.$style"; 91 | bStyle = "BorderStyle.$style"; 92 | break; 93 | } 94 | return; 95 | } 96 | 97 | if (borderColorPattern.hasMatch(cls)) { 98 | final match = borderColorPattern.firstMatch(cls)!; 99 | final side = match.group(1); 100 | final color = match.group(2); 101 | final alpha = match.group(3); 102 | 103 | final value = parseColor(color, alpha); 104 | if (value == null) return; 105 | 106 | switch (side) { 107 | case "-t": 108 | tColor = value; 109 | break; 110 | case "-l": 111 | lColor = value; 112 | break; 113 | case "-r": 114 | rColor = value; 115 | break; 116 | case "-b": 117 | bColor = value; 118 | break; 119 | case "-x": 120 | lColor = value; 121 | rColor = value; 122 | break; 123 | case "-y": 124 | tColor = value; 125 | bColor = value; 126 | break; 127 | default: 128 | tColor = value; 129 | lColor = value; 130 | rColor = value; 131 | bColor = value; 132 | break; 133 | } 134 | return; 135 | } 136 | } 137 | 138 | @override 139 | String? generate() { 140 | /// must set border width 141 | /// if not set, return null 142 | if (tWidth == null && lWidth == null && rWidth == null && bWidth == null) { 143 | return null; 144 | } 145 | 146 | final top = tWidth != null 147 | ? (CodeConstrutor("BorderSide") 148 | ..add("color", tColor) 149 | ..add("width", tWidth) 150 | ..add("style", tStyle)) 151 | .maybeGenerate() 152 | : null; 153 | final left = lWidth != null 154 | ? (CodeConstrutor("BorderSide") 155 | ..add("color", lColor) 156 | ..add("width", lWidth) 157 | ..add("style", lStyle)) 158 | .maybeGenerate() 159 | : null; 160 | final right = rWidth != null 161 | ? (CodeConstrutor("BorderSide") 162 | ..add("color", rColor) 163 | ..add("width", rWidth) 164 | ..add("style", rStyle)) 165 | .maybeGenerate() 166 | : null; 167 | final bottom = bWidth != null 168 | ? (CodeConstrutor("BorderSide") 169 | ..add("color", bColor) 170 | ..add("width", bWidth) 171 | ..add("style", bStyle)) 172 | .maybeGenerate() 173 | : null; 174 | 175 | return (CodeConstrutor("Border") 176 | ..add("top", top) 177 | ..add("left", left) 178 | ..add("right", right) 179 | ..add("bottom", bottom)) 180 | .generate(); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /easy_dsl_gen/lib/generator/iter/box.dart: -------------------------------------------------------------------------------- 1 | import 'attr.dart'; 2 | import '../code/code_gen.dart'; 3 | 4 | final spacePattern = RegExp(r'^space(-x|y)?-(\[\d+(?:\.\d+)\]|.+)$'); 5 | final gapPattern = RegExp(r'^gap(-x|y)?-(\[\d+(?:\.\d+)\]|.+)$'); 6 | 7 | class BoxIter extends AttrIter { 8 | String _box = "Column"; 9 | final _set = {}; 10 | String _size = "MainAxisSize.max"; 11 | String _align = "MainAxisAlignment.start"; 12 | String _cross = "CrossAxisAlignment.start"; 13 | 14 | String _stackAlign = "AlignmentDirectional.topStart"; 15 | String _stackFit = "StackFit.loose"; 16 | String _stackClip = "Clip.hardEdge"; 17 | String? _stackDirection; 18 | 19 | String? _gapX, _gapY; 20 | 21 | @override 22 | void iter(String cls) { 23 | _set.add(cls); 24 | 25 | switch (cls) { 26 | case "row": 27 | case "flex": 28 | if (!_set.contains("flex-col")) _box = "Row"; 29 | break; 30 | case "col": 31 | case "flex-col": 32 | _box = "Column"; 33 | break; 34 | // MainAxisSize 35 | case "inline": 36 | case "inline-block": 37 | _size = "MainAxisSize.min"; 38 | break; 39 | case "block": 40 | _size = "MainAxisSize.max"; 41 | break; 42 | // MainAxisAlignment 43 | case "justify-center": 44 | _align = "MainAxisAlignment.center"; 45 | break; 46 | case "justify-end": 47 | _align = "MainAxisAlignment.end"; 48 | break; 49 | case "justify-between": 50 | _align = "MainAxisAlignment.spaceBetween"; 51 | break; 52 | case "justify-around": 53 | _align = "MainAxisAlignment.spaceAround"; 54 | break; 55 | case "justify-evenly": 56 | _align = "MainAxisAlignment.spaceEvenly"; 57 | break; 58 | // CrossAxisAlignment 59 | case "items-start": 60 | _cross = "CrossAxisAlignment.start"; 61 | break; 62 | case "items-end": 63 | _cross = "CrossAxisAlignment.end"; 64 | break; 65 | case "items-center": 66 | _cross = "CrossAxisAlignment.center"; 67 | break; 68 | case "items-stretch": 69 | _cross = "CrossAxisAlignment.stretch"; 70 | break; 71 | case "items-baseline": 72 | _cross = "CrossAxisAlignment.baseline"; 73 | break; 74 | 75 | // stack 76 | case "stack": 77 | _box = "Stack"; 78 | break; 79 | // stack fit 80 | case "stack-expand": 81 | _stackFit = "StackFit.expand"; 82 | break; 83 | case "stack-loose": 84 | _stackFit = "StackFit.loose"; 85 | break; 86 | // stack text direction 87 | case "stack-ltr": 88 | _stackDirection = "TextDirection.ltr"; 89 | break; 90 | case "stack-rtl": 91 | _stackDirection = "TextDirection.rtl"; 92 | break; 93 | // stack clip 94 | case "stack-soft": 95 | _stackClip = "Clip.antiAlias"; 96 | break; 97 | case "stack-hard": 98 | _stackClip = "Clip.hardEdge"; 99 | break; 100 | case "stack-soft-save": 101 | _stackClip = "Clip.antiAliasWithSaveLayer"; 102 | break; 103 | // stack align 104 | case "stack-top-start": 105 | _stackAlign = "AlignmentDirectional.topStart"; 106 | break; 107 | case "stack-top-center": 108 | _stackAlign = "AlignmentDirectional.topCenter"; 109 | break; 110 | case "stack-top-end": 111 | _stackAlign = "AlignmentDirectional.topEnd"; 112 | break; 113 | case "stack-center-start": 114 | _stackAlign = "AlignmentDirectional.centerStart"; 115 | break; 116 | case "stack-center": 117 | _stackAlign = "AlignmentDirectional.center"; 118 | break; 119 | case "stack-center-end": 120 | _stackAlign = "AlignmentDirectional.centerEnd"; 121 | break; 122 | case "stack-bottom-start": 123 | _stackAlign = "AlignmentDirectional.bottomStart"; 124 | break; 125 | case "stack-bottom-center": 126 | _stackAlign = "AlignmentDirectional.bottomCenter"; 127 | break; 128 | case "stack-bottom-end": 129 | _stackAlign = "AlignmentDirectional.bottomEnd"; 130 | break; 131 | default: 132 | if (spacePattern.hasMatch(cls)) { 133 | final match = spacePattern.firstMatch(cls)!; 134 | final axis = match.group(1); 135 | final value = match.group(2); 136 | if (value == null) return; 137 | 138 | if (axis == "-x") { 139 | _gapX = parseValue(value); 140 | } else if (axis == "-y") { 141 | _gapY = parseValue(value); 142 | } else { 143 | _gapX = parseValue(value); 144 | _gapY = _gapX; 145 | } 146 | } else if (gapPattern.hasMatch(cls)) { 147 | final match = gapPattern.firstMatch(cls)!; 148 | final axis = match.group(1); 149 | final value = match.group(2); 150 | if (value == null) return; 151 | 152 | if (axis == "-x") { 153 | _gapX = parseValue(value); 154 | } else if (axis == "-y") { 155 | _gapY = parseValue(value); 156 | } else { 157 | _gapX = parseValue(value); 158 | _gapY = _gapX; 159 | } 160 | } 161 | break; 162 | } 163 | } 164 | 165 | @override 166 | String? generate() { 167 | if (_box == "Stack") { 168 | return (CodeConstrutor(_box) 169 | ..add("fit", _stackFit) 170 | ..add("clipBehavior", _stackClip) 171 | ..add("alignment", _stackAlign) 172 | ..add("textDirection", _stackDirection) 173 | ..add("children", "children")) 174 | .generate(); 175 | } 176 | 177 | final flex = CodeConstrutor(_box) 178 | ..add("mainAxisSize", _size) 179 | ..add("mainAxisAlignment", _align) 180 | ..add("crossAxisAlignment", _cross); 181 | 182 | if (_gapX != null && _box == "Row") { 183 | flex.add("children", "joinSpacer(children, SizedBox(width: $_gapX))"); 184 | } else if (_gapY != null && _box == "Column") { 185 | flex.add("children", "joinSpacer(children, SizedBox(height: $_gapY))"); 186 | } else { 187 | flex.add("children", "children!"); 188 | } 189 | 190 | return flex.generate(); 191 | } 192 | 193 | String? parseValue(String value) { 194 | if (value.startsWith("[") && value.endsWith("]")) { 195 | return value.substring(1, value.length - 1); 196 | } 197 | return "spacing('$value')"; 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /easy_dsl_gen/lib/generator/iter/iter.dart: -------------------------------------------------------------------------------- 1 | export 'attr.dart'; 2 | export 'box.dart'; 3 | export 'background.dart'; 4 | export 'border.dart'; 5 | export 'padding.dart'; 6 | export 'margin.dart'; 7 | export 'size.dart'; 8 | export 'rounded.dart'; 9 | export 'aspect.dart'; 10 | export 'opacity.dart'; 11 | export 'safearea.dart'; 12 | // export 'text.dart'; 13 | // export 'text_style.dart'; 14 | -------------------------------------------------------------------------------- /easy_dsl_gen/lib/generator/iter/margin.dart: -------------------------------------------------------------------------------- 1 | import 'attr.dart'; 2 | 3 | class MarginIter extends AttrIter { 4 | String top = "0", left = "0", right = "0", bottom = "0"; 5 | 6 | @override 7 | void iter(String cls) { 8 | if (cls.startsWith("m-[")) { 9 | final v = _parseDouble(cls, 3); 10 | top = v; 11 | left = v; 12 | right = v; 13 | bottom = v; 14 | } else if (cls.startsWith("mt-[")) { 15 | top = _parseDouble(cls, 4); 16 | } else if (cls.startsWith("ml-[")) { 17 | left = _parseDouble(cls, 4); 18 | } else if (cls.startsWith("mr-[")) { 19 | right = _parseDouble(cls, 4); 20 | } else if (cls.startsWith("mb-[")) { 21 | bottom = _parseDouble(cls, 4); 22 | } else if (cls.startsWith("mx-[")) { 23 | left = _parseDouble(cls, 4); 24 | right = left; 25 | } else if (cls.startsWith("my-[")) { 26 | top = _parseDouble(cls, 4); 27 | bottom = top; 28 | } 29 | // parse option 30 | else if (cls.startsWith("m-")) { 31 | final v = _parseOption(cls, 2); 32 | top = v; 33 | left = v; 34 | right = v; 35 | bottom = v; 36 | } else if (cls.startsWith("mt-")) { 37 | top = _parseOption(cls, 3); 38 | } else if (cls.startsWith("ml-")) { 39 | left = _parseOption(cls, 3); 40 | } else if (cls.startsWith("mr-")) { 41 | right = _parseOption(cls, 3); 42 | } else if (cls.startsWith("mb-")) { 43 | bottom = _parseOption(cls, 3); 44 | } else if (cls.startsWith("mx-")) { 45 | left = _parseOption(cls, 3); 46 | right = left; 47 | } else if (cls.startsWith("my-")) { 48 | top = _parseOption(cls, 3); 49 | bottom = top; 50 | } 51 | } 52 | 53 | @override 54 | String? generate() { 55 | if (top == "0" && left == "0" && right == "0" && bottom == "0") { 56 | return null; 57 | } 58 | return "EdgeInsets.only(top: $top, left: $left, right: $right, bottom: $bottom)"; 59 | } 60 | 61 | _parseDouble(String cls, int begin) { 62 | final v = double.parse(cls.substring(begin, cls.length - 1)); 63 | if (v > 0 && !v.isNaN) return "$v"; 64 | return "0"; 65 | } 66 | 67 | _parseOption(String cls, int begin) => "spacing('${cls.substring(begin)}')"; 68 | } 69 | -------------------------------------------------------------------------------- /easy_dsl_gen/lib/generator/iter/opacity.dart: -------------------------------------------------------------------------------- 1 | import 'attr.dart'; 2 | 3 | final opacityPattern = RegExp(r'^opacity-(\[\.\d+\]|\d+)$'); 4 | 5 | class OpacityIter extends AttrIter { 6 | String? _opacity; 7 | 8 | @override 9 | void iter(String cls) { 10 | if (opacityPattern.hasMatch(cls)) { 11 | var value = opacityPattern.firstMatch(cls)?.group(1); 12 | if (value == null) return; 13 | if (value.startsWith('[') && value.endsWith(']')) { 14 | value = value.substring(1, value.length - 1); 15 | } else { 16 | var v = (double.parse(value) / 100); 17 | if (v > 1) v = 1; 18 | if (v < 0) v = 0; 19 | value = "$v"; 20 | } 21 | _opacity = value; 22 | } 23 | } 24 | 25 | @override 26 | String? wrapper(String? child) { 27 | if (_opacity == null) return child; 28 | return "Opacity(opacity: $_opacity, child: $child)"; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /easy_dsl_gen/lib/generator/iter/padding.dart: -------------------------------------------------------------------------------- 1 | import 'attr.dart'; 2 | 3 | class PaddingIter extends AttrIter { 4 | String top = "0", left = "0", right = "0", bottom = "0"; 5 | 6 | @override 7 | void iter(String cls) { 8 | if (cls.startsWith("p-[")) { 9 | final v = _parseDouble(cls, 3); 10 | top = v; 11 | left = v; 12 | right = v; 13 | bottom = v; 14 | } else if (cls.startsWith("pt-[")) { 15 | top = _parseDouble(cls, 4); 16 | } else if (cls.startsWith("pl-[")) { 17 | left = _parseDouble(cls, 4); 18 | } else if (cls.startsWith("pr-[")) { 19 | right = _parseDouble(cls, 4); 20 | } else if (cls.startsWith("pb-[")) { 21 | bottom = _parseDouble(cls, 4); 22 | } else if (cls.startsWith("px-[")) { 23 | left = _parseDouble(cls, 4); 24 | right = left; 25 | } else if (cls.startsWith("py-[")) { 26 | top = _parseDouble(cls, 4); 27 | bottom = top; 28 | } 29 | // parse option 30 | else if (cls.startsWith("p-")) { 31 | final v = _parseOption(cls, 2); 32 | top = v; 33 | left = v; 34 | right = v; 35 | bottom = v; 36 | } else if (cls.startsWith("pt-")) { 37 | top = _parseOption(cls, 3); 38 | } else if (cls.startsWith("pl-")) { 39 | left = _parseOption(cls, 3); 40 | } else if (cls.startsWith("pr-")) { 41 | right = _parseOption(cls, 3); 42 | } else if (cls.startsWith("pb-")) { 43 | bottom = _parseOption(cls, 3); 44 | } else if (cls.startsWith("px-")) { 45 | left = _parseOption(cls, 3); 46 | right = left; 47 | } else if (cls.startsWith("py-")) { 48 | top = _parseOption(cls, 3); 49 | bottom = top; 50 | } 51 | } 52 | 53 | @override 54 | String? generate() { 55 | if (top == "0" && left == "0" && right == "0" && bottom == "0") { 56 | return null; 57 | } 58 | return "EdgeInsets.only(top: $top, left: $left, right: $right, bottom: $bottom)"; 59 | } 60 | 61 | _parseDouble(String cls, int begin) { 62 | final v = double.parse(cls.substring(begin, cls.length - 1)); 63 | if (v > 0 && !v.isNaN) return "$v"; 64 | return "0"; 65 | } 66 | 67 | _parseOption(String cls, int begin) => "spacing('${cls.substring(begin)}')"; 68 | } 69 | -------------------------------------------------------------------------------- /easy_dsl_gen/lib/generator/iter/rounded.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_dsl_gen/generator/code/code_gen.dart'; 2 | 3 | import 'attr.dart'; 4 | 5 | final roundedPattern = 6 | RegExp(r'^rounded(-[tlrb]{1,2})?(?:-(\[\d+(?:\.\d+)?\]|.+))?$'); 7 | 8 | class RoundedIter extends AttrIter { 9 | String? tl, tr, bl, br; 10 | 11 | @override 12 | void iter(String cls) { 13 | if (cls == "rounded") { 14 | tl = "Radius.circular(rounded('default'))"; 15 | tr = tl; 16 | bl = tl; 17 | br = tl; 18 | return; 19 | } 20 | if (roundedPattern.hasMatch(cls)) { 21 | final match = roundedPattern.firstMatch(cls)!; 22 | final side = match.group(1); 23 | final radius = parseRounded(match.group(2)); 24 | 25 | switch (side) { 26 | case "-t": 27 | tl = radius; 28 | tr = radius; 29 | break; 30 | case "-l": 31 | tl = radius; 32 | bl = radius; 33 | break; 34 | case "-r": 35 | tr = radius; 36 | br = radius; 37 | break; 38 | case "-b": 39 | bl = radius; 40 | br = radius; 41 | break; 42 | case "-lt": 43 | case "-tl": 44 | tl = radius; 45 | break; 46 | case "-rt": 47 | case "-tr": 48 | tr = radius; 49 | break; 50 | case "-lb": 51 | case "-bl": 52 | bl = radius; 53 | break; 54 | case "-rb": 55 | case "-br": 56 | br = radius; 57 | break; 58 | default: 59 | tl = radius; 60 | tr = radius; 61 | bl = radius; 62 | br = radius; 63 | break; 64 | } 65 | } 66 | } 67 | 68 | @override 69 | String? generate() { 70 | final radius = CodeConstrutor("BorderRadius.only") 71 | ..add("topLeft", tl) 72 | ..add("topRight", tr) 73 | ..add("bottomLeft", bl) 74 | ..add("bottomRight", br); 75 | return radius.maybeGenerate(); 76 | } 77 | 78 | String? parseRounded(String? radius) { 79 | if (radius == null || radius == "none") return null; 80 | var v = "rounded('$radius')"; 81 | if (radius.startsWith("[") && radius.endsWith("]")) { 82 | v = radius.substring(1, radius.length - 1); 83 | } 84 | return "Radius.circular($v)"; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /easy_dsl_gen/lib/generator/iter/safearea.dart: -------------------------------------------------------------------------------- 1 | import "package:easy_dsl_gen/generator/code/code_gen.dart"; 2 | 3 | import "attr.dart"; 4 | 5 | final safePattern = RegExp(r"safe(?:-(inner|outer))?(-[tlrbxy])?"); 6 | 7 | class SafeAreaIter extends AttrIter { 8 | bool _it = false, _ib = false, _il = false, _ir = false; 9 | bool _ot = false, _ob = false, _ol = false, _or = false; 10 | 11 | bool get hasInner => _it || _ib || _il || _ir; 12 | bool get hasOuter => _ot || _ob || _ol || _or; 13 | 14 | @override 15 | void iter(String cls) { 16 | if (safePattern.hasMatch(cls)) { 17 | final match = safePattern.firstMatch(cls)!; 18 | final safe = match.group(1) ?? "outer"; 19 | final dir = match.group(2); 20 | 21 | if (safe == "inner") { 22 | switch (dir) { 23 | case "-t": 24 | _it = true; 25 | break; 26 | case "-b": 27 | _ib = true; 28 | break; 29 | case "-l": 30 | _il = true; 31 | break; 32 | case "-r": 33 | _ir = true; 34 | break; 35 | case "-x": 36 | _il = true; 37 | _ir = true; 38 | break; 39 | case "-y": 40 | _it = true; 41 | _ib = true; 42 | break; 43 | default: 44 | _it = true; 45 | _ib = true; 46 | _il = true; 47 | _ir = true; 48 | } 49 | return; 50 | } 51 | 52 | if (safe == "outer") { 53 | switch (dir) { 54 | case "-t": 55 | _ot = true; 56 | break; 57 | case "-b": 58 | _ob = true; 59 | break; 60 | case "-l": 61 | _ol = true; 62 | break; 63 | case "-r": 64 | _or = true; 65 | break; 66 | case "-x": 67 | _ol = true; 68 | _or = true; 69 | break; 70 | case "-y": 71 | _ot = true; 72 | _ob = true; 73 | break; 74 | default: 75 | _ot = true; 76 | _ob = true; 77 | _ol = true; 78 | _or = true; 79 | } 80 | return; 81 | } 82 | } 83 | } 84 | 85 | String? wrapperInner(String? child) { 86 | if (child == null) return null; 87 | if (!hasInner) return child; 88 | 89 | final code = CodeConstrutor("SafeArea") 90 | ..add("top", _it.toString()) 91 | ..add("bottom", _ib.toString()) 92 | ..add("left", _il.toString()) 93 | ..add("right", _ir.toString()) 94 | ..add("child", child); 95 | 96 | return code.generate(); 97 | } 98 | 99 | String? wrapperOuter(String? child) { 100 | if (child == null) return null; 101 | if (!hasOuter) return child; 102 | 103 | final code = CodeConstrutor("SafeArea") 104 | ..add("top", _ot.toString()) 105 | ..add("bottom", _ob.toString()) 106 | ..add("left", _ol.toString()) 107 | ..add("right", _or.toString()) 108 | ..add("child", child); 109 | 110 | return code.generate(); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /easy_dsl_gen/lib/generator/iter/size.dart: -------------------------------------------------------------------------------- 1 | import 'attr.dart'; 2 | import '../code/code_gen.dart'; 3 | 4 | final wPattern = RegExp(r'^w-(\[\d+(?:\.\d+)\]|.+)$'); 5 | final hPattern = RegExp(r'^h-(\[\d+(?:\.\d+)\]|.+)$'); 6 | final maxWidthPattern = RegExp(r'^max-w-(\[\d+(?:\.\d+)\]|.+)$'); 7 | final maxHeightPattern = RegExp(r'^max-h-(\[\d+(?:\.\d+)\]|.+)$'); 8 | final minWidthPattern = RegExp(r'^min-w-(\[\d+(?:\.\d+)\]|.+)$'); 9 | final minHeightPattern = RegExp(r'^min-h-(\[\d+(?:\.\d+)\]|.+)$'); 10 | 11 | class SizeIter extends AttrIter { 12 | String? width, height; 13 | String? maxWidth, maxHeight, minWidth, minHeight; 14 | 15 | @override 16 | void iter(String cls) { 17 | if (wPattern.hasMatch(cls)) { 18 | final value = wPattern.firstMatch(cls)?.group(1); 19 | if (value == null) return; 20 | width = parseValue(value); 21 | return; 22 | } 23 | 24 | if (hPattern.hasMatch(cls)) { 25 | final value = hPattern.firstMatch(cls)?.group(1); 26 | if (value == null) return; 27 | height = parseValue(value); 28 | return; 29 | } 30 | 31 | if (maxWidthPattern.hasMatch(cls)) { 32 | final value = maxWidthPattern.firstMatch(cls)?.group(1); 33 | if (value == null) return; 34 | maxWidth = parseValue(value); 35 | return; 36 | } 37 | 38 | if (maxHeightPattern.hasMatch(cls)) { 39 | final value = maxHeightPattern.firstMatch(cls)?.group(1); 40 | if (value == null) return; 41 | maxHeight = parseValue(value); 42 | return; 43 | } 44 | 45 | if (minWidthPattern.hasMatch(cls)) { 46 | final value = minWidthPattern.firstMatch(cls)?.group(1); 47 | if (value == null) return; 48 | minWidth = parseValue(value); 49 | return; 50 | } 51 | 52 | if (minHeightPattern.hasMatch(cls)) { 53 | final value = minHeightPattern.firstMatch(cls)?.group(1); 54 | if (value == null) return; 55 | minHeight = parseValue(value); 56 | return; 57 | } 58 | } 59 | 60 | String? parseValue(String value) { 61 | if (value == "auto") return null; 62 | if (value == "full") return "double.infinity"; 63 | if (value.startsWith("[") && value.endsWith("]")) { 64 | return value.substring(1, value.length - 1); 65 | } 66 | return "spacingOr('$value')"; 67 | } 68 | 69 | String? genConstraints() { 70 | return (CodeConstrutor("BoxConstraints") 71 | ..add("maxWidth", maxWidth) 72 | ..add("maxHeight", maxHeight) 73 | ..add("minWidth", minWidth) 74 | ..add("minHeight", minHeight)) 75 | .maybeGenerate(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /easy_dsl_gen/lib/generator/iter/utils.dart: -------------------------------------------------------------------------------- 1 | final colorPattern = 2 | RegExp(r'^\[(#[a-fA-F0-9]{6}|rgb\(\d+,\d+,\d+\)|hsl\(\d+,\d+%,\d+%\))\]$'); 3 | 4 | final rgbPattern = RegExp(r'^rgb\((\d+),(\d+),(\d+)\)$'); 5 | final hslPattern = RegExp(r'^hsl\((\d+),(\d+)%,(\d+)%\)$'); 6 | 7 | /// text: [#ffffff] [rgb(100,101,101)] [hsl(180,100%,50%)] currentColor 8 | /// alpha: 10 [0.1] [.1] 9 | String? parseColor(String? text, String? alpha) { 10 | if (text == null) return null; 11 | 12 | final a = parseAlpha(alpha); 13 | if (colorPattern.hasMatch(text)) { 14 | final color = colorPattern.firstMatch(text)?.group(1); 15 | if (color == null) return null; 16 | 17 | if (color.startsWith('#')) { 18 | final c = color.substring(1); 19 | final af = (a * 255).toInt().toRadixString(16).padLeft(2, '0'); 20 | return "Color(0x$af$c)"; 21 | } 22 | if (rgbPattern.hasMatch(color)) { 23 | final match = rgbPattern.firstMatch(color)!; 24 | final r = match.group(1); 25 | final g = match.group(2); 26 | final b = match.group(3); 27 | return "Color.fromRGBO($r,$g,$b,$a)"; 28 | } 29 | if (hslPattern.hasMatch(color)) { 30 | final match = hslPattern.firstMatch(color)!; 31 | final h = match.group(1); 32 | final s = double.parse(match.group(2) ?? "100"); 33 | final l = double.parse(match.group(3) ?? "100"); 34 | return "HSLColor.fromAHSL($a,$h,$s,$l).toColor()"; 35 | } 36 | } 37 | if (text.startsWith('[') && text.endsWith(']')) { 38 | return null; 39 | } 40 | 41 | if (a >= 1) return "color('$text')"; 42 | 43 | final ai = (a * 255).toInt(); 44 | return "color('$text').withAlpha($ai)"; 45 | } 46 | 47 | double parseAlpha(String? alpha) { 48 | if (alpha == null) return 1; 49 | 50 | double value = 1; 51 | if (alpha.startsWith('[') && alpha.endsWith(']')) { 52 | value = double.parse(alpha.substring(1, alpha.length - 1)); 53 | } else { 54 | value = double.parse(alpha) / 100; 55 | } 56 | 57 | if (value.isNaN) { 58 | value = 1; 59 | } else if (value < 0) { 60 | value = 0; 61 | } else if (value > 1) { 62 | value = 1; 63 | } 64 | 65 | return value; 66 | } 67 | -------------------------------------------------------------------------------- /easy_dsl_gen/lib/generator/widget_generator.dart: -------------------------------------------------------------------------------- 1 | import 'cls_item.dart'; 2 | import 'code/code_gen.dart'; 3 | import 'iter/iter.dart'; 4 | 5 | class WidgetGenerator { 6 | WidgetGenerator(this.clsItem, this.constructor); 7 | 8 | final ClsItem clsItem; 9 | final String constructor; 10 | 11 | String generate() { 12 | final boxIter = BoxIter(); 13 | final sizeIter = SizeIter(); 14 | final bgIter = BackgroundIter(); 15 | final borderIter = BorderIter(); 16 | final paddingIter = PaddingIter(); 17 | final marginIter = MarginIter(); 18 | final roundedIter = RoundedIter(); 19 | final aspectIter = AspectIter(); 20 | final opacityIter = OpacityIter(); 21 | final safeAreaIter = SafeAreaIter(); 22 | 23 | for (var cls in clsItem.clsSet) { 24 | boxIter.iter(cls); 25 | sizeIter.iter(cls); 26 | bgIter.iter(cls); 27 | borderIter.iter(cls); 28 | paddingIter.iter(cls); 29 | marginIter.iter(cls); 30 | roundedIter.iter(cls); 31 | aspectIter.iter(cls); 32 | opacityIter.iter(cls); 33 | safeAreaIter.iter(cls); 34 | } 35 | 36 | var current = boxIter.generate(); 37 | current = "(children?.length ?? 0) > 0 ? $current : const SizedBox()"; 38 | current = safeAreaIter.wrapperInner(current); 39 | 40 | final container = CodeConstrutor("Container") 41 | ..add("margin", marginIter.generate()) 42 | ..add("padding", paddingIter.generate()) 43 | ..add("width", sizeIter.width) 44 | ..add("height", sizeIter.height) 45 | ..add("constraints", sizeIter.genConstraints()) 46 | ..add( 47 | "decoration", 48 | (CodeConstrutor("BoxDecoration") 49 | ..add("color", bgIter.generate()) 50 | ..add("border", borderIter.generate()) 51 | ..add("borderRadius", roundedIter.generate())) 52 | .maybeGenerate()); 53 | 54 | if (container.hasFields()) { 55 | container.add("child", current); 56 | current = container.generate(); 57 | } 58 | 59 | current = aspectIter.wrapper(current); 60 | current = opacityIter.wrapper(current); 61 | current = safeAreaIter.wrapperOuter(current); 62 | 63 | return "class $constructor extends EasyDivImpl {\n" 64 | " const $constructor({\n" 65 | " super.key, required super.className, required super.children,\n" 66 | " required super.option,\n" 67 | " });\n" 68 | " @override\n" 69 | " Widget build(BuildContext context) {\n" 70 | " return $current;\n" 71 | " }\n" 72 | "}"; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /easy_dsl_gen/lib/visitor/classname_visitor.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:analyzer/dart/ast/visitor.dart'; 3 | import 'package:analyzer/dart/element/element.dart'; 4 | 5 | class ClassNameVisitor extends GeneralizingAstVisitor { 6 | ClassNameVisitor({this.nodeName = "Div"}); 7 | 8 | final String nodeName; 9 | final foundClassNames = []; 10 | 11 | @override 12 | void visitNode(AstNode node) { 13 | if (node is InstanceCreationExpression) { 14 | final curNodeName = node.constructorName.type.toSource(); 15 | _findDivAndClassName(curNodeName, node.argumentList.arguments); 16 | } else if (node is MethodInvocation) { 17 | final curNodeName = node.methodName.name; 18 | _findDivAndClassName(curNodeName, node.argumentList.arguments); 19 | } 20 | 21 | super.visitNode(node); 22 | } 23 | 24 | void _findDivAndClassName(String curNode, NodeList arguments) { 25 | if (curNode != nodeName) { 26 | return; 27 | } 28 | 29 | for (var arg in arguments) { 30 | if (arg is NamedExpression && arg.name.label.name == 'className') { 31 | var value = arg.expression; 32 | 33 | if (value is StringLiteral) { 34 | foundClassNames.add(value.stringValue!); 35 | } else if (value is SimpleIdentifier && value.inConstantContext) { 36 | if (value.staticElement is VariableElement) { 37 | var staticElement = value.staticElement as VariableElement; 38 | var constantValue = staticElement.computeConstantValue(); 39 | var runtimeString = constantValue?.toStringValue(); 40 | 41 | if (runtimeString != null) { 42 | foundClassNames.add(runtimeString); 43 | } 44 | } 45 | } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /easy_dsl_gen/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: easy_dsl_gen 2 | version: 0.0.4 3 | description: Streamlining Flutter UI Development with DSL-Based Code Generation 4 | repository: https://github.com/zzzgydi/easy_dsl 5 | homepage: https://github.com/zzzgydi/easy_dsl 6 | topics: 7 | - dsl 8 | - tailwind 9 | - build-runner 10 | - codegen 11 | 12 | environment: 13 | sdk: ^3.2.3 14 | 15 | dependencies: 16 | analyzer: ">=5.2.0 <7.0.0" 17 | build: ^2.0.0 18 | source_gen: ^1.5.0 19 | glob: ^2.1.2 20 | path: ^1.8.0 21 | code_builder: ^4.9.0 22 | dart_style: ^2.3.4 23 | easy_dsl: ^0.0.4 24 | 25 | dev_dependencies: 26 | lints: ^2.1.0 27 | test: ^1.24.0 28 | build_runner: ^2.4.0 29 | -------------------------------------------------------------------------------- /easy_dsl_gen/test/test1.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/analysis/features.dart'; 2 | import 'package:analyzer/dart/analysis/utilities.dart'; 3 | import 'package:analyzer/dart/ast/ast.dart'; 4 | import 'package:analyzer/dart/ast/visitor.dart'; 5 | 6 | void main() { 7 | // 替换为你的 Dart 文件路径 8 | var code = '''class HomePage extends StatelessWidget { 9 | const HomePage({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | var div = Div(className: "flex items-center hhh", children: [ 14 | Text('He'), 15 | ]); 16 | 17 | return Container( 18 | child: Column( 19 | children: [ 20 | Text('Hello'), 21 | Div( 22 | className: "block items-center", children: [ 23 | const Div( 24 | className: "flex inline items-center", 25 | children: [Text("test")], 26 | ), 27 | ]) 28 | ], 29 | ), 30 | ); 31 | } 32 | } 33 | 34 | class HomePage2 extends StatelessWidget { 35 | const HomePage2({super.key}); 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | return const Column( 40 | children: [ 41 | // Text('Hello'), 42 | 43 | Div(className: "flex items-center222", children: [ 44 | Text('He'), 45 | ]) 46 | ], 47 | ); 48 | } 49 | } 50 | 51 | class TestDemo extends StatelessWidget { 52 | const TestDemo({super.key}); 53 | 54 | @override 55 | Widget build(BuildContext context) { 56 | return const Div( 57 | className: "flex items-center", 58 | children: [Text("test")], 59 | ); 60 | } 61 | }'''; 62 | 63 | // 解析 Dart 代码 64 | var result = parseString( 65 | content: code, featureSet: FeatureSet.latestLanguageVersion()); 66 | 67 | // print(result.unit); 68 | // 访问 AST 69 | var visitor = CustomVisitor(); 70 | result.unit.visitChildren(visitor); 71 | 72 | // 输出所有找到的 className 值 73 | for (var className in visitor.foundClassNames) { 74 | print("result: $className"); 75 | } 76 | } 77 | 78 | class CustomVisitor extends GeneralizingAstVisitor { 79 | final foundClassNames = []; 80 | 81 | @override 82 | void visitNode(AstNode node) { 83 | print("visitNode: ${node.runtimeType.toString()} ${node.toSource()}"); 84 | 85 | if (node is InstanceCreationExpression) { 86 | var arguments = node.argumentList.arguments; 87 | for (var arg in arguments) { 88 | if (arg is NamedExpression && arg.name.label.name == 'className') { 89 | var value = arg.expression; 90 | if (value is StringLiteral) { 91 | foundClassNames.add(value.stringValue!); 92 | } 93 | } 94 | } 95 | } 96 | 97 | if (node is MethodInvocation) { 98 | var arguments = node.argumentList.arguments; 99 | for (var arg in arguments) { 100 | if (arg is NamedExpression && arg.name.label.name == 'className') { 101 | var value = arg.expression; 102 | if (value is StringLiteral) { 103 | foundClassNames.add(value.stringValue!); 104 | } 105 | } 106 | } 107 | } 108 | 109 | // 继续遍历 AST 110 | super.visitNode(node); 111 | } 112 | } 113 | 114 | class DivVisitor extends RecursiveAstVisitor { 115 | final foundClassNames = []; 116 | 117 | @override 118 | void visitInstanceCreationExpression(InstanceCreationExpression node) { 119 | print(node.constructorName.type.toSource()); 120 | 121 | if (node.constructorName.type.toSource() == 'Div') { 122 | var arguments = node.argumentList.arguments; 123 | for (var arg in arguments) { 124 | if (arg is NamedExpression && arg.name.label.name == 'className') { 125 | var value = arg.expression; 126 | if (value is StringLiteral) { 127 | foundClassNames.add(value.stringValue!); 128 | } 129 | } 130 | } 131 | } 132 | } 133 | 134 | @override 135 | void visitMethodInvocation(MethodInvocation node) { 136 | print("Invocation: ${node.runtimeType.toString()} ${node.methodName}"); 137 | 138 | if (node.methodName.toString() == 'Div') { 139 | var arguments = node.argumentList.arguments; 140 | for (var arg in arguments) { 141 | if (arg is NamedExpression && arg.name.label.name == 'className') { 142 | var value = arg.expression; 143 | if (value is StringLiteral) { 144 | foundClassNames.add(value.stringValue!); 145 | } 146 | } 147 | } 148 | } 149 | super.visitMethodInvocation(node); 150 | } 151 | 152 | @override 153 | void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { 154 | print( 155 | "FunctionExpressionInvocation: ${node.runtimeType.toString()} ${node.function}"); 156 | super.visitFunctionExpressionInvocation(node); 157 | } 158 | 159 | @override 160 | void visitFunctionDeclaration(FunctionDeclaration node) { 161 | print("FunctionDeclaration: ${node.runtimeType.toString()} ${node.name}"); 162 | super.visitFunctionDeclaration(node); 163 | } 164 | 165 | @override 166 | void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { 167 | print( 168 | "TopLevelVariableDeclaration: ${node.runtimeType.toString()} ${node.variables.variables}"); 169 | super.visitTopLevelVariableDeclaration(node); 170 | } 171 | 172 | @override 173 | void visitCompilationUnit(CompilationUnit node) { 174 | print( 175 | "CompilationUnit: ${node.runtimeType.toString()} ${node.declarations}"); 176 | super.visitCompilationUnit(node); 177 | } 178 | 179 | @override 180 | void visitClassDeclaration(ClassDeclaration node) { 181 | print("ClassDeclaration: ${node.runtimeType.toString()} ${node.name}"); 182 | super.visitClassDeclaration(node); 183 | } 184 | 185 | @override 186 | void visitFieldDeclaration(FieldDeclaration node) { 187 | print("FieldDeclaration: ${node.runtimeType.toString()} ${node.fields}"); 188 | super.visitFieldDeclaration(node); 189 | } 190 | 191 | // @override 192 | // void visitConstantConstructorInvocation() { 193 | // print("ConstantConstructorInvocation"); 194 | // super.visitConstantConstructorInvocation(); 195 | // } 196 | 197 | // @override 198 | // void visitNamedExpression(NamedExpression node) { 199 | // print("NamedExpression: ${node.runtimeType.toString()} ${node.name}"); 200 | // super.visitNamedExpression(node); 201 | // } 202 | 203 | // @override 204 | // void visitVariableDeclaration(VariableDeclaration node) { 205 | // print("VariableDeclaration: ${node.runtimeType.toString()} ${node.name}"); 206 | // super.visitVariableDeclaration(node); 207 | // } 208 | } 209 | -------------------------------------------------------------------------------- /easy_dsl_gen/test/test2.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_dsl_gen/generator/iter/utils.dart'; 2 | 3 | void main() { 4 | final className = 5 | "border-t-1 border-[#909090]/[.02] border-[30.2] border-hello/80 " 6 | "border-[rgb(100,100,200)]"; 7 | 8 | final widthPattern = 9 | RegExp(r'^border(-[tlrbxy])?(?:-(\d+|\[\d+(?:\.\d+)?\]))?$'); 10 | final colorPattern = 11 | RegExp(r'^border(-[tlrbxy])?-(\[.+?\]|.+?)(?:/(\[\.\d+\]|\d+))?$'); 12 | 13 | final clsList = className.split(" ").map((e) => e.trim()).toList(); 14 | for (var cls in clsList) { 15 | // width 16 | if (widthPattern.hasMatch(cls)) { 17 | final match = widthPattern.firstMatch(cls)!; 18 | final side = match.group(1); 19 | final width = (match.group(2) ?? "1"); 20 | var w = width; 21 | if (width.startsWith('[') && width.endsWith(']')) { 22 | w = width.substring(1, width.length - 1); 23 | } 24 | print("src: $cls \n== mode: width \n== side: $side \n== width: $w"); 25 | } 26 | // color 27 | else if (colorPattern.hasMatch(cls)) { 28 | final match = colorPattern.firstMatch(cls)!; 29 | final side = match.group(1); 30 | final color = match.group(2); 31 | final alpha = match.group(3); 32 | 33 | String? value; 34 | if (color != null) { 35 | value = parseColor(color, alpha); 36 | } 37 | 38 | print("src: $cls \n== mode: color \n== side: $side \n== color: $value"); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /easy_dsl_gen/test/test3.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | int easyEditDistance(List str1, List str2) { 4 | int len1 = str1.length; 5 | int len2 = str2.length; 6 | 7 | List> dp = List.generate( 8 | len1 + 1, (i) => List.generate(len2 + 1, (j) => 0, growable: false), 9 | growable: false); 10 | 11 | for (int i = 0; i <= len1; i++) { 12 | for (int j = 0; j <= len2; j++) { 13 | if (i == 0) { 14 | dp[i][j] = j; 15 | } else if (j == 0) { 16 | dp[i][j] = i; 17 | } else if (str1[i - 1] == str2[j - 1]) { 18 | dp[i][j] = dp[i - 1][j - 1]; 19 | } else { 20 | dp[i][j] = 1 + min(dp[i - 1][j], min(dp[i][j - 1], dp[i - 1][j - 1])); 21 | } 22 | } 23 | } 24 | 25 | return dp[len1][len2]; 26 | } 27 | 28 | void main() { 29 | var str1 = "p-[10] p-10 m-10 bg-red safe-inner-x".split(" "); 30 | var str2 = "p-10 m-10 bg-black".split(" "); 31 | print( 32 | "Edit Distance between \$str1 and \$str2 is: ${easyEditDistance(str1, str2)}"); 33 | } 34 | -------------------------------------------------------------------------------- /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 | 45 | lib/**/*.g.dart 46 | test/**/*.g.dart 47 | -------------------------------------------------------------------------------- /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: "78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 17 | base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 18 | - platform: android 19 | create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 20 | base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 21 | - platform: ios 22 | create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 23 | base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 24 | - platform: linux 25 | create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 26 | base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 27 | - platform: macos 28 | create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 29 | base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 30 | - platform: web 31 | create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 32 | base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 33 | - platform: windows 34 | create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 35 | base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 36 | 37 | # User provided section 38 | 39 | # List of Local paths (relative to this file) that should be 40 | # ignored by the migrate tool. 41 | # 42 | # Files that are not part of the templates will be ignored by default. 43 | unmanaged_files: 44 | - 'lib/main.dart' 45 | - 'ios/Runner.xcodeproj/project.pbxproj' 46 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # example 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) 13 | 14 | For help getting started with Flutter development, view the 15 | [online documentation](https://docs.flutter.dev/), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /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/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | id "dev.flutter.flutter-gradle-plugin" 5 | } 6 | 7 | def localProperties = new Properties() 8 | def localPropertiesFile = rootProject.file('local.properties') 9 | if (localPropertiesFile.exists()) { 10 | localPropertiesFile.withReader('UTF-8') { reader -> 11 | localProperties.load(reader) 12 | } 13 | } 14 | 15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 16 | if (flutterVersionCode == null) { 17 | flutterVersionCode = '1' 18 | } 19 | 20 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 21 | if (flutterVersionName == null) { 22 | flutterVersionName = '1.0' 23 | } 24 | 25 | android { 26 | namespace "com.example.example" 27 | compileSdkVersion flutter.compileSdkVersion 28 | ndkVersion flutter.ndkVersion 29 | 30 | compileOptions { 31 | sourceCompatibility JavaVersion.VERSION_1_8 32 | targetCompatibility JavaVersion.VERSION_1_8 33 | } 34 | 35 | kotlinOptions { 36 | jvmTarget = '1.8' 37 | } 38 | 39 | sourceSets { 40 | main.java.srcDirs += 'src/main/kotlin' 41 | } 42 | 43 | defaultConfig { 44 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 45 | applicationId "com.example.example" 46 | // You can update the following values to match your application needs. 47 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. 48 | minSdkVersion flutter.minSdkVersion 49 | targetSdkVersion flutter.targetSdkVersion 50 | versionCode flutterVersionCode.toInteger() 51 | versionName flutterVersionName 52 | } 53 | 54 | buildTypes { 55 | release { 56 | // TODO: Add your own signing config for the release build. 57 | // Signing with the debug keys for now, so `flutter run --release` works. 58 | signingConfig signingConfigs.debug 59 | } 60 | } 61 | } 62 | 63 | flutter { 64 | source '../..' 65 | } 66 | 67 | dependencies {} 68 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 14 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/example/example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.7.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 10 | } 11 | } 12 | 13 | allprojects { 14 | repositories { 15 | google() 16 | mavenCentral() 17 | } 18 | } 19 | 20 | rootProject.buildDir = '../build' 21 | subprojects { 22 | project.buildDir = "${rootProject.buildDir}/${project.name}" 23 | } 24 | subprojects { 25 | project.evaluationDependsOn(':app') 26 | } 27 | 28 | tasks.register("clean", Delete) { 29 | delete rootProject.buildDir 30 | } 31 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip 6 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | } 9 | settings.ext.flutterSdkPath = flutterSdkPath() 10 | 11 | includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") 12 | 13 | repositories { 14 | google() 15 | mavenCentral() 16 | gradlePluginPortal() 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false 21 | } 22 | } 23 | 24 | plugins { 25 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 26 | id "com.android.application" version "7.3.0" apply false 27 | } 28 | 29 | include ":app" 30 | -------------------------------------------------------------------------------- /example/build.yaml: -------------------------------------------------------------------------------- 1 | targets: 2 | $default: 3 | builders: 4 | easy_dsl_gen|easy_gen: 5 | generate_for: 6 | - lib/**.dart 7 | -------------------------------------------------------------------------------- /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 | 11.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/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/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/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/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/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/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/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/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/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/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/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/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/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/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/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/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/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/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/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/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/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/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/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/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/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/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/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/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/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/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/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/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/gen/demo1.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class GenDemo1 extends StatelessWidget { 4 | const GenDemo1({super.key}); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return Container( 9 | margin: EdgeInsets.all(10), 10 | padding: EdgeInsets.all(10), 11 | width: 100, 12 | height: 100, 13 | decoration: BoxDecoration( 14 | color: Colors.white, 15 | // border: Border.all(color: Colors.black, width: 1), 16 | border: const Border( 17 | top: BorderSide(color: Colors.black, width: 1), 18 | // left: BorderSide(color: Colors.black, width: 1), 19 | // right: BorderSide(color: Colors.black, width: 1), 20 | // bottom: BorderSide(color: Colors.black, width: 1), 21 | ), 22 | borderRadius: BorderRadius.circular(10), 23 | ), 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'pages/demo1.dart'; 4 | 5 | void main() { 6 | runApp(const MyApp()); 7 | } 8 | 9 | class MyApp extends StatelessWidget { 10 | const MyApp({super.key}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return MaterialApp( 15 | title: 'Flutter Demo', 16 | theme: ThemeData( 17 | colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), 18 | useMaterial3: true, 19 | ), 20 | home: const MyHomePage(title: 'Flutter Demo Home Page'), 21 | ); 22 | } 23 | } 24 | 25 | class MyHomePage extends StatelessWidget { 26 | const MyHomePage({super.key, required this.title}); 27 | 28 | final String title; 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | return Scaffold( 33 | appBar: AppBar( 34 | backgroundColor: Theme.of(context).colorScheme.inversePrimary, 35 | title: Text(title), 36 | ), 37 | body: const Demo1Page(), 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /example/lib/pages/demo1.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'div.dart'; 4 | 5 | class Demo1Page extends StatelessWidget { 6 | const Demo1Page({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | const className = "items-center bg-black/50 w-full justify-center"; 11 | 12 | return const Div( 13 | className: className, 14 | children: [ 15 | Div( 16 | className: "bg-red-500 px-[100] py-[50] gap-4 rounded-2xl", 17 | children: [ 18 | Div( 19 | className: 20 | "bg-gray-400 w-[200] h-20 flex items-center justify-center gap-[4] rounded-tl-[20] rounded-br-[20]", 21 | children: [Text('This is'), Text('EasyDSL')], 22 | ), 23 | Div( 24 | className: "max-w-[200] flex items-center p-[10] bg-gray-500/50", 25 | children: [ 26 | Text('Hello'), 27 | SizedBox(width: 10), 28 | Div( 29 | className: 30 | "bg-orange-100/50 p-2 border-x-[10] border-red-500 rounded-[10]", 31 | children: [Text('World')], 32 | ) 33 | ], 34 | ), 35 | ], 36 | ), 37 | ], 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /example/lib/pages/demo2.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'div.dart'; 4 | 5 | class Demo2 extends StatelessWidget { 6 | const Demo2({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | const div = Div(className: "flex items-center demo2", children: [ 11 | Text('He'), 12 | Div( 13 | className: "flex inline items-center demo2", 14 | children: [Text("test")], 15 | ), 16 | ]); 17 | 18 | final data = ""; 19 | 20 | return Column( 21 | mainAxisAlignment: MainAxisAlignment.center, 22 | children: [ 23 | div, 24 | Text(data), 25 | const Div( 26 | className: "demo2 test", 27 | children: [], 28 | ) 29 | ], 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /example/lib/pages/div.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:easy_dsl/easy_dsl.dart'; 4 | 5 | part 'div.easy.g.dart'; 6 | 7 | @EasyDSL() 8 | class Div extends $Div { 9 | const Div({super.key, required super.className, super.children}) 10 | : super(option: const EasyOption.empty()); 11 | } 12 | -------------------------------------------------------------------------------- /example/lib/pages/home.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/pages/div.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | const a = 1; 5 | const b = 2; 6 | 7 | class HomePage extends StatelessWidget { 8 | const HomePage({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return const Column( 13 | children: [ 14 | Text('Hello'), 15 | Div(className: "home flex items-center 1", children: [ 16 | Text('He'), 17 | ]) 18 | ], 19 | ); 20 | } 21 | } 22 | 23 | class TestDemo extends StatelessWidget { 24 | const TestDemo({super.key}); 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return const Div( 29 | className: "home flex items-center 2", 30 | children: [Text("test")], 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /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/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/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/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/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/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/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/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/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/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/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/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/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/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/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 © 2024 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.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: A example and test demo for easy_dsl 3 | publish_to: "none" # Remove this line if you wish to publish to pub.dev 4 | 5 | version: 1.0.0+1 6 | 7 | environment: 8 | sdk: ">=3.2.3 <4.0.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | cupertino_icons: ^1.0.2 14 | easy_dsl: any 15 | 16 | dev_dependencies: 17 | flutter_test: 18 | sdk: flutter 19 | flutter_lints: ^2.0.0 20 | build_runner: ^2.4.0 21 | easy_dsl_gen: any 22 | 23 | dependency_overrides: 24 | easy_dsl: 25 | path: ../easy_dsl/ 26 | easy_dsl_gen: 27 | path: ../easy_dsl_gen/ 28 | 29 | # For information on the generic Dart part of this file, see the 30 | # following page: https://dart.dev/tools/pub/pubspec 31 | 32 | # The following section is specific to Flutter packages. 33 | flutter: 34 | # The following line ensures that the Material Icons font is 35 | # included with your application, so that you can use the icons in 36 | # the material Icons class. 37 | uses-material-design: true 38 | 39 | # To add assets to your application, add an assets section, like this: 40 | # assets: 41 | # - images/a_dot_burr.jpeg 42 | # - images/a_dot_ham.jpeg 43 | 44 | # An image asset can refer to one or more resolution-specific "variants", see 45 | # https://flutter.dev/assets-and-images/#resolution-aware 46 | 47 | # For details regarding adding assets from package dependencies, see 48 | # https://flutter.dev/assets-and-images/#from-packages 49 | 50 | # To add custom fonts to your application, add a fonts section here, 51 | # in this "flutter" section. Each entry in this list should have a 52 | # "family" key with the font family name, and a "fonts" key with a 53 | # list giving the asset and other descriptors for the font. For 54 | # example: 55 | # fonts: 56 | # - family: Schyler 57 | # fonts: 58 | # - asset: fonts/Schyler-Regular.ttf 59 | # - asset: fonts/Schyler-Italic.ttf 60 | # style: italic 61 | # - family: Trajan Pro 62 | # fonts: 63 | # - asset: fonts/TrajanPro.ttf 64 | # - asset: fonts/TrajanPro_Bold.ttf 65 | # weight: 700 66 | # 67 | # For details regarding fonts from package dependencies, 68 | # see https://flutter.dev/custom-fonts/#from-packages 69 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility in the flutter_test package. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:example/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(const MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /example/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/example/web/favicon.png -------------------------------------------------------------------------------- /example/web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/example/web/icons/Icon-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/example/web/icons/Icon-512.png -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/example/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/example/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /example/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | example 33 | 34 | 35 | 39 | 40 | 41 | 42 | 43 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /example/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "short_name": "example", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /example/windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /example/windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Project-level configuration. 2 | cmake_minimum_required(VERSION 3.14) 3 | project(example LANGUAGES CXX) 4 | 5 | # The name of the executable created for the application. Change this to change 6 | # the on-disk name of your application. 7 | set(BINARY_NAME "example") 8 | 9 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent 10 | # versions of CMake. 11 | cmake_policy(VERSION 3.14...3.25) 12 | 13 | # Define build configuration option. 14 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 15 | if(IS_MULTICONFIG) 16 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" 17 | CACHE STRING "" FORCE) 18 | else() 19 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 20 | set(CMAKE_BUILD_TYPE "Debug" CACHE 21 | STRING "Flutter build mode" FORCE) 22 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 23 | "Debug" "Profile" "Release") 24 | endif() 25 | endif() 26 | # Define settings for the Profile build mode. 27 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") 28 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") 29 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") 30 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") 31 | 32 | # Use Unicode for all projects. 33 | add_definitions(-DUNICODE -D_UNICODE) 34 | 35 | # Compilation settings that should be applied to most targets. 36 | # 37 | # Be cautious about adding new options here, as plugins use this function by 38 | # default. In most cases, you should add new options to specific targets instead 39 | # of modifying this function. 40 | function(APPLY_STANDARD_SETTINGS TARGET) 41 | target_compile_features(${TARGET} PUBLIC cxx_std_17) 42 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") 43 | target_compile_options(${TARGET} PRIVATE /EHsc) 44 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") 45 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") 46 | endfunction() 47 | 48 | # Flutter library and tool build rules. 49 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 50 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 51 | 52 | # Application build; see runner/CMakeLists.txt. 53 | add_subdirectory("runner") 54 | 55 | 56 | # Generated plugin build rules, which manage building the plugins and adding 57 | # them to the application. 58 | include(flutter/generated_plugins.cmake) 59 | 60 | 61 | # === Installation === 62 | # Support files are copied into place next to the executable, so that it can 63 | # run in place. This is done instead of making a separate bundle (as on Linux) 64 | # so that building and running from within Visual Studio will work. 65 | set(BUILD_BUNDLE_DIR "$") 66 | # Make the "install" step default, as it's required to run. 67 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 68 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 69 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 70 | endif() 71 | 72 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 73 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") 74 | 75 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 76 | COMPONENT Runtime) 77 | 78 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 79 | COMPONENT Runtime) 80 | 81 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 82 | COMPONENT Runtime) 83 | 84 | if(PLUGIN_BUNDLED_LIBRARIES) 85 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 86 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 87 | COMPONENT Runtime) 88 | endif() 89 | 90 | # Copy the native assets provided by the build.dart from all packages. 91 | set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") 92 | install(DIRECTORY "${NATIVE_ASSETS_DIR}" 93 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 94 | COMPONENT Runtime) 95 | 96 | # Fully re-copy the assets directory on each build to avoid having stale files 97 | # from a previous install. 98 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 99 | install(CODE " 100 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 101 | " COMPONENT Runtime) 102 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 103 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 104 | 105 | # Install the AOT library on non-Debug builds only. 106 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 107 | CONFIGURATIONS Profile;Release 108 | COMPONENT Runtime) 109 | -------------------------------------------------------------------------------- /example/windows/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.14) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") 12 | 13 | # Set fallback configurations for older versions of the flutter tool. 14 | if (NOT DEFINED FLUTTER_TARGET_PLATFORM) 15 | set(FLUTTER_TARGET_PLATFORM "windows-x64") 16 | endif() 17 | 18 | # === Flutter Library === 19 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") 20 | 21 | # Published to parent scope for install step. 22 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 23 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 24 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 25 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) 26 | 27 | list(APPEND FLUTTER_LIBRARY_HEADERS 28 | "flutter_export.h" 29 | "flutter_windows.h" 30 | "flutter_messenger.h" 31 | "flutter_plugin_registrar.h" 32 | "flutter_texture_registrar.h" 33 | ) 34 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") 35 | add_library(flutter INTERFACE) 36 | target_include_directories(flutter INTERFACE 37 | "${EPHEMERAL_DIR}" 38 | ) 39 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") 40 | add_dependencies(flutter flutter_assemble) 41 | 42 | # === Wrapper === 43 | list(APPEND CPP_WRAPPER_SOURCES_CORE 44 | "core_implementations.cc" 45 | "standard_codec.cc" 46 | ) 47 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") 48 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN 49 | "plugin_registrar.cc" 50 | ) 51 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") 52 | list(APPEND CPP_WRAPPER_SOURCES_APP 53 | "flutter_engine.cc" 54 | "flutter_view_controller.cc" 55 | ) 56 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") 57 | 58 | # Wrapper sources needed for a plugin. 59 | add_library(flutter_wrapper_plugin STATIC 60 | ${CPP_WRAPPER_SOURCES_CORE} 61 | ${CPP_WRAPPER_SOURCES_PLUGIN} 62 | ) 63 | apply_standard_settings(flutter_wrapper_plugin) 64 | set_target_properties(flutter_wrapper_plugin PROPERTIES 65 | POSITION_INDEPENDENT_CODE ON) 66 | set_target_properties(flutter_wrapper_plugin PROPERTIES 67 | CXX_VISIBILITY_PRESET hidden) 68 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) 69 | target_include_directories(flutter_wrapper_plugin PUBLIC 70 | "${WRAPPER_ROOT}/include" 71 | ) 72 | add_dependencies(flutter_wrapper_plugin flutter_assemble) 73 | 74 | # Wrapper sources needed for the runner. 75 | add_library(flutter_wrapper_app STATIC 76 | ${CPP_WRAPPER_SOURCES_CORE} 77 | ${CPP_WRAPPER_SOURCES_APP} 78 | ) 79 | apply_standard_settings(flutter_wrapper_app) 80 | target_link_libraries(flutter_wrapper_app PUBLIC flutter) 81 | target_include_directories(flutter_wrapper_app PUBLIC 82 | "${WRAPPER_ROOT}/include" 83 | ) 84 | add_dependencies(flutter_wrapper_app flutter_assemble) 85 | 86 | # === Flutter tool backend === 87 | # _phony_ is a non-existent file to force this command to run every time, 88 | # since currently there's no way to get a full input/output list from the 89 | # flutter tool. 90 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") 91 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) 92 | add_custom_command( 93 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 94 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} 95 | ${CPP_WRAPPER_SOURCES_APP} 96 | ${PHONY_OUTPUT} 97 | COMMAND ${CMAKE_COMMAND} -E env 98 | ${FLUTTER_TOOL_ENVIRONMENT} 99 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" 100 | ${FLUTTER_TARGET_PLATFORM} $ 101 | VERBATIM 102 | ) 103 | add_custom_target(flutter_assemble DEPENDS 104 | "${FLUTTER_LIBRARY}" 105 | ${FLUTTER_LIBRARY_HEADERS} 106 | ${CPP_WRAPPER_SOURCES_CORE} 107 | ${CPP_WRAPPER_SOURCES_PLUGIN} 108 | ${CPP_WRAPPER_SOURCES_APP} 109 | ) 110 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | 10 | void RegisterPlugins(flutter::PluginRegistry* registry) { 11 | } 12 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | ) 7 | 8 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 9 | ) 10 | 11 | set(PLUGIN_BUNDLED_LIBRARIES) 12 | 13 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 14 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 15 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 16 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 18 | endforeach(plugin) 19 | 20 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 21 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 22 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 23 | endforeach(ffi_plugin) 24 | -------------------------------------------------------------------------------- /example/windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} WIN32 10 | "flutter_window.cpp" 11 | "main.cpp" 12 | "utils.cpp" 13 | "win32_window.cpp" 14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 15 | "Runner.rc" 16 | "runner.exe.manifest" 17 | ) 18 | 19 | # Apply the standard set of build settings. This can be removed for applications 20 | # that need different build settings. 21 | apply_standard_settings(${BINARY_NAME}) 22 | 23 | # Add preprocessor definitions for the build version. 24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") 25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") 26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") 27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") 28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") 29 | 30 | # Disable Windows macros that collide with C++ standard library functions. 31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 32 | 33 | # Add dependency libraries and include directories. Add any application-specific 34 | # dependencies here. 35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 36 | target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") 37 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 38 | 39 | # Run the Flutter tool portions of the build. This must not be removed. 40 | add_dependencies(${BINARY_NAME} flutter_assemble) 41 | -------------------------------------------------------------------------------- /example/windows/runner/Runner.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | #include "resource.h" 5 | 6 | #define APSTUDIO_READONLY_SYMBOLS 7 | ///////////////////////////////////////////////////////////////////////////// 8 | // 9 | // Generated from the TEXTINCLUDE 2 resource. 10 | // 11 | #include "winres.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_APP_ICON ICON "resources\\app_icon.ico" 56 | 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | #if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) 64 | #define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD 65 | #else 66 | #define VERSION_AS_NUMBER 1,0,0,0 67 | #endif 68 | 69 | #if defined(FLUTTER_VERSION) 70 | #define VERSION_AS_STRING FLUTTER_VERSION 71 | #else 72 | #define VERSION_AS_STRING "1.0.0" 73 | #endif 74 | 75 | VS_VERSION_INFO VERSIONINFO 76 | FILEVERSION VERSION_AS_NUMBER 77 | PRODUCTVERSION VERSION_AS_NUMBER 78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 79 | #ifdef _DEBUG 80 | FILEFLAGS VS_FF_DEBUG 81 | #else 82 | FILEFLAGS 0x0L 83 | #endif 84 | FILEOS VOS__WINDOWS32 85 | FILETYPE VFT_APP 86 | FILESUBTYPE 0x0L 87 | BEGIN 88 | BLOCK "StringFileInfo" 89 | BEGIN 90 | BLOCK "040904e4" 91 | BEGIN 92 | VALUE "CompanyName", "com.example" "\0" 93 | VALUE "FileDescription", "example" "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "example" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2024 com.example. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "example.exe" "\0" 98 | VALUE "ProductName", "example" "\0" 99 | VALUE "ProductVersion", VERSION_AS_STRING "\0" 100 | END 101 | END 102 | BLOCK "VarFileInfo" 103 | BEGIN 104 | VALUE "Translation", 0x409, 1252 105 | END 106 | END 107 | 108 | #endif // English (United States) resources 109 | ///////////////////////////////////////////////////////////////////////////// 110 | 111 | 112 | 113 | #ifndef APSTUDIO_INVOKED 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // Generated from the TEXTINCLUDE 3 resource. 117 | // 118 | 119 | 120 | ///////////////////////////////////////////////////////////////////////////// 121 | #endif // not APSTUDIO_INVOKED 122 | -------------------------------------------------------------------------------- /example/windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | 30 | flutter_controller_->engine()->SetNextFrameCallback([&]() { 31 | this->Show(); 32 | }); 33 | 34 | // Flutter can complete the first frame before the "show window" callback is 35 | // registered. The following call ensures a frame is pending to ensure the 36 | // window is shown. It is a no-op if the first frame hasn't completed yet. 37 | flutter_controller_->ForceRedraw(); 38 | 39 | return true; 40 | } 41 | 42 | void FlutterWindow::OnDestroy() { 43 | if (flutter_controller_) { 44 | flutter_controller_ = nullptr; 45 | } 46 | 47 | Win32Window::OnDestroy(); 48 | } 49 | 50 | LRESULT 51 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 52 | WPARAM const wparam, 53 | LPARAM const lparam) noexcept { 54 | // Give Flutter, including plugins, an opportunity to handle window messages. 55 | if (flutter_controller_) { 56 | std::optional result = 57 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 58 | lparam); 59 | if (result) { 60 | return *result; 61 | } 62 | } 63 | 64 | switch (message) { 65 | case WM_FONTCHANGE: 66 | flutter_controller_->engine()->ReloadSystemFonts(); 67 | break; 68 | } 69 | 70 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 71 | } 72 | -------------------------------------------------------------------------------- /example/windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /example/windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.Create(L"example", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /example/windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /example/windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzzgydi/easy_dsl/3ad8e25b9252ab5c1c52201e279b552651c45cc6/example/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /example/windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /example/windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr) 51 | -1; // remove the trailing null character 52 | int input_length = (int)wcslen(utf16_string); 53 | std::string utf8_string; 54 | if (target_length <= 0 || target_length > utf8_string.max_size()) { 55 | return utf8_string; 56 | } 57 | utf8_string.resize(target_length); 58 | int converted_length = ::WideCharToMultiByte( 59 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 60 | input_length, utf8_string.data(), target_length, nullptr, nullptr); 61 | if (converted_length == 0) { 62 | return std::string(); 63 | } 64 | return utf8_string; 65 | } 66 | -------------------------------------------------------------------------------- /example/windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /example/windows/runner/win32_window.cpp: -------------------------------------------------------------------------------- 1 | #include "win32_window.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "resource.h" 7 | 8 | namespace { 9 | 10 | /// Window attribute that enables dark mode window decorations. 11 | /// 12 | /// Redefined in case the developer's machine has a Windows SDK older than 13 | /// version 10.0.22000.0. 14 | /// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute 15 | #ifndef DWMWA_USE_IMMERSIVE_DARK_MODE 16 | #define DWMWA_USE_IMMERSIVE_DARK_MODE 20 17 | #endif 18 | 19 | constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; 20 | 21 | /// Registry key for app theme preference. 22 | /// 23 | /// A value of 0 indicates apps should use dark mode. A non-zero or missing 24 | /// value indicates apps should use light mode. 25 | constexpr const wchar_t kGetPreferredBrightnessRegKey[] = 26 | L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; 27 | constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; 28 | 29 | // The number of Win32Window objects that currently exist. 30 | static int g_active_window_count = 0; 31 | 32 | using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); 33 | 34 | // Scale helper to convert logical scaler values to physical using passed in 35 | // scale factor 36 | int Scale(int source, double scale_factor) { 37 | return static_cast(source * scale_factor); 38 | } 39 | 40 | // Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. 41 | // This API is only needed for PerMonitor V1 awareness mode. 42 | void EnableFullDpiSupportIfAvailable(HWND hwnd) { 43 | HMODULE user32_module = LoadLibraryA("User32.dll"); 44 | if (!user32_module) { 45 | return; 46 | } 47 | auto enable_non_client_dpi_scaling = 48 | reinterpret_cast( 49 | GetProcAddress(user32_module, "EnableNonClientDpiScaling")); 50 | if (enable_non_client_dpi_scaling != nullptr) { 51 | enable_non_client_dpi_scaling(hwnd); 52 | } 53 | FreeLibrary(user32_module); 54 | } 55 | 56 | } // namespace 57 | 58 | // Manages the Win32Window's window class registration. 59 | class WindowClassRegistrar { 60 | public: 61 | ~WindowClassRegistrar() = default; 62 | 63 | // Returns the singleton registrar instance. 64 | static WindowClassRegistrar* GetInstance() { 65 | if (!instance_) { 66 | instance_ = new WindowClassRegistrar(); 67 | } 68 | return instance_; 69 | } 70 | 71 | // Returns the name of the window class, registering the class if it hasn't 72 | // previously been registered. 73 | const wchar_t* GetWindowClass(); 74 | 75 | // Unregisters the window class. Should only be called if there are no 76 | // instances of the window. 77 | void UnregisterWindowClass(); 78 | 79 | private: 80 | WindowClassRegistrar() = default; 81 | 82 | static WindowClassRegistrar* instance_; 83 | 84 | bool class_registered_ = false; 85 | }; 86 | 87 | WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; 88 | 89 | const wchar_t* WindowClassRegistrar::GetWindowClass() { 90 | if (!class_registered_) { 91 | WNDCLASS window_class{}; 92 | window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); 93 | window_class.lpszClassName = kWindowClassName; 94 | window_class.style = CS_HREDRAW | CS_VREDRAW; 95 | window_class.cbClsExtra = 0; 96 | window_class.cbWndExtra = 0; 97 | window_class.hInstance = GetModuleHandle(nullptr); 98 | window_class.hIcon = 99 | LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); 100 | window_class.hbrBackground = 0; 101 | window_class.lpszMenuName = nullptr; 102 | window_class.lpfnWndProc = Win32Window::WndProc; 103 | RegisterClass(&window_class); 104 | class_registered_ = true; 105 | } 106 | return kWindowClassName; 107 | } 108 | 109 | void WindowClassRegistrar::UnregisterWindowClass() { 110 | UnregisterClass(kWindowClassName, nullptr); 111 | class_registered_ = false; 112 | } 113 | 114 | Win32Window::Win32Window() { 115 | ++g_active_window_count; 116 | } 117 | 118 | Win32Window::~Win32Window() { 119 | --g_active_window_count; 120 | Destroy(); 121 | } 122 | 123 | bool Win32Window::Create(const std::wstring& title, 124 | const Point& origin, 125 | const Size& size) { 126 | Destroy(); 127 | 128 | const wchar_t* window_class = 129 | WindowClassRegistrar::GetInstance()->GetWindowClass(); 130 | 131 | const POINT target_point = {static_cast(origin.x), 132 | static_cast(origin.y)}; 133 | HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); 134 | UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); 135 | double scale_factor = dpi / 96.0; 136 | 137 | HWND window = CreateWindow( 138 | window_class, title.c_str(), WS_OVERLAPPEDWINDOW, 139 | Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), 140 | Scale(size.width, scale_factor), Scale(size.height, scale_factor), 141 | nullptr, nullptr, GetModuleHandle(nullptr), this); 142 | 143 | if (!window) { 144 | return false; 145 | } 146 | 147 | UpdateTheme(window); 148 | 149 | return OnCreate(); 150 | } 151 | 152 | bool Win32Window::Show() { 153 | return ShowWindow(window_handle_, SW_SHOWNORMAL); 154 | } 155 | 156 | // static 157 | LRESULT CALLBACK Win32Window::WndProc(HWND const window, 158 | UINT const message, 159 | WPARAM const wparam, 160 | LPARAM const lparam) noexcept { 161 | if (message == WM_NCCREATE) { 162 | auto window_struct = reinterpret_cast(lparam); 163 | SetWindowLongPtr(window, GWLP_USERDATA, 164 | reinterpret_cast(window_struct->lpCreateParams)); 165 | 166 | auto that = static_cast(window_struct->lpCreateParams); 167 | EnableFullDpiSupportIfAvailable(window); 168 | that->window_handle_ = window; 169 | } else if (Win32Window* that = GetThisFromHandle(window)) { 170 | return that->MessageHandler(window, message, wparam, lparam); 171 | } 172 | 173 | return DefWindowProc(window, message, wparam, lparam); 174 | } 175 | 176 | LRESULT 177 | Win32Window::MessageHandler(HWND hwnd, 178 | UINT const message, 179 | WPARAM const wparam, 180 | LPARAM const lparam) noexcept { 181 | switch (message) { 182 | case WM_DESTROY: 183 | window_handle_ = nullptr; 184 | Destroy(); 185 | if (quit_on_close_) { 186 | PostQuitMessage(0); 187 | } 188 | return 0; 189 | 190 | case WM_DPICHANGED: { 191 | auto newRectSize = reinterpret_cast(lparam); 192 | LONG newWidth = newRectSize->right - newRectSize->left; 193 | LONG newHeight = newRectSize->bottom - newRectSize->top; 194 | 195 | SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, 196 | newHeight, SWP_NOZORDER | SWP_NOACTIVATE); 197 | 198 | return 0; 199 | } 200 | case WM_SIZE: { 201 | RECT rect = GetClientArea(); 202 | if (child_content_ != nullptr) { 203 | // Size and position the child window. 204 | MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, 205 | rect.bottom - rect.top, TRUE); 206 | } 207 | return 0; 208 | } 209 | 210 | case WM_ACTIVATE: 211 | if (child_content_ != nullptr) { 212 | SetFocus(child_content_); 213 | } 214 | return 0; 215 | 216 | case WM_DWMCOLORIZATIONCOLORCHANGED: 217 | UpdateTheme(hwnd); 218 | return 0; 219 | } 220 | 221 | return DefWindowProc(window_handle_, message, wparam, lparam); 222 | } 223 | 224 | void Win32Window::Destroy() { 225 | OnDestroy(); 226 | 227 | if (window_handle_) { 228 | DestroyWindow(window_handle_); 229 | window_handle_ = nullptr; 230 | } 231 | if (g_active_window_count == 0) { 232 | WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); 233 | } 234 | } 235 | 236 | Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { 237 | return reinterpret_cast( 238 | GetWindowLongPtr(window, GWLP_USERDATA)); 239 | } 240 | 241 | void Win32Window::SetChildContent(HWND content) { 242 | child_content_ = content; 243 | SetParent(content, window_handle_); 244 | RECT frame = GetClientArea(); 245 | 246 | MoveWindow(content, frame.left, frame.top, frame.right - frame.left, 247 | frame.bottom - frame.top, true); 248 | 249 | SetFocus(child_content_); 250 | } 251 | 252 | RECT Win32Window::GetClientArea() { 253 | RECT frame; 254 | GetClientRect(window_handle_, &frame); 255 | return frame; 256 | } 257 | 258 | HWND Win32Window::GetHandle() { 259 | return window_handle_; 260 | } 261 | 262 | void Win32Window::SetQuitOnClose(bool quit_on_close) { 263 | quit_on_close_ = quit_on_close; 264 | } 265 | 266 | bool Win32Window::OnCreate() { 267 | // No-op; provided for subclasses. 268 | return true; 269 | } 270 | 271 | void Win32Window::OnDestroy() { 272 | // No-op; provided for subclasses. 273 | } 274 | 275 | void Win32Window::UpdateTheme(HWND const window) { 276 | DWORD light_mode; 277 | DWORD light_mode_size = sizeof(light_mode); 278 | LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, 279 | kGetPreferredBrightnessRegValue, 280 | RRF_RT_REG_DWORD, nullptr, &light_mode, 281 | &light_mode_size); 282 | 283 | if (result == ERROR_SUCCESS) { 284 | BOOL enable_dark_mode = light_mode == 0; 285 | DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, 286 | &enable_dark_mode, sizeof(enable_dark_mode)); 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /example/windows/runner/win32_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_WIN32_WINDOW_H_ 2 | #define RUNNER_WIN32_WINDOW_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be 11 | // inherited from by classes that wish to specialize with custom 12 | // rendering and input handling 13 | class Win32Window { 14 | public: 15 | struct Point { 16 | unsigned int x; 17 | unsigned int y; 18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {} 19 | }; 20 | 21 | struct Size { 22 | unsigned int width; 23 | unsigned int height; 24 | Size(unsigned int width, unsigned int height) 25 | : width(width), height(height) {} 26 | }; 27 | 28 | Win32Window(); 29 | virtual ~Win32Window(); 30 | 31 | // Creates a win32 window with |title| that is positioned and sized using 32 | // |origin| and |size|. New windows are created on the default monitor. Window 33 | // sizes are specified to the OS in physical pixels, hence to ensure a 34 | // consistent size this function will scale the inputted width and height as 35 | // as appropriate for the default monitor. The window is invisible until 36 | // |Show| is called. Returns true if the window was created successfully. 37 | bool Create(const std::wstring& title, const Point& origin, const Size& size); 38 | 39 | // Show the current window. Returns true if the window was successfully shown. 40 | bool Show(); 41 | 42 | // Release OS resources associated with window. 43 | void Destroy(); 44 | 45 | // Inserts |content| into the window tree. 46 | void SetChildContent(HWND content); 47 | 48 | // Returns the backing Window handle to enable clients to set icon and other 49 | // window properties. Returns nullptr if the window has been destroyed. 50 | HWND GetHandle(); 51 | 52 | // If true, closing this window will quit the application. 53 | void SetQuitOnClose(bool quit_on_close); 54 | 55 | // Return a RECT representing the bounds of the current client area. 56 | RECT GetClientArea(); 57 | 58 | protected: 59 | // Processes and route salient window messages for mouse handling, 60 | // size change and DPI. Delegates handling of these to member overloads that 61 | // inheriting classes can handle. 62 | virtual LRESULT MessageHandler(HWND window, 63 | UINT const message, 64 | WPARAM const wparam, 65 | LPARAM const lparam) noexcept; 66 | 67 | // Called when CreateAndShow is called, allowing subclass window-related 68 | // setup. Subclasses should return false if setup fails. 69 | virtual bool OnCreate(); 70 | 71 | // Called when Destroy is called. 72 | virtual void OnDestroy(); 73 | 74 | private: 75 | friend class WindowClassRegistrar; 76 | 77 | // OS callback called by message pump. Handles the WM_NCCREATE message which 78 | // is passed when the non-client area is being created and enables automatic 79 | // non-client DPI scaling so that the non-client area automatically 80 | // responds to changes in DPI. All other messages are handled by 81 | // MessageHandler. 82 | static LRESULT CALLBACK WndProc(HWND const window, 83 | UINT const message, 84 | WPARAM const wparam, 85 | LPARAM const lparam) noexcept; 86 | 87 | // Retrieves a class instance pointer for |window| 88 | static Win32Window* GetThisFromHandle(HWND const window) noexcept; 89 | 90 | // Update the window frame's theme to match the system theme. 91 | static void UpdateTheme(HWND const window); 92 | 93 | bool quit_on_close_ = false; 94 | 95 | // window handle for top level window. 96 | HWND window_handle_ = nullptr; 97 | 98 | // window handle for hosted content. 99 | HWND child_content_ = nullptr; 100 | }; 101 | 102 | #endif // RUNNER_WIN32_WINDOW_H_ 103 | --------------------------------------------------------------------------------