├── .flutter_tool_state ├── .gitattributes ├── .github ├── lock.yml └── workflows │ ├── actions.yml │ └── publish.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── README_CN.md ├── README_PT.md ├── demo_en.png ├── demo_tablet_en.png ├── demo_tablet_zh.png ├── demo_zh.png ├── example ├── .gitignore ├── .metadata ├── README.md ├── android │ ├── .gitignore │ ├── .settings │ │ └── org.eclipse.buildship.core.prefs │ ├── app │ │ ├── .settings │ │ │ └── org.eclipse.buildship.core.prefs │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── li │ │ │ │ │ └── zhuoyuan │ │ │ │ │ └── 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 ├── example.iml ├── example_android.iml ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ ├── Flutter.podspec │ │ ├── Release.xcconfig │ │ └── flutter_export_environment.sh │ ├── Podfile │ ├── 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.h │ │ ├── AppDelegate.m │ │ ├── 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 │ │ └── main.m ├── lib │ ├── main.dart │ ├── main_zh.dart │ ├── responsive_widgets.g.dart │ ├── responsive_widgets.su.dart │ ├── src │ │ ├── first_method.dart │ │ ├── home.dart │ │ └── second_method.dart │ └── src_zh │ │ ├── first_method.dart │ │ ├── home.dart │ │ └── second_method.dart ├── linux │ ├── .gitignore │ ├── CMakeLists.txt │ ├── flutter │ │ ├── CMakeLists.txt │ │ ├── generated_plugin_registrant.cc │ │ ├── generated_plugin_registrant.h │ │ └── generated_plugins.cmake │ ├── main.cc │ ├── my_application.cc │ └── my_application.h ├── pubspec.yaml ├── test │ └── widget_test.dart ├── web │ ├── favicon.png │ ├── icons │ │ ├── Icon-192.png │ │ └── Icon-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 │ ├── run_loop.cpp │ ├── run_loop.h │ ├── runner.exe.manifest │ ├── utils.cpp │ ├── utils.h │ ├── win32_window.cpp │ └── win32_window.h ├── lib ├── flutter_screenutil.dart └── src │ ├── _flutter_widgets.dart │ ├── r_padding.dart │ ├── r_sizedbox.dart │ ├── screen_util.dart │ ├── screenutil_init.dart │ ├── screenutil_mixin.dart │ └── size_extension.dart ├── pubspec.yaml └── test ├── flutter_screenutil_test.dart └── home.test.dart /.flutter_tool_state: -------------------------------------------------------------------------------- 1 | { 2 | "is-bot": true, 3 | "redisplay-welcome-message": false 4 | } 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.h linguist-language=Dart 2 | -------------------------------------------------------------------------------- /.github/lock.yml: -------------------------------------------------------------------------------- 1 | # Configuration for Lock Threads - https://github.com/dessant/lock-threads-app 2 | 3 | # Number of days of inactivity before a closed issue or pull request is locked 4 | daysUntilLock: 14 5 | 6 | # Skip issues and pull requests created before a given timestamp. Timestamp must 7 | # follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable 8 | skipCreatedBefore: false 9 | 10 | # Issues and pull requests with these labels will be ignored. Set to `[]` to disable 11 | exemptLabels: [] 12 | 13 | # Label to add before locking, such as `outdated`. Set to `false` to disable 14 | lockLabel: false 15 | 16 | # Comment to post before locking. Set to `false` to disable 17 | lockComment: > 18 | This thread has been automatically locked since there has not been 19 | any recent activity after it was closed. If you are still experiencing a 20 | similar issue, please open a new bug, including the output of 21 | `flutter doctor -v` and a minimal reproduction of the issue. 22 | # Assign `resolved` as the reason for locking. Set to `false` to disable 23 | setLockReason: false 24 | 25 | # Limit to only `issues` or `pulls` 26 | only: issues 27 | 28 | # Optionally, specify configuration settings just for `issues` or `pulls` 29 | # issues: 30 | # exemptLabels: 31 | # - help-wanted 32 | # lockLabel: outdated 33 | 34 | # pulls: 35 | # daysUntilLock: 30 36 | 37 | # Repository to extend settings from 38 | # _extends: repo -------------------------------------------------------------------------------- /.github/workflows/actions.yml: -------------------------------------------------------------------------------- 1 | # .github/workflows/publish.yml 2 | name: Publish to pub.dev 3 | 4 | on: 5 | push: 6 | tags: 7 | - 'v[0-9]+.[0-9]+.[0-9]+*' # tag pattern on pub.dev: 'v' 8 | 9 | # Publish using custom workflow 10 | jobs: 11 | publish: 12 | permissions: 13 | id-token: write # Required for authentication using OIDC 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | - uses: dart-lang/setup-dart@v1 18 | - name: Install dependencies 19 | run: dart pub get 20 | # Here you can insert custom steps you need 21 | # - run: dart tool/generate-code.dart 22 | - name: Publish 23 | run: dart pub publish --force -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to Pub.dev 2 | 3 | # 流程触发时机,当有标签创建时触发,如 v1.0.0。当然也可以选择别的触发时机,如 push,release 等 4 | on: 5 | push: 6 | tags: 7 | - 'v[0-9]+.[0-9]+.[0-9]+*' # tag pattern on pub.dev: 'v' 8 | 9 | jobs: 10 | publishing: 11 | runs-on: ubuntu-latest 12 | steps: 13 | # 拉去仓库代码 14 | - name: "Checkout" 15 | uses: actions/checkout@main 16 | # 发布插件 17 | - name: Dart and Flutter Package Publisher 18 | uses: k-paxian/dart-package-publisher@master 19 | with: 20 | # 设置发布插件需要的 Token 21 | accessToken: ${{ secrets.OAUTH_ACCESS_TOKEN }} 22 | refreshToken: ${{ secrets.OAUTH_REFRESH_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.lock 4 | *.log 5 | *.pyc 6 | *.swp 7 | .DS_Store 8 | .atom/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | 13 | # IntelliJ related 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # Visual Studio Code related 19 | .classpath 20 | .project 21 | .vscode/ 22 | 23 | # Flutter repo-specific 24 | /bin/cache/ 25 | /bin/internal/bootstrap.bat 26 | /bin/internal/bootstrap.sh 27 | /bin/mingit/ 28 | /dev/benchmarks/mega_gallery/ 29 | /dev/bots/.recipe_deps 30 | /dev/bots/android_tools/ 31 | /dev/devicelab/ABresults*.json 32 | /dev/docs/doc/ 33 | /dev/docs/flutter.docs.zip 34 | /dev/docs/lib/ 35 | /dev/docs/pubspec.yaml 36 | /dev/integration_tests/**/xcuserdata 37 | /dev/integration_tests/**/Pods 38 | /packages/flutter/coverage/ 39 | version 40 | analysis_benchmark.json 41 | 42 | # packages file containing multi-root paths 43 | .packages.generated 44 | 45 | # Flutter/Dart/Pub related 46 | **/doc/api/ 47 | .dart_tool/ 48 | .flutter-plugins 49 | .flutter-plugins-dependencies 50 | .packages 51 | .pub-cache/ 52 | .pub/ 53 | build/ 54 | flutter_*.png 55 | linked_*.ds 56 | unlinked.ds 57 | unlinked_spec.ds 58 | 59 | # Android related 60 | **/android/**/gradle-wrapper.jar 61 | .gradle/ 62 | **/android/captures/ 63 | **/android/gradlew 64 | **/android/gradlew.bat 65 | **/android/local.properties 66 | **/android/**/GeneratedPluginRegistrant.java 67 | **/android/key.properties 68 | *.jks 69 | 70 | # iOS/XCode related 71 | **/ios/**/*.mode1v3 72 | **/ios/**/*.mode2v3 73 | **/ios/**/*.moved-aside 74 | **/ios/**/*.pbxuser 75 | **/ios/**/*.perspectivev3 76 | **/ios/**/*sync/ 77 | **/ios/**/.sconsign.dblite 78 | **/ios/**/.tags* 79 | **/ios/**/.vagrant/ 80 | **/ios/**/DerivedData/ 81 | **/ios/**/Icon? 82 | **/ios/**/Pods/ 83 | **/ios/**/.symlinks/ 84 | **/ios/**/profile 85 | **/ios/**/xcuserdata 86 | **/ios/.generated/ 87 | **/ios/Flutter/.last_build_id 88 | **/ios/Flutter/App.framework 89 | **/ios/Flutter/Flutter.framework 90 | **/ios/Flutter/Generated.xcconfig 91 | **/ios/Flutter/ephemeral 92 | **/ios/Flutter/app.flx 93 | **/ios/Flutter/app.zip 94 | **/ios/Flutter/flutter_assets/ 95 | **/ios/ServiceDefinitions.json 96 | **/ios/Runner/GeneratedPluginRegistrant.* 97 | 98 | # macOS 99 | **/Flutter/ephemeral/ 100 | **/Pods/ 101 | **/macos/Flutter/GeneratedPluginRegistrant.swift 102 | **/macos/Flutter/ephemeral 103 | **/xcuserdata/ 104 | 105 | 106 | # Coverage 107 | coverage/ 108 | 109 | # Symbols 110 | app.*.symbols 111 | 112 | # Exceptions to above rules. 113 | !**/ios/**/default.mode1v3 114 | !**/ios/**/default.mode2v3 115 | !**/ios/**/default.pbxuser 116 | !**/ios/**/default.perspectivev3 117 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 118 | !/dev/ci/**/Gemfile.lock 119 | 120 | # Don’t commit the following directories created by pub. 121 | .buildlog 122 | .dart_tool/ 123 | .pub/ 124 | build/ 125 | packages 126 | *.packages 127 | .idea/ 128 | web/experimental 129 | doc 130 | .packages 131 | 132 | # Or the files created by dart2js. 133 | *.dart.js 134 | *.js_ 135 | *.js.deps 136 | *.js.map 137 | 138 | # Include when developing application packages. 139 | .lock 140 | coverage* 141 | *.lock 142 | 143 | # Don't commit .fvm directory containing machine-specific symlink to sdk & flutter version 144 | **/.fvm 145 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 5.9.3 2 | feat: support for disabling scaling 3 | 4 | # 5.9.2 5 | - Add ability to exclude widgets from from rebuild list 6 | - Depend on View instead of MediaQuery 7 | - update readme 8 | 9 | # 5.9.1 10 | - merge pr #550 #555 #556 11 | - update readme 12 | 13 | # 5.9.0 14 | - ScreenUtilInit won't rebuild the whole widget tree 15 | - Add `fontSizeResolver` to specify how font size should be scaled 16 | - Add `diameter` & `diagonal` factors 17 | - `useInheritedMediaQuery` has not effect, and will be removed in next release 18 | - Fix `ensureScreenSize` in web platform 19 | - add deviceType 20 | 21 | # 5.8.4 22 | - bug fix 23 | - change useInheritedMediaQuery default value to false 24 | 25 | # 5.8.3 26 | - Adapt to flutter3.10 27 | 28 | # 5.8.2 29 | - change useInheritedMediaQuery default value to true 30 | - update readme 31 | - remove ScreenutilInit layout listener 32 | 33 | # 5.8.1 34 | - Limit flutter version > 3.10.0 35 | 36 | # 5.7.0 37 | - merge #464 38 | 39 | # 5.6.1 40 | 41 | - add spMax => max(toDouble(), sp), and spMin, for example: 12.spMax 42 | - FlutterView instead of FlutterWindow 43 | 44 | # 5.6.0 45 | 46 | - bug fix: #434 47 | - add w and h on EdgeInsets,Radius,BorderRadius,BoxConstraints 48 | 49 | # 5.5.4 50 | 51 | - Bug Fix: False sizes when using DevicePreview 52 | 53 | # 5.5.3+2 54 | 55 | - Fix #398 56 | 57 | # 5.5.3+1 58 | 59 | - Fix compatibility with flutter sdk 2.x 60 | 61 | # 5.5.3 62 | 63 | - Bug Fix: Some widgets disapear because of parent rebuild. 64 | - Bug Fix: issue #362. Null check operator used on a null value when using ScreenUtil.init(). 65 | - Re-add context to builder parameters **(users request)**. 66 | - Add some standard rebuild factors. 67 | 68 | # 5.5.2 69 | 70 | - Add rebuildFactor property 71 | - Bug Fix: False sizes when resizing 72 | 73 | # 5.5.1 74 | 75 | - Bug Fix: Assertion Failed (Find MediaQuery in ancestors) 76 | - Some performance improvements and fixes 77 | 78 | # 5.5.0 79 | 80 | - Bug Fix: Reaching context that no longer used. 81 | 82 | # 5.4.0+1 83 | 84 | - delete log 85 | 86 | # 5.4.0 87 | 88 | - merge #352 89 | 90 | # 5.3.1 91 | 92 | - add num.verticalSpacingRadius num.horizontalSpaceRadius num.setVerticalSpacingFromWidth 93 | - update num.horizontalSpace 94 | 95 | # 5.3.0 96 | 97 | - For the size, use the context to get it first, which needs to provide the Context More rigorous 98 | context checking 99 | 100 | # 5.2.0 101 | 102 | - Rollback of 5.1.1 commit 103 | - Fix the problem of inaccurate height 104 | - merge #332(https://github.com/OpenFlutter/flutter_screenutil/pull/332) 105 | add num.setVerticalSpacing // SizedBox(height: num * scaleHeight) 106 | num.horizontalSpace // SizedBox(height: num * scaleWidth) 107 | 108 | # 5.1.1 109 | 110 | - .w,.h use MediaQuery 111 | 112 | # 5.1.0 113 | 114 | - Break Change: updated the first initialization method, please refer to README.md 115 | 116 | # 5.0.4 117 | 118 | - Break Change : add setContext() , the first initialization method requires calling 119 | - fix # 310 120 | - update ReadMe.md 121 | 122 | # 5.0.3 123 | 124 | - init method add "context" param 125 | - update ReadMe.md 126 | 127 | # 5.0.2+1 128 | 129 | - fix splitScreenMode to false 130 | 131 | # 5.0.2 132 | 133 | - add "minTextAdapt" param , Font adaptation is based on the minimum value of width and height or 134 | only based on width(default) 135 | - update readme 136 | 137 | # 5.0.1+3 138 | 139 | - fix .r 140 | 141 | # 5.0.1+2 142 | 143 | - Text adaptation no longer considers the height of the screen 144 | 145 | # 5.0.1+1 146 | 147 | - split default value change to false 148 | 149 | # 5.0.1 150 | 151 | - support for split screen 152 | - add number.sm (return min(number.sp , number)) 153 | 154 | # 5.0.0+2 155 | 156 | - update readme 157 | 158 | # 5.0.0+1 159 | 160 | - update readme 161 | 162 | # 5.0.0 163 | 164 | -Breaking change. Use a new way to set font scaling -Deprecated ssp and nsp 165 | 166 | # 5.0.0-nullsafety.11 167 | 168 | - revert 5.0.0-nullsafety.10 169 | - fix #230 170 | 171 | # 5.0.0-nullsafety.10 172 | 173 | - fix #228 174 | 175 | # 5.0.0-nullsafety.9 176 | 177 | - Supplementary documentation, supports two initialization methods 178 | 179 | # 5.0.0-nullsafety.8 180 | 181 | - merge v4 182 | - Add a method to get the screen orientation 183 | 184 | # 5.0.0-nullsafety.7 185 | 186 | - fix #221 187 | 188 | # 5.0.0-nullsafety.6 189 | 190 | - merge #216 #218 191 | 192 | # 5.0.0-nullsafety.5 193 | 194 | - Optimize initialization method 195 | 196 | # 5.0.0-nullsafety.4 197 | 198 | - merge #205 199 | 200 | # 5.0.0-nullsafety.3 201 | 202 | - merge 4.0.2+3 203 | 204 | # 5.0.0-nullsafety.2 205 | 206 | - merge 4.0.2+2 #186 207 | 208 | # 5.0.0-nullsafety.1 209 | 210 | - merge 4.0.1 ,4.0.2 #183 211 | 212 | # 5.0.0-nullsafety.0 213 | 214 | - Migrated flutter_screenutil to non-nullable 215 | 216 | # 4.0.2 217 | 218 | - add r(),adapt according to the smaller of width or height 219 | 220 | # 4.0.1 221 | 222 | - Modify the initialization unit to dp 223 | - delete screenWidthPx and screenHeightPx(No one use these method,I guess) 224 | 225 | # 4.0.0 226 | 227 | - update to 4.0.0 228 | 229 | # 4.0.0-beta3 230 | 231 | - Optimize the way of initialization 232 | 233 | # 4.0.0-beta2 234 | 235 | - fix error:'window is not a type.' 236 | 237 | # 4.0.0-beta1 238 | 239 | - change readme 240 | 241 | # 4.0.0-beta 242 | 243 | - Modified the initialization method 244 | - Support font adaptation in themedata 245 | 246 | # 3.2.0 247 | 248 | - Modify the method name to be more semantic: wp->sw , hp->sh 249 | - Remove the restriction of flutter version 250 | - Modify the return type num to double 251 | 252 | # 3.1.1 253 | 254 | - change readme 255 | 256 | # 3.1.0 257 | 258 | - Use the way back to v2 version 259 | - Modify registration method 260 | 261 | # 3.0.2+1 262 | 263 | - Guide users to use V2 version 264 | 265 | # 3.0.2 266 | 267 | - Change the unit of'statusBarHeight' and 'bottomBarHeight' to dp 268 | 269 | # 3.0.1 270 | 271 | - update readme 272 | 273 | # 3.0.0 274 | 275 | - After a period of experimentation, I think it's time to release the official version 276 | 277 | # 3.0.0-beta.2 278 | 279 | - readme update 280 | 281 | # 3.0.0-beta.1 282 | 283 | **BREAKING CHANGES** 284 | 285 | - `BuildContext` is no more required while initializing. i.e. ScreenUtil.init(~~context~~) 286 | - Initialize size of design draft using `designSize` instead of width & height. 287 | - All the static methods are now member methods. 288 | 289 | # 2.3.1 290 | 291 | - add textStyle Example. 292 | 293 | # 2.3.0 294 | 295 | - We still need context to initialize, sorry. 296 | 297 | # 2.2.0 298 | 299 | - add 'wp','hp'. Get the height/width of the screen proportionally 300 | - For example: 0.5.wp : Half the width of the screen. 301 | 302 | # 2.1.0 303 | 304 | - add 'nsp' , you can use 'fontSize: 24.nsp' instead of 'fontSize: ScreenUtil().setSp(24, 305 | allowFontScalingSelf: false)' 306 | 307 | # 2.0.0 308 | 309 | - Use `MediaQueryData.fromWindow(window)` instead of `MediaQuery.of(context)`, no context parameter 310 | required 311 | - API changes, please note 312 | 313 | # 1.1.0 314 | 315 | - support ExtensionMethod Dart-SDK-2.6.0 316 | - you can use 'width: 50.w' instead of 'width: ScreenUtil().setWidth(50)' 317 | '50.h' instead of 'ScreenUtil().setHeight(50)' 318 | '24.sp' instead of 'ScreenUtil().setSp(24)' 319 | '24.ssp' instead of 'ScreenUtil().setSp(24, allowFontScalingSelf: true)' 320 | 321 | # 1.0.2 322 | 323 | - fix #89 324 | - 优化屏幕旋转效果 325 | - 字体适配统一使用宽度 326 | 327 | # 1.0.1 328 | 329 | - Rebuild code, change API Delete "getInstance()", please use "ScreenUtil ()" instead of " 330 | ScreenUtil.getInstance()" 331 | use "ScreenUtil().setSp(24, allowFontScalingSelf: true)" instead of "ScreenUtil.getInstance() 332 | .setSp(14, true)" 333 | - Modify the initialization method 334 | - Fix #68 335 | - Change example code Example CompileSdkVersion change to 28 336 | 337 | **If there is significant impact, please return to 0.7.0** 338 | 339 | # 0.7.0 340 | 341 | - Replace textScaleFactory with textScaleFactor , It's a typo. 342 | 343 | # 0.6.1 344 | 345 | - Add return types to all methods. 346 | 347 | # 0.6.0 348 | 349 | - Completing comments , adding English commentsWelcome to add, correct 350 | - 参数同时支持传入 int / double 或者是var size = 100 , var size = 100.0. 351 | - The argument also supports passing in in / double / var size = 100 /var size = 100.0 352 | 353 | # 0.5.3 354 | 355 | - Change the units of statusBarHeight and bottomBarHeight to dp 356 | 357 | # 0.5.2 358 | 359 | - Change the parameter type from int to double 360 | 361 | - setWidth,setHeight,setSp. for example: you can use setWidth(100) or setWidth(100.0) 362 | 363 | # 0.5.1 364 | 365 | - Fix the wrong way of using 366 | 367 | - It is recommended to use `ScreenUtil.getInstance()` instead of `ScreenUtil()` , for 368 | example: `ScreenUtil.getInstance().setHeight(25)` instead of `ScreenUtil().setHeight(25)` 369 | 370 | # 0.4.4 371 | 372 | - Fix bugs that default fonts change with the system 373 | 374 | # 0.4.3 375 | 376 | - Modify the font to change with the system zoom mode. The default value is false. 377 | 378 | - setSp(int fontSize, [allowFontScaling = false]) => allowFontScaling ? setWidth(fontSize) \* 379 | \_textScaleFactor 380 | : setWidth(fontSize); 381 | 382 | # 0.4.2 383 | 384 | - add two Properties 385 | - ///Current device width dp 386 | - ///当前设备宽度 dp 387 | - ScreenUtil.screenWidthDp 388 | 389 | - ///Current device height dp 390 | - ///当前设备高度 dp 391 | - ScreenUtil.screenHeightDp 392 | 393 | # 0.4.1 394 | 395 | - Fix font adaptation issues 396 | 397 | # 0.4.0 398 | 399 | - Optimize font adaptation method 400 | 401 | # 0.3.1 402 | 403 | - Perfect documentation 404 | - Width is enlarged relative to the design draft => The ratio of font and width to the size of the 405 | design 406 | - Height is enlarged relative to the design draft => The ratio of height width to the size of the 407 | design 408 | 409 | # 0.3.0 410 | 411 | - Add font size adaptation 412 | 413 | # 0.2.2 414 | 415 | - Optimize documentation 416 | 417 | # 0.0.2 418 | 419 | - Fixed bug when releasing 420 | 421 | # 0.0.1 422 | 423 | - first version 424 | 425 | 426 | 427 | 428 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flutter_screenutil 2 | 3 | [![Flutter Package](https://img.shields.io/pub/v/flutter_screenutil.svg)](https://pub.dev/packages/flutter_screenutil) 4 | [![Pub Points](https://img.shields.io/pub/points/flutter_screenutil)](https://pub.dev/packages/flutter_screenutil/score) 5 | [![Popularity](https://img.shields.io/pub/popularity/flutter_screenutil)](https://pub.dev/packages/flutter_screenutil/score) 6 | [![CodeFactor](https://www.codefactor.io/repository/github/openflutter/flutter_screenutil/badge)](https://www.codefactor.io/repository/github/openflutter/flutter_screenutil) 7 | 8 | **A flutter plugin for adapting screen and font size.Let your UI display a reasonable layout on different screen sizes!** 9 | 10 | *Note*: This plugin is still under development, and some APIs might not be available yet. 11 | 12 | [中文文档](https://github.com/OpenFlutter/flutter_screenutil/blob/master/README_CN.md) 13 | 14 | [README em Português](https://github.com/OpenFlutter/flutter_screenutil/blob/master/README_PT.md) 15 | 16 | [github](https://github.com/OpenFlutter/flutter_screenutil) 17 | 18 | [Update log](https://github.com/OpenFlutter/flutter_screenutil/blob/master/CHANGELOG.md) 19 | 20 | ## Usage 21 | 22 | ### Add dependency 23 | 24 | Please check the latest version before installation. 25 | If there is any problem with the new version, please use the previous version 26 | 27 | ```yaml 28 | dependencies: 29 | flutter: 30 | sdk: flutter 31 | # add flutter_screenutil 32 | flutter_screenutil: ^{latest version} 33 | ``` 34 | 35 | ### Add the following imports to your Dart code 36 | 37 | ```dart 38 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 39 | ``` 40 | 41 | ### Properties 42 | 43 | | Property | Type | Default Value | Description | 44 | | ----------------- | ---------------- | ------------- |-----------------------------------------------------------------------------------------------------------------------------------------------| 45 | | designSize | Size | Size(360,690) | The size of the device screen in the design draft, in dp | 46 | | builder | Function | null | Return widget that uses the library in a property (ex: MaterialApp's theme) | 47 | | child | Widget | null | A part of builder that its dependencies/properties don't use the library | 48 | | rebuildFactor | Function | _default_ | Function that take old and new screen metrics and returns whether to rebuild or not when changes. | 49 | | splitScreenMode | bool | false | support for split screen | 50 | | minTextAdapt | bool | false | Whether to adapt the text according to the minimum of width and height | 51 | | context | BuildContext | null | Get physical device data if not provided, by MediaQuery.of(context) | 52 | | fontSizeResolver | Function | _default_ | Function that specify how font size should be adapted. Default is that font size scale with width of screen. | 53 | | responsiveWidgets | Iterable | null | List/Set of widget names that should be included in rebuilding tree. (See [How flutter_screenutil marks a widget needs build](#rebuild-list)) | 54 | | excludeWidgets | Iterable | null | List/Set of widget names that should be excluded from rebuilding tree. | 55 | | enableScaleWH | Function | null | Support enable scale width and height. | 56 | | enableScaleText | Function | null | Support enable scale text. | 57 | 58 | 59 | **Note : You must either provide builder, child or both.** 60 | 61 | ### Rebuild list 62 | Starting from version 5.9.0, ScreenUtilInit won't rebuild the whole widget tree, instead it will mark widget needs build only if: 63 | - Widget is not a flutter widget (widgets are available in [Flutter Docs](https://docs.flutter.dev/reference/widgets)) 64 | - Widget does not start with underscore (`_`) 65 | - Widget does not declare `SU` mixin 66 | - `responsiveWidgets` does not contains widget name 67 | 68 | If you have a widget that uses the library and doesn't meet these options you can either add `SU` mixin or add widget name in responsiveWidgets list. 69 | 70 | ### Initialize and set the fit size and font size to scale according to the system's "font size" accessibility option 71 | 72 | Please set the size of the design draft before use, the width and height of the design draft. 73 | 74 | #### The first way (You should use it once in your app) 75 | 76 | ```dart 77 | void main() => runApp(MyApp()); 78 | 79 | class MyApp extends StatelessWidget { 80 | const MyApp({Key? key}) : super(key: key); 81 | 82 | @override 83 | Widget build(BuildContext context) { 84 | //Set the fit size (Find your UI design, look at the dimensions of the device screen and fill it in,unit in dp) 85 | return ScreenUtilInit( 86 | designSize: const Size(360, 690), 87 | minTextAdapt: true, 88 | splitScreenMode: true, 89 | // Use builder only if you need to use library outside ScreenUtilInit context 90 | builder: (_ , child) { 91 | return MaterialApp( 92 | debugShowCheckedModeBanner: false, 93 | title: 'First Method', 94 | // You can use the library anywhere in the app even in theme 95 | theme: ThemeData( 96 | primarySwatch: Colors.blue, 97 | textTheme: Typography.englishLike2018.apply(fontSizeFactor: 1.sp), 98 | ), 99 | home: child, 100 | ); 101 | }, 102 | child: const HomePage(title: 'First Method'), 103 | ); 104 | } 105 | } 106 | ``` 107 | 108 | #### The second way:You need a trick to support font adaptation in the textTheme of app theme 109 | 110 | **Hybrid development uses the second way** 111 | 112 | not support this: 113 | 114 | ```dart 115 | MaterialApp( 116 | ... 117 | //To support the following, you need to use the first initialization method 118 | theme: ThemeData( 119 | textTheme: TextTheme( 120 | button: TextStyle(fontSize: 45.sp) 121 | ), 122 | ), 123 | ) 124 | ``` 125 | 126 | but you can do this: 127 | 128 | ```dart 129 | void main() async { 130 | // Add this line 131 | await ScreenUtil.ensureScreenSize(); 132 | runApp(MyApp()); 133 | } 134 | ... 135 | MaterialApp( 136 | ... 137 | builder: (ctx, child) { 138 | ScreenUtil.init(ctx); 139 | return Theme( 140 | data: ThemeData( 141 | primarySwatch: Colors.blue, 142 | textTheme: TextTheme(bodyText2: TextStyle(fontSize: 30.sp)), 143 | ), 144 | child: HomePage(title: 'FlutterScreenUtil Demo'), 145 | ); 146 | }, 147 | ) 148 | ``` 149 | 150 | ```dart 151 | class MyApp extends StatelessWidget { 152 | @override 153 | Widget build(BuildContext context) { 154 | return MaterialApp( 155 | debugShowCheckedModeBanner: false, 156 | title: 'Flutter_ScreenUtil', 157 | theme: ThemeData( 158 | primarySwatch: Colors.blue, 159 | ), 160 | home: HomePage(title: 'FlutterScreenUtil Demo'), 161 | ); 162 | } 163 | } 164 | 165 | class HomePage extends StatefulWidget { 166 | const HomePage({Key key, this.title}) : super(key: key); 167 | 168 | final String title; 169 | 170 | @override 171 | _HomePageState createState() => _HomePageState(); 172 | } 173 | 174 | class _HomePageState extends State { 175 | @override 176 | Widget build(BuildContext context) { 177 | //Set the fit size (fill in the screen size of the device in the design) 178 | //If the design is based on the size of the 360*690(dp) 179 | ScreenUtil.init(context, designSize: const Size(360, 690)); 180 | ... 181 | } 182 | } 183 | ``` 184 | 185 | **Note: calling ScreenUtil.init second time, any non-provided parameter will not be replaced with default value. Use ScreenUtil.configure instead** 186 | 187 | ### API 188 | 189 | #### Enable or disable scale 190 | 191 | ```dart 192 | Widget build(BuildContext context) { 193 | return ScreenUtilInit( 194 | enableScaleWH: ()=>false, 195 | enableScaleText: ()=>false, 196 | //... 197 | ); 198 | } 199 | ``` 200 | 201 | or 202 | 203 | ```dart 204 | ScreenUtil.enableScale(enableWH: () => false, enableText: () => false); 205 | ``` 206 | 207 | 208 | #### Pass the dp size of the design draft 209 | 210 | ```dart 211 | ScreenUtil().setWidth(540) (dart sdk>=2.6 : 540.w) //Adapted to screen width 212 | ScreenUtil().setHeight(200) (dart sdk>=2.6 : 200.h) //Adapted to screen height , under normal circumstances, the height still uses x.w 213 | ScreenUtil().radius(200) (dart sdk>=2.6 : 200.r) //Adapt according to the smaller of width or height 214 | ScreenUtil().setSp(24) (dart sdk>=2.6 : 24.sp) //Adapter font 215 | 12.sm //return min(12,12.sp) 216 | 217 | ScreenUtil().pixelRatio //Device pixel density 218 | ScreenUtil().screenWidth (dart sdk>=2.6 : 1.sw) //Device width 219 | ScreenUtil().screenHeight (dart sdk>=2.6 : 1.sh) //Device height 220 | ScreenUtil().bottomBarHeight //Bottom safe zone distance, suitable for buttons with full screen 221 | ScreenUtil().statusBarHeight //Status bar height , Notch will be higher 222 | ScreenUtil().textScaleFactor //System font scaling factor 223 | 224 | ScreenUtil().scaleWidth //The ratio of actual width to UI design 225 | ScreenUtil().scaleHeight //The ratio of actual height to UI design 226 | 227 | ScreenUtil().orientation //Screen orientation 228 | 0.2.sw //0.2 times the screen width 229 | 0.5.sh //50% of screen height 230 | 20.setVerticalSpacing // SizedBox(height: 20 * scaleHeight) 231 | 20.horizontalSpace // SizedBox(height: 20 * scaleWidth) 232 | const RPadding.all(8) // Padding.all(8.r) - take advantage of const key word 233 | EdgeInsets.all(10).w //EdgeInsets.all(10.w) 234 | REdgeInsets.all(8) // EdgeInsets.all(8.r) 235 | EdgeInsets.only(left:8,right:8).r // EdgeInsets.only(left:8.r,right:8.r). 236 | BoxConstraints(maxWidth: 100, minHeight: 100).w //BoxConstraints(maxWidth: 100.w, minHeight: 100.w) 237 | Radius.circular(16).w //Radius.circular(16.w) 238 | BorderRadius.all(Radius.circular(16)).w 239 | ``` 240 | 241 | #### Adapt screen size 242 | 243 | Pass the dp size of the design draft((The unit is the same as the unit at initialization)): 244 | 245 | Adapted to screen width: `ScreenUtil().setWidth(540)`, 246 | 247 | Adapted to screen height: `ScreenUtil().setHeight(200)`, In general, the height is best to adapt to the width 248 | 249 | If your dart sdk>=2.6, you can use extension functions: 250 | 251 | example: 252 | 253 | instead of : 254 | 255 | ```dart 256 | Container( 257 | width: ScreenUtil().setWidth(50), 258 | height:ScreenUtil().setHeight(200), 259 | ) 260 | ``` 261 | 262 | you can use it like this: 263 | 264 | ```dart 265 | Container( 266 | width: 50.w, 267 | height:200.h 268 | ) 269 | ``` 270 | 271 | #### `Note` 272 | 273 | The height can also use setWidth to ensure that it is not deformed(when you want a square) 274 | 275 | The setHeight method is mainly to adapt to the height, which is used when you want to control the height of a screen on the UI to be the same as the actual display. 276 | 277 | Generally speaking, 50.w!=50.h. 278 | 279 | ```dart 280 | //for example: 281 | 282 | //If you want to display a rectangle: 283 | Container( 284 | width: 375.w, 285 | height: 375.h, 286 | ), 287 | 288 | //If you want to display a square based on width: 289 | Container( 290 | width: 300.w, 291 | height: 300.w, 292 | ), 293 | 294 | //If you want to display a square based on height: 295 | Container( 296 | width: 300.h, 297 | height: 300.h, 298 | ), 299 | 300 | //If you want to display a square based on minimum(height, width): 301 | Container( 302 | width: 300.r, 303 | height: 300.r, 304 | ), 305 | ``` 306 | 307 | #### Adapter font 308 | 309 | ``` dart 310 | //Incoming font size(The unit is the same as the unit at initialization) 311 | ScreenUtil().setSp(28) 312 | 28.sp 313 | 314 | //for example: 315 | Column( 316 | crossAxisAlignment: CrossAxisAlignment.start, 317 | children: [ 318 | Text( 319 | '16sp, will not change with the system.', 320 | style: TextStyle( 321 | color: Colors.black, 322 | fontSize: 16.sp, 323 | ), 324 | textScaleFactor: 1.0, 325 | ), 326 | Text( 327 | '16sp,if data is not set in MediaQuery,my font size will change with the system.', 328 | style: TextStyle( 329 | color: Colors.black, 330 | fontSize: 16.sp, 331 | ), 332 | ), 333 | ], 334 | ) 335 | ``` 336 | 337 | #### Setting font does not change with system font size 338 | 339 | APP global: 340 | 341 | ```dart 342 | MaterialApp( 343 | debugShowCheckedModeBanner: false, 344 | title: 'Flutter_ScreenUtil', 345 | theme: ThemeData( 346 | primarySwatch: Colors.blue, 347 | ), 348 | builder: (context, widget) { 349 | return MediaQuery( 350 | ///Setting font does not change with system font size 351 | data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), 352 | child: widget, 353 | ); 354 | }, 355 | home: HomePage(title: 'FlutterScreenUtil Demo'), 356 | ), 357 | ``` 358 | 359 | Specified Text: 360 | 361 | ```dart 362 | Text("text", textScaleFactor: 1.0) 363 | ``` 364 | 365 | Specified Widget: 366 | 367 | ```dart 368 | MediaQuery( 369 | // If there is no context available you can wrap [MediaQuery] with [Builder] 370 | data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), 371 | child: AnyWidget(), 372 | ) 373 | ``` 374 | 375 | [widget test](https://github.com/OpenFlutter/flutter_screenutil/issues/115) 376 | 377 | ### Example 378 | 379 | [example demo](https://github.com/OpenFlutter/flutter_screenutil/blob/master/example/lib) 380 | 381 | To use second method run: `flutter run --dart-define=method=2` 382 | 383 | ### Effect 384 | 385 | ![effect](demo_en.png) 386 | ![tablet effect](demo_tablet_en.png) 387 | 388 | 389 | ### Update for Version 5.9.0 (Tests) 390 | Reported as bug in [#515](https://github.com/OpenFlutter/flutter_screenutil/issues/515) 391 | 392 | 393 | In version 5.9.0, to ensure compatibility and proper functioning of your tests, it is crucial to use the method `tester.pumpAndSettle()`; when conducting widget tests that depend on animations or a settling time to complete their state. 394 | 395 | In the previous version, this step was not strictly necessary. However, to maintain consistency in your tests and avoid unexpected errors, it's strongly recommended incorporating await tester.pumpAndSettle(); in your widget tests if you are using version 5.9.0 396 | 397 | Example usage: 398 | ```dart 399 | testWidgets('Should ensure widgets settle correctly', (WidgetTester tester) async { 400 | await tester.pumpWidget( 401 | const MaterialApp( 402 | home: ScreenUtilInit( 403 | child: MyApp(), 404 | ), 405 | ), 406 | ); 407 | // Insertion of recommended method to prevent failures 408 | await tester.pumpAndSettle(); 409 | // Continue with your assertions and tests 410 | }); 411 | ``` -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | # flutter_screenutil 2 | 3 | [![Flutter Package](https://img.shields.io/pub/v/flutter_screenutil.svg)](https://pub.dev/packages/flutter_screenutil) 4 | [![Pub Points](https://img.shields.io/pub/points/flutter_screenutil)](https://pub.dev/packages/flutter_screenutil/score) 5 | [![Popularity](https://img.shields.io/pub/popularity/flutter_screenutil)](https://pub.dev/packages/flutter_screenutil/score) 6 | [![CodeFactor](https://www.codefactor.io/repository/github/openflutter/flutter_screenutil/badge)](https://www.codefactor.io/repository/github/openflutter/flutter_screenutil) 7 | 8 | **flutter 屏幕适配方案,用于调整屏幕和字体大小的flutter插件,让你的UI在不同尺寸的屏幕上都能显示合理的布局!** 9 | 10 | 11 | *注意*:此插件仍处于开发阶段,某些API可能尚不可用。 12 | 13 | [README of English](https://github.com/OpenFlutter/flutter_ScreenUtil/blob/master/README.md) 14 | 15 | [README em Português](https://github.com/OpenFlutter/flutter_screenutil/blob/master/README_PT.md) 16 | 17 | [github](https://github.com/OpenFlutter/flutter_screenutil) 18 | 19 | [csdn博客工具介绍](https://blog.csdn.net/u011272795/article/details/82795477) 20 | 21 | [更新日志](https://github.com/OpenFlutter/flutter_screenutil/blob/master/CHANGELOG.md) 22 | 23 | ## 使用方法: 24 | 25 | ### 安装依赖: 26 | 27 | 安装之前请查看最新版本 28 | 新版本如有问题请使用以前的版本 29 | ```yaml 30 | dependencies: 31 | flutter: 32 | sdk: flutter 33 | # 添加依赖 34 | flutter_screenutil: ^{latest version} 35 | ``` 36 | ### 在每个使用的地方导入包: 37 | ```dart 38 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 39 | ``` 40 | 41 | ### 属性 42 | 43 | |属性|类型|默认值|描述| 44 | |:---|:---|:---|:---| 45 | |designSize|Size|Size(360, 690)|设计稿中设备的尺寸(单位随意,建议dp,但在使用过程中必须保持一致)| 46 | | deviceSize | Size | null | 物理设备的大小 | 47 | |builder|Widget Function()|Container()|一般返回一个MaterialApp类型的Function()| 48 | |orientation|Orientation|portrait|屏幕方向| 49 | |splitScreenMode|bool|false|支持分屏尺寸| 50 | |minTextAdapt|bool|false|是否根据宽度/高度中的最小值适配文字| 51 | |context|BuildContext|null|传入context会更灵敏的根据屏幕变化而改变| 52 | | child | Widget | null | builder的一部分,其依赖项属性不使用该库 | 53 | | rebuildFactor | Function | *default* | 返回屏幕指标更改时是否重建。 | 54 | 55 | 注意:builder和child中必须填写至少一项 56 | 57 | ### 初始化并设置适配尺寸及字体大小是否根据系统的“字体大小”辅助选项来进行缩放 58 | 在使用之前请设置好设计稿的宽度和高度,传入设计稿的宽度和高度(单位随意,但在使用过程中必须保持一致) 59 | 一定要进行初始化(只需设置一次),以保证在每次使用之前设置好了适配尺寸: 60 | 61 | #### 方式一(您必须在app中使用它一次): 62 | ```dart 63 | void main() => runApp(MyApp()); 64 | 65 | class MyApp extends StatelessWidget { 66 | @override 67 | Widget build(BuildContext context) { 68 | //填入设计稿中设备的屏幕尺寸,单位dp 69 | return ScreenUtilInit( 70 | designSize: const Size(360, 690), 71 | minTextAdapt: true, 72 | splitScreenMode: true, 73 | builder: (context , child) { 74 | return MaterialApp( 75 | debugShowCheckedModeBanner: false, 76 | title: 'First Method', 77 | // You can use the library anywhere in the app even in theme 78 | theme: ThemeData( 79 | primarySwatch: Colors.blue, 80 | textTheme: Typography.englishLike2018.apply(fontSizeFactor: 1.sp), 81 | ), 82 | home: child, 83 | ); 84 | }, 85 | child: const HomePage(title: 'First Method'), 86 | ); 87 | } 88 | } 89 | ``` 90 | 91 | #### 方式二: 你需要一个技巧来支持文字自适应主题 92 | 93 | **混合开发使用方式二** 94 | 95 | 不支持这样做: 96 | ```dart 97 | MaterialApp( 98 | ... 99 | //如果你想这样做,你应该选择方式一 100 | theme: ThemeData( 101 | textTheme: TextTheme( 102 | button: TextStyle(fontSize: 45.sp) 103 | ), 104 | ), 105 | ) 106 | ``` 107 | 108 | 正确的方法应当是这样: 109 | ```dart 110 | void main() async { 111 | // Add this line 112 | await ScreenUtil.ensureScreenSize(); 113 | runApp(MyApp()); 114 | } 115 | ... 116 | MaterialApp( 117 | ... 118 | builder: (ctx, child) { 119 | ScreenUtil.init(ctx); 120 | return Theme( 121 | data: ThemeData( 122 | primarySwatch: Colors.blue, 123 | textTheme: TextTheme(bodyText2: TextStyle(fontSize: 30.sp)), 124 | ), 125 | child: HomePage(title: 'FlutterScreenUtil Demo'), 126 | ); 127 | }, 128 | ) 129 | ``` 130 | ```dart 131 | class MyApp extends StatelessWidget { 132 | @override 133 | Widget build(BuildContext context) { 134 | return MaterialApp( 135 | debugShowCheckedModeBanner: false, 136 | title: 'Flutter_ScreenUtil', 137 | theme: ThemeData( 138 | primarySwatch: Colors.blue, 139 | ), 140 | home: HomePage(title: 'FlutterScreenUtil Demo'), 141 | ); 142 | } 143 | } 144 | 145 | class HomePage extends StatefulWidget { 146 | const HomePage({Key key, this.title}) : super(key: key); 147 | 148 | final String title; 149 | 150 | @override 151 | _HomePageState createState() => _HomePageState(); 152 | } 153 | 154 | class _HomePageState extends State { 155 | @override 156 | Widget build(BuildContext context) { 157 | //设置尺寸(填写设计中设备的屏幕尺寸)如果设计基于360dp * 690dp的屏幕 158 | ScreenUtil.init(context, designSize: const Size(360, 690)); 159 | ... 160 | } 161 | } 162 | ``` 163 | 164 | ### 使用 165 | 166 | ### API 167 | #### 传入设计稿的dp尺寸 168 | ```dart 169 | ScreenUtil().setWidth(540) (dart sdk>=2.6 : 540.w) //根据屏幕宽度适配尺寸 170 | ScreenUtil().setHeight(200) (dart sdk>=2.6 : 200.h) //根据屏幕高度适配尺寸(一般根据宽度适配即可) 171 | ScreenUtil().radius(200) (dart sdk>=2.6 : 200.r) //根据宽度或高度中的较小者进行调整 172 | ScreenUtil().setSp(24) (dart sdk>=2.6 : 24.sp) //适配字体 173 | 12.sm // 取12和12.sp中的最小值 174 | 175 | ScreenUtil.pixelRatio //设备的像素密度 176 | ScreenUtil.screenWidth (dart sdk>=2.6 : 1.sw) //设备宽度 177 | ScreenUtil.screenHeight (dart sdk>=2.6 : 1.sh) //设备高度 178 | ScreenUtil.bottomBarHeight //底部安全区距离,适用于全面屏下面有按键的 179 | ScreenUtil.statusBarHeight //状态栏高度 刘海屏会更高 180 | ScreenUtil.textScaleFactor //系统字体缩放比例 181 | 182 | ScreenUtil().scaleWidth // 实际宽度设计稿宽度的比例 183 | ScreenUtil().scaleHeight // 实际高度与设计稿高度度的比例 184 | 185 | ScreenUtil().orientation //屏幕方向 186 | 187 | 0.2.sw //屏幕宽度的0.2倍 188 | 0.5.sh //屏幕高度的50% 189 | 20.setVerticalSpacing // SizedBox(height: 20 * scaleHeight) 190 | 20.horizontalSpace // SizedBox(height: 20 * scaleWidth) 191 | const RPadding.all(8) // Padding.all(8.r) - 获取到const的优点 192 | EdgeInsets.all(10).w //EdgeInsets.all(10.w) 193 | REdgeInsets.all(8) // EdgeInsets.all(8.r) 194 | EdgeInsets.only(left:8,right:8).r // EdgeInsets.only(left:8.r,right:8.r). 195 | BoxConstraints(maxWidth: 100, minHeight: 100).w //BoxConstraints(maxWidth: 100.w, minHeight: 100.w) 196 | Radius.circular(16).w //Radius.circular(16.w) 197 | BorderRadius.all(Radius.circular(16)).w 198 | ``` 199 | 200 | 201 | #### 适配尺寸 202 | 203 | 传入设计稿的尺寸(单位与初始化时的单位相同): 204 | 205 | 根据屏幕宽度适配 `width: ScreenUtil().setWidth(540)`, 206 | 207 | 根据屏幕高度适配 `height: ScreenUtil().setHeight(200)`, 一般来说,控件高度也根据宽度进行适配 208 | 209 | 如果您的 dart sdk>=2.6,则可以使用一些特殊的函数: 210 | 211 | 例子: 212 | 213 | 你不应当这样做: 214 | ```dart 215 | Container( 216 | width: ScreenUtil().setWidth(50), 217 | height:ScreenUtil().setHeight(200), 218 | ) 219 | ``` 220 | 正确的方法应当是这样使用: 221 | ```dart 222 | Container( 223 | width: 50.w, 224 | height:200.h 225 | ) 226 | ``` 227 | 228 | `注意` 229 | 230 | 高度也根据setWidth来做适配可以保证不变形(当你想要一个正方形的时候) 231 | 232 | setHeight方法主要是在高度上进行适配, 在你想控制UI上一屏的高度与实际中显示一样时使用. 233 | 234 | 一般来说,50.w!=50.h 235 | 236 | 例如: 237 | 238 | ```dart 239 | //如果你想显示一个矩形: 240 | Container( 241 | width: 375.w, 242 | height: 375.h, 243 | ), 244 | 245 | //如果你想基于宽显示一个正方形: 246 | Container( 247 | width: 300.w, 248 | height: 300.w, 249 | ), 250 | 251 | //如果你想基于高显示一个正方形: 252 | Container( 253 | width: 300.h, 254 | height: 300.h, 255 | ), 256 | 257 | //如果你想基于高或宽中的较小值显示一个正方形: 258 | Container( 259 | width: 300.r, 260 | height: 300.r, 261 | ), 262 | ``` 263 | 264 | **适配字体** 265 | 266 | ```dart 267 | //输入字体大小(单位与初始化时的单位相同) 268 | ScreenUtil().setSp(28) 269 | 28.sp 270 | 271 | //例子: 272 | Column( 273 | crossAxisAlignment: CrossAxisAlignment.start, 274 | children: [ 275 | Text( 276 | '16sp, 因为设置了`textScaleFactor`,不会随系统变化.', 277 | style: TextStyle( 278 | color: Colors.black, 279 | fontSize: 16.sp, 280 | ), 281 | textScaleFactor: 1.0, 282 | ), 283 | Text( 284 | '16sp,如果未设置,我的字体大小将随系统而变化.', 285 | style: TextStyle( 286 | color: Colors.black, 287 | fontSize: 16.sp, 288 | ), 289 | ), 290 | ], 291 | ) 292 | ``` 293 | 294 | #### 设置字体不随系统字体大小进行改变 295 | 296 | APP全局: 297 | ```dart 298 | MaterialApp( 299 | debugShowCheckedModeBanner: false, 300 | title: 'Flutter_ScreenUtil', 301 | theme: ThemeData( 302 | primarySwatch: Colors.blue, 303 | ), 304 | builder: (context, widget) { 305 | return MediaQuery( 306 | ///设置文字大小不随系统设置改变 307 | data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), 308 | child: widget, 309 | ); 310 | }, 311 | home: HomePage(title: 'FlutterScreenUtil Demo'), 312 | ), 313 | ``` 314 | 单独的Text: 315 | ```dart 316 | Text("text", textScaleFactor: 1.0) 317 | ``` 318 | 319 | 指定的小部件: 320 | ```dart 321 | MediaQuery( 322 | // 如果这里context不可用,你可以新建一个 [Builder] 将 [MediaQuery] 放入其中 323 | data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), 324 | child: AnyWidget(), 325 | ) 326 | ``` 327 | 328 | [小部件测试](https://github.com/OpenFlutter/flutter_screenutil/issues/115) 329 | 330 | ### 使用示例: 331 | 332 | [示例演示](https://github.com/OpenFlutter/flutter_ScreenUtil/blob/master/example/lib/main_zh.dart) 333 | 334 | 要使用第二种方法,请运行:`flutter run --dart-define=method=2` 335 | 336 | 效果: 337 | 338 | ![手机效果](demo_zh.png) 339 | ![平板效果](demo_tablet_zh.png) 340 | -------------------------------------------------------------------------------- /demo_en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/demo_en.png -------------------------------------------------------------------------------- /demo_tablet_en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/demo_tablet_en.png -------------------------------------------------------------------------------- /demo_tablet_zh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/demo_tablet_zh.png -------------------------------------------------------------------------------- /demo_zh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/demo_zh.png -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | 9 | .flutter-plugins 10 | -------------------------------------------------------------------------------- /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: 3b309bda072a6b326e8aa4591a5836af600923ce 8 | channel: beta 9 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | ![手机效果](../demo_zh.png) 4 | ![phone effect](../demo_en.png) 5 | 6 | ![平板效果](../demo_tablet_zh.png) 7 | ![tablet effect](../demo_tablet_en.png) -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /example/android/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | connection.project.dir= 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /example/android/app/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | connection.project.dir=.. 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 33 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 41 | applicationId "li.zhuoyuan.example" 42 | minSdkVersion flutter.minSdkVersion 43 | targetSdkVersion 31 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | } 47 | 48 | buildTypes { 49 | release { 50 | // TODO: Add your own signing config for the release build. 51 | // Signing with the debug keys for now, so `flutter run --release` works. 52 | signingConfig signingConfigs.debug 53 | } 54 | } 55 | } 56 | 57 | flutter { 58 | source '../..' 59 | } 60 | 61 | dependencies { 62 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 63 | } 64 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 12 | 20 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/li/zhuoyuan/example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package li.zhuoyuan.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 | 11 | 12 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/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/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/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/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/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/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/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/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/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 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.6.10' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.1.2' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | tasks.register("clean", Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip 7 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Flutter Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | include ':app' 6 | 7 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 8 | def properties = new Properties() 9 | 10 | assert localPropertiesFile.exists() 11 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 12 | 13 | def flutterSdkPath = properties.getProperty("flutter.sdk") 14 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 15 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 16 | -------------------------------------------------------------------------------- /example/example.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/example_android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /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/Generated.xcconfig 21 | Flutter/ephemeral/ 22 | Flutter/app.flx 23 | Flutter/app.zip 24 | Flutter/flutter_assets/ 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /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? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Flutter.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # This podspec is NOT to be published. It is only used as a local source! 3 | # This is a generated file; do not edit or check into version control. 4 | # 5 | 6 | Pod::Spec.new do |s| 7 | s.name = 'Flutter' 8 | s.version = '1.0.0' 9 | s.summary = 'A UI toolkit for beautiful and fast apps.' 10 | s.homepage = 'https://flutter.dev' 11 | s.license = { :type => 'BSD' } 12 | s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } 13 | s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } 14 | s.ios.deployment_target = '11.0' 15 | # Framework linking is handled by Flutter tooling, not CocoaPods. 16 | # Add a placeholder to satisfy `s.dependency 'Flutter'` plugin podspecs. 17 | s.vendored_frameworks = 'path/to/nothing' 18 | end 19 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/flutter_export_environment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is a generated file; do not edit or check into version control. 3 | export "FLUTTER_ROOT=/Users/lizhuoyuan/fvm/versions/stable" 4 | export "FLUTTER_APPLICATION_PATH=/Users/lizhuoyuan/Development/Project/flutter_screenutil/example" 5 | export "COCOAPODS_PARALLEL_CODE_SIGN=true" 6 | export "FLUTTER_TARGET=lib/main.dart" 7 | export "FLUTTER_BUILD_DIR=build" 8 | export "FLUTTER_BUILD_NAME=1.0.0" 9 | export "FLUTTER_BUILD_NUMBER=1" 10 | export "DART_OBFUSCATION=false" 11 | export "TRACK_WIDGET_CREATION=true" 12 | export "TREE_SHAKE_ICONS=false" 13 | export "PACKAGE_CONFIG=.dart_tool/package_config.json" 14 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '11.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 32 | end 33 | 34 | post_install do |installer| 35 | installer.pods_project.targets.each do |target| 36 | flutter_additional_ios_build_settings(target) 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /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 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /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.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /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/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/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/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/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/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/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/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/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/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/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/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/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/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/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/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/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/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/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/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/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/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/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/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/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/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/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/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/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/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/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/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/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 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | flutter_screenutil 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | CADisableMinimumFrameDurationOnPhone 45 | 46 | UIApplicationSupportsIndirectInputEvents 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:device_preview/device_preview.dart'; 2 | import 'package:flutter/foundation.dart'; 3 | import 'package:flutter/widgets.dart'; 4 | import 'src/first_method.dart' as firstMethod; 5 | import 'src/second_method.dart' as secondMethod; 6 | 7 | void main() { 8 | const method = int.fromEnvironment('method', defaultValue: 1); 9 | runApp( 10 | DevicePreview( 11 | enabled: kDebugMode && kIsWeb, 12 | builder: (context) { 13 | return method == 1 ? firstMethod.MyApp() : secondMethod.MyApp(); 14 | }, 15 | ), 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /example/lib/main_zh.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'src_zh/first_method.dart' as firstMethod; 3 | import 'src_zh/second_method.dart' as secondMethod; 4 | 5 | void main() { 6 | const method = int.fromEnvironment('method', defaultValue: 1); 7 | 8 | runApp(method == 1 ? firstMethod.MyApp() : secondMethod.MyApp()); 9 | } 10 | -------------------------------------------------------------------------------- /example/lib/responsive_widgets.g.dart: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////// 2 | /// Generated via plugin: flutter_screenutil_generator - Do Not Touch /// 3 | ///////////////////////////////////////////////////////////////////////// 4 | 5 | part of 'responsive_widgets.su.dart'; 6 | 7 | const _responsiveWidgets = { 8 | 'MyThemedApp', 9 | 'MyApp', 10 | 'HomePageScaffold', 11 | }; 12 | -------------------------------------------------------------------------------- /example/lib/responsive_widgets.su.dart: -------------------------------------------------------------------------------- 1 | part 'responsive_widgets.g.dart'; 2 | 3 | get responsiveWidgets => _responsiveWidgets; 4 | -------------------------------------------------------------------------------- /example/lib/src/first_method.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/responsive_widgets.su.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | 5 | import '../src_zh/home.dart'; 6 | 7 | class MyApp extends StatelessWidget { 8 | const MyApp({Key? key}) : super(key: key); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | // In first method you only need to wrap [MaterialApp] with [ScreenUtilInit] and that's it 13 | return ScreenUtilInit( 14 | responsiveWidgets: responsiveWidgets, 15 | ensureScreenSize: true, 16 | child: MaterialApp( 17 | debugShowCheckedModeBanner: false, 18 | title: 'First Method', 19 | // You can use the library anywhere in the app even in theme 20 | theme: ThemeData( 21 | primarySwatch: Colors.blue, 22 | textTheme: Typography(platform: TargetPlatform.iOS) 23 | .black 24 | .apply(fontSizeFactor: 1), 25 | ), 26 | home: const HomePage(title: 'First Method'), 27 | ), 28 | ); 29 | } 30 | } 31 | 32 | class HomePage extends StatefulWidget { 33 | const HomePage({Key? key, required this.title}) : super(key: key); 34 | 35 | final String title; 36 | 37 | @override 38 | _HomePageState createState() => _HomePageState(); 39 | } 40 | 41 | class _HomePageState extends State { 42 | @override 43 | Widget build(BuildContext context) => HomePageScaffold(title: widget.title); 44 | } 45 | -------------------------------------------------------------------------------- /example/lib/src/home.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | 5 | class HomePageScaffold extends StatelessWidget with SU { 6 | const HomePageScaffold({Key? key, this.title = ''}) : super(key: key); 7 | 8 | void printScreenInformation(BuildContext context) { 9 | print('Device Size:${Size(1.sw, 1.sh)}'); 10 | print('Device pixel density:${ScreenUtil().pixelRatio}'); 11 | print('Bottom safe zone distance dp:${ScreenUtil().bottomBarHeight}dp'); 12 | print('Status bar height dp:${ScreenUtil().statusBarHeight}dp'); 13 | print('The ratio of actual width to UI design:${ScreenUtil().scaleWidth}'); 14 | print( 15 | 'The ratio of actual height to UI design:${ScreenUtil().scaleHeight}'); 16 | print('System font scaling:${ScreenUtil().textScaleFactor}'); 17 | print('0.5 times the screen width:${0.5.sw}dp'); 18 | print('0.5 times the screen height:${0.5.sh}dp'); 19 | print('Screen orientation:${ScreenUtil().orientation}'); 20 | } 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | printScreenInformation(context); 25 | 26 | /// Uncomment if you wanna force current widget to be rebuilt with updated values 27 | /// Must use it if you use the second method, or if you use ScreenUtilInit's child. 28 | /// Note: don't use it along with ScreenUtil.init() 29 | // ScreenUtil.registerToBuild(context); 30 | return Scaffold( 31 | appBar: AppBar( 32 | title: Text(title), 33 | ), 34 | body: SingleChildScrollView( 35 | child: Column( 36 | crossAxisAlignment: CrossAxisAlignment.start, 37 | children: [ 38 | RSizedBox.square(dimension: 20), 39 | Row( 40 | children: [ 41 | // Using Extensions 42 | Container( 43 | padding: EdgeInsets.all(10.w), 44 | width: 0.5.sw, 45 | height: 200.h, 46 | color: Colors.red, 47 | child: Text( 48 | 'My actual width: ${0.5.sw}dp \n\n' 49 | 'My actual height: ${200.h}dp', 50 | style: TextStyle( 51 | color: Colors.white, 52 | fontSize: 12.sp, 53 | ), 54 | ), 55 | ), 56 | // Without using Extensions 57 | Container( 58 | color: Colors.blue, 59 | constraints: BoxConstraints(maxWidth: 180, minHeight: 200).hw, 60 | padding: EdgeInsets.all(10.w), 61 | child: Text( 62 | 'My design draft width: 180dp\n\n' 63 | 'My design draft height: 200dp', 64 | style: TextStyle( 65 | color: Colors.white, 66 | fontSize: ScreenUtil().setSp(12), 67 | ), 68 | ), 69 | ), 70 | ], 71 | ), 72 | Container( 73 | padding: EdgeInsets.all(10).w, 74 | decoration: BoxDecoration( 75 | borderRadius: BorderRadius.all(Radius.circular(16).w), 76 | color: Colors.green, 77 | ), 78 | width: 100.r, 79 | height: 100.r, 80 | child: Text( 81 | 'I am a square with a side length of 100', 82 | style: TextStyle( 83 | color: Colors.white, 84 | fontSize: 12.sp, 85 | ), 86 | ), 87 | ), 88 | Padding( 89 | padding: const EdgeInsets.all(18).r, 90 | child: Column( 91 | crossAxisAlignment: CrossAxisAlignment.stretch, 92 | children: [ 93 | ElevatedButton( 94 | onPressed: () { 95 | showDialog( 96 | context: context, 97 | builder: (_) { 98 | return Dialog( 99 | child: Padding( 100 | padding: EdgeInsets.all(12.r), 101 | child: Column( 102 | children: [ 103 | const Text('Dialog'), 104 | Spacer(), 105 | TextField(), 106 | ], 107 | ), 108 | ), 109 | ); 110 | }, 111 | ); 112 | }, 113 | child: const Text('Show Dialog'), 114 | ), 115 | 18.verticalSpace, 116 | ElevatedButton( 117 | onPressed: () { 118 | Navigator.of(context).push( 119 | MaterialPageRoute(builder: (context) { 120 | return const HomePageScaffold(); 121 | }), 122 | ); 123 | }, 124 | child: const Text('Go to next page'), 125 | ), 126 | 18.verticalSpace, 127 | ElevatedButton( 128 | onPressed: () { 129 | showModalBottomSheet( 130 | context: context, 131 | builder: (BuildContext context) { 132 | return Container( 133 | height: 200.w + 134 | MediaQuery.of(context).viewInsets.bottom, 135 | color: Colors.amber, 136 | child: Center( 137 | child: Column( 138 | mainAxisAlignment: MainAxisAlignment.center, 139 | mainAxisSize: MainAxisSize.min, 140 | children: [ 141 | const Text('Modal BottomSheet'), 142 | Spacer(), 143 | TextField(), 144 | ElevatedButton( 145 | child: const Text('Close BottomSheet'), 146 | onPressed: Navigator.of(context).pop, 147 | ), 148 | SizedBox( 149 | height: MediaQuery.of(context) 150 | .viewInsets 151 | .bottom), 152 | ], 153 | ), 154 | ), 155 | ); 156 | }, 157 | ); 158 | }, 159 | child: const Text('Open BottomSheet'), 160 | ), 161 | 18.verticalSpace, 162 | TextField( 163 | decoration: InputDecoration( 164 | border: OutlineInputBorder(), 165 | ), 166 | ), 167 | 18.verticalSpace, 168 | Text('Device width:${ScreenUtil().screenWidth}dp'), 169 | Text('Device height:${ScreenUtil().screenHeight}dp'), 170 | Text('Device pixel density:${ScreenUtil().pixelRatio}'), 171 | Text( 172 | 'Bottom safe zone distance:${ScreenUtil().bottomBarHeight}dp'), 173 | Text('Status bar height:${ScreenUtil().statusBarHeight}dp'), 174 | Text( 175 | 'The ratio of actual width to UI design:${ScreenUtil().scaleWidth}'), 176 | Text( 177 | 'The ratio of actual height to UI design:${ScreenUtil().scaleHeight}'), 178 | 10.verticalSpace, 179 | Text( 180 | 'System font scaling factor:${ScreenUtil().textScaleFactor}'), 181 | 5.verticalSpace, 182 | Column( 183 | crossAxisAlignment: CrossAxisAlignment.stretch, 184 | children: [ 185 | Text( 186 | '16sp, will not change with the system.', 187 | style: TextStyle( 188 | color: Colors.black, 189 | fontSize: 16.sp, 190 | ), 191 | textScaleFactor: 1.0, 192 | ), 193 | Text( 194 | '16sp,if data is not set in MediaQuery,my font size will change with the system.', 195 | style: TextStyle( 196 | color: Colors.black, 197 | fontSize: 16.sp, 198 | ), 199 | ), 200 | ], 201 | ), 202 | ], 203 | ), 204 | ), 205 | ], 206 | ), 207 | ), 208 | floatingActionButton: FloatingActionButton.extended( 209 | onPressed: () { 210 | SystemChrome.setPreferredOrientations([ 211 | MediaQuery.of(context).orientation == Orientation.portrait 212 | ? DeviceOrientation.landscapeRight 213 | : DeviceOrientation.portraitUp, 214 | ]); 215 | // setState(() {}); 216 | }, 217 | label: const Text('Rotate'), 218 | ), 219 | ); 220 | } 221 | 222 | final String title; 223 | } 224 | -------------------------------------------------------------------------------- /example/lib/src/second_method.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/src/home.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | 5 | /// Note that you still can use [Theme] to theme your widget, but if you want 6 | /// to theme MaterialApp you must use ScreenUtil.init in builder method and 7 | /// wrap child with Theme, and remove theme and home properties from MaterialApp. 8 | /// See [MyThemedApp]. 9 | /// 10 | /// example 11 | /// ```dart 12 | /// Theme( 13 | /// data: ThemeData(...), 14 | /// child: widget, 15 | /// ) 16 | /// ``` 17 | class MyApp extends StatelessWidget { 18 | const MyApp({Key? key}) : super(key: key); 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | // In first method you only need to wrap [MaterialApp] with [ScreenUtilInit] and that's it 23 | return MaterialApp( 24 | debugShowCheckedModeBanner: false, 25 | title: 'Second Method', 26 | theme: ThemeData( 27 | primarySwatch: Colors.blue, 28 | ), 29 | home: HomePage(title: 'Second Method'), 30 | ); 31 | } 32 | } 33 | 34 | class HomePage extends StatefulWidget { 35 | const HomePage({Key? key, required this.title}) : super(key: key); 36 | 37 | final String title; 38 | 39 | @override 40 | _HomePageState createState() => _HomePageState(); 41 | } 42 | 43 | class _HomePageState extends State { 44 | @override 45 | Widget build(BuildContext context) { 46 | ScreenUtil.init(context); 47 | return HomePageScaffold(title: widget.title); 48 | } 49 | } 50 | 51 | class MyThemedApp extends StatelessWidget { 52 | const MyThemedApp({Key? key}) : super(key: key); 53 | 54 | @override 55 | Widget build(BuildContext context) { 56 | return MaterialApp( 57 | debugShowCheckedModeBanner: false, 58 | title: 'First Method (Themed)', 59 | builder: (ctx, child) { 60 | ScreenUtil.init(ctx); 61 | return Theme( 62 | data: ThemeData( 63 | primarySwatch: Colors.blue, 64 | textTheme: TextTheme(bodyMedium: TextStyle(fontSize: 30.sp)), 65 | ), 66 | child: HomePage(title: 'FlutterScreenUtil Demo'), 67 | ); 68 | }, 69 | ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /example/lib/src_zh/first_method.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/src_zh/home.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | 5 | class MyApp extends StatelessWidget { 6 | const MyApp({Key? key}) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | // In first method you only need to wrap [MaterialApp] with [ScreenUtilInit] and that's it 11 | return ScreenUtilInit( 12 | builder: (_, child) { 13 | return MaterialApp( 14 | debugShowCheckedModeBanner: false, 15 | title: '第一种方法', 16 | // You can use the library anywhere in the app even in theme 17 | theme: ThemeData( 18 | primarySwatch: Colors.blue, 19 | textTheme: TextTheme(bodyMedium: TextStyle(fontSize: 30.sp)), 20 | ), 21 | home: child, 22 | ); 23 | }, 24 | child: HomePage(title: '第一种方法'), 25 | ); 26 | } 27 | } 28 | 29 | class HomePage extends StatefulWidget { 30 | const HomePage({Key? key, required this.title}) : super(key: key); 31 | 32 | final String title; 33 | 34 | @override 35 | _HomePageState createState() => _HomePageState(); 36 | } 37 | 38 | class _HomePageState extends State { 39 | @override 40 | Widget build(BuildContext context) => HomePageScaffold(title: widget.title); 41 | } 42 | -------------------------------------------------------------------------------- /example/lib/src_zh/home.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | 5 | class HomePageScaffold extends StatelessWidget { 6 | const HomePageScaffold({Key? key, required this.title}) : super(key: key); 7 | 8 | void printScreenInformation() { 9 | print('设备宽度:${1.sw}dp'); 10 | print('设备高度:${1.sh}dp'); 11 | print('设备的像素密度:${ScreenUtil().pixelRatio}'); 12 | print('底部安全区距离:${ScreenUtil().bottomBarHeight}dp'); 13 | print('状态栏高度:${ScreenUtil().statusBarHeight}dp'); 14 | print('实际宽度和字体(dp)与设计稿(dp)的比例:${ScreenUtil().scaleWidth}'); 15 | print('实际高度(dp)与设计稿(dp)的比例:${ScreenUtil().scaleHeight}'); 16 | print('高度相对于设计稿放大的比例:${ScreenUtil().scaleHeight}'); 17 | print('系统的字体缩放比例:${ScreenUtil().textScaleFactor}'); 18 | print('屏幕宽度的0.5:${0.5.sw}dp'); 19 | print('屏幕高度的0.5:${0.5.sh}dp'); 20 | print('屏幕方向:${ScreenUtil().orientation}'); 21 | } 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | printScreenInformation(); 26 | 27 | return Scaffold( 28 | appBar: AppBar( 29 | title: Text(title), 30 | ), 31 | body: SingleChildScrollView( 32 | child: Column( 33 | crossAxisAlignment: CrossAxisAlignment.center, 34 | children: [ 35 | Row( 36 | children: [ 37 | Container( 38 | padding: EdgeInsets.all(ScreenUtil().setWidth(10)), 39 | width: 180.w, 40 | height: 200.h, 41 | color: Colors.red, 42 | child: Text( 43 | '我的实际宽度:${180.w}dp \n' 44 | '我的实际高度:${200.h}dp', 45 | style: TextStyle(color: Colors.white, fontSize: 12.sp), 46 | ), 47 | ), 48 | Container( 49 | padding: EdgeInsets.all(ScreenUtil().setWidth(10)), 50 | width: ScreenUtil().setWidth(180), 51 | height: ScreenUtil().setHeight(200), 52 | color: Colors.blue, 53 | child: Text( 54 | '我的设计稿宽度: 180dp \n' 55 | '我的设计稿高度: 200dp', 56 | style: TextStyle( 57 | color: Colors.white, 58 | fontSize: ScreenUtil().setSp(12))), 59 | ), 60 | ], 61 | ), 62 | Container( 63 | decoration: BoxDecoration( 64 | borderRadius: BorderRadius.all(Radius.circular(16)).w, 65 | color: Colors.green, 66 | ), 67 | constraints: BoxConstraints(maxWidth: 100, minHeight: 100).r, 68 | padding: EdgeInsets.all(10).w, 69 | child: Text( 70 | '我是正方形,边长是100', 71 | style: TextStyle( 72 | color: Colors.white, 73 | fontSize: ScreenUtil().setSp(12), 74 | ), 75 | ), 76 | ), 77 | Text('设备宽度:${ScreenUtil().screenWidth}dp'), 78 | Text('设备高度:${ScreenUtil().screenHeight}dp'), 79 | Text('设备的像素密度:${ScreenUtil().pixelRatio}'), 80 | Text('底部安全区距离:${ScreenUtil().bottomBarHeight}dp'), 81 | Text('状态栏高度:${ScreenUtil().statusBarHeight}dp'), 82 | Text( 83 | '实际宽度与设计稿的比例:${ScreenUtil().scaleWidth}', 84 | textAlign: TextAlign.center, 85 | ), 86 | Text( 87 | '实际高度与设计稿的比例:${ScreenUtil().scaleHeight}', 88 | textAlign: TextAlign.center, 89 | ), 90 | SizedBox( 91 | height: 50.h, 92 | ), 93 | Text('系统的字体缩放比例:${ScreenUtil().textScaleFactor}'), 94 | Column( 95 | crossAxisAlignment: CrossAxisAlignment.start, 96 | children: [ 97 | Text( 98 | '我的文字大小在设计稿上是16dp,因为设置了`textScaleFactor`,所以不会随着系统的文字缩放比例变化', 99 | style: TextStyle( 100 | color: Colors.black, 101 | fontSize: 16.sp, 102 | ), 103 | textScaleFactor: 1.0, 104 | ), 105 | Text( 106 | '我的文字大小在设计稿上是16dp,会随着系统的文字缩放比例变化', 107 | style: TextStyle( 108 | color: Colors.black, 109 | fontSize: 16.sp, 110 | ), 111 | ), 112 | ], 113 | ) 114 | ], 115 | ), 116 | ), 117 | floatingActionButton: FloatingActionButton.extended( 118 | onPressed: () { 119 | SystemChrome.setPreferredOrientations([ 120 | MediaQuery.of(context).orientation == Orientation.portrait 121 | ? DeviceOrientation.landscapeRight 122 | : DeviceOrientation.portraitUp, 123 | ]); 124 | // setState(() {}); 125 | }, 126 | label: const Text('旋转'), 127 | ), 128 | ); 129 | } 130 | 131 | final String title; 132 | } 133 | -------------------------------------------------------------------------------- /example/lib/src_zh/second_method.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/src_zh/home.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | 5 | /// 请注意,您仍然可以使用 [Theme] 为您的小部件设置主题,但如果您想为 MaterialApp 6 | /// 设置主题,您必须在 builder 方法中使用 ScreenUtil.init 并使用 Theme 包装子项 7 | /// 并从 MaterialApp 中删除主题和主页属性。 请参阅 [MyThemedApp]。 8 | /// 9 | /// 例子 10 | /// ```dart 11 | /// Theme( 12 | /// data: ThemeData(...), 13 | /// child: widget, 14 | /// ) 15 | /// ``` 16 | class MyApp extends StatelessWidget { 17 | const MyApp({Key? key}) : super(key: key); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | // In first method you only need to wrap [MaterialApp] with [ScreenUtilInit] and that's it 22 | return MaterialApp( 23 | debugShowCheckedModeBanner: false, 24 | title: '第二种方法', 25 | theme: ThemeData( 26 | primarySwatch: Colors.blue, 27 | ), 28 | home: HomePage(title: '第二种方法'), 29 | ); 30 | } 31 | } 32 | 33 | class HomePage extends StatefulWidget { 34 | const HomePage({Key? key, required this.title}) : super(key: key); 35 | 36 | final String title; 37 | 38 | @override 39 | _HomePageState createState() => _HomePageState(); 40 | } 41 | 42 | class _HomePageState extends State { 43 | @override 44 | Widget build(BuildContext context) { 45 | ScreenUtil.init(context); 46 | return HomePageScaffold(title: widget.title); 47 | } 48 | } 49 | 50 | class MyThemedApp extends StatelessWidget { 51 | const MyThemedApp({Key? key}) : super(key: key); 52 | 53 | @override 54 | Widget build(BuildContext context) { 55 | return MaterialApp( 56 | debugShowCheckedModeBanner: false, 57 | title: '第二种方法(带主题)', 58 | builder: (ctx, child) { 59 | ScreenUtil.init(ctx); 60 | return Theme( 61 | data: ThemeData( 62 | primarySwatch: Colors.blue, 63 | textTheme: TextTheme(bodyMedium: TextStyle(fontSize: 30.sp)), 64 | ), 65 | child: HomePage(title: '第二种方法(带主题)'), 66 | ); 67 | }, 68 | ); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /example/linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /example/linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(runner LANGUAGES CXX) 3 | 4 | set(BINARY_NAME "example") 5 | set(APPLICATION_ID "li.zhuoyuan.example") 6 | 7 | cmake_policy(SET CMP0063 NEW) 8 | 9 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 10 | 11 | # Root filesystem for cross-building. 12 | if(FLUTTER_TARGET_PLATFORM_SYSROOT) 13 | set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) 14 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) 15 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 16 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 17 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 18 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 19 | endif() 20 | 21 | # Configure build options. 22 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 23 | set(CMAKE_BUILD_TYPE "Debug" CACHE 24 | STRING "Flutter build mode" FORCE) 25 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 26 | "Debug" "Profile" "Release") 27 | endif() 28 | 29 | # Compilation settings that should be applied to most targets. 30 | function(APPLY_STANDARD_SETTINGS TARGET) 31 | target_compile_features(${TARGET} PUBLIC cxx_std_14) 32 | target_compile_options(${TARGET} PRIVATE -Wall -Werror) 33 | target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") 34 | target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") 35 | endfunction() 36 | 37 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 38 | 39 | # Flutter library and tool build rules. 40 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 41 | 42 | # System-level dependencies. 43 | find_package(PkgConfig REQUIRED) 44 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 45 | 46 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") 47 | 48 | # Application build 49 | add_executable(${BINARY_NAME} 50 | "main.cc" 51 | "my_application.cc" 52 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 53 | ) 54 | apply_standard_settings(${BINARY_NAME}) 55 | target_link_libraries(${BINARY_NAME} PRIVATE flutter) 56 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) 57 | add_dependencies(${BINARY_NAME} flutter_assemble) 58 | # Only the install-generated bundle's copy of the executable will launch 59 | # correctly, since the resources must in the right relative locations. To avoid 60 | # people trying to run the unbundled copy, put it in a subdirectory instead of 61 | # the default top-level location. 62 | set_target_properties(${BINARY_NAME} 63 | PROPERTIES 64 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" 65 | ) 66 | 67 | # Generated plugin build rules, which manage building the plugins and adding 68 | # them to the application. 69 | include(flutter/generated_plugins.cmake) 70 | 71 | 72 | # === Installation === 73 | # By default, "installing" just makes a relocatable bundle in the build 74 | # directory. 75 | set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") 76 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 77 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 78 | endif() 79 | 80 | # Start with a clean build bundle directory every time. 81 | install(CODE " 82 | file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") 83 | " COMPONENT Runtime) 84 | 85 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 86 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") 87 | 88 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 89 | COMPONENT Runtime) 90 | 91 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 92 | COMPONENT Runtime) 93 | 94 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 95 | COMPONENT Runtime) 96 | 97 | if(PLUGIN_BUNDLED_LIBRARIES) 98 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 99 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 100 | COMPONENT Runtime) 101 | endif() 102 | 103 | # Fully re-copy the assets directory on each build to avoid having stale files 104 | # from a previous install. 105 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 106 | install(CODE " 107 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 108 | " COMPONENT Runtime) 109 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 110 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 111 | 112 | # Install the AOT library on non-Debug builds only. 113 | if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") 114 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 115 | COMPONENT Runtime) 116 | endif() 117 | -------------------------------------------------------------------------------- /example/linux/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 4 | 5 | # Configuration provided via flutter tool. 6 | include(${EPHEMERAL_DIR}/generated_config.cmake) 7 | 8 | # TODO: Move the rest of this into files in ephemeral. See 9 | # https://github.com/flutter/flutter/issues/57146. 10 | 11 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...), 12 | # which isn't available in 3.10. 13 | function(list_prepend LIST_NAME PREFIX) 14 | set(NEW_LIST "") 15 | foreach(element ${${LIST_NAME}}) 16 | list(APPEND NEW_LIST "${PREFIX}${element}") 17 | endforeach(element) 18 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) 19 | endfunction() 20 | 21 | # === Flutter Library === 22 | # System-level dependencies. 23 | find_package(PkgConfig REQUIRED) 24 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 25 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) 26 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) 27 | 28 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") 29 | 30 | # Published to parent scope for install step. 31 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 32 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 33 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 34 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) 35 | 36 | list(APPEND FLUTTER_LIBRARY_HEADERS 37 | "fl_basic_message_channel.h" 38 | "fl_binary_codec.h" 39 | "fl_binary_messenger.h" 40 | "fl_dart_project.h" 41 | "fl_engine.h" 42 | "fl_json_message_codec.h" 43 | "fl_json_method_codec.h" 44 | "fl_message_codec.h" 45 | "fl_method_call.h" 46 | "fl_method_channel.h" 47 | "fl_method_codec.h" 48 | "fl_method_response.h" 49 | "fl_plugin_registrar.h" 50 | "fl_plugin_registry.h" 51 | "fl_standard_message_codec.h" 52 | "fl_standard_method_codec.h" 53 | "fl_string_codec.h" 54 | "fl_value.h" 55 | "fl_view.h" 56 | "flutter_linux.h" 57 | ) 58 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") 59 | add_library(flutter INTERFACE) 60 | target_include_directories(flutter INTERFACE 61 | "${EPHEMERAL_DIR}" 62 | ) 63 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") 64 | target_link_libraries(flutter INTERFACE 65 | PkgConfig::GTK 66 | PkgConfig::GLIB 67 | PkgConfig::GIO 68 | ) 69 | add_dependencies(flutter flutter_assemble) 70 | 71 | # === Flutter tool backend === 72 | # _phony_ is a non-existent file to force this command to run every time, 73 | # since currently there's no way to get a full input/output list from the 74 | # flutter tool. 75 | add_custom_command( 76 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 77 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_ 78 | COMMAND ${CMAKE_COMMAND} -E env 79 | ${FLUTTER_TOOL_ENVIRONMENT} 80 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" 81 | ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} 82 | VERBATIM 83 | ) 84 | add_custom_target(flutter_assemble DEPENDS 85 | "${FLUTTER_LIBRARY}" 86 | ${FLUTTER_LIBRARY_HEADERS} 87 | ) 88 | -------------------------------------------------------------------------------- /example/linux/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 fl_register_plugins(FlPluginRegistry* registry) { 11 | } 12 | -------------------------------------------------------------------------------- /example/linux/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 fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /example/linux/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}/linux 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}/linux plugins/${ffi_plugin}) 22 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 23 | endforeach(ffi_plugin) 24 | -------------------------------------------------------------------------------- /example/linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /example/linux/my_application.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | #include 4 | #ifdef GDK_WINDOWING_X11 5 | #include 6 | #endif 7 | 8 | #include "flutter/generated_plugin_registrant.h" 9 | 10 | struct _MyApplication { 11 | GtkApplication parent_instance; 12 | char** dart_entrypoint_arguments; 13 | }; 14 | 15 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) 16 | 17 | // Implements GApplication::activate. 18 | static void my_application_activate(GApplication* application) { 19 | MyApplication* self = MY_APPLICATION(application); 20 | GtkWindow* window = 21 | GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); 22 | 23 | // Use a header bar when running in GNOME as this is the common style used 24 | // by applications and is the setup most users will be using (e.g. Ubuntu 25 | // desktop). 26 | // If running on X and not using GNOME then just use a traditional title bar 27 | // in case the window manager does more exotic layout, e.g. tiling. 28 | // If running on Wayland assume the header bar will work (may need changing 29 | // if future cases occur). 30 | gboolean use_header_bar = TRUE; 31 | #ifdef GDK_WINDOWING_X11 32 | GdkScreen* screen = gtk_window_get_screen(window); 33 | if (GDK_IS_X11_SCREEN(screen)) { 34 | const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); 35 | if (g_strcmp0(wm_name, "GNOME Shell") != 0) { 36 | use_header_bar = FALSE; 37 | } 38 | } 39 | #endif 40 | if (use_header_bar) { 41 | GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); 42 | gtk_widget_show(GTK_WIDGET(header_bar)); 43 | gtk_header_bar_set_title(header_bar, "example"); 44 | gtk_header_bar_set_show_close_button(header_bar, TRUE); 45 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); 46 | } else { 47 | gtk_window_set_title(window, "example"); 48 | } 49 | 50 | gtk_window_set_default_size(window, 1280, 720); 51 | gtk_widget_show(GTK_WIDGET(window)); 52 | 53 | g_autoptr(FlDartProject) project = fl_dart_project_new(); 54 | fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); 55 | 56 | FlView* view = fl_view_new(project); 57 | gtk_widget_show(GTK_WIDGET(view)); 58 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); 59 | 60 | fl_register_plugins(FL_PLUGIN_REGISTRY(view)); 61 | 62 | gtk_widget_grab_focus(GTK_WIDGET(view)); 63 | } 64 | 65 | // Implements GApplication::local_command_line. 66 | static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { 67 | MyApplication* self = MY_APPLICATION(application); 68 | // Strip out the first argument as it is the binary name. 69 | self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); 70 | 71 | g_autoptr(GError) error = nullptr; 72 | if (!g_application_register(application, nullptr, &error)) { 73 | g_warning("Failed to register: %s", error->message); 74 | *exit_status = 1; 75 | return TRUE; 76 | } 77 | 78 | g_application_activate(application); 79 | *exit_status = 0; 80 | 81 | return TRUE; 82 | } 83 | 84 | // Implements GObject::dispose. 85 | static void my_application_dispose(GObject* object) { 86 | MyApplication* self = MY_APPLICATION(object); 87 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); 88 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object); 89 | } 90 | 91 | static void my_application_class_init(MyApplicationClass* klass) { 92 | G_APPLICATION_CLASS(klass)->activate = my_application_activate; 93 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; 94 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose; 95 | } 96 | 97 | static void my_application_init(MyApplication* self) {} 98 | 99 | MyApplication* my_application_new() { 100 | return MY_APPLICATION(g_object_new(my_application_get_type(), 101 | "application-id", APPLICATION_ID, 102 | "flags", G_APPLICATION_NON_UNIQUE, 103 | nullptr)); 104 | } 105 | -------------------------------------------------------------------------------- /example/linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: flutter_screenutil的使用示例 3 | publish_to: none 4 | 5 | # The following defines the version and build number for your application. 6 | # A version number is three numbers separated by dots, like 1.2.43 7 | # followed by an optional build number separated by a +. 8 | # Both the version and the builder number may be overridden in flutter 9 | # build by specifying --build-name and --build-number, respectively. 10 | # Read more about versioning at semver.org. 11 | version: 1.0.0+1 12 | 13 | environment: 14 | sdk: ">=2.12.0 <3.0.0" 15 | 16 | dependencies: 17 | flutter: 18 | sdk: flutter 19 | 20 | flutter_screenutil: 21 | path: ../ 22 | device_preview: ^1.1.0 23 | 24 | dev_dependencies: 25 | flutter_test: 26 | sdk: flutter 27 | test: ^1.15.7 28 | 29 | flutter: 30 | uses-material-design: true 31 | -------------------------------------------------------------------------------- /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 that Flutter provides. 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_screenutil/flutter_screenutil.dart'; 10 | import 'package:flutter_test/flutter_test.dart'; 11 | 12 | void main() { 13 | testWidgets('Test Responsiveness', (WidgetTester tester) async { 14 | // Declare Sizes 15 | const Size designSize = Size(360, 640); 16 | const Size initialSize = designSize; 17 | const Size biggerSize = Size(480, 920); 18 | const Size smallerSize = Size(300, 560); 19 | 20 | // We'll use MediaQuery to simulate diffrent screen sizes 21 | MediaQueryData currentData = MediaQueryData(size: initialSize); 22 | const MediaQueryData biggerData = MediaQueryData(size: biggerSize); 23 | const MediaQueryData smallerData = MediaQueryData(size: smallerSize); 24 | 25 | // Used to find a widget. See [CommonFinders.byKey]. 26 | final _key = UniqueKey(); 27 | 28 | // Click on button. See code bellow. 29 | Future tap() async { 30 | await tester.tap(find.byKey(_key)); 31 | return tester.pumpAndSettle(); 32 | } 33 | 34 | void testSize(Size size) { 35 | expect(1.w, equals(size.width / designSize.width)); 36 | expect(1.h, equals(size.height / designSize.height)); 37 | print('[OK] Size: $size, width: ${1.w}, height: ${1.h}'); 38 | } 39 | 40 | await tester.pumpWidget(StatefulBuilder( 41 | builder: (BuildContext context, StateSetter setState) { 42 | return MediaQuery( 43 | data: currentData, 44 | child: ScreenUtilInit( 45 | designSize: designSize, 46 | child: MaterialApp( 47 | home: Material( 48 | child: TextButton( 49 | key: _key, 50 | child: Text('Change data'), 51 | onPressed: () { 52 | setState(() { 53 | currentData = currentData.size == initialSize 54 | // First test with bigger screen 55 | ? biggerData 56 | // Test with smaller screen 57 | : smallerData; 58 | }); 59 | }, 60 | ), 61 | ), 62 | ), 63 | ), 64 | ); 65 | }, 66 | )); 67 | 68 | // Tests with initial screen size 69 | testSize(initialSize); 70 | 71 | // Wait for FutureBuilder to be resolved 72 | await tester.pumpAndSettle(); 73 | 74 | // Click On button to simulate changing screen size 75 | await tap(); 76 | // Tests with bigger screen size 77 | testSize(biggerSize); 78 | 79 | // Click On button to simulate changing screen size 80 | await tap(); 81 | // Tests with bigger screen size 82 | testSize(smallerSize); 83 | 84 | await tester.pumpAndSettle(); 85 | }); 86 | } 87 | -------------------------------------------------------------------------------- /example/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/example/web/favicon.png -------------------------------------------------------------------------------- /example/web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/example/web/icons/Icon-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/example/web/icons/Icon-512.png -------------------------------------------------------------------------------- /example/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | example 30 | 31 | 32 | 33 | 36 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /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 | } 24 | -------------------------------------------------------------------------------- /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 | cmake_minimum_required(VERSION 3.15) 2 | project(example LANGUAGES CXX) 3 | 4 | set(BINARY_NAME "example") 5 | 6 | cmake_policy(SET CMP0063 NEW) 7 | 8 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 9 | 10 | # Configure build options. 11 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 12 | if(IS_MULTICONFIG) 13 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" 14 | CACHE STRING "" FORCE) 15 | else() 16 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 17 | set(CMAKE_BUILD_TYPE "Debug" CACHE 18 | STRING "Flutter build mode" FORCE) 19 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 20 | "Debug" "Profile" "Release") 21 | endif() 22 | endif() 23 | 24 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") 25 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") 26 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") 27 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") 28 | 29 | # Use Unicode for all projects. 30 | add_definitions(-DUNICODE -D_UNICODE) 31 | 32 | # Compilation settings that should be applied to most targets. 33 | function(APPLY_STANDARD_SETTINGS TARGET) 34 | target_compile_features(${TARGET} PUBLIC cxx_std_17) 35 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") 36 | target_compile_options(${TARGET} PRIVATE /EHsc) 37 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") 38 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") 39 | endfunction() 40 | 41 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 42 | 43 | # Flutter library and tool build rules. 44 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 45 | 46 | # Application build 47 | add_subdirectory("runner") 48 | 49 | # Generated plugin build rules, which manage building the plugins and adding 50 | # them to the application. 51 | include(flutter/generated_plugins.cmake) 52 | 53 | 54 | # === Installation === 55 | # Support files are copied into place next to the executable, so that it can 56 | # run in place. This is done instead of making a separate bundle (as on Linux) 57 | # so that building and running from within Visual Studio will work. 58 | set(BUILD_BUNDLE_DIR "$") 59 | # Make the "install" step default, as it's required to run. 60 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 61 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 62 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 63 | endif() 64 | 65 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 66 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") 67 | 68 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 69 | COMPONENT Runtime) 70 | 71 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 72 | COMPONENT Runtime) 73 | 74 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 75 | COMPONENT Runtime) 76 | 77 | if(PLUGIN_BUNDLED_LIBRARIES) 78 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 79 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 80 | COMPONENT Runtime) 81 | endif() 82 | 83 | # Fully re-copy the assets directory on each build to avoid having stale files 84 | # from a previous install. 85 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 86 | install(CODE " 87 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 88 | " COMPONENT Runtime) 89 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 90 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 91 | 92 | # Install the AOT library on non-Debug builds only. 93 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 94 | CONFIGURATIONS Profile;Release 95 | COMPONENT Runtime) 96 | -------------------------------------------------------------------------------- /example/windows/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 4 | 5 | # Configuration provided via flutter tool. 6 | include(${EPHEMERAL_DIR}/generated_config.cmake) 7 | 8 | # TODO: Move the rest of this into files in ephemeral. See 9 | # https://github.com/flutter/flutter/issues/57146. 10 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") 11 | 12 | # === Flutter Library === 13 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") 14 | 15 | # Published to parent scope for install step. 16 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 17 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 18 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 19 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) 20 | 21 | list(APPEND FLUTTER_LIBRARY_HEADERS 22 | "flutter_export.h" 23 | "flutter_windows.h" 24 | "flutter_messenger.h" 25 | "flutter_plugin_registrar.h" 26 | "flutter_texture_registrar.h" 27 | ) 28 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") 29 | add_library(flutter INTERFACE) 30 | target_include_directories(flutter INTERFACE 31 | "${EPHEMERAL_DIR}" 32 | ) 33 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") 34 | add_dependencies(flutter flutter_assemble) 35 | 36 | # === Wrapper === 37 | list(APPEND CPP_WRAPPER_SOURCES_CORE 38 | "core_implementations.cc" 39 | "standard_codec.cc" 40 | ) 41 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") 42 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN 43 | "plugin_registrar.cc" 44 | ) 45 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") 46 | list(APPEND CPP_WRAPPER_SOURCES_APP 47 | "flutter_engine.cc" 48 | "flutter_view_controller.cc" 49 | ) 50 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") 51 | 52 | # Wrapper sources needed for a plugin. 53 | add_library(flutter_wrapper_plugin STATIC 54 | ${CPP_WRAPPER_SOURCES_CORE} 55 | ${CPP_WRAPPER_SOURCES_PLUGIN} 56 | ) 57 | apply_standard_settings(flutter_wrapper_plugin) 58 | set_target_properties(flutter_wrapper_plugin PROPERTIES 59 | POSITION_INDEPENDENT_CODE ON) 60 | set_target_properties(flutter_wrapper_plugin PROPERTIES 61 | CXX_VISIBILITY_PRESET hidden) 62 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) 63 | target_include_directories(flutter_wrapper_plugin PUBLIC 64 | "${WRAPPER_ROOT}/include" 65 | ) 66 | add_dependencies(flutter_wrapper_plugin flutter_assemble) 67 | 68 | # Wrapper sources needed for the runner. 69 | add_library(flutter_wrapper_app STATIC 70 | ${CPP_WRAPPER_SOURCES_CORE} 71 | ${CPP_WRAPPER_SOURCES_APP} 72 | ) 73 | apply_standard_settings(flutter_wrapper_app) 74 | target_link_libraries(flutter_wrapper_app PUBLIC flutter) 75 | target_include_directories(flutter_wrapper_app PUBLIC 76 | "${WRAPPER_ROOT}/include" 77 | ) 78 | add_dependencies(flutter_wrapper_app flutter_assemble) 79 | 80 | # === Flutter tool backend === 81 | # _phony_ is a non-existent file to force this command to run every time, 82 | # since currently there's no way to get a full input/output list from the 83 | # flutter tool. 84 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") 85 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) 86 | add_custom_command( 87 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 88 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} 89 | ${CPP_WRAPPER_SOURCES_APP} 90 | ${PHONY_OUTPUT} 91 | COMMAND ${CMAKE_COMMAND} -E env 92 | ${FLUTTER_TOOL_ENVIRONMENT} 93 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" 94 | windows-x64 $ 95 | VERBATIM 96 | ) 97 | add_custom_target(flutter_assemble DEPENDS 98 | "${FLUTTER_LIBRARY}" 99 | ${FLUTTER_LIBRARY_HEADERS} 100 | ${CPP_WRAPPER_SOURCES_CORE} 101 | ${CPP_WRAPPER_SOURCES_PLUGIN} 102 | ${CPP_WRAPPER_SOURCES_APP} 103 | ) 104 | -------------------------------------------------------------------------------- /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.15) 2 | project(runner LANGUAGES CXX) 3 | 4 | add_executable(${BINARY_NAME} WIN32 5 | "flutter_window.cpp" 6 | "main.cpp" 7 | "run_loop.cpp" 8 | "utils.cpp" 9 | "win32_window.cpp" 10 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 11 | "Runner.rc" 12 | "runner.exe.manifest" 13 | ) 14 | apply_standard_settings(${BINARY_NAME}) 15 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 16 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 17 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 18 | add_dependencies(${BINARY_NAME} flutter_assemble) 19 | -------------------------------------------------------------------------------- /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", "li.zhuoyuan" "\0" 93 | VALUE "FileDescription", "A new Flutter project." "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "example" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2021 li.zhuoyuan. 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(RunLoop* run_loop, 8 | const flutter::DartProject& project) 9 | : run_loop_(run_loop), project_(project) {} 10 | 11 | FlutterWindow::~FlutterWindow() {} 12 | 13 | bool FlutterWindow::OnCreate() { 14 | if (!Win32Window::OnCreate()) { 15 | return false; 16 | } 17 | 18 | RECT frame = GetClientArea(); 19 | 20 | // The size here must match the window dimensions to avoid unnecessary surface 21 | // creation / destruction in the startup path. 22 | flutter_controller_ = std::make_unique( 23 | frame.right - frame.left, frame.bottom - frame.top, project_); 24 | // Ensure that basic setup of the controller was successful. 25 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 26 | return false; 27 | } 28 | RegisterPlugins(flutter_controller_->engine()); 29 | run_loop_->RegisterFlutterInstance(flutter_controller_->engine()); 30 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 31 | return true; 32 | } 33 | 34 | void FlutterWindow::OnDestroy() { 35 | if (flutter_controller_) { 36 | run_loop_->UnregisterFlutterInstance(flutter_controller_->engine()); 37 | flutter_controller_ = nullptr; 38 | } 39 | 40 | Win32Window::OnDestroy(); 41 | } 42 | 43 | LRESULT 44 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 45 | WPARAM const wparam, 46 | LPARAM const lparam) noexcept { 47 | // Give Flutter, including plugins, an opporutunity to handle window messages. 48 | if (flutter_controller_) { 49 | std::optional result = 50 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 51 | lparam); 52 | if (result) { 53 | return *result; 54 | } 55 | } 56 | 57 | switch (message) { 58 | case WM_FONTCHANGE: 59 | flutter_controller_->engine()->ReloadSystemFonts(); 60 | break; 61 | } 62 | 63 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 64 | } 65 | -------------------------------------------------------------------------------- /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 "run_loop.h" 10 | #include "win32_window.h" 11 | 12 | // A window that does nothing but host a Flutter view. 13 | class FlutterWindow : public Win32Window { 14 | public: 15 | // Creates a new FlutterWindow driven by the |run_loop|, hosting a 16 | // Flutter view running |project|. 17 | explicit FlutterWindow(RunLoop* run_loop, 18 | const flutter::DartProject& project); 19 | virtual ~FlutterWindow(); 20 | 21 | protected: 22 | // Win32Window: 23 | bool OnCreate() override; 24 | void OnDestroy() override; 25 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 26 | LPARAM const lparam) noexcept override; 27 | 28 | private: 29 | // The run loop driving events for this window. 30 | RunLoop* run_loop_; 31 | 32 | // The project to run. 33 | flutter::DartProject project_; 34 | 35 | // The Flutter instance hosted by this window. 36 | std::unique_ptr flutter_controller_; 37 | }; 38 | 39 | #endif // RUNNER_FLUTTER_WINDOW_H_ 40 | -------------------------------------------------------------------------------- /example/windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "run_loop.h" 7 | #include "utils.h" 8 | 9 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 10 | _In_ wchar_t *command_line, _In_ int show_command) { 11 | // Attach to console when present (e.g., 'flutter run') or create a 12 | // new console when running with a debugger. 13 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 14 | CreateAndAttachConsole(); 15 | } 16 | 17 | // Initialize COM, so that it is available for use in the library and/or 18 | // plugins. 19 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 20 | 21 | RunLoop run_loop; 22 | 23 | flutter::DartProject project(L"data"); 24 | 25 | std::vector command_line_arguments = 26 | GetCommandLineArguments(); 27 | 28 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 29 | 30 | FlutterWindow window(&run_loop, project); 31 | Win32Window::Point origin(10, 10); 32 | Win32Window::Size size(1280, 720); 33 | if (!window.CreateAndShow(L"example", origin, size)) { 34 | return EXIT_FAILURE; 35 | } 36 | window.SetQuitOnClose(true); 37 | 38 | run_loop.Run(); 39 | 40 | ::CoUninitialize(); 41 | return EXIT_SUCCESS; 42 | } 43 | -------------------------------------------------------------------------------- /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/OpenFlutter/flutter_screenutil/6982b1242ffef72c6bce5dad1146f2f000d8eddf/example/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /example/windows/runner/run_loop.cpp: -------------------------------------------------------------------------------- 1 | #include "run_loop.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | RunLoop::RunLoop() {} 8 | 9 | RunLoop::~RunLoop() {} 10 | 11 | void RunLoop::Run() { 12 | bool keep_running = true; 13 | TimePoint next_flutter_event_time = TimePoint::clock::now(); 14 | while (keep_running) { 15 | std::chrono::nanoseconds wait_duration = 16 | std::max(std::chrono::nanoseconds(0), 17 | next_flutter_event_time - TimePoint::clock::now()); 18 | ::MsgWaitForMultipleObjects( 19 | 0, nullptr, FALSE, static_cast(wait_duration.count() / 1000), 20 | QS_ALLINPUT); 21 | bool processed_events = false; 22 | MSG message; 23 | // All pending Windows messages must be processed; MsgWaitForMultipleObjects 24 | // won't return again for items left in the queue after PeekMessage. 25 | while (::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) { 26 | processed_events = true; 27 | if (message.message == WM_QUIT) { 28 | keep_running = false; 29 | break; 30 | } 31 | ::TranslateMessage(&message); 32 | ::DispatchMessage(&message); 33 | // Allow Flutter to process messages each time a Windows message is 34 | // processed, to prevent starvation. 35 | next_flutter_event_time = 36 | std::min(next_flutter_event_time, ProcessFlutterMessages()); 37 | } 38 | // If the PeekMessage loop didn't run, process Flutter messages. 39 | if (!processed_events) { 40 | next_flutter_event_time = 41 | std::min(next_flutter_event_time, ProcessFlutterMessages()); 42 | } 43 | } 44 | } 45 | 46 | void RunLoop::RegisterFlutterInstance( 47 | flutter::FlutterEngine* flutter_instance) { 48 | flutter_instances_.insert(flutter_instance); 49 | } 50 | 51 | void RunLoop::UnregisterFlutterInstance( 52 | flutter::FlutterEngine* flutter_instance) { 53 | flutter_instances_.erase(flutter_instance); 54 | } 55 | 56 | RunLoop::TimePoint RunLoop::ProcessFlutterMessages() { 57 | TimePoint next_event_time = TimePoint::max(); 58 | for (auto instance : flutter_instances_) { 59 | std::chrono::nanoseconds wait_duration = instance->ProcessMessages(); 60 | if (wait_duration != std::chrono::nanoseconds::max()) { 61 | next_event_time = 62 | std::min(next_event_time, TimePoint::clock::now() + wait_duration); 63 | } 64 | } 65 | return next_event_time; 66 | } 67 | -------------------------------------------------------------------------------- /example/windows/runner/run_loop.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_RUN_LOOP_H_ 2 | #define RUNNER_RUN_LOOP_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | // A runloop that will service events for Flutter instances as well 10 | // as native messages. 11 | class RunLoop { 12 | public: 13 | RunLoop(); 14 | ~RunLoop(); 15 | 16 | // Prevent copying 17 | RunLoop(RunLoop const&) = delete; 18 | RunLoop& operator=(RunLoop const&) = delete; 19 | 20 | // Runs the run loop until the application quits. 21 | void Run(); 22 | 23 | // Registers the given Flutter instance for event servicing. 24 | void RegisterFlutterInstance( 25 | flutter::FlutterEngine* flutter_instance); 26 | 27 | // Unregisters the given Flutter instance from event servicing. 28 | void UnregisterFlutterInstance( 29 | flutter::FlutterEngine* flutter_instance); 30 | 31 | private: 32 | using TimePoint = std::chrono::steady_clock::time_point; 33 | 34 | // Processes all currently pending messages for registered Flutter instances. 35 | TimePoint ProcessFlutterMessages(); 36 | 37 | std::set flutter_instances_; 38 | }; 39 | 40 | #endif // RUNNER_RUN_LOOP_H_ 41 | -------------------------------------------------------------------------------- /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 | if (target_length == 0) { 52 | return std::string(); 53 | } 54 | std::string utf8_string; 55 | utf8_string.resize(target_length); 56 | int converted_length = ::WideCharToMultiByte( 57 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 58 | -1, utf8_string.data(), 59 | target_length, nullptr, nullptr); 60 | if (converted_length == 0) { 61 | return std::string(); 62 | } 63 | return utf8_string; 64 | } 65 | -------------------------------------------------------------------------------- /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 | 5 | #include "resource.h" 6 | 7 | namespace { 8 | 9 | constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; 10 | 11 | // The number of Win32Window objects that currently exist. 12 | static int g_active_window_count = 0; 13 | 14 | using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); 15 | 16 | // Scale helper to convert logical scaler values to physical using passed in 17 | // scale factor 18 | int Scale(int source, double scale_factor) { 19 | return static_cast(source * scale_factor); 20 | } 21 | 22 | // Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. 23 | // This API is only needed for PerMonitor V1 awareness mode. 24 | void EnableFullDpiSupportIfAvailable(HWND hwnd) { 25 | HMODULE user32_module = LoadLibraryA("User32.dll"); 26 | if (!user32_module) { 27 | return; 28 | } 29 | auto enable_non_client_dpi_scaling = 30 | reinterpret_cast( 31 | GetProcAddress(user32_module, "EnableNonClientDpiScaling")); 32 | if (enable_non_client_dpi_scaling != nullptr) { 33 | enable_non_client_dpi_scaling(hwnd); 34 | FreeLibrary(user32_module); 35 | } 36 | } 37 | 38 | } // namespace 39 | 40 | // Manages the Win32Window's window class registration. 41 | class WindowClassRegistrar { 42 | public: 43 | ~WindowClassRegistrar() = default; 44 | 45 | // Returns the singleton registar instance. 46 | static WindowClassRegistrar* GetInstance() { 47 | if (!instance_) { 48 | instance_ = new WindowClassRegistrar(); 49 | } 50 | return instance_; 51 | } 52 | 53 | // Returns the name of the window class, registering the class if it hasn't 54 | // previously been registered. 55 | const wchar_t* GetWindowClass(); 56 | 57 | // Unregisters the window class. Should only be called if there are no 58 | // instances of the window. 59 | void UnregisterWindowClass(); 60 | 61 | private: 62 | WindowClassRegistrar() = default; 63 | 64 | static WindowClassRegistrar* instance_; 65 | 66 | bool class_registered_ = false; 67 | }; 68 | 69 | WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; 70 | 71 | const wchar_t* WindowClassRegistrar::GetWindowClass() { 72 | if (!class_registered_) { 73 | WNDCLASS window_class{}; 74 | window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); 75 | window_class.lpszClassName = kWindowClassName; 76 | window_class.style = CS_HREDRAW | CS_VREDRAW; 77 | window_class.cbClsExtra = 0; 78 | window_class.cbWndExtra = 0; 79 | window_class.hInstance = GetModuleHandle(nullptr); 80 | window_class.hIcon = 81 | LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); 82 | window_class.hbrBackground = 0; 83 | window_class.lpszMenuName = nullptr; 84 | window_class.lpfnWndProc = Win32Window::WndProc; 85 | RegisterClass(&window_class); 86 | class_registered_ = true; 87 | } 88 | return kWindowClassName; 89 | } 90 | 91 | void WindowClassRegistrar::UnregisterWindowClass() { 92 | UnregisterClass(kWindowClassName, nullptr); 93 | class_registered_ = false; 94 | } 95 | 96 | Win32Window::Win32Window() { 97 | ++g_active_window_count; 98 | } 99 | 100 | Win32Window::~Win32Window() { 101 | --g_active_window_count; 102 | Destroy(); 103 | } 104 | 105 | bool Win32Window::CreateAndShow(const std::wstring& title, 106 | const Point& origin, 107 | const Size& size) { 108 | Destroy(); 109 | 110 | const wchar_t* window_class = 111 | WindowClassRegistrar::GetInstance()->GetWindowClass(); 112 | 113 | const POINT target_point = {static_cast(origin.x), 114 | static_cast(origin.y)}; 115 | HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); 116 | UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); 117 | double scale_factor = dpi / 96.0; 118 | 119 | HWND window = CreateWindow( 120 | window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 121 | Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), 122 | Scale(size.width, scale_factor), Scale(size.height, scale_factor), 123 | nullptr, nullptr, GetModuleHandle(nullptr), this); 124 | 125 | if (!window) { 126 | return false; 127 | } 128 | 129 | return OnCreate(); 130 | } 131 | 132 | // static 133 | LRESULT CALLBACK Win32Window::WndProc(HWND const window, 134 | UINT const message, 135 | WPARAM const wparam, 136 | LPARAM const lparam) noexcept { 137 | if (message == WM_NCCREATE) { 138 | auto window_struct = reinterpret_cast(lparam); 139 | SetWindowLongPtr(window, GWLP_USERDATA, 140 | reinterpret_cast(window_struct->lpCreateParams)); 141 | 142 | auto that = static_cast(window_struct->lpCreateParams); 143 | EnableFullDpiSupportIfAvailable(window); 144 | that->window_handle_ = window; 145 | } else if (Win32Window* that = GetThisFromHandle(window)) { 146 | return that->MessageHandler(window, message, wparam, lparam); 147 | } 148 | 149 | return DefWindowProc(window, message, wparam, lparam); 150 | } 151 | 152 | LRESULT 153 | Win32Window::MessageHandler(HWND hwnd, 154 | UINT const message, 155 | WPARAM const wparam, 156 | LPARAM const lparam) noexcept { 157 | switch (message) { 158 | case WM_DESTROY: 159 | window_handle_ = nullptr; 160 | Destroy(); 161 | if (quit_on_close_) { 162 | PostQuitMessage(0); 163 | } 164 | return 0; 165 | 166 | case WM_DPICHANGED: { 167 | auto newRectSize = reinterpret_cast(lparam); 168 | LONG newWidth = newRectSize->right - newRectSize->left; 169 | LONG newHeight = newRectSize->bottom - newRectSize->top; 170 | 171 | SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, 172 | newHeight, SWP_NOZORDER | SWP_NOACTIVATE); 173 | 174 | return 0; 175 | } 176 | case WM_SIZE: { 177 | RECT rect = GetClientArea(); 178 | if (child_content_ != nullptr) { 179 | // Size and position the child window. 180 | MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, 181 | rect.bottom - rect.top, TRUE); 182 | } 183 | return 0; 184 | } 185 | 186 | case WM_ACTIVATE: 187 | if (child_content_ != nullptr) { 188 | SetFocus(child_content_); 189 | } 190 | return 0; 191 | } 192 | 193 | return DefWindowProc(window_handle_, message, wparam, lparam); 194 | } 195 | 196 | void Win32Window::Destroy() { 197 | OnDestroy(); 198 | 199 | if (window_handle_) { 200 | DestroyWindow(window_handle_); 201 | window_handle_ = nullptr; 202 | } 203 | if (g_active_window_count == 0) { 204 | WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); 205 | } 206 | } 207 | 208 | Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { 209 | return reinterpret_cast( 210 | GetWindowLongPtr(window, GWLP_USERDATA)); 211 | } 212 | 213 | void Win32Window::SetChildContent(HWND content) { 214 | child_content_ = content; 215 | SetParent(content, window_handle_); 216 | RECT frame = GetClientArea(); 217 | 218 | MoveWindow(content, frame.left, frame.top, frame.right - frame.left, 219 | frame.bottom - frame.top, true); 220 | 221 | SetFocus(child_content_); 222 | } 223 | 224 | RECT Win32Window::GetClientArea() { 225 | RECT frame; 226 | GetClientRect(window_handle_, &frame); 227 | return frame; 228 | } 229 | 230 | HWND Win32Window::GetHandle() { 231 | return window_handle_; 232 | } 233 | 234 | void Win32Window::SetQuitOnClose(bool quit_on_close) { 235 | quit_on_close_ = quit_on_close; 236 | } 237 | 238 | bool Win32Window::OnCreate() { 239 | // No-op; provided for subclasses. 240 | return true; 241 | } 242 | 243 | void Win32Window::OnDestroy() { 244 | // No-op; provided for subclasses. 245 | } 246 | -------------------------------------------------------------------------------- /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 and shows a win32 window with |title| and position and size 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 to will treat the width height passed in to this function 35 | // as logical pixels and scale to appropriate for the default monitor. Returns 36 | // true if the window was created successfully. 37 | bool CreateAndShow(const std::wstring& title, 38 | const Point& origin, 39 | const Size& size); 40 | 41 | // Release OS resources associated with window. 42 | void Destroy(); 43 | 44 | // Inserts |content| into the window tree. 45 | void SetChildContent(HWND content); 46 | 47 | // Returns the backing Window handle to enable clients to set icon and other 48 | // window properties. Returns nullptr if the window has been destroyed. 49 | HWND GetHandle(); 50 | 51 | // If true, closing this window will quit the application. 52 | void SetQuitOnClose(bool quit_on_close); 53 | 54 | // Return a RECT representing the bounds of the current client area. 55 | RECT GetClientArea(); 56 | 57 | protected: 58 | // Processes and route salient window messages for mouse handling, 59 | // size change and DPI. Delegates handling of these to member overloads that 60 | // inheriting classes can handle. 61 | virtual LRESULT MessageHandler(HWND window, 62 | UINT const message, 63 | WPARAM const wparam, 64 | LPARAM const lparam) noexcept; 65 | 66 | // Called when CreateAndShow is called, allowing subclass window-related 67 | // setup. Subclasses should return false if setup fails. 68 | virtual bool OnCreate(); 69 | 70 | // Called when Destroy is called. 71 | virtual void OnDestroy(); 72 | 73 | private: 74 | friend class WindowClassRegistrar; 75 | 76 | // OS callback called by message pump. Handles the WM_NCCREATE message which 77 | // is passed when the non-client area is being created and enables automatic 78 | // non-client DPI scaling so that the non-client area automatically 79 | // responsponds to changes in DPI. All other messages are handled by 80 | // MessageHandler. 81 | static LRESULT CALLBACK WndProc(HWND const window, 82 | UINT const message, 83 | WPARAM const wparam, 84 | LPARAM const lparam) noexcept; 85 | 86 | // Retrieves a class instance pointer for |window| 87 | static Win32Window* GetThisFromHandle(HWND const window) noexcept; 88 | 89 | bool quit_on_close_ = false; 90 | 91 | // window handle for top level window. 92 | HWND window_handle_ = nullptr; 93 | 94 | // window handle for hosted content. 95 | HWND child_content_ = nullptr; 96 | }; 97 | 98 | #endif // RUNNER_WIN32_WINDOW_H_ 99 | -------------------------------------------------------------------------------- /lib/flutter_screenutil.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Created by 李卓原 on 2018/9/29. 3 | * email: zhuoyuan93@gmail.com 4 | */ 5 | 6 | library flutter_screenutil; 7 | 8 | export 'src/r_padding.dart'; 9 | export 'src/r_sizedbox.dart'; 10 | export 'src/screen_util.dart'; 11 | export 'src/screenutil_init.dart'; 12 | export 'src/size_extension.dart'; 13 | export 'src/screenutil_mixin.dart'; 14 | -------------------------------------------------------------------------------- /lib/src/r_padding.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/rendering.dart'; 3 | 4 | import 'size_extension.dart'; 5 | 6 | class RPadding extends SingleChildRenderObjectWidget { 7 | /// Creates an adapt widget that insets its child. 8 | /// 9 | /// The [padding] argument must not be null. 10 | const RPadding({ 11 | Key? key, 12 | required Widget child, 13 | required this.padding, 14 | }) : super(key: key, child: child); 15 | 16 | /// The amount of space by which to inset the child. 17 | final EdgeInsets padding; 18 | 19 | @override 20 | RenderPadding createRenderObject(BuildContext context) { 21 | return RenderPadding( 22 | padding: padding is REdgeInsets ? padding : padding.r, 23 | textDirection: Directionality.maybeOf(context), 24 | ); 25 | } 26 | } 27 | 28 | class REdgeInsets extends EdgeInsets { 29 | /// Creates adapt insets from offsets from the left, top, right, and bottom. 30 | REdgeInsets.fromLTRB(double left, double top, double right, double bottom) 31 | : super.fromLTRB(left.r, top.r, right.r, bottom.r); 32 | 33 | /// Creates adapt insets where all the offsets are `value`. 34 | /// 35 | /// {@tool snippet} 36 | /// 37 | /// Adapt height-pixel margin on all sides: 38 | /// 39 | /// ```dart 40 | /// const REdgeInsets.all(8.0) 41 | /// ``` 42 | /// {@end-tool} 43 | REdgeInsets.all(double value) : super.all(value.r); 44 | 45 | /// Creates adapt insets with symmetrical vertical and horizontal offsets. 46 | /// 47 | /// {@tool snippet} 48 | /// 49 | /// Adapt Eight pixel margin above and below, no horizontal margins: 50 | /// 51 | /// ```dart 52 | /// const REdgeInsets.symmetric(vertical: 8.0) 53 | /// ``` 54 | /// {@end-tool} 55 | REdgeInsets.symmetric({ 56 | double vertical = 0, 57 | double horizontal = 0, 58 | }) : super.symmetric(vertical: vertical.r, horizontal: horizontal.r); 59 | 60 | /// Creates adapt insets with only the given values non-zero. 61 | /// 62 | /// {@tool snippet} 63 | /// 64 | /// Adapt left margin indent of 40 pixels: 65 | /// 66 | /// ```dart 67 | /// const REdgeInsets.only(left: 40.0) 68 | /// ``` 69 | /// {@end-tool} 70 | REdgeInsets.only({ 71 | double bottom = 0, 72 | double right = 0, 73 | double left = 0, 74 | double top = 0, 75 | }) : super.only( 76 | bottom: bottom.r, 77 | right: right.r, 78 | left: left.r, 79 | top: top.r, 80 | ); 81 | } 82 | 83 | class REdgeInsetsDirectional extends EdgeInsetsDirectional { 84 | /// Creates insets where all the offsets are `value`. 85 | /// 86 | /// {@tool snippet} 87 | /// 88 | /// Adapt eight-pixel margin on all sides: 89 | /// 90 | /// ```dart 91 | /// const REdgeInsetsDirectional.all(8.0) 92 | /// ``` 93 | /// {@end-tool} 94 | REdgeInsetsDirectional.all(double value) : super.all(value.r); 95 | 96 | /// Creates insets with only the given values non-zero. 97 | /// 98 | /// {@tool snippet} 99 | /// 100 | /// Adapt margin indent of 40 pixels on the leading side: 101 | /// 102 | /// ```dart 103 | /// const REdgeInsetsDirectional.only(start: 40.0) 104 | /// ``` 105 | /// {@end-tool} 106 | REdgeInsetsDirectional.only({ 107 | double bottom = 0, 108 | double end = 0, 109 | double start = 0, 110 | double top = 0, 111 | }) : super.only( 112 | bottom: bottom.r, 113 | start: start.r, 114 | end: end.r, 115 | top: top.r, 116 | ); 117 | 118 | /// Creates adapt insets from offsets from the start, top, end, and bottom. 119 | REdgeInsetsDirectional.fromSTEB( 120 | double start, 121 | double top, 122 | double end, 123 | double bottom, 124 | ) : super.fromSTEB( 125 | start.r, 126 | top.r, 127 | end.r, 128 | bottom.r, 129 | ); 130 | } 131 | -------------------------------------------------------------------------------- /lib/src/r_sizedbox.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/rendering.dart'; 3 | 4 | import 'size_extension.dart'; 5 | 6 | class RSizedBox extends SizedBox { 7 | const RSizedBox({ 8 | Key? key, 9 | double? height, 10 | double? width, 11 | Widget? child, 12 | }) : _square = false, 13 | super(key: key, child: child, width: width, height: height); 14 | 15 | const RSizedBox.vertical( 16 | double? height, { 17 | Key? key, 18 | Widget? child, 19 | }) : _square = false, 20 | super(key: key, child: child, height: height); 21 | 22 | const RSizedBox.horizontal( 23 | double? width, { 24 | Key? key, 25 | Widget? child, 26 | }) : _square = false, 27 | super(key: key, child: child, width: width); 28 | 29 | const RSizedBox.square({ 30 | Key? key, 31 | double? height, 32 | double? dimension, 33 | Widget? child, 34 | }) : _square = true, 35 | super.square(key: key, child: child, dimension: dimension); 36 | 37 | RSizedBox.fromSize({ 38 | Key? key, 39 | Size? size, 40 | Widget? child, 41 | }) : _square = false, 42 | super.fromSize(key: key, child: child, size: size); 43 | 44 | @override 45 | RenderConstrainedBox createRenderObject(BuildContext context) { 46 | return RenderConstrainedBox( 47 | additionalConstraints: _additionalConstraints, 48 | ); 49 | } 50 | 51 | final bool _square; 52 | 53 | BoxConstraints get _additionalConstraints { 54 | final boxConstraints = 55 | BoxConstraints.tightFor(width: width, height: height); 56 | return _square ? boxConstraints.r : boxConstraints.hw; 57 | } 58 | 59 | @override 60 | void updateRenderObject( 61 | BuildContext context, RenderConstrainedBox renderObject) { 62 | renderObject.additionalConstraints = _additionalConstraints; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/src/screen_util.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Created by 李卓原 on 2018/9/29. 3 | * email: zhuoyuan93@gmail.com 4 | */ 5 | 6 | import 'package:flutter/foundation.dart' show kIsWeb, defaultTargetPlatform; 7 | 8 | import 'dart:math' show min, max; 9 | import 'dart:ui' as ui show FlutterView; 10 | 11 | import 'package:flutter/widgets.dart'; 12 | 13 | typedef FontSizeResolver = double Function(num fontSize, ScreenUtil instance); 14 | 15 | class ScreenUtil { 16 | static const Size defaultSize = Size(360, 690); 17 | static ScreenUtil _instance = ScreenUtil._(); 18 | 19 | static bool Function() _enableScaleWH = () => true; 20 | static bool Function() _enableScaleText = () => true; 21 | 22 | 23 | /// UI设计中手机尺寸 , dp 24 | /// Size of the phone in UI Design , dp 25 | late Size _uiSize; 26 | 27 | ///屏幕方向 28 | late Orientation _orientation; 29 | 30 | late bool _minTextAdapt; 31 | late MediaQueryData _data; 32 | late bool _splitScreenMode; 33 | FontSizeResolver? fontSizeResolver; 34 | 35 | ScreenUtil._(); 36 | 37 | factory ScreenUtil() => _instance; 38 | 39 | /// Enable scale 40 | /// 41 | /// if the enableWH return false, the width and the height scale ratio will be 1 42 | /// if the enableText return false, the text scale ratio will be 1 43 | /// 44 | static void enableScale({bool Function()? enableWH, bool Function()? enableText}) { 45 | _enableScaleWH = enableWH ?? () => true; 46 | _enableScaleText = enableText ?? () => true; 47 | } 48 | 49 | /// Manually wait for window size to be initialized 50 | /// 51 | /// `Recommended` to use before you need access window size 52 | /// or in custom splash/bootstrap screen [FutureBuilder] 53 | /// 54 | /// example: 55 | /// ```dart 56 | /// ... 57 | /// ScreenUtil.init(context, ...); 58 | /// ... 59 | /// FutureBuilder( 60 | /// future: Future.wait([..., ensureScreenSize(), ...]), 61 | /// builder: (context, snapshot) { 62 | /// if (snapshot.hasData) return const HomeScreen(); 63 | /// return Material( 64 | /// child: LayoutBuilder( 65 | /// ... 66 | /// ), 67 | /// ); 68 | /// }, 69 | /// ) 70 | /// ``` 71 | static Future ensureScreenSize([ 72 | ui.FlutterView? window, 73 | Duration duration = const Duration(milliseconds: 10), 74 | ]) async { 75 | final binding = WidgetsFlutterBinding.ensureInitialized(); 76 | binding.deferFirstFrame(); 77 | 78 | await Future.doWhile(() { 79 | if (window == null) { 80 | window = binding.platformDispatcher.implicitView; 81 | } 82 | 83 | if (window == null || window!.physicalSize.isEmpty) { 84 | return Future.delayed(duration, () => true); 85 | } 86 | 87 | return false; 88 | }); 89 | 90 | binding.allowFirstFrame(); 91 | } 92 | 93 | Set? _elementsToRebuild; 94 | 95 | /// ### Experimental 96 | /// Register current page and all its descendants to rebuild. 97 | /// Helpful when building for web and desktop 98 | static void registerToBuild( 99 | BuildContext context, [ 100 | bool withDescendants = false, 101 | ]) { 102 | (_instance._elementsToRebuild ??= {}).add(context as Element); 103 | 104 | if (withDescendants) { 105 | context.visitChildren((element) { 106 | registerToBuild(element, true); 107 | }); 108 | } 109 | } 110 | 111 | static void configure({ 112 | MediaQueryData? data, 113 | Size? designSize, 114 | bool? splitScreenMode, 115 | bool? minTextAdapt, 116 | FontSizeResolver? fontSizeResolver, 117 | }) { 118 | try { 119 | if (data != null) 120 | _instance._data = data; 121 | else 122 | data = _instance._data; 123 | 124 | if (designSize != null) 125 | _instance._uiSize = designSize; 126 | else 127 | designSize = _instance._uiSize; 128 | } catch (_) { 129 | throw Exception( 130 | 'You must either use ScreenUtil.init or ScreenUtilInit first'); 131 | } 132 | 133 | final MediaQueryData? deviceData = data.nonEmptySizeOrNull(); 134 | final Size deviceSize = deviceData?.size ?? designSize; 135 | 136 | final orientation = deviceData?.orientation ?? 137 | (deviceSize.width > deviceSize.height 138 | ? Orientation.landscape 139 | : Orientation.portrait); 140 | 141 | _instance 142 | ..fontSizeResolver = fontSizeResolver ?? _instance.fontSizeResolver 143 | .._minTextAdapt = minTextAdapt ?? _instance._minTextAdapt 144 | .._splitScreenMode = splitScreenMode ?? _instance._splitScreenMode 145 | .._orientation = orientation; 146 | 147 | _instance._elementsToRebuild?.forEach((el) => el.markNeedsBuild()); 148 | } 149 | 150 | /// Initializing the library. 151 | static void init( 152 | BuildContext context, { 153 | Size designSize = defaultSize, 154 | bool splitScreenMode = false, 155 | bool minTextAdapt = false, 156 | FontSizeResolver? fontSizeResolver, 157 | }) { 158 | final view = View.maybeOf(context); 159 | return configure( 160 | data: view != null ? MediaQueryData.fromView(view) : null, 161 | designSize: designSize, 162 | splitScreenMode: splitScreenMode, 163 | minTextAdapt: minTextAdapt, 164 | fontSizeResolver: fontSizeResolver, 165 | ); 166 | } 167 | 168 | static Future ensureScreenSizeAndInit( 169 | BuildContext context, { 170 | Size designSize = defaultSize, 171 | bool splitScreenMode = false, 172 | bool minTextAdapt = false, 173 | FontSizeResolver? fontSizeResolver, 174 | }) { 175 | return ScreenUtil.ensureScreenSize().then((_) { 176 | return init( 177 | context, 178 | designSize: designSize, 179 | minTextAdapt: minTextAdapt, 180 | splitScreenMode: splitScreenMode, 181 | fontSizeResolver: fontSizeResolver, 182 | ); 183 | }); 184 | } 185 | 186 | ///获取屏幕方向 187 | ///Get screen orientation 188 | Orientation get orientation => _orientation; 189 | 190 | /// 每个逻辑像素的字体像素数,字体的缩放比例 191 | /// The number of font pixels for each logical pixel. 192 | double get textScaleFactor => _data.textScaleFactor; 193 | 194 | /// 设备的像素密度 195 | /// The size of the media in logical pixels (e.g, the size of the screen). 196 | double? get pixelRatio => _data.devicePixelRatio; 197 | 198 | /// 当前设备宽度 dp 199 | /// The horizontal extent of this size. 200 | double get screenWidth => _data.size.width; 201 | 202 | ///当前设备高度 dp 203 | ///The vertical extent of this size. dp 204 | double get screenHeight => _data.size.height; 205 | 206 | /// 状态栏高度 dp 刘海屏会更高 207 | /// The offset from the top, in dp 208 | double get statusBarHeight => _data.padding.top; 209 | 210 | /// 底部安全区距离 dp 211 | /// The offset from the bottom, in dp 212 | double get bottomBarHeight => _data.padding.bottom; 213 | 214 | /// 实际尺寸与UI设计的比例 215 | /// The ratio of actual width to UI design 216 | double get scaleWidth => !_enableScaleWH() ? 1 : screenWidth / _uiSize.width; 217 | 218 | /// The ratio of actual height to UI design 219 | double get scaleHeight => 220 | !_enableScaleWH() ? 1 : (_splitScreenMode ? max(screenHeight, 700) : screenHeight) / 221 | _uiSize.height; 222 | 223 | double get scaleText => 224 | !_enableScaleText() ? 1 : (_minTextAdapt ? min(scaleWidth, scaleHeight) : scaleWidth); 225 | 226 | /// 根据UI设计的设备宽度适配 227 | /// 高度也可以根据这个来做适配可以保证不变形,比如你想要一个正方形的时候. 228 | /// Adapted to the device width of the UI Design. 229 | /// Height can also be adapted according to this to ensure no deformation , 230 | /// if you want a square 231 | double setWidth(num width) => width * scaleWidth; 232 | 233 | /// 根据UI设计的设备高度适配 234 | /// 当发现UI设计中的一屏显示的与当前样式效果不符合时, 235 | /// 或者形状有差异时,建议使用此方法实现高度适配. 236 | /// 高度适配主要针对想根据UI设计的一屏展示一样的效果 237 | /// Highly adaptable to the device according to UI Design 238 | /// It is recommended to use this method to achieve a high degree of adaptation 239 | /// when it is found that one screen in the UI design 240 | /// does not match the current style effect, or if there is a difference in shape. 241 | double setHeight(num height) => height * scaleHeight; 242 | 243 | ///根据宽度或高度中的较小值进行适配 244 | ///Adapt according to the smaller of width or height 245 | double radius(num r) => r * min(scaleWidth, scaleHeight); 246 | 247 | /// Adapt according to the both width and height 248 | double diagonal(num d) => d * scaleHeight * scaleWidth; 249 | 250 | /// Adapt according to the maximum value of scale width and scale height 251 | double diameter(num d) => d * max(scaleWidth, scaleHeight); 252 | 253 | ///字体大小适配方法 254 | ///- [fontSize] UI设计上字体的大小,单位dp. 255 | ///Font size adaptation method 256 | ///- [fontSize] The size of the font on the UI design, in dp. 257 | double setSp(num fontSize) => 258 | fontSizeResolver?.call(fontSize, _instance) ?? fontSize * scaleText; 259 | 260 | DeviceType deviceType(BuildContext context) { 261 | var deviceType = DeviceType.web; 262 | final screenWidth = MediaQuery.of(context).size.width; 263 | final screenHeight = MediaQuery.of(context).size.height; 264 | final orientation = MediaQuery.of(context).orientation; 265 | 266 | if (kIsWeb) { 267 | deviceType = DeviceType.web; 268 | } else { 269 | bool isMobile = defaultTargetPlatform == TargetPlatform.iOS || 270 | defaultTargetPlatform == TargetPlatform.android; 271 | bool isTablet = 272 | (orientation == Orientation.portrait && screenWidth >= 600) || 273 | (orientation == Orientation.landscape && screenHeight >= 600); 274 | 275 | if (isMobile) { 276 | deviceType = isTablet ? DeviceType.tablet : DeviceType.mobile; 277 | } else { 278 | switch (defaultTargetPlatform) { 279 | case TargetPlatform.linux: 280 | deviceType = DeviceType.linux; 281 | break; 282 | case TargetPlatform.macOS: 283 | deviceType = DeviceType.mac; 284 | break; 285 | case TargetPlatform.windows: 286 | deviceType = DeviceType.windows; 287 | break; 288 | case TargetPlatform.fuchsia: 289 | deviceType = DeviceType.fuchsia; 290 | break; 291 | default: 292 | break; 293 | } 294 | } 295 | } 296 | 297 | return deviceType; 298 | } 299 | 300 | SizedBox setVerticalSpacing(num height) => 301 | SizedBox(height: setHeight(height)); 302 | 303 | SizedBox setVerticalSpacingFromWidth(num height) => 304 | SizedBox(height: setWidth(height)); 305 | 306 | SizedBox setHorizontalSpacing(num width) => SizedBox(width: setWidth(width)); 307 | 308 | SizedBox setHorizontalSpacingRadius(num width) => 309 | SizedBox(width: radius(width)); 310 | 311 | SizedBox setVerticalSpacingRadius(num height) => 312 | SizedBox(height: radius(height)); 313 | 314 | SizedBox setHorizontalSpacingDiameter(num width) => 315 | SizedBox(width: diameter(width)); 316 | 317 | SizedBox setVerticalSpacingDiameter(num height) => 318 | SizedBox(height: diameter(height)); 319 | 320 | SizedBox setHorizontalSpacingDiagonal(num width) => 321 | SizedBox(width: diagonal(width)); 322 | 323 | SizedBox setVerticalSpacingDiagonal(num height) => 324 | SizedBox(height: diagonal(height)); 325 | } 326 | 327 | extension on MediaQueryData? { 328 | MediaQueryData? nonEmptySizeOrNull() { 329 | if (this?.size.isEmpty ?? true) 330 | return null; 331 | else 332 | return this; 333 | } 334 | } 335 | 336 | enum DeviceType { mobile, tablet, web, mac, windows, linux, fuchsia } 337 | -------------------------------------------------------------------------------- /lib/src/screenutil_init.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:collection'; 3 | 4 | import 'package:flutter/widgets.dart'; 5 | 6 | import './_flutter_widgets.dart'; 7 | import 'screen_util.dart'; 8 | import 'screenutil_mixin.dart'; 9 | 10 | typedef RebuildFactor = bool Function(MediaQueryData old, MediaQueryData data); 11 | 12 | typedef ScreenUtilInitBuilder = Widget Function( 13 | BuildContext context, 14 | Widget? child, 15 | ); 16 | 17 | abstract class RebuildFactors { 18 | static bool size(MediaQueryData old, MediaQueryData data) { 19 | return old.size != data.size; 20 | } 21 | 22 | static bool orientation(MediaQueryData old, MediaQueryData data) { 23 | return old.orientation != data.orientation; 24 | } 25 | 26 | static bool sizeAndViewInsets(MediaQueryData old, MediaQueryData data) { 27 | return old.viewInsets != data.viewInsets; 28 | } 29 | 30 | static bool change(MediaQueryData old, MediaQueryData data) { 31 | return old != data; 32 | } 33 | 34 | static bool always(MediaQueryData _, MediaQueryData __) { 35 | return true; 36 | } 37 | 38 | static bool none(MediaQueryData _, MediaQueryData __) { 39 | return false; 40 | } 41 | } 42 | 43 | abstract class FontSizeResolvers { 44 | static double width(num fontSize, ScreenUtil instance) { 45 | return instance.setWidth(fontSize); 46 | } 47 | 48 | static double height(num fontSize, ScreenUtil instance) { 49 | return instance.setHeight(fontSize); 50 | } 51 | 52 | static double radius(num fontSize, ScreenUtil instance) { 53 | return instance.radius(fontSize); 54 | } 55 | 56 | static double diameter(num fontSize, ScreenUtil instance) { 57 | return instance.diameter(fontSize); 58 | } 59 | 60 | static double diagonal(num fontSize, ScreenUtil instance) { 61 | return instance.diagonal(fontSize); 62 | } 63 | } 64 | 65 | class ScreenUtilInit extends StatefulWidget { 66 | /// A helper widget that initializes [ScreenUtil] 67 | const ScreenUtilInit({ 68 | Key? key, 69 | this.builder, 70 | this.child, 71 | this.rebuildFactor = RebuildFactors.size, 72 | this.designSize = ScreenUtil.defaultSize, 73 | this.splitScreenMode = false, 74 | this.minTextAdapt = false, 75 | this.useInheritedMediaQuery = false, 76 | this.ensureScreenSize = false, 77 | this.enableScaleWH, 78 | this.enableScaleText, 79 | this.responsiveWidgets, 80 | this.excludeWidgets, 81 | this.fontSizeResolver = FontSizeResolvers.width, 82 | }) : super(key: key); 83 | 84 | final ScreenUtilInitBuilder? builder; 85 | final Widget? child; 86 | final bool splitScreenMode; 87 | final bool minTextAdapt; 88 | final bool useInheritedMediaQuery; 89 | final bool ensureScreenSize; 90 | final bool Function()? enableScaleWH; 91 | final bool Function()? enableScaleText; 92 | final RebuildFactor rebuildFactor; 93 | final FontSizeResolver fontSizeResolver; 94 | 95 | /// The [Size] of the device in the design draft, in dp 96 | final Size designSize; 97 | final Iterable? responsiveWidgets; 98 | final Iterable? excludeWidgets; 99 | 100 | @override 101 | State createState() => _ScreenUtilInitState(); 102 | } 103 | 104 | class _ScreenUtilInitState extends State with WidgetsBindingObserver { 105 | final _canMarkedToBuild = HashSet(); 106 | final _excludedWidgets = HashSet(); 107 | MediaQueryData? _mediaQueryData; 108 | final _binding = WidgetsBinding.instance; 109 | final _screenSizeCompleter = Completer(); 110 | 111 | @override 112 | void initState() { 113 | if (widget.responsiveWidgets != null) { 114 | _canMarkedToBuild.addAll(widget.responsiveWidgets!); 115 | } 116 | 117 | ScreenUtil.enableScale(enableWH: widget.enableScaleWH, enableText: widget.enableScaleText); 118 | 119 | _validateSize().then(_screenSizeCompleter.complete); 120 | 121 | super.initState(); 122 | _binding.addObserver(this); 123 | } 124 | 125 | @override 126 | void didChangeMetrics() { 127 | super.didChangeMetrics(); 128 | _revalidate(); 129 | } 130 | 131 | @override 132 | void didChangeDependencies() { 133 | super.didChangeDependencies(); 134 | _revalidate(); 135 | } 136 | 137 | MediaQueryData? _newData() { 138 | final view = View.maybeOf(context); 139 | if (view != null) return MediaQueryData.fromView(view); 140 | return null; 141 | } 142 | 143 | Future _validateSize() async { 144 | if (widget.ensureScreenSize) return ScreenUtil.ensureScreenSize(); 145 | } 146 | 147 | void _markNeedsBuildIfAllowed(Element el) { 148 | final widgetName = el.widget.runtimeType.toString(); 149 | if (_excludedWidgets.contains(widgetName)) return; 150 | final allowed = widget is SU || 151 | _canMarkedToBuild.contains(widgetName) || 152 | !(widgetName.startsWith('_') || flutterWidgets.contains(widgetName)); 153 | 154 | if (allowed) el.markNeedsBuild(); 155 | } 156 | 157 | void _updateTree(Element el) { 158 | _markNeedsBuildIfAllowed(el); 159 | el.visitChildren(_updateTree); 160 | } 161 | 162 | void _revalidate([void Function()? callback]) { 163 | final oldData = _mediaQueryData; 164 | final newData = _newData(); 165 | 166 | if (newData == null) return; 167 | 168 | if (oldData == null || widget.rebuildFactor(oldData, newData)) { 169 | setState(() { 170 | _mediaQueryData = newData; 171 | _updateTree(context as Element); 172 | callback?.call(); 173 | }); 174 | } 175 | } 176 | 177 | @override 178 | Widget build(BuildContext context) { 179 | final mq = _mediaQueryData; 180 | 181 | if (mq == null) return const SizedBox.shrink(); 182 | 183 | if (!widget.ensureScreenSize) { 184 | ScreenUtil.configure( 185 | data: mq, 186 | designSize: widget.designSize, 187 | splitScreenMode: widget.splitScreenMode, 188 | minTextAdapt: widget.minTextAdapt, 189 | fontSizeResolver: widget.fontSizeResolver, 190 | ); 191 | 192 | return widget.builder?.call(context, widget.child) ?? widget.child!; 193 | } 194 | 195 | return FutureBuilder( 196 | future: _screenSizeCompleter.future, 197 | builder: (c, snapshot) { 198 | ScreenUtil.configure( 199 | data: mq, 200 | designSize: widget.designSize, 201 | splitScreenMode: widget.splitScreenMode, 202 | minTextAdapt: widget.minTextAdapt, 203 | fontSizeResolver: widget.fontSizeResolver, 204 | ); 205 | 206 | if (snapshot.connectionState == ConnectionState.done) { 207 | return widget.builder?.call(context, widget.child) ?? widget.child!; 208 | } 209 | 210 | return const SizedBox.shrink(); 211 | }, 212 | ); 213 | } 214 | 215 | @override 216 | void dispose() { 217 | _binding.removeObserver(this); 218 | super.dispose(); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /lib/src/screenutil_mixin.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | mixin SU on Widget {} 4 | -------------------------------------------------------------------------------- /lib/src/size_extension.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | import 'screen_util.dart'; 6 | 7 | extension SizeExtension on num { 8 | ///[ScreenUtil.setWidth] 9 | double get w => ScreenUtil().setWidth(this); 10 | 11 | ///[ScreenUtil.setHeight] 12 | double get h => ScreenUtil().setHeight(this); 13 | 14 | ///[ScreenUtil.radius] 15 | double get r => ScreenUtil().radius(this); 16 | 17 | ///[ScreenUtil.diagonal] 18 | double get dg => ScreenUtil().diagonal(this); 19 | 20 | ///[ScreenUtil.diameter] 21 | double get dm => ScreenUtil().diameter(this); 22 | 23 | ///[ScreenUtil.setSp] 24 | double get sp => ScreenUtil().setSp(this); 25 | 26 | ///smart size : it check your value - if it is bigger than your value it will set your value 27 | ///for example, you have set 16.sm() , if for your screen 16.sp() is bigger than 16 , then it will set 16 not 16.sp() 28 | ///I think that it is good for save size balance on big sizes of screen 29 | double get spMin => min(toDouble(), sp); 30 | 31 | @Deprecated('use spMin instead') 32 | double get sm => min(toDouble(), sp); 33 | 34 | double get spMax => max(toDouble(), sp); 35 | 36 | ///屏幕宽度的倍数 37 | ///Multiple of screen width 38 | double get sw => ScreenUtil().screenWidth * this; 39 | 40 | ///屏幕高度的倍数 41 | ///Multiple of screen height 42 | double get sh => ScreenUtil().screenHeight * this; 43 | 44 | ///[ScreenUtil.setHeight] 45 | SizedBox get verticalSpace => ScreenUtil().setVerticalSpacing(this); 46 | 47 | ///[ScreenUtil.setVerticalSpacingFromWidth] 48 | SizedBox get verticalSpaceFromWidth => 49 | ScreenUtil().setVerticalSpacingFromWidth(this); 50 | 51 | ///[ScreenUtil.setWidth] 52 | SizedBox get horizontalSpace => ScreenUtil().setHorizontalSpacing(this); 53 | 54 | ///[ScreenUtil.radius] 55 | SizedBox get horizontalSpaceRadius => 56 | ScreenUtil().setHorizontalSpacingRadius(this); 57 | 58 | ///[ScreenUtil.radius] 59 | SizedBox get verticalSpacingRadius => 60 | ScreenUtil().setVerticalSpacingRadius(this); 61 | 62 | ///[ScreenUtil.diameter] 63 | SizedBox get horizontalSpaceDiameter => 64 | ScreenUtil().setHorizontalSpacingDiameter(this); 65 | 66 | ///[ScreenUtil.diameter] 67 | SizedBox get verticalSpacingDiameter => 68 | ScreenUtil().setVerticalSpacingDiameter(this); 69 | 70 | ///[ScreenUtil.diagonal] 71 | SizedBox get horizontalSpaceDiagonal => 72 | ScreenUtil().setHorizontalSpacingDiagonal(this); 73 | 74 | ///[ScreenUtil.diagonal] 75 | SizedBox get verticalSpacingDiagonal => 76 | ScreenUtil().setVerticalSpacingDiagonal(this); 77 | } 78 | 79 | extension EdgeInsetsExtension on EdgeInsets { 80 | /// Creates adapt insets using r [SizeExtension]. 81 | EdgeInsets get r => copyWith( 82 | top: top.r, 83 | bottom: bottom.r, 84 | right: right.r, 85 | left: left.r, 86 | ); 87 | 88 | EdgeInsets get dm => copyWith( 89 | top: top.dm, 90 | bottom: bottom.dm, 91 | right: right.dm, 92 | left: left.dm, 93 | ); 94 | 95 | EdgeInsets get dg => copyWith( 96 | top: top.dg, 97 | bottom: bottom.dg, 98 | right: right.dg, 99 | left: left.dg, 100 | ); 101 | 102 | EdgeInsets get w => copyWith( 103 | top: top.w, 104 | bottom: bottom.w, 105 | right: right.w, 106 | left: left.w, 107 | ); 108 | 109 | EdgeInsets get h => copyWith( 110 | top: top.h, 111 | bottom: bottom.h, 112 | right: right.h, 113 | left: left.h, 114 | ); 115 | } 116 | 117 | extension BorderRadiusExtension on BorderRadius { 118 | /// Creates adapt BorderRadius using r [SizeExtension]. 119 | BorderRadius get r => copyWith( 120 | bottomLeft: bottomLeft.r, 121 | bottomRight: bottomRight.r, 122 | topLeft: topLeft.r, 123 | topRight: topRight.r, 124 | ); 125 | 126 | BorderRadius get w => copyWith( 127 | bottomLeft: bottomLeft.w, 128 | bottomRight: bottomRight.w, 129 | topLeft: topLeft.w, 130 | topRight: topRight.w, 131 | ); 132 | 133 | BorderRadius get h => copyWith( 134 | bottomLeft: bottomLeft.h, 135 | bottomRight: bottomRight.h, 136 | topLeft: topLeft.h, 137 | topRight: topRight.h, 138 | ); 139 | } 140 | 141 | extension RadiusExtension on Radius { 142 | /// Creates adapt Radius using r [SizeExtension]. 143 | Radius get r => Radius.elliptical(x.r, y.r); 144 | 145 | Radius get dm => Radius.elliptical(x.dm, y.dm); 146 | 147 | Radius get dg => Radius.elliptical(x.dg, y.dg); 148 | 149 | Radius get w => Radius.elliptical(x.w, y.w); 150 | 151 | Radius get h => Radius.elliptical(x.h, y.h); 152 | } 153 | 154 | extension BoxConstraintsExtension on BoxConstraints { 155 | /// Creates adapt BoxConstraints using r [SizeExtension]. 156 | BoxConstraints get r => this.copyWith( 157 | maxHeight: maxHeight.r, 158 | maxWidth: maxWidth.r, 159 | minHeight: minHeight.r, 160 | minWidth: minWidth.r, 161 | ); 162 | 163 | /// Creates adapt BoxConstraints using h-w [SizeExtension]. 164 | BoxConstraints get hw => this.copyWith( 165 | maxHeight: maxHeight.h, 166 | maxWidth: maxWidth.w, 167 | minHeight: minHeight.h, 168 | minWidth: minWidth.w, 169 | ); 170 | 171 | BoxConstraints get w => this.copyWith( 172 | maxHeight: maxHeight.w, 173 | maxWidth: maxWidth.w, 174 | minHeight: minHeight.w, 175 | minWidth: minWidth.w, 176 | ); 177 | 178 | BoxConstraints get h => this.copyWith( 179 | maxHeight: maxHeight.h, 180 | maxWidth: maxWidth.h, 181 | minHeight: minHeight.h, 182 | minWidth: minWidth.h, 183 | ); 184 | } 185 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_screenutil 2 | description: A flutter plugin for adapting screen and font size.Guaranteed to look good on different models 3 | version: 5.9.3 4 | homepage: https://github.com/OpenFlutter/flutter_screenutil 5 | 6 | environment: 7 | sdk: ">=2.17.0 <4.0.0" 8 | flutter: ">=3.10.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | 14 | dev_dependencies: 15 | flutter_test: 16 | sdk: flutter 17 | -------------------------------------------------------------------------------- /test/flutter_screenutil_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 | import 'package:flutter_test/flutter_test.dart'; 4 | 5 | import 'home.test.dart'; 6 | 7 | void main() { 8 | const smallerDeviceSize = Size(300, 600); 9 | const smallerDeviceData = MediaQueryData(size: smallerDeviceSize); 10 | 11 | const biggerDeviceSize = Size(500, 900); 12 | const biggerDeviceData = MediaQueryData(size: biggerDeviceSize); 13 | 14 | const uiSize = Size(470, 740); 15 | 16 | group('[Test calculations]', () { 17 | test('Test smaller size', () { 18 | ScreenUtil.configure( 19 | data: smallerDeviceData, 20 | designSize: uiSize, 21 | minTextAdapt: true, 22 | splitScreenMode: false, 23 | ); 24 | 25 | expect(1.w, smallerDeviceSize.width / uiSize.width); 26 | expect(1.w < 1, true); 27 | expect(1.h, smallerDeviceSize.height / uiSize.height); 28 | expect(1.h < 1, true); 29 | }); 30 | 31 | test('Test bigger size', () { 32 | ScreenUtil.configure( 33 | data: biggerDeviceData, 34 | designSize: uiSize, 35 | minTextAdapt: true, 36 | splitScreenMode: false, 37 | ); 38 | 39 | expect(1.w, biggerDeviceSize.width / uiSize.width); 40 | expect(1.w > 1, true); 41 | expect(1.h, biggerDeviceSize.height / uiSize.height); 42 | expect(1.h > 1, true); 43 | }); 44 | }); 45 | 46 | group('[Test overflow]', () { 47 | testWidgets('Test overflow width', (tester) async { 48 | await tester.pumpWidget(ScreenUtilInit( 49 | designSize: uiSize, 50 | child: MaterialApp(home: WidgetTest(width: () => uiSize.width.w)), 51 | )); 52 | 53 | // Wait until all widget rendered 54 | await tester.pumpAndSettle(); 55 | 56 | // a Text widget must be present 57 | expect(find.text('Test'), findsOneWidget); 58 | }); 59 | 60 | testWidgets('Test overflow height', (tester) async { 61 | await tester.pumpWidget(ScreenUtilInit( 62 | designSize: uiSize, 63 | child: MaterialApp(home: WidgetTest(height: () => uiSize.height.h)), 64 | )); 65 | 66 | // Wait until all widget rendered 67 | await tester.pumpAndSettle(); 68 | 69 | // a Text widget must be present 70 | expect(find.text('Test'), findsOneWidget); 71 | }); 72 | }); 73 | 74 | testWidgets('[Rebuilding]', (tester) async { 75 | final textFieldKey = UniqueKey(); 76 | final buildCountNotifier = ValueNotifier(0); 77 | final focusNode = FocusNode(); 78 | 79 | Finder textField() => find.byKey(textFieldKey); 80 | 81 | await tester.pumpWidget(ScreenUtilInit( 82 | designSize: uiSize, 83 | rebuildFactor: RebuildFactors.always, 84 | child: MaterialApp( 85 | home: Scaffold( 86 | body: Builder( 87 | builder: (context) { 88 | buildCountNotifier.value += 1; 89 | 90 | assert(uiSize.width.w == MediaQuery.of(context).size.width); 91 | 92 | return SizedBox( 93 | width: 1.sw, 94 | child: Column( 95 | children: [ 96 | ValueListenableBuilder( 97 | valueListenable: buildCountNotifier, 98 | builder: (_, count, __) => Text('Built count: $count'), 99 | ), 100 | TextField( 101 | key: textFieldKey, 102 | focusNode: focusNode, 103 | ), 104 | ], 105 | ), 106 | ); 107 | }, 108 | ), 109 | ), 110 | ), 111 | )); 112 | 113 | await tester.pumpAndSettle(); 114 | expect(buildCountNotifier.value, 1); 115 | 116 | expect(textField(), findsOneWidget); 117 | expect(focusNode.hasFocus, false); 118 | 119 | await tester.tap(textField()).then((_) => tester.pumpAndSettle()); 120 | expect(textField(), findsOneWidget); 121 | expect(focusNode.hasFocus, true); 122 | expect(buildCountNotifier.value, 1); 123 | 124 | // Simulate keyboard 125 | tester.view.viewInsets = FakeViewPadding(bottom: 20); 126 | 127 | await tester.pumpAndSettle(); 128 | expect(focusNode.hasFocus, true); 129 | expect(buildCountNotifier.value, 1); 130 | }); 131 | } 132 | -------------------------------------------------------------------------------- /test/home.test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class WidgetTest extends StatelessWidget { 4 | const WidgetTest({ 5 | super.key, 6 | this.width = _zero, 7 | this.height = _zero, 8 | }); 9 | 10 | final double Function() width; 11 | final double Function() height; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return LayoutBuilder( 16 | builder: (_, c) { 17 | final w = width(), h = height(); 18 | 19 | if (c.biggest >= Size(w, h)) { 20 | return const Text('Test'); 21 | } 22 | 23 | throw Error(); 24 | }, 25 | ); 26 | } 27 | 28 | static double _zero() => 0; 29 | } 30 | --------------------------------------------------------------------------------