├── .fvmrc ├── .github ├── dependabot.yaml └── workflows │ ├── gh-pages.yaml │ └── pr.yaml ├── .gitignore ├── .metadata ├── .pubignore ├── .vscode ├── launch.json └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── doc ├── basic.gif ├── screenshot_cross_axis_start.png ├── screenshot_main_axis_start.png ├── screenshot_nav_bar.png ├── screenshot_source_code.png └── set_sizes.gif ├── example ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable-v21 │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values-night │ │ │ │ └── styles.xml │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── assets │ └── fonts │ │ └── RedditMono-VariableFont_wght.ttf ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Podfile │ ├── Podfile.lock │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ ├── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ │ └── LaunchImage.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ └── README.md │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h │ └── RunnerTests │ │ └── RunnerTests.swift ├── lib │ ├── extensions │ │ └── int_ext.dart │ ├── file_asset_paths.dart │ ├── main.dart │ ├── screens │ │ ├── basic │ │ │ ├── basic_example_help_dialog.dart │ │ │ └── basic_example_screen.dart │ │ ├── cascading_delta │ │ │ ├── cascading_delta_help_dialog.dart │ │ │ └── cascading_delta_screen.dart │ │ ├── controller_listen │ │ │ ├── controller_listen_example_help_dialog.dart │ │ │ └── controller_listen_example_screen.dart │ │ ├── controller_set_sizes │ │ │ ├── controller_set_sizes_example_help_dialog.dart │ │ │ └── controller_set_sizes_example_screen.dart │ │ ├── divider │ │ │ └── custom_divider_example_screen.dart │ │ ├── pixels │ │ │ ├── pixels_example_help_dialog.dart │ │ │ └── pixels_example_screen.dart │ │ ├── ratio │ │ │ ├── ratio_example_help_dialog.dart │ │ │ └── ratio_example_screen.dart │ │ └── shrink_and_flex │ │ │ ├── future_builder_shrink_example_screen.dart │ │ │ ├── shrink_and_flex_example_screen.dart │ │ │ └── shrink_and_flex_help_dialog.dart │ ├── utils.dart │ └── widgets │ │ ├── app_version.dart │ │ ├── code_view_dialog.dart │ │ ├── colored_box.dart │ │ ├── help_dialog.dart │ │ ├── nav_drawer.dart │ │ └── size_label.dart ├── macos │ ├── .gitignore │ ├── Flutter │ │ ├── Flutter-Debug.xcconfig │ │ ├── Flutter-Release.xcconfig │ │ └── GeneratedPluginRegistrant.swift │ ├── Podfile │ ├── Podfile.lock │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── app_icon_1024.png │ │ │ │ ├── app_icon_128.png │ │ │ │ ├── app_icon_16.png │ │ │ │ ├── app_icon_256.png │ │ │ │ ├── app_icon_32.png │ │ │ │ ├── app_icon_512.png │ │ │ │ └── app_icon_64.png │ │ ├── Base.lproj │ │ │ └── MainMenu.xib │ │ ├── Configs │ │ │ ├── AppInfo.xcconfig │ │ │ ├── Debug.xcconfig │ │ │ ├── Release.xcconfig │ │ │ └── Warnings.xcconfig │ │ ├── DebugProfile.entitlements │ │ ├── Info.plist │ │ ├── MainFlutterWindow.swift │ │ └── Release.entitlements │ └── RunnerTests │ │ └── RunnerTests.swift ├── pubspec.lock ├── pubspec.yaml ├── web │ ├── favicon.png │ ├── icons │ │ ├── Icon-192.png │ │ ├── Icon-512.png │ │ ├── Icon-maskable-192.png │ │ └── Icon-maskable-512.png │ ├── index.html │ └── manifest.json └── windows │ ├── .gitignore │ ├── CMakeLists.txt │ ├── flutter │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake │ └── runner │ ├── CMakeLists.txt │ ├── Runner.rc │ ├── flutter_window.cpp │ ├── flutter_window.h │ ├── main.cpp │ ├── resource.h │ ├── resources │ └── app_icon.ico │ ├── runner.exe.manifest │ ├── utils.cpp │ ├── utils.h │ ├── win32_window.cpp │ └── win32_window.h ├── lib ├── flutter_resizable_container.dart └── src │ ├── divider_painter.dart │ ├── extensions │ ├── box_constraints_ext.dart │ ├── iterable_ext.dart │ └── num_ext.dart │ ├── layout │ ├── resizable_layout.dart │ └── resizable_layout_direction.dart │ ├── resizable_child.dart │ ├── resizable_container.dart │ ├── resizable_container_divider.dart │ ├── resizable_controller.dart │ ├── resizable_divider.dart │ └── resizable_size.dart ├── pubspec.yaml └── test ├── layout └── resizable_layout_direction_test.dart ├── resizable_container_test.dart ├── resizable_controller_test.dart ├── resizable_divider_test.dart └── resizable_size_test.dart /.fvmrc: -------------------------------------------------------------------------------- 1 | { 2 | "flutter": "3.29.3" 3 | } -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "pub" 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | open-pull-requests-limit: 5 8 | -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yaml: -------------------------------------------------------------------------------- 1 | name: Deploy example to GH Pages 2 | on: 3 | push: 4 | branches: 5 | - main 6 | permissions: 7 | contents: write 8 | jobs: 9 | build-and-deploy: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v4 14 | 15 | - name: Set up Flutter 16 | uses: subosito/flutter-action@v2 17 | with: 18 | flutter-version: "3.22.1" 19 | 20 | - name: Install dependencies 21 | run: flutter pub get 22 | 23 | - name: Build 24 | run: cd example && flutter build web 25 | 26 | - name: Deploy 🚀 27 | uses: JamesIves/github-pages-deploy-action@v4 28 | with: 29 | folder: example/build/web 30 | -------------------------------------------------------------------------------- /.github/workflows/pr.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | pull_request: 3 | types: [opened, synchronize] 4 | branches: 5 | - main 6 | 7 | jobs: 8 | test: 9 | name: Run tests 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Set up Flutter 14 | uses: kuhnroyal/flutter-fvm-config-action/setup@v3 15 | - name: Get packages 16 | run: flutter pub get 17 | - name: Analyze 18 | run: flutter analyze 19 | - name: Test 20 | run: flutter test 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 26 | /pubspec.lock 27 | **/doc/api/ 28 | .dart_tool/ 29 | .packages 30 | build/ 31 | 32 | # FVM Version Cache 33 | .fvm/ -------------------------------------------------------------------------------- /.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: 135454af32477f815a7525073027a3ff9eff1bfd 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /.pubignore: -------------------------------------------------------------------------------- 1 | **.dart_tool 2 | **.idea 3 | **build 4 | **/macos 5 | **/web 6 | **/windows 7 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "example", 9 | "cwd": "example", 10 | "request": "launch", 11 | "type": "dart" 12 | }, 13 | ] 14 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "dart.flutterSdkPath": ".fvm/versions/3.29.3" 3 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2023-2024 Andrew Horn 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | analyzer: 4 | exclude: [build/**] 5 | 6 | # Additional information about this file can be found at 7 | # https://dart.dev/guides/language/analysis-options 8 | -------------------------------------------------------------------------------- /doc/basic.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/doc/basic.gif -------------------------------------------------------------------------------- /doc/screenshot_cross_axis_start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/doc/screenshot_cross_axis_start.png -------------------------------------------------------------------------------- /doc/screenshot_main_axis_start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/doc/screenshot_main_axis_start.png -------------------------------------------------------------------------------- /doc/screenshot_nav_bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/doc/screenshot_nav_bar.png -------------------------------------------------------------------------------- /doc/screenshot_source_code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/doc/screenshot_source_code.png -------------------------------------------------------------------------------- /doc/set_sizes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/doc/set_sizes.gif -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .build/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | .swiftpm/ 13 | migrate_working_dir/ 14 | 15 | # IntelliJ related 16 | *.iml 17 | *.ipr 18 | *.iws 19 | .idea/ 20 | 21 | # The .vscode folder contains launch configuration and tasks you configure in 22 | # VS Code which you may wish to be included in version control, so this line 23 | # is commented out by default. 24 | #.vscode/ 25 | 26 | # Flutter/Dart/Pub related 27 | **/doc/api/ 28 | **/ios/Flutter/.last_build_id 29 | .dart_tool/ 30 | .flutter-plugins 31 | .flutter-plugins-dependencies 32 | .packages 33 | .pub-cache/ 34 | .pub/ 35 | /build/ 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "2663184aa79047d0a33a14a3b607954f8fdd8730" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 17 | base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 18 | - platform: android 19 | create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 20 | base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 21 | 22 | # User provided section 23 | 24 | # List of Local paths (relative to this file) that should be 25 | # ignored by the migrate tool. 26 | # 27 | # Files that are not part of the templates will be ignored by default. 28 | unmanaged_files: 29 | - 'lib/main.dart' 30 | - 'ios/Runner.xcodeproj/project.pbxproj' 31 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # example 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) 13 | 14 | For help getting started with Flutter development, view the 15 | [online documentation](https://docs.flutter.dev/), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | analyzer: 13 | exclude: [build/**] 14 | 15 | linter: 16 | # The lint rules applied to this project can be customized in the 17 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 18 | # included above or to enable additional rules. A list of all available lints 19 | # and their documentation is published at 20 | # https://dart-lang.github.io/linter/lints/index.html. 21 | # 22 | # Instead of disabling a lint rule for the entire project in the 23 | # section below, it can also be suppressed for a single line of code 24 | # or a specific dart file by using the `// ignore: name_of_lint` and 25 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 26 | # producing the lint. 27 | rules: 28 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 29 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 30 | 31 | # Additional information about this file can be found at 32 | # https://dart.dev/guides/language/analysis-options 33 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/to/reference-keystore 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. 5 | id "dev.flutter.flutter-gradle-plugin" 6 | } 7 | 8 | android { 9 | namespace = "com.example.example" 10 | compileSdk = flutter.compileSdkVersion 11 | ndkVersion = flutter.ndkVersion 12 | 13 | compileOptions { 14 | sourceCompatibility = JavaVersion.VERSION_1_8 15 | targetCompatibility = JavaVersion.VERSION_1_8 16 | } 17 | 18 | kotlinOptions { 19 | jvmTarget = JavaVersion.VERSION_1_8 20 | } 21 | 22 | defaultConfig { 23 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 24 | applicationId = "com.example.example" 25 | // You can update the following values to match your application needs. 26 | // For more information, see: https://flutter.dev/to/review-gradle-config. 27 | minSdk = flutter.minSdkVersion 28 | targetSdk = flutter.targetSdkVersion 29 | versionCode = flutter.versionCode 30 | versionName = flutter.versionName 31 | } 32 | 33 | buildTypes { 34 | release { 35 | // TODO: Add your own signing config for the release build. 36 | // Signing with the debug keys for now, so `flutter run --release` works. 37 | signingConfig = signingConfigs.debug 38 | } 39 | } 40 | } 41 | 42 | flutter { 43 | source = "../.." 44 | } 45 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 15 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 33 | 34 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/example/example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() 6 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/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/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/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/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/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/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/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/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | rootProject.buildDir = "../build" 9 | subprojects { 10 | project.buildDir = "${rootProject.buildDir}/${project.name}" 11 | } 12 | subprojects { 13 | project.evaluationDependsOn(":app") 14 | } 15 | 16 | tasks.register("clean", Delete) { 17 | delete rootProject.buildDir 18 | } 19 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip 6 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | }() 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 21 | id "com.android.application" version "8.1.0" apply false 22 | id "org.jetbrains.kotlin.android" version "1.8.22" apply false 23 | } 24 | 25 | include ":app" 26 | -------------------------------------------------------------------------------- /example/assets/fonts/RedditMono-VariableFont_wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/example/assets/fonts/RedditMono-VariableFont_wght.ttf -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '12.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 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | target 'RunnerTests' do 36 | inherit! :search_paths 37 | end 38 | end 39 | 40 | post_install do |installer| 41 | installer.pods_project.targets.each do |target| 42 | flutter_additional_ios_build_settings(target) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - package_info_plus (0.4.5): 4 | - Flutter 5 | 6 | DEPENDENCIES: 7 | - Flutter (from `Flutter`) 8 | - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) 9 | 10 | EXTERNAL SOURCES: 11 | Flutter: 12 | :path: Flutter 13 | package_info_plus: 14 | :path: ".symlinks/plugins/package_info_plus/ios" 15 | 16 | SPEC CHECKSUMS: 17 | Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 18 | package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c 19 | 20 | PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796 21 | 22 | COCOAPODS: 1.15.2 23 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 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.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | 4 | @main 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/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/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/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/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/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/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/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/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/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/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/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/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/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/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/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/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/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/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/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/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/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/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/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/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/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/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/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/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/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/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/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Example 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | example 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | CADisableMinimumFrameDurationOnPhone 45 | 46 | UIApplicationSupportsIndirectInputEvents 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /example/lib/extensions/int_ext.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/utils.dart'; 2 | 3 | extension IntegerExtensions on int { 4 | int get digitCount => (log10(this) + 1).truncate(); 5 | } 6 | -------------------------------------------------------------------------------- /example/lib/file_asset_paths.dart: -------------------------------------------------------------------------------- 1 | enum FileAssetPaths { 2 | basicScreen( 3 | 'lib/screens/basic/basic_example_screen.dart', 4 | ), 5 | controllerListenScreen( 6 | 'lib/screens/controller_listen/controller_listen_example_screen.dart', 7 | ), 8 | controllerSetSizesScreen( 9 | 'lib/screens/controller_set_sizes/controller_set_sizes_example_screen.dart', 10 | ), 11 | dividerScreen( 12 | 'lib/screens/divider/custom_divider_example_screen.dart', 13 | ), 14 | pixelsScreen( 15 | 'lib/screens/pixels/pixels_example_screen.dart', 16 | ), 17 | ratioScreen( 18 | 'lib/screens/ratio/ratio_example_screen.dart', 19 | ), 20 | shrinkAndFlexScreen( 21 | 'lib/screens/shrink_and_flex/shrink_and_flex_example_screen.dart', 22 | ), 23 | cascadingDeltaScreen( 24 | 'lib/screens/cascading_delta/cascading_delta_screen.dart', 25 | ), 26 | ; 27 | 28 | const FileAssetPaths(this.path); 29 | final String path; 30 | } 31 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/screens/basic/basic_example_screen.dart'; 2 | import 'package:example/screens/cascading_delta/cascading_delta_screen.dart'; 3 | import 'package:example/screens/controller_listen/controller_listen_example_screen.dart'; 4 | import 'package:example/screens/controller_set_sizes/controller_set_sizes_example_screen.dart'; 5 | import 'package:example/screens/divider/custom_divider_example_screen.dart'; 6 | import 'package:example/screens/pixels/pixels_example_screen.dart'; 7 | import 'package:example/screens/ratio/ratio_example_screen.dart'; 8 | import 'package:example/screens/shrink_and_flex/future_builder_shrink_example_screen.dart'; 9 | import 'package:example/screens/shrink_and_flex/shrink_and_flex_example_screen.dart'; 10 | import 'package:flutter/material.dart'; 11 | 12 | void main() { 13 | runApp(const ExampleApp()); 14 | } 15 | 16 | class ExampleApp extends StatelessWidget { 17 | const ExampleApp({super.key}); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return MaterialApp( 22 | theme: ThemeData.light(useMaterial3: true), 23 | debugShowCheckedModeBanner: false, 24 | initialRoute: 'basic', 25 | routes: { 26 | 'basic': (context) => const BasicExampleScreen(), 27 | 'ratio': (context) => const RatioExampleScreen(), 28 | 'pixels': (context) => const PixelsExampleScreen(), 29 | 'listen': (context) => const ControllerListenExampleScreen(), 30 | 'sizes': (context) => const ControllerSetSizesExampleScreen(), 31 | 'divider': (context) => const CustomDividerExampleScreen(), 32 | 'shrink': (context) => const ShrinkAndFlexExampleScreen(), 33 | 'future-builder-shrink': (context) => 34 | const FutureBuilderShrinkExampleScreen(), 35 | 'cascading-delta': (context) => const CascadingDeltaScreen(), 36 | }, 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /example/lib/screens/basic/basic_example_help_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/widgets/help_dialog.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class BasicExampleHelpDialog extends StatelessWidget { 5 | const BasicExampleHelpDialog._({super.key}); 6 | 7 | static void show({ 8 | required BuildContext context, 9 | }) { 10 | HelpDialog.show( 11 | context: context, 12 | child: const BasicExampleHelpDialog._( 13 | key: Key('BasicExampleHelpDialog'), 14 | ), 15 | ); 16 | } 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return const HelpDialog( 21 | title: 'About this example', 22 | content: Column( 23 | mainAxisSize: MainAxisSize.min, 24 | crossAxisAlignment: CrossAxisAlignment.start, 25 | children: [ 26 | Text( 27 | 'In this example, there are two children.', 28 | ), 29 | Text.rich(TextSpan( 30 | children: [ 31 | TextSpan(text: 'Each child uses the default '), 32 | TextSpan( 33 | text: 'ResizableSize.expand()', 34 | style: TextStyle( 35 | fontWeight: FontWeight.bold, 36 | ), 37 | ), 38 | TextSpan(text: ' attribute.'), 39 | ], 40 | )), 41 | SizedBox(height: 15), 42 | Text( 43 | 'This results in two areas of equal width that can be adjusted freely.', 44 | ), 45 | ], 46 | ), 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /example/lib/screens/basic/basic_example_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/file_asset_paths.dart'; 2 | import 'package:example/screens/basic/basic_example_help_dialog.dart'; 3 | import 'package:example/widgets/code_view_dialog.dart'; 4 | import 'package:example/widgets/nav_drawer.dart'; 5 | import 'package:example/widgets/size_label.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:flutter_resizable_container/flutter_resizable_container.dart'; 8 | 9 | class BasicExampleScreen extends StatelessWidget { 10 | const BasicExampleScreen({super.key}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Scaffold( 15 | appBar: AppBar( 16 | title: const Text('Basic two-pane example'), 17 | actions: [ 18 | IconButton( 19 | icon: const Icon(Icons.help_center), 20 | onPressed: () => BasicExampleHelpDialog.show(context: context), 21 | ), 22 | IconButton( 23 | icon: const Icon(Icons.code), 24 | onPressed: () => CodeViewDialog.show( 25 | context: context, 26 | filePath: FileAssetPaths.basicScreen, 27 | ), 28 | ), 29 | ], 30 | ), 31 | drawer: const NavDrawer(), 32 | body: ResizableContainer( 33 | direction: Axis.horizontal, 34 | children: [ 35 | ResizableChild( 36 | child: ColoredBox( 37 | color: Theme.of(context).colorScheme.primaryContainer, 38 | child: const SizeLabel(), 39 | ), 40 | ), 41 | ResizableChild( 42 | child: ColoredBox( 43 | color: Theme.of(context).colorScheme.secondaryContainer, 44 | child: const SizeLabel(), 45 | ), 46 | ), 47 | ], 48 | ), 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /example/lib/screens/cascading_delta/cascading_delta_help_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/widgets/help_dialog.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class CascadingDeltaHelpDialog extends StatelessWidget { 5 | const CascadingDeltaHelpDialog._({super.key}); 6 | 7 | static void show({ 8 | required BuildContext context, 9 | }) { 10 | HelpDialog.show( 11 | context: context, 12 | child: const CascadingDeltaHelpDialog._( 13 | key: Key('CascadingDeltaHelpDialog'), 14 | ), 15 | ); 16 | } 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return const HelpDialog( 21 | title: 'About this example', 22 | content: Column( 23 | mainAxisSize: MainAxisSize.min, 24 | crossAxisAlignment: CrossAxisAlignment.start, 25 | children: [ 26 | Text( 27 | 'In this example, there are four children, each with a minimum width of 50 pixels.', 28 | ), 29 | SizedBox(height: 12), 30 | Text.rich(TextSpan(children: [ 31 | TextSpan(text: 'Use the '), 32 | TextSpan( 33 | text: 'Cascade', 34 | style: TextStyle(fontWeight: FontWeight.bold), 35 | ), 36 | TextSpan(text: ' switch to enable/disable the '), 37 | TextSpan( 38 | text: 'cascadeNegativeDelta', 39 | style: TextStyle( 40 | fontFamily: 'Monospace', 41 | color: Colors.blueGrey, 42 | ), 43 | ), 44 | TextSpan(text: ' flag in the ResizableContainer.'), 45 | ])), 46 | SizedBox(height: 12), 47 | Text.rich(TextSpan(children: [ 48 | TextSpan(text: 'With the switch '), 49 | TextSpan( 50 | text: 'enabled', 51 | style: TextStyle(fontWeight: FontWeight.bold), 52 | ), 53 | TextSpan( 54 | text: 55 | ', reducing the size of a child beyond its bound will "cascade" the change to its sibling(s).', 56 | ), 57 | TextSpan(text: ' With the switch '), 58 | TextSpan( 59 | text: 'disabled', 60 | style: TextStyle(fontWeight: FontWeight.bold), 61 | ), 62 | TextSpan( 63 | text: 64 | ', reducing the size of a child beyond its bound will have no effect (default behavior).', 65 | ), 66 | ])), 67 | ], 68 | ), 69 | ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /example/lib/screens/cascading_delta/cascading_delta_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/file_asset_paths.dart'; 2 | import 'package:example/screens/cascading_delta/cascading_delta_help_dialog.dart'; 3 | import 'package:example/widgets/code_view_dialog.dart'; 4 | import 'package:example/widgets/nav_drawer.dart'; 5 | import 'package:example/widgets/size_label.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:flutter_resizable_container/flutter_resizable_container.dart'; 8 | 9 | class CascadingDeltaScreen extends StatefulWidget { 10 | const CascadingDeltaScreen({super.key}); 11 | 12 | @override 13 | State createState() => _CascadingDeltaScreenState(); 14 | } 15 | 16 | class _CascadingDeltaScreenState extends State { 17 | var cascade = false; 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return Scaffold( 22 | appBar: AppBar( 23 | title: const Text('Cascading delta example'), 24 | actions: [ 25 | Row( 26 | children: [ 27 | const Text('Cascade'), 28 | const SizedBox(width: 8), 29 | Switch( 30 | value: cascade, 31 | onChanged: (cascade) => setState(() => this.cascade = cascade), 32 | ), 33 | ], 34 | ), 35 | IconButton( 36 | icon: const Icon(Icons.help_center), 37 | onPressed: () => CascadingDeltaHelpDialog.show(context: context), 38 | ), 39 | IconButton( 40 | icon: const Icon(Icons.code), 41 | onPressed: () => CodeViewDialog.show( 42 | context: context, 43 | filePath: FileAssetPaths.cascadingDeltaScreen, 44 | ), 45 | ), 46 | ], 47 | ), 48 | drawer: const NavDrawer(), 49 | body: ResizableContainer( 50 | direction: Axis.horizontal, 51 | cascadeNegativeDelta: cascade, 52 | children: [ 53 | ResizableChild( 54 | size: const ResizableSize.expand(min: 50), 55 | child: ColoredBox( 56 | color: Theme.of(context).colorScheme.primaryContainer, 57 | child: const SizeLabel(), 58 | ), 59 | ), 60 | ResizableChild( 61 | size: const ResizableSize.expand(min: 50), 62 | child: ColoredBox( 63 | color: Theme.of(context).colorScheme.secondaryContainer, 64 | child: const SizeLabel(), 65 | ), 66 | ), 67 | ResizableChild( 68 | size: const ResizableSize.expand(min: 50), 69 | child: ColoredBox( 70 | color: Theme.of(context).colorScheme.primaryContainer, 71 | child: const SizeLabel(), 72 | ), 73 | ), 74 | ResizableChild( 75 | size: const ResizableSize.expand(min: 50), 76 | child: ColoredBox( 77 | color: Theme.of(context).colorScheme.secondaryContainer, 78 | child: const SizeLabel(), 79 | ), 80 | ), 81 | ], 82 | ), 83 | ); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /example/lib/screens/controller_listen/controller_listen_example_help_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/widgets/help_dialog.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class ControllerListenExampleHelpDialog extends StatelessWidget { 5 | const ControllerListenExampleHelpDialog._({super.key}); 6 | 7 | static void show({ 8 | required BuildContext context, 9 | }) { 10 | HelpDialog.show( 11 | context: context, 12 | child: const ControllerListenExampleHelpDialog._( 13 | key: Key('ControllerListenExampleHelpDialog'), 14 | ), 15 | ); 16 | } 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return const HelpDialog( 21 | title: 'About this example', 22 | content: Column( 23 | mainAxisSize: MainAxisSize.min, 24 | crossAxisAlignment: CrossAxisAlignment.start, 25 | children: [ 26 | Text.rich(TextSpan( 27 | children: [ 28 | TextSpan( 29 | text: 'In this example, we have retained a reference to the', 30 | ), 31 | TextSpan( 32 | text: ' ResizableController ', 33 | style: TextStyle( 34 | fontWeight: FontWeight.bold, 35 | ), 36 | ), 37 | TextSpan( 38 | text: 39 | 'in our StatefulWidget. This allows us to add a listener to the controller and react to any changes in our business logic.', 40 | ), 41 | ], 42 | )), 43 | SizedBox(height: 15), 44 | Text( 45 | 'In this example, we have set up a listener that extracts the sizes of the children and updates the label at the bottom of the screen.', 46 | ), 47 | ], 48 | ), 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /example/lib/screens/controller_listen/controller_listen_example_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/file_asset_paths.dart'; 2 | import 'package:example/screens/controller_listen/controller_listen_example_help_dialog.dart'; 3 | import 'package:example/widgets/code_view_dialog.dart'; 4 | import 'package:example/widgets/nav_drawer.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_resizable_container/flutter_resizable_container.dart'; 7 | 8 | class ControllerListenExampleScreen extends StatefulWidget { 9 | const ControllerListenExampleScreen({super.key}); 10 | 11 | @override 12 | State createState() => 13 | _ControllerListenExampleScreenState(); 14 | } 15 | 16 | class _ControllerListenExampleScreenState 17 | extends State { 18 | final controller = ResizableController(); 19 | 20 | double? leftWidth; 21 | double? rightWidth; 22 | 23 | @override 24 | void initState() { 25 | super.initState(); 26 | controller.addListener(() { 27 | final sizes = controller.pixels; 28 | setState(() { 29 | leftWidth = sizes.first; 30 | rightWidth = sizes.last; 31 | }); 32 | }); 33 | } 34 | 35 | @override 36 | void dispose() { 37 | controller.dispose(); 38 | super.dispose(); 39 | } 40 | 41 | @override 42 | Widget build(BuildContext context) { 43 | return Scaffold( 44 | appBar: AppBar( 45 | title: const Text('Controller listen example'), 46 | actions: [ 47 | IconButton( 48 | onPressed: () => ControllerListenExampleHelpDialog.show( 49 | context: context, 50 | ), 51 | icon: const Icon(Icons.help_center), 52 | ), 53 | IconButton( 54 | onPressed: () => CodeViewDialog.show( 55 | context: context, 56 | filePath: FileAssetPaths.controllerListenScreen, 57 | ), 58 | icon: const Icon(Icons.code), 59 | ), 60 | ], 61 | ), 62 | drawer: const NavDrawer(), 63 | body: Column( 64 | children: [ 65 | Expanded( 66 | child: ResizableContainer( 67 | controller: controller, 68 | direction: Axis.horizontal, 69 | children: [ 70 | ResizableChild( 71 | child: ColoredBox( 72 | color: Theme.of(context).colorScheme.primaryContainer, 73 | child: const Center(child: Text('Left')), 74 | ), 75 | ), 76 | ResizableChild( 77 | child: ColoredBox( 78 | color: Theme.of(context).colorScheme.tertiaryContainer, 79 | child: const Center(child: Text('Right')), 80 | ), 81 | ), 82 | ], 83 | ), 84 | ), 85 | Builder(builder: (context) { 86 | final left = leftWidth; 87 | final right = rightWidth; 88 | 89 | if (left == null || right == null) { 90 | return const SizedBox.shrink(); 91 | } 92 | 93 | final leftLabel = left.toStringAsFixed(2); 94 | final rightLabel = right.toStringAsFixed(2); 95 | final totalSpace = left + right; 96 | final totalSpaceLabel = totalSpace.toStringAsFixed(2); 97 | 98 | return Text( 99 | 'Left: $leftLabel | Right: $rightLabel | Total: $totalSpaceLabel', 100 | ); 101 | }), 102 | ], 103 | ), 104 | ); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /example/lib/screens/controller_set_sizes/controller_set_sizes_example_help_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/widgets/help_dialog.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class ControllerSetSizesExampleHelpDialog extends StatelessWidget { 5 | const ControllerSetSizesExampleHelpDialog._({super.key}); 6 | 7 | static void show({ 8 | required BuildContext context, 9 | }) { 10 | HelpDialog.show( 11 | context: context, 12 | child: const ControllerSetSizesExampleHelpDialog._( 13 | key: Key('ControllerSetSizesExampleHelpDialog'), 14 | ), 15 | ); 16 | } 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return const HelpDialog( 21 | title: 'About this example', 22 | content: Column( 23 | mainAxisSize: MainAxisSize.min, 24 | crossAxisAlignment: CrossAxisAlignment.start, 25 | children: [ 26 | Text.rich(TextSpan( 27 | children: [ 28 | TextSpan(text: 'This example retains a reference to the '), 29 | TextSpan( 30 | text: 'ResizableController', 31 | style: TextStyle( 32 | fontWeight: FontWeight.bold, 33 | ), 34 | ), 35 | TextSpan(text: ' for the purpose of using the '), 36 | TextSpan( 37 | text: 'setSizes', 38 | style: TextStyle( 39 | fontWeight: FontWeight.bold, 40 | ), 41 | ), 42 | TextSpan(text: ' method.'), 43 | ], 44 | )), 45 | SizedBox(height: 15), 46 | Text.rich(TextSpan( 47 | children: [ 48 | TextSpan( 49 | text: 50 | 'Using the menu button in the app bar, you can programmatically set the sizes of each of the children using ', 51 | ), 52 | TextSpan( 53 | text: 'ratios', 54 | style: TextStyle(fontWeight: FontWeight.bold), 55 | ), 56 | TextSpan(text: ', '), 57 | TextSpan( 58 | text: 'pixels', 59 | style: TextStyle(fontWeight: FontWeight.bold), 60 | ), 61 | TextSpan(text: ', or '), 62 | TextSpan( 63 | text: 'expand/flex', 64 | style: TextStyle(fontWeight: FontWeight.bold), 65 | ), 66 | TextSpan(text: '.'), 67 | ], 68 | )), 69 | SizedBox(height: 15), 70 | Text.rich(TextSpan( 71 | children: [ 72 | TextSpan(text: 'The '), 73 | TextSpan( 74 | text: 'ratios', 75 | style: TextStyle(fontWeight: FontWeight.bold), 76 | ), 77 | TextSpan( 78 | text: ' setter will assign the children with ratios of ', 79 | ), 80 | TextSpan( 81 | text: '0.5', 82 | style: TextStyle(fontWeight: FontWeight.bold), 83 | ), 84 | TextSpan(text: ', '), 85 | TextSpan( 86 | text: '0.2', 87 | style: TextStyle(fontWeight: FontWeight.bold), 88 | ), 89 | TextSpan(text: ', and '), 90 | TextSpan( 91 | text: '0.3', 92 | style: TextStyle(fontWeight: FontWeight.bold), 93 | ), 94 | TextSpan(text: '.'), 95 | ], 96 | )), 97 | SizedBox(height: 15), 98 | Text.rich(TextSpan( 99 | children: [ 100 | TextSpan(text: 'The '), 101 | TextSpan( 102 | text: 'pixels', 103 | style: TextStyle(fontWeight: FontWeight.bold), 104 | ), 105 | TextSpan(text: ' setter will use '), 106 | TextSpan( 107 | text: 'MediaQuery.sizeOf(context)', 108 | style: TextStyle(fontWeight: FontWeight.bold), 109 | ), 110 | TextSpan( 111 | text: 112 | ' to measure the available space, then assign the children with ', 113 | ), 114 | TextSpan( 115 | text: '1/4', 116 | style: TextStyle(fontWeight: FontWeight.bold), 117 | ), 118 | TextSpan(text: ', '), 119 | TextSpan( 120 | text: '1/2', 121 | style: TextStyle(fontWeight: FontWeight.bold), 122 | ), 123 | TextSpan(text: ', and '), 124 | TextSpan( 125 | text: '1/4', 126 | style: TextStyle(fontWeight: FontWeight.bold), 127 | ), 128 | TextSpan(text: ' of the width.'), 129 | ], 130 | )), 131 | SizedBox(height: 15), 132 | Text.rich(TextSpan( 133 | children: [ 134 | TextSpan(text: 'The '), 135 | TextSpan( 136 | text: 'flex', 137 | style: TextStyle(fontWeight: FontWeight.bold), 138 | ), 139 | TextSpan( 140 | text: ' setter will assign the children with flex values of ', 141 | ), 142 | TextSpan( 143 | text: '1', 144 | style: TextStyle(fontWeight: FontWeight.bold), 145 | ), 146 | TextSpan(text: ', '), 147 | TextSpan( 148 | text: '3', 149 | style: TextStyle(fontWeight: FontWeight.bold), 150 | ), 151 | TextSpan(text: ', and '), 152 | TextSpan( 153 | text: '2', 154 | style: TextStyle(fontWeight: FontWeight.bold), 155 | ), 156 | TextSpan(text: '.'), 157 | ], 158 | )), 159 | ], 160 | ), 161 | ); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /example/lib/screens/controller_set_sizes/controller_set_sizes_example_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/file_asset_paths.dart'; 2 | import 'package:example/screens/controller_set_sizes/controller_set_sizes_example_help_dialog.dart'; 3 | import 'package:example/widgets/code_view_dialog.dart'; 4 | import 'package:example/widgets/nav_drawer.dart'; 5 | import 'package:example/widgets/size_label.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:flutter_resizable_container/flutter_resizable_container.dart'; 8 | 9 | class ControllerSetSizesExampleScreen extends StatefulWidget { 10 | const ControllerSetSizesExampleScreen({super.key}); 11 | 12 | @override 13 | State createState() => 14 | _ControllerSetSizesExampleScreenState(); 15 | } 16 | 17 | class _ControllerSetSizesExampleScreenState 18 | extends State { 19 | final controller = ResizableController(); 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | return Scaffold( 24 | appBar: AppBar( 25 | title: const Text('Controller set sizes example'), 26 | actions: [ 27 | PopupMenuButton( 28 | color: Theme.of(context).colorScheme.primaryContainer, 29 | itemBuilder: (context) { 30 | return const [ 31 | PopupMenuItem( 32 | value: 'ratios', 33 | child: Text('Ratios'), 34 | ), 35 | PopupMenuItem( 36 | value: 'pixels', 37 | child: Text('Pixels'), 38 | ), 39 | PopupMenuItem( 40 | value: 'flex', 41 | child: Text('Flex'), 42 | ), 43 | ]; 44 | }, 45 | onSelected: (code) { 46 | switch (code) { 47 | case 'ratios': 48 | controller.setSizes(const [ 49 | ResizableSize.ratio(0.5), 50 | ResizableSize.ratio(0.2), 51 | ResizableSize.ratio(0.3), 52 | ]); 53 | case 'pixels': 54 | const numDividers = 2; 55 | const dividerWidth = 2; 56 | final availableSpace = MediaQuery.sizeOf(context).width - 57 | (numDividers * dividerWidth); 58 | final quarterSpace = availableSpace / 4; 59 | controller.setSizes([ 60 | ResizableSize.pixels(quarterSpace), 61 | ResizableSize.pixels(quarterSpace * 2), 62 | ResizableSize.pixels(quarterSpace), 63 | ]); 64 | case 'flex': 65 | controller.setSizes(const [ 66 | ResizableSize.expand(), 67 | ResizableSize.expand(flex: 3), 68 | ResizableSize.expand(flex: 2), 69 | ]); 70 | } 71 | }, 72 | icon: const Icon(Icons.shape_line), 73 | ), 74 | IconButton( 75 | onPressed: () => ControllerSetSizesExampleHelpDialog.show( 76 | context: context, 77 | ), 78 | icon: const Icon(Icons.help_center), 79 | ), 80 | IconButton( 81 | onPressed: () => CodeViewDialog.show( 82 | context: context, 83 | filePath: FileAssetPaths.controllerSetSizesScreen, 84 | ), 85 | icon: const Icon(Icons.code), 86 | ), 87 | ], 88 | ), 89 | drawer: const NavDrawer(), 90 | body: ResizableContainer( 91 | controller: controller, 92 | direction: Axis.horizontal, 93 | children: [ 94 | ResizableChild( 95 | child: ColoredBox( 96 | color: Theme.of(context).colorScheme.primaryContainer, 97 | child: const SizeLabel(), 98 | ), 99 | ), 100 | ResizableChild( 101 | child: ColoredBox( 102 | color: Theme.of(context).colorScheme.tertiaryContainer, 103 | child: const SizeLabel(), 104 | ), 105 | ), 106 | ResizableChild( 107 | child: ColoredBox( 108 | color: Theme.of(context).colorScheme.surfaceContainer, 109 | child: const SizeLabel(), 110 | ), 111 | ), 112 | ], 113 | ), 114 | ); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /example/lib/screens/divider/custom_divider_example_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/file_asset_paths.dart'; 2 | import 'package:example/widgets/code_view_dialog.dart'; 3 | import 'package:example/widgets/nav_drawer.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_resizable_container/flutter_resizable_container.dart'; 6 | 7 | class CustomDividerExampleScreen extends StatefulWidget { 8 | const CustomDividerExampleScreen({super.key}); 9 | 10 | @override 11 | State createState() => 12 | _CustomDividerExampleScreenState(); 13 | } 14 | 15 | class _CustomDividerExampleScreenState 16 | extends State { 17 | var hovered = false; 18 | var length = 0.5; 19 | var thickness = 2.0; 20 | var padding = 5.0; 21 | var crossAxisAlignment = CrossAxisAlignment.center; 22 | var mainAxisAlignment = MainAxisAlignment.center; 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | return Scaffold( 27 | appBar: AppBar( 28 | title: const Text('Custom divider example'), 29 | centerTitle: false, 30 | actions: [ 31 | IconButton( 32 | onPressed: () => CodeViewDialog.show( 33 | context: context, 34 | filePath: FileAssetPaths.dividerScreen, 35 | ), 36 | icon: const Icon(Icons.code), 37 | ), 38 | ], 39 | ), 40 | drawer: const NavDrawer(), 41 | body: Column( 42 | children: [ 43 | Wrap( 44 | alignment: WrapAlignment.start, 45 | crossAxisAlignment: WrapCrossAlignment.center, 46 | runSpacing: 18, 47 | spacing: 18, 48 | children: [ 49 | SizedBox( 50 | width: 200, 51 | child: Column( 52 | crossAxisAlignment: CrossAxisAlignment.center, 53 | children: [ 54 | const Text('Length'), 55 | Slider( 56 | min: 0.01, 57 | max: 1.0, 58 | value: length, 59 | onChanged: (value) => setState(() => length = value), 60 | ), 61 | Text('Ratio: ${(length * 100).toStringAsFixed((2))}%'), 62 | ], 63 | ), 64 | ), 65 | SizedBox( 66 | width: 200, 67 | child: Column( 68 | crossAxisAlignment: CrossAxisAlignment.center, 69 | children: [ 70 | const Text('Thickness'), 71 | Slider( 72 | min: 1, 73 | max: 20.0, 74 | divisions: 19, 75 | value: thickness, 76 | onChanged: (value) => setState(() => thickness = value), 77 | ), 78 | Text('${thickness}px'), 79 | ], 80 | ), 81 | ), 82 | SizedBox( 83 | width: 200, 84 | child: Column( 85 | crossAxisAlignment: CrossAxisAlignment.center, 86 | children: [ 87 | const Text('Padding'), 88 | Slider( 89 | min: 0, 90 | max: 20, 91 | divisions: 20, 92 | value: padding, 93 | onChanged: (value) => setState(() => padding = value), 94 | ), 95 | Text('${padding}px'), 96 | ], 97 | ), 98 | ), 99 | SizedBox( 100 | width: 200, 101 | child: Column( 102 | crossAxisAlignment: CrossAxisAlignment.center, 103 | children: [ 104 | const Text('Cross-Axis Alignment'), 105 | DropdownButton( 106 | value: crossAxisAlignment, 107 | items: const [ 108 | DropdownMenuItem( 109 | value: CrossAxisAlignment.start, 110 | child: Text('Start'), 111 | ), 112 | DropdownMenuItem( 113 | value: CrossAxisAlignment.center, 114 | child: Text('Center'), 115 | ), 116 | DropdownMenuItem( 117 | value: CrossAxisAlignment.end, 118 | child: Text('End'), 119 | ), 120 | ], 121 | onChanged: (value) => setState( 122 | () => crossAxisAlignment = value!, 123 | ), 124 | ), 125 | ], 126 | ), 127 | ), 128 | SizedBox( 129 | width: 200, 130 | child: Column( 131 | crossAxisAlignment: CrossAxisAlignment.center, 132 | children: [ 133 | const Text('Main-Axis Alignment'), 134 | DropdownButton( 135 | value: mainAxisAlignment, 136 | items: const [ 137 | DropdownMenuItem( 138 | value: MainAxisAlignment.start, 139 | child: Text('Start'), 140 | ), 141 | DropdownMenuItem( 142 | value: MainAxisAlignment.center, 143 | child: Text('Center'), 144 | ), 145 | DropdownMenuItem( 146 | value: MainAxisAlignment.end, 147 | child: Text('End'), 148 | ), 149 | ], 150 | onChanged: (value) => setState( 151 | () => mainAxisAlignment = value!, 152 | ), 153 | ), 154 | ], 155 | ), 156 | ), 157 | ], 158 | ), 159 | const SizedBox(height: 10), 160 | Expanded( 161 | child: ResizableContainer( 162 | direction: Axis.horizontal, 163 | children: [ 164 | ResizableChild( 165 | divider: ResizableDivider( 166 | color: hovered 167 | ? Theme.of(context).colorScheme.primary 168 | : Theme.of(context).colorScheme.inversePrimary, 169 | thickness: thickness, 170 | padding: padding, 171 | crossAxisAlignment: crossAxisAlignment, 172 | mainAxisAlignment: mainAxisAlignment, 173 | length: ResizableSize.ratio(length), 174 | onHoverEnter: () => setState(() => hovered = true), 175 | onHoverExit: () => setState(() => hovered = false), 176 | onTapDown: () => setState(() => hovered = true), 177 | onTapUp: () => setState(() => hovered = false), 178 | ), 179 | child: ColoredBox( 180 | color: Theme.of(context).colorScheme.primaryContainer, 181 | child: const Center(child: Text('Left')), 182 | ), 183 | ), 184 | ResizableChild( 185 | child: ColoredBox( 186 | color: Theme.of(context).colorScheme.tertiaryContainer, 187 | child: const Center(child: Text('Right')), 188 | ), 189 | ), 190 | ], 191 | ), 192 | ), 193 | ], 194 | ), 195 | ); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /example/lib/screens/pixels/pixels_example_help_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/widgets/help_dialog.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class PixelsExampleHelpDialog extends StatelessWidget { 5 | const PixelsExampleHelpDialog._({super.key}); 6 | 7 | static void show({ 8 | required BuildContext context, 9 | }) { 10 | HelpDialog.show( 11 | context: context, 12 | child: const PixelsExampleHelpDialog._( 13 | key: Key('PixelsExampleHelpDialog'), 14 | ), 15 | ); 16 | } 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return const HelpDialog( 21 | title: 'About this example', 22 | content: Column( 23 | mainAxisSize: MainAxisSize.min, 24 | crossAxisAlignment: CrossAxisAlignment.start, 25 | children: [ 26 | Text( 27 | 'In this example, the first child (left) has been given an absolute size of 333 logical pixels.', 28 | ), 29 | SizedBox(height: 15), 30 | Text( 31 | 'This is different from the other two size options, which both use proportions of the available space.', 32 | ), 33 | SizedBox(height: 15), 34 | Text.rich(TextSpan( 35 | children: [ 36 | TextSpan(text: 'The second child uses the default'), 37 | TextSpan( 38 | text: ' ResizableSize.expand() ', 39 | style: TextStyle( 40 | fontWeight: FontWeight.bold, 41 | ), 42 | ), 43 | TextSpan( 44 | text: 'and has been given the remaining available space.', 45 | ), 46 | ], 47 | )), 48 | ], 49 | ), 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /example/lib/screens/pixels/pixels_example_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/file_asset_paths.dart'; 2 | import 'package:example/screens/pixels/pixels_example_help_dialog.dart'; 3 | import 'package:example/widgets/code_view_dialog.dart'; 4 | import 'package:example/widgets/nav_drawer.dart'; 5 | import 'package:example/widgets/size_label.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:flutter_resizable_container/flutter_resizable_container.dart'; 8 | 9 | class PixelsExampleScreen extends StatelessWidget { 10 | const PixelsExampleScreen({super.key}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Scaffold( 15 | appBar: AppBar( 16 | title: const Text('Pixels example'), 17 | actions: [ 18 | IconButton( 19 | onPressed: () => PixelsExampleHelpDialog.show(context: context), 20 | icon: const Icon(Icons.help_center), 21 | ), 22 | IconButton( 23 | onPressed: () => CodeViewDialog.show( 24 | context: context, 25 | filePath: FileAssetPaths.pixelsScreen, 26 | ), 27 | icon: const Icon(Icons.code), 28 | ), 29 | ], 30 | ), 31 | drawer: const NavDrawer(), 32 | body: ResizableContainer( 33 | direction: Axis.horizontal, 34 | children: [ 35 | ResizableChild( 36 | size: const ResizableSize.pixels(333), 37 | child: ColoredBox( 38 | color: Theme.of(context).colorScheme.primaryContainer, 39 | child: const SizeLabel(), 40 | ), 41 | ), 42 | ResizableChild( 43 | child: ColoredBox( 44 | color: Theme.of(context).colorScheme.tertiaryContainer, 45 | child: const SizeLabel(), 46 | ), 47 | ), 48 | ], 49 | ), 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /example/lib/screens/ratio/ratio_example_help_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/widgets/help_dialog.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class RatioExampleHelpDialog extends StatelessWidget { 5 | const RatioExampleHelpDialog._({super.key}); 6 | 7 | static void show({ 8 | required BuildContext context, 9 | }) { 10 | HelpDialog.show( 11 | context: context, 12 | child: const RatioExampleHelpDialog._( 13 | key: Key('RatioExampleHelpDialog'), 14 | ), 15 | ); 16 | } 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return const HelpDialog( 21 | title: 'About this example', 22 | content: Column( 23 | mainAxisSize: MainAxisSize.min, 24 | children: [ 25 | Text.rich(TextSpan( 26 | children: [ 27 | TextSpan( 28 | text: 'In this example, the two outer children are using a', 29 | ), 30 | TextSpan( 31 | text: ' ResizableSize.ratio(0.15), ', 32 | style: TextStyle( 33 | fontWeight: FontWeight.bold, 34 | ), 35 | ), 36 | TextSpan(text: 'while the center child is using a'), 37 | TextSpan( 38 | text: ' ResizableSize.expand()', 39 | style: TextStyle( 40 | fontWeight: FontWeight.bold, 41 | ), 42 | ), 43 | TextSpan(text: '.'), 44 | ], 45 | )), 46 | SizedBox(height: 15), 47 | Text.rich(TextSpan( 48 | children: [ 49 | TextSpan( 50 | text: 51 | 'This gives the outer children a fixed starting size of 15% of the total available width and then allocates', 52 | ), 53 | TextSpan( 54 | text: ' all ', 55 | style: TextStyle(fontStyle: FontStyle.italic), 56 | ), 57 | TextSpan( 58 | text: 59 | 'of the remaining space, 70% of the total width, to the middle child.', 60 | ), 61 | ], 62 | )), 63 | ], 64 | ), 65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /example/lib/screens/ratio/ratio_example_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/file_asset_paths.dart'; 2 | import 'package:example/screens/ratio/ratio_example_help_dialog.dart'; 3 | import 'package:example/widgets/code_view_dialog.dart'; 4 | import 'package:example/widgets/nav_drawer.dart'; 5 | import 'package:example/widgets/size_label.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:flutter_resizable_container/flutter_resizable_container.dart'; 8 | 9 | class RatioExampleScreen extends StatelessWidget { 10 | const RatioExampleScreen({super.key}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Scaffold( 15 | appBar: AppBar( 16 | title: const Text('Ratio example'), 17 | actions: [ 18 | IconButton( 19 | onPressed: () => RatioExampleHelpDialog.show(context: context), 20 | icon: const Icon(Icons.help_center), 21 | ), 22 | IconButton( 23 | onPressed: () => CodeViewDialog.show( 24 | context: context, 25 | filePath: FileAssetPaths.ratioScreen, 26 | ), 27 | icon: const Icon(Icons.code), 28 | ), 29 | ], 30 | ), 31 | drawer: const NavDrawer(), 32 | body: ResizableContainer( 33 | direction: Axis.horizontal, 34 | children: [ 35 | ResizableChild( 36 | size: const ResizableSize.ratio(0.15), 37 | child: ColoredBox( 38 | color: Theme.of(context).colorScheme.primaryContainer, 39 | child: const SizeLabel(), 40 | ), 41 | ), 42 | ResizableChild( 43 | child: ColoredBox( 44 | color: Theme.of(context).colorScheme.tertiaryContainer, 45 | child: const SizeLabel(), 46 | ), 47 | ), 48 | ResizableChild( 49 | size: const ResizableSize.ratio(0.15), 50 | child: ColoredBox( 51 | color: Theme.of(context).colorScheme.primaryContainer, 52 | child: const SizeLabel(), 53 | ), 54 | ), 55 | ], 56 | ), 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /example/lib/screens/shrink_and_flex/future_builder_shrink_example_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/widgets/nav_drawer.dart'; 2 | import 'package:example/widgets/size_label.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_resizable_container/flutter_resizable_container.dart'; 5 | 6 | class FutureBuilderShrinkExampleScreen extends StatelessWidget { 7 | const FutureBuilderShrinkExampleScreen({super.key}); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Scaffold( 12 | appBar: AppBar( 13 | title: const Text('FutureBuilder shrink example'), 14 | ), 15 | drawer: const NavDrawer(), 16 | body: FutureBuilder( 17 | future: Future.delayed( 18 | const Duration(seconds: 3), 19 | () => 'Future/Stream Content', 20 | ), 21 | builder: (context, snapshot) { 22 | return ResizableContainer( 23 | children: [ 24 | ResizableChild( 25 | size: const ResizableSize.shrink(), 26 | child: switch (snapshot.connectionState) { 27 | ConnectionState.done => ColoredBox( 28 | color: Theme.of(context).colorScheme.secondaryContainer, 29 | child: Padding( 30 | padding: const EdgeInsets.symmetric( 31 | horizontal: 24, 32 | vertical: 8, 33 | ), 34 | child: Center(child: Text(snapshot.data!)), 35 | ), 36 | ), 37 | _ => const Center( 38 | child: Padding( 39 | padding: EdgeInsets.symmetric( 40 | horizontal: 4, 41 | vertical: 8, 42 | ), 43 | child: SizedBox.square( 44 | dimension: 12, 45 | child: CircularProgressIndicator( 46 | strokeWidth: 3, 47 | ), 48 | ), 49 | ), 50 | ), 51 | }, 52 | ), 53 | ResizableChild( 54 | child: ColoredBox( 55 | color: Theme.of(context).colorScheme.primaryContainer, 56 | child: const SizeLabel(), 57 | ), 58 | ), 59 | ], 60 | direction: Axis.horizontal, 61 | ); 62 | }, 63 | ), 64 | ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /example/lib/screens/shrink_and_flex/shrink_and_flex_example_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/file_asset_paths.dart'; 2 | import 'package:example/screens/shrink_and_flex/shrink_and_flex_help_dialog.dart'; 3 | import 'package:example/widgets/code_view_dialog.dart'; 4 | import 'package:example/widgets/nav_drawer.dart'; 5 | import 'package:example/widgets/size_label.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:flutter_resizable_container/flutter_resizable_container.dart'; 8 | 9 | class ShrinkAndFlexExampleScreen extends StatefulWidget { 10 | const ShrinkAndFlexExampleScreen({super.key}); 11 | 12 | @override 13 | State createState() => 14 | _ShrinkAndFlexExampleScreenState(); 15 | } 16 | 17 | class _ShrinkAndFlexExampleScreenState 18 | extends State { 19 | @override 20 | Widget build(BuildContext context) { 21 | return Scaffold( 22 | appBar: AppBar( 23 | title: const Text('Shrink and flex example'), 24 | actions: [ 25 | IconButton( 26 | onPressed: () => ShrinkAndFlexHelpDialog.show(context: context), 27 | icon: const Icon(Icons.help_center), 28 | ), 29 | IconButton( 30 | onPressed: () => CodeViewDialog.show( 31 | context: context, 32 | filePath: FileAssetPaths.shrinkAndFlexScreen, 33 | ), 34 | icon: const Icon(Icons.code), 35 | ), 36 | ], 37 | ), 38 | drawer: const NavDrawer(), 39 | body: Column( 40 | children: [ 41 | Expanded( 42 | child: ResizableContainer( 43 | direction: Axis.horizontal, 44 | children: [ 45 | ResizableChild( 46 | size: const ResizableSize.expand(), 47 | child: ColoredBox( 48 | color: Theme.of(context).colorScheme.primaryContainer, 49 | child: const SizeLabel(), 50 | ), 51 | ), 52 | ResizableChild( 53 | size: const ResizableSize.shrink(), 54 | child: SizedBox( 55 | width: 100, 56 | child: ColoredBox( 57 | color: Theme.of(context).colorScheme.tertiaryContainer, 58 | child: const SizeLabel(), 59 | ), 60 | ), 61 | ), 62 | ], 63 | ), 64 | ), 65 | ], 66 | ), 67 | ); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /example/lib/screens/shrink_and_flex/shrink_and_flex_help_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/widgets/help_dialog.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class ShrinkAndFlexHelpDialog extends StatelessWidget { 5 | const ShrinkAndFlexHelpDialog._({super.key}); 6 | 7 | static void show({ 8 | required BuildContext context, 9 | }) { 10 | HelpDialog.show( 11 | context: context, 12 | child: const ShrinkAndFlexHelpDialog._( 13 | key: Key('ShrinkAndFlexHelpDialog'), 14 | ), 15 | ); 16 | } 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return const HelpDialog( 21 | title: 'About this example', 22 | content: Column( 23 | mainAxisSize: MainAxisSize.min, 24 | children: [ 25 | Text.rich(TextSpan( 26 | children: [ 27 | TextSpan(text: 'In this example, the left child is using a '), 28 | TextSpan( 29 | text: 'ResizableSize.expand()', 30 | style: TextStyle( 31 | fontFamily: 'monospace', 32 | fontWeight: FontWeight.bold, 33 | )), 34 | TextSpan(text: ' while the right child is using a '), 35 | TextSpan( 36 | text: 'ResizableSize.shrink()', 37 | style: TextStyle( 38 | fontFamily: 'monospace', 39 | fontWeight: FontWeight.bold, 40 | )), 41 | TextSpan(text: '.'), 42 | ], 43 | )), 44 | SizedBox(height: 15), 45 | Text.rich(TextSpan( 46 | children: [ 47 | TextSpan( 48 | text: 49 | 'This means that the left child will take up all available space, while the right child will take up only the space it needs.', 50 | ), 51 | ], 52 | )), 53 | ], 54 | ), 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /example/lib/utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | double log10(num n) => log(n) / ln10; 4 | -------------------------------------------------------------------------------- /example/lib/widgets/app_version.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:package_info_plus/package_info_plus.dart'; 3 | 4 | class AppVersion extends StatelessWidget { 5 | const AppVersion({super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return FutureBuilder( 10 | future: PackageInfo.fromPlatform(), 11 | builder: (context, snapshot) { 12 | if (snapshot.hasData) { 13 | final data = snapshot.data!; 14 | return Padding( 15 | padding: const EdgeInsets.symmetric( 16 | vertical: 1, 17 | horizontal: 8, 18 | ), 19 | child: Text('v${data.version}'), 20 | ); 21 | } 22 | 23 | return const SizedBox.shrink(); 24 | }, 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /example/lib/widgets/code_view_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/extensions/int_ext.dart'; 2 | import 'package:example/file_asset_paths.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class CodeViewDialog extends StatelessWidget { 6 | const CodeViewDialog._({ 7 | super.key, 8 | required this.filePath, 9 | }); 10 | 11 | static void show({ 12 | required BuildContext context, 13 | required FileAssetPaths filePath, 14 | }) { 15 | showDialog( 16 | context: context, 17 | builder: (context) => CodeViewDialog._( 18 | key: const Key('CodeViewDialog'), 19 | filePath: filePath.path, 20 | ), 21 | ); 22 | } 23 | 24 | final String filePath; 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return Dialog.fullscreen( 29 | child: Column( 30 | crossAxisAlignment: CrossAxisAlignment.stretch, 31 | children: [ 32 | Row( 33 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 34 | children: [ 35 | Padding( 36 | padding: const EdgeInsets.all(8.0), 37 | child: Text( 38 | filePath, 39 | style: Theme.of(context).textTheme.labelLarge, 40 | ), 41 | ), 42 | IconButton( 43 | icon: const Icon(Icons.close), 44 | onPressed: () => Navigator.of(context).pop(), 45 | ), 46 | ], 47 | ), 48 | const Divider( 49 | height: 0, 50 | ), 51 | Expanded( 52 | child: FutureBuilder( 53 | future: DefaultAssetBundle.of(context).loadString(filePath), 54 | builder: (context, snapshot) { 55 | if (snapshot.hasError) { 56 | return Center(child: Text(snapshot.error!.toString())); 57 | } 58 | 59 | if (!snapshot.hasData) { 60 | return const Center( 61 | child: CircularProgressIndicator(), 62 | ); 63 | } 64 | 65 | final content = snapshot.data!; 66 | final lines = content.split('\n'); 67 | final maxDigits = lines.length.digitCount; 68 | 69 | return DecoratedBox( 70 | decoration: const BoxDecoration( 71 | color: Colors.white, 72 | ), 73 | child: SelectionArea( 74 | focusNode: FocusNode(), 75 | selectionControls: DesktopTextSelectionControls(), 76 | child: Padding( 77 | padding: const EdgeInsets.symmetric( 78 | horizontal: 12, 79 | vertical: 6, 80 | ), 81 | child: ListView.builder( 82 | itemCount: lines.length, 83 | itemBuilder: (context, index) { 84 | final digitCount = (index + 1).digitCount; 85 | final padding = (maxDigits - digitCount) + 2; 86 | final label = '${index + 1}${' ' * padding}'; 87 | 88 | return Text.rich( 89 | TextSpan( 90 | children: [ 91 | TextSpan(text: label), 92 | TextSpan(text: lines[index]), 93 | ], 94 | ), 95 | style: const TextStyle( 96 | fontFamily: 'Reddit Mono', 97 | ), 98 | softWrap: true, 99 | ); 100 | }, 101 | ), 102 | ), 103 | ), 104 | ); 105 | }, 106 | ), 107 | ), 108 | ], 109 | ), 110 | ); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /example/lib/widgets/colored_box.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ColoredBox extends StatelessWidget { 4 | const ColoredBox({ 5 | super.key, 6 | required this.color, 7 | required this.child, 8 | }); 9 | 10 | final Color color; 11 | final Widget child; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return DecoratedBox( 16 | decoration: BoxDecoration( 17 | color: color, 18 | ), 19 | child: SizedBox.expand( 20 | child: child, 21 | ), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /example/lib/widgets/help_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class HelpDialog extends StatelessWidget { 4 | const HelpDialog({ 5 | super.key, 6 | required this.title, 7 | required this.content, 8 | }); 9 | 10 | final String title; 11 | final Widget content; 12 | 13 | static void show({ 14 | required BuildContext context, 15 | required Widget child, 16 | }) { 17 | showDialog( 18 | context: context, 19 | builder: (context) => child, 20 | ); 21 | } 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return AlertDialog( 26 | contentTextStyle: Theme.of(context).textTheme.bodyLarge, 27 | insetPadding: EdgeInsets.symmetric( 28 | horizontal: MediaQuery.sizeOf(context).width * 0.15, 29 | vertical: MediaQuery.sizeOf(context).height * 0.15, 30 | ), 31 | title: Text(title), 32 | content: SingleChildScrollView(child: content), 33 | actions: [ 34 | TextButton( 35 | onPressed: () => Navigator.of(context).pop(), 36 | child: const Text('Close'), 37 | ), 38 | ], 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /example/lib/widgets/nav_drawer.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/widgets/app_version.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class NavDrawer extends StatelessWidget { 5 | const NavDrawer({super.key}); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return Drawer( 10 | child: Column( 11 | crossAxisAlignment: CrossAxisAlignment.stretch, 12 | children: [ 13 | Align( 14 | alignment: Alignment.centerRight, 15 | child: Padding( 16 | padding: const EdgeInsets.all(8.0), 17 | child: InkWell( 18 | onTap: () => Navigator.of(context).pop(), 19 | child: const Icon(Icons.close), 20 | ), 21 | ), 22 | ), 23 | const NavSectionHeader(title: 'Basic Examples'), 24 | ListTile( 25 | title: const Text('Basic Example'), 26 | onTap: () => Navigator.of(context).pushReplacementNamed('basic'), 27 | ), 28 | ListTile( 29 | title: const Text('Ratio Example'), 30 | onTap: () => Navigator.of(context).pushReplacementNamed('ratio'), 31 | ), 32 | ListTile( 33 | title: const Text('Pixels Example'), 34 | onTap: () => Navigator.of(context).pushReplacementNamed('pixels'), 35 | ), 36 | ListTile( 37 | title: const Text('Custom Divider Example'), 38 | onTap: () => Navigator.of(context).pushReplacementNamed('divider'), 39 | ), 40 | ListTile( 41 | title: const Text('Shrink and Flex Example'), 42 | onTap: () => Navigator.of(context).pushReplacementNamed('shrink'), 43 | ), 44 | ListTile( 45 | title: const Text('FutureBuilder Shrink Example'), 46 | onTap: () => Navigator.of(context) 47 | .pushReplacementNamed('future-builder-shrink'), 48 | ), 49 | ListTile( 50 | title: const Text('Cascading Delta Example'), 51 | onTap: () => 52 | Navigator.of(context).pushReplacementNamed('cascading-delta'), 53 | ), 54 | const SizedBox(height: 15), 55 | const NavSectionHeader(title: 'Controller Examples'), 56 | ListTile( 57 | title: const Text('Controller Listen Example'), 58 | onTap: () => Navigator.of(context).pushReplacementNamed('listen'), 59 | ), 60 | ListTile( 61 | title: const Text('Controller Set Sizes Example'), 62 | onTap: () => Navigator.of(context).pushReplacementNamed('sizes'), 63 | ), 64 | const Spacer(), 65 | const AppVersion(), 66 | ], 67 | ), 68 | ); 69 | } 70 | } 71 | 72 | class NavSectionHeader extends StatelessWidget { 73 | const NavSectionHeader({ 74 | super.key, 75 | required this.title, 76 | }); 77 | 78 | final String title; 79 | 80 | @override 81 | Widget build(BuildContext context) { 82 | return Padding( 83 | padding: const EdgeInsets.fromLTRB(8, 0, 8, 0), 84 | child: DecoratedBox( 85 | decoration: BoxDecoration( 86 | color: Theme.of(context).colorScheme.secondaryContainer, 87 | borderRadius: BorderRadius.circular(8), 88 | ), 89 | child: Padding( 90 | padding: const EdgeInsets.all(8.0), 91 | child: Text( 92 | title, 93 | style: Theme.of(context).textTheme.titleMedium, 94 | textAlign: TextAlign.center, 95 | ), 96 | ), 97 | ), 98 | ); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /example/lib/widgets/size_label.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class SizeLabel extends StatelessWidget { 4 | const SizeLabel({super.key}); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return LayoutBuilder(builder: (context, constraints) { 9 | final height = constraints.maxHeight.toStringAsFixed(2); 10 | final width = constraints.maxWidth.toStringAsFixed(2); 11 | 12 | return Center( 13 | child: Column( 14 | mainAxisSize: MainAxisSize.min, 15 | children: [ 16 | Text('Height: $height', textAlign: TextAlign.center), 17 | Text('Width: $width', textAlign: TextAlign.center), 18 | ], 19 | ), 20 | ); 21 | }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import package_info_plus 9 | 10 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 11 | FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) 12 | } 13 | -------------------------------------------------------------------------------- /example/macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.14' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_macos_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | use_modular_headers! 32 | 33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) 34 | target 'RunnerTests' do 35 | inherit! :search_paths 36 | end 37 | end 38 | 39 | post_install do |installer| 40 | installer.pods_project.targets.each do |target| 41 | flutter_additional_macos_build_settings(target) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /example/macos/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - FlutterMacOS (1.0.0) 3 | - package_info_plus (0.0.1): 4 | - FlutterMacOS 5 | 6 | DEPENDENCIES: 7 | - FlutterMacOS (from `Flutter/ephemeral`) 8 | - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) 9 | 10 | EXTERNAL SOURCES: 11 | FlutterMacOS: 12 | :path: Flutter/ephemeral 13 | package_info_plus: 14 | :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos 15 | 16 | SPEC CHECKSUMS: 17 | FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 18 | package_info_plus: f0052d280d17aa382b932f399edf32507174e870 19 | 20 | PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 21 | 22 | COCOAPODS: 1.16.2 23 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 64 | 66 | 72 | 73 | 74 | 75 | 81 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @main 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | 10 | override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { 11 | return true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /example/macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = example 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2024 com.example. All rights reserved. 15 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /example/macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /example/macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /example/macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import FlutterMacOS 2 | import Cocoa 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: A new Flutter project. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | # In Windows, build-name is used as the major, minor, and patch parts 19 | # of the product and file versions while build-number is used as the build suffix. 20 | version: 3.0.0 21 | 22 | environment: 23 | sdk: '>=3.3.0 <4.0.0' 24 | flutter: '>=3.22.0' 25 | 26 | # Dependencies specify other packages that your package needs in order to work. 27 | # To automatically upgrade your package dependencies to the latest versions 28 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 29 | # dependencies can be manually updated by changing the version numbers below to 30 | # the latest version available on pub.dev. To see which dependencies have newer 31 | # versions available, run `flutter pub outdated`. 32 | dependencies: 33 | flutter: 34 | sdk: flutter 35 | 36 | flutter_resizable_container: 37 | path: '../' 38 | 39 | # The following adds the Cupertino Icons font to your application. 40 | # Use with the CupertinoIcons class for iOS style icons. 41 | cupertino_icons: ^1.0.2 42 | package_info_plus: ^8.0.0 43 | 44 | dev_dependencies: 45 | flutter_test: 46 | sdk: flutter 47 | 48 | # The "flutter_lints" package below contains a set of recommended lints to 49 | # encourage good coding practices. The lint set provided by the package is 50 | # activated in the `analysis_options.yaml` file located at the root of your 51 | # package. See that file for information about deactivating specific lint 52 | # rules and activating additional ones. 53 | flutter_lints: ^2.0.0 54 | 55 | # For information on the generic Dart part of this file, see the 56 | # following page: https://dart.dev/tools/pub/pubspec 57 | 58 | # The following section is specific to Flutter packages. 59 | flutter: 60 | # The following line ensures that the Material Icons font is 61 | # included with your application, so that you can use the icons in 62 | # the material Icons class. 63 | uses-material-design: true 64 | 65 | assets: 66 | - lib/screens/basic/basic_example_screen.dart 67 | - lib/screens/controller_listen/controller_listen_example_screen.dart 68 | - lib/screens/controller_set_sizes/controller_set_sizes_example_screen.dart 69 | - lib/screens/divider/custom_divider_example_screen.dart 70 | - lib/screens/pixels/pixels_example_screen.dart 71 | - lib/screens/ratio/ratio_example_screen.dart 72 | - lib/screens/shrink_and_flex/shrink_and_flex_example_screen.dart 73 | - lib/screens/cascading_delta/cascading_delta_screen.dart 74 | 75 | # An image asset can refer to one or more resolution-specific "variants", see 76 | # https://flutter.dev/assets-and-images/#resolution-aware 77 | 78 | # For details regarding adding assets from package dependencies, see 79 | # https://flutter.dev/assets-and-images/#from-packages 80 | 81 | # To add custom fonts to your application, add a fonts section here, 82 | # in this "flutter" section. Each entry in this list should have a 83 | # "family" key with the font family name, and a "fonts" key with a 84 | # list giving the asset and other descriptors for the font. For 85 | # example: 86 | fonts: 87 | - family: Reddit Mono 88 | fonts: 89 | - asset: assets/fonts/RedditMono-VariableFont_wght.ttf 90 | # - family: Schyler 91 | # fonts: 92 | # - asset: fonts/Schyler-Regular.ttf 93 | # - asset: fonts/Schyler-Italic.ttf 94 | # style: italic 95 | # - family: Trajan Pro 96 | # fonts: 97 | # - asset: fonts/TrajanPro.ttf 98 | # - asset: fonts/TrajanPro_Bold.ttf 99 | # weight: 700 100 | # 101 | # For details regarding fonts from package dependencies, 102 | # see https://flutter.dev/custom-fonts/#from-packages 103 | -------------------------------------------------------------------------------- /example/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/example/web/favicon.png -------------------------------------------------------------------------------- /example/web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/example/web/icons/Icon-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/example/web/icons/Icon-512.png -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/example/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/example/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /example/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 23 | 26 | 27 | 28 | 31 | 34 | 37 | 40 | 41 | 42 | 46 | 47 | example 48 | 51 | 52 | 53 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /example/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "short_name": "example", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /example/windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /example/windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Project-level configuration. 2 | cmake_minimum_required(VERSION 3.14) 3 | project(example LANGUAGES CXX) 4 | 5 | # The name of the executable created for the application. Change this to change 6 | # the on-disk name of your application. 7 | set(BINARY_NAME "example") 8 | 9 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent 10 | # versions of CMake. 11 | cmake_policy(VERSION 3.14...3.25) 12 | 13 | # Define build configuration option. 14 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 15 | if(IS_MULTICONFIG) 16 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" 17 | CACHE STRING "" FORCE) 18 | else() 19 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 20 | set(CMAKE_BUILD_TYPE "Debug" CACHE 21 | STRING "Flutter build mode" FORCE) 22 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 23 | "Debug" "Profile" "Release") 24 | endif() 25 | endif() 26 | # Define settings for the Profile build mode. 27 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") 28 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") 29 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") 30 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") 31 | 32 | # Use Unicode for all projects. 33 | add_definitions(-DUNICODE -D_UNICODE) 34 | 35 | # Compilation settings that should be applied to most targets. 36 | # 37 | # Be cautious about adding new options here, as plugins use this function by 38 | # default. In most cases, you should add new options to specific targets instead 39 | # of modifying this function. 40 | function(APPLY_STANDARD_SETTINGS TARGET) 41 | target_compile_features(${TARGET} PUBLIC cxx_std_17) 42 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") 43 | target_compile_options(${TARGET} PRIVATE /EHsc) 44 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") 45 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") 46 | endfunction() 47 | 48 | # Flutter library and tool build rules. 49 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 50 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 51 | 52 | # Application build; see runner/CMakeLists.txt. 53 | add_subdirectory("runner") 54 | 55 | 56 | # Generated plugin build rules, which manage building the plugins and adding 57 | # them to the application. 58 | include(flutter/generated_plugins.cmake) 59 | 60 | 61 | # === Installation === 62 | # Support files are copied into place next to the executable, so that it can 63 | # run in place. This is done instead of making a separate bundle (as on Linux) 64 | # so that building and running from within Visual Studio will work. 65 | set(BUILD_BUNDLE_DIR "$") 66 | # Make the "install" step default, as it's required to run. 67 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 68 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 69 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 70 | endif() 71 | 72 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 73 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") 74 | 75 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 76 | COMPONENT Runtime) 77 | 78 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 79 | COMPONENT Runtime) 80 | 81 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 82 | COMPONENT Runtime) 83 | 84 | if(PLUGIN_BUNDLED_LIBRARIES) 85 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 86 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 87 | COMPONENT Runtime) 88 | endif() 89 | 90 | # Copy the native assets provided by the build.dart from all packages. 91 | set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") 92 | install(DIRECTORY "${NATIVE_ASSETS_DIR}" 93 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 94 | COMPONENT Runtime) 95 | 96 | # Fully re-copy the assets directory on each build to avoid having stale files 97 | # from a previous install. 98 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 99 | install(CODE " 100 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 101 | " COMPONENT Runtime) 102 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 103 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 104 | 105 | # Install the AOT library on non-Debug builds only. 106 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 107 | CONFIGURATIONS Profile;Release 108 | COMPONENT Runtime) 109 | -------------------------------------------------------------------------------- /example/windows/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.14) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") 12 | 13 | # Set fallback configurations for older versions of the flutter tool. 14 | if (NOT DEFINED FLUTTER_TARGET_PLATFORM) 15 | set(FLUTTER_TARGET_PLATFORM "windows-x64") 16 | endif() 17 | 18 | # === Flutter Library === 19 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") 20 | 21 | # Published to parent scope for install step. 22 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 23 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 24 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 25 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) 26 | 27 | list(APPEND FLUTTER_LIBRARY_HEADERS 28 | "flutter_export.h" 29 | "flutter_windows.h" 30 | "flutter_messenger.h" 31 | "flutter_plugin_registrar.h" 32 | "flutter_texture_registrar.h" 33 | ) 34 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") 35 | add_library(flutter INTERFACE) 36 | target_include_directories(flutter INTERFACE 37 | "${EPHEMERAL_DIR}" 38 | ) 39 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") 40 | add_dependencies(flutter flutter_assemble) 41 | 42 | # === Wrapper === 43 | list(APPEND CPP_WRAPPER_SOURCES_CORE 44 | "core_implementations.cc" 45 | "standard_codec.cc" 46 | ) 47 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") 48 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN 49 | "plugin_registrar.cc" 50 | ) 51 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") 52 | list(APPEND CPP_WRAPPER_SOURCES_APP 53 | "flutter_engine.cc" 54 | "flutter_view_controller.cc" 55 | ) 56 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") 57 | 58 | # Wrapper sources needed for a plugin. 59 | add_library(flutter_wrapper_plugin STATIC 60 | ${CPP_WRAPPER_SOURCES_CORE} 61 | ${CPP_WRAPPER_SOURCES_PLUGIN} 62 | ) 63 | apply_standard_settings(flutter_wrapper_plugin) 64 | set_target_properties(flutter_wrapper_plugin PROPERTIES 65 | POSITION_INDEPENDENT_CODE ON) 66 | set_target_properties(flutter_wrapper_plugin PROPERTIES 67 | CXX_VISIBILITY_PRESET hidden) 68 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) 69 | target_include_directories(flutter_wrapper_plugin PUBLIC 70 | "${WRAPPER_ROOT}/include" 71 | ) 72 | add_dependencies(flutter_wrapper_plugin flutter_assemble) 73 | 74 | # Wrapper sources needed for the runner. 75 | add_library(flutter_wrapper_app STATIC 76 | ${CPP_WRAPPER_SOURCES_CORE} 77 | ${CPP_WRAPPER_SOURCES_APP} 78 | ) 79 | apply_standard_settings(flutter_wrapper_app) 80 | target_link_libraries(flutter_wrapper_app PUBLIC flutter) 81 | target_include_directories(flutter_wrapper_app PUBLIC 82 | "${WRAPPER_ROOT}/include" 83 | ) 84 | add_dependencies(flutter_wrapper_app flutter_assemble) 85 | 86 | # === Flutter tool backend === 87 | # _phony_ is a non-existent file to force this command to run every time, 88 | # since currently there's no way to get a full input/output list from the 89 | # flutter tool. 90 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") 91 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) 92 | add_custom_command( 93 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 94 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} 95 | ${CPP_WRAPPER_SOURCES_APP} 96 | ${PHONY_OUTPUT} 97 | COMMAND ${CMAKE_COMMAND} -E env 98 | ${FLUTTER_TOOL_ENVIRONMENT} 99 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" 100 | ${FLUTTER_TARGET_PLATFORM} $ 101 | VERBATIM 102 | ) 103 | add_custom_target(flutter_assemble DEPENDS 104 | "${FLUTTER_LIBRARY}" 105 | ${FLUTTER_LIBRARY_HEADERS} 106 | ${CPP_WRAPPER_SOURCES_CORE} 107 | ${CPP_WRAPPER_SOURCES_PLUGIN} 108 | ${CPP_WRAPPER_SOURCES_APP} 109 | ) 110 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | 10 | void RegisterPlugins(flutter::PluginRegistry* registry) { 11 | } 12 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /example/windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | ) 7 | 8 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 9 | ) 10 | 11 | set(PLUGIN_BUNDLED_LIBRARIES) 12 | 13 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 14 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 15 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 16 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 18 | endforeach(plugin) 19 | 20 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 21 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 22 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 23 | endforeach(ffi_plugin) 24 | -------------------------------------------------------------------------------- /example/windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} WIN32 10 | "flutter_window.cpp" 11 | "main.cpp" 12 | "utils.cpp" 13 | "win32_window.cpp" 14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 15 | "Runner.rc" 16 | "runner.exe.manifest" 17 | ) 18 | 19 | # Apply the standard set of build settings. This can be removed for applications 20 | # that need different build settings. 21 | apply_standard_settings(${BINARY_NAME}) 22 | 23 | # Add preprocessor definitions for the build version. 24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") 25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") 26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") 27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") 28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") 29 | 30 | # Disable Windows macros that collide with C++ standard library functions. 31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 32 | 33 | # Add dependency libraries and include directories. Add any application-specific 34 | # dependencies here. 35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 36 | target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") 37 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 38 | 39 | # Run the Flutter tool portions of the build. This must not be removed. 40 | add_dependencies(${BINARY_NAME} flutter_assemble) 41 | -------------------------------------------------------------------------------- /example/windows/runner/Runner.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | #include "resource.h" 5 | 6 | #define APSTUDIO_READONLY_SYMBOLS 7 | ///////////////////////////////////////////////////////////////////////////// 8 | // 9 | // Generated from the TEXTINCLUDE 2 resource. 10 | // 11 | #include "winres.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_APP_ICON ICON "resources\\app_icon.ico" 56 | 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | #if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) 64 | #define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD 65 | #else 66 | #define VERSION_AS_NUMBER 1,0,0,0 67 | #endif 68 | 69 | #if defined(FLUTTER_VERSION) 70 | #define VERSION_AS_STRING FLUTTER_VERSION 71 | #else 72 | #define VERSION_AS_STRING "1.0.0" 73 | #endif 74 | 75 | VS_VERSION_INFO VERSIONINFO 76 | FILEVERSION VERSION_AS_NUMBER 77 | PRODUCTVERSION VERSION_AS_NUMBER 78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 79 | #ifdef _DEBUG 80 | FILEFLAGS VS_FF_DEBUG 81 | #else 82 | FILEFLAGS 0x0L 83 | #endif 84 | FILEOS VOS__WINDOWS32 85 | FILETYPE VFT_APP 86 | FILESUBTYPE 0x0L 87 | BEGIN 88 | BLOCK "StringFileInfo" 89 | BEGIN 90 | BLOCK "040904e4" 91 | BEGIN 92 | VALUE "CompanyName", "com.example" "\0" 93 | VALUE "FileDescription", "example" "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "example" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2024 com.example. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "example.exe" "\0" 98 | VALUE "ProductName", "example" "\0" 99 | VALUE "ProductVersion", VERSION_AS_STRING "\0" 100 | END 101 | END 102 | BLOCK "VarFileInfo" 103 | BEGIN 104 | VALUE "Translation", 0x409, 1252 105 | END 106 | END 107 | 108 | #endif // English (United States) resources 109 | ///////////////////////////////////////////////////////////////////////////// 110 | 111 | 112 | 113 | #ifndef APSTUDIO_INVOKED 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // Generated from the TEXTINCLUDE 3 resource. 117 | // 118 | 119 | 120 | ///////////////////////////////////////////////////////////////////////////// 121 | #endif // not APSTUDIO_INVOKED 122 | -------------------------------------------------------------------------------- /example/windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | 30 | flutter_controller_->engine()->SetNextFrameCallback([&]() { 31 | this->Show(); 32 | }); 33 | 34 | // Flutter can complete the first frame before the "show window" callback is 35 | // registered. The following call ensures a frame is pending to ensure the 36 | // window is shown. It is a no-op if the first frame hasn't completed yet. 37 | flutter_controller_->ForceRedraw(); 38 | 39 | return true; 40 | } 41 | 42 | void FlutterWindow::OnDestroy() { 43 | if (flutter_controller_) { 44 | flutter_controller_ = nullptr; 45 | } 46 | 47 | Win32Window::OnDestroy(); 48 | } 49 | 50 | LRESULT 51 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 52 | WPARAM const wparam, 53 | LPARAM const lparam) noexcept { 54 | // Give Flutter, including plugins, an opportunity to handle window messages. 55 | if (flutter_controller_) { 56 | std::optional result = 57 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 58 | lparam); 59 | if (result) { 60 | return *result; 61 | } 62 | } 63 | 64 | switch (message) { 65 | case WM_FONTCHANGE: 66 | flutter_controller_->engine()->ReloadSystemFonts(); 67 | break; 68 | } 69 | 70 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 71 | } 72 | -------------------------------------------------------------------------------- /example/windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /example/windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.Create(L"example", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /example/windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /example/windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andyhorn/flutter_resizable_container/b9cf13b25d8738dd0cfeba6cfd83327c36bf24fa/example/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /example/windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /example/windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr) 51 | -1; // remove the trailing null character 52 | int input_length = (int)wcslen(utf16_string); 53 | std::string utf8_string; 54 | if (target_length <= 0 || target_length > utf8_string.max_size()) { 55 | return utf8_string; 56 | } 57 | utf8_string.resize(target_length); 58 | int converted_length = ::WideCharToMultiByte( 59 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 60 | input_length, utf8_string.data(), target_length, nullptr, nullptr); 61 | if (converted_length == 0) { 62 | return std::string(); 63 | } 64 | return utf8_string; 65 | } 66 | -------------------------------------------------------------------------------- /example/windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /example/windows/runner/win32_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_WIN32_WINDOW_H_ 2 | #define RUNNER_WIN32_WINDOW_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be 11 | // inherited from by classes that wish to specialize with custom 12 | // rendering and input handling 13 | class Win32Window { 14 | public: 15 | struct Point { 16 | unsigned int x; 17 | unsigned int y; 18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {} 19 | }; 20 | 21 | struct Size { 22 | unsigned int width; 23 | unsigned int height; 24 | Size(unsigned int width, unsigned int height) 25 | : width(width), height(height) {} 26 | }; 27 | 28 | Win32Window(); 29 | virtual ~Win32Window(); 30 | 31 | // Creates a win32 window with |title| that is positioned and sized using 32 | // |origin| and |size|. New windows are created on the default monitor. Window 33 | // sizes are specified to the OS in physical pixels, hence to ensure a 34 | // consistent size this function will scale the inputted width and height as 35 | // as appropriate for the default monitor. The window is invisible until 36 | // |Show| is called. Returns true if the window was created successfully. 37 | bool Create(const std::wstring& title, const Point& origin, const Size& size); 38 | 39 | // Show the current window. Returns true if the window was successfully shown. 40 | bool Show(); 41 | 42 | // Release OS resources associated with window. 43 | void Destroy(); 44 | 45 | // Inserts |content| into the window tree. 46 | void SetChildContent(HWND content); 47 | 48 | // Returns the backing Window handle to enable clients to set icon and other 49 | // window properties. Returns nullptr if the window has been destroyed. 50 | HWND GetHandle(); 51 | 52 | // If true, closing this window will quit the application. 53 | void SetQuitOnClose(bool quit_on_close); 54 | 55 | // Return a RECT representing the bounds of the current client area. 56 | RECT GetClientArea(); 57 | 58 | protected: 59 | // Processes and route salient window messages for mouse handling, 60 | // size change and DPI. Delegates handling of these to member overloads that 61 | // inheriting classes can handle. 62 | virtual LRESULT MessageHandler(HWND window, 63 | UINT const message, 64 | WPARAM const wparam, 65 | LPARAM const lparam) noexcept; 66 | 67 | // Called when CreateAndShow is called, allowing subclass window-related 68 | // setup. Subclasses should return false if setup fails. 69 | virtual bool OnCreate(); 70 | 71 | // Called when Destroy is called. 72 | virtual void OnDestroy(); 73 | 74 | private: 75 | friend class WindowClassRegistrar; 76 | 77 | // OS callback called by message pump. Handles the WM_NCCREATE message which 78 | // is passed when the non-client area is being created and enables automatic 79 | // non-client DPI scaling so that the non-client area automatically 80 | // responds to changes in DPI. All other messages are handled by 81 | // MessageHandler. 82 | static LRESULT CALLBACK WndProc(HWND const window, 83 | UINT const message, 84 | WPARAM const wparam, 85 | LPARAM const lparam) noexcept; 86 | 87 | // Retrieves a class instance pointer for |window| 88 | static Win32Window* GetThisFromHandle(HWND const window) noexcept; 89 | 90 | // Update the window frame's theme to match the system theme. 91 | static void UpdateTheme(HWND const window); 92 | 93 | bool quit_on_close_ = false; 94 | 95 | // window handle for top level window. 96 | HWND window_handle_ = nullptr; 97 | 98 | // window handle for hosted content. 99 | HWND child_content_ = nullptr; 100 | }; 101 | 102 | #endif // RUNNER_WIN32_WINDOW_H_ 103 | -------------------------------------------------------------------------------- /lib/flutter_resizable_container.dart: -------------------------------------------------------------------------------- 1 | library; 2 | 3 | export 'src/resizable_container.dart'; 4 | export 'src/resizable_controller.dart' show ResizableController; 5 | export 'src/resizable_child.dart'; 6 | export 'src/resizable_divider.dart'; 7 | export 'src/resizable_size.dart' show ResizableSize; 8 | -------------------------------------------------------------------------------- /lib/src/divider_painter.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_resizable_container/flutter_resizable_container.dart'; 5 | 6 | @visibleForTesting 7 | class DividerPainter extends CustomPainter { 8 | const DividerPainter({ 9 | required this.color, 10 | required this.direction, 11 | required this.thickness, 12 | required this.padding, 13 | required this.length, 14 | required this.crossAxisAlignment, 15 | required this.mainAxisAlignment, 16 | }); 17 | 18 | final Axis direction; 19 | final double thickness; 20 | final double padding; 21 | final ResizableSize length; 22 | final Color color; 23 | final MainAxisAlignment mainAxisAlignment; 24 | final CrossAxisAlignment crossAxisAlignment; 25 | 26 | @override 27 | void paint(Canvas canvas, Size size) { 28 | final path = _getPath(size); 29 | final paint = _getPaint(); 30 | 31 | canvas.drawPath(path, paint); 32 | } 33 | 34 | @override 35 | bool shouldRepaint(covariant CustomPainter oldDelegate) { 36 | return true; 37 | } 38 | 39 | Paint _getPaint() { 40 | return Paint() 41 | ..color = color 42 | ..style = PaintingStyle.stroke 43 | ..strokeWidth = thickness; 44 | } 45 | 46 | Path _getPath(Size size) { 47 | final startingPoint = _getStartingPoint(size); 48 | final endingPoint = _getEndingPoint(size); 49 | 50 | return Path() 51 | ..moveTo(startingPoint.x, startingPoint.y) 52 | ..lineTo(endingPoint.x, endingPoint.y) 53 | ..close(); 54 | } 55 | 56 | Point _getStartingPoint(Size size) { 57 | if (direction == Axis.horizontal) { 58 | // If the direction is horizontal, the divider is a vertical line and the 59 | // "start" is at the top. 60 | 61 | final double xCoord = switch (mainAxisAlignment) { 62 | // If the mainAxisAlignment is `start`, the divider should sit at the 63 | // very left edge of the available space. 64 | MainAxisAlignment.start => thickness / 2, 65 | // If the mainAxisAlignment is `end`, we want to draw the line at the 66 | // very right edge of the available space. 67 | MainAxisAlignment.end => size.width - (thickness / 2), 68 | // In all other cases, we want to draw the line down the middle of the 69 | // available space. 70 | _ => size.width / 2, 71 | }; 72 | 73 | return Point(xCoord, 0); 74 | } 75 | 76 | // If the direction is vertical, the divider is a horizontal line and the 77 | // "start" is at the left. 78 | 79 | final double yCoord = switch (mainAxisAlignment) { 80 | // If the mainAxisAlignment is `start`, the divider should be at the very 81 | // top of the available space. 82 | MainAxisAlignment.start => thickness / 2, 83 | // If the mainAxisAlignment is `end`, the divider should sit at the very 84 | // bottom edge of the available space. 85 | MainAxisAlignment.end => size.height - (thickness / 2), 86 | // In all other cases, the divider should sit in the middle of the 87 | // available space. 88 | _ => size.height / 2, 89 | }; 90 | 91 | return Point(0, yCoord); 92 | } 93 | 94 | Point _getEndingPoint(Size size) { 95 | if (direction == Axis.horizontal) { 96 | // If the direction is horizontal, the divider is a vertical line. 97 | 98 | final double xCoord = switch (mainAxisAlignment) { 99 | // If the mainAxisAlignment is "start", the divider should sit at the 100 | // left edge of the available space. 101 | MainAxisAlignment.start => thickness / 2, 102 | // If the mainAxisAlignment is `end`, the divider should sit at the 103 | // right edge of the available space. 104 | MainAxisAlignment.end => size.width - (thickness / 2), 105 | // In all other cases, the divider should sit in the middle of the 106 | // available space. 107 | _ => size.width / 2, 108 | }; 109 | 110 | return Point(xCoord, size.height); 111 | } 112 | 113 | // If the direction is vertical, the divider is a horizontal line. 114 | 115 | final double yCoord = switch (mainAxisAlignment) { 116 | // If the mainAxisAlignment is `start`, the divider should sit at the top 117 | // edge of the available space. 118 | MainAxisAlignment.start => thickness / 2, 119 | // If the mainAxisAlignment is `end`, the divider should sit at the bottom 120 | // edge of the available space. 121 | MainAxisAlignment.end => size.height - (thickness / 2), 122 | // In all other cases, the divider should sit in the middle of the 123 | // available space. 124 | _ => size.height / 2, 125 | }; 126 | 127 | return Point(size.width, yCoord); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /lib/src/extensions/box_constraints_ext.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | extension BoxConstraintsExtensions on BoxConstraints { 4 | double maxForDirection(Axis direction) => switch (direction) { 5 | Axis.horizontal => maxWidth, 6 | Axis.vertical => maxHeight, 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /lib/src/extensions/iterable_ext.dart: -------------------------------------------------------------------------------- 1 | extension IterableExtensions on Iterable { 2 | int nullCount() => where((item) => item == null).length; 3 | 4 | int count(bool Function(T) test) => where(test).length; 5 | 6 | Iterable evenIndices() => [ 7 | for (var i = 0; i < length; i++) ...[ 8 | if (i % 2 == 0) ...[ 9 | elementAt(i), 10 | ], 11 | ], 12 | ]; 13 | 14 | Iterable indicesWhere(bool Function(T) test) => [ 15 | for (var i = 0; i < length; i++) ...[ 16 | if (test(elementAt(i))) ...[ 17 | i, 18 | ], 19 | ], 20 | ]; 21 | } 22 | -------------------------------------------------------------------------------- /lib/src/extensions/num_ext.dart: -------------------------------------------------------------------------------- 1 | import 'package:decimal/decimal.dart'; 2 | 3 | extension DoubleExtensions on double { 4 | Decimal toDecimal() => Decimal.parse(toString()); 5 | } 6 | 7 | extension ListDoubleExtensions on Iterable { 8 | double sum() => fold(0.0, (sum, curr) => sum + curr); 9 | } 10 | 11 | extension ListIntExtensions on Iterable { 12 | int sum() => fold(0, (sum, curr) => sum + curr); 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/layout/resizable_layout_direction.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | sealed class ResizableLayoutDirection { 4 | const ResizableLayoutDirection._(); 5 | 6 | factory ResizableLayoutDirection.forAxis(Axis direction) { 7 | return switch (direction) { 8 | Axis.horizontal => ResizableHorizontalLayout(), 9 | Axis.vertical => ResizableVerticalLayout(), 10 | }; 11 | } 12 | 13 | double getMaxConstraint(BoxConstraints constraints); 14 | double getSizeDimension(Size size); 15 | Offset getOffset(double currentPosition); 16 | Size getSize(double value, BoxConstraints constraints); 17 | double getMinIntrinsicDimension(RenderBox child); 18 | BoxConstraints copyConstraintsWith(BoxConstraints constraints, double value); 19 | } 20 | 21 | @visibleForTesting 22 | class ResizableHorizontalLayout extends ResizableLayoutDirection { 23 | const ResizableHorizontalLayout() : super._(); 24 | 25 | @override 26 | double getMaxConstraint(BoxConstraints constraints) { 27 | return constraints.maxWidth; 28 | } 29 | 30 | @override 31 | Offset getOffset(double currentPosition) { 32 | return Offset(currentPosition, 0.0); 33 | } 34 | 35 | @override 36 | double getSizeDimension(Size size) { 37 | return size.width; 38 | } 39 | 40 | @override 41 | Size getSize(double value, BoxConstraints constraints) { 42 | return Size(value, constraints.maxHeight); 43 | } 44 | 45 | @override 46 | double getMinIntrinsicDimension(RenderBox child) { 47 | return child.getMinIntrinsicWidth(double.infinity); 48 | } 49 | 50 | @override 51 | BoxConstraints copyConstraintsWith(BoxConstraints constraints, double value) { 52 | return constraints.copyWith(minWidth: value, maxWidth: value); 53 | } 54 | } 55 | 56 | @visibleForTesting 57 | class ResizableVerticalLayout extends ResizableLayoutDirection { 58 | const ResizableVerticalLayout() : super._(); 59 | 60 | @override 61 | double getMaxConstraint(BoxConstraints constraints) { 62 | return constraints.maxHeight; 63 | } 64 | 65 | @override 66 | Offset getOffset(double currentPosition) { 67 | return Offset(0.0, currentPosition); 68 | } 69 | 70 | @override 71 | double getSizeDimension(Size size) { 72 | return size.height; 73 | } 74 | 75 | @override 76 | Size getSize(double value, BoxConstraints constraints) { 77 | return Size(constraints.maxWidth, value); 78 | } 79 | 80 | @override 81 | double getMinIntrinsicDimension(RenderBox child) { 82 | return child.getMinIntrinsicHeight(double.infinity); 83 | } 84 | 85 | @override 86 | BoxConstraints copyConstraintsWith(BoxConstraints constraints, double value) { 87 | return constraints.copyWith(minHeight: value, maxHeight: value); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lib/src/resizable_child.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_resizable_container/flutter_resizable_container.dart'; 4 | 5 | /// Controls the sizing parameters for the [child] Widget. 6 | class ResizableChild extends Equatable { 7 | /// Create a new instance of the [ResizableChild] class. 8 | const ResizableChild({ 9 | required this.child, 10 | this.key, 11 | this.size = const ResizableSize.expand(), 12 | this.divider = const ResizableDivider(), 13 | }); 14 | 15 | /// The (optional) key for this child Widget's container in the list. 16 | final Key? key; 17 | 18 | /// The size of the corresponding widget. May use a ratio of the 19 | /// available space, an absolute size in logical pixels, or it can 20 | /// auto-expand to fill available space. 21 | /// 22 | /// ```dart 23 | /// // Ratio of available space 24 | /// size: const ResizableSize.ratio(0.25); 25 | /// 26 | /// // Absolute size in logical pixels 27 | /// size: const ResizableSize.pixels(300); 28 | /// 29 | /// // Auto-fill available space 30 | /// size: const ResizableSize.expand(), 31 | /// 32 | /// // Conform to the child's intrinsic size 33 | /// size: const ResizableSize.shrink(), 34 | /// ``` 35 | final ResizableSize size; 36 | 37 | /// The child [Widget] to be displayed. 38 | final Widget child; 39 | 40 | /// The divider configuration to be used after this child. 41 | /// 42 | /// If not provided, the default divider will be used. 43 | /// 44 | /// If this is the last child, the divider will not be used. 45 | final ResizableDivider divider; 46 | 47 | @override 48 | bool get stringify => true; 49 | 50 | @override 51 | List get props => [size, child.key, child.runtimeType]; 52 | } 53 | -------------------------------------------------------------------------------- /lib/src/resizable_container.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_resizable_container/flutter_resizable_container.dart'; 4 | import 'package:flutter_resizable_container/src/extensions/box_constraints_ext.dart'; 5 | import 'package:flutter_resizable_container/src/extensions/iterable_ext.dart'; 6 | import 'package:flutter_resizable_container/src/extensions/num_ext.dart'; 7 | import 'package:flutter_resizable_container/src/layout/resizable_layout.dart'; 8 | import 'package:flutter_resizable_container/src/resizable_container_divider.dart'; 9 | import 'package:flutter_resizable_container/src/resizable_controller.dart'; 10 | 11 | /// A container that holds multiple child [Widget]s that can be resized. 12 | /// 13 | /// Dividing lines will be added between each child. Dragging the dividers 14 | /// will resize the children along the [direction] axis. 15 | class ResizableContainer extends StatefulWidget { 16 | /// Creates a new [ResizableContainer] with the given [direction] and list 17 | /// of [children] Widgets. 18 | const ResizableContainer({ 19 | super.key, 20 | required this.children, 21 | required this.direction, 22 | this.controller, 23 | this.cascadeNegativeDelta = false, 24 | }); 25 | 26 | /// A list of [ResizableChild] containing the child [Widget]s and 27 | /// their sizing configuration. 28 | final List children; 29 | 30 | /// The controller that will be used to manage programmatic resizing of the children. 31 | final ResizableController? controller; 32 | 33 | /// The direction along which the child widgets will be laid and resized. 34 | final Axis direction; 35 | 36 | /// When enabled, reducing the size of a child beyond its lower bound will reduce the 37 | /// size of its sibling(s). Defaults to `false`. 38 | final bool cascadeNegativeDelta; 39 | 40 | @override 41 | State createState() => _ResizableContainerState(); 42 | } 43 | 44 | class _ResizableContainerState extends State { 45 | late final controller = widget.controller ?? ResizableController(); 46 | late final isDefaultController = widget.controller == null; 47 | late final manager = ResizableControllerManager(controller); 48 | 49 | @override 50 | void initState() { 51 | super.initState(); 52 | 53 | manager.initChildren(widget.children); 54 | manager.setCascadeNegativeDelta(widget.cascadeNegativeDelta); 55 | } 56 | 57 | @override 58 | void didUpdateWidget(covariant ResizableContainer oldWidget) { 59 | final childrenChanged = !listEquals(oldWidget.children, widget.children); 60 | final directionChanged = oldWidget.direction != widget.direction; 61 | final cascadeChanged = 62 | oldWidget.cascadeNegativeDelta != widget.cascadeNegativeDelta; 63 | 64 | if (childrenChanged) { 65 | controller.setChildren(widget.children); 66 | } 67 | 68 | if (cascadeChanged) { 69 | manager.setCascadeNegativeDelta(widget.cascadeNegativeDelta); 70 | } 71 | 72 | if (childrenChanged || directionChanged) { 73 | manager.setNeedsLayout(); 74 | } 75 | 76 | super.didUpdateWidget(oldWidget); 77 | } 78 | 79 | @override 80 | void dispose() { 81 | if (isDefaultController) { 82 | controller.dispose(); 83 | } 84 | 85 | super.dispose(); 86 | } 87 | 88 | @override 89 | Widget build(BuildContext context) { 90 | return LayoutBuilder( 91 | builder: (context, constraints) { 92 | final availableSpace = _getAvailableSpace(constraints); 93 | manager.setAvailableSpace(availableSpace); 94 | 95 | return AnimatedBuilder( 96 | animation: controller, 97 | builder: (context, _) { 98 | if (controller.needsLayout) { 99 | return ResizableLayout( 100 | direction: widget.direction, 101 | onComplete: (sizes) { 102 | final childSizes = sizes.evenIndices().toList(); 103 | WidgetsBinding.instance.addPostFrameCallback((_) { 104 | manager.setRenderedSizes(childSizes); 105 | }); 106 | }, 107 | sizes: controller.sizes, 108 | resizableChildren: widget.children, 109 | children: [ 110 | for (var i = 0; i < widget.children.length; i++) ...[ 111 | widget.children[i].child, 112 | if (i < widget.children.length - 1) ...[ 113 | ResizableContainerDivider.placeholder( 114 | config: widget.children[i].divider, 115 | direction: widget.direction, 116 | ), 117 | ], 118 | ], 119 | ], 120 | ); 121 | } else { 122 | return Flex( 123 | crossAxisAlignment: CrossAxisAlignment.stretch, 124 | direction: widget.direction, 125 | children: [ 126 | for (var i = 0; i < widget.children.length; i++) ...[ 127 | Builder( 128 | key: widget.children[i].key, 129 | builder: (context) { 130 | final child = widget.children[i].child; 131 | 132 | final height = _getChildSize( 133 | index: i, 134 | direction: Axis.vertical, 135 | constraints: constraints, 136 | ); 137 | 138 | final width = _getChildSize( 139 | index: i, 140 | direction: Axis.horizontal, 141 | constraints: constraints, 142 | ); 143 | 144 | return SizedBox( 145 | height: height, 146 | width: width, 147 | child: child, 148 | ); 149 | }, 150 | ), 151 | if (i < widget.children.length - 1) ...[ 152 | ResizableContainerDivider( 153 | config: widget.children[i].divider, 154 | direction: widget.direction, 155 | onResizeUpdate: (delta) => manager.adjustChildSize( 156 | index: i, 157 | delta: delta, 158 | ), 159 | ), 160 | ], 161 | ], 162 | ], 163 | ); 164 | } 165 | }, 166 | ); 167 | }, 168 | ); 169 | } 170 | 171 | double _getAvailableSpace(BoxConstraints constraints) { 172 | final totalSpace = constraints.maxForDirection(widget.direction); 173 | final dividerSpace = widget.children 174 | .take(widget.children.length - 1) 175 | .map((child) => child.divider) 176 | .map((divider) => divider.thickness + divider.padding) 177 | .sum(); 178 | 179 | return totalSpace - dividerSpace; 180 | } 181 | 182 | double _getChildSize({ 183 | required int index, 184 | required Axis direction, 185 | required BoxConstraints constraints, 186 | }) { 187 | if (direction != direction) { 188 | return constraints.maxForDirection(direction); 189 | } else { 190 | return controller.pixels[index]; 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /lib/src/resizable_container_divider.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:flutter_resizable_container/flutter_resizable_container.dart'; 6 | import 'package:flutter_resizable_container/src/divider_painter.dart'; 7 | import 'package:flutter_resizable_container/src/resizable_divider.dart'; 8 | import 'package:flutter_resizable_container/src/resizable_size.dart'; 9 | 10 | class ResizableContainerDivider extends StatefulWidget { 11 | const ResizableContainerDivider({ 12 | super.key, 13 | required this.direction, 14 | required this.config, 15 | required void Function(double) this.onResizeUpdate, 16 | }); 17 | 18 | const ResizableContainerDivider.placeholder({ 19 | super.key, 20 | required this.config, 21 | required this.direction, 22 | }) : onResizeUpdate = null; 23 | 24 | final Axis direction; 25 | final void Function(double)? onResizeUpdate; 26 | final ResizableDivider config; 27 | 28 | @override 29 | State createState() => 30 | _ResizableContainerDividerState(); 31 | } 32 | 33 | class _ResizableContainerDividerState extends State { 34 | bool isDragging = false; 35 | bool isHovered = false; 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | return LayoutBuilder(builder: (context, constraints) { 40 | final width = _getWidth(constraints.maxWidth); 41 | final height = _getHeight(constraints.maxHeight); 42 | 43 | return Align( 44 | alignment: switch (widget.config.crossAxisAlignment) { 45 | CrossAxisAlignment.start => switch (widget.direction) { 46 | Axis.horizontal => Alignment.topCenter, 47 | Axis.vertical => Alignment.centerLeft, 48 | }, 49 | CrossAxisAlignment.end => switch (widget.direction) { 50 | Axis.horizontal => Alignment.bottomCenter, 51 | Axis.vertical => Alignment.bottomRight, 52 | }, 53 | _ => Alignment.center, 54 | }, 55 | child: MouseRegion( 56 | cursor: _getCursor(), 57 | onEnter: _onEnter, 58 | onExit: _onExit, 59 | child: GestureDetector( 60 | onVerticalDragStart: _onVerticalDragStart, 61 | onVerticalDragUpdate: _onVerticalDragUpdate, 62 | onVerticalDragEnd: _onVerticalDragEnd, 63 | onHorizontalDragStart: _onHorizontalDragStart, 64 | onHorizontalDragUpdate: _getOnHorizontalDragUpdate( 65 | Directionality.of(context), 66 | ), 67 | onHorizontalDragEnd: _onHorizontalDragEnd, 68 | onTapDown: _onTapDown, 69 | onTapUp: _onTapUp, 70 | child: CustomPaint( 71 | size: Size(width, height), 72 | painter: DividerPainter( 73 | direction: widget.direction, 74 | color: widget.config.color ?? Theme.of(context).dividerColor, 75 | thickness: widget.config.thickness, 76 | crossAxisAlignment: widget.config.crossAxisAlignment, 77 | length: widget.config.length, 78 | mainAxisAlignment: widget.config.mainAxisAlignment, 79 | padding: widget.config.padding, 80 | ), 81 | ), 82 | ), 83 | ), 84 | ); 85 | }); 86 | } 87 | 88 | MouseCursor _getCursor() { 89 | return switch (widget.direction) { 90 | Axis.horizontal => 91 | widget.config.cursor ?? SystemMouseCursors.resizeLeftRight, 92 | Axis.vertical => widget.config.cursor ?? SystemMouseCursors.resizeUpDown, 93 | }; 94 | } 95 | 96 | double _getHeight(double maxHeight) { 97 | return switch (widget.direction) { 98 | Axis.horizontal => switch (widget.config.length) { 99 | ResizableSizePixels(:final pixels) => min(pixels, maxHeight), 100 | ResizableSizeExpand() => maxHeight, 101 | ResizableSizeRatio(:final ratio) => maxHeight * ratio, 102 | ResizableSizeShrink() => 0.0, 103 | }, 104 | Axis.vertical => widget.config.thickness + widget.config.padding, 105 | }; 106 | } 107 | 108 | double _getWidth(double maxWidth) { 109 | return switch (widget.direction) { 110 | Axis.horizontal => widget.config.thickness + widget.config.padding, 111 | Axis.vertical => switch (widget.config.length) { 112 | ResizableSizePixels(:final pixels) => min(pixels, maxWidth), 113 | ResizableSizeExpand() => maxWidth, 114 | ResizableSizeRatio(:final ratio) => maxWidth * ratio, 115 | ResizableSizeShrink() => 0.0, 116 | }, 117 | }; 118 | } 119 | 120 | void _onEnter(PointerEnterEvent _) { 121 | setState(() => isHovered = true); 122 | widget.config.onHoverEnter?.call(); 123 | } 124 | 125 | void _onExit(PointerExitEvent _) { 126 | setState(() => isHovered = false); 127 | 128 | if (!isDragging) { 129 | widget.config.onHoverExit?.call(); 130 | } 131 | } 132 | 133 | void _onVerticalDragStart(DragStartDetails _) { 134 | if (widget.direction == Axis.vertical) { 135 | setState(() => isDragging = true); 136 | widget.config.onDragStart?.call(); 137 | } 138 | } 139 | 140 | void _onVerticalDragUpdate(DragUpdateDetails details) { 141 | if (widget.direction == Axis.vertical) { 142 | widget.onResizeUpdate?.call(details.delta.dy); 143 | } 144 | } 145 | 146 | void _onVerticalDragEnd(DragEndDetails _) { 147 | if (widget.direction == Axis.vertical) { 148 | setState(() => isDragging = false); 149 | widget.config.onDragEnd?.call(); 150 | 151 | if (!isHovered) { 152 | widget.config.onHoverExit?.call(); 153 | } 154 | } 155 | } 156 | 157 | void _onHorizontalDragStart(DragStartDetails _) { 158 | if (widget.direction == Axis.horizontal) { 159 | setState(() => isDragging = true); 160 | widget.config.onDragStart?.call(); 161 | } 162 | } 163 | 164 | void Function(DragUpdateDetails) _getOnHorizontalDragUpdate( 165 | TextDirection textDirection, 166 | ) { 167 | return (details) { 168 | if (widget.direction == Axis.horizontal) { 169 | final delta = details.delta.dx; 170 | 171 | widget.onResizeUpdate?.call(switch (textDirection) { 172 | TextDirection.ltr => delta, 173 | TextDirection.rtl => -delta, 174 | }); 175 | } 176 | }; 177 | } 178 | 179 | void _onHorizontalDragEnd(DragEndDetails _) { 180 | if (widget.direction == Axis.horizontal) { 181 | setState(() => isDragging = false); 182 | widget.config.onDragEnd?.call(); 183 | 184 | if (!isHovered) { 185 | widget.config.onHoverExit?.call(); 186 | } 187 | } 188 | } 189 | 190 | void _onTapDown(TapDownDetails _) { 191 | widget.config.onTapDown?.call(); 192 | } 193 | 194 | void _onTapUp(TapUpDetails _) { 195 | widget.config.onTapUp?.call(); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /lib/src/resizable_divider.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_resizable_container/src/resizable_size.dart'; 4 | 5 | class ResizableDivider extends Equatable { 6 | const ResizableDivider({ 7 | this.thickness = 1.0, 8 | this.length = const ResizableSize.expand(), 9 | this.padding = 0, 10 | this.color, 11 | this.onHoverEnter, 12 | this.onHoverExit, 13 | this.onTapDown, 14 | this.onTapUp, 15 | this.onDragEnd, 16 | this.onDragStart, 17 | this.cursor, 18 | this.mainAxisAlignment = MainAxisAlignment.center, 19 | this.crossAxisAlignment = CrossAxisAlignment.center, 20 | }) : assert(thickness > 0, '[thickness] must be > 0.'), 21 | assert( 22 | length is! ResizableSizeShrink, 23 | 'length does not support the "shrink" size', 24 | ); 25 | 26 | @override 27 | bool get stringify => true; 28 | 29 | /// The thickness of the line drawn within the divider. 30 | /// 31 | /// Defaults to 1.0. 32 | final double thickness; 33 | 34 | /// The length of the divider along the cross-axis. 35 | /// 36 | /// Defaults to `ResizableSize.expand()`, which will take up the full length. 37 | /// If given a pixel value, the length will be the smaller of the provided value 38 | /// and the available space. 39 | final ResizableSize length; 40 | 41 | /// The main-axis padding around the divider line. The position of the line 42 | /// within this padding is dictated by the [alignment] property. 43 | final double padding; 44 | 45 | /// The alignment of the divider within its main-axis padding; `spaceAround`, 46 | /// `spaceBetween`, and `spaceEvenly` will have no effect. 47 | /// 48 | /// For example, use [MainAxisAlignment.center] to position the divider in the 49 | /// middle of the padding. 50 | final MainAxisAlignment mainAxisAlignment; 51 | 52 | /// The alignment of the divider along the cross-axis. If the divider takes up 53 | /// the full available space, this setting will have no effect. `stretch` and 54 | /// `baseline` will also have no effect. 55 | /// 56 | /// For example, use [CrossAxisAlignment.center] to position the divider in the 57 | /// middle of its cross-axis space. This is useful for creating a small "handle" 58 | /// in the middle of the space. 59 | final CrossAxisAlignment crossAxisAlignment; 60 | 61 | /// The color of the dividers between children. 62 | /// 63 | /// Defaults to [ThemeData.dividerColor]. 64 | final Color? color; 65 | 66 | /// Triggers when the user's cursor begins hovering over this divider. 67 | final VoidCallback? onHoverEnter; 68 | 69 | /// Triggers when the user's cursor ends hovering over this divider. 70 | final VoidCallback? onHoverExit; 71 | 72 | /// Triggers when the user's tap is detected on this divider. 73 | final VoidCallback? onTapDown; 74 | 75 | /// Triggers when the user's tap is released on this divider. 76 | final VoidCallback? onTapUp; 77 | 78 | /// Triggers when the user begins dragging this divider. 79 | final VoidCallback? onDragStart; 80 | 81 | /// Triggers when the user stops dragging this divider. 82 | final VoidCallback? onDragEnd; 83 | 84 | /// The cursor to display when hovering over this divider. 85 | final MouseCursor? cursor; 86 | 87 | @override 88 | List get props => [ 89 | thickness, 90 | length, 91 | padding, 92 | color, 93 | onHoverEnter, 94 | onHoverExit, 95 | onTapDown, 96 | onTapUp, 97 | cursor, 98 | mainAxisAlignment, 99 | crossAxisAlignment, 100 | ]; 101 | } 102 | -------------------------------------------------------------------------------- /lib/src/resizable_size.dart: -------------------------------------------------------------------------------- 1 | sealed class ResizableSize { 2 | const ResizableSize._({this.min, this.max}) 3 | : assert( 4 | min == null || min >= 0, 5 | 'min must be greater than or equal to 0', 6 | ), 7 | assert( 8 | min == null || min < double.infinity, 9 | 'min cannot be equal to infinity', 10 | ), 11 | assert( 12 | max == null || max >= 0, 13 | 'max must be greater than or equal to 0', 14 | ), 15 | assert( 16 | max == null || max < double.infinity, 17 | 'max cannot be equal to infinity', 18 | ), 19 | assert( 20 | min == null || max == null || min <= max, 21 | 'min must be less than or equal to max', 22 | ); 23 | 24 | final double? min; 25 | final double? max; 26 | 27 | /// Creates a [ResizableSize] with a fixed size in pixels. 28 | /// 29 | /// 0 <= [pixels] < infinity 30 | /// 0 <= [min] < [max] < infinity 31 | const factory ResizableSize.pixels( 32 | double pixels, { 33 | double? min, 34 | double? max, 35 | }) = ResizableSizePixels._; 36 | 37 | /// Creates a [ResizableSize] with a size equal to a ratio of the available space. 38 | /// 39 | /// 0 <= [ratio] <= 1 40 | /// 0 <= [min] < [max] < infinity 41 | const factory ResizableSize.ratio( 42 | double ratio, { 43 | double? min, 44 | double? max, 45 | }) = ResizableSizeRatio._; 46 | 47 | /// Creates a [ResizableSize] that expands to fill the available space not taken 48 | /// by other resizable children. 49 | /// 50 | /// 1 <= [flex] < infinity 51 | /// 0 <= [min] < [max] < infinity 52 | const factory ResizableSize.expand({ 53 | int flex, 54 | double? min, 55 | double? max, 56 | }) = ResizableSizeExpand._; 57 | 58 | /// Creates a [ResizableSize] that shrinks to fit the size of its child [Widget]. 59 | /// 60 | /// 0 <= [min] < [max] < infinity 61 | const factory ResizableSize.shrink({ 62 | double? min, 63 | double? max, 64 | }) = ResizableSizeShrink._; 65 | } 66 | 67 | final class ResizableSizePixels extends ResizableSize { 68 | const ResizableSizePixels._(this.pixels, {super.min, super.max}) 69 | : assert(pixels >= 0, 'pixels must be greater than or equal to 0'), 70 | super._(); 71 | 72 | final double pixels; 73 | 74 | @override 75 | String toString() => 'ResizableSizePixels($pixels)'; 76 | 77 | @override 78 | operator ==(Object other) => 79 | other is ResizableSizePixels && other.pixels == pixels; 80 | 81 | @override 82 | int get hashCode => pixels.hashCode; 83 | } 84 | 85 | final class ResizableSizeRatio extends ResizableSize { 86 | const ResizableSizeRatio._(this.ratio, {super.min, super.max}) 87 | : assert(ratio >= 0, 'ratio must be greater than or equal to 0'), 88 | assert(ratio <= 1, 'ratio must be less than or equal to 1'), 89 | super._(); 90 | 91 | final double ratio; 92 | 93 | @override 94 | String toString() => 'ResizableSizeRatio($ratio)'; 95 | 96 | @override 97 | operator ==(Object other) => 98 | other is ResizableSizeRatio && other.ratio == ratio; 99 | 100 | @override 101 | int get hashCode => ratio.hashCode; 102 | } 103 | 104 | final class ResizableSizeExpand extends ResizableSize { 105 | const ResizableSizeExpand._({this.flex = 1, super.min, super.max}) 106 | : assert(flex > 0, 'flex must be greater than 0'), 107 | super._(); 108 | 109 | final int flex; 110 | 111 | @override 112 | String toString() => 'ResizableSizeExpand(flex: $flex)'; 113 | 114 | @override 115 | operator ==(Object other) => 116 | other is ResizableSizeExpand && other.flex == flex; 117 | 118 | @override 119 | int get hashCode => flex.hashCode; 120 | } 121 | 122 | final class ResizableSizeShrink extends ResizableSize { 123 | const ResizableSizeShrink._({super.min, super.max}) : super._(); 124 | 125 | @override 126 | String toString() => 'ResizableSizeShrink()'; 127 | } 128 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_resizable_container 2 | description: Add nestable, resizable containers to your Flutter app with ease. 3 | version: 4.2.0 4 | homepage: https://github.com/andyhorn/flutter_resizable_container 5 | 6 | environment: 7 | sdk: '>=3.0.0 <4.0.0' 8 | flutter: '>=1.17.0' 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | decimal: ^3.1.0 14 | equatable: ^2.0.7 15 | 16 | dev_dependencies: 17 | flutter_test: 18 | sdk: flutter 19 | flutter_lints: ">=3.0.1 <6.0.0" 20 | mocktail: ^1.0.3 21 | -------------------------------------------------------------------------------- /test/layout/resizable_layout_direction_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_resizable_container/src/layout/resizable_layout_direction.dart'; 3 | import 'package:flutter_test/flutter_test.dart'; 4 | 5 | void main() { 6 | group(ResizableLayoutDirection, () { 7 | group('forAxis', () { 8 | test('returns vertical layout for vertical Axis', () { 9 | final direction = ResizableLayoutDirection.forAxis(Axis.vertical); 10 | 11 | expect(direction, isA()); 12 | }); 13 | 14 | test('returns horizontal layout for horizontal Axis', () { 15 | final direction = ResizableLayoutDirection.forAxis(Axis.horizontal); 16 | 17 | expect(direction, isA()); 18 | }); 19 | }); 20 | 21 | group(ResizableHorizontalLayout, () { 22 | final layout = ResizableHorizontalLayout(); 23 | 24 | test('getMaxConstraint returns maxWidth from constraints', () { 25 | final constraints = BoxConstraints(maxWidth: 100, maxHeight: 200); 26 | 27 | final result = layout.getMaxConstraint(constraints); 28 | 29 | expect(result, 100); 30 | }); 31 | 32 | test('getOffset returns Offset with x position', () { 33 | final result = layout.getOffset(100); 34 | 35 | expect(result, const Offset(100, 0)); 36 | }); 37 | 38 | test('getSizeDimension returns width from size', () { 39 | final size = const Size(100, 200); 40 | 41 | final result = layout.getSizeDimension(size); 42 | 43 | expect(result, 100); 44 | }); 45 | 46 | test('getSize returns Size with value and maxHeight', () { 47 | final constraints = BoxConstraints(maxWidth: 100, maxHeight: 200); 48 | 49 | final result = layout.getSize(100, constraints); 50 | 51 | expect(result, const Size(100, 200)); 52 | }); 53 | 54 | testWidgets('getMinIntrinsicDimension return min intrinsic width', 55 | (tester) async { 56 | final key = GlobalKey(); 57 | final child = SizedBox(key: key, height: 100, width: 200); 58 | 59 | await tester.pumpWidget(MaterialApp( 60 | home: Scaffold( 61 | body: Center( 62 | child: child, 63 | ), 64 | ), 65 | )); 66 | 67 | final renderBox = key.currentContext?.findRenderObject() as RenderBox?; 68 | 69 | if (renderBox == null) { 70 | throw Exception('RenderBox not found'); 71 | } 72 | 73 | final result = layout.getMinIntrinsicDimension(renderBox); 74 | 75 | expect(result, 200); 76 | }); 77 | }); 78 | 79 | group(ResizableVerticalLayout, () { 80 | final layout = ResizableVerticalLayout(); 81 | 82 | test('getMaxConstraint returns maxHeight from constraints', () { 83 | final constraints = BoxConstraints(maxWidth: 100, maxHeight: 200); 84 | 85 | final result = layout.getMaxConstraint(constraints); 86 | 87 | expect(result, 200); 88 | }); 89 | 90 | test('getOffset returns Offset with y position', () { 91 | final result = layout.getOffset(100); 92 | 93 | expect(result, const Offset(0, 100)); 94 | }); 95 | 96 | test('getSizeDimension returns height from size', () { 97 | final size = const Size(100, 200); 98 | 99 | final result = layout.getSizeDimension(size); 100 | 101 | expect(result, 200); 102 | }); 103 | 104 | test('getSize returns Size with value and maxWidth', () { 105 | final constraints = BoxConstraints(maxWidth: 100, maxHeight: 200); 106 | 107 | final result = layout.getSize(200, constraints); 108 | 109 | expect(result, const Size(100, 200)); 110 | }); 111 | 112 | testWidgets('getMinIntrinsicDimension return min intrinsic height', 113 | (tester) async { 114 | final key = GlobalKey(); 115 | final child = SizedBox(key: key, height: 100, width: 200); 116 | 117 | await tester.pumpWidget(MaterialApp( 118 | home: Scaffold( 119 | body: Center( 120 | child: child, 121 | ), 122 | ), 123 | )); 124 | 125 | final renderBox = key.currentContext?.findRenderObject() as RenderBox?; 126 | 127 | if (renderBox == null) { 128 | throw Exception('RenderBox not found'); 129 | } 130 | 131 | final result = layout.getMinIntrinsicDimension(renderBox); 132 | 133 | expect(result, 100); 134 | }); 135 | }); 136 | }); 137 | } 138 | -------------------------------------------------------------------------------- /test/resizable_size_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_resizable_container/src/resizable_size.dart'; 3 | import 'package:flutter_test/flutter_test.dart'; 4 | 5 | void main() { 6 | WidgetsFlutterBinding.ensureInitialized(); 7 | group(ResizableSize, () { 8 | group('constructor', () { 9 | group('ratio', () { 10 | test('throws if the value is less than 0', () { 11 | expect(() => ResizableSize.ratio(-1), throwsAssertionError); 12 | }); 13 | 14 | test('throws if the value is greater than 1', () { 15 | expect(() => ResizableSize.ratio(1.1), throwsAssertionError); 16 | }); 17 | 18 | test('does not throw for a value of 0', () { 19 | expect(() => const ResizableSize.ratio(0), isNot(throwsA(anything))); 20 | }); 21 | 22 | test('does not throw for a value of 1', () { 23 | expect(() => const ResizableSize.ratio(1), isNot(throwsA(anything))); 24 | }); 25 | }); 26 | 27 | group('pixels', () { 28 | test('throws for a value less than 0', () { 29 | expect(() => ResizableSize.pixels(-1), throwsAssertionError); 30 | }); 31 | 32 | test('does not throw for a value of 0', () { 33 | expect(() => const ResizableSize.pixels(0), isNot(throwsA(anything))); 34 | }); 35 | }); 36 | 37 | group('equality', () { 38 | test('returns true for equal objects', () { 39 | expect( 40 | const ResizableSize.pixels(1) == const ResizableSize.pixels(1), 41 | isTrue, 42 | ); 43 | }); 44 | 45 | test('returns false for different objects', () { 46 | expect( 47 | const ResizableSize.pixels(1) == const ResizableSize.ratio(1), 48 | isFalse, 49 | ); 50 | }); 51 | 52 | test('returns false for similar objects with different values', () { 53 | expect( 54 | const ResizableSize.pixels(1) == const ResizableSize.pixels(2), 55 | isFalse, 56 | ); 57 | }); 58 | }); 59 | }); 60 | }); 61 | } 62 | --------------------------------------------------------------------------------