├── .gitignore ├── .idea ├── .gitignore ├── codeStyles │ └── Project.xml ├── libraries │ ├── Dart_Packages.xml │ ├── Dart_SDK.xml │ └── Flutter_Plugins.xml ├── misc.xml ├── modules.xml ├── vcs.xml └── workspace.xml ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── doc ├── wall_layout_horizontal.gif └── wall_layout_vertical.gif ├── example ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle.kts │ │ └── 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.kts │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle.kts ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h ├── lib │ └── main.dart ├── pubspec.yaml └── test │ └── widget_test.dart ├── flutter_wall_layout.iml ├── lib ├── flutter_wall_layout.dart ├── src │ ├── default_wall_builder.dart │ ├── stone.dart │ └── wall_layout.dart └── wall_builder.dart ├── pubspec.yaml └── test ├── stone_position_test.dart ├── stone_test.dart ├── wall_build_handler_test.dart ├── wall_layout_test.dart └── wall_size_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://www.dartlang.org/guides/libraries/private-files 2 | 3 | # Files and directories created by pub 4 | .dart_tool/ 5 | .packages 6 | build/ 7 | # If you're building an application, you may want to check-in your pubspec.lock 8 | pubspec.lock 9 | 10 | # Directory created by dartdoc 11 | # If you don't generate documentation locally you can remove this line. 12 | doc/api/ 13 | 14 | # Avoid committing generated Javascript files: 15 | *.dart.js 16 | *.info.json # Produced by the --dump-info flag. 17 | *.js # When generated by dart2js. Don't specify *.js if your 18 | # project includes source files written in JavaScript. 19 | *.js_ 20 | *.js.deps 21 | *.js.map 22 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Project exclude paths 2 | /. 3 | 4 | 5 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 |
116 |
-------------------------------------------------------------------------------- /.idea/libraries/Dart_Packages.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | -------------------------------------------------------------------------------- /.idea/libraries/Dart_SDK.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /.idea/libraries/Flutter_Plugins.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 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 | 37 | 38 | 40 | 41 | 42 | 47 | 48 | 49 | 51 | 52 | 53 | 55 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 75 | 76 | 81 | 82 | 87 | 88 | 93 | 94 | 99 | 100 | 105 | 106 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 1609438963798 128 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | file://$PROJECT_DIR$/lib/src/wall_layout.dart 139 | 186 140 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /.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: 78910062997c3a836feee883712c241a5fd22983 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [2.1.2] - 29/03/2025 2 | * Upgrade versions 3 | * Add primaryAxisStoneSize feature 4 | ## [2.1.1] - 26/09/2021 Fix documentation 5 | * Fix documentation 6 | 7 | ## [2.1.0] - 26/09/2021 Externalize positionning algorithm 8 | * Adding WallBuilder parameter, allowing you to define your own algorithm involved in positioning stones 9 | 10 | ## [2.0.0] - Migrate to Null Safety. 11 | * Migration to dart null safety. 12 | 13 | 14 | ## [1.0.0] - 31/12/2020. 15 | 16 | * Add base code that let you display a wall of stones, like Maya did (does the end of the world is tonight ? XD). 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 David GONZALEZ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flutter_wall_layout - Layout child having different shape into a vertical or horizontal list 2 | This library provides a unique widget, WallLayout, rendering into a list children having different shapes. 3 | 4 | ![](doc/wall_layout_vertical.gif) ![](doc/wall_layout_horizontal.gif) 5 | 6 | 7 | There is two main classes: WallLayout and Stone. 8 | - A wall is composed by a set of stones, arranged in a certain way. 9 | - A stone is a component defining its shape by a width and a height. 10 | 11 | ### Wall Layout Widget 12 | 13 | This component has been inspired by the ListView widget, in which you can choose scroll direction and the way. 14 | Added to that, you can also define how many rows or column have the wall, if you want to create a responsive design. 15 | ```dart 16 | WallLayout( 17 | stones: _buildStonesList(), 18 | layersCount: 3, 19 | scrollDirection: Axis.vertical, 20 | reverse: false, 21 | ); 22 | ``` 23 | 24 | ### Wall Layout Widget Properties 25 | 26 | | Parameter | Description | Default value | 27 | | ------------------ | ------------------------------------------------- | -------------- | 28 | | stones | List of Stone widgets, representing wall layout's children. | - | 29 | | layersCount | Define the number of layers the wall have. Must be higher or equal to 2. When direction is Axis.vertical, it defines the number of columns the wall has. When direction is Axis.horizontal, it defines the number of rows. | - | 30 | | wallBuilder | Define how the wall is built: where each stone is positioned within the wall.The wall builder is allowed to use less stones than provided, and can create new stones (see ./example for more details). | WallBuilder.standard() | 31 | | stonePadding | Padding between stones. | 16.0 | 32 | | primaryAxisStoneSize | Optional. If specified, indicates the exact size of each stone in the direction of the primary scroll axis. If not specified, each stone will be exactly square. | - | 33 | | scrollController | Same as [ListView.scrollController]: "control the position to which this scroll view is scrolled". | - | 34 | | physics | Same as [ListView.physics]: "How the scroll view should respond to user input". | - | 35 | | restorationId | Same as [ListView.restorationId]: used "to save and restore the scroll offset of the scrollable". | - | 36 | | dragStartBehavior | Same as [ListView.dragStartBehavior]: "Determines the way that drag start behavior is handled". | DragStartBehavior.start | 37 | | clipBehavior | Same as [ListView.clipBehavior]: "ways to clip a widget's content". | Clip.hardEdge | 38 | | primary | Same as [ListView.primary]: "Whether this is the primary scroll view associated with the parent [PrimaryScrollController]" | false | 39 | | scrollDirection | Same as [ListView.scrollDirection]: "axis along which the scroll view scrolls". | Axis.vertical | 40 | | reverse | Same as [ListView.reverse property]: "whether the scroll view scrolls in the reading direction". | false | 41 | 42 | ### Stone Widget 43 | 44 | A Stone Widget is a widget proxy defining stone's size (width and height). 45 | It's rendered object is optimized thanks to it's id property. 46 | 47 | ```dart 48 | Stone( 49 | id: 7, 50 | width: 2, 51 | height: 2, 52 | child: Container( 53 | color: Colors.red, 54 | child: Center(child: Text("2x2")), 55 | ), 56 | ); 57 | ``` 58 | 59 | ### Stone Widget Properties 60 | 61 | | Parameter | Description | Default value | 62 | | --------- | ------------------------------------------------------- | ------------- | 63 | | width | Stone width (relative to the number of wall divisions). Must be higher or equal to 1. | - | 64 | | height | Stone height (relative to the number of wall divisions). Must be higher or equal to 1. | - | 65 | | id | Proxy layout identifier: must be unique. | - | 66 | | child | Widget to render, where its size will be constrained by the stone width and height. | - | -------------------------------------------------------------------------------- /doc/wall_layout_horizontal.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/doc/wall_layout_horizontal.gif -------------------------------------------------------------------------------- /doc/wall_layout_vertical.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/doc/wall_layout_vertical.gif -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | -------------------------------------------------------------------------------- /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: "c23637390482d4cf9598c3ce3f2be31aa7332daf" 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: c23637390482d4cf9598c3ce3f2be31aa7332daf 17 | base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf 18 | - platform: android 19 | create_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf 20 | base_revision: c23637390482d4cf9598c3ce3f2be31aa7332daf 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://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at https://dart.dev/lints. 17 | # 18 | # Instead of disabling a lint rule for the entire project in the 19 | # section below, it can also be suppressed for a single line of code 20 | # or a specific dart file by using the `// ignore: name_of_lint` and 21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 22 | # producing the lint. 23 | rules: 24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 26 | 27 | # Additional information about this file can be found at 28 | # https://dart.dev/guides/language/analysis-options 29 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | .cxx/ 9 | 10 | # Remember to never publicly share your keystore. 11 | # See https://flutter.dev/to/reference-keystore 12 | key.properties 13 | **/*.keystore 14 | **/*.jks 15 | -------------------------------------------------------------------------------- /example/android/app/build.gradle.kts: -------------------------------------------------------------------------------- 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_11 15 | targetCompatibility = JavaVersion.VERSION_11 16 | } 17 | 18 | kotlinOptions { 19 | jvmTarget = JavaVersion.VERSION_11.toString() 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.getByName("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/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/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/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/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/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/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/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/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/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/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.kts: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() 9 | rootProject.layout.buildDirectory.value(newBuildDir) 10 | 11 | subprojects { 12 | val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) 13 | project.layout.buildDirectory.value(newSubprojectBuildDir) 14 | } 15 | subprojects { 16 | project.evaluationDependsOn(":app") 17 | } 18 | 19 | tasks.register("clean") { 20 | delete(rootProject.layout.buildDirectory) 21 | } 22 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -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.10.2-all.zip 6 | -------------------------------------------------------------------------------- /example/android/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | val flutterSdkPath = run { 3 | val properties = java.util.Properties() 4 | file("local.properties").inputStream().use { properties.load(it) } 5 | val flutterSdkPath = properties.getProperty("flutter.sdk") 6 | require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } 7 | 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.7.0" apply false 22 | id("org.jetbrains.kotlin.android") version "1.8.22" apply false 23 | } 24 | 25 | include(":app") 26 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXCopyFilesBuildPhase section */ 19 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 20 | isa = PBXCopyFilesBuildPhase; 21 | buildActionMask = 2147483647; 22 | dstPath = ""; 23 | dstSubfolderSpec = 10; 24 | files = ( 25 | ); 26 | name = "Embed Frameworks"; 27 | runOnlyForDeploymentPostprocessing = 0; 28 | }; 29 | /* End PBXCopyFilesBuildPhase section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 33 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 34 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 35 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 36 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 37 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 38 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 39 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 40 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 42 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 43 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 44 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | ); 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXFrameworksBuildPhase section */ 56 | 57 | /* Begin PBXGroup section */ 58 | 9740EEB11CF90186004384FC /* Flutter */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 62 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 63 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 64 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 65 | ); 66 | name = Flutter; 67 | sourceTree = ""; 68 | }; 69 | 97C146E51CF9000F007C117D = { 70 | isa = PBXGroup; 71 | children = ( 72 | 9740EEB11CF90186004384FC /* Flutter */, 73 | 97C146F01CF9000F007C117D /* Runner */, 74 | 97C146EF1CF9000F007C117D /* Products */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | 97C146EF1CF9000F007C117D /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 97C146EE1CF9000F007C117D /* Runner.app */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | 97C146F01CF9000F007C117D /* Runner */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 90 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 91 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 92 | 97C147021CF9000F007C117D /* Info.plist */, 93 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 94 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 95 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 96 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 97 | ); 98 | path = Runner; 99 | sourceTree = ""; 100 | }; 101 | /* End PBXGroup section */ 102 | 103 | /* Begin PBXNativeTarget section */ 104 | 97C146ED1CF9000F007C117D /* Runner */ = { 105 | isa = PBXNativeTarget; 106 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 107 | buildPhases = ( 108 | 9740EEB61CF901F6004384FC /* Run Script */, 109 | 97C146EA1CF9000F007C117D /* Sources */, 110 | 97C146EB1CF9000F007C117D /* Frameworks */, 111 | 97C146EC1CF9000F007C117D /* Resources */, 112 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 113 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 114 | ); 115 | buildRules = ( 116 | ); 117 | dependencies = ( 118 | ); 119 | name = Runner; 120 | productName = Runner; 121 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 122 | productType = "com.apple.product-type.application"; 123 | }; 124 | /* End PBXNativeTarget section */ 125 | 126 | /* Begin PBXProject section */ 127 | 97C146E61CF9000F007C117D /* Project object */ = { 128 | isa = PBXProject; 129 | attributes = { 130 | LastUpgradeCheck = 1020; 131 | ORGANIZATIONNAME = ""; 132 | TargetAttributes = { 133 | 97C146ED1CF9000F007C117D = { 134 | CreatedOnToolsVersion = 7.3.1; 135 | LastSwiftMigration = 1100; 136 | }; 137 | }; 138 | }; 139 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 140 | compatibilityVersion = "Xcode 9.3"; 141 | developmentRegion = en; 142 | hasScannedForEncodings = 0; 143 | knownRegions = ( 144 | en, 145 | Base, 146 | ); 147 | mainGroup = 97C146E51CF9000F007C117D; 148 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 149 | projectDirPath = ""; 150 | projectRoot = ""; 151 | targets = ( 152 | 97C146ED1CF9000F007C117D /* Runner */, 153 | ); 154 | }; 155 | /* End PBXProject section */ 156 | 157 | /* Begin PBXResourcesBuildPhase section */ 158 | 97C146EC1CF9000F007C117D /* Resources */ = { 159 | isa = PBXResourcesBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 163 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 164 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 165 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXResourcesBuildPhase section */ 170 | 171 | /* Begin PBXShellScriptBuildPhase section */ 172 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 173 | isa = PBXShellScriptBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | ); 177 | inputPaths = ( 178 | ); 179 | name = "Thin Binary"; 180 | outputPaths = ( 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | shellPath = /bin/sh; 184 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 185 | }; 186 | 9740EEB61CF901F6004384FC /* Run Script */ = { 187 | isa = PBXShellScriptBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | ); 191 | inputPaths = ( 192 | ); 193 | name = "Run Script"; 194 | outputPaths = ( 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | shellPath = /bin/sh; 198 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 199 | }; 200 | /* End PBXShellScriptBuildPhase section */ 201 | 202 | /* Begin PBXSourcesBuildPhase section */ 203 | 97C146EA1CF9000F007C117D /* Sources */ = { 204 | isa = PBXSourcesBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 208 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | }; 212 | /* End PBXSourcesBuildPhase section */ 213 | 214 | /* Begin PBXVariantGroup section */ 215 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 216 | isa = PBXVariantGroup; 217 | children = ( 218 | 97C146FB1CF9000F007C117D /* Base */, 219 | ); 220 | name = Main.storyboard; 221 | sourceTree = ""; 222 | }; 223 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 224 | isa = PBXVariantGroup; 225 | children = ( 226 | 97C147001CF9000F007C117D /* Base */, 227 | ); 228 | name = LaunchScreen.storyboard; 229 | sourceTree = ""; 230 | }; 231 | /* End PBXVariantGroup section */ 232 | 233 | /* Begin XCBuildConfiguration section */ 234 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 235 | isa = XCBuildConfiguration; 236 | buildSettings = { 237 | ALWAYS_SEARCH_USER_PATHS = NO; 238 | CLANG_ANALYZER_NONNULL = YES; 239 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 240 | CLANG_CXX_LIBRARY = "libc++"; 241 | CLANG_ENABLE_MODULES = YES; 242 | CLANG_ENABLE_OBJC_ARC = YES; 243 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 244 | CLANG_WARN_BOOL_CONVERSION = YES; 245 | CLANG_WARN_COMMA = YES; 246 | CLANG_WARN_CONSTANT_CONVERSION = YES; 247 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 248 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 249 | CLANG_WARN_EMPTY_BODY = YES; 250 | CLANG_WARN_ENUM_CONVERSION = YES; 251 | CLANG_WARN_INFINITE_RECURSION = YES; 252 | CLANG_WARN_INT_CONVERSION = YES; 253 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 254 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 255 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 256 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 257 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 258 | CLANG_WARN_STRICT_PROTOTYPES = YES; 259 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 260 | CLANG_WARN_UNREACHABLE_CODE = YES; 261 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 262 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 263 | COPY_PHASE_STRIP = NO; 264 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 265 | ENABLE_NS_ASSERTIONS = NO; 266 | ENABLE_STRICT_OBJC_MSGSEND = YES; 267 | GCC_C_LANGUAGE_STANDARD = gnu99; 268 | GCC_NO_COMMON_BLOCKS = YES; 269 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 270 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 271 | GCC_WARN_UNDECLARED_SELECTOR = YES; 272 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 273 | GCC_WARN_UNUSED_FUNCTION = YES; 274 | GCC_WARN_UNUSED_VARIABLE = YES; 275 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 276 | MTL_ENABLE_DEBUG_INFO = NO; 277 | SDKROOT = iphoneos; 278 | SUPPORTED_PLATFORMS = iphoneos; 279 | TARGETED_DEVICE_FAMILY = "1,2"; 280 | VALIDATE_PRODUCT = YES; 281 | }; 282 | name = Profile; 283 | }; 284 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 285 | isa = XCBuildConfiguration; 286 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 287 | buildSettings = { 288 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 289 | CLANG_ENABLE_MODULES = YES; 290 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 291 | ENABLE_BITCODE = NO; 292 | FRAMEWORK_SEARCH_PATHS = ( 293 | "$(inherited)", 294 | "$(PROJECT_DIR)/Flutter", 295 | ); 296 | INFOPLIST_FILE = Runner/Info.plist; 297 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 298 | LIBRARY_SEARCH_PATHS = ( 299 | "$(inherited)", 300 | "$(PROJECT_DIR)/Flutter", 301 | ); 302 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example; 303 | PRODUCT_NAME = "$(TARGET_NAME)"; 304 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 305 | SWIFT_VERSION = 5.0; 306 | VERSIONING_SYSTEM = "apple-generic"; 307 | }; 308 | name = Profile; 309 | }; 310 | 97C147031CF9000F007C117D /* Debug */ = { 311 | isa = XCBuildConfiguration; 312 | buildSettings = { 313 | ALWAYS_SEARCH_USER_PATHS = NO; 314 | CLANG_ANALYZER_NONNULL = YES; 315 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 316 | CLANG_CXX_LIBRARY = "libc++"; 317 | CLANG_ENABLE_MODULES = YES; 318 | CLANG_ENABLE_OBJC_ARC = YES; 319 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 320 | CLANG_WARN_BOOL_CONVERSION = YES; 321 | CLANG_WARN_COMMA = YES; 322 | CLANG_WARN_CONSTANT_CONVERSION = YES; 323 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 324 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 325 | CLANG_WARN_EMPTY_BODY = YES; 326 | CLANG_WARN_ENUM_CONVERSION = YES; 327 | CLANG_WARN_INFINITE_RECURSION = YES; 328 | CLANG_WARN_INT_CONVERSION = YES; 329 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 330 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 331 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 332 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 333 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 334 | CLANG_WARN_STRICT_PROTOTYPES = YES; 335 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 336 | CLANG_WARN_UNREACHABLE_CODE = YES; 337 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 338 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 339 | COPY_PHASE_STRIP = NO; 340 | DEBUG_INFORMATION_FORMAT = dwarf; 341 | ENABLE_STRICT_OBJC_MSGSEND = YES; 342 | ENABLE_TESTABILITY = YES; 343 | GCC_C_LANGUAGE_STANDARD = gnu99; 344 | GCC_DYNAMIC_NO_PIC = NO; 345 | GCC_NO_COMMON_BLOCKS = YES; 346 | GCC_OPTIMIZATION_LEVEL = 0; 347 | GCC_PREPROCESSOR_DEFINITIONS = ( 348 | "DEBUG=1", 349 | "$(inherited)", 350 | ); 351 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 352 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 353 | GCC_WARN_UNDECLARED_SELECTOR = YES; 354 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 355 | GCC_WARN_UNUSED_FUNCTION = YES; 356 | GCC_WARN_UNUSED_VARIABLE = YES; 357 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 358 | MTL_ENABLE_DEBUG_INFO = YES; 359 | ONLY_ACTIVE_ARCH = YES; 360 | SDKROOT = iphoneos; 361 | TARGETED_DEVICE_FAMILY = "1,2"; 362 | }; 363 | name = Debug; 364 | }; 365 | 97C147041CF9000F007C117D /* Release */ = { 366 | isa = XCBuildConfiguration; 367 | buildSettings = { 368 | ALWAYS_SEARCH_USER_PATHS = NO; 369 | CLANG_ANALYZER_NONNULL = YES; 370 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 371 | CLANG_CXX_LIBRARY = "libc++"; 372 | CLANG_ENABLE_MODULES = YES; 373 | CLANG_ENABLE_OBJC_ARC = YES; 374 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 375 | CLANG_WARN_BOOL_CONVERSION = YES; 376 | CLANG_WARN_COMMA = YES; 377 | CLANG_WARN_CONSTANT_CONVERSION = YES; 378 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 379 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 380 | CLANG_WARN_EMPTY_BODY = YES; 381 | CLANG_WARN_ENUM_CONVERSION = YES; 382 | CLANG_WARN_INFINITE_RECURSION = YES; 383 | CLANG_WARN_INT_CONVERSION = YES; 384 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 385 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 386 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 387 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 388 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 389 | CLANG_WARN_STRICT_PROTOTYPES = YES; 390 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 391 | CLANG_WARN_UNREACHABLE_CODE = YES; 392 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 393 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 394 | COPY_PHASE_STRIP = NO; 395 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 396 | ENABLE_NS_ASSERTIONS = NO; 397 | ENABLE_STRICT_OBJC_MSGSEND = YES; 398 | GCC_C_LANGUAGE_STANDARD = gnu99; 399 | GCC_NO_COMMON_BLOCKS = YES; 400 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 401 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 402 | GCC_WARN_UNDECLARED_SELECTOR = YES; 403 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 404 | GCC_WARN_UNUSED_FUNCTION = YES; 405 | GCC_WARN_UNUSED_VARIABLE = YES; 406 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 407 | MTL_ENABLE_DEBUG_INFO = NO; 408 | SDKROOT = iphoneos; 409 | SUPPORTED_PLATFORMS = iphoneos; 410 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 411 | TARGETED_DEVICE_FAMILY = "1,2"; 412 | VALIDATE_PRODUCT = YES; 413 | }; 414 | name = Release; 415 | }; 416 | 97C147061CF9000F007C117D /* Debug */ = { 417 | isa = XCBuildConfiguration; 418 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 419 | buildSettings = { 420 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 421 | CLANG_ENABLE_MODULES = YES; 422 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 423 | ENABLE_BITCODE = NO; 424 | FRAMEWORK_SEARCH_PATHS = ( 425 | "$(inherited)", 426 | "$(PROJECT_DIR)/Flutter", 427 | ); 428 | INFOPLIST_FILE = Runner/Info.plist; 429 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 430 | LIBRARY_SEARCH_PATHS = ( 431 | "$(inherited)", 432 | "$(PROJECT_DIR)/Flutter", 433 | ); 434 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example; 435 | PRODUCT_NAME = "$(TARGET_NAME)"; 436 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 437 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 438 | SWIFT_VERSION = 5.0; 439 | VERSIONING_SYSTEM = "apple-generic"; 440 | }; 441 | name = Debug; 442 | }; 443 | 97C147071CF9000F007C117D /* Release */ = { 444 | isa = XCBuildConfiguration; 445 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 446 | buildSettings = { 447 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 448 | CLANG_ENABLE_MODULES = YES; 449 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 450 | ENABLE_BITCODE = NO; 451 | FRAMEWORK_SEARCH_PATHS = ( 452 | "$(inherited)", 453 | "$(PROJECT_DIR)/Flutter", 454 | ); 455 | INFOPLIST_FILE = Runner/Info.plist; 456 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 457 | LIBRARY_SEARCH_PATHS = ( 458 | "$(inherited)", 459 | "$(PROJECT_DIR)/Flutter", 460 | ); 461 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example; 462 | PRODUCT_NAME = "$(TARGET_NAME)"; 463 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 464 | SWIFT_VERSION = 5.0; 465 | VERSIONING_SYSTEM = "apple-generic"; 466 | }; 467 | name = Release; 468 | }; 469 | /* End XCBuildConfiguration section */ 470 | 471 | /* Begin XCConfigurationList section */ 472 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 473 | isa = XCConfigurationList; 474 | buildConfigurations = ( 475 | 97C147031CF9000F007C117D /* Debug */, 476 | 97C147041CF9000F007C117D /* Release */, 477 | 249021D3217E4FDB00AE95B9 /* Profile */, 478 | ); 479 | defaultConfigurationIsVisible = 0; 480 | defaultConfigurationName = Release; 481 | }; 482 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 483 | isa = XCConfigurationList; 484 | buildConfigurations = ( 485 | 97C147061CF9000F007C117D /* Debug */, 486 | 97C147071CF9000F007C117D /* Release */, 487 | 249021D4217E4FDB00AE95B9 /* Profile */, 488 | ); 489 | defaultConfigurationIsVisible = 0; 490 | defaultConfigurationName = Release; 491 | }; 492 | /* End XCConfigurationList section */ 493 | }; 494 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 495 | } 496 | -------------------------------------------------------------------------------- /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 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/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/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/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/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/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/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/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/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/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/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/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/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/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/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/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/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/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/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/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/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/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/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/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/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/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/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/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/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/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/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GONZALEZD/flutter_wall_layout/d6e4aa5d25fe57cf889de95d396731005b31ae30/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 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_wall_layout/flutter_wall_layout.dart'; 6 | import 'package:flutter_wall_layout/wall_builder.dart'; 7 | 8 | void main() { 9 | runApp(MyApp()); 10 | } 11 | 12 | class MyApp extends StatelessWidget { 13 | const MyApp(); 14 | @override 15 | Widget build(BuildContext context) { 16 | return MaterialApp( 17 | title: 'Wall Layout Demo', 18 | theme: ThemeData( 19 | primarySwatch: Colors.blue, 20 | visualDensity: VisualDensity.adaptivePlatformDensity, 21 | colorScheme: ColorScheme.light(background: Color(0xFFF5F5F5)), 22 | ), 23 | home: MyHomePage(title: 'Wall Layout Demo'), 24 | ); 25 | } 26 | } 27 | 28 | class MyHomePage extends StatefulWidget { 29 | MyHomePage({Key? key, required this.title}) : super(key: key); 30 | 31 | final String title; 32 | 33 | @override 34 | _MyHomePageState createState() => _MyHomePageState(); 35 | } 36 | 37 | class _MyHomePageState extends State with TickerProviderStateMixin { 38 | late final AnimationController _controller; 39 | late bool _fixedPrimaryAxisStoneSize; 40 | late bool _reversed; 41 | late Axis _direction; 42 | late int _nbLayers; 43 | late bool _wrapedOptions; 44 | bool _random = false; 45 | late List _stones; 46 | 47 | @override 48 | void initState() { 49 | super.initState(); 50 | _controller = 51 | AnimationController(duration: Duration(milliseconds: 500), vsync: this); 52 | _fixedPrimaryAxisStoneSize = false; 53 | _reversed = false; 54 | _direction = Axis.vertical; 55 | _nbLayers = 3; 56 | _controller.forward(from: 0); 57 | _wrapedOptions = true; 58 | _stones = _buildStonesList(); 59 | } 60 | 61 | @override 62 | Widget build(BuildContext context) { 63 | final backgroundColor = Theme.of(context).colorScheme.background; 64 | return Scaffold( 65 | backgroundColor: backgroundColor, 66 | appBar: AppBar( 67 | title: Text(this.widget.title), 68 | actions: [ 69 | IconButton( 70 | onPressed: _onRandomClicked, 71 | icon: Icon( 72 | _random ? Icons.sync : Icons.sync_disabled, 73 | ), 74 | tooltip: "Random stone sizes + custom WallHandler", 75 | ) 76 | ], 77 | ), 78 | body: buildWallLayout(), 79 | floatingActionButton: _buildOptions(context), 80 | ); 81 | } 82 | 83 | void _onRandomClicked() { 84 | setState(() { 85 | _random = !_random; 86 | if (_random) { 87 | _stones = _buildRandomStonesList(_nbLayers); 88 | } else { 89 | _stones = _buildStonesList(); 90 | } 91 | }); 92 | } 93 | 94 | Widget _buildOptions(BuildContext context) { 95 | return AnimatedSize( 96 | duration: Duration(milliseconds: 200), 97 | reverseDuration: Duration(milliseconds: 200), 98 | alignment: Alignment.bottomRight, 99 | child: Container( 100 | margin: EdgeInsets.only(left: 32), 101 | decoration: BoxDecoration( 102 | color: Theme.of(context).colorScheme.background, 103 | boxShadow: [ 104 | BoxShadow(color: Colors.black26, blurRadius: 6.0), 105 | ], 106 | borderRadius: BorderRadius.circular(30.0), 107 | ), 108 | child: Column( 109 | mainAxisSize: MainAxisSize.min, 110 | crossAxisAlignment: CrossAxisAlignment.end, 111 | children: [ 112 | if (!_wrapedOptions) 113 | Padding( 114 | padding: EdgeInsets.symmetric(vertical: 20.0), 115 | child: Column( 116 | mainAxisSize: MainAxisSize.min, 117 | crossAxisAlignment: CrossAxisAlignment.end, 118 | children: [ 119 | __buildDivisionsOption(), 120 | __buildDirectionOption(), 121 | __buildReverseOption(), 122 | __buildFixedPrimaryAxisStoneSizeOption(), 123 | ], 124 | ), 125 | ), 126 | FloatingActionButton( 127 | elevation: 0.0, 128 | highlightElevation: 0.0, 129 | onPressed: () => setState(() => _wrapedOptions = !_wrapedOptions), 130 | child: Icon(Icons.build), 131 | ), 132 | ], 133 | ), 134 | ), 135 | ); 136 | } 137 | 138 | Widget __buildDivisionsOption() { 139 | return _buildOption( 140 | "Layers", 141 | CupertinoSegmentedControl( 142 | groupValue: _nbLayers, 143 | children: {2: Text("2"), 3: Text("3"), 4: Text("4")}, 144 | onValueChanged: (value) => setState(() { 145 | _controller.forward(from: 0.0); 146 | _nbLayers = value; 147 | if (_random) { 148 | _stones = _buildRandomStonesList(_nbLayers); 149 | } 150 | }), 151 | ), 152 | ); 153 | } 154 | 155 | Widget __buildFixedPrimaryAxisStoneSizeOption() { 156 | return _buildOption( 157 | "Fixed Primary Axis Size", 158 | CupertinoSegmentedControl( 159 | groupValue: _fixedPrimaryAxisStoneSize, 160 | children: {false: Text("no"), true: Text("yes (300px)")}, 161 | onValueChanged: (value) => setState(() { 162 | _controller.forward(from: 0.0); 163 | _fixedPrimaryAxisStoneSize = value; 164 | }), 165 | ), 166 | ); 167 | } 168 | 169 | Widget __buildReverseOption() { 170 | return _buildOption( 171 | "Reverse", 172 | CupertinoSegmentedControl( 173 | groupValue: _reversed, 174 | children: {false: Text("no"), true: Text("yes")}, 175 | onValueChanged: (value) => setState(() { 176 | _controller.forward(from: 0.0); 177 | _reversed = value; 178 | }), 179 | ), 180 | ); 181 | } 182 | 183 | Widget __buildDirectionOption() { 184 | return _buildOption( 185 | "Direction", 186 | CupertinoSegmentedControl( 187 | groupValue: _direction, 188 | children: { 189 | Axis.vertical: Text("vertical"), 190 | Axis.horizontal: Text("horizontal") 191 | }, 192 | onValueChanged: (value) => setState(() { 193 | _controller.forward(from: 0.0); 194 | _direction = value; 195 | }), 196 | ), 197 | ); 198 | } 199 | 200 | Widget _buildOption(String text, Widget child) { 201 | return Padding( 202 | padding: const EdgeInsets.only(top: 4.0, left: 8.0, bottom: 4.0), 203 | child: Row( 204 | mainAxisSize: MainAxisSize.min, 205 | mainAxisAlignment: MainAxisAlignment.end, 206 | children: [ 207 | Flexible( 208 | child: Text(text), 209 | flex: 1, 210 | ), 211 | Expanded( 212 | child: child, 213 | flex: 2, 214 | ), 215 | ], 216 | ), 217 | ); 218 | } 219 | 220 | Widget buildWallLayout() { 221 | return WallLayout( 222 | wallBuilder: _random 223 | ? FillingHolesWallBuildHandler( 224 | context: context, 225 | childBuilder: (_) => DecoratedBox( 226 | decoration: BoxDecoration( 227 | color: Color(0xffececee), 228 | borderRadius: BorderRadius.circular(12)), 229 | ), 230 | ) 231 | : WallBuilder.standard(), 232 | scrollDirection: _direction, 233 | stones: _stones, 234 | reverse: _reversed, 235 | layersCount: _nbLayers, 236 | primaryAxisStoneSize: _fixedPrimaryAxisStoneSize ? 300 : null, 237 | ); 238 | } 239 | 240 | List _buildRandomStonesList(int maxLayer) { 241 | Random r = Random(); 242 | final next = () => r.nextInt(maxLayer) + 1; 243 | final colors = [ 244 | Colors.red, 245 | Colors.greenAccent, 246 | Colors.lightBlue, 247 | Colors.purple, 248 | Colors.yellow, 249 | Colors.cyanAccent, 250 | Colors.orange, 251 | Colors.green, 252 | Colors.pink, 253 | Colors.blueAccent, 254 | Colors.amber, 255 | Colors.teal, 256 | Colors.lightGreenAccent, 257 | Colors.deepOrange, 258 | Colors.deepPurpleAccent, 259 | Colors.lightBlueAccent, 260 | Colors.limeAccent, 261 | ]; 262 | return colors.map((color) { 263 | int width = next(); 264 | int height = next(); 265 | return Stone( 266 | id: colors.indexOf(color), 267 | width: width, 268 | height: height, 269 | child: __buildStoneChild( 270 | background: color, 271 | text: "${width}x$height", 272 | surface: (width * height).toDouble(), 273 | ), 274 | ); 275 | }).toList(); 276 | } 277 | 278 | List _buildStonesList() { 279 | final data = [ 280 | {"color": Colors.red, "width": 2, "height": 2}, 281 | {"color": Colors.greenAccent, "width": 1, "height": 1}, 282 | {"color": Colors.lightBlue, "width": 1, "height": 2}, 283 | {"color": Colors.purple, "width": 2, "height": 1}, 284 | {"color": Colors.yellow, "width": 1, "height": 1}, 285 | {"color": Colors.cyanAccent, "width": 1, "height": 1}, 286 | {"color": Colors.orange, "width": 2, "height": 2}, 287 | {"color": Colors.green, "width": 1, "height": 1}, 288 | {"color": Colors.pink, "width": 2, "height": 1}, 289 | {"color": Colors.blueAccent, "width": 1, "height": 1}, 290 | {"color": Colors.amber, "width": 1, "height": 2}, 291 | {"color": Colors.teal, "width": 2, "height": 1}, 292 | {"color": Colors.lightGreenAccent, "width": 1, "height": 1}, 293 | {"color": Colors.deepOrange, "width": 1, "height": 1}, 294 | {"color": Colors.deepPurpleAccent, "width": 2, "height": 2}, 295 | {"color": Colors.lightBlueAccent, "width": 1, "height": 1}, 296 | {"color": Colors.limeAccent, "width": 1, "height": 1}, 297 | ]; 298 | return data.map((d) { 299 | int width = d["width"] as int; 300 | int height = d["height"] as int; 301 | return Stone( 302 | id: data.indexOf(d), 303 | width: width, 304 | height: height, 305 | child: __buildStoneChild( 306 | background: d["color"] as Color, 307 | text: "${width}x$height", 308 | surface: (width * height).toDouble(), 309 | ), 310 | ); 311 | }).toList(); 312 | } 313 | 314 | Widget __buildStoneChild( 315 | {required Color background, 316 | required String text, 317 | required double surface}) { 318 | return ScaleTransition( 319 | scale: CurveTween(curve: Interval(0.0, min(1.0, 0.25 + surface / 6.0))) 320 | .animate(_controller), 321 | child: Container( 322 | alignment: Alignment.center, 323 | decoration: BoxDecoration( 324 | gradient: LinearGradient( 325 | colors: [ 326 | background, 327 | Color.alphaBlend(background.withOpacity(0.6), Colors.black) 328 | ], 329 | begin: Alignment.topLeft, 330 | end: Alignment.bottomRight, 331 | ), 332 | borderRadius: BorderRadius.circular(12), 333 | ), 334 | child: 335 | Text(text, style: TextStyle(color: Colors.white, fontSize: 32.0)), 336 | ), 337 | ); 338 | } 339 | } 340 | 341 | // Fill empty place with stone 1x1 342 | class FillingHolesWallBuildHandler extends WallBuilder { 343 | final WallBuilder proxy = WallBuilder.standard(); 344 | final BuildContext context; 345 | final WidgetBuilder childBuilder; 346 | 347 | FillingHolesWallBuildHandler( 348 | {required this.childBuilder, required this.context}) 349 | : super(); 350 | 351 | WallBlueprint _buildBlueprint(List stones) { 352 | return proxy.build( 353 | mainAxisSeparations: mainAxisSeparations, 354 | reverse: reverse, 355 | direction: direction, 356 | stones: stones); 357 | } 358 | 359 | void _findHoles(WallBlueprint blueprint, Function(int x, int y) onHoleFound) { 360 | List bounds = blueprint.stonesPosition 361 | .map((key, value) => MapEntry( 362 | key, 363 | Rect.fromLTWH(value.x.toDouble(), value.y.toDouble(), 364 | key.width.toDouble(), key.height.toDouble()))) 365 | .values 366 | .toList(); 367 | for (int x = 0; x < blueprint.size.width; x++) { 368 | for (int y = 0; y < blueprint.size.height; y++) { 369 | Rect area = Rect.fromLTWH(x.toDouble(), y.toDouble(), 1.0, 1.0); 370 | bounds.firstWhere( 371 | (element) => area.overlaps(element), 372 | orElse: () { 373 | onHoleFound(x, y); 374 | return area; 375 | }, 376 | ); 377 | bounds.add(area); 378 | } 379 | } 380 | } 381 | 382 | @override 383 | Map computeStonePositions(List stones) { 384 | final blueprint = _buildBlueprint(stones); 385 | Map positions = blueprint.stonesPosition; 386 | int idStart = 10000; 387 | _findHoles(blueprint, (x, y) { 388 | final stone = Stone( 389 | height: 1, 390 | width: 1, 391 | id: idStart++, 392 | child: childBuilder.call(context), 393 | ); 394 | positions[stone] = StoneStartPosition(x: x, y: y); 395 | }); 396 | return positions; 397 | } 398 | } 399 | -------------------------------------------------------------------------------- /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 `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 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 | version: 2.1.0+2 19 | 20 | environment: 21 | sdk: ">=2.12.0 <3.0.0" 22 | 23 | dependencies: 24 | flutter: 25 | sdk: flutter 26 | 27 | # flutter wall layout (relative dependency) 28 | flutter_wall_layout: 29 | path: "../" 30 | 31 | 32 | # The following adds the Cupertino Icons font to your application. 33 | # Use with the CupertinoIcons class for iOS style icons. 34 | cupertino_icons: ^1.0.0 35 | 36 | dev_dependencies: 37 | flutter_test: 38 | sdk: flutter 39 | 40 | # For information on the generic Dart part of this file, see the 41 | # following page: https://dart.dev/tools/pub/pubspec 42 | 43 | # The following section is specific to Flutter. 44 | flutter: 45 | 46 | # The following line ensures that the Material Icons font is 47 | # included with your application, so that you can use the icons in 48 | # the material Icons class. 49 | uses-material-design: true 50 | 51 | # To add assets to your application, add an assets section, like this: 52 | # assets: 53 | # - images/a_dot_burr.jpeg 54 | # - images/a_dot_ham.jpeg 55 | 56 | # An image asset can refer to one or more resolution-specific "variants", see 57 | # https://flutter.dev/assets-and-images/#resolution-aware. 58 | 59 | # For details regarding adding assets from package dependencies, see 60 | # https://flutter.dev/assets-and-images/#from-packages 61 | 62 | # To add custom fonts to your application, add a fonts section here, 63 | # in this "flutter" section. Each entry in this list should have a 64 | # "family" key with the font family name, and a "fonts" key with a 65 | # list giving the asset and other descriptors for the font. For 66 | # example: 67 | # fonts: 68 | # - family: Schyler 69 | # fonts: 70 | # - asset: fonts/Schyler-Regular.ttf 71 | # - asset: fonts/Schyler-Italic.ttf 72 | # style: italic 73 | # - family: Trajan Pro 74 | # fonts: 75 | # - asset: fonts/TrajanPro.ttf 76 | # - asset: fonts/TrajanPro_Bold.ttf 77 | # weight: 700 78 | # 79 | # For details regarding fonts from package dependencies, 80 | # see https://flutter.dev/custom-fonts/#from-packages 81 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility in the flutter_test package. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:example/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(const MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /flutter_wall_layout.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /lib/flutter_wall_layout.dart: -------------------------------------------------------------------------------- 1 | library flutter_wall_layout; 2 | 3 | export 'package:flutter_wall_layout/src/wall_layout.dart' show WallLayout; 4 | export 'package:flutter_wall_layout/src/stone.dart' show Stone; 5 | -------------------------------------------------------------------------------- /lib/src/default_wall_builder.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_wall_layout/src/stone.dart'; 6 | 7 | import '../wall_builder.dart'; 8 | 9 | /// Class determining how the wall will be built. 10 | /// It main goals are to compute wall height and position every stones into the wall. 11 | class DefaultWallBuildHandler extends WallBuilder { 12 | /// Internal attribute storing stones positions as a 2D Table. 13 | @visibleForTesting 14 | late List grid; 15 | int _gridLastIndex = 0; 16 | 17 | DefaultWallBuildHandler() : super(); 18 | 19 | /// Compute stones position and wall size. 20 | /// Must be executed before accessing to wall size property and getPosition method. 21 | @override 22 | Map computeStonePositions(List stones) { 23 | // instantiate grid 24 | final surface = stones.fold(0, (sum, cell) => sum + cell.surface); 25 | grid = List.generate(surface * mainAxisSeparations, (index) => null); 26 | 27 | // set stones positions in grid 28 | stones.forEach((stone) => computeStonePosition(stone)); 29 | 30 | //remove unwanted grid data 31 | int startRemove = _gridLastIndex + 1; 32 | int diff = startRemove % mainAxisSeparations; 33 | if (diff > 0) { 34 | startRemove += mainAxisSeparations - diff; 35 | } 36 | grid.removeRange(startRemove, grid.length); 37 | 38 | //reverse grid if we are in reverse display mode 39 | if (reverse) { 40 | grid = grid.reversed.toList(); 41 | } 42 | 43 | return stones 44 | .asMap() 45 | .map((key, value) => MapEntry(value, _getPosition(value))); 46 | } 47 | 48 | bool __canFit(Stone stone, int firstIndex) { 49 | final placeLeft = 50 | this.mainAxisSeparations - (firstIndex % this.mainAxisSeparations); 51 | if ((this.direction == Axis.vertical ? stone.width : stone.height) > 52 | placeLeft) { 53 | return false; 54 | } 55 | 56 | bool found = true; 57 | for (var j = 0; j < stone.width; j++) { 58 | for (var k = 0; k < stone.height; k++) { 59 | found &= grid[firstIndex + __getGridPos(j, k)] == null; 60 | } 61 | } 62 | return found; 63 | } 64 | 65 | int __getGridPos(int column, int row) { 66 | if (this.direction == Axis.vertical) { 67 | return column + mainAxisSeparations * row; 68 | } else { 69 | return row + mainAxisSeparations * column; 70 | } 71 | } 72 | 73 | void __placeOnGrid(Stone brick, int firstIndex) { 74 | int pos = 0; 75 | for (var j = 0; j < brick.width; j++) { 76 | for (var k = 0; k < brick.height; k++) { 77 | pos = firstIndex + __getGridPos(j, k); 78 | grid[pos] = brick.id as int; 79 | _gridLastIndex = max(_gridLastIndex, pos); 80 | } 81 | } 82 | } 83 | 84 | /// Compute the position of the stone, and set it on the grid. 85 | @visibleForTesting 86 | void computeStonePosition(Stone stone) { 87 | // find first place in grid that accept brick's surface 88 | bool found = false; 89 | int startSearchPlace = 0; 90 | int? availablePlace; 91 | 92 | while (!found) { 93 | availablePlace = 94 | grid.indexWhere((element) => element == null, startSearchPlace); 95 | found = __canFit(stone, availablePlace); 96 | startSearchPlace = availablePlace + 1; 97 | } 98 | __placeOnGrid(stone, availablePlace!); 99 | } 100 | 101 | /// Returns the position of a specific stone. 102 | /// Throw an error if [compute] method hasn't been called before. 103 | StoneStartPosition _getPosition(Stone stone) { 104 | int start = this.grid.indexOf(stone.id as int); 105 | int x, y; 106 | if (this.direction == Axis.vertical) { 107 | x = start % mainAxisSeparations; 108 | y = start ~/ mainAxisSeparations; 109 | } else { 110 | x = start ~/ mainAxisSeparations; 111 | y = start % mainAxisSeparations; 112 | } 113 | return StoneStartPosition(x: x, y: y); 114 | } 115 | 116 | @override 117 | String toString() { 118 | final stringBuffer = StringBuffer("$DefaultWallBuildHandler\n"); 119 | if (this.direction == Axis.vertical) { 120 | for (int i = 0; i < grid.length; i += mainAxisSeparations) { 121 | stringBuffer 122 | .writeln(grid.sublist(i, i + mainAxisSeparations).join(" | ")); 123 | } 124 | } else { 125 | List> rows = 126 | List>.generate(mainAxisSeparations, (index) => []); 127 | for (int i = 0; i < grid.length; i++) { 128 | rows[i % mainAxisSeparations].add(grid[i]!); 129 | } 130 | rows.forEach((row) => stringBuffer.writeln(row.join(" | "))); 131 | } 132 | return stringBuffer.toString(); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /lib/src/stone.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/rendering.dart'; 3 | 4 | /// A Stone Widget is a widget proxy defining stone's size (width and height). 5 | /// It's rendered object is optimized thanks to it's id property. 6 | /// Depending on the wall (rendered) direction, a stone width or height cannot exceed the wall layers count. 7 | /// If so, the [WallLayout] constructor throws an error. 8 | /// Added to that, [WallLayout] constructor check that every Stone identifier is unique. 9 | class Stone extends LayoutId { 10 | /// Stone width (relative to the number of wall layers count). Must be higher or equal to 1. 11 | final int width; 12 | 13 | /// Stone height (relative to the number of wall layers count). Must be higher or equal to 1. 14 | final int height; 15 | 16 | Stone( 17 | {required int id, 18 | required Widget child, 19 | required this.width, 20 | required this.height}) 21 | : assert(width > 0), 22 | assert(height > 0), 23 | super(child: child, id: id); 24 | 25 | /// Computes the surface area of the stone. 26 | int get surface => width * height; 27 | 28 | @override 29 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 30 | super.debugFillProperties(properties); 31 | properties.add(DiagnosticsProperty('width', width)); 32 | properties.add(DiagnosticsProperty('height', height)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/src/wall_layout.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/gestures.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_wall_layout/src/stone.dart'; 4 | import 'package:flutter_wall_layout/wall_builder.dart'; 5 | 6 | class WallLayout extends StatefulWidget { 7 | static const double DEFAULT_BRICK_PADDING = 16.0; 8 | 9 | /// Define the number of layers the wall have. Must be higher or equal to 2. 10 | /// When direction is Axis.vertical, it defines the number of columns the wall has. 11 | /// When direction is Axis.horizontal, it defines the number of rows. 12 | final int layersCount; 13 | 14 | /// List of Stone widgets, representing wall layout's children. 15 | final List stones; 16 | 17 | /// Define how the wall is built: where each stone is positioned within the wall. 18 | /// If not provided, the [WallLayout] uses the [WallBuilder.standard]. 19 | final WallBuilder _wallBuildHandler; 20 | 21 | /// Padding between stones. 22 | final double stonePadding; 23 | 24 | /// Optional. If specified, indicates the exact size of each stone in the direction 25 | /// of the primary scroll axis. If not specified, each stone will be exactly square. 26 | final double? primaryAxisStoneSize; 27 | 28 | /// Same as [ListView].scrollController: "control the position to which this scroll view is scrolled". 29 | final ScrollController? scrollController; 30 | 31 | /// Same as [ListView].physics: "How the scroll view should respond to user input". 32 | final ScrollPhysics? physics; 33 | 34 | /// Same as [ListView].restorationId: used "to save and restore the scroll offset of the scrollable". 35 | final String? restorationId; 36 | 37 | /// Same as [ListView].dragStartBehavior: "Determines the way that drag start behavior is handled". 38 | final DragStartBehavior dragStartBehavior; 39 | 40 | /// Same as [ListView].clipBehavior: "ways to clip a widget's content". 41 | final Clip clipBehavior; 42 | 43 | /// Same as [ListView].primary: "Whether this is the primary scroll view associated with the parent [PrimaryScrollController]". 44 | final bool? primary; 45 | 46 | /// Same as [ListView].scrollDirection: "axis along which the scroll view scrolls". 47 | final Axis scrollDirection; 48 | 49 | /// Same as [ListView].reverse: "whether the scroll view scrolls in the reading direction". 50 | final bool reverse; 51 | 52 | WallLayout( 53 | {required this.layersCount, 54 | required this.stones, 55 | WallBuilder? wallBuilder, 56 | this.stonePadding = DEFAULT_BRICK_PADDING, 57 | this.primaryAxisStoneSize, 58 | this.scrollController, 59 | this.primary, 60 | this.physics, 61 | this.restorationId, 62 | this.clipBehavior = Clip.hardEdge, 63 | this.dragStartBehavior = DragStartBehavior.start, 64 | this.scrollDirection = Axis.vertical, 65 | this.reverse = false}) 66 | : _wallBuildHandler = wallBuilder ?? WallBuilder.standard(), 67 | assert(stones.isNotEmpty), 68 | assert(layersCount >= 2, 69 | "You must define layers count from as an integer higher or equal to 2"), 70 | assert(stonePadding >= 0.0), 71 | assert(primaryAxisStoneSize == null || primaryAxisStoneSize > 0.0), 72 | assert( 73 | !(scrollController != null && primary == true), 74 | 'Primary ScrollViews obtain their ScrollController via inheritance from a PrimaryScrollController widget. ' 75 | 'You cannot both set primary to true and pass an explicit controller.'), 76 | super() { 77 | assert( 78 | this.stones.map((stone) => stone.id).toSet().length == 79 | this.stones.length, 80 | "Stones identifier must be unique."); 81 | this.stones.forEach((stone) { 82 | final constrainedSide = 83 | this.scrollDirection == Axis.vertical ? stone.width : stone.height; 84 | assert(constrainedSide <= this.layersCount, 85 | "Stone $stone is too big to fit in wall : constrained side ($constrainedSide) is higher than axisDivision ($layersCount)"); 86 | }); 87 | } 88 | 89 | @override 90 | State createState() => _WallLayoutState(); 91 | } 92 | 93 | class _WallLayoutState extends State { 94 | late WallBlueprint _blueprint; 95 | 96 | @override 97 | void initState() { 98 | super.initState(); 99 | _resetHandler(); 100 | } 101 | 102 | void _resetHandler() { 103 | _blueprint = widget._wallBuildHandler.build( 104 | mainAxisSeparations: this.widget.layersCount, 105 | stones: this.widget.stones, 106 | direction: this.widget.scrollDirection, 107 | reverse: this.widget.reverse, 108 | ); 109 | } 110 | 111 | @override 112 | void didUpdateWidget(covariant WallLayout oldWidget) { 113 | super.didUpdateWidget(oldWidget); 114 | if (this.widget.layersCount != oldWidget.layersCount || 115 | this.widget.scrollDirection != oldWidget.scrollDirection || 116 | this.widget.reverse != oldWidget.reverse || 117 | oldWidget.stones != this.widget.stones) { 118 | _resetHandler(); 119 | } 120 | } 121 | 122 | @override 123 | Widget build(BuildContext context) { 124 | return SingleChildScrollView( 125 | reverse: this.widget.reverse, 126 | scrollDirection: this.widget.scrollDirection, 127 | restorationId: this.widget.restorationId, 128 | controller: this.widget.scrollController, 129 | physics: this.widget.physics, 130 | dragStartBehavior: this.widget.dragStartBehavior, 131 | primary: this.widget.primary, 132 | clipBehavior: this.widget.clipBehavior, 133 | child: CustomMultiChildLayout( 134 | delegate: _WallLayoutDelegate( 135 | blueprint: _blueprint, 136 | stonePadding: this.widget.stonePadding, 137 | primaryAxisStoneSize: this.widget.primaryAxisStoneSize, 138 | direction: this.widget.scrollDirection, 139 | mainAxisSeparations: this.widget.layersCount, 140 | ), 141 | children: _blueprint.stonesPosition.keys.toList(), 142 | ), 143 | ); 144 | } 145 | } 146 | 147 | /// Delegates for CustomMultiChildLayout that position and size Stones. 148 | class _WallLayoutDelegate extends MultiChildLayoutDelegate { 149 | final WallBlueprint blueprint; 150 | final double stonePadding; 151 | final double? primaryAxisStoneSize; 152 | final Axis direction; 153 | final int mainAxisSeparations; 154 | 155 | _WallLayoutDelegate( 156 | {required this.stonePadding, 157 | required this.primaryAxisStoneSize, 158 | required this.blueprint, 159 | required this.direction, 160 | required this.mainAxisSeparations, 161 | Listenable? relayout}) 162 | : super(relayout: relayout); 163 | 164 | @override 165 | Size getSize(BoxConstraints constraints) { 166 | if (direction == Axis.vertical) { 167 | double side = 168 | primaryAxisStoneSize ?? (constraints.maxWidth / mainAxisSeparations); 169 | return Size(constraints.maxWidth, (blueprint.size * side).height); 170 | } else { 171 | double side = 172 | primaryAxisStoneSize ?? (constraints.maxHeight / mainAxisSeparations); 173 | return Size((blueprint.size * side).width, constraints.maxHeight); 174 | } 175 | } 176 | 177 | @override 178 | void performLayout(Size size) { 179 | double side = ((direction == Axis.vertical ? size.width : size.height) - 180 | this.stonePadding) / 181 | mainAxisSeparations; 182 | double primaryAxisSide = primaryAxisStoneSize ?? side; 183 | final singleStoneSize = Size( 184 | direction == Axis.vertical ? side : primaryAxisSide, 185 | direction == Axis.vertical ? primaryAxisSide : side, 186 | ); 187 | final initialPadding = Offset(this.stonePadding, this.stonePadding); 188 | blueprint.stonesPosition.forEach((stone, stonePos) { 189 | Offset offset = stonePos * singleStoneSize; 190 | Size size = Size( 191 | stone.width * singleStoneSize.width - this.stonePadding, 192 | stone.height * singleStoneSize.height - this.stonePadding, 193 | ); 194 | 195 | positionChild(stone.id, initialPadding + offset); 196 | layoutChild(stone.id, BoxConstraints.tight(size)); 197 | }); 198 | } 199 | 200 | @override 201 | bool shouldRelayout(covariant MultiChildLayoutDelegate oldDelegate) => true; 202 | } 203 | -------------------------------------------------------------------------------- /lib/wall_builder.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_wall_layout/src/default_wall_builder.dart'; 5 | import 'package:flutter_wall_layout/src/stone.dart'; 6 | 7 | /// Relative start position of a stone in the Wall. 8 | class StoneStartPosition { 9 | /// Wall column position. 10 | final int x; 11 | 12 | /// Wall row position. 13 | final int y; 14 | 15 | StoneStartPosition({required this.x, required this.y}) 16 | : assert(x >= 0), 17 | assert(y >= 0); 18 | 19 | /// Computes the absolute brick position in a wall. 20 | /// [stoneSide] represents the smallest stone width/height. 21 | Offset operator *(Size stoneSide) => 22 | Offset(this.x * stoneSide.width, this.y * stoneSide.height); 23 | 24 | @override 25 | String toString() { 26 | return '$StoneStartPosition{x: $x, y: $y}'; 27 | } 28 | } 29 | 30 | /// Define the wall width and height, where 1 unit refers to the side of a wall layer. 31 | /// So all these values must be multiplied by the length of a side (in logical pixels), 32 | /// if you want to have to logical pixels dimension. 33 | class WallSize { 34 | /// Wall relative width 35 | final int width; 36 | 37 | /// Wall relative height 38 | final int height; 39 | 40 | const WallSize(this.width, this.height) 41 | : assert(width > 0), 42 | assert(height > 0); 43 | 44 | /// Flip width with Height, and returns a new instance 45 | get flipped => WallSize(this.height, this.width); 46 | 47 | /// Compute surface area of the wall size 48 | get surface => this.height * this.width; 49 | 50 | /// Let's you multiply the wall size by a logical pixels width, 51 | /// to have the logical pixels dimensions of the wall. 52 | Size operator *(double stoneSide) => 53 | Size(width.toDouble() * stoneSide, height.toDouble() * stoneSide); 54 | 55 | @override 56 | int get hashCode => Object.hashAll([width, height]); 57 | 58 | @override 59 | bool operator ==(Object other) { 60 | if (other is WallSize) { 61 | return this.width == other.width && this.height == other.height; 62 | } 63 | return super == other; 64 | } 65 | 66 | @override 67 | String toString() { 68 | return "$WallSize(width:$width, height:$height)"; 69 | } 70 | } 71 | 72 | /// Define the blueprint of the wall. 73 | /// It contains (relative) location of all stones, 74 | /// and the overall size of the wall. 75 | class WallBlueprint { 76 | final Map stonesPosition; 77 | final WallSize size; 78 | 79 | WallBlueprint({required this.stonesPosition, required this.size}); 80 | } 81 | 82 | /// Defines how the wall will be built 83 | /// If you want to define your own algorithm, 84 | /// you will have to inherit from this abstract class, 85 | /// and implements the method [WallBuilder.computeStonePositions]. 86 | abstract class WallBuilder { 87 | /// Default [WallBuilder] implementation 88 | factory WallBuilder.standard() => DefaultWallBuildHandler(); 89 | 90 | /// Define how many layers of the main axis the wall possess. 91 | late int _mainAxisSeparations; 92 | 93 | /// Define how many layers of the main axis the wall possess. 94 | int get mainAxisSeparations => _mainAxisSeparations; 95 | 96 | /// Define wall build direction, like [ListView].direction input parameter. 97 | late Axis _direction; 98 | 99 | /// Define wall build direction, like [ListView].direction input parameter. 100 | Axis get direction => _direction; 101 | 102 | /// Define whether the wall must be displayed in reverse, like [ListView].reverse input parameter. 103 | late bool _reverse; 104 | 105 | /// Define whether the wall must be displayed in reverse, like [ListView].reverse input parameter. 106 | bool get reverse => _reverse; 107 | 108 | /// Default constructor. 109 | WallBuilder(); 110 | 111 | /// Build the wall with scrollview parameters 112 | WallBlueprint build( 113 | {required int mainAxisSeparations, 114 | required bool reverse, 115 | required Axis direction, 116 | required List stones}) { 117 | this._direction = direction; 118 | this._reverse = reverse; 119 | this._mainAxisSeparations = mainAxisSeparations; 120 | final positions = computeStonePositions(stones); 121 | assertNoOverlap(positions); 122 | assertNoDrawOutside(positions); 123 | final wallSize = computeSize(positions); 124 | return WallBlueprint(stonesPosition: positions, size: wallSize); 125 | } 126 | 127 | /// Let you compute stone position for each stone. 128 | /// StonePositions must not overlap (stack not allowed), 129 | /// and not been drawn outside the wall 130 | /// (depending on [WallBuilder.mainAxisSeparations]). 131 | /// 132 | /// To help you, the [WallBuilder] will check these two requisites. 133 | /// Added to that, you have access to layout main data: 134 | /// - [WallBuilder.direction] : Direction of the wall to draw 135 | /// - [WallBuilder.mainAxisSeparations] : number of elements of the main direction. 136 | /// - [WallBuilder.reverse] : define whether to display the stones in reverse mode (check [ScrollView.reverse]) 137 | /// The returned data will be used to draw stones. 138 | /// If a stone is not in this map, it won't be drawn. 139 | /// All new created Stone inserted in the Map will be taken into account. 140 | Map computeStonePositions(List stones); 141 | 142 | /// Compute the wall size from the stones positions. 143 | @visibleForTesting 144 | WallSize computeSize(Map stonesPositions) { 145 | final posBottomRight = stonesPositions.map((key, value) => MapEntry(key, 146 | StoneStartPosition(x: value.x + key.width, y: value.y + key.height))); 147 | int maxX = 0, maxY = 0; 148 | posBottomRight.forEach((stone, pos) { 149 | maxX = max(maxX, pos.x); 150 | maxY = max(maxY, pos.y); 151 | }); 152 | return WallSize(maxX, maxY); 153 | } 154 | 155 | @visibleForTesting 156 | void assertNoOverlap(Map stonesPositions) { 157 | final bounds = stonesPositions.map((key, value) => MapEntry( 158 | key, 159 | Rect.fromLTWH(value.x.toDouble(), value.y.toDouble(), 160 | key.width.toDouble(), key.height.toDouble()))); 161 | bool overlap = false; 162 | bounds.forEach((key, value) { 163 | bounds.forEach((key2, value2) { 164 | if (key != key2 && value.overlaps(value2)) { 165 | overlap = true; 166 | print( 167 | "Overlapping stones: $key (${stonesPositions[key]})\n $key2 (${stonesPositions[key2]})"); 168 | } 169 | }); 170 | }); 171 | assert(!overlap, 172 | "Two stones or more are overlapping (check console for more details)!"); 173 | } 174 | 175 | @visibleForTesting 176 | void assertNoDrawOutside(Map stonesPositions) { 177 | final stonesOutside = stonesPositions.map((key, value) { 178 | bool isOutside = false; 179 | if (this.direction == Axis.vertical) { 180 | isOutside = (value.x + key.width) > mainAxisSeparations; 181 | } else { 182 | isOutside = (value.y + key.height) > mainAxisSeparations; 183 | } 184 | return MapEntry(key, isOutside); 185 | }); 186 | stonesOutside.removeWhere((key, value) => value == false); 187 | stonesOutside.forEach((key, _) { 188 | print( 189 | "Error: stone $key will be drawn outside wall with position ${stonesPositions[key]}"); 190 | }); 191 | assert(stonesOutside.isEmpty, 192 | "Stones must not draw outside the wall (see logs for more details)"); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_wall_layout 2 | description: A Layout widget displaying a set of bricks (widgets with different shapes) as a vertical or horizontal list, like a wall... 3 | version: 2.1.2 4 | repository: https://github.com/GONZALEZD/flutter_wall_layout 5 | 6 | environment: 7 | sdk: '>=2.14.0 <4.0.0' 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | 13 | dev_dependencies: 14 | flutter_test: 15 | sdk: flutter -------------------------------------------------------------------------------- /test/stone_position_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:flutter_wall_layout/wall_builder.dart'; 5 | 6 | void main() { 7 | group("$StoneStartPosition Class", () { 8 | test("Initialisation", () { 9 | expect(() => StoneStartPosition(x: -1, y: 1), throwsAssertionError, 10 | reason: "Stone must throw error if X is lower than 1."); 11 | expect(() => StoneStartPosition(x: 3, y: -1), throwsAssertionError, 12 | reason: "Stone must throw error if Y is lower than 1."); 13 | }); 14 | 15 | test("* operator", () { 16 | expect(StoneStartPosition(x: 2, y: 10) * Size(12.0, 12.0), Offset(24.0, 120.0), 17 | reason: "Incorrect multiplication."); 18 | expect(StoneStartPosition(x: 3, y: 1) * Size(-6.0, -6.0), Offset(-18.0, -6.0), 19 | reason: "Incorrect multiplication."); 20 | }); 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /test/stone_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:flutter_wall_layout/flutter_wall_layout.dart'; 5 | 6 | void main() { 7 | group("$Stone Class", () { 8 | test("Initialization", () { 9 | expect(() => Stone(id: 1, width: 0, height: 2, child: Container()), 10 | throwsAssertionError, 11 | reason: "Stone must check that width is higher than 0"); 12 | expect(() => Stone(id: 1, width: 2, height: 0, child: Container()), 13 | throwsAssertionError, 14 | reason: "Stone must check that height is higher than 0"); 15 | }); 16 | test("Surface", () { 17 | expect(Stone(id: 1, width: 2, height: 2, child: Container()).surface, 4, 18 | reason: "Incorrect stone surface"); 19 | expect(Stone(id: 1, width: 1, height: 20, child: Container()).surface, 20, 20 | reason: "Incorrect stone surface"); 21 | expect(Stone(id: 1, width: 6, height: 3, child: Container()).surface, 18, 22 | reason: "Incorrect stone surface"); 23 | expect(Stone(id: 1, width: 1, height: 1, child: Container()).surface, 1, 24 | reason: "Incorrect stone surface"); 25 | }); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /test/wall_build_handler_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:flutter_wall_layout/src/default_wall_builder.dart'; 4 | import 'package:flutter_wall_layout/src/stone.dart'; 5 | import 'package:flutter_wall_layout/wall_builder.dart'; 6 | 7 | class WallBuildHandlerMock extends WallBuilder { 8 | final Map data; 9 | final WallSize size; 10 | 11 | late int mainAxisSeparations; 12 | late Axis direction; 13 | late bool reverse; 14 | 15 | WallBuildHandlerMock({required this.data, required this.size}); 16 | 17 | @override 18 | Map computeStonePositions(List stones) { 19 | return data; 20 | } 21 | 22 | @override 23 | WallBlueprint build( 24 | {required int mainAxisSeparations, 25 | required bool reverse, 26 | required Axis direction, 27 | required List stones}) { 28 | this.mainAxisSeparations = mainAxisSeparations; 29 | this.direction = direction; 30 | this.reverse = reverse; 31 | return WallBlueprint(stonesPosition: data, size: size); 32 | } 33 | } 34 | 35 | void main() { 36 | group("$DefaultWallBuildHandler Class", () { 37 | late List stones; 38 | setUp(() { 39 | stones = [ 40 | Stone( 41 | id: 1, 42 | child: Container(), 43 | width: 2, 44 | height: 1, 45 | ), 46 | Stone( 47 | id: 2, 48 | child: Container(), 49 | width: 2, 50 | height: 2, 51 | ), 52 | Stone( 53 | id: 3, 54 | child: Container(), 55 | width: 1, 56 | height: 1, 57 | ), 58 | Stone( 59 | id: 4, 60 | child: Container(), 61 | width: 1, 62 | height: 1, 63 | ), 64 | ]; 65 | }); 66 | test("Vertical list", () { 67 | final handler = DefaultWallBuildHandler(); 68 | final blueprint = handler.build( 69 | stones: stones, 70 | mainAxisSeparations: 2, 71 | direction: Axis.vertical, 72 | reverse: false); 73 | expect(blueprint.size, WallSize(2, 4), 74 | reason: "Computed wall size is incorrect"); 75 | expect(handler.grid, [1, 1, 2, 2, 2, 2, 3, 4], 76 | reason: "Computed grid is incorrect"); 77 | }); 78 | test("Vertical list 2", () { 79 | final handler = DefaultWallBuildHandler(); 80 | final blueprint = handler.build( 81 | stones: stones, 82 | mainAxisSeparations: 3, 83 | direction: Axis.vertical, 84 | reverse: false); 85 | expect(blueprint.size, WallSize(3, 3), 86 | reason: "Computed wall size is incorrect"); 87 | expect(handler.grid, [1, 1, 3, 2, 2, 4, 2, 2, null], 88 | reason: "Computed grid is incorrect"); 89 | }); 90 | test("Reverse vertical list", () { 91 | final handler = DefaultWallBuildHandler(); 92 | final blueprint = handler.build( 93 | stones: stones, 94 | mainAxisSeparations: 3, 95 | reverse: true, 96 | direction: Axis.vertical); 97 | expect(blueprint.size, WallSize(3, 3), 98 | reason: "Computed wall size is incorrect"); 99 | expect(handler.grid, [null, 2, 2, 4, 2, 2, 3, 1, 1], 100 | reason: "Computed grid is incorrect"); 101 | }); 102 | test("Horizontal list", () { 103 | final handler = DefaultWallBuildHandler(); 104 | final blueprint = handler.build( 105 | stones: stones, 106 | mainAxisSeparations: 2, 107 | direction: Axis.horizontal, 108 | reverse: false); 109 | expect(blueprint.size, WallSize(4, 2), 110 | reason: "Computed wall size is incorrect"); 111 | expect(handler.grid, [1, 3, 1, 4, 2, 2, 2, 2], 112 | reason: "Computed grid is incorrect"); 113 | }); 114 | test("Reverse horizontal list", () { 115 | final handler = DefaultWallBuildHandler(); 116 | final blueprint = handler.build( 117 | stones: stones, 118 | mainAxisSeparations: 3, 119 | direction: Axis.horizontal, 120 | reverse: true); 121 | expect(blueprint.size, WallSize(3, 3), 122 | reason: "Computed wall size is incorrect"); 123 | expect(handler.grid, [null, 4, 3, 2, 2, 1, 2, 2, 1], 124 | reason: "Computed grid is incorrect"); 125 | }); 126 | test("Overlapping Stones check", () { 127 | final handler = WallBuildHandlerMock(data: { 128 | Stone(id: 1, child: Container(), width: 2, height: 2): 129 | StoneStartPosition(x: 0, y: 0), 130 | Stone(id: 2, child: Container(), width: 1, height: 1): 131 | StoneStartPosition(x: 2, y: 0), 132 | Stone(id: 3, child: Container(), width: 1, height: 1): 133 | StoneStartPosition(x: 2, y: 1), 134 | Stone(id: 4, child: Container(), width: 2, height: 2): 135 | StoneStartPosition(x: 1, y: 1), 136 | }, size: WallSize(1, 1)); 137 | handler.build( 138 | mainAxisSeparations: 3, 139 | reverse: false, 140 | direction: Axis.vertical, 141 | stones: handler.data.keys.toList()); 142 | expect(() => handler.assertNoOverlap(handler.data), throwsAssertionError); 143 | }); 144 | test("Drawn outside stones check", () { 145 | final handler = WallBuildHandlerMock(data: { 146 | Stone(id: 1, child: Container(), width: 2, height: 2): 147 | StoneStartPosition(x: 0, y: 0), 148 | Stone(id: 2, child: Container(), width: 1, height: 1): 149 | StoneStartPosition(x: 2, y: 0), 150 | Stone(id: 3, child: Container(), width: 1, height: 1): 151 | StoneStartPosition(x: 2, y: 1), 152 | Stone(id: 4, child: Container(), width: 2, height: 2): 153 | StoneStartPosition(x: 2, y: 1), 154 | }, size: WallSize(1, 1)); 155 | handler.build( 156 | mainAxisSeparations: 3, 157 | reverse: false, 158 | direction: Axis.vertical, 159 | stones: handler.data.keys.toList()); 160 | expect(() => handler.assertNoDrawOutside(handler.data), 161 | throwsAssertionError); 162 | }); 163 | }); 164 | } 165 | -------------------------------------------------------------------------------- /test/wall_layout_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:flutter_wall_layout/flutter_wall_layout.dart'; 4 | 5 | void main() { 6 | late List stones; 7 | setUp(() { 8 | stones = [ 9 | Stone( 10 | id: 1, 11 | child: Container(), 12 | width: 2, 13 | height: 2, 14 | ), 15 | Stone( 16 | id: 2, 17 | child: Container(), 18 | width: 2, 19 | height: 1, 20 | ), 21 | Stone( 22 | id: 3, 23 | child: Container(), 24 | width: 1, 25 | height: 2, 26 | ), 27 | Stone( 28 | id: 4, 29 | child: Container(), 30 | width: 1, 31 | height: 1, 32 | ), 33 | Stone( 34 | id: 5, 35 | child: Container(), 36 | width: 3, 37 | height: 2, 38 | ), 39 | Stone( 40 | id: 6, 41 | child: Container(), 42 | width: 1, 43 | height: 1, 44 | ), 45 | Stone( 46 | id: 7, 47 | child: Container(), 48 | width: 2, 49 | height: 3, 50 | ), 51 | ]; 52 | }); 53 | group("$WallLayout Class", () { 54 | test("Initialization", () { 55 | expect( 56 | () => WallLayout( 57 | stones: [], 58 | layersCount: 2, 59 | ), 60 | throwsAssertionError, 61 | reason: "$WallLayout must throw error if stones list is empty"); 62 | expect( 63 | () => WallLayout( 64 | stones: stones, 65 | layersCount: 1, 66 | ), 67 | throwsAssertionError, 68 | reason: 69 | "$WallLayout must throw error if stones divisions is lower than 2 (axis division equal to 1 is equivalent to a ListView, so no interest)"); 70 | expect( 71 | () => WallLayout( 72 | stones: stones, 73 | layersCount: 2, 74 | scrollDirection: Axis.vertical, 75 | ), 76 | throwsAssertionError, 77 | reason: 78 | "$WallLayout must throw error if at least one stone width is bigger than allowed divisions (in vertical Axis)."); 79 | expect( 80 | () => WallLayout( 81 | stones: stones, 82 | layersCount: 2, 83 | scrollDirection: Axis.horizontal, 84 | ), 85 | throwsAssertionError, 86 | reason: 87 | "$WallLayout must throw error if at least one stone height is bigger than allowed divisions (in horizontal Axis)."); 88 | 89 | expect( 90 | () => WallLayout( 91 | stones: stones + 92 | [ 93 | Stone( 94 | id: 1, 95 | child: Container(), 96 | width: 2, 97 | height: 3, 98 | ) 99 | ], 100 | layersCount: 4), 101 | throwsAssertionError, 102 | reason: 103 | "$WallLayout must throw error if two stones have the same id."); 104 | }); 105 | }); 106 | } 107 | -------------------------------------------------------------------------------- /test/wall_size_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | import 'package:flutter_wall_layout/wall_builder.dart'; 5 | 6 | void main() { 7 | group("$WallSize Class", () { 8 | test("Initialization", () { 9 | expect(() => WallSize(-2, 2), throwsAssertionError, 10 | reason: "$WallSize must throw error is width is lower than 1."); 11 | expect(() => WallSize(2, -4), throwsAssertionError, 12 | reason: "$WallSize must throw error is height is lower than 1."); 13 | }); 14 | test("Surface", () { 15 | expect(WallSize(2, 3).surface, 6, 16 | reason: "Incorrect surface computation"); 17 | expect(WallSize(1, 23).surface, 23, 18 | reason: "Incorrect surface computation"); 19 | expect(WallSize(12, 2).surface, 24, 20 | reason: "Incorrect surface computation"); 21 | }); 22 | test("* operator", () { 23 | expect(WallSize(6, 2) * 2.0, Size(12.0, 4.0), 24 | reason: "Incorrect multiplication"); 25 | expect(WallSize(1, 3) * -3.0, Size(-3.0, -9.0), 26 | reason: "Incorrect multiplication"); 27 | }); 28 | test("Flipped $WallSize", () { 29 | expect(WallSize(2, 3).flipped, WallSize(3, 2), 30 | reason: "Flipped height and width not working"); 31 | }); 32 | }); 33 | } 34 | --------------------------------------------------------------------------------