├── .fvm
├── flutter_sdk
└── fvm_config.json
├── .fvmrc
├── .gitignore
├── .metadata
├── CHANGELOG.md
├── LICENSE
├── README.md
├── analysis_options.yaml
├── android
├── .gitignore
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── settings.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── kotlin
│ └── com
│ └── github
│ └── humazed
│ └── flutter_toolbox
│ └── FlutterToolboxPlugin.kt
├── example
├── .fvm
│ ├── flutter_sdk
│ └── fvm_config.json
├── .gitignore
├── .metadata
├── README.md
├── analysis_options.yaml
├── android
│ ├── .gitignore
│ ├── app
│ │ ├── build.gradle
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── github
│ │ │ │ │ └── humazed
│ │ │ │ │ └── flutter_toolbox_example
│ │ │ │ │ ├── App.kt
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── res
│ │ │ │ ├── drawable
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── values-night
│ │ │ │ └── styles.xml
│ │ │ │ └── values
│ │ │ │ └── styles.xml
│ │ │ └── profile
│ │ │ └── AndroidManifest.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ └── settings.gradle
├── ios
│ ├── .gitignore
│ ├── Flutter
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ │ └── WorkspaceSettings.xcsettings
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ └── contents.xcworkspacedata
│ ├── Runner
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ │ ├── AppIcon.appiconset
│ │ │ │ ├── Contents.json
│ │ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ │ ├── Icon-App-20x20@1x.png
│ │ │ │ ├── Icon-App-20x20@2x.png
│ │ │ │ ├── Icon-App-20x20@3x.png
│ │ │ │ ├── Icon-App-29x29@1x.png
│ │ │ │ ├── Icon-App-29x29@2x.png
│ │ │ │ ├── Icon-App-29x29@3x.png
│ │ │ │ ├── Icon-App-40x40@1x.png
│ │ │ │ ├── Icon-App-40x40@2x.png
│ │ │ │ ├── Icon-App-40x40@3x.png
│ │ │ │ ├── Icon-App-60x60@2x.png
│ │ │ │ ├── Icon-App-60x60@3x.png
│ │ │ │ ├── Icon-App-76x76@1x.png
│ │ │ │ ├── Icon-App-76x76@2x.png
│ │ │ │ └── Icon-App-83.5x83.5@2x.png
│ │ │ └── LaunchImage.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ ├── LaunchImage.png
│ │ │ │ ├── LaunchImage@2x.png
│ │ │ │ ├── LaunchImage@3x.png
│ │ │ │ └── README.md
│ │ ├── Base.lproj
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── Runner-Bridging-Header.h
│ └── RunnerTests
│ │ └── RunnerTests.swift
├── lib
│ ├── auth_navigation.dart
│ ├── auth_provider.dart
│ ├── main.dart
│ └── paginated_list_view_example.dart
├── pubspec.lock
├── pubspec.yaml
└── test
│ └── widget_test.dart
├── flutter_toolbox.iml
├── flutter_utils.iml
├── ios
├── .gitignore
├── Assets
│ └── .gitkeep
├── Classes
│ └── SwiftFlutterToolboxPlugin.swift
└── flutter_toolbox.podspec
├── lib
├── flutter_toolbox.dart
├── generated
│ ├── intl
│ │ ├── messages_all.dart
│ │ ├── messages_ar.dart
│ │ ├── messages_en.dart
│ │ └── messages_ru.dart
│ └── l10n.dart
├── l10n
│ ├── intl_ar.arb
│ ├── intl_en.arb
│ └── intl_ru.arb
└── src
│ ├── config
│ ├── toolbox_app.dart
│ └── toolbox_config.dart
│ ├── extensions
│ ├── date_time_extensions.dart
│ ├── future_extensions.dart
│ ├── iterable_extensions.dart
│ ├── list_extensions.dart
│ └── string_extensions.dart
│ ├── http.dart
│ ├── image_picker.dart
│ ├── log.dart
│ ├── model
│ └── error
│ │ ├── error_response.dart
│ │ └── error_response.g.dart
│ ├── navigation.dart
│ ├── prefs.dart
│ ├── time.dart
│ ├── ui
│ ├── connection_status_bar.dart
│ ├── date_picker_field.dart
│ ├── ink_well_stacked.dart
│ ├── listview
│ │ ├── common.dart
│ │ ├── pagewise
│ │ │ ├── flutter_pagewise.dart
│ │ │ └── helpers
│ │ │ │ └── grid_helpers.dart
│ │ ├── paginated_grid_view_count.dart
│ │ ├── paginated_grid_view_extent.dart
│ │ ├── paginated_list_view.dart
│ │ └── paginated_sliver_list.dart
│ ├── loading_builder.dart
│ ├── multi_select_chip.dart
│ ├── net_image
│ │ ├── extensions.dart
│ │ └── net_image.dart
│ ├── search_item_picker.dart
│ ├── tab_rounded_line_indicator.dart
│ ├── time_picker_field.dart
│ ├── toast.dart
│ └── wrap_dropdown_button.dart
│ └── url_launchers.dart
├── pubspec.lock
├── pubspec.yaml
└── test
└── flutter_toolbox_test.dart
/.fvm/flutter_sdk:
--------------------------------------------------------------------------------
1 | /Users/ibrahimeid/Developer/fvm/versions/stable
--------------------------------------------------------------------------------
/.fvm/fvm_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "flutterSdkVersion": "stable"
3 | }
--------------------------------------------------------------------------------
/.fvmrc:
--------------------------------------------------------------------------------
1 | {
2 | "flutter": "stable",
3 | "flavors": {}
4 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .dart_tool/
3 |
4 | .packages
5 | .pub/
6 |
7 | build/
8 |
9 | .idea/
10 | example/.flutter-plugins-dependencies
11 |
12 | # FVM Version Cache
13 | .fvm/
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 7a4c33425ddd78c54aba07d86f3f9a4a0051769b
8 | channel: stable
9 |
10 | project_type: plugin
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # flutter_toolbox
2 |
3 | Common flutter widgets and helper methods.
4 |
5 | ## Getting Started
6 |
7 | This project is a starting point for a Flutter
8 | [plug-in package](https://flutter.dev/developing-packages/),
9 | a specialized package that includes platform-specific implementation code for
10 | Android and/or iOS.
11 |
12 | For help getting started with Flutter, view our
13 | [online documentation](https://flutter.dev/docs), which offers tutorials,
14 | samples, guidance on mobile development, and a full API reference.
15 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | include: package:flutter_lints/flutter.yaml
2 |
3 | # Additional information about this file can be found at
4 | # https://dart.dev/guides/language/analysis-options
5 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | group 'com.github.humazed.flutter_toolbox'
2 | version '1.0-SNAPSHOT'
3 |
4 | buildscript {
5 | ext.kotlin_version = '1.8.22'
6 | repositories {
7 | google()
8 | mavenCentral()
9 | }
10 |
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:8.7.3'
13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14 | }
15 | }
16 |
17 | rootProject.allprojects {
18 | repositories {
19 | google()
20 | mavenCentral()
21 | }
22 | }
23 |
24 | apply plugin: 'com.android.library'
25 | apply plugin: 'kotlin-android'
26 |
27 | android {
28 | if (project.android.hasProperty("namespace")) {
29 | namespace "com.github.humazed.flutter_toolbox"
30 | }
31 |
32 | compileSdkVersion 33
33 |
34 | compileOptions {
35 | sourceCompatibility JavaVersion.VERSION_1_8
36 | targetCompatibility JavaVersion.VERSION_1_8
37 | }
38 |
39 | kotlinOptions {
40 | jvmTarget = '1.8'
41 | }
42 |
43 | sourceSets {
44 | main.java.srcDirs += 'src/main/kotlin'
45 | test.java.srcDirs += 'src/test/kotlin'
46 | }
47 |
48 | defaultConfig {
49 | minSdkVersion 19
50 | }
51 |
52 | dependencies {
53 | testImplementation 'org.jetbrains.kotlin:kotlin-test'
54 | testImplementation 'org.mockito:mockito-core:5.0.0'
55 | }
56 |
57 | testOptions {
58 | unitTests.all {
59 | useJUnitPlatform()
60 |
61 | testLogging {
62 | events "passed", "skipped", "failed", "standardOut", "standardError"
63 | outputs.upToDateWhen {false}
64 | showStandardStreams = true
65 | }
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 |
--------------------------------------------------------------------------------
/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.9-bin.zip
6 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'flutter_toolbox'
2 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/com/github/humazed/flutter_toolbox/FlutterToolboxPlugin.kt:
--------------------------------------------------------------------------------
1 | package com.github.humazed.flutter_toolbox
2 |
3 | import androidx.annotation.NonNull
4 |
5 | import io.flutter.embedding.engine.plugins.FlutterPlugin
6 | import io.flutter.plugin.common.MethodCall
7 | import io.flutter.plugin.common.MethodChannel
8 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler
9 | import io.flutter.plugin.common.MethodChannel.Result
10 |
11 | class FlutterToolboxPlugin : FlutterPlugin, MethodCallHandler {
12 | /// The MethodChannel that will the communication between Flutter and native Android
13 | ///
14 | /// This local reference serves to register the plugin with the Flutter Engine and unregister it
15 | /// when the Flutter Engine is detached from the Activity
16 | private lateinit var channel: MethodChannel
17 |
18 | override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
19 | channel = MethodChannel(flutterPluginBinding.binaryMessenger, "flutter_toolbox")
20 | channel.setMethodCallHandler(this)
21 | }
22 |
23 | override fun onMethodCall(call: MethodCall, result: Result) {
24 | if (call.method == "getPlatformVersion") {
25 | result.success("Android ${android.os.Build.VERSION.RELEASE}")
26 | } else {
27 | result.notImplemented()
28 | }
29 | }
30 |
31 | override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
32 | channel.setMethodCallHandler(null)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/example/.fvm/flutter_sdk:
--------------------------------------------------------------------------------
1 | E:/AS/fvm/versions/stable
--------------------------------------------------------------------------------
/example/.fvm/fvm_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "flutterSdkVersion": "stable",
3 | "flavors": {}
4 | }
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .build/
9 | .buildlog/
10 | .history
11 | .svn/
12 | .swiftpm/
13 | migrate_working_dir/
14 |
15 | # IntelliJ related
16 | *.iml
17 | *.ipr
18 | *.iws
19 | .idea/
20 |
21 | # The .vscode folder contains launch configuration and tasks you configure in
22 | # VS Code which you may wish to be included in version control, so this line
23 | # is commented out by default.
24 | #.vscode/
25 |
26 | # Flutter/Dart/Pub related
27 | **/doc/api/
28 | **/ios/Flutter/.last_build_id
29 | .dart_tool/
30 | .flutter-plugins
31 | .flutter-plugins-dependencies
32 | .pub-cache/
33 | .pub/
34 | /build/
35 |
36 | # Symbolication related
37 | app.*.symbols
38 |
39 | # Obfuscation related
40 | app.*.map.json
41 |
42 | # Android Studio will place build artifacts here
43 | /android/app/debug
44 | /android/app/profile
45 | /android/app/release
46 |
--------------------------------------------------------------------------------
/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: 20e59316b8b8474554b38493b8ca888794b0234a
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # flutter_toolbox_example
2 |
3 | Demonstrates how to use the flutter_toolbox plugin.
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:
--------------------------------------------------------------------------------
1 | plugins {
2 | id "com.android.application"
3 | id "kotlin-android"
4 | id "dev.flutter.flutter-gradle-plugin"
5 | }
6 |
7 | def localProperties = new Properties()
8 | def localPropertiesFile = rootProject.file('local.properties')
9 | if (localPropertiesFile.exists()) {
10 | localPropertiesFile.withReader('UTF-8') { reader ->
11 | localProperties.load(reader)
12 | }
13 | }
14 |
15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
16 | if (flutterVersionCode == null) {
17 | flutterVersionCode = '1'
18 | }
19 |
20 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
21 | if (flutterVersionName == null) {
22 | flutterVersionName = '1.0'
23 | }
24 |
25 | android {
26 | namespace "com.github.humazed.flutter_toolbox_example"
27 | compileSdkVersion flutter.compileSdkVersion
28 | ndkVersion "27.0.12077973"
29 |
30 | compileOptions {
31 | sourceCompatibility JavaVersion.VERSION_1_8
32 | targetCompatibility JavaVersion.VERSION_1_8
33 | }
34 |
35 | kotlinOptions {
36 | jvmTarget = '1.8'
37 | }
38 |
39 | sourceSets {
40 | main.java.srcDirs += 'src/main/kotlin'
41 | }
42 |
43 | defaultConfig {
44 | applicationId "com.github.humazed.flutter_toolbox_example"
45 | // You can update the following values to match your application needs.
46 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
47 | minSdkVersion flutter.minSdkVersion
48 | targetSdkVersion flutter.targetSdkVersion
49 | versionCode flutterVersionCode.toInteger()
50 | versionName flutterVersionName
51 | multiDexEnabled true
52 | }
53 |
54 | buildTypes {
55 | release {
56 | // TODO: Add your own signing config for the release build.
57 | // Signing with the debug keys for now, so `flutter run --release` works.
58 | signingConfig signingConfigs.debug
59 | }
60 | }
61 | }
62 |
63 | flutter {
64 | source '../..'
65 | }
66 |
67 | dependencies {
68 | implementation 'com.android.support:multidex:1.0.3'
69 | }
70 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
14 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
29 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/example/android/app/src/main/kotlin/com/github/humazed/flutter_toolbox_example/App.kt:
--------------------------------------------------------------------------------
1 | package com.github.humazed.flutter_toolbox_example
2 |
3 | import android.content.Context
4 | import androidx.multidex.MultiDex
5 | import androidx.multidex.MultiDexApplication
6 |
7 | class App : MultiDexApplication() {
8 | override fun attachBaseContext(base: Context) {
9 | super.attachBaseContext(base)
10 | MultiDex.install(this)
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/kotlin/com/github/humazed/flutter_toolbox_example/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.github.humazed.flutter_toolbox_example
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity : FlutterActivity()
6 |
--------------------------------------------------------------------------------
/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/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/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/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/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/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/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/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/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/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.8.22'
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
10 | }
11 | }
12 |
13 | allprojects {
14 | repositories {
15 | google()
16 | mavenCentral()
17 | }
18 | }
19 |
20 | rootProject.buildDir = '../build'
21 | subprojects {
22 | project.buildDir = "${rootProject.buildDir}/${project.name}"
23 | }
24 | subprojects {
25 | project.evaluationDependsOn(':app')
26 | }
27 |
28 | tasks.register("clean", Delete) {
29 | delete rootProject.buildDir
30 | }
31 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx5G
2 |
3 | android.useAndroidX=true
4 | android.enableJetifier=true
5 | android.enableR8=true
6 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
7 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | def flutterSdkPath = {
3 | def properties = new Properties()
4 | file("local.properties").withInputStream { properties.load(it) }
5 | def flutterSdkPath = properties.getProperty("flutter.sdk")
6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
7 | return flutterSdkPath
8 | }
9 | settings.ext.flutterSdkPath = flutterSdkPath()
10 |
11 | includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
12 |
13 | repositories {
14 | google()
15 | mavenCentral()
16 | gradlePluginPortal()
17 | }
18 |
19 | plugins {
20 | id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
21 | }
22 | }
23 |
24 | plugins {
25 | id "dev.flutter.flutter-plugin-loader" version "1.0.0"
26 | id "com.android.application" version "8.7.3" apply false
27 | }
28 |
29 | include ":app"
30 |
--------------------------------------------------------------------------------
/example/ios/.gitignore:
--------------------------------------------------------------------------------
1 | **/dgph
2 | *.mode1v3
3 | *.mode2v3
4 | *.moved-aside
5 | *.pbxuser
6 | *.perspectivev3
7 | **/*sync/
8 | .sconsign.dblite
9 | .tags*
10 | **/.vagrant/
11 | **/DerivedData/
12 | Icon?
13 | **/Pods/
14 | **/.symlinks/
15 | profile
16 | xcuserdata
17 | **/.generated/
18 | Flutter/App.framework
19 | Flutter/Flutter.framework
20 | Flutter/Flutter.podspec
21 | Flutter/Generated.xcconfig
22 | Flutter/ephemeral/
23 | Flutter/app.flx
24 | Flutter/app.zip
25 | Flutter/flutter_assets/
26 | Flutter/flutter_export_environment.sh
27 | ServiceDefinitions.json
28 | Runner/GeneratedPluginRegistrant.*
29 |
30 | # Exceptions to above rules.
31 | !default.mode1v3
32 | !default.mode2v3
33 | !default.pbxuser
34 | !default.perspectivev3
35 |
--------------------------------------------------------------------------------
/example/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 12.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '12.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | target 'RunnerTests' do
36 | inherit! :search_paths
37 | end
38 | end
39 |
40 | post_install do |installer|
41 | installer.pods_project.targets.each do |target|
42 | flutter_additional_ios_build_settings(target)
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/example/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - connectivity_plus (0.0.1):
3 | - Flutter
4 | - Flutter (1.0.0)
5 | - flutter_image_compress_common (1.0.0):
6 | - Flutter
7 | - Mantle
8 | - SDWebImage
9 | - SDWebImageWebPCoder
10 | - image_picker_ios (0.0.1):
11 | - Flutter
12 | - integration_test (0.0.1):
13 | - Flutter
14 | - libwebp (1.5.0):
15 | - libwebp/demux (= 1.5.0)
16 | - libwebp/mux (= 1.5.0)
17 | - libwebp/sharpyuv (= 1.5.0)
18 | - libwebp/webp (= 1.5.0)
19 | - libwebp/demux (1.5.0):
20 | - libwebp/webp
21 | - libwebp/mux (1.5.0):
22 | - libwebp/demux
23 | - libwebp/sharpyuv (1.5.0)
24 | - libwebp/webp (1.5.0):
25 | - libwebp/sharpyuv
26 | - Mantle (2.2.0):
27 | - Mantle/extobjc (= 2.2.0)
28 | - Mantle/extobjc (2.2.0)
29 | - path_provider_foundation (0.0.1):
30 | - Flutter
31 | - FlutterMacOS
32 | - SDWebImage (5.20.1):
33 | - SDWebImage/Core (= 5.20.1)
34 | - SDWebImage/Core (5.20.1)
35 | - SDWebImageWebPCoder (0.14.6):
36 | - libwebp (~> 1.0)
37 | - SDWebImage/Core (~> 5.17)
38 | - shared_preferences_foundation (0.0.1):
39 | - Flutter
40 | - FlutterMacOS
41 | - sqflite_darwin (0.0.4):
42 | - Flutter
43 | - FlutterMacOS
44 | - url_launcher_ios (0.0.1):
45 | - Flutter
46 |
47 | DEPENDENCIES:
48 | - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
49 | - Flutter (from `Flutter`)
50 | - flutter_image_compress_common (from `.symlinks/plugins/flutter_image_compress_common/ios`)
51 | - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
52 | - integration_test (from `.symlinks/plugins/integration_test/ios`)
53 | - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
54 | - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
55 | - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
56 | - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
57 |
58 | SPEC REPOS:
59 | trunk:
60 | - libwebp
61 | - Mantle
62 | - SDWebImage
63 | - SDWebImageWebPCoder
64 |
65 | EXTERNAL SOURCES:
66 | connectivity_plus:
67 | :path: ".symlinks/plugins/connectivity_plus/ios"
68 | Flutter:
69 | :path: Flutter
70 | flutter_image_compress_common:
71 | :path: ".symlinks/plugins/flutter_image_compress_common/ios"
72 | image_picker_ios:
73 | :path: ".symlinks/plugins/image_picker_ios/ios"
74 | integration_test:
75 | :path: ".symlinks/plugins/integration_test/ios"
76 | path_provider_foundation:
77 | :path: ".symlinks/plugins/path_provider_foundation/darwin"
78 | shared_preferences_foundation:
79 | :path: ".symlinks/plugins/shared_preferences_foundation/darwin"
80 | sqflite_darwin:
81 | :path: ".symlinks/plugins/sqflite_darwin/darwin"
82 | url_launcher_ios:
83 | :path: ".symlinks/plugins/url_launcher_ios/ios"
84 |
85 | SPEC CHECKSUMS:
86 | connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d
87 | Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
88 | flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e
89 | image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
90 | integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573
91 | libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
92 | Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
93 | path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
94 | SDWebImage: 33d0f23bddeb5d209ae959153883247be6703713
95 | SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380
96 | shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
97 | sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
98 | url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
99 |
100 | PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796
101 |
102 | COCOAPODS: 1.16.2
103 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
43 |
49 |
50 |
51 |
52 |
53 |
64 |
66 |
72 |
73 |
74 |
75 |
81 |
83 |
89 |
90 |
91 |
92 |
94 |
95 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/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/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/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/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/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/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/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/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/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/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/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/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/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/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/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/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/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/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/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/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/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/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/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/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/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/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/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/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/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/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/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/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | FlutterToolboxExample
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | flutter_toolbox_example
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(FLUTTER_BUILD_NAME)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(FLUTTER_BUILD_NUMBER)
25 | LSRequiresIPhoneOS
26 |
27 | UILaunchStoryboardName
28 | LaunchScreen
29 | UIMainStoryboardFile
30 | Main
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 | UIViewControllerBasedStatusBarAppearance
45 |
46 | CADisableMinimumFrameDurationOnPhone
47 |
48 | UIApplicationSupportsIndirectInputEvents
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/example/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
--------------------------------------------------------------------------------
/example/ios/RunnerTests/RunnerTests.swift:
--------------------------------------------------------------------------------
1 | import Flutter
2 | import UIKit
3 | import XCTest
4 |
5 | @testable import FlutterToolbox
6 |
7 | // This demonstrates a simple unit test of the Swift portion of this plugin's implementation.
8 | //
9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
10 |
11 | class RunnerTests: XCTestCase {
12 |
13 | func testGetPlatformVersion() {
14 | let plugin = FlutterToolboxPlugin()
15 |
16 | let call = FlutterMethodCall(methodName: "getPlatformVersion", arguments: [])
17 |
18 | let resultExpectation = expectation(description: "result block must be called.")
19 | plugin.handle(call) { result in
20 | XCTAssertEqual(result as! String, "iOS " + UIDevice.current.systemVersion)
21 | resultExpectation.fulfill()
22 | }
23 | waitForExpectations(timeout: 1)
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/example/lib/auth_navigation.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_toolbox/flutter_toolbox.dart';
3 | import 'package:flutter_toolbox_example/auth_provider.dart';
4 |
5 | class AuthNavHomePage extends StatelessWidget {
6 | const AuthNavHomePage({super.key});
7 |
8 | @override
9 | Widget build(BuildContext context) {
10 | final authProvider = AuthProvider.of(context);
11 | return Scaffold(
12 | appBar: AppBar(
13 | title: const Text('AuthNavHomePage'),
14 | ),
15 | body: Column(
16 | children: [
17 | Row(
18 | children: [
19 | MaterialButton(
20 | child: const Text('NeedsAuthPage'),
21 | onPressed: () => push(context, const NeedsAuthPage()),
22 | ),
23 | MaterialButton(
24 | child: const Text('NoAuthPage'),
25 | onPressed: () => push(context, const NoAuthPage()),
26 | ),
27 | ],
28 | ),
29 | Row(
30 | children: [
31 | MaterialButton(
32 | child: const Text('Login'),
33 | onPressed: () => authProvider.login(),
34 | ),
35 | MaterialButton(
36 | child: const Text('Logout'),
37 | onPressed: () => authProvider.logout(),
38 | ),
39 | ],
40 | ),
41 | ],
42 | ),
43 | );
44 | }
45 | }
46 |
47 | class NeedsAuthPage extends StatelessWidget {
48 | const NeedsAuthPage({super.key});
49 |
50 | @override
51 | Widget build(BuildContext context) {
52 | return Scaffold(
53 | appBar: AppBar(
54 | title: const Text('NeedsAuthPage'),
55 | ),
56 | );
57 | }
58 | }
59 |
60 | class NoAuthPage extends StatelessWidget {
61 | const NoAuthPage({super.key});
62 |
63 | @override
64 | Widget build(BuildContext context) {
65 | return Scaffold(
66 | appBar: AppBar(
67 | title: const Text('NoAuthPage'),
68 | ),
69 | );
70 | }
71 | }
72 |
73 | class LoginPage extends StatelessWidget {
74 | const LoginPage({super.key});
75 |
76 | @override
77 | Widget build(BuildContext context) {
78 | return Scaffold(
79 | appBar: AppBar(
80 | title: const Text('LoginPage'),
81 | ),
82 | );
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/example/lib/auth_provider.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:provider/provider.dart';
3 |
4 | class AuthProvider extends ChangeNotifier {
5 | static AuthProvider of(BuildContext context, {bool listen = true}) =>
6 | Provider.of(context, listen: listen);
7 |
8 | User? _user;
9 |
10 | User? getUserCashed() => _user;
11 |
12 | void login() {
13 | _user = User();
14 | notifyListeners();
15 | }
16 |
17 | void logout() {
18 | _user = null;
19 | notifyListeners();
20 | }
21 | }
22 |
23 | class User {}
24 |
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_localizations/flutter_localizations.dart';
3 | import 'package:flutter_toolbox/flutter_toolbox.dart';
4 | import 'package:flutter_toolbox/generated/l10n.dart' as toolbox;
5 | import 'package:flutter_toolbox_example/auth_navigation.dart';
6 | import 'package:provider/provider.dart';
7 |
8 | import 'auth_provider.dart';
9 | import 'paginated_list_view_example.dart';
10 |
11 | void main() => runApp(const MyApp());
12 |
13 | class MyApp extends StatefulWidget {
14 | const MyApp({super.key});
15 |
16 | @override
17 | _MyAppState createState() => _MyAppState();
18 | }
19 |
20 | class _MyAppState extends State {
21 | @override
22 | Widget build(BuildContext context) {
23 | return MultiProvider(
24 | providers: [
25 | ChangeNotifierProvider(create: (_) => AuthProvider()),
26 | ],
27 | child: Consumer(
28 | builder: (context, value, child) {
29 | return ToolboxApp(
30 | toolboxConfig: ToolboxConfig(
31 | useWeservResizer: true,
32 | noItemsFoundWidget: const Icon(Icons.subject),
33 | unAuthenticatedPages: const [
34 | LoginPage,
35 | AuthNavHomePage,
36 | NoAuthPage,
37 | PaginatedListViewPage,
38 | PaginatedListViewExample,
39 | PaginatedListViewEmptyExample,
40 | PagewiseSliverListExample,
41 | ],
42 | isAuthenticated: () {
43 | final isLoggedIn = value.getUserCashed() != null;
44 | d("isLoggedIn = $isLoggedIn");
45 |
46 | return isLoggedIn;
47 | },
48 | onAuthorizedNavigation: (BuildContext context, Type pageType) {
49 | d("onAuthorizedNavigation#pageType = $pageType");
50 | return push(context, const LoginPage());
51 | },
52 | ),
53 | child: MaterialApp(
54 | localizationsDelegates: const [
55 | toolbox.S.delegate,
56 | GlobalMaterialLocalizations.delegate,
57 | GlobalWidgetsLocalizations.delegate,
58 | GlobalCupertinoLocalizations.delegate,
59 | ],
60 | supportedLocales: const [
61 | Locale("en", ""),
62 | Locale("ar", ""),
63 | ],
64 | theme: ThemeData(
65 | tabBarTheme: TabBarTheme(
66 | indicator: TabRoundedLineIndicator(
67 | context,
68 | indicatorSize: TabRoundedLineIndicatorSize.normal,
69 | indicatorHeight: 3,
70 | ),
71 | ),
72 | ),
73 | home: const HomePage(),
74 | ),
75 | );
76 | },
77 | ),
78 | );
79 | }
80 | }
81 |
82 | class HomePage extends StatefulWidget {
83 | const HomePage({super.key});
84 |
85 | @override
86 | HomePageState createState() => HomePageState();
87 | }
88 |
89 | class HomePageState extends State {
90 | @override
91 | void initState() {
92 | super.initState();
93 |
94 | ConnectionStatusBar.init(context);
95 | }
96 |
97 | @override
98 | Widget build(BuildContext context) {
99 | var themeData = Theme.of(context);
100 |
101 | return DefaultTabController(
102 | length: 3,
103 | child: Scaffold(
104 | appBar: AppBar(
105 | centerTitle: true,
106 | title: const Text(
107 | 'Plugin example app',
108 | style: TextStyle(color: Colors.black87),
109 | ),
110 | backgroundColor: Colors.white,
111 | bottom: TabBar(
112 | labelStyle: const TextStyle(fontWeight: FontWeight.w700),
113 | indicatorSize: TabBarIndicatorSize.label,
114 | labelColor: themeData.primaryColor,
115 | unselectedLabelColor: const Color(0xff5f6368),
116 | isScrollable: true,
117 | indicator: TabRoundedLineIndicator(
118 | context,
119 | indicatorSize: TabRoundedLineIndicatorSize.normal,
120 | indicatorHeight: 3,
121 | indicatorColor: Theme.of(context).primaryColor,
122 | ),
123 | tabs: const [
124 | Tab(text: "Home"),
125 | Tab(text: "Personal info"),
126 | Tab(text: "Data & personalization"),
127 | ],
128 | ),
129 | actions: [
130 | TextButton(
131 | onPressed: () => push(context, const PaginatedListViewPage()),
132 | child: const Text('PaginatedList page'),
133 | )
134 | ],
135 | ),
136 | body: Builder(builder: (context) {
137 | return Column(
138 | children: [
139 | Text(toolbox.S.of(context)?.please_check_your_connection ??
140 | 'Please check your connection'),
141 | const NetImage(
142 | 'https://via.placeholder.com/300',
143 | width: 300,
144 | fullScreen: true,
145 | ),
146 | const NetImage(
147 | 'https://via.placeholder.com/50',
148 | fullScreen: true,
149 | width: 50,
150 | ),
151 | NetImage(
152 | 'https://via.placeholder.com/50',
153 | width: 50,
154 | borderRadius: BorderRadius.circular(8),
155 | ),
156 | NetImage(
157 | "",
158 | width: 50,
159 | borderRadius: BorderRadius.circular(8),
160 | ),
161 | Row(
162 | children: [
163 | MaterialButton(
164 | child: const Text('Error toast'),
165 | onPressed: () => errorToast('Error'),
166 | ),
167 | MaterialButton(
168 | child: const Text('Success toast'),
169 | onPressed: () => successToast('Success'),
170 | ),
171 | MaterialButton(
172 | child: const Text('toast'),
173 | onPressed: () => toast('أهلا بكم'),
174 | ),
175 | ],
176 | ),
177 | MaterialButton(
178 | child: const Text('Auth navigation'),
179 | onPressed: () => push(context, const AuthNavHomePage()),
180 | ),
181 | ],
182 | );
183 | }),
184 | ),
185 | );
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/example/lib/paginated_list_view_example.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_toolbox/flutter_toolbox.dart';
5 | import 'package:http/http.dart' as http;
6 |
7 | void main() => runApp(const MyApp());
8 |
9 | class MyApp extends StatelessWidget {
10 | const MyApp({super.key});
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | return const MaterialApp(
15 | title: 'Pagewise Demo',
16 | home: PaginatedListViewPage(),
17 | );
18 | }
19 | }
20 |
21 | class PaginatedListViewPage extends StatelessWidget {
22 | const PaginatedListViewPage({super.key});
23 |
24 | @override
25 | Widget build(BuildContext context) {
26 | return DefaultTabController(
27 | length: 3,
28 | child: Scaffold(
29 | appBar: AppBar(
30 | title: const Text('Pagewise'),
31 | bottom: const TabBar(tabs: [
32 | Tab(text: 'List'),
33 | Tab(text: 'Empty List'),
34 | Tab(text: 'SliverList'),
35 | ]),
36 | ),
37 | body: const TabBarView(
38 | children: [
39 | PaginatedListViewExample(),
40 | PaginatedListViewEmptyExample(),
41 | PagewiseSliverListExample(),
42 | ],
43 | )),
44 | );
45 | }
46 | }
47 |
48 | class PaginatedListViewExample extends StatefulWidget {
49 | static const int PAGE_SIZE = 10;
50 |
51 | const PaginatedListViewExample({super.key});
52 |
53 | @override
54 | _PaginatedListViewExampleState createState() =>
55 | _PaginatedListViewExampleState();
56 | }
57 |
58 | class _PaginatedListViewExampleState extends State {
59 | @override
60 | void initState() {
61 | super.initState();
62 | d('_PaginatedListViewExampleState.initState');
63 | }
64 |
65 | @override
66 | Widget build(BuildContext context) {
67 | return PaginatedListView(
68 | pageSize: PaginatedListViewExample.PAGE_SIZE,
69 | refreshIndicatorBackgroundColor: Colors.white,
70 | refreshIndicatorColor: Colors.blue,
71 | // mutable: true,
72 | showRefreshIndicator: true,
73 | noItemsFoundWidget: const Icon(Icons.hourglass_empty),
74 | itemBuilder: _itemBuilder,
75 | loadingBuilder: (_) => const Icon(Icons.hourglass_full),
76 | pageFuture: (int pageIndex) {
77 | return BackendService.getPosts(
78 | pageIndex * PaginatedListViewExample.PAGE_SIZE,
79 | PaginatedListViewExample.PAGE_SIZE);
80 | },
81 | );
82 | }
83 |
84 | Widget _itemBuilder(BuildContext context, PostModel entry, int index) {
85 | return Column(
86 | children: [
87 | ListTile(
88 | leading: Icon(
89 | Icons.person,
90 | color: Colors.brown[200],
91 | ),
92 | title: Text(entry.title!),
93 | subtitle: Text(entry.body!),
94 | ),
95 | const Divider()
96 | ],
97 | );
98 | }
99 | }
100 |
101 | class PaginatedListViewEmptyExample extends StatelessWidget {
102 | static const int PAGE_SIZE = 0;
103 |
104 | const PaginatedListViewEmptyExample({super.key});
105 |
106 | @override
107 | Widget build(BuildContext context) {
108 | return PaginatedListView(
109 | pageSize: PAGE_SIZE,
110 | // mutable: true,
111 | showRefreshIndicator: true,
112 | // noItemsFoundWidget: Icon(Icons.hourglass_empty),
113 | itemBuilder: _itemBuilder,
114 | pageFuture: ((pageIndex) =>
115 | BackendService.getPosts(pageIndex * PAGE_SIZE, PAGE_SIZE)),
116 | );
117 | }
118 |
119 | Widget _itemBuilder(BuildContext context, PostModel entry, int index) {
120 | return Column(
121 | children: [
122 | ListTile(
123 | leading: Icon(
124 | Icons.person,
125 | color: Colors.brown[200],
126 | ),
127 | title: Text(entry.title!),
128 | subtitle: Text(entry.body!),
129 | ),
130 | const Divider()
131 | ],
132 | );
133 | }
134 | }
135 |
136 | class PagewiseSliverListExample extends StatelessWidget {
137 | static const int PAGE_SIZE = 6;
138 |
139 | const PagewiseSliverListExample({super.key});
140 |
141 | @override
142 | Widget build(BuildContext context) {
143 | return CustomScrollView(slivers: [
144 | const SliverAppBar(
145 | title: Text('This is a sliver app bar'),
146 | snap: true,
147 | floating: true,
148 | ),
149 | PaginatedSliverList(
150 | pageSize: PAGE_SIZE,
151 | itemBuilder: _itemBuilder,
152 | noItemsFoundWidget: const Icon(Icons.hourglass_empty),
153 | pageFuture: ((pageIndex) =>
154 | BackendService.getPosts(pageIndex * PAGE_SIZE, PAGE_SIZE)),
155 | ),
156 | ]);
157 | }
158 |
159 | Widget _itemBuilder(BuildContext context, PostModel entry, int index) {
160 | return Column(
161 | children: [
162 | ListTile(
163 | leading: Icon(
164 | Icons.person,
165 | color: Colors.brown[200],
166 | ),
167 | title: Text(entry.title!),
168 | subtitle: Text(entry.body!),
169 | ),
170 | const Divider()
171 | ],
172 | );
173 | }
174 | }
175 |
176 | class BackendService {
177 | static Future> getPosts(offset, limit) async {
178 | final responseBody = (await http.get(Uri.parse(
179 | 'https://jsonplaceholder.typicode.com/posts?_start=$offset&_limit=$limit')))
180 | .body;
181 |
182 | // The response body is an array of items
183 | return PostModel.fromJsonList(json.decode(responseBody)) ?? [];
184 | }
185 |
186 | static Future> getImages(offset, limit) async {
187 | final responseBody = (await http.get(Uri.parse(
188 | 'https://jsonplaceholder.typicode.com/photos?_start=$offset&_limit=$limit')))
189 | .body;
190 |
191 | // The response body is an array of items.
192 | return ImageModel.fromJsonList(json.decode(responseBody)) ?? [];
193 | }
194 | }
195 |
196 | class PostModel {
197 | String? title;
198 | String? body;
199 |
200 | PostModel.fromJson(obj) {
201 | title = obj['title'];
202 | body = obj['body'];
203 | }
204 |
205 | static List? fromJsonList(jsonList) {
206 | return jsonList.map((obj) => PostModel.fromJson(obj)).toList();
207 | }
208 | }
209 |
210 | class ImageModel {
211 | String? title;
212 | String? id;
213 | String? thumbnailUrl;
214 |
215 | ImageModel.fromJson(obj) {
216 | title = obj['title'];
217 | id = obj['id'].toString();
218 | thumbnailUrl = obj['thumbnailUrl'];
219 | }
220 |
221 | static List? fromJsonList(jsonList) {
222 | return jsonList.map((obj) => ImageModel.fromJson(obj)).toList();
223 | }
224 | }
225 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_toolbox_example
2 | description: "Demonstrates how to use the flutter_toolbox plugin."
3 | # The following line prevents the package from being accidentally published to
4 | # pub.dev using `flutter pub publish`. This is preferred for private packages.
5 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev
6 |
7 | environment:
8 | sdk: '>=3.2.1 <4.0.0'
9 |
10 | dependencies:
11 | flutter:
12 | sdk: flutter
13 | flutter_localizations:
14 | sdk: flutter
15 | intl:
16 |
17 | flutter_toolbox:
18 | path: ../
19 |
20 | # The following adds the Cupertino Icons font to your application.
21 | # Use with the CupertinoIcons class for iOS style icons.
22 | cupertino_icons: ^1.0.8
23 |
24 | dev_dependencies:
25 | integration_test:
26 | sdk: flutter
27 | flutter_test:
28 | sdk: flutter
29 |
30 | # The "flutter_lints" package below contains a set of recommended lints to
31 | # encourage good coding practices. The lint set provided by the package is
32 | # activated in the `analysis_options.yaml` file located at the root of your
33 | # package. See that file for information about deactivating specific lint
34 | # rules and activating additional ones.
35 | flutter_lints: ^5.0.0
36 |
37 | # For information on the generic Dart part of this file, see the
38 | # following page: https://dart.dev/tools/pub/pubspec
39 |
40 | # The following section is specific to Flutter.
41 | flutter:
42 |
43 | # The following line ensures that the Material Icons font is
44 | # included with your application, so that you can use the icons in
45 | # the material Icons class.
46 | uses-material-design: true
47 |
48 | # To add assets to your application, add an assets section, like this:
49 | # assets:
50 | # - images/a_dot_burr.jpeg
51 | # - images/a_dot_ham.jpeg
52 |
53 | # An image asset can refer to one or more resolution-specific "variants", see
54 | # https://flutter.dev/assets-and-images/#resolution-aware.
55 |
56 | # For details regarding adding assets from package dependencies, see
57 | # https://flutter.dev/assets-and-images/#from-packages
58 |
59 | # To add custom fonts to your application, add a fonts section here,
60 | # in this "flutter" section. Each entry in this list should have a
61 | # "family" key with the font family name, and a "fonts" key with a
62 | # list giving the asset and other descriptors for the font. For
63 | # example:
64 | # fonts:
65 | # - family: Schyler
66 | # fonts:
67 | # - asset: fonts/Schyler-Regular.ttf
68 | # - asset: fonts/Schyler-Italic.ttf
69 | # style: italic
70 | # - family: Trajan Pro
71 | # fonts:
72 | # - asset: fonts/TrajanPro.ttf
73 | # - asset: fonts/TrajanPro_Bold.ttf
74 | # weight: 700
75 | #
76 | # For details regarding fonts from package dependencies,
77 | # see https://flutter.dev/custom-fonts/#from-packages
78 |
--------------------------------------------------------------------------------
/example/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | void main() {}
2 |
--------------------------------------------------------------------------------
/flutter_utils.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .vagrant/
3 | .sconsign.dblite
4 | .svn/
5 |
6 | .DS_Store
7 | *.swp
8 | profile
9 |
10 | DerivedData/
11 | build/
12 | GeneratedPluginRegistrant.h
13 | GeneratedPluginRegistrant.m
14 |
15 | .generated/
16 |
17 | *.pbxuser
18 | *.mode1v3
19 | *.mode2v3
20 | *.perspectivev3
21 |
22 | !default.pbxuser
23 | !default.mode1v3
24 | !default.mode2v3
25 | !default.perspectivev3
26 |
27 | xcuserdata
28 |
29 | *.moved-aside
30 |
31 | *.pyc
32 | *sync/
33 | Icon?
34 | .tags*
35 |
36 | /Flutter/Generated.xcconfig
37 |
--------------------------------------------------------------------------------
/ios/Assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/humazed/flutter_toolbox/ee9341770219f816a5b84da7577c91139d9395e3/ios/Assets/.gitkeep
--------------------------------------------------------------------------------
/ios/Classes/SwiftFlutterToolboxPlugin.swift:
--------------------------------------------------------------------------------
1 | import Flutter
2 | import UIKit
3 |
4 | public class Plug002Plugin: NSObject, FlutterPlugin {
5 | public static func register(with registrar: FlutterPluginRegistrar) {
6 | let channel = FlutterMethodChannel(name: "flutter_toolbox", binaryMessenger: registrar.messenger())
7 | let instance = SwiftFlutterToolboxPlugin()
8 | registrar.addMethodCallDelegate(instance, channel: channel)
9 | }
10 |
11 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
12 | switch call.method {
13 | case "getPlatformVersion":
14 | result("iOS " + UIDevice.current.systemVersion)
15 | default:
16 | result(FlutterMethodNotImplemented)
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ios/flutter_toolbox.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
3 | # Run `pod lib lint flutter_toolbox.podspec` to validate before publishing.
4 | #
5 | Pod::Spec.new do |s|
6 | s.name = 'flutter_toolbox'
7 | s.version = '0.0.1'
8 | s.summary = 'Common flutter widgets and helper methods.'
9 | s.description = <<-DESC
10 | Common flutter widgets and helper methods.
11 | DESC
12 | s.homepage = 'http://example.com'
13 | s.license = { :file => '../LICENSE' }
14 | s.author = { 'Your Company' => 'email@example.com' }
15 | s.source = { :path => '.' }
16 | s.source_files = 'Classes/**/*'
17 | s.dependency 'Flutter'
18 | s.platform = :ios, '11.0'
19 |
20 | # Flutter.framework does not contain a i386 slice.
21 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
22 | s.swift_version = '5.0'
23 | end
24 |
--------------------------------------------------------------------------------
/lib/flutter_toolbox.dart:
--------------------------------------------------------------------------------
1 | export 'package:flutter_toolbox/src/http.dart';
2 | export 'package:flutter_toolbox/src/image_picker.dart';
3 | export 'package:flutter_toolbox/src/log.dart';
4 | export 'package:flutter_toolbox/src/model/error/error_response.dart';
5 | export 'package:flutter_toolbox/src/prefs.dart';
6 | export 'package:flutter_toolbox/src/time.dart';
7 | export 'package:flutter_toolbox/src/navigation.dart';
8 | export 'package:flutter_toolbox/src/ui/date_picker_field.dart';
9 | export 'package:flutter_toolbox/src/ui/loading_builder.dart';
10 | export 'package:flutter_toolbox/src/ui/multi_select_chip.dart';
11 | export 'package:flutter_toolbox/src/ui/time_picker_field.dart';
12 | export 'package:flutter_toolbox/src/ui/toast.dart';
13 | export 'package:flutter_toolbox/src/ui/wrap_dropdown_button.dart';
14 | export 'package:flutter_toolbox/src/ui/net_image/net_image.dart';
15 | export 'package:flutter_toolbox/src/ui/listview/paginated_list_view.dart';
16 | export 'package:flutter_toolbox/src/ui/listview/paginated_sliver_list.dart';
17 | export 'package:flutter_toolbox/src/ui/listview/paginated_grid_view_count.dart';
18 | export 'package:flutter_toolbox/src/ui/listview/paginated_grid_view_extent.dart';
19 | export 'package:flutter_toolbox/src/ui/tab_rounded_line_indicator.dart';
20 | export 'package:flutter_toolbox/src/ui/connection_status_bar.dart';
21 | export 'package:flutter_toolbox/src/ui/search_item_picker.dart';
22 | export 'package:flutter_toolbox/src/ui/ink_well_stacked.dart';
23 | export 'package:flutter_toolbox/src/url_launchers.dart';
24 | export 'package:flutter_toolbox/src/config/toolbox_app.dart';
25 | export 'package:flutter_toolbox/src/config/toolbox_config.dart';
26 | export 'package:flutter_toolbox/src/extensions/string_extensions.dart';
27 | export 'package:flutter_toolbox/src/extensions/list_extensions.dart';
28 | export 'package:flutter_toolbox/src/extensions/future_extensions.dart';
29 | export 'package:flutter_toolbox/src/extensions/iterable_extensions.dart';
30 | export 'package:flutter_toolbox/src/extensions/date_time_extensions.dart';
31 |
--------------------------------------------------------------------------------
/lib/generated/intl/messages_all.dart:
--------------------------------------------------------------------------------
1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
2 | // This is a library that looks up messages for specific locales by
3 | // delegating to the appropriate library.
4 |
5 | // Ignore issues from commonly used lints in this file.
6 | // ignore_for_file:implementation_imports, file_names, unnecessary_new
7 | // ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering
8 | // ignore_for_file:argument_type_not_assignable, invalid_assignment
9 | // ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases
10 | // ignore_for_file:comment_references
11 |
12 | import 'dart:async';
13 |
14 | import 'package:intl/intl.dart';
15 | import 'package:intl/message_lookup_by_library.dart';
16 | import 'package:intl/src/intl_helpers.dart';
17 |
18 | import 'messages_ar.dart' as messages_ar;
19 | import 'messages_en.dart' as messages_en;
20 | import 'messages_ru.dart' as messages_ru;
21 |
22 | typedef Future LibraryLoader();
23 | Map _deferredLibraries = {
24 | 'ar': () => new Future.value(null),
25 | 'en': () => new Future.value(null),
26 | 'ru': () => new Future.value(null),
27 | };
28 |
29 | MessageLookupByLibrary? _findExact(String localeName) {
30 | switch (localeName) {
31 | case 'ar':
32 | return messages_ar.messages;
33 | case 'en':
34 | return messages_en.messages;
35 | case 'ru':
36 | return messages_ru.messages;
37 | default:
38 | return null;
39 | }
40 | }
41 |
42 | /// User programs should call this before using [localeName] for messages.
43 | Future initializeMessages(String localeName) async {
44 | var availableLocale = Intl.verifiedLocale(
45 | localeName, (locale) => _deferredLibraries[locale] != null,
46 | onFailure: (_) => null);
47 | if (availableLocale == null) {
48 | return new Future.value(false);
49 | }
50 | var lib = _deferredLibraries[availableLocale];
51 | await (lib == null ? new Future.value(false) : lib());
52 | initializeInternalMessageLookup(() => new CompositeMessageLookup());
53 | messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor);
54 | return new Future.value(true);
55 | }
56 |
57 | bool _messagesExistFor(String locale) {
58 | try {
59 | return _findExact(locale) != null;
60 | } catch (e) {
61 | return false;
62 | }
63 | }
64 |
65 | MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) {
66 | var actualLocale =
67 | Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null);
68 | if (actualLocale == null) return null;
69 | return _findExact(actualLocale);
70 | }
71 |
--------------------------------------------------------------------------------
/lib/generated/intl/messages_ar.dart:
--------------------------------------------------------------------------------
1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
2 | // This is a library that provides messages for a ar locale. All the
3 | // messages from the main program should be duplicated here with the same
4 | // function name.
5 |
6 | // Ignore issues from commonly used lints in this file.
7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes
11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes
12 |
13 | import 'package:intl/intl.dart';
14 | import 'package:intl/message_lookup_by_library.dart';
15 |
16 | final messages = new MessageLookup();
17 |
18 | typedef String MessageIfAbsent(String messageStr, List args);
19 |
20 | class MessageLookup extends MessageLookupByLibrary {
21 | String get localeName => 'ar';
22 |
23 | final messages = _notInlinedMessages(_notInlinedMessages);
24 | static Map _notInlinedMessages(_) => {
25 | "add_photo": MessageLookupByLibrary.simpleMessage("إضافة صورة"),
26 | "camera": MessageLookupByLibrary.simpleMessage("كاميرا"),
27 | "could_not_launch_google_maps":
28 | MessageLookupByLibrary.simpleMessage("لا يمكن فتح خرائط جوجل"),
29 | "couldnt_open_the_mail_app": MessageLookupByLibrary.simpleMessage(
30 | "لا يمكن فتح تطبيق البريد الإلكتروني"),
31 | "couldnt_open_the_phone_app":
32 | MessageLookupByLibrary.simpleMessage("لا يمكن فتح تطبيق الهاتف"),
33 | "couldnt_open_this_url":
34 | MessageLookupByLibrary.simpleMessage("لا يمكن فتح هذا الرابط"),
35 | "gallery": MessageLookupByLibrary.simpleMessage("المعرض"),
36 | "please_check_your_connection":
37 | MessageLookupByLibrary.simpleMessage("تأكد من وجود انترنت"),
38 | "please_check_your_internet_connection":
39 | MessageLookupByLibrary.simpleMessage(
40 | "الرجاء التاكد من اتصالك بالأنترنت"),
41 | "required_field": MessageLookupByLibrary.simpleMessage("حقل مطلوب"),
42 | "search": MessageLookupByLibrary.simpleMessage("إبحث..."),
43 | "select_date": MessageLookupByLibrary.simpleMessage("إختر التاريخ"),
44 | "select_time": MessageLookupByLibrary.simpleMessage("إختر الوقت"),
45 | "server_error":
46 | MessageLookupByLibrary.simpleMessage("خطأ من الخادم حاول مرة اخري"),
47 | "the_email_address_or_password_is_wrong":
48 | MessageLookupByLibrary.simpleMessage(
49 | "البريد الإلكتروني او كلمة المرور غير صحيحة"),
50 | "this_file_is_not_an_image":
51 | MessageLookupByLibrary.simpleMessage("هذا الملف ليس صورة")
52 | };
53 | }
54 |
--------------------------------------------------------------------------------
/lib/generated/intl/messages_en.dart:
--------------------------------------------------------------------------------
1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
2 | // This is a library that provides messages for a en locale. All the
3 | // messages from the main program should be duplicated here with the same
4 | // function name.
5 |
6 | // Ignore issues from commonly used lints in this file.
7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes
11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes
12 |
13 | import 'package:intl/intl.dart';
14 | import 'package:intl/message_lookup_by_library.dart';
15 |
16 | final messages = new MessageLookup();
17 |
18 | typedef String MessageIfAbsent(String messageStr, List args);
19 |
20 | class MessageLookup extends MessageLookupByLibrary {
21 | String get localeName => 'en';
22 |
23 | final messages = _notInlinedMessages(_notInlinedMessages);
24 | static Map _notInlinedMessages(_) => {
25 | "add_photo": MessageLookupByLibrary.simpleMessage("Add photo"),
26 | "camera": MessageLookupByLibrary.simpleMessage("Camera"),
27 | "could_not_launch_google_maps": MessageLookupByLibrary.simpleMessage(
28 | "Could not launch google maps"),
29 | "couldnt_open_the_mail_app":
30 | MessageLookupByLibrary.simpleMessage("Couldn\'t open the mail app"),
31 | "couldnt_open_the_phone_app": MessageLookupByLibrary.simpleMessage(
32 | "Couldn\'t open the phone app"),
33 | "couldnt_open_this_url":
34 | MessageLookupByLibrary.simpleMessage("Couldn\'t open this url"),
35 | "gallery": MessageLookupByLibrary.simpleMessage("Gallery"),
36 | "please_check_your_connection": MessageLookupByLibrary.simpleMessage(
37 | "Please check your connection"),
38 | "please_check_your_internet_connection":
39 | MessageLookupByLibrary.simpleMessage(
40 | "Please check your internet connection"),
41 | "required_field":
42 | MessageLookupByLibrary.simpleMessage("Required Field"),
43 | "search": MessageLookupByLibrary.simpleMessage("Search..."),
44 | "select_date": MessageLookupByLibrary.simpleMessage("Select date"),
45 | "select_time": MessageLookupByLibrary.simpleMessage("Select time"),
46 | "server_error": MessageLookupByLibrary.simpleMessage("Server error"),
47 | "the_email_address_or_password_is_wrong":
48 | MessageLookupByLibrary.simpleMessage(
49 | "The email address or password is wrong"),
50 | "this_file_is_not_an_image":
51 | MessageLookupByLibrary.simpleMessage("This file is not an image")
52 | };
53 | }
54 |
--------------------------------------------------------------------------------
/lib/generated/intl/messages_ru.dart:
--------------------------------------------------------------------------------
1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
2 | // This is a library that provides messages for a ru locale. All the
3 | // messages from the main program should be duplicated here with the same
4 | // function name.
5 |
6 | // Ignore issues from commonly used lints in this file.
7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes
11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes
12 |
13 | import 'package:intl/intl.dart';
14 | import 'package:intl/message_lookup_by_library.dart';
15 |
16 | final messages = new MessageLookup();
17 |
18 | typedef String MessageIfAbsent(String messageStr, List args);
19 |
20 | class MessageLookup extends MessageLookupByLibrary {
21 | String get localeName => 'ru';
22 |
23 | final messages = _notInlinedMessages(_notInlinedMessages);
24 | static Map _notInlinedMessages(_) => {
25 | "add_photo": MessageLookupByLibrary.simpleMessage("Добавить фото"),
26 | "camera": MessageLookupByLibrary.simpleMessage("камера"),
27 | "could_not_launch_google_maps": MessageLookupByLibrary.simpleMessage(
28 | "Не удалось запустить Google Maps"),
29 | "couldnt_open_the_mail_app":
30 | MessageLookupByLibrary.simpleMessage("Couldn\'t open the mail app"),
31 | "couldnt_open_the_phone_app": MessageLookupByLibrary.simpleMessage(
32 | "Couldn\'t open the phone app"),
33 | "couldnt_open_this_url":
34 | MessageLookupByLibrary.simpleMessage("Couldn\'t open this url"),
35 | "gallery": MessageLookupByLibrary.simpleMessage("Галерея"),
36 | "please_check_your_connection": MessageLookupByLibrary.simpleMessage(
37 | "Пожалуйста, проверьте ваше соединение"),
38 | "please_check_your_internet_connection":
39 | MessageLookupByLibrary.simpleMessage(
40 | "Пожалуйста, проверьте ваше соединение"),
41 | "required_field":
42 | MessageLookupByLibrary.simpleMessage("Обязательное поле"),
43 | "search": MessageLookupByLibrary.simpleMessage("Поиск..."),
44 | "select_date": MessageLookupByLibrary.simpleMessage("Выберите дату"),
45 | "select_time": MessageLookupByLibrary.simpleMessage("Выберите время"),
46 | "server_error": MessageLookupByLibrary.simpleMessage("Ошибка сервера"),
47 | "the_email_address_or_password_is_wrong":
48 | MessageLookupByLibrary.simpleMessage(
49 | "Неверный адрес электронной почты или пароль"),
50 | "this_file_is_not_an_image": MessageLookupByLibrary.simpleMessage(
51 | "Этот файл не является изображением")
52 | };
53 | }
54 |
--------------------------------------------------------------------------------
/lib/generated/l10n.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 | import 'package:flutter/material.dart';
3 | import 'package:intl/intl.dart';
4 | import 'intl/messages_all.dart';
5 |
6 | // **************************************************************************
7 | // Generator: Flutter Intl IDE plugin
8 | // Made by Localizely
9 | // **************************************************************************
10 |
11 | // ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars
12 | // ignore_for_file: join_return_with_assignment, prefer_final_in_for_each
13 | // ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes
14 |
15 | class S {
16 | S();
17 |
18 | static S? _current;
19 |
20 | static S get current {
21 | assert(_current != null,
22 | 'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.');
23 | return _current!;
24 | }
25 |
26 | static const AppLocalizationDelegate delegate = AppLocalizationDelegate();
27 |
28 | static Future load(Locale locale) {
29 | final name = (locale.countryCode?.isEmpty ?? false)
30 | ? locale.languageCode
31 | : locale.toString();
32 | final localeName = Intl.canonicalizedLocale(name);
33 | return initializeMessages(localeName).then((_) {
34 | Intl.defaultLocale = localeName;
35 | final instance = S();
36 | S._current = instance;
37 |
38 | return instance;
39 | });
40 | }
41 |
42 | static S? of(BuildContext context) {
43 | final instance = S.maybeOf(context);
44 | assert(instance != null,
45 | 'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?');
46 | return instance;
47 | }
48 |
49 | static S? maybeOf(BuildContext context) {
50 | return Localizations.of(context, S);
51 | }
52 |
53 | /// `Required Field`
54 | String get required_field {
55 | return Intl.message(
56 | 'Required Field',
57 | name: 'required_field',
58 | desc: '',
59 | args: [],
60 | );
61 | }
62 |
63 | /// `Please check your connection`
64 | String get please_check_your_connection {
65 | return Intl.message(
66 | 'Please check your connection',
67 | name: 'please_check_your_connection',
68 | desc: '',
69 | args: [],
70 | );
71 | }
72 |
73 | /// `Server error`
74 | String get server_error {
75 | return Intl.message(
76 | 'Server error',
77 | name: 'server_error',
78 | desc: '',
79 | args: [],
80 | );
81 | }
82 |
83 | /// `The email address or password is wrong`
84 | String get the_email_address_or_password_is_wrong {
85 | return Intl.message(
86 | 'The email address or password is wrong',
87 | name: 'the_email_address_or_password_is_wrong',
88 | desc: '',
89 | args: [],
90 | );
91 | }
92 |
93 | /// `This file is not an image`
94 | String get this_file_is_not_an_image {
95 | return Intl.message(
96 | 'This file is not an image',
97 | name: 'this_file_is_not_an_image',
98 | desc: '',
99 | args: [],
100 | );
101 | }
102 |
103 | /// `Select time`
104 | String get select_time {
105 | return Intl.message(
106 | 'Select time',
107 | name: 'select_time',
108 | desc: '',
109 | args: [],
110 | );
111 | }
112 |
113 | /// `Select date`
114 | String get select_date {
115 | return Intl.message(
116 | 'Select date',
117 | name: 'select_date',
118 | desc: '',
119 | args: [],
120 | );
121 | }
122 |
123 | /// `Please check your internet connection`
124 | String get please_check_your_internet_connection {
125 | return Intl.message(
126 | 'Please check your internet connection',
127 | name: 'please_check_your_internet_connection',
128 | desc: '',
129 | args: [],
130 | );
131 | }
132 |
133 | /// `Search...`
134 | String get search {
135 | return Intl.message(
136 | 'Search...',
137 | name: 'search',
138 | desc: '',
139 | args: [],
140 | );
141 | }
142 |
143 | /// `Could not launch google maps`
144 | String get could_not_launch_google_maps {
145 | return Intl.message(
146 | 'Could not launch google maps',
147 | name: 'could_not_launch_google_maps',
148 | desc: '',
149 | args: [],
150 | );
151 | }
152 |
153 | /// `Couldn't open this url`
154 | String get couldnt_open_this_url {
155 | return Intl.message(
156 | 'Couldn\'t open this url',
157 | name: 'couldnt_open_this_url',
158 | desc: '',
159 | args: [],
160 | );
161 | }
162 |
163 | /// `Couldn't open the mail app`
164 | String get couldnt_open_the_mail_app {
165 | return Intl.message(
166 | 'Couldn\'t open the mail app',
167 | name: 'couldnt_open_the_mail_app',
168 | desc: '',
169 | args: [],
170 | );
171 | }
172 |
173 | /// `Couldn't open the phone app`
174 | String get couldnt_open_the_phone_app {
175 | return Intl.message(
176 | 'Couldn\'t open the phone app',
177 | name: 'couldnt_open_the_phone_app',
178 | desc: '',
179 | args: [],
180 | );
181 | }
182 |
183 | /// `Add photo`
184 | String get add_photo {
185 | return Intl.message(
186 | 'Add photo',
187 | name: 'add_photo',
188 | desc: '',
189 | args: [],
190 | );
191 | }
192 |
193 | /// `Camera`
194 | String get camera {
195 | return Intl.message(
196 | 'Camera',
197 | name: 'camera',
198 | desc: '',
199 | args: [],
200 | );
201 | }
202 |
203 | /// `Gallery`
204 | String get gallery {
205 | return Intl.message(
206 | 'Gallery',
207 | name: 'gallery',
208 | desc: '',
209 | args: [],
210 | );
211 | }
212 | }
213 |
214 | class AppLocalizationDelegate extends LocalizationsDelegate {
215 | const AppLocalizationDelegate();
216 |
217 | List get supportedLocales {
218 | return const [
219 | Locale.fromSubtags(languageCode: 'en'),
220 | Locale.fromSubtags(languageCode: 'ar'),
221 | Locale.fromSubtags(languageCode: 'ru'),
222 | ];
223 | }
224 |
225 | @override
226 | bool isSupported(Locale locale) => _isSupported(locale);
227 | @override
228 | Future load(Locale locale) => S.load(locale);
229 | @override
230 | bool shouldReload(AppLocalizationDelegate old) => false;
231 |
232 | bool _isSupported(Locale locale) {
233 | for (var supportedLocale in supportedLocales) {
234 | if (supportedLocale.languageCode == locale.languageCode) {
235 | return true;
236 | }
237 | }
238 | return false;
239 | }
240 | }
241 |
--------------------------------------------------------------------------------
/lib/l10n/intl_ar.arb:
--------------------------------------------------------------------------------
1 | {
2 | "@@locale": "ar",
3 | "required_field": "حقل مطلوب",
4 | "please_check_your_connection": "تأكد من وجود انترنت",
5 | "server_error": "خطأ من الخادم حاول مرة اخري",
6 | "the_email_address_or_password_is_wrong": "البريد الإلكتروني او كلمة المرور غير صحيحة",
7 | "this_file_is_not_an_image": "هذا الملف ليس صورة",
8 | "select_time": "إختر الوقت",
9 | "select_date": "إختر التاريخ",
10 | "please_check_your_internet_connection": "الرجاء التاكد من اتصالك بالأنترنت",
11 | "search": "إبحث...",
12 | "could_not_launch_google_maps": "لا يمكن فتح خرائط جوجل",
13 | "couldnt_open_this_url": "لا يمكن فتح هذا الرابط",
14 | "couldnt_open_the_mail_app": "لا يمكن فتح تطبيق البريد الإلكتروني",
15 | "couldnt_open_the_phone_app": "لا يمكن فتح تطبيق الهاتف",
16 | "add_photo": "إضافة صورة",
17 | "camera": "كاميرا",
18 | "gallery": "المعرض"
19 | }
--------------------------------------------------------------------------------
/lib/l10n/intl_en.arb:
--------------------------------------------------------------------------------
1 | {
2 | "@@locale": "en",
3 | "required_field": "Required Field",
4 | "please_check_your_connection": "Please check your connection",
5 | "server_error": "Server error",
6 | "the_email_address_or_password_is_wrong": "The email address or password is wrong",
7 | "this_file_is_not_an_image": "This file is not an image",
8 | "select_time": "Select time",
9 | "select_date": "Select date",
10 | "please_check_your_internet_connection": "Please check your internet connection",
11 | "search": "Search...",
12 | "could_not_launch_google_maps": "Could not launch google maps",
13 | "couldnt_open_this_url": "Couldn't open this url",
14 | "couldnt_open_the_mail_app": "Couldn't open the mail app",
15 | "couldnt_open_the_phone_app": "Couldn't open the phone app",
16 | "add_photo": "Add photo",
17 | "camera": "Camera",
18 | "gallery": "Gallery"
19 | }
--------------------------------------------------------------------------------
/lib/l10n/intl_ru.arb:
--------------------------------------------------------------------------------
1 | {
2 | "@@locale": "ru",
3 | "required_field": "Обязательное поле",
4 | "please_check_your_connection": "Пожалуйста, проверьте ваше соединение",
5 | "server_error": "Ошибка сервера",
6 | "the_email_address_or_password_is_wrong": "Неверный адрес электронной почты или пароль",
7 | "this_file_is_not_an_image": "Этот файл не является изображением",
8 | "select_time": "Выберите время",
9 | "select_date": "Выберите дату",
10 | "please_check_your_internet_connection": "Пожалуйста, проверьте ваше соединение",
11 | "search": "Поиск...",
12 | "could_not_launch_google_maps": "Не удалось запустить Google Maps",
13 | "couldnt_open_this_url": "Couldn't open this url",
14 | "couldnt_open_the_mail_app": "Couldn't open the mail app",
15 | "couldnt_open_the_phone_app": "Couldn't open the phone app",
16 | "add_photo": "Добавить фото",
17 | "camera": "камера",
18 | "gallery": "Галерея"
19 | }
--------------------------------------------------------------------------------
/lib/src/config/toolbox_app.dart:
--------------------------------------------------------------------------------
1 | import 'dart:collection';
2 |
3 | import 'package:flutter/widgets.dart';
4 | import 'package:flutter_toolbox/src/config/toolbox_config.dart';
5 | import 'package:oktoast/oktoast.dart';
6 | import 'package:provider/provider.dart';
7 |
8 | LinkedHashMap<_ToolboxAppState, BuildContext> contextMap = LinkedHashMap();
9 |
10 | class ToolboxApp extends StatefulWidget {
11 | const ToolboxApp({
12 | super.key,
13 | required this.child,
14 | this.toolboxConfig,
15 | });
16 |
17 | /// Usually should be [MaterialApp] or [CupertinoApp].
18 | final Widget child;
19 |
20 | final ToolboxConfig? toolboxConfig;
21 |
22 | @override
23 | _ToolboxAppState createState() => _ToolboxAppState();
24 | }
25 |
26 | class _ToolboxAppState extends State {
27 | @override
28 | void initState() {
29 | super.initState();
30 | }
31 |
32 | @override
33 | void dispose() {
34 | contextMap.remove(this);
35 | super.dispose();
36 | }
37 |
38 | @override
39 | Widget build(BuildContext context) {
40 | contextMap[this] = context;
41 |
42 | return OKToast(
43 | textPadding: const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
44 | position: ToastPosition.bottom,
45 | radius: 50,
46 | child: Provider.value(
47 | value: widget.toolboxConfig,
48 | child: widget.child,
49 | ),
50 | );
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/lib/src/config/toolbox_config.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_toolbox/src/ui/listview/pagewise/flutter_pagewise.dart';
3 | import 'package:provider/provider.dart';
4 |
5 | class ToolboxConfig {
6 | ToolboxConfig({
7 | this.noItemsFoundBuilder,
8 | this.noItemsFoundWidget,
9 | this.useWeservResizer = false,
10 | this.logLoadedImageUrl = false,
11 | this.unAuthenticatedPages,
12 | this.isAuthenticated,
13 | this.onAuthorizedNavigation,
14 | });
15 |
16 | final NoItemsFoundBuilder? noItemsFoundBuilder;
17 | final Widget? noItemsFoundWidget;
18 | final bool useWeservResizer;
19 | final bool logLoadedImageUrl;
20 |
21 | final List? unAuthenticatedPages;
22 | final bool Function()? isAuthenticated;
23 | final Future Function(BuildContext context, Type pageType)?
24 | onAuthorizedNavigation;
25 |
26 | static ToolboxConfig of(BuildContext context, {bool listen = true}) =>
27 | Provider.of(context, listen: listen);
28 | }
29 |
--------------------------------------------------------------------------------
/lib/src/extensions/date_time_extensions.dart:
--------------------------------------------------------------------------------
1 | extension DateExtension on DateTime {
2 | int get secondsSinceEpoch => (millisecondsSinceEpoch / 1000).round();
3 | }
4 |
--------------------------------------------------------------------------------
/lib/src/extensions/future_extensions.dart:
--------------------------------------------------------------------------------
1 | extension FutureExtension on Iterable> {
2 | Future> wait() {
3 | return Future.wait(this);
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/lib/src/extensions/iterable_extensions.dart:
--------------------------------------------------------------------------------
1 | extension IterableExtension on Iterable {
2 | Iterable mapIndexed(T Function(int index, E e) f) {
3 | var index = 0;
4 | return map((e) => f(index++, e));
5 | }
6 |
7 | void forEachIndexed(T Function(int index, E e) f) {
8 | var index = 0;
9 | return forEach((e) => f(index++, e));
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/lib/src/extensions/list_extensions.dart:
--------------------------------------------------------------------------------
1 | extension ListExtension on List {
2 | E? elementAtOrNull(int index) {
3 | return getOrNull(index);
4 | }
5 |
6 | E? getOrNull(int index) {
7 | return (index >= 0 && length > index) ? elementAt(index) : null;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/lib/src/extensions/string_extensions.dart:
--------------------------------------------------------------------------------
1 | extension StringExtension on String {
2 | bool containsIgnoreCase(String other) {
3 | return toLowerCase().contains(other.toLowerCase());
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/lib/src/http.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:chopper/chopper.dart';
4 | import 'package:flutter_toolbox/generated/l10n.dart';
5 | import 'package:flutter_toolbox/src/log.dart';
6 | import 'package:flutter_toolbox/src/ui/toast.dart';
7 | import 'package:http/http.dart' as http;
8 |
9 | import 'model/error/error_response.dart';
10 |
11 | Future multiFile(File file, String name) async {
12 | return http.MultipartFile.fromPath(name, file.path);
13 | }
14 |
15 | Future safeRequest(
16 | Future> request, {
17 | dynamic Function(T? result)? onSuccess,
18 | dynamic Function(ErrorResponse? error)? onError,
19 | dynamic Function(dynamic error)? onUnknownError,
20 | bool showServerErrorMessage = true,
21 | }) async {
22 | try {
23 | final Response response = await request;
24 | if (response.isSuccessful) {
25 | return await onSuccess?.call(response.body);
26 | } else {
27 | final error = (response.error as ErrorResponse).error;
28 | if (showServerErrorMessage) {
29 | if (error == 'Unauthorized access' || error == 'Unauthorized') {
30 | errorToast(S.current.the_email_address_or_password_is_wrong);
31 | } else {
32 | errorToast(error!);
33 | }
34 | }
35 | return await onError?.call(response.error as ErrorResponse?);
36 | }
37 | } on SocketException catch (e) {
38 | d2('SocketException-> $e');
39 | errorToast(S.current.please_check_your_connection);
40 | } on ErrorResponse catch (e) {
41 | d2('ErrorResponse-> $e');
42 | if (showServerErrorMessage) errorToast(e.error!);
43 |
44 | return await onError?.call(e);
45 | } catch (e) {
46 | d2('UnknownError-> $e');
47 | errorToast(S.current.server_error);
48 | return await onUnknownError?.call(e);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/lib/src/image_picker.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 | import 'dart:typed_data';
3 |
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_image_compress/flutter_image_compress.dart';
6 | import 'package:flutter_toolbox/generated/l10n.dart';
7 | import 'package:http/http.dart';
8 | import 'package:image_picker/image_picker.dart';
9 | import 'package:path/path.dart' as p;
10 | import 'package:path_provider/path_provider.dart';
11 | import 'package:uuid/uuid.dart';
12 |
13 | import 'http.dart';
14 |
15 | Future picImage(BuildContext context) async {
16 | ImageSource? source = await showDialog(
17 | context: context,
18 | builder: (BuildContext context) {
19 | return SimpleDialog(
20 | title: Text(S.of(context)?.add_photo ?? 'Add photo'),
21 | children: [
22 | SimpleDialogOption(
23 | onPressed: () {
24 | FocusScope.of(context).unfocus();
25 | Navigator.pop(context, ImageSource.camera);
26 | },
27 | child: ListTile(
28 | leading: const Icon(Icons.camera_alt),
29 | title: Text(S.of(context)?.camera??'Camera'),
30 | ),
31 | ),
32 | SimpleDialogOption(
33 | onPressed: () {
34 | FocusScope.of(context).unfocus();
35 | Navigator.pop(context, ImageSource.gallery);
36 | },
37 | child: ListTile(
38 | leading: const Icon(Icons.image),
39 | title: Text(S.of(context)?.gallery??'Gallery'),
40 | ),
41 | ),
42 | ],
43 | );
44 | });
45 |
46 | if (source == null) return null;
47 |
48 | final pickedFile = await ImagePicker().pickImage(source: source);
49 | if (pickedFile == null) return null;
50 |
51 | File image = File((pickedFile).path);
52 | return fixExifRotation(image);
53 | }
54 |
55 | Future picImageMultiFile(
56 | BuildContext context, String name) async {
57 | var image = await picImage(context);
58 | if (image == null) return null;
59 | return await multiFile(image, name);
60 | }
61 |
62 | Future fixExifRotation(File image, {deleteOriginal = false}) async {
63 | List imageBytes = await image.readAsBytes();
64 |
65 | List result = await FlutterImageCompress.compressWithList(
66 | Uint8List.fromList(imageBytes),
67 | quality: 100,
68 | rotate: 0,
69 | );
70 |
71 | final String processedImageUuid = const Uuid().v4();
72 | String imageExtension = p.basename(image.path);
73 |
74 | final String tempPath = (await getTemporaryDirectory()).path;
75 |
76 | File fixedImage = File('$tempPath/$processedImageUuid$imageExtension');
77 |
78 | await fixedImage.writeAsBytes(result);
79 |
80 | if (deleteOriginal) await image.delete();
81 |
82 | return fixedImage;
83 | }
84 |
--------------------------------------------------------------------------------
/lib/src/log.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/foundation.dart';
2 | import 'package:stack_trace/stack_trace.dart';
3 |
4 | void d(Object object) {
5 | final output = "${Trace.current().frames[1].location} | $object";
6 |
7 | // the console prints the first 1000+ char and discard the rest so this work around.
8 | final pattern = RegExp('.{1,1000}'); // 1000 is the size of each chunk
9 | pattern.allMatches(output).forEach((match) => debugPrint(match.group(0)));
10 | }
11 |
12 | // Use this instead of d() for long string when you want to see output in the logcat when filtering by location.
13 | // d() will not print the location for the first 1000+ char.
14 | // Short for debug long.
15 | void dl(Object object) {
16 | var location = Trace.current().frames[1].location;
17 |
18 | // the console prints the first 1000+ char and discard the rest so this work around.
19 | final pattern = RegExp('.{1,1000}'); // 1000 is the size of each chunk
20 | pattern
21 | .allMatches(object.toString())
22 | .forEach((match) => debugPrint("$location | ${match.group(0)}"));
23 | }
24 |
25 | void d2(Object object) {
26 | final output = "${Trace.current().frames[2].location} | $object";
27 |
28 | // the console prints the first 1000+ char and discard the rest so this work around.
29 | final pattern = RegExp('.{1,1000}'); // 1000 is the size of each chunk
30 | pattern.allMatches(output).forEach((match) => debugPrint(match.group(0)));
31 | }
32 |
33 | void dAll(Object object) {
34 | final output = "${Trace.current()} | $object";
35 |
36 | // the console prints the first 1000+ char and discard the rest so this work around.
37 | final pattern = RegExp('.{1,1000}'); // 1000 is the size of each chunk
38 | pattern.allMatches(output).forEach((match) => debugPrint(match.group(0)));
39 | }
40 |
--------------------------------------------------------------------------------
/lib/src/model/error/error_response.dart:
--------------------------------------------------------------------------------
1 | library errorresponse;
2 |
3 | import 'package:json_annotation/json_annotation.dart';
4 |
5 | part 'error_response.g.dart';
6 |
7 | @JsonSerializable()
8 | class ErrorResponse {
9 | String? error;
10 |
11 | factory ErrorResponse.fromJson(Map json) =>
12 | _$ErrorResponseFromJson(json);
13 |
14 | Map toJson() => _$ErrorResponseToJson(this);
15 |
16 | static const fromJsonFactory = _$ErrorResponseFromJson;
17 |
18 | ErrorResponse();
19 |
20 | @override
21 | String toString() {
22 | return 'ErrorResponse{error: $error}';
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/src/model/error/error_response.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of errorresponse;
4 |
5 | // **************************************************************************
6 | // JsonSerializableGenerator
7 | // **************************************************************************
8 |
9 | ErrorResponse _$ErrorResponseFromJson(Map json) {
10 | return ErrorResponse()..error = json['error']?.toString();
11 | }
12 |
13 | Map _$ErrorResponseToJson(ErrorResponse instance) =>
14 | {'error': instance.error};
15 |
--------------------------------------------------------------------------------
/lib/src/navigation.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_toolbox/flutter_toolbox.dart';
3 |
4 | Future push(
5 | BuildContext context,
6 | Widget widget, {
7 | bool setName = true,
8 | bool authCheck = true,
9 | }) =>
10 | _safeNav(
11 | context,
12 | widget,
13 | () => Navigator.push(
14 | context,
15 | materialRoute(widget, setName: setName),
16 | ),
17 | authCheck: authCheck,
18 | );
19 |
20 | Future pushReplacement(
21 | BuildContext context,
22 | Widget widget, {
23 | bool authCheck = true,
24 | }) =>
25 | _safeNav(
26 | context,
27 | widget,
28 | () => Navigator.pushReplacement(context, materialRoute(widget)),
29 | authCheck: authCheck,
30 | );
31 |
32 | /// you can pass null to [untilPage] and it will clear the stack
33 | Future pushAndRemoveUntil(
34 | BuildContext context,
35 | Widget widget,
36 | Widget? untilPage, {
37 | bool authCheck = true,
38 | }) {
39 | return _safeNav(
40 | context,
41 | widget,
42 | () {
43 | return Navigator.pushAndRemoveUntil(
44 | context,
45 | materialRoute(widget),
46 | (Route route) =>
47 | route.settings.name == untilPage.runtimeType.toString(),
48 | );
49 | },
50 | authCheck: authCheck,
51 | );
52 | }
53 |
54 | MaterialPageRoute materialRoute(Widget widget, {bool setName = true}) =>
55 | MaterialPageRoute(
56 | builder: (context) => widget,
57 | settings:
58 | setName ? RouteSettings(name: widget.runtimeType.toString()) : null,
59 | );
60 |
61 | // only navigate if the page in [isUnAuthenticatedPage] or the user [isAuthenticated]
62 | // also navigate if [isUnAuthenticatedPage] or [isAuthenticated] is unset
63 | Future _safeNav(
64 | BuildContext context,
65 | Widget widget,
66 | Function() onCanNavigate, {
67 | bool authCheck = true,
68 | }) {
69 | final config = ToolboxConfig.of(context, listen: false);
70 |
71 | final isUnAuthenticatedPage =
72 | config.unAuthenticatedPages?.contains(widget.runtimeType) ?? true;
73 |
74 | if (isUnAuthenticatedPage ||
75 | config.isAuthenticated!() != false ||
76 | authCheck == false) {
77 | return onCanNavigate();
78 | } else {
79 | return config.onAuthorizedNavigation!(context, widget.runtimeType);
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/lib/src/prefs.dart:
--------------------------------------------------------------------------------
1 | import 'package:shared_preferences/shared_preferences.dart';
2 |
3 | Future prefs() async =>
4 | await SharedPreferences.getInstance();
5 |
--------------------------------------------------------------------------------
/lib/src/time.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:intl/intl.dart';
3 |
4 | String serverTimeFormat(TimeOfDay time) {
5 | var hour = time.hour < 10 ? '0${time.hour}' : time.hour;
6 | var minute = time.minute < 10 ? '0${time.minute}' : time.minute;
7 |
8 | return '$hour:$minute';
9 | }
10 |
11 | final serverFormatter = DateFormat('yyyy-MM-dd', 'en');
12 |
13 | String serverDateFormat(DateTime date) {
14 | return serverFormatter.format(date);
15 | }
16 |
17 | bool isInRange(int time, int startTime, int endTime) =>
18 | time >= startTime && time <= endTime;
19 |
20 | extension TimeStringExtentions on String {
21 | DateTime toDateTimeFromServerFormat() => serverFormatter.parse(this);
22 | }
23 |
24 | extension TimeOfDayExtentions on TimeOfDay {
25 | int toInt() => minute + (hour * 60);
26 | }
27 |
28 | extension DateTimeExtentions on DateTime {
29 | TimeOfDay toTimeOfDay() => TimeOfDay.fromDateTime(this);
30 | }
31 |
--------------------------------------------------------------------------------
/lib/src/ui/connection_status_bar.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 |
4 | import 'package:connectivity_plus/connectivity_plus.dart';
5 | import 'package:flutter/foundation.dart';
6 | import 'package:flutter/material.dart';
7 | import 'package:flutter/scheduler.dart';
8 | import 'package:flutter_toolbox/generated/l10n.dart';
9 |
10 | class ConnectionStatusBar extends StatefulWidget {
11 | final Widget? title;
12 | final Color? color;
13 |
14 | const ConnectionStatusBar({this.title, this.color, super.key});
15 |
16 | @override
17 | _ConnectionStatusBarState createState() => _ConnectionStatusBarState();
18 |
19 | static void init(BuildContext context) async {
20 | if (kIsWeb) return;
21 |
22 | final overlayEntry = OverlayEntry(
23 | builder: (BuildContext context) => IgnorePointer(
24 | child: Stack(
25 | children: [
26 | Positioned(
27 | top: 0,
28 | child: Material(
29 | color: Colors.transparent,
30 | child: Container(
31 | alignment: Alignment.center,
32 | width: MediaQuery.of(context).size.width,
33 | child: const ConnectionStatusBar(),
34 | ),
35 | ),
36 | ),
37 | ],
38 | ),
39 | ),
40 | );
41 | SchedulerBinding.instance.addPostFrameCallback((_) {
42 | Overlay.of(context).insert(overlayEntry);
43 | });
44 | }
45 | }
46 |
47 | class _ConnectionStatusBarState extends State
48 | with SingleTickerProviderStateMixin {
49 | late StreamSubscription _connectionChangeStream;
50 | bool _hasConnection = true;
51 | late AnimationController controller;
52 | late Animation offset;
53 |
54 | @override
55 | void initState() {
56 | _ConnectionStatusSingleton connectionStatus =
57 | _ConnectionStatusSingleton.getInstance();
58 | connectionStatus.initialize();
59 | _connectionChangeStream =
60 | connectionStatus.connectionChange.listen(_connectionChanged);
61 | controller =
62 | AnimationController(vsync: this, duration: const Duration(milliseconds: 200));
63 |
64 | offset = Tween(begin: const Offset(0.0, -1.0), end: const Offset(0.0, 0.0))
65 | .animate(controller);
66 | super.initState();
67 | }
68 |
69 | void _connectionChanged(bool hasConnection) {
70 | if (_hasConnection == hasConnection) return;
71 | hasConnection == false ? controller.forward() : controller.reverse();
72 | _hasConnection = hasConnection;
73 | }
74 |
75 | @override
76 | Widget build(BuildContext context) {
77 | return SlideTransition(
78 | position: offset,
79 | child: SafeArea(
80 | bottom: false,
81 | child: Container(
82 | color: widget.color ?? Colors.redAccent,
83 | width: double.maxFinite,
84 | height: 25,
85 | child: Center(
86 | child: widget.title ?? Text(
87 | S.of(context)?.please_check_your_internet_connection ??
88 | 'Please check your internet connection',
89 | style: const TextStyle(color: Colors.white, fontSize: 14),
90 | ),
91 | ),
92 | ),
93 | ),
94 | );
95 | }
96 |
97 | @override
98 | void dispose() {
99 | _connectionChangeStream.cancel();
100 |
101 | super.dispose();
102 | }
103 | }
104 |
105 | class _ConnectionStatusSingleton {
106 | static final _ConnectionStatusSingleton _singleton =
107 | _ConnectionStatusSingleton._internal();
108 |
109 | _ConnectionStatusSingleton._internal();
110 |
111 | static _ConnectionStatusSingleton getInstance() => _singleton;
112 |
113 | bool hasConnection = true;
114 |
115 | StreamController connectionChangeController =
116 | StreamController.broadcast();
117 |
118 | final Connectivity _connectivity = Connectivity();
119 |
120 | void initialize() {
121 | _connectivity.onConnectivityChanged.listen(_connectionChange);
122 | checkConnection();
123 | }
124 |
125 | Stream get connectionChange => connectionChangeController.stream;
126 |
127 | void dispose() {
128 | connectionChangeController.close();
129 | }
130 |
131 | void _connectionChange(List result) {
132 | checkConnection();
133 | }
134 |
135 | Future checkConnection() async {
136 | bool previousConnection = hasConnection;
137 |
138 | try {
139 | final result = await InternetAddress.lookup('google.com');
140 | if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
141 | hasConnection = true;
142 | } else {
143 | hasConnection = false;
144 | }
145 | } on SocketException catch (_) {
146 | hasConnection = false;
147 | }
148 |
149 | if (previousConnection != hasConnection) {
150 | connectionChangeController.add(hasConnection);
151 | }
152 |
153 | return hasConnection;
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/lib/src/ui/date_picker_field.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_toolbox/generated/l10n.dart';
5 | import 'package:intl/intl.dart';
6 |
7 | class DatePickerField extends StatelessWidget {
8 | const DatePickerField({
9 | super.key,
10 | this.labelText,
11 | this.selectedDate,
12 | this.onDateSelected,
13 | this.firstDate,
14 | this.lastDate,
15 | });
16 |
17 | final String? labelText;
18 | final DateTime? selectedDate;
19 | final ValueChanged? onDateSelected;
20 | final DateTime? firstDate;
21 | final DateTime? lastDate;
22 |
23 | Future _selectDate(BuildContext context) async {
24 | final DateTime? picked = await showDatePicker(
25 | context: context,
26 | initialDate: selectedDate ?? DateTime.now(),
27 | firstDate: firstDate ?? DateTime(2015, 8),
28 | lastDate: lastDate ?? DateTime(2101),
29 | );
30 | if (picked != null && picked != selectedDate) onDateSelected!(picked);
31 | }
32 |
33 | @override
34 | Widget build(BuildContext context) {
35 | final TextStyle? valueStyle = Theme.of(context).textTheme.titleLarge;
36 | return _InputDropdown(
37 | labelText: labelText,
38 | valueText: selectedDate == null
39 | ? S.of(context)?.select_date ?? 'Select date'
40 | : DateFormat.yMMMd().format(selectedDate!),
41 | valueStyle: valueStyle,
42 | onPressed: () => _selectDate(context),
43 | );
44 | }
45 | }
46 |
47 | class _InputDropdown extends StatelessWidget {
48 | const _InputDropdown({
49 | super.key,
50 | this.child,
51 | this.labelText,
52 | this.valueText,
53 | this.valueStyle,
54 | this.onPressed,
55 | });
56 |
57 | final String? labelText;
58 | final String? valueText;
59 | final TextStyle? valueStyle;
60 | final VoidCallback? onPressed;
61 | final Widget? child;
62 |
63 | @override
64 | Widget build(BuildContext context) {
65 | return InkWell(
66 | onTap: onPressed,
67 | child: InputDecorator(
68 | decoration: InputDecoration(labelText: labelText),
69 | baseStyle: valueStyle,
70 | child: Row(
71 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
72 | mainAxisSize: MainAxisSize.min,
73 | children: [
74 | Text(valueText!, style: valueStyle),
75 | Icon(
76 | Icons.arrow_drop_down,
77 | color: Theme.of(context).brightness == Brightness.light
78 | ? Colors.grey.shade700
79 | : Colors.white70,
80 | ),
81 | ],
82 | ),
83 | ),
84 | );
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/lib/src/ui/ink_well_stacked.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class InkWellStacked extends StatelessWidget {
4 | const InkWellStacked({
5 | super.key,
6 | this.child,
7 | this.onTap,
8 | this.onTapDown,
9 | this.onTapCancel,
10 | this.onDoubleTap,
11 | this.onLongPress,
12 | this.onHighlightChanged,
13 | this.onHover,
14 | this.containedInkWell = false,
15 | this.highlightShape = BoxShape.circle,
16 | this.radius,
17 | this.borderRadius,
18 | this.customBorder,
19 | this.focusColor,
20 | this.hoverColor,
21 | this.highlightColor,
22 | this.splashColor,
23 | this.splashFactory,
24 | this.enableFeedback = true,
25 | this.excludeFromSemantics = false,
26 | this.focusNode,
27 | this.canRequestFocus = true,
28 | this.onFocusChange,
29 | this.autofocus = false,
30 | });
31 |
32 | /// The widget below this widget in the tree.
33 | ///
34 | /// {@macro flutter.widgets.child}
35 | final Widget? child;
36 |
37 | /// Called when the user taps this part of the material.
38 | final GestureTapCallback? onTap;
39 |
40 | /// Called when the user taps down this part of the material.
41 | final GestureTapDownCallback? onTapDown;
42 |
43 | /// Called when the user cancels a tap that was started on this part of the
44 | /// material.
45 | final GestureTapCallback? onTapCancel;
46 |
47 | /// Called when the user double taps this part of the material.
48 | final GestureTapCallback? onDoubleTap;
49 |
50 | /// Called when the user long-presses on this part of the material.
51 | final GestureLongPressCallback? onLongPress;
52 |
53 | /// Called when this part of the material either becomes highlighted or stops
54 | /// being highlighted.
55 | ///
56 | /// The value passed to the callback is true if this part of the material has
57 | /// become highlighted and false if this part of the material has stopped
58 | /// being highlighted.
59 | ///
60 | /// If all of [onTap], [onDoubleTap], and [onLongPress] become null while a
61 | /// gesture is ongoing, then [onTapCancel] will be fired and
62 | /// [onHighlightChanged] will be fired with the value false _during the
63 | /// build_. This means, for instance, that in that scenario [State.setState]
64 | /// cannot be called.
65 | final ValueChanged? onHighlightChanged;
66 |
67 | /// Called when a pointer enters or exits the ink response area.
68 | ///
69 | /// The value passed to the callback is true if a pointer has entered this
70 | /// part of the material and false if a pointer has exited this part of the
71 | /// material.
72 | final ValueChanged? onHover;
73 |
74 | /// Whether this ink response should be clipped its bounds.
75 | ///
76 | /// This flag also controls whether the splash migrates to the center of the
77 | /// [InkResponse] or not. If [containedInkWell] is true, the splash remains
78 | /// centered around the tap location. If it is false, the splash migrates to
79 | /// the center of the [InkResponse] as it grows.
80 | ///
81 | /// See also:
82 | ///
83 | /// * [highlightShape], the shape of the focus, hover, and pressed
84 | /// highlights.
85 | /// * [borderRadius], which controls the corners when the box is a rectangle.
86 | /// * [getRectCallback], which controls the size and position of the box when
87 | /// it is a rectangle.
88 | final bool containedInkWell;
89 |
90 | /// The shape (e.g., circle, rectangle) to use for the highlight drawn around
91 | /// this part of the material when pressed, hovered over, or focused.
92 | ///
93 | /// The same shape is used for the pressed highlight (see [highlightColor]),
94 | /// the focus highlight (see [focusColor]), and the hover highlight (see
95 | /// [hoverColor]).
96 | ///
97 | /// If the shape is [BoxShape.circle], then the highlight is centered on the
98 | /// [InkResponse]. If the shape is [BoxShape.rectangle], then the highlight
99 | /// fills the [InkResponse], or the rectangle provided by [getRectCallback] if
100 | /// the callback is specified.
101 | ///
102 | /// See also:
103 | ///
104 | /// * [containedInkWell], which controls clipping behavior.
105 | /// * [borderRadius], which controls the corners when the box is a rectangle.
106 | /// * [highlightColor], the color of the highlight.
107 | /// * [getRectCallback], which controls the size and position of the box when
108 | /// it is a rectangle.
109 | final BoxShape highlightShape;
110 |
111 | /// The radius of the ink splash.
112 | ///
113 | /// Splashes grow up to this size. By default, this size is determined from
114 | /// the size of the rectangle provided by [getRectCallback], or the size of
115 | /// the [InkResponse] itself.
116 | ///
117 | /// See also:
118 | ///
119 | /// * [splashColor], the color of the splash.
120 | /// * [splashFactory], which defines the appearance of the splash.
121 | final double? radius;
122 |
123 | /// The clipping radius of the containing rect. This is effective only if
124 | /// [customBorder] is null.
125 | ///
126 | /// If this is null, it is interpreted as [BorderRadius.zero].
127 | final BorderRadius? borderRadius;
128 |
129 | /// The custom clip border which overrides [borderRadius].
130 | final ShapeBorder? customBorder;
131 |
132 | /// The color of the ink response when the parent widget is focused. If this
133 | /// property is null then the focus color of the theme,
134 | /// [ThemeData.focusColor], will be used.
135 | ///
136 | /// See also:
137 | ///
138 | /// * [highlightShape], the shape of the focus, hover, and pressed
139 | /// highlights.
140 | /// * [hoverColor], the color of the hover highlight.
141 | /// * [splashColor], the color of the splash.
142 | /// * [splashFactory], which defines the appearance of the splash.
143 | final Color? focusColor;
144 |
145 | /// The color of the ink response when a pointer is hovering over it. If this
146 | /// property is null then the hover color of the theme,
147 | /// [ThemeData.hoverColor], will be used.
148 | ///
149 | /// See also:
150 | ///
151 | /// * [highlightShape], the shape of the focus, hover, and pressed
152 | /// highlights.
153 | /// * [highlightColor], the color of the pressed highlight.
154 | /// * [focusColor], the color of the focus highlight.
155 | /// * [splashColor], the color of the splash.
156 | /// * [splashFactory], which defines the appearance of the splash.
157 | final Color? hoverColor;
158 |
159 | /// The highlight color of the ink response when pressed. If this property is
160 | /// null then the highlight color of the theme, [ThemeData.highlightColor],
161 | /// will be used.
162 | ///
163 | /// See also:
164 | ///
165 | /// * [hoverColor], the color of the hover highlight.
166 | /// * [focusColor], the color of the focus highlight.
167 | /// * [highlightShape], the shape of the focus, hover, and pressed
168 | /// highlights.
169 | /// * [splashColor], the color of the splash.
170 | /// * [splashFactory], which defines the appearance of the splash.
171 | final Color? highlightColor;
172 |
173 | /// The splash color of the ink response. If this property is null then the
174 | /// splash color of the theme, [ThemeData.splashColor], will be used.
175 | ///
176 | /// See also:
177 | ///
178 | /// * [splashFactory], which defines the appearance of the splash.
179 | /// * [radius], the (maximum) size of the ink splash.
180 | /// * [highlightColor], the color of the highlight.
181 | final Color? splashColor;
182 |
183 | /// Defines the appearance of the splash.
184 | ///
185 | /// Defaults to the value of the theme's splash factory: [ThemeData.splashFactory].
186 | ///
187 | /// See also:
188 | ///
189 | /// * [radius], the (maximum) size of the ink splash.
190 | /// * [splashColor], the color of the splash.
191 | /// * [highlightColor], the color of the highlight.
192 | /// * [InkSplash.splashFactory], which defines the default splash.
193 | /// * [InkRipple.splashFactory], which defines a splash that spreads out
194 | /// more aggressively than the default.
195 | final InteractiveInkFeatureFactory? splashFactory;
196 |
197 | /// Whether detected gestures should provide acoustic and/or haptic feedback.
198 | ///
199 | /// For example, on Android a tap will produce a clicking sound and a
200 | /// long-press will produce a short vibration, when feedback is enabled.
201 | ///
202 | /// See also:
203 | ///
204 | /// * [Feedback] for providing platform-specific feedback to certain actions.
205 | final bool enableFeedback;
206 |
207 | /// Whether to exclude the gestures introduced by this widget from the
208 | /// semantics tree.
209 | ///
210 | /// For example, a long-press gesture for showing a tooltip is usually
211 | /// excluded because the tooltip itself is included in the semantics
212 | /// tree directly and so having a gesture to show it would result in
213 | /// duplication of information.
214 | final bool excludeFromSemantics;
215 |
216 | /// Handler called when the focus changes.
217 | ///
218 | /// Called with true if this widget's node gains focus, and false if it loses
219 | /// focus.
220 | final ValueChanged? onFocusChange;
221 |
222 | /// {@macro flutter.widgets.Focus.autofocus}
223 | final bool autofocus;
224 |
225 | /// {@macro flutter.widgets.Focus.focusNode}
226 | final FocusNode? focusNode;
227 |
228 | /// {@template flutter.widgets.Focus.canRequestFocus}
229 | final bool canRequestFocus;
230 |
231 | @override
232 | Widget build(BuildContext context) {
233 | return Stack(
234 | children: [
235 | child!,
236 | Positioned.fill(
237 | child: Material(
238 | type: MaterialType.transparency,
239 | child: InkWell(
240 | key: key,
241 | onTap: onTap,
242 | onDoubleTap: onDoubleTap,
243 | onLongPress: onLongPress,
244 | onTapDown: onTapDown,
245 | onTapCancel: onTapCancel,
246 | onHighlightChanged: onHighlightChanged,
247 | onHover: onHover,
248 | focusColor: focusColor,
249 | hoverColor: hoverColor,
250 | highlightColor: highlightColor,
251 | splashColor: splashColor,
252 | splashFactory: splashFactory,
253 | radius: radius,
254 | borderRadius: borderRadius,
255 | customBorder: customBorder,
256 | enableFeedback: enableFeedback,
257 | excludeFromSemantics: excludeFromSemantics,
258 | focusNode: focusNode,
259 | canRequestFocus: canRequestFocus,
260 | onFocusChange: onFocusChange,
261 | autofocus: autofocus,
262 | ),
263 | ),
264 | ),
265 | ],
266 | );
267 | }
268 | }
269 |
--------------------------------------------------------------------------------
/lib/src/ui/listview/common.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_toolbox/src/config/toolbox_config.dart';
3 | import 'package:flutter_toolbox/src/ui/listview/pagewise/flutter_pagewise.dart';
4 |
5 | NoItemsFoundBuilder noItemsFoundBuilder(
6 | BuildContext context, {
7 | required NoItemsFoundBuilder? noItemsFoundBuilder,
8 | required Widget? noItemsFoundWidget,
9 | }) {
10 | final defaultNoItemsFoundBuilder = noItemsFoundWidget == null
11 | ? null
12 | : (BuildContext context) {
13 | final height = MediaQuery.of(context).size.height - kToolbarHeight;
14 | return SizedBox(
15 | height: height,
16 | child: Center(
17 | child: noItemsFoundWidget,
18 | ),
19 | );
20 | };
21 |
22 | final localNoItemFound = noItemsFoundBuilder ?? defaultNoItemsFoundBuilder;
23 |
24 | final config = ToolboxConfig.of(context);
25 | final globalNoItemFound = config.noItemsFoundBuilder ??
26 | _buildNoItemFoundBuilder(config.noItemsFoundWidget);
27 |
28 | return localNoItemFound ?? globalNoItemFound;
29 | }
30 |
31 | NoItemsFoundBuilder _buildNoItemFoundBuilder(Widget? noItemsFoundWidget) {
32 | return (BuildContext context) {
33 | // remove the height of the appBar and some to make sure
34 | // the noItemsFoundWidget don't scroll
35 | final height = MediaQuery.of(context).size.height - kToolbarHeight * 3;
36 | return SizedBox(
37 | height: height,
38 | child: Center(
39 | child: noItemsFoundWidget,
40 | ),
41 | );
42 | };
43 | }
44 |
--------------------------------------------------------------------------------
/lib/src/ui/listview/pagewise/helpers/grid_helpers.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/rendering.dart';
2 |
3 | class SliverGridDelegateWithFixedCrossAxisCountAndLoading
4 | extends SliverGridDelegateWithFixedCrossAxisCount {
5 | final int itemCount;
6 |
7 | const SliverGridDelegateWithFixedCrossAxisCountAndLoading({
8 | required crossAxisCount,
9 | required this.itemCount,
10 | mainAxisSpacing = 0.0,
11 | crossAxisSpacing = 0.0,
12 | childAspectRatio = 1.0,
13 | }) : super(
14 | crossAxisCount: crossAxisCount,
15 | mainAxisSpacing: mainAxisSpacing,
16 | crossAxisSpacing: crossAxisSpacing,
17 | childAspectRatio: childAspectRatio);
18 |
19 | @override
20 | SliverGridLayout getLayout(SliverConstraints constraints) {
21 | final double usableCrossAxisExtent =
22 | constraints.crossAxisExtent - crossAxisSpacing * (crossAxisCount - 1);
23 | final double childCrossAxisExtent = usableCrossAxisExtent / crossAxisCount;
24 | final double childMainAxisExtent = childCrossAxisExtent / childAspectRatio;
25 | return SliverGridRegularTileLayoutAndLoading(
26 | crossAxisCount: crossAxisCount,
27 | mainAxisStride: childMainAxisExtent + mainAxisSpacing,
28 | crossAxisStride: childCrossAxisExtent + crossAxisSpacing,
29 | childMainAxisExtent: childMainAxisExtent,
30 | childCrossAxisExtent: childCrossAxisExtent,
31 | reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection),
32 | fullCrossAccessExtent: usableCrossAxisExtent,
33 | itemCount: itemCount,
34 | );
35 | }
36 | }
37 |
38 | class SliverGridDelegateWithMaxCrossAxisExtentAndLoading
39 | extends SliverGridDelegateWithMaxCrossAxisExtent {
40 | final int itemCount;
41 |
42 | const SliverGridDelegateWithMaxCrossAxisExtentAndLoading({
43 | required maxCrossAxisExtent,
44 | required this.itemCount,
45 | mainAxisSpacing = 0.0,
46 | crossAxisSpacing = 0.0,
47 | childAspectRatio = 1.0,
48 | }) : super(
49 | maxCrossAxisExtent: maxCrossAxisExtent,
50 | mainAxisSpacing: mainAxisSpacing,
51 | crossAxisSpacing: crossAxisSpacing,
52 | childAspectRatio: childAspectRatio);
53 |
54 | @override
55 | SliverGridLayout getLayout(SliverConstraints constraints) {
56 | final int crossAxisCount =
57 | (constraints.crossAxisExtent / (maxCrossAxisExtent + crossAxisSpacing))
58 | .ceil();
59 | final double usableCrossAxisExtent =
60 | constraints.crossAxisExtent - crossAxisSpacing * (crossAxisCount - 1);
61 | final double childCrossAxisExtent = usableCrossAxisExtent / crossAxisCount;
62 | final double childMainAxisExtent = childCrossAxisExtent / childAspectRatio;
63 | return SliverGridRegularTileLayoutAndLoading(
64 | itemCount: itemCount,
65 | fullCrossAccessExtent: usableCrossAxisExtent,
66 | crossAxisCount: crossAxisCount,
67 | mainAxisStride: childMainAxisExtent + mainAxisSpacing,
68 | crossAxisStride: childCrossAxisExtent + crossAxisSpacing,
69 | childMainAxisExtent: childMainAxisExtent,
70 | childCrossAxisExtent: childCrossAxisExtent,
71 | reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection),
72 | );
73 | }
74 | }
75 |
76 | class SliverGridRegularTileLayoutAndLoading
77 | extends SliverGridRegularTileLayout {
78 | final int itemCount;
79 | final double fullCrossAccessExtent;
80 |
81 | const SliverGridRegularTileLayoutAndLoading(
82 | {required crossAxisCount,
83 | required mainAxisStride,
84 | required crossAxisStride,
85 | required childMainAxisExtent,
86 | required childCrossAxisExtent,
87 | required reverseCrossAxis,
88 | required this.fullCrossAccessExtent,
89 | required this.itemCount})
90 | : super(
91 | crossAxisCount: crossAxisCount,
92 | mainAxisStride: mainAxisStride,
93 | crossAxisStride: crossAxisStride,
94 | childMainAxisExtent: childMainAxisExtent,
95 | childCrossAxisExtent: childCrossAxisExtent,
96 | reverseCrossAxis: reverseCrossAxis);
97 |
98 | @override
99 | SliverGridGeometry getGeometryForChildIndex(int index) {
100 | if (index == itemCount - 1) {
101 | return SliverGridGeometry(
102 | scrollOffset: (index ~/ crossAxisCount) * mainAxisStride,
103 | crossAxisOffset: 0.0,
104 | mainAxisExtent: childMainAxisExtent,
105 | crossAxisExtent: fullCrossAccessExtent);
106 | }
107 |
108 | return super.getGeometryForChildIndex(index);
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/lib/src/ui/listview/paginated_grid_view_count.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import 'common.dart';
4 | import 'pagewise/flutter_pagewise.dart';
5 |
6 | class PaginatedGridViewCount extends StatefulWidget {
7 | final ItemBuilder itemBuilder;
8 | final PageFuture pageFuture;
9 |
10 | /// The number of children in the cross axis.
11 | final int crossAxisCount;
12 |
13 | final double childAspectRatio;
14 |
15 | final double crossAxisSpacing;
16 |
17 | final double mainAxisSpacing;
18 |
19 | final int pageSize;
20 | final EdgeInsetsGeometry? padding;
21 | final NoItemsFoundBuilder? noItemsFoundBuilder;
22 | final Widget? noItemsFoundWidget;
23 | final PagewiseLoadController? pageLoadController;
24 |
25 | /// default is false
26 | ///
27 | /// set to true if the future will change.
28 | final bool mutable;
29 |
30 | /// default is false
31 | ///
32 | /// set to true if the future will change.
33 | final bool showRefreshIndicator;
34 |
35 | final Axis scrollDirection;
36 | final bool shrinkWrap;
37 | final ScrollPhysics? physics;
38 |
39 | const PaginatedGridViewCount({
40 | super.key,
41 | required this.itemBuilder,
42 | required this.pageFuture,
43 | required this.crossAxisCount,
44 | this.childAspectRatio = 1.0,
45 | this.crossAxisSpacing = 0.0,
46 | this.mainAxisSpacing = 0.0,
47 | this.pageSize = 10,
48 | this.padding,
49 | this.noItemsFoundBuilder,
50 | this.noItemsFoundWidget,
51 | this.pageLoadController,
52 | this.mutable = false,
53 | this.showRefreshIndicator = false,
54 | this.scrollDirection = Axis.vertical,
55 | this.shrinkWrap = false,
56 | this.physics,
57 | });
58 |
59 | @override
60 | _PaginatedGridViewCountState createState() =>
61 | _PaginatedGridViewCountState();
62 | }
63 |
64 | class _PaginatedGridViewCountState extends State> {
65 | bool _reload = false;
66 |
67 | @override
68 | Widget build(BuildContext context) {
69 | return widget.showRefreshIndicator
70 | ? RefreshIndicator(
71 | onRefresh: () async {
72 | await widget.pageFuture(1);
73 | setState(() => _reload = true);
74 | },
75 | child: buildGridView(),
76 | )
77 | : buildGridView();
78 | }
79 |
80 | Widget buildGridView() {
81 | final mutable = widget.mutable ||
82 | // widget.showRefreshIndicator || removed as there it serve no purpose leave here just in case there is a side effect
83 | _reload ||
84 | widget.pageLoadController != null;
85 |
86 | _reload = false;
87 |
88 | pageFuture(int? pageIndex) => widget.pageFuture(pageIndex! + 1);
89 |
90 | final pageLoadController = widget.pageLoadController ??
91 | PagewiseLoadController(
92 | pageSize: widget.pageSize,
93 | pageFuture: pageFuture,
94 | );
95 |
96 | return PagewiseGridView.count(
97 | crossAxisCount: widget.crossAxisCount,
98 | childAspectRatio: widget.childAspectRatio,
99 | crossAxisSpacing: widget.crossAxisSpacing,
100 | mainAxisSpacing: widget.mainAxisSpacing,
101 | itemBuilder: widget.itemBuilder,
102 | padding: widget.padding,
103 | noItemsFoundBuilder: noItemsFoundBuilder(
104 | context,
105 | noItemsFoundBuilder: widget.noItemsFoundBuilder,
106 | noItemsFoundWidget: widget.noItemsFoundWidget,
107 | ),
108 | pageLoadController: mutable ? pageLoadController : null,
109 | pageSize: mutable ? null : widget.pageSize,
110 | pageFuture: mutable ? null : pageFuture,
111 | scrollDirection: widget.scrollDirection,
112 | shrinkWrap: widget.shrinkWrap,
113 | physics: widget.physics,
114 | );
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/lib/src/ui/listview/paginated_grid_view_extent.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import 'common.dart';
4 | import 'pagewise/flutter_pagewise.dart';
5 |
6 | class PaginatedGridViewExtent extends StatefulWidget {
7 | final ItemBuilder itemBuilder;
8 | final PageFuture pageFuture;
9 |
10 | /// The number of children in the cross axis.
11 | final double maxCrossAxisExtent;
12 |
13 | final double childAspectRatio;
14 |
15 | final double crossAxisSpacing;
16 |
17 | final double mainAxisSpacing;
18 |
19 | final int pageSize;
20 | final EdgeInsetsGeometry? padding;
21 | final NoItemsFoundBuilder? noItemsFoundBuilder;
22 | final Widget? noItemsFoundWidget;
23 | final PagewiseLoadController? pageLoadController;
24 |
25 | /// default is false
26 | ///
27 | /// set to true if the future will change.
28 | final bool mutable;
29 |
30 | /// default is false
31 | ///
32 | /// set to true if the future will change.
33 | final bool showRefreshIndicator;
34 |
35 | final Axis scrollDirection;
36 | final bool shrinkWrap;
37 | final ScrollPhysics? physics;
38 |
39 | const PaginatedGridViewExtent({
40 | super.key,
41 | required this.itemBuilder,
42 | required this.pageFuture,
43 | required this.maxCrossAxisExtent,
44 | this.childAspectRatio = 1.0,
45 | this.crossAxisSpacing = 0.0,
46 | this.mainAxisSpacing = 0.0,
47 | this.pageSize = 10,
48 | this.padding,
49 | this.noItemsFoundBuilder,
50 | this.noItemsFoundWidget,
51 | this.pageLoadController,
52 | this.mutable = false,
53 | this.showRefreshIndicator = false,
54 | this.scrollDirection = Axis.vertical,
55 | this.shrinkWrap = false,
56 | this.physics,
57 | });
58 |
59 | @override
60 | _PaginatedGridViewExtentState createState() =>
61 | _PaginatedGridViewExtentState();
62 | }
63 |
64 | class _PaginatedGridViewExtentState
65 | extends State> {
66 | bool _reload = false;
67 |
68 | @override
69 | Widget build(BuildContext context) {
70 | return widget.showRefreshIndicator
71 | ? RefreshIndicator(
72 | onRefresh: () async {
73 | await widget.pageFuture(1);
74 | setState(() => _reload = true);
75 | },
76 | child: buildGridView(),
77 | )
78 | : buildGridView();
79 | }
80 |
81 | Widget buildGridView() {
82 | final mutable = widget.mutable ||
83 | // widget.showRefreshIndicator || removed as there it serve no purpose leave here just in case there is a side effect
84 | _reload ||
85 | widget.pageLoadController != null;
86 |
87 | _reload = false;
88 |
89 | pageFuture(int? pageIndex) => widget.pageFuture(pageIndex! + 1);
90 |
91 | final pageLoadController = widget.pageLoadController ??
92 | PagewiseLoadController(
93 | pageSize: widget.pageSize,
94 | pageFuture: pageFuture,
95 | );
96 |
97 | return PagewiseGridView.extent(
98 | maxCrossAxisExtent: widget.maxCrossAxisExtent,
99 | childAspectRatio: widget.childAspectRatio,
100 | crossAxisSpacing: widget.crossAxisSpacing,
101 | mainAxisSpacing: widget.mainAxisSpacing,
102 | itemBuilder: widget.itemBuilder,
103 | padding: widget.padding,
104 | noItemsFoundBuilder: noItemsFoundBuilder(
105 | context,
106 | noItemsFoundBuilder: widget.noItemsFoundBuilder,
107 | noItemsFoundWidget: widget.noItemsFoundWidget,
108 | ),
109 | pageLoadController: mutable ? pageLoadController : null,
110 | pageSize: mutable ? null : widget.pageSize,
111 | pageFuture: mutable ? null : pageFuture,
112 | scrollDirection: widget.scrollDirection,
113 | shrinkWrap: widget.shrinkWrap,
114 | physics: widget.physics,
115 | );
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/lib/src/ui/listview/paginated_list_view.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import 'common.dart';
4 | import 'pagewise/flutter_pagewise.dart';
5 |
6 | class PaginatedListView extends StatefulWidget {
7 | final ItemBuilder itemBuilder;
8 | final PageFuture pageFuture;
9 | final int pageSize;
10 | final EdgeInsetsGeometry? padding;
11 | final NoItemsFoundBuilder? noItemsFoundBuilder;
12 | final Widget? noItemsFoundWidget;
13 | final PagewiseLoadController? pageLoadController;
14 |
15 | /// default is false
16 | ///
17 | /// set to true if the future will change.
18 | final bool mutable;
19 |
20 | /// default is false
21 | ///
22 | /// set to true if the future will change.
23 | final bool showRefreshIndicator;
24 |
25 | /// Called when loading each page.
26 | ///
27 | /// It is expected to return a widget to display while the page is loading.
28 | /// For example:
29 | /// ```dart
30 | /// (BuildContext context) {
31 | /// return Text('Loading...');
32 | /// }
33 | /// ```
34 | ///
35 | /// If not specified, a [CircularProgressIndicator](https://docs.flutter.io/flutter/material/CircularProgressIndicator-class.html) will be shown
36 | final LoadingBuilder? loadingBuilder;
37 |
38 | final Axis scrollDirection;
39 | final bool shrinkWrap;
40 | final ScrollPhysics? physics;
41 |
42 | final Color? refreshIndicatorBackgroundColor;
43 | final Color? refreshIndicatorColor;
44 |
45 | const PaginatedListView({
46 | super.key,
47 | required this.itemBuilder,
48 | required this.pageFuture,
49 | this.pageSize = 10,
50 | this.padding,
51 | this.noItemsFoundBuilder,
52 | this.noItemsFoundWidget,
53 | this.pageLoadController,
54 | this.mutable = false,
55 | this.showRefreshIndicator = false,
56 | this.loadingBuilder,
57 | this.scrollDirection = Axis.vertical,
58 | this.shrinkWrap = false,
59 | this.physics,
60 | this.refreshIndicatorBackgroundColor,
61 | this.refreshIndicatorColor,
62 | });
63 |
64 | @override
65 | State> createState() => _PaginatedListViewState();
66 | }
67 |
68 | class _PaginatedListViewState extends State> {
69 | bool _reload = false;
70 |
71 | @override
72 | Widget build(BuildContext context) {
73 | return widget.showRefreshIndicator
74 | ? RefreshIndicator(
75 | backgroundColor: widget.refreshIndicatorBackgroundColor,
76 | color: widget.refreshIndicatorColor,
77 | onRefresh: () async {
78 | await widget.pageFuture(1);
79 | setState(() => _reload = true);
80 | },
81 | child: buildListView(),
82 | )
83 | : buildListView();
84 | }
85 |
86 | Widget buildListView() {
87 | final mutable = widget.mutable ||
88 | // widget.showRefreshIndicator || removed as there it serve no purpose leave here just in case there is a side effect
89 | _reload ||
90 | widget.pageLoadController != null;
91 |
92 | _reload = false;
93 |
94 | pageFuture(int? pageIndex) => widget.pageFuture(pageIndex! + 1);
95 |
96 | final pageLoadController = widget.pageLoadController ??
97 | PagewiseLoadController(
98 | pageSize: widget.pageSize,
99 | pageFuture: pageFuture,
100 | );
101 |
102 | return PagewiseListView(
103 | itemBuilder: widget.itemBuilder,
104 | padding: widget.padding,
105 | noItemsFoundBuilder: noItemsFoundBuilder(
106 | context,
107 | noItemsFoundBuilder: widget.noItemsFoundBuilder,
108 | noItemsFoundWidget: widget.noItemsFoundWidget,
109 | ),
110 | pageLoadController: mutable ? pageLoadController : null,
111 | pageSize: mutable ? null : widget.pageSize,
112 | pageFuture: mutable ? null : pageFuture,
113 | scrollDirection: widget.scrollDirection,
114 | shrinkWrap: widget.shrinkWrap,
115 | physics: widget.physics,
116 | loadingBuilder: widget.loadingBuilder,
117 | );
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/lib/src/ui/listview/paginated_sliver_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import 'common.dart';
4 | import 'pagewise/flutter_pagewise.dart';
5 |
6 | class PaginatedSliverList extends StatefulWidget {
7 | final ItemBuilder itemBuilder;
8 | final PageFuture pageFuture;
9 | final int pageSize;
10 | final NoItemsFoundBuilder? noItemsFoundBuilder;
11 | final Widget? noItemsFoundWidget;
12 | final PagewiseLoadController? pageLoadController;
13 |
14 | /// default is false
15 | ///
16 | /// set to true if the future will change.
17 | final bool mutable;
18 |
19 | const PaginatedSliverList({
20 | super.key,
21 | required this.itemBuilder,
22 | required this.pageFuture,
23 | this.pageSize = 10,
24 | this.noItemsFoundBuilder,
25 | this.noItemsFoundWidget,
26 | this.pageLoadController,
27 | this.mutable = false,
28 | });
29 |
30 | @override
31 | _PaginatedSliverListState createState() => _PaginatedSliverListState();
32 | }
33 |
34 | class _PaginatedSliverListState extends State> {
35 | final bool _reload = false;
36 |
37 | @override
38 | Widget build(BuildContext context) {
39 | return buildListView();
40 | }
41 |
42 | Widget buildListView() {
43 | final mutable =
44 | widget.mutable || _reload || widget.pageLoadController != null;
45 |
46 | pageFuture(int? pageIndex) => widget.pageFuture(pageIndex! + 1);
47 |
48 | final pageLoadController = widget.pageLoadController ??
49 | PagewiseLoadController(
50 | pageSize: widget.pageSize,
51 | pageFuture: pageFuture,
52 | );
53 |
54 | return PagewiseSliverList(
55 | itemBuilder: widget.itemBuilder,
56 | noItemsFoundBuilder: noItemsFoundBuilder(
57 | context,
58 | noItemsFoundBuilder: widget.noItemsFoundBuilder,
59 | noItemsFoundWidget: widget.noItemsFoundWidget,
60 | ),
61 | pageLoadController: mutable ? pageLoadController : null,
62 | pageSize: mutable ? null : widget.pageSize,
63 | pageFuture: mutable ? null : pageFuture,
64 | );
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/lib/src/ui/loading_builder.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:chopper/chopper.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_toolbox/flutter_toolbox.dart';
6 | import 'package:flutter_toolbox/generated/l10n.dart';
7 |
8 | typedef WidgetBuilder = Widget Function(BuildContext context, T snapshot);
9 | typedef LoadingWidgetBuilder = Widget Function(BuildContext context);
10 |
11 | class LoadingBuilder extends StatefulWidget {
12 | const LoadingBuilder({
13 | super.key,
14 | required this.future,
15 | required this.builder,
16 | this.initialData,
17 | this.mutable = false,
18 | this.loadingBuilder,
19 | this.loadingWidget,
20 | });
21 |
22 | /// The asynchronous computation to which this builder is currently connected,
23 | /// possibly null.
24 | ///
25 | /// If no future has yet completed, including in the case where [future] is
26 | /// null, the data provided to the [builder] will be set to [initialData].
27 | final Future> future;
28 |
29 | final WidgetBuilder builder;
30 |
31 | /// The data that will be used to create the snapshots provided until a
32 | /// non-null [future] has completed.
33 | ///
34 | /// If the future completes with an error, the data in the [AsyncSnapshot]
35 | /// provided to the [builder] will become null, regardless of [initialData].
36 | /// (The error itself will be available in [AsyncSnapshot.error], and
37 | /// [AsyncSnapshot.hasError] will be true.)
38 | final T? initialData;
39 |
40 | /// default is false
41 | ///
42 | /// set to true if the future will change.
43 | ///
44 | /// when `mutable` is set to true then false the last data is returned.
45 | final bool mutable;
46 |
47 | /// set only if loadingWidget if null
48 | final LoadingWidgetBuilder? loadingBuilder;
49 |
50 | /// set only if loadingBuilder if null
51 | final Widget? loadingWidget;
52 |
53 | @override
54 | _LoadingBuilderState createState() => _LoadingBuilderState();
55 | }
56 |
57 | class _LoadingBuilderState extends State> {
58 | @override
59 | Widget build(BuildContext context) {
60 | return FutureLoadingBuilder>(
61 | future: widget.future,
62 | // initialData: widget.initialData,
63 | mutable: widget.mutable,
64 | builder: (context, response) {
65 | return widget.builder(context, response.body);
66 | },
67 | loadingBuilder: widget.loadingBuilder,
68 | loadingWidget: widget.loadingWidget,
69 | );
70 | }
71 | }
72 |
73 | class FutureLoadingBuilder extends StatefulWidget {
74 | const FutureLoadingBuilder({
75 | super.key,
76 | required this.future,
77 | required this.builder,
78 | this.loadingBuilder,
79 | this.loadingWidget,
80 | this.initialData,
81 | this.mutable = false,
82 | }) : assert((loadingBuilder == null && loadingWidget == null) ||
83 | (loadingBuilder != null && loadingWidget == null) ||
84 | loadingBuilder == null && loadingWidget != null);
85 |
86 | /// The asynchronous computation to which this builder is currently connected,
87 | /// possibly null.
88 | ///
89 | /// If no future has yet completed, including in the case where [future] is
90 | /// null, the data provided to the [builder] will be set to [initialData].
91 | final Future future;
92 |
93 | final WidgetBuilder builder;
94 |
95 | /// The data that will be used to create the snapshots provided until a
96 | /// non-null [future] has completed.
97 | ///
98 | /// If the future completes with an error, the data in the [AsyncSnapshot]
99 | /// provided to the [builder] will become null, regardless of [initialData].
100 | /// (The error itself will be available in [AsyncSnapshot.error], and
101 | /// [AsyncSnapshot.hasError] will be true.)
102 | final T? initialData;
103 |
104 | /// default is true
105 | ///
106 | /// set to false if the future will change.
107 | ///
108 | /// when `mutable` is set to true then false the last data is returned.
109 | final bool mutable;
110 |
111 | /// set only if loadingWidget if null
112 | final LoadingWidgetBuilder? loadingBuilder;
113 |
114 | /// set only if loadingBuilder if null
115 | final Widget? loadingWidget;
116 |
117 | @override
118 | _FutureLoadingBuilderState createState() =>
119 | _FutureLoadingBuilderState();
120 |
121 | static bool atMostOneIsSet(List