mirror of
https://github.com/Iconica-Development/flutter_grid_to_list.git
synced 2025-05-19 04:23:45 +02:00
initial commit
This commit is contained in:
commit
5ca9f7ffa6
20 changed files with 1395 additions and 0 deletions
39
.gitignore
vendored
Normal file
39
.gitignore
vendored
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||||
|
/pubspec.lock
|
||||||
|
**/doc/api/
|
||||||
|
.dart_tool/
|
||||||
|
.packages
|
||||||
|
build/
|
||||||
|
|
||||||
|
example/android
|
||||||
|
example/ios
|
||||||
|
example/web
|
||||||
|
example/linux
|
||||||
|
example/macos
|
||||||
|
example/windows
|
||||||
|
|
||||||
|
coverage/
|
45
.metadata
Normal file
45
.metadata
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# This file tracks properties of this Flutter project.
|
||||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
|
#
|
||||||
|
# This file should be version controlled.
|
||||||
|
|
||||||
|
version:
|
||||||
|
revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
channel: stable
|
||||||
|
|
||||||
|
project_type: app
|
||||||
|
|
||||||
|
# Tracks metadata for the flutter migrate command
|
||||||
|
migration:
|
||||||
|
platforms:
|
||||||
|
- platform: root
|
||||||
|
create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
- platform: android
|
||||||
|
create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
- platform: ios
|
||||||
|
create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
- platform: linux
|
||||||
|
create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
- platform: macos
|
||||||
|
create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
- platform: web
|
||||||
|
create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
- platform: windows
|
||||||
|
create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
|
||||||
|
# User provided section
|
||||||
|
|
||||||
|
# List of Local paths (relative to this file) that should be
|
||||||
|
# ignored by the migrate tool.
|
||||||
|
#
|
||||||
|
# Files that are not part of the templates will be ignored by default.
|
||||||
|
unmanaged_files:
|
||||||
|
- 'lib/main.dart'
|
||||||
|
- 'ios/Runner.xcodeproj/project.pbxproj'
|
3
CHANGELOG.md
Normal file
3
CHANGELOG.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
## [0.0.1] 15 March 2023
|
||||||
|
|
||||||
|
- Initial Release
|
194
CONTRIBUTING.md
Normal file
194
CONTRIBUTING.md
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
# Contributing
|
||||||
|
First off, thanks for taking the time to contribute! ❤️
|
||||||
|
|
||||||
|
All types of contributions are encouraged and valued.
|
||||||
|
See the [Table of Contents](#table-of-contents) for different ways to help and details about how we handle them.
|
||||||
|
Please make sure to read the relevant section before making your contribution.
|
||||||
|
It will make it a lot easier for us maintainers and smooth out the experience for all involved.
|
||||||
|
Iconica looks forward to your contributions. 🎉
|
||||||
|
|
||||||
|
## Table of contents
|
||||||
|
- [Code of conduct](#code-of-conduct)
|
||||||
|
- [I Have a Question](#i-have-a-question)
|
||||||
|
- [I Want To Contribute](#i-want-to-contribute)
|
||||||
|
- [Reporting Bugs](#reporting-bugs)
|
||||||
|
- [Contributing code](#contributing-code)
|
||||||
|
|
||||||
|
## Code of conduct
|
||||||
|
|
||||||
|
### Legal notice
|
||||||
|
When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license.
|
||||||
|
All accepted pull requests and other additions to this project will be considered intellectual property of Iconica.
|
||||||
|
|
||||||
|
All repositories should be kept clean of jokes, easter eggs and other unnecessary additions.
|
||||||
|
|
||||||
|
## I have a question
|
||||||
|
|
||||||
|
If you want to ask a question, we assume that you have read the available documentation found within the code.
|
||||||
|
Before you ask a question, it is best to search for existing issues that might help you.
|
||||||
|
In case you have found a suitable issue but still need clarification, you can ask your question
|
||||||
|
It is also advisable to search the internet for answers first.
|
||||||
|
|
||||||
|
If you then still feel the need to ask a question and need clarification, we recommend the following:
|
||||||
|
|
||||||
|
- Open an issue.
|
||||||
|
- Provide as much context as you can about what you're running into.
|
||||||
|
|
||||||
|
We will then take care of the issue as soon as possible.
|
||||||
|
|
||||||
|
## I want to contribute
|
||||||
|
|
||||||
|
### Reporting bugs
|
||||||
|
|
||||||
|
<!-- omit in toc -->
|
||||||
|
**Before submitting a bug report**
|
||||||
|
|
||||||
|
A good bug report shouldn't leave others needing to chase you up for more information.
|
||||||
|
Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report.
|
||||||
|
Please complete the following steps in advance to help us fix any potential bug as fast as possible.
|
||||||
|
|
||||||
|
- Make sure that you are using the latest version.
|
||||||
|
- Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment components/versions (If you are looking for support, you might want to check [this section](#i-have-a-question)).
|
||||||
|
- To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error.
|
||||||
|
- Also make sure to search the internet (including Stack Overflow) to see if users outside of Iconica have discussed the issue.
|
||||||
|
- Collect information about the bug:
|
||||||
|
- Stack trace (Traceback)
|
||||||
|
- OS, Platform and Version (Windows, Linux, macOS, x86, ARM)
|
||||||
|
- Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant.
|
||||||
|
- Time and date of occurance
|
||||||
|
- Describe the expected result and actual result
|
||||||
|
- Can you reliably reproduce the issue? And can you also reproduce it with older versions? Describe all steps that lead to the bug.
|
||||||
|
|
||||||
|
Once it's filed:
|
||||||
|
|
||||||
|
- The project team will label the issue accordingly.
|
||||||
|
- A team member will try to reproduce the issue with your provided steps.
|
||||||
|
If there are no reproduction steps or no obvious way to reproduce the issue, the team will ask you for additional information.
|
||||||
|
- If the team is able to reproduce the issue, it will be moved into the backlog, as well as marked with a priority, and the issue will be left to be [implemented by someone](#contributing-code).
|
||||||
|
|
||||||
|
### Contributing code
|
||||||
|
|
||||||
|
When you start working on your contribution, make sure you are aware of the relevant documentation and the functionality of the component you are working on.
|
||||||
|
|
||||||
|
When writing code, follow the style guidelines set by Dart: [Effective Dart](https://Dart.dev/guides/language/effective-Dart). This contains most information you will need to write clean Dart code that is well documented.
|
||||||
|
|
||||||
|
**Documentation**
|
||||||
|
|
||||||
|
As Effective Dart indicates, documenting your public methods with Dart doc comments is recommended.
|
||||||
|
Aside from Effective Dart, we require specific information in the documentation of a method:
|
||||||
|
|
||||||
|
At the very least, your documentation should first name what the code does, then followed below by requirements for calling the method, the result of the method.
|
||||||
|
Any references to internal variables or other methods should be done through [var] to indicate a reference.
|
||||||
|
|
||||||
|
If the method or class is complex enough (determined by the reviewers) an example is required.
|
||||||
|
If unsure, add an example in the docs using code blocks.
|
||||||
|
|
||||||
|
For classes and methods, document the individual parameters with their implications.
|
||||||
|
|
||||||
|
> Tip: Remember that the shortest documentation can be written by having good descriptive names in the first place.
|
||||||
|
|
||||||
|
An example:
|
||||||
|
```Dart
|
||||||
|
library iconica_utilities.bidirectional_sorter;
|
||||||
|
|
||||||
|
part 'sorter.Dart';
|
||||||
|
part 'enum.Dart';
|
||||||
|
|
||||||
|
/// Generic sort method, allow sorting of list with primitives or complex types.
|
||||||
|
/// Uses [SortDirection] to determine the direction, either Ascending or Descending,
|
||||||
|
/// Gets called on [List] toSort of type [T] which cannot be shorter than 2.
|
||||||
|
/// Optionally for complex types a [Comparable] [Function] can be given to compare complex types.
|
||||||
|
/// ```
|
||||||
|
/// List<TestObject> objects = [];
|
||||||
|
/// for (int i = 0; i < 10; i++) {
|
||||||
|
/// objects.add(TestObject(name: "name", id: i));
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// sort<TestObject>(
|
||||||
|
/// SortDirection.descending, objects, (object) => object.id);
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// In the above example a list of TestObjects is created, and then sorted in descending order.
|
||||||
|
/// If the implementation of TestObject is as following:
|
||||||
|
/// ```
|
||||||
|
/// class TestObject {
|
||||||
|
/// final String name;
|
||||||
|
/// final int id;
|
||||||
|
///
|
||||||
|
/// TestObject({required this.name, required this.id});
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// And the list is logged to the console, the following will appear:
|
||||||
|
/// ```
|
||||||
|
/// [name9, name8, name7, name6, name5, name4, name3, name2, name1, name0]
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
void sort<T>(
|
||||||
|
/// Determines the sorting direction, can be either Ascending or Descending
|
||||||
|
SortDirection sortDirection,
|
||||||
|
|
||||||
|
/// Incoming list, which gets sorted
|
||||||
|
List<T> toSort, [
|
||||||
|
|
||||||
|
/// Optional comparable, which is only necessary for complex types
|
||||||
|
SortFieldGetter<T>? sortValueCallback,
|
||||||
|
]) {
|
||||||
|
if (toSort.length < 2) return;
|
||||||
|
assert(
|
||||||
|
toSort.whereType<Comparable>().isNotEmpty || sortValueCallback != null);
|
||||||
|
BidirectionalSorter<T>(
|
||||||
|
sortInstructions: <SortInstruction<T>>[
|
||||||
|
SortInstruction(
|
||||||
|
sortValueCallback ?? (t) => t as Comparable, sortDirection),
|
||||||
|
],
|
||||||
|
).sort(toSort);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// same functionality as [sort] but with the added functionality
|
||||||
|
/// of sorting multiple values
|
||||||
|
void sortMulti<T>(
|
||||||
|
/// Incoming list, which gets sorted
|
||||||
|
List<T> toSort,
|
||||||
|
|
||||||
|
/// list of comparables to sort multiple values at once,
|
||||||
|
/// priority based on index
|
||||||
|
List<SortInstruction<T>> sortValueCallbacks,
|
||||||
|
) {
|
||||||
|
if (toSort.length < 2) return;
|
||||||
|
assert(sortValueCallbacks.isNotEmpty);
|
||||||
|
BidirectionalSorter<T>(
|
||||||
|
sortInstructions: sortValueCallbacks,
|
||||||
|
).sort(toSort);
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**Tests**
|
||||||
|
|
||||||
|
For each public method that was created, excluding widgets, which contains any form of logic (e.g. Calculations, predicates or major side-effects) tests are required.
|
||||||
|
|
||||||
|
A set of tests is written for each method, covering at least each path within the method. For example:
|
||||||
|
|
||||||
|
```Dart
|
||||||
|
void foo() {
|
||||||
|
try {
|
||||||
|
var bar = doSomething();
|
||||||
|
if (bar) {
|
||||||
|
doSomethingElse();
|
||||||
|
} else {
|
||||||
|
doSomethingCool();
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
displayError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
The method above should result in 3 tests:
|
||||||
|
|
||||||
|
1. A test for the path leading to displayError by the cause of an exception
|
||||||
|
2. A test for if bar is true, resulting in doSomethingElse()
|
||||||
|
3. A test for if bar is false, resulting in the doSomethingCool() method being called.
|
||||||
|
|
||||||
|
This means that we require 100% coverage of each method you test.
|
24
README.md
Normal file
24
README.md
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# Grid to list
|
||||||
|
Grid to List that animates between grid and list when a child in either gets tapped
|
||||||
|
|
||||||
|
https://user-images.githubusercontent.com/57899901/225304113-28cac130-36e8-4c7b-8fed-50032972ad4f.mp4
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
To use this package, add `flutter_grid_to_list` as a dependency in your pubspec.yaml file.
|
||||||
|
|
||||||
|
## How to use
|
||||||
|
|
||||||
|
See the [Example Code](example/lib/main.dart) for an example on how to use this package.
|
||||||
|
|
||||||
|
## Issues
|
||||||
|
|
||||||
|
Please file any issues, bugs or feature request as an issue on our [GitHub](https://github.com/Iconica-Development/to_do_list) page. Commercial support is available if you need help with integration with your app or services. You can contact us at [support@iconica.nl](mailto:support@iconica.nl).
|
||||||
|
|
||||||
|
## Want to contribute
|
||||||
|
|
||||||
|
If you would like to contribute to the plugin (e.g. by improving the documentation, solving a bug or adding a cool new feature), please carefully review our [contribution guide](./CONTRIBUTING.md) and send us your [pull request](https://github.com/Iconica-Development/to_do_list/pulls).
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
This `flutter_grid_to_list` for Flutter is developed by [Iconica](https://iconica.nl). You can contact us at <support@iconica.nl>
|
29
analysis_options.yaml
Normal file
29
analysis_options.yaml
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# This file configures the analyzer, which statically analyzes Dart code to
|
||||||
|
# check for errors, warnings, and lints.
|
||||||
|
#
|
||||||
|
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||||
|
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||||
|
# invoked from the command line by running `flutter analyze`.
|
||||||
|
|
||||||
|
# The following line activates a set of recommended lints for Flutter apps,
|
||||||
|
# packages, and plugins designed to encourage good coding practices.
|
||||||
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
linter:
|
||||||
|
# The lint rules applied to this project can be customized in the
|
||||||
|
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||||
|
# included above or to enable additional rules. A list of all available lints
|
||||||
|
# and their documentation is published at
|
||||||
|
# https://dart-lang.github.io/linter/lints/index.html.
|
||||||
|
#
|
||||||
|
# Instead of disabling a lint rule for the entire project in the
|
||||||
|
# section below, it can also be suppressed for a single line of code
|
||||||
|
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||||
|
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||||
|
# producing the lint.
|
||||||
|
rules:
|
||||||
|
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||||
|
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||||
|
|
||||||
|
# Additional information about this file can be found at
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
44
example/.gitignore
vendored
Normal file
44
example/.gitignore
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
#.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
**/doc/api/
|
||||||
|
**/ios/Flutter/.last_build_id
|
||||||
|
.dart_tool/
|
||||||
|
.flutter-plugins
|
||||||
|
.flutter-plugins-dependencies
|
||||||
|
.packages
|
||||||
|
.pub-cache/
|
||||||
|
.pub/
|
||||||
|
/build/
|
||||||
|
|
||||||
|
# Symbolication related
|
||||||
|
app.*.symbols
|
||||||
|
|
||||||
|
# Obfuscation related
|
||||||
|
app.*.map.json
|
||||||
|
|
||||||
|
# Android Studio will place build artifacts here
|
||||||
|
/android/app/debug
|
||||||
|
/android/app/profile
|
||||||
|
/android/app/release
|
45
example/.metadata
Normal file
45
example/.metadata
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# This file tracks properties of this Flutter project.
|
||||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
|
#
|
||||||
|
# This file should be version controlled.
|
||||||
|
|
||||||
|
version:
|
||||||
|
revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
channel: stable
|
||||||
|
|
||||||
|
project_type: app
|
||||||
|
|
||||||
|
# Tracks metadata for the flutter migrate command
|
||||||
|
migration:
|
||||||
|
platforms:
|
||||||
|
- platform: root
|
||||||
|
create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
- platform: android
|
||||||
|
create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
- platform: ios
|
||||||
|
create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
- platform: linux
|
||||||
|
create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
- platform: macos
|
||||||
|
create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
- platform: web
|
||||||
|
create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
- platform: windows
|
||||||
|
create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
|
||||||
|
|
||||||
|
# User provided section
|
||||||
|
|
||||||
|
# List of Local paths (relative to this file) that should be
|
||||||
|
# ignored by the migrate tool.
|
||||||
|
#
|
||||||
|
# Files that are not part of the templates will be ignored by default.
|
||||||
|
unmanaged_files:
|
||||||
|
- 'lib/main.dart'
|
||||||
|
- 'ios/Runner.xcodeproj/project.pbxproj'
|
3
example/README.md
Normal file
3
example/README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# example
|
||||||
|
|
||||||
|
This is an example project showing how to use the grid to list component.
|
29
example/analysis_options.yaml
Normal file
29
example/analysis_options.yaml
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# This file configures the analyzer, which statically analyzes Dart code to
|
||||||
|
# check for errors, warnings, and lints.
|
||||||
|
#
|
||||||
|
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||||
|
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||||
|
# invoked from the command line by running `flutter analyze`.
|
||||||
|
|
||||||
|
# The following line activates a set of recommended lints for Flutter apps,
|
||||||
|
# packages, and plugins designed to encourage good coding practices.
|
||||||
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
linter:
|
||||||
|
# The lint rules applied to this project can be customized in the
|
||||||
|
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||||
|
# included above or to enable additional rules. A list of all available lints
|
||||||
|
# and their documentation is published at
|
||||||
|
# https://dart-lang.github.io/linter/lints/index.html.
|
||||||
|
#
|
||||||
|
# Instead of disabling a lint rule for the entire project in the
|
||||||
|
# section below, it can also be suppressed for a single line of code
|
||||||
|
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||||
|
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||||
|
# producing the lint.
|
||||||
|
rules:
|
||||||
|
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||||
|
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||||
|
|
||||||
|
# Additional information about this file can be found at
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
80
example/lib/main.dart
Normal file
80
example/lib/main.dart
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:flutter_grid_to_list/flutter_grid_to_list.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
runApp(const MaterialApp(
|
||||||
|
home: GridToList(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
class GridToList extends StatefulWidget {
|
||||||
|
const GridToList({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<GridToList> createState() => _GridToListState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _GridToListState extends State<GridToList> {
|
||||||
|
late AnimatedGridToListController controller;
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
controller = AnimatedGridToListController();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
var containers = <Container>[
|
||||||
|
...List.generate(80, (index) => index)
|
||||||
|
.map((e) => Container(
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
color: Color((Random().nextDouble() * 0xFFFFFF).toInt())
|
||||||
|
.withOpacity(1.0),
|
||||||
|
))
|
||||||
|
.toList(),
|
||||||
|
];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
//get size
|
||||||
|
return Scaffold(
|
||||||
|
body: AnimatedGridToList(
|
||||||
|
controller: controller,
|
||||||
|
onTap: (i) {
|
||||||
|
if (!controller.isExpanded) {
|
||||||
|
controller.expand(i, const Duration(seconds: 2));
|
||||||
|
} else {
|
||||||
|
controller.shrink(const Duration(seconds: 2));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
itemBuilder: AnimatedGridToListItemBuilder(
|
||||||
|
gridItemBuilder: (context, index) {
|
||||||
|
return containers[index];
|
||||||
|
},
|
||||||
|
listItemBuilder: (context, index) => Container(
|
||||||
|
height: MediaQuery.of(context).size.height * 0.6,
|
||||||
|
width: MediaQuery.of(context).size.width * 0.8,
|
||||||
|
color: containers[index].color,
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: Text('Item $index'),
|
||||||
|
),
|
||||||
|
listItemSize: Size(
|
||||||
|
MediaQuery.of(context).size.width * 0.8,
|
||||||
|
MediaQuery.of(context).size.height * 0.6,
|
||||||
|
),
|
||||||
|
gridItemSize: const Size(50, 50),
|
||||||
|
itemCount: containers.length,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
195
example/pubspec.lock
Normal file
195
example/pubspec.lock
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
# Generated by pub
|
||||||
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
|
packages:
|
||||||
|
async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: async
|
||||||
|
sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.10.0"
|
||||||
|
boolean_selector:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: boolean_selector
|
||||||
|
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
|
characters:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: characters
|
||||||
|
sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
|
clock:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: clock
|
||||||
|
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
|
collection:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: collection
|
||||||
|
sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.17.0"
|
||||||
|
cupertino_icons:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: cupertino_icons
|
||||||
|
sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.5"
|
||||||
|
fake_async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fake_async
|
||||||
|
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.1"
|
||||||
|
flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
flutter_grid_to_list:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: ".."
|
||||||
|
relative: true
|
||||||
|
source: path
|
||||||
|
version: "1.0.0+1"
|
||||||
|
flutter_lints:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: flutter_lints
|
||||||
|
sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.1"
|
||||||
|
flutter_test:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
js:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: js
|
||||||
|
sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.5"
|
||||||
|
lints:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: lints
|
||||||
|
sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.1"
|
||||||
|
matcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: matcher
|
||||||
|
sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.12.13"
|
||||||
|
material_color_utilities:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: material_color_utilities
|
||||||
|
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.0"
|
||||||
|
meta:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: meta
|
||||||
|
sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.8.0"
|
||||||
|
path:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path
|
||||||
|
sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.8.2"
|
||||||
|
sky_engine:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.99"
|
||||||
|
source_span:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_span
|
||||||
|
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.9.1"
|
||||||
|
stack_trace:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stack_trace
|
||||||
|
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.11.0"
|
||||||
|
stream_channel:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stream_channel
|
||||||
|
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
|
string_scanner:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: string_scanner
|
||||||
|
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.0"
|
||||||
|
term_glyph:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: term_glyph
|
||||||
|
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
|
test_api:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: test_api
|
||||||
|
sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.4.16"
|
||||||
|
vector_math:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_math
|
||||||
|
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.4"
|
||||||
|
sdks:
|
||||||
|
dart: ">=2.19.3 <3.0.0"
|
26
example/pubspec.yaml
Normal file
26
example/pubspec.yaml
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
name: example
|
||||||
|
description: A new Flutter project.
|
||||||
|
|
||||||
|
publish_to: "none"
|
||||||
|
|
||||||
|
version: 1.0.0+1
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ">=2.19.3 <3.0.0"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_grid_to_list:
|
||||||
|
path: ..
|
||||||
|
|
||||||
|
cupertino_icons: ^1.0.2
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
|
flutter_lints: ^2.0.0
|
||||||
|
|
||||||
|
flutter:
|
||||||
|
uses-material-design: true
|
BIN
flutter_grid_to_list.gif
Normal file
BIN
flutter_grid_to_list.gif
Normal file
Binary file not shown.
7
lib/flutter_grid_to_list.dart
Normal file
7
lib/flutter_grid_to_list.dart
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
library flutter_grid_to_list;
|
||||||
|
|
||||||
|
export 'src/animated_grid_to_list.dart';
|
302
lib/src/animated_grid_to_list.dart
Normal file
302
lib/src/animated_grid_to_list.dart
Normal file
|
@ -0,0 +1,302 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_grid_to_list/src/animated_grid_to_list_item_builder.dart';
|
||||||
|
import 'package:flutter_grid_to_list/src/animated_grid_to_list_type.dart';
|
||||||
|
|
||||||
|
export 'animated_grid_to_list_item_builder.dart';
|
||||||
|
|
||||||
|
class AnimatedGridToList extends StatefulWidget {
|
||||||
|
const AnimatedGridToList({
|
||||||
|
Key? key,
|
||||||
|
required this.itemBuilder,
|
||||||
|
required this.controller,
|
||||||
|
this.startWithGrid = true,
|
||||||
|
this.onTap,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final AnimatedGridToListItemBuilder itemBuilder;
|
||||||
|
final AnimatedGridToListController controller;
|
||||||
|
final bool startWithGrid;
|
||||||
|
final Function(int)? onTap;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AnimatedGridToList> createState() => _AnimatedGridToListState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AnimatedGridToListState extends State<AnimatedGridToList>
|
||||||
|
with TickerProviderStateMixin {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
widget.controller._itemBuilder = widget.itemBuilder;
|
||||||
|
widget.controller._type =
|
||||||
|
widget.startWithGrid ? GridToListType.grid : GridToListType.list;
|
||||||
|
widget.controller._vsync = this;
|
||||||
|
widget.controller.addListener(listenerFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
void listenerFunction() {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
widget.controller.removeListener(listenerFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
widget.controller._context = context;
|
||||||
|
return ScrollConfiguration(
|
||||||
|
behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
// physics: MagnetScrollPhysics(
|
||||||
|
// itemSize: widget.controller._boxHeight ??
|
||||||
|
// widget.itemBuilder.gridItemSize.height,
|
||||||
|
// ),
|
||||||
|
controller: widget.controller._scrollController,
|
||||||
|
child: widget.controller._type.render(
|
||||||
|
spacing: widget.controller._spacing,
|
||||||
|
boxWidth: widget.controller._boxWidth,
|
||||||
|
boxHeight: widget.controller._boxHeight,
|
||||||
|
tappedItem: widget.controller._tappedItem,
|
||||||
|
isAnimating: widget.controller._isAnimating,
|
||||||
|
wrapAlignment: widget.controller._wrapAlignment,
|
||||||
|
onTap: widget.onTap,
|
||||||
|
context: context,
|
||||||
|
itemBuilder: widget.itemBuilder,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnimatedGridToListController extends ChangeNotifier {
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
|
late AnimationController _controller;
|
||||||
|
late Animation<double> _widthAnimation;
|
||||||
|
late Animation<double> _heightAnimation;
|
||||||
|
late AnimatedGridToListItemBuilder _itemBuilder;
|
||||||
|
late BuildContext _context;
|
||||||
|
late TickerProvider _vsync;
|
||||||
|
late GridToListType _type;
|
||||||
|
|
||||||
|
double? _boxWidth;
|
||||||
|
double? _boxHeight;
|
||||||
|
double? _initalHeight;
|
||||||
|
double? _initialWidth;
|
||||||
|
double? _previousScrollOffset;
|
||||||
|
double? _previousScrollOffsetCopy;
|
||||||
|
double _spacing = 0;
|
||||||
|
|
||||||
|
bool _isAnimating = false;
|
||||||
|
bool _isExpanded = false;
|
||||||
|
|
||||||
|
int? _tappedItem;
|
||||||
|
int? _prevIndex;
|
||||||
|
|
||||||
|
WrapAlignment _wrapAlignment = WrapAlignment.center;
|
||||||
|
|
||||||
|
void _setAnimation() {
|
||||||
|
var endValueWidth = _itemBuilder.listItemSize.width;
|
||||||
|
var endValueHeight = _itemBuilder.listItemSize.height == double.infinity
|
||||||
|
? _itemBuilder.gridItemSize.height
|
||||||
|
: _itemBuilder.listItemSize.height;
|
||||||
|
|
||||||
|
_initialWidth ??= _itemBuilder.gridItemSize.width;
|
||||||
|
_initalHeight ??= _itemBuilder.gridItemSize.height;
|
||||||
|
|
||||||
|
if (_type == GridToListType.list) {
|
||||||
|
_widthAnimation =
|
||||||
|
Tween<double>(begin: endValueWidth, end: _initialWidth).animate(
|
||||||
|
CurvedAnimation(
|
||||||
|
parent: _controller,
|
||||||
|
curve: Curves.easeInQuint,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
_heightAnimation =
|
||||||
|
Tween<double>(begin: endValueHeight, end: _initalHeight).animate(
|
||||||
|
CurvedAnimation(
|
||||||
|
parent: _controller,
|
||||||
|
curve: Curves.easeInQuint,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
_widthAnimation =
|
||||||
|
Tween<double>(begin: _initialWidth, end: endValueWidth).animate(
|
||||||
|
CurvedAnimation(
|
||||||
|
parent: _controller,
|
||||||
|
curve: Curves.easeOutQuart,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
_heightAnimation =
|
||||||
|
Tween<double>(begin: _initalHeight, end: endValueHeight).animate(
|
||||||
|
CurvedAnimation(
|
||||||
|
parent: _controller,
|
||||||
|
curve: Curves.easeOutQuart,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleScroll(int index) {
|
||||||
|
switch (_type) {
|
||||||
|
case GridToListType.grid:
|
||||||
|
_scrollController.jumpTo((index * (_boxHeight ?? 0)));
|
||||||
|
if ([WrapAlignment.start, WrapAlignment.end].contains(_wrapAlignment) &&
|
||||||
|
_controller.value > 0.5) {
|
||||||
|
_wrapAlignment = WrapAlignment.center;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GridToListType.list:
|
||||||
|
_previousScrollOffset = null;
|
||||||
|
_scrollController.jumpTo(index * (_boxHeight ?? 0));
|
||||||
|
if ([WrapAlignment.start, WrapAlignment.end].contains(_wrapAlignment) &&
|
||||||
|
_controller.value > 0.5) {
|
||||||
|
_wrapAlignment = WrapAlignment.center;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _finalize(double prevScrollOffset) {
|
||||||
|
if (_controller.isCompleted) {
|
||||||
|
switch (_type) {
|
||||||
|
case GridToListType.grid:
|
||||||
|
_type = _type.next;
|
||||||
|
_handleScroll(_tappedItem!);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case GridToListType.list:
|
||||||
|
_wrapAlignment = WrapAlignment.center;
|
||||||
|
_type = _type.next;
|
||||||
|
|
||||||
|
_handleScroll(_tappedItem!);
|
||||||
|
|
||||||
|
_scrollController.jumpTo(prevScrollOffset);
|
||||||
|
_spacing = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
_controller.removeListener(_listenerFunction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _listenerFunction() {
|
||||||
|
if (_itemBuilder.listItemSize.height == double.infinity) {
|
||||||
|
_boxHeight = null;
|
||||||
|
} else {
|
||||||
|
_boxHeight = _heightAnimation.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
_boxWidth = _widthAnimation.value;
|
||||||
|
|
||||||
|
if (_context.size != null) {
|
||||||
|
_spacing = _context.size!.width - _boxWidth!;
|
||||||
|
} else {
|
||||||
|
_spacing = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isAnimating = _controller.isAnimating;
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
_finalize(_previousScrollOffsetCopy!);
|
||||||
|
}
|
||||||
|
|
||||||
|
WrapAlignment _determineAlignment(int index, BuildContext context,
|
||||||
|
AnimatedGridToListItemBuilder itemBuilder) {
|
||||||
|
var amountOfItems =
|
||||||
|
((context.size?.width ?? 0) / itemBuilder.gridItemSize.width).floor();
|
||||||
|
|
||||||
|
var determineSeq = amountOfItems % 3;
|
||||||
|
var itemLowest = (amountOfItems / 3).floor();
|
||||||
|
late int itemIndex;
|
||||||
|
|
||||||
|
if (amountOfItems != 0) {
|
||||||
|
itemIndex = index % amountOfItems;
|
||||||
|
} else {
|
||||||
|
itemIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
late int itemsSide;
|
||||||
|
late int itemsCenter;
|
||||||
|
|
||||||
|
switch (determineSeq) {
|
||||||
|
case 0:
|
||||||
|
itemsSide = itemLowest;
|
||||||
|
itemsCenter = itemLowest;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
itemsSide = itemLowest;
|
||||||
|
itemsCenter = itemLowest + 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
itemsSide = itemLowest + 1;
|
||||||
|
itemsCenter = itemLowest;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((itemIndex + 1) <= itemsSide) {
|
||||||
|
return WrapAlignment.start;
|
||||||
|
} else if ((itemIndex + 1) <= itemsSide + itemsCenter) {
|
||||||
|
return WrapAlignment.center;
|
||||||
|
} else {
|
||||||
|
return WrapAlignment.end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Can be used to retrieve the status of the widget. This is only useful if you use [shrink] and [expand] methods
|
||||||
|
bool get isExpanded => _isExpanded;
|
||||||
|
|
||||||
|
/// Can be called to shrink to widget to it's grid state. Only works when the current state is of type [GridToListType.list]
|
||||||
|
void shrink([Duration? duration]) {
|
||||||
|
_isExpanded = false;
|
||||||
|
if (_type == GridToListType.list) {
|
||||||
|
resize(_prevIndex!, duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Can be called to expand to widget to it's list state. Only works when the current state is of type [GridToListType.grid]
|
||||||
|
///
|
||||||
|
/// Gets an index to determine which item it needs to transform.
|
||||||
|
void expand(int index, [Duration? duration]) {
|
||||||
|
_isExpanded = true;
|
||||||
|
_prevIndex = index;
|
||||||
|
if (_type == GridToListType.grid) {
|
||||||
|
resize(index, duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Can be called to dynamically change the state of the widget. Works in either [GridToListType.grid] or [GridToListType.list]
|
||||||
|
///
|
||||||
|
/// Gets an index to determine which item it needs to transform.
|
||||||
|
void resize(int index, Duration? duration) {
|
||||||
|
if (!_isAnimating) {
|
||||||
|
_previousScrollOffset ??= _scrollController.offset;
|
||||||
|
_previousScrollOffsetCopy = _previousScrollOffset ?? 0;
|
||||||
|
|
||||||
|
_controller = AnimationController(
|
||||||
|
vsync: _vsync,
|
||||||
|
duration: duration ?? const Duration(milliseconds: 200),
|
||||||
|
);
|
||||||
|
|
||||||
|
_tappedItem = index;
|
||||||
|
_wrapAlignment = _determineAlignment(index, _context, _itemBuilder);
|
||||||
|
notifyListeners();
|
||||||
|
|
||||||
|
_setAnimation();
|
||||||
|
|
||||||
|
_controller.addListener(_listenerFunction);
|
||||||
|
|
||||||
|
_controller.forward();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
40
lib/src/animated_grid_to_list_item_builder.dart
Normal file
40
lib/src/animated_grid_to_list_item_builder.dart
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
/// Class required by [AnimatedGridToList] to build items.
|
||||||
|
/// Requires [gridItemBuilder] which is an [IndexedWidgetBuilder] to build items in [AnimatedGridToList] grid state.
|
||||||
|
///
|
||||||
|
/// Requires [listItemBuilder] which is an [IndexedWidgetBuilder] to build items in [AnimatedGridToList] list state.
|
||||||
|
///
|
||||||
|
/// Requires [gridItemSize] which is of type [Size] to build the items and handle the scrolling accordingly.
|
||||||
|
///
|
||||||
|
/// Requires [listItemSize] which is of type [Size] to build the items and handle the scrolling accordingly.
|
||||||
|
///
|
||||||
|
/// It also requires an [itemCount], which is of type [int].
|
||||||
|
class AnimatedGridToListItemBuilder {
|
||||||
|
AnimatedGridToListItemBuilder({
|
||||||
|
required this.gridItemBuilder,
|
||||||
|
required this.listItemBuilder,
|
||||||
|
required this.itemCount,
|
||||||
|
required this.gridItemSize,
|
||||||
|
required this.listItemSize,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// [IndexedWidgetBuilder] which build the items in grid state.
|
||||||
|
final IndexedWidgetBuilder gridItemBuilder;
|
||||||
|
|
||||||
|
/// [IndexedWidgetBuilder] which build the items in list state.
|
||||||
|
final IndexedWidgetBuilder listItemBuilder;
|
||||||
|
|
||||||
|
/// A [Size] to build the items in the correct manner and handle scrolling in grid state.
|
||||||
|
final Size gridItemSize;
|
||||||
|
|
||||||
|
/// A [Size] to build the items in the correct manner and handle scrolling in list state.
|
||||||
|
final Size listItemSize;
|
||||||
|
|
||||||
|
/// A [int] of the amount of items to build.
|
||||||
|
int itemCount;
|
||||||
|
}
|
81
lib/src/animated_grid_to_list_type.dart
Normal file
81
lib/src/animated_grid_to_list_type.dart
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Iconica
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_grid_to_list/src/animated_grid_to_list_item_builder.dart';
|
||||||
|
|
||||||
|
/// An enum to which offers the possibilties the [AnimatedGridToList] can be rendered in.
|
||||||
|
enum GridToListType {
|
||||||
|
/// [grid] type of [GridToListType] which build a grid for [AnimatedGridToList]
|
||||||
|
grid,
|
||||||
|
|
||||||
|
/// [list] type of [GridToListType] which build a grid for [AnimatedGridToList]
|
||||||
|
list,
|
||||||
|
}
|
||||||
|
|
||||||
|
extension NextType on GridToListType {
|
||||||
|
/// Extenstion method on [GridToListType] which can be called to return the next value.
|
||||||
|
get next =>
|
||||||
|
this == GridToListType.grid ? GridToListType.list : GridToListType.grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Render on GridToListType {
|
||||||
|
Widget render({
|
||||||
|
required WrapAlignment wrapAlignment,
|
||||||
|
required double spacing,
|
||||||
|
required double? boxWidth,
|
||||||
|
required double? boxHeight,
|
||||||
|
required int? tappedItem,
|
||||||
|
required AnimatedGridToListItemBuilder itemBuilder,
|
||||||
|
required Function(int)? onTap,
|
||||||
|
required BuildContext context,
|
||||||
|
required bool isAnimating,
|
||||||
|
}) {
|
||||||
|
return !isAnimating
|
||||||
|
? Wrap(
|
||||||
|
alignment: WrapAlignment.center,
|
||||||
|
spacing: spacing,
|
||||||
|
children: [
|
||||||
|
for (int i = 0; i < itemBuilder.itemCount; i++) ...[
|
||||||
|
Opacity(
|
||||||
|
opacity: isAnimating && tappedItem == i
|
||||||
|
? 1
|
||||||
|
: isAnimating && tappedItem != i
|
||||||
|
? 0
|
||||||
|
: 1,
|
||||||
|
child: this == GridToListType.grid
|
||||||
|
? SizedBox(
|
||||||
|
width: boxWidth ?? itemBuilder.gridItemSize.width,
|
||||||
|
height: boxHeight ?? itemBuilder.gridItemSize.height,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => onTap?.call(i),
|
||||||
|
child: itemBuilder.gridItemBuilder(context, i),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: SizedBox(
|
||||||
|
width: boxWidth ?? itemBuilder.listItemSize.width,
|
||||||
|
height: null,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => onTap?.call(i),
|
||||||
|
child: itemBuilder.listItemBuilder(context, i),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: Align(
|
||||||
|
alignment: tappedItem != itemBuilder.itemCount - 1
|
||||||
|
? Alignment.topCenter
|
||||||
|
: Alignment.bottomCenter,
|
||||||
|
child: SizedBox(
|
||||||
|
width: boxWidth ?? itemBuilder.listItemSize.width,
|
||||||
|
height: boxHeight ?? itemBuilder.listItemSize.height,
|
||||||
|
child: GestureDetector(
|
||||||
|
child: itemBuilder.listItemBuilder(context, tappedItem!),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
188
pubspec.lock
Normal file
188
pubspec.lock
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
# Generated by pub
|
||||||
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
|
packages:
|
||||||
|
async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: async
|
||||||
|
sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.10.0"
|
||||||
|
boolean_selector:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: boolean_selector
|
||||||
|
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
|
characters:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: characters
|
||||||
|
sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
|
clock:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: clock
|
||||||
|
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
|
collection:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: collection
|
||||||
|
sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.17.0"
|
||||||
|
cupertino_icons:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: cupertino_icons
|
||||||
|
sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.5"
|
||||||
|
fake_async:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fake_async
|
||||||
|
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.1"
|
||||||
|
flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
flutter_lints:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description:
|
||||||
|
name: flutter_lints
|
||||||
|
sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.1"
|
||||||
|
flutter_test:
|
||||||
|
dependency: "direct dev"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
js:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: js
|
||||||
|
sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.5"
|
||||||
|
lints:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: lints
|
||||||
|
sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.1"
|
||||||
|
matcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: matcher
|
||||||
|
sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.12.13"
|
||||||
|
material_color_utilities:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: material_color_utilities
|
||||||
|
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.0"
|
||||||
|
meta:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: meta
|
||||||
|
sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.8.0"
|
||||||
|
path:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path
|
||||||
|
sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.8.2"
|
||||||
|
sky_engine:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.99"
|
||||||
|
source_span:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_span
|
||||||
|
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.9.1"
|
||||||
|
stack_trace:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stack_trace
|
||||||
|
sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.11.0"
|
||||||
|
stream_channel:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: stream_channel
|
||||||
|
sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
|
string_scanner:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: string_scanner
|
||||||
|
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.0"
|
||||||
|
term_glyph:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: term_glyph
|
||||||
|
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
|
test_api:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: test_api
|
||||||
|
sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.4.16"
|
||||||
|
vector_math:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vector_math
|
||||||
|
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.4"
|
||||||
|
sdks:
|
||||||
|
dart: ">=2.19.3 <3.0.0"
|
21
pubspec.yaml
Normal file
21
pubspec.yaml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
name: flutter_grid_to_list
|
||||||
|
description: A new Flutter project.
|
||||||
|
|
||||||
|
publish_to: "none"
|
||||||
|
version: 1.0.0+1
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ">=2.19.3 <3.0.0"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
cupertino_icons: ^1.0.2
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_lints: ^2.0.0
|
||||||
|
|
||||||
|
flutter:
|
||||||
|
uses-material-design: true
|
Loading…
Reference in a new issue