Repository: CHB61/multi_select_flutter
Branch: master
Commit: 24a11cf4ea56
Files: 59
Total size: 168.5 KB
Directory structure:
gitextract_bazogzq2/
├── .gitignore
├── .metadata
├── CHANGELOG.md
├── LICENSE
├── README.md
├── analysis_options.yaml
├── example/
│ ├── .gitignore
│ ├── .metadata
│ ├── README.md
│ ├── android/
│ │ ├── .gitignore
│ │ ├── app/
│ │ │ ├── build.gradle
│ │ │ └── src/
│ │ │ ├── debug/
│ │ │ │ └── AndroidManifest.xml
│ │ │ ├── main/
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ ├── kotlin/
│ │ │ │ │ └── com/
│ │ │ │ │ └── example/
│ │ │ │ │ └── example/
│ │ │ │ │ └── MainActivity.kt
│ │ │ │ └── res/
│ │ │ │ ├── drawable/
│ │ │ │ │ └── launch_background.xml
│ │ │ │ └── values/
│ │ │ │ └── styles.xml
│ │ │ └── profile/
│ │ │ └── AndroidManifest.xml
│ │ ├── build.gradle
│ │ ├── gradle/
│ │ │ └── wrapper/
│ │ │ └── gradle-wrapper.properties
│ │ ├── gradle.properties
│ │ └── settings.gradle
│ ├── ios/
│ │ ├── .gitignore
│ │ ├── Flutter/
│ │ │ ├── AppFrameworkInfo.plist
│ │ │ ├── Debug.xcconfig
│ │ │ └── Release.xcconfig
│ │ ├── Runner/
│ │ │ ├── AppDelegate.swift
│ │ │ ├── Assets.xcassets/
│ │ │ │ ├── AppIcon.appiconset/
│ │ │ │ │ └── Contents.json
│ │ │ │ └── LaunchImage.imageset/
│ │ │ │ ├── Contents.json
│ │ │ │ └── README.md
│ │ │ ├── Base.lproj/
│ │ │ │ ├── LaunchScreen.storyboard
│ │ │ │ └── Main.storyboard
│ │ │ ├── Info.plist
│ │ │ └── Runner-Bridging-Header.h
│ │ ├── Runner.xcodeproj/
│ │ │ ├── project.pbxproj
│ │ │ ├── project.xcworkspace/
│ │ │ │ ├── contents.xcworkspacedata
│ │ │ │ └── xcshareddata/
│ │ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ │ └── WorkspaceSettings.xcsettings
│ │ │ └── xcshareddata/
│ │ │ └── xcschemes/
│ │ │ └── Runner.xcscheme
│ │ └── Runner.xcworkspace/
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata/
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
│ ├── lib/
│ │ └── main.dart
│ ├── pubspec.yaml
│ ├── test/
│ │ └── widget_test.dart
│ └── web/
│ ├── index.html
│ └── manifest.json
├── lib/
│ ├── bottom_sheet/
│ │ ├── multi_select_bottom_sheet.dart
│ │ └── multi_select_bottom_sheet_field.dart
│ ├── chip_display/
│ │ └── multi_select_chip_display.dart
│ ├── chip_field/
│ │ └── multi_select_chip_field.dart
│ ├── dialog/
│ │ ├── mult_select_dialog.dart
│ │ └── multi_select_dialog_field.dart
│ ├── multi_select_flutter.dart
│ └── util/
│ ├── horizontal_scrollbar.dart
│ ├── multi_select_actions.dart
│ ├── multi_select_item.dart
│ └── multi_select_list_type.dart
├── pubspec.yaml
└── test/
└── multi_select_flutter_test.dart
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# 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/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
build/
# Android related
**/android/**/gradle-wrapper.jar
**/android/.gradle
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Flutter.podspec
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/Flutter/flutter_export_environment.sh
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
================================================
FILE: .metadata
================================================
# 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 and should not be manually edited.
version:
revision: b041144f833e05cf463b8887fa12efdec9493488
channel: stable
project_type: package
================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file.
## [4.1.3] - 2022-11-27
### Changed
- make initialValue non-nullable
- add didUpdateWidget that sets the selected items if initialValue changed
- add isDismissable parameter
- fixed 'items still selected after cancel'
## [4.1.2] - 2022-03-14
### Changed
- Merged [PR #87](https://github.com/CHB61/multi_select_flutter/pull/87). Thanks @benyaminbeyzaie
## [4.1.1] - 2022-03-14
### Changed
- Created a bug in 4.0.0 by removing the 'collection' dependency. It's only used to call firstWhereOrNull in _buildInheritedChipDisplay in both MultiSelectDialogField and MultiSelectBottomSheetField. This function is meant to automatically build the chips, but it may be changed or removed in the future as I don't think it is really necessary, and has caused some issues. For now, the package still depends on collection.
## [4.1.0] - 2022-03-14
### Changed
- BREAKING: re-named the 'width' and 'height' fields to dialogWidth and dialogHeight.
- Allow to adjust the width of the dialog.
### Added
- param 'separateSelectedItems' which can be used with LIST type only.
## [4.0.0] - 2021-03-20
### Changed
- Added null safety. Thanks to @ihancock for taking the initiative to apply this.
## [3.1.8] - 2021-02-15
### Fixed
- [Slow with big list](https://github.com/CHB61/multi_select_flutter/issues/18). Using a ListView.builder solves this but haven't found a way to use ListView.builder when the listType is set to MultiSelectListType.CHIP. Currently when this is set, the widget renders a Wrap inside of a SingleChildScrollView. For now, if you have a big list, do not use `MultiSelectListType.CHIP`.
## [3.1.7] - 2021-02-14
### Changed
- title param type from Text to Widget
### Fixed
- Missing generic type `<V>` on MultiSelectChipDisplay declarations
- MultiSelectDialogField.dart line 33 in MultiSelectDialogField
- MultiSelectDialogField.dart line 181 in _MultiSelectDialogFieldView
- MultiSelectBottomSheetField.dart line 69 in MultiSelectBottomSheetField
- MultiSelectBottomSheetField.dart line 209 _MultiSelectBottomSheetFieldView
This would cause a type error if you were to specify the type of a MultiSelectChipDisplay that's part of a MultiSelectDialogField or MultiSelectBottomSheetField.
```dart
MultiSelectBottomSheetField(
onConfirm: (values) {
_selectedAnimals = values;
},
items: _items,
chipDisplay: MultiSelectChipDisplay<Animal>( // supplying the runtime type here will cause error
onTap: (item) {
_selectedAnimals.remove(item);
return _selectedAnimals;
},
),
)
```
The type being supplied plus returning a List<Animal> would mean that the onTap function is now `List<Animal> Function(Animal)`
However, since the declaration of the chipDisplay was missing the type (ex. MultiSelectChipDisplay chipDisplay;), it is expecting onTap to be
`dynamic Function(dynamic)`.
## [3.1.6] - 2020-12-05
### Changed
- Autofocus search textfield when search icon is tapped.
## [3.1.5] - 2020-12-01
### Fixed
- Readme tables
## [3.1.4] - 2020-11-30
### Added
- A constructor MultiSelectChipDisplay.none() which disables the default chipDisplay that is part of MultiSelectDialogField and
MultiSelectBottomSheetField.
### Fixed
- When using a MultiSelectDialogField with pre-selected values, can't remove from the chipDisplay until after first confirm.
- The onConfirm function provides a reference to the list of selected values that MultiSelectDialogField uses.
- If you have pre-selected values set, you can now simply return the list of updated values in the onTap of the
MultiSelectChipDisplay.
## [3.1.3] - 2020-10-09
### Added
- param `chipWidth` for MultiSelectChipDisplay and MultiSelectChipField. When this is set, overflowing text will show ellipses.
## [3.1.2] - 2020-10-09
### Added
- param `scroll`, `scrollBar`, `height` for MultiSelectChipDisplay.
## [3.1.1] - 2020-10-08
### Added
- param `showHeader` for MultiSelectChipField allowing it to be completely removed if so desired.
## [3.1.0] - 2020-10-04
### Fixed
- `type Color is not a subtype of type bool` when selecting item in MultiSelectBottomSheet
- These errors appear in cases that use conditional expressions like this: `widget.selectedColor ?? widget.var != null ? widget.var.func() : widget.otherVar`.
- Show selected chips in MultiSelectChipDisplay when `initialValue` is set
- Previously if you set the initialValue on a MultiSelectBottomSheetField, it would show the selected items in the bottomsheet
but not in the chipDisplay.
### Added
- param `checkColor` for dialog and bottomsheet widgets.
### Changed
- BREAKING: Replace `chipColor` with `unselectedColor` for dialog and bottomsheet widgets. Apply the color to unselected checkbox border or the
unselected chip body depending which `listType` is used.
- BREAKING: Replace deprecated `autovalidate` with `autovalidateMode` on FormField widgets
## [3.0.1] - 2020-09-28
### Fixed
- `type List<dynamic> is not a subtype of type bool` when using chipDisplay param on MultiSelectBottomSheet.
## [3.0.0] - 2020-09-27
### Added
- MultiSelectChipField
- Similar to MultiSelectChipDisplay except that it is the primary
interface to select items
- supports scroll or wrap, autoscroll, formfield features, searchable, custom items
### Changed
- MultiSelectDialogField and MultiSelectBottomSheetField now come with a default `chipDisplay`.
- User no longer needs `chipDisplay` param to have a MultiSelectChipDisplay, a MultiSelectDialogField with only `items` specified is enough.
- User can override or remove it. To remove it, override with MultiSelectChipDisplay(items: [])
- User no longer needs to specify `items` on MultiSelectChipDisplay when using it as a chipDisplay param
- Combine Field/FormField widgets
- Instead of having 2 widgets e.g. MultiSelectDialogField and MultiSelectDialogFormField,
move the features of MultiSelectDialogFormField into MultiSelectDialogField.
- Made MultiSelectChipDisplay stateless
- Renamed searchPlaceholder to searchHint
- Removed opacity param from MultiSelectChipDisplay.
## [2.3.0] - 2020-09-14
### Fixed
- An error was being produced when using the `icon` param in MultiSelectChipDisplay with no `colorator` applied.
- When no `title` is provided in MultiSelectDialogField, default title of Text("Select") should be provided, not a String of "Select"
### Changed
- BREAKING: Removed `chipOpacity` from MultiSelectDialog and MultiSelectBottomSheet widgets.
- can simply set the opacity along with the color.
### Added
- Param `selectedItemsTextStyle` that applies to dialog / bottomsheet, list and chip.
## [2.2.0] - 2020-08-31
### Added
A number of parameters that allow more customizations.
- `backgroundColor`
- `chipColor`
- `chipOpacity`
- `searchIcon`, `closeSearchIcon`
- `itemsTextStyle`, `searchTextStyle`, `searchHintStyle`
- `icon` and `shape` for MultiSelectChipDisplay
## [2.1.1] - 2020-08-27
### Changed
- When colorator is applied to Field or FormField, apply the same colorator to the chipDisplay if there is one.
Previously, when using a MultiSelectDialogField with a chipDisplay and you wanted the same effect both within
the dialog and on the chipDisplay, you would have to define the colorator for both widgets. This is repetitive
and not ideal. Now, the MultiSelectChipDisplay inherits the colorator from the parent field, and can still
override that with the use of its own colorator.
## [2.1.0] - 2020-08-24
### Added
- `colorator` param for all widgets. Set the color of individual items based on their value.
- works like FormField's validator
- takes a function in which you compare the value
- return a color based on the value
- return null if no conditions satisfied
- applies to selected chips and checkboxes
- `opacity` param for MultiSelectChipDisplay
### Changed
- MultiSelectChipDisplay's `chipColor` param now automatically sets the opacity to 0.33 and sets the text to the same color but with full opacity.
- previously you would have to set the chipColor to `Colors.blue.withOpacity(0.33)`
and textStyle to `TextStyle(Colors.blue)` to achieve this simple look.
- you can still override this if you want different colored text by using the `textStyle` param
- and you can override the default 0.33 opacity of the chip body with the new `opacity` param
- set the color of the FormField widgets bottom border to `selectedColor` if there is one
- set the color of confirm/cancel buttons to `selectedColor` if there is one
## [2.0.3] - 2020-08-22
### Added
- parameter `searchPlaceholder` to replace the default "Search" text.
### Fixed
- `selectedColor` wasn't being passed to MultiSelectDialog when using MultiSelectDialogField or MultiSelectDialogFormField
## [2.0.2] - 2020-08-17
### Added
- dartdoc comments
## [2.0.1] - 2020-08-17
### Added
- `selectedColor` param which controls the color of the selected checkbox / chips within a dialog / bottomsheet
- `height` param to MultiSelectDialog widgets.
### Changed
- Set the default color of the confirmText and cancelText to primary for BottomSheet widgets.
### Fixed
- onSelectionChanged wasn't being called for all widgets.
## [2.0.0] - 2020-08-16
### Added
- `MultiSelectBottomSheet`, `MultiSelectBottomSheetField`, `MultiSelectBottomSheetFormField`
- `barrierColor` param to MultiSelectDialog
### Changed
- The addition of the MultiSelectBottomSheet widgets prompted a bit of a re-write in order to de-couple widgets. Didn't want the generic MultiSelectField to be responsible for both types (dialog, bottomsheet). The new structure makes more sense and is easier to work with.
- MultiSelectField replaced with MultiSelectDialogField / MultiSelectBottomSheetField.
- MultiSelectListDialog and MultiSelectChipDialog have been replaced with MultiSelectDialog.
- dialogType replaced with listType, now accepts MultiSelectListType instead of MultiSelectDialogType.
- Improved the look of validator text, matches the look of TextFormField validator style much more closely.
- buttonText, confirmText, cancelText all now accept a Text widget instead of a string, allowing the removal of the textStyle param.
## [1.0.6] - 2020-08-14
### Fixed
- Implement scrolling in MultiSelectChipDialog.
- When the source list was large enough, the chip dialog would just overflow but it is now scrollable.
### Changed
- `title` is no longer required for List / Chip dialogs, default to "Select".
- `initialSelectedItems` is now required for List / Chip dialogs.
- `items` is now required for MultiSelectChipDisplay.
- Removed iconSize - can set icon size as param of Icon.
- Removed `state` as a parameter of MultiSelectField and created a constructor `MultiSelectField.withState()` that gets called by MultiSelectFormField.
- `state` never needs to be set by the user, and would do nothing if set explicitly, so it shouldn't be a named param on the default constructor.
- Updated docs
## [1.0.5] - 2020-07-10
### Fixed
- A bug was introduced in version 1.0.4 that caused the MultiSelectFormField to not highlight any of the selected values in the dialog.
### Added
- Boolean parameter 'searchable'. Useful for larger lists, the searchable parameter enables a search icon in the dialog which shows a search bar that lets you query the list.
- String parameters for 'confirmText' and 'cancelText'. This is important for users who want text other than 'OK' and 'CANCEL', especially for other languages and alphabets.
## [1.0.4] - 2020-07-06
### Changed
- Allow user to set initial selected values for MultiSelectField and MultiSelectFormField by adding param 'initialValue' to MultiSelectField, and enabled the same functionality for MultiSelectFormField by passing its existing 'initialValue' param to the MultiSelectField's new 'initialValue' param.
- MultiSelectFormField creates a MultiSelectField which never had an 'initialValue' param that could be set by the user. Even if 'initialValue' was set on the MultiSelectFormField, it never got passed to the MultiSelectField that it creates. Now it does.
- Previously when using a MultiSelectField or FormField, the values in the MultiSelectListDialog / MultiSelectChipDialog were only being stored internally when a user confirms the values. Now the initial values can be set before any user interaction has occurred.
- Another use case is when a MultiSelectField is being re-inserted into the widget tree (such as one inside of a PersistentBottomSheet), and if the developer wants the previously selected values to remain after the bottomsheet re-opens, they can use this parameter to achieve that.
- Updated example app
## [1.0.3] - 2020-06-21
### Changed
- Updated pubspec.yaml project description and added homepage link
## [1.0.2] - 2020-06-20
### Added
- Example project
- analysis_options.yaml
## [1.0.1] - 2020-06-20
### Changed
- Added readme
## [1.0.0] - 2020-06-20
### Added
- Creation of MultiSelect package
- Widgets include MultiSelectListDialog, MultiSelectChipDialog, MultiSelectChipDisplay, MultiSelectField, MultiSelectFormField.
================================================
FILE: LICENSE
================================================
Copyright (c) 2020, Chris Botha
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: README.md
================================================
# Multi Select Flutter
[](https://pub.dev/packages/multi_select_flutter)
Multi Select Flutter is a package for creating multi-select widgets in a variety of ways.
| <img src="https://i.imgur.com/12ba2NO.gif" width="250"/><br /><sub><b>Dialog</b></sub> | <img src="https://i.imgur.com/NqldWcV.gif" width="250"/><br /><sub><b>BottomSheet</b></sub> | <img src="https://i.imgur.com/m7FzBIW.gif" width="250"/><br /><sub><b>ChoiceChip</b></sub> |
| :---: | :---: | :---: |
## Features
- Supports FormField features like validator.
- Neutral default design.
- Dialog, BottomSheet, or ChoiceChip style widgets.
- Make your multi select `searchable` for larger lists.
## Usage
### MultiSelectDialogField / MultiSelectBottomSheetField
<img src="https://i.imgur.com/JoTYWce.png" /><img src="https://i.imgur.com/Co8fhrD.png" />
These widgets provide an InkWell button which open the dialog or bottom sheet and are equipped with FormField features. You can customize it using the provided parameters.
To store the selected values, you can use the `onConfirm` parameter. You could also use `onSelectionChanged` for this.
By default these widgets render a MultiSelectChipDisplay below the field. This can be overridden with the `chipDisplay` parameter or removed completely by using `chipDisplay: MultiSelectChipDisplay.none()`.
```dart
MultiSelectDialogField(
items: _animals.map((e) => MultiSelectItem(e, e.name)).toList(),
listType: MultiSelectListType.CHIP,
onConfirm: (values) {
_selectedAnimals = values;
},
),
```
### MultiSelectDialog / MultiSelectBottomSheet
<img src="https://i.imgur.com/XeuzNtQ.png" height="250" /> <img src="https://i.imgur.com/AwVOr54.png" height="200" />
If you prefer to create your own button for opening the dialog or bottom sheet, you may do so and then make a call to a function like this:
`MultiSelectDialog` can be used in the builder of `showDialog()`.
```dart
void _showMultiSelect(BuildContext context) async {
await showDialog(
context: context,
builder: (ctx) {
return MultiSelectDialog(
items: _items,
initialValue: _selectedAnimals,
onConfirm: (values) {...},
);
},
);
}
```
`MultiSelectBottomSheet` can be used in the builder of `showModalBottomSheet()`.
```dart
void _showMultiSelect(BuildContext context) async {
await showModalBottomSheet(
isScrollControlled: true, // required for min/max child size
context: context,
builder: (ctx) {
return MultiSelectBottomSheet(
items: _items,
initialValue: _selectedAnimals,
onConfirm: (values) {...},
maxChildSize: 0.8,
);
},
);
}
```
### MultiSelectChipDisplay
<img src="https://i.imgur.com/hMbuSxk.png" width="300" />
To display the selected items, this widget can be used alongside your own button or it can be specified as a `chipDisplay` parameter of widgets like `MultiSelectDialogField`.
You can also remove items from the source list in the onTap function.
```dart
MultiSelectChipDisplay(
items: _selectedAnimals.map((e) => MultiSelectItem(e, e)).toList(),
onTap: (value) {
setState(() {
_selectedAnimals.remove(value);
});
},
),
```
A MultiSelectChipDisplay that is part of a MultiSelectDialogField still renders outside the BoxDecoration of the MultiSelectDialogField as seen here:
<img src="https://imgur.com/RuluMpS.png" alt="chipDisplay"/>
If you want to encapsulate the MultiSelectChipDisplay, wrap the MultiSelectDialogField in a Container and apply the decoration to that instead:
```dart
Container(
decoration: BoxDecoration(...),
child: MultiSelectDialogField(
items: _items,
chipDisplay: MultiSelectChipDisplay(...),
),
),
```
<img src="https://imgur.com/EcCyly4.png" alt="chipDisplay"/>
### MultiSelectChipField
<img src="https://i.imgur.com/KDmtpmV.png" alt="chipField"/>
This widget is similar to MultiSelectChipDisplay, except these chips are the primary interface for selecting items.
```dart
MultiSelectChipField<Animal>(
items: _items,
icon: Icon(Icons.check),
onTap: (values) {
_selectedAnimals = values;
},
),
```
#### Using `itemBuilder` to create custom items:
```dart
MultiSelectChipField<Animal>(
items: _items,
key: _multiSelectKey,
validator: (values) {...}
itemBuilder: (item, state) {
// return your custom widget here
return InkWell(
onTap: () {
_selectedAnimals.contains(item.value)
? _selectedAnimals.remove(item.value)
: _selectedAnimals.add(item.value);
state.didChange(_selectedAnimals);
_multiSelectKey.currentState.validate();
},
child: Text(item.value.name),
);
},
),
```
The `itemBuilder` param takes a function that will create a widget for each of the provided `items`.
In order to use validator and other FormField features with custom widgets, you must call `state.didChange(_updatedList)` any time the list of selected items is updated.
#### Using `scrollControl` to auto scroll:
```dart
MultiSelectChipField(
items: _items,
scrollControl: (controller) {
_startAnimation(controller);
},
)
// waits 5 seconds, scrolls to end slow, then back fast
void _startAnimation(ScrollController controller) {
// when using more than one animation, use async/await
Future.delayed(const Duration(milliseconds: 5000), () async {
await controller.animateTo(
controller.position.maxScrollExtent,
duration: Duration(milliseconds: 8000),
curve: Curves.linear);
await controller.animateTo(
controller.position.minScrollExtent,
duration: Duration(milliseconds: 1250),
curve: Curves.fastLinearToSlowEaseIn);
});
}
```
## Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
================================================
FILE: analysis_options.yaml
================================================
analyzer:
exclude: [build/**]
strong-mode:
implicit-casts: true
linter:
rules:
- camel_case_types
================================================
FILE: example/.gitignore
================================================
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# 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/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Exceptions to above rules.
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
================================================
FILE: example/.metadata
================================================
# 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 and should not be manually edited.
version:
revision: b041144f833e05cf463b8887fa12efdec9493488
channel: stable
project_type: app
================================================
FILE: example/README.md
================================================
# example
Example for multi_select_flutter package.
Check out some different ways to implement multi_select_flutter.
================================================
FILE: example/android/.gitignore
================================================
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
================================================
FILE: example/android/app/build.gradle
================================================
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 28
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.example"
minSdkVersion 16
targetSdkVersion 28
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
================================================
FILE: example/android/app/src/debug/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.example">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
================================================
FILE: example/android/app/src/main/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.example">
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application
android:name="${applicationName}"
android:label="example"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<!-- Displays an Android View that continues showing the launch screen
Drawable until Flutter paints its first frame, then this splash
screen fades out. A splash screen is useful to avoid any visual
gap between the end of Android's launch screen and the painting of
Flutter's first frame. -->
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
================================================
FILE: example/android/app/src/main/kotlin/com/example/example/MainActivity.kt
================================================
package com.example.example
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}
================================================
FILE: example/android/app/src/main/res/drawable/launch_background.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
================================================
FILE: example/android/app/src/main/res/values/styles.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">@android:color/white</item>
</style>
</resources>
================================================
FILE: example/android/app/src/profile/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.example">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
================================================
FILE: example/android/build.gradle
================================================
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
jcenter()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: example/android/gradle/wrapper/gradle-wrapper.properties
================================================
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
================================================
FILE: example/android/gradle.properties
================================================
org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true
================================================
FILE: example/android/settings.gradle
================================================
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
include ':app'
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
================================================
FILE: example/ios/.gitignore
================================================
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3
================================================
FILE: example/ios/Flutter/AppFrameworkInfo.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>8.0</string>
</dict>
</plist>
================================================
FILE: example/ios/Flutter/Debug.xcconfig
================================================
#include "Generated.xcconfig"
================================================
FILE: example/ios/Flutter/Release.xcconfig
================================================
#include "Generated.xcconfig"
================================================
FILE: example/ios/Runner/AppDelegate.swift
================================================
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
================================================
FILE: example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
================================================
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
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.
================================================
FILE: example/ios/Runner/Base.lproj/LaunchScreen.storyboard
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>
================================================
FILE: example/ios/Runner/Base.lproj/Main.storyboard
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
================================================
FILE: example/ios/Runner/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>example</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>
================================================
FILE: example/ios/Runner/Runner-Bridging-Header.h
================================================
#import "GeneratedPluginRegistrant.h"
================================================
FILE: example/ios/Runner.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
97C146F11CF9000F007C117D /* Supporting Files */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
97C146F11CF9000F007C117D /* Supporting Files */ = {
isa = PBXGroup;
children = (
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
================================================
FILE: example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>
================================================
FILE: example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
================================================
FILE: example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
================================================
FILE: example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
================================================
FILE: example/ios/Runner.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>
================================================
FILE: example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
================================================
FILE: example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
================================================
FILE: example/lib/main.dart
================================================
import 'package:flutter/material.dart';
import 'package:multi_select_flutter/multi_select_flutter.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Multi Select',
theme: ThemeData(
primarySwatch: Colors.purple,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Multi Select'),
);
}
}
class Animal {
final int id;
final String name;
Animal({
this.id,
this.name,
});
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
static List<Animal> _animals = [
Animal(id: 1, name: "Lion"),
Animal(id: 2, name: "Flamingo"),
Animal(id: 3, name: "Hippo"),
Animal(id: 4, name: "Horse"),
Animal(id: 5, name: "Tiger"),
Animal(id: 6, name: "Penguin"),
Animal(id: 7, name: "Spider"),
Animal(id: 8, name: "Snake"),
Animal(id: 9, name: "Bear"),
Animal(id: 10, name: "Beaver"),
Animal(id: 11, name: "Cat"),
Animal(id: 12, name: "Fish"),
Animal(id: 13, name: "Rabbit"),
Animal(id: 14, name: "Mouse"),
Animal(id: 15, name: "Dog"),
Animal(id: 16, name: "Zebra"),
Animal(id: 17, name: "Cow"),
Animal(id: 18, name: "Frog"),
Animal(id: 19, name: "Blue Jay"),
Animal(id: 20, name: "Moose"),
Animal(id: 21, name: "Gecko"),
Animal(id: 22, name: "Kangaroo"),
Animal(id: 23, name: "Shark"),
Animal(id: 24, name: "Crocodile"),
Animal(id: 25, name: "Owl"),
Animal(id: 26, name: "Dragonfly"),
Animal(id: 27, name: "Dolphin"),
];
final _items = _animals
.map((animal) => MultiSelectItem<Animal>(animal, animal.name))
.toList();
//List<Animal> _selectedAnimals = [];
List<Animal> _selectedAnimals2 = [];
List<Animal> _selectedAnimals3 = [];
//List<Animal> _selectedAnimals4 = [];
List<Animal> _selectedAnimals5 = [];
final _multiSelectKey = GlobalKey<FormFieldState>();
@override
void initState() {
_selectedAnimals5 = _animals;
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: SingleChildScrollView(
child: Container(
alignment: Alignment.center,
padding: EdgeInsets.all(20),
child: Column(
children: <Widget>[
SizedBox(height: 40),
//################################################################################################
// Rounded blue MultiSelectDialogField
//################################################################################################
MultiSelectDialogField(
items: _items,
title: Text("Animals"),
selectedColor: Colors.blue,
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.1),
borderRadius: BorderRadius.all(Radius.circular(40)),
border: Border.all(
color: Colors.blue,
width: 2,
),
),
buttonIcon: Icon(
Icons.pets,
color: Colors.blue,
),
buttonText: Text(
"Favorite Animals",
style: TextStyle(
color: Colors.blue[800],
fontSize: 16,
),
),
onConfirm: (results) {
//_selectedAnimals = results;
},
),
SizedBox(height: 50),
//################################################################################################
// This MultiSelectBottomSheetField has no decoration, but is instead wrapped in a Container that has
// decoration applied. This allows the ChipDisplay to render inside the same Container.
//################################################################################################
Container(
decoration: BoxDecoration(
color: Theme.of(context).primaryColor.withOpacity(.4),
border: Border.all(
color: Theme.of(context).primaryColor,
width: 2,
),
),
child: Column(
children: <Widget>[
MultiSelectBottomSheetField(
initialChildSize: 0.4,
listType: MultiSelectListType.CHIP,
searchable: true,
buttonText: Text("Favorite Animals"),
title: Text("Animals"),
items: _items,
onConfirm: (values) {
_selectedAnimals2 = values;
},
chipDisplay: MultiSelectChipDisplay(
onTap: (value) {
setState(() {
_selectedAnimals2.remove(value);
});
},
),
),
_selectedAnimals2 == null || _selectedAnimals2.isEmpty
? Container(
padding: EdgeInsets.all(10),
alignment: Alignment.centerLeft,
child: Text(
"None selected",
style: TextStyle(color: Colors.black54),
))
: Container(),
],
),
),
SizedBox(height: 40),
//################################################################################################
// MultiSelectBottomSheetField with validators
//################################################################################################
MultiSelectBottomSheetField<Animal>(
key: _multiSelectKey,
initialChildSize: 0.7,
maxChildSize: 0.95,
title: Text("Animals"),
buttonText: Text("Favorite Animals"),
items: _items,
searchable: true,
validator: (values) {
if (values == null || values.isEmpty) {
return "Required";
}
List<String> names = values.map((e) => e.name).toList();
if (names.contains("Frog")) {
return "Frogs are weird!";
}
return null;
},
onConfirm: (values) {
setState(() {
_selectedAnimals3 = values;
});
_multiSelectKey.currentState.validate();
},
chipDisplay: MultiSelectChipDisplay(
onTap: (item) {
setState(() {
_selectedAnimals3.remove(item);
});
_multiSelectKey.currentState.validate();
},
),
),
SizedBox(height: 40),
//################################################################################################
// MultiSelectChipField
//################################################################################################
MultiSelectChipField(
items: _items,
initialValue: [_animals[4], _animals[7], _animals[9]],
title: Text("Animals"),
headerColor: Colors.blue.withOpacity(0.5),
decoration: BoxDecoration(
border: Border.all(color: Colors.blue[700], width: 1.8),
),
selectedChipColor: Colors.blue.withOpacity(0.5),
selectedTextStyle: TextStyle(color: Colors.blue[800]),
onTap: (values) {
//_selectedAnimals4 = values;
},
),
SizedBox(height: 40),
//################################################################################################
// MultiSelectDialogField with initial values
//################################################################################################
MultiSelectDialogField(
onConfirm: (val) {
_selectedAnimals5 = val;
},
dialogWidth: MediaQuery.of(context).size.width * 0.7,
items: _items,
initialValue:
_selectedAnimals5, // setting the value of this in initState() to pre-select values.
),
],
),
),
),
);
}
}
================================================
FILE: example/pubspec.yaml
================================================
name: example
description: A new Flutter project.
# The following line prevents the package from being accidentally published to
# pub.dev using `pub publish`. This is preferred for private packages.
publish_to: "none" # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
sdk: ">=2.7.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
multi_select_flutter:
path: ../
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.3
dev_dependencies:
flutter_test:
sdk: flutter
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages
================================================
FILE: example/test/widget_test.dart
================================================
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:example/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(MyApp());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}
================================================
FILE: example/web/index.html
================================================
<!DOCTYPE html>
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="example">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>example</title>
<link rel="manifest" href="manifest.json">
</head>
<body>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
<script>
var serviceWorkerVersion = null;
var scriptLoaded = false;
function loadMainDartJs() {
if (scriptLoaded) {
return;
}
scriptLoaded = true;
var scriptTag = document.createElement('script');
scriptTag.src = 'main.dart.js';
scriptTag.type = 'application/javascript';
document.body.append(scriptTag);
}
if ('serviceWorker' in navigator) {
// Service workers are supported. Use them.
window.addEventListener('load', function () {
// Wait for registration to finish before dropping the <script> tag.
// Otherwise, the browser will load the script multiple times,
// potentially different versions.
var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
navigator.serviceWorker.register(serviceWorkerUrl)
.then((reg) => {
function waitForActivation(serviceWorker) {
serviceWorker.addEventListener('statechange', () => {
if (serviceWorker.state == 'activated') {
console.log('Installed new service worker.');
loadMainDartJs();
}
});
}
if (!reg.active && (reg.installing || reg.waiting)) {
// No active web worker and we have installed or are installing
// one for the first time. Simply wait for it to activate.
waitForActivation(reg.installing || reg.waiting);
} else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
// When the app updates the serviceWorkerVersion changes, so we
// need to ask the service worker to update.
console.log('New service worker available.');
reg.update();
waitForActivation(reg.installing);
} else {
// Existing service worker is still good.
console.log('Loading app from service worker.');
loadMainDartJs();
}
});
// If service worker doesn't succeed in a reasonable amount of time,
// fallback to plaint <script> tag.
setTimeout(() => {
if (!scriptLoaded) {
console.warn(
'Failed to load app from service worker. Falling back to plain <script> tag.',
);
loadMainDartJs();
}
}, 4000);
});
} else {
// Service workers not supported. Just drop the <script> tag.
loadMainDartJs();
}
</script>
</body>
</html>
================================================
FILE: example/web/manifest.json
================================================
{
"name": "example",
"short_name": "example",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "A new Flutter project.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "icons/Icon-maskable-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "icons/Icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}
================================================
FILE: lib/bottom_sheet/multi_select_bottom_sheet.dart
================================================
import 'package:flutter/material.dart';
import '../util/multi_select_item.dart';
import '../util/multi_select_actions.dart';
import '../util/multi_select_list_type.dart';
/// A bottom sheet widget containing either a classic checkbox style list, or a chip style list.
class MultiSelectBottomSheet<T> extends StatefulWidget
with MultiSelectActions<T> {
/// List of items to select from.
final List<MultiSelectItem<T>> items;
/// The list of selected values before interaction.
final List<T> initialValue;
/// The text at the top of the BottomSheet.
final Widget? title;
/// Fires when the an item is selected / unselected.
final void Function(List<T>)? onSelectionChanged;
/// Fires when confirm is tapped.
final void Function(List<T>)? onConfirm;
/// Toggles search functionality.
final bool searchable;
/// Text on the confirm button.
final Text? confirmText;
/// Text on the cancel button.
final Text? cancelText;
/// An enum that determines which type of list to render.
final MultiSelectListType? listType;
/// Sets the color of the checkbox or chip when it's selected.
final Color? selectedColor;
/// Set the initial height of the BottomSheet.
final double? initialChildSize;
/// Set the minimum height threshold of the BottomSheet before it closes.
final double? minChildSize;
/// Set the maximum height of the BottomSheet.
final double? maxChildSize;
/// Set the placeholder text of the search field.
final String? searchHint;
/// A function that sets the color of selected items based on their value.
/// It will either set the chip color, or the checkbox color depending on the list type.
final Color? Function(T)? colorator;
/// Color of the chip body or checkbox border while not selected.
final Color? unselectedColor;
/// Icon button that shows the search field.
final Icon? searchIcon;
/// Icon button that hides the search field
final Icon? closeSearchIcon;
/// Style the text on the chips or list tiles.
final TextStyle? itemsTextStyle;
/// Style the text on the selected chips or list tiles.
final TextStyle? selectedItemsTextStyle;
/// Style the search text.
final TextStyle? searchTextStyle;
/// Style the search hint.
final TextStyle? searchHintStyle;
/// Moves the selected items to the top of the list.
final bool separateSelectedItems;
/// Set the color of the check in the checkbox
final Color? checkColor;
MultiSelectBottomSheet({
required this.items,
required this.initialValue,
this.title,
this.onSelectionChanged,
this.onConfirm,
this.listType,
this.cancelText,
this.confirmText,
this.searchable = false,
this.selectedColor,
this.initialChildSize,
this.minChildSize,
this.maxChildSize,
this.colorator,
this.unselectedColor,
this.searchIcon,
this.closeSearchIcon,
this.itemsTextStyle,
this.searchTextStyle,
this.searchHint,
this.searchHintStyle,
this.selectedItemsTextStyle,
this.separateSelectedItems = false,
this.checkColor,
});
@override
_MultiSelectBottomSheetState<T> createState() =>
_MultiSelectBottomSheetState<T>(items);
}
class _MultiSelectBottomSheetState<T> extends State<MultiSelectBottomSheet<T>> {
List<T> _selectedValues = [];
bool _showSearch = false;
List<MultiSelectItem<T>> _items;
_MultiSelectBottomSheetState(this._items);
@override
void initState() {
super.initState();
_selectedValues.addAll(widget.initialValue);
for (int i = 0; i < _items.length; i++) {
_items[i].selected = false;
if (_selectedValues.contains(_items[i].value)) {
_items[i].selected = true;
}
}
if (widget.separateSelectedItems) {
_items = widget.separateSelected(_items);
}
}
/// Returns a CheckboxListTile
Widget _buildListItem(MultiSelectItem<T> item) {
return Theme(
data: ThemeData(
unselectedWidgetColor: widget.unselectedColor ?? Colors.black54,
),
child: CheckboxListTile(
checkColor: widget.checkColor,
value: item.selected,
activeColor: widget.colorator != null
? widget.colorator!(item.value) ?? widget.selectedColor
: widget.selectedColor,
title: Text(
item.label,
style: item.selected
? widget.selectedItemsTextStyle
: widget.itemsTextStyle,
),
controlAffinity: ListTileControlAffinity.leading,
onChanged: (checked) {
setState(() {
_selectedValues = widget.onItemCheckedChange(
_selectedValues, item.value, checked!);
if (checked) {
item.selected = true;
} else {
item.selected = false;
}
if (widget.separateSelectedItems) {
_items = widget.separateSelected(_items);
}
});
if (widget.onSelectionChanged != null) {
widget.onSelectionChanged!(_selectedValues);
}
},
),
);
}
/// Returns a ChoiceChip
Widget _buildChipItem(MultiSelectItem<T> item) {
return Container(
padding: const EdgeInsets.all(2.0),
child: ChoiceChip(
backgroundColor: widget.unselectedColor,
selectedColor:
widget.colorator != null && widget.colorator!(item.value) != null
? widget.colorator!(item.value)
: widget.selectedColor != null
? widget.selectedColor
: Theme.of(context).primaryColor.withOpacity(0.35),
label: Text(
item.label,
style: _selectedValues.contains(item.value)
? TextStyle(
color: widget.selectedItemsTextStyle?.color ??
widget.colorator?.call(item.value) ??
widget.selectedColor?.withOpacity(1) ??
Theme.of(context).primaryColor,
fontSize: widget.selectedItemsTextStyle != null
? widget.selectedItemsTextStyle!.fontSize
: null,
)
: widget.itemsTextStyle,
),
selected: item.selected,
onSelected: (checked) {
if (checked) {
item.selected = true;
} else {
item.selected = false;
}
setState(() {
_selectedValues = widget.onItemCheckedChange(
_selectedValues, item.value, checked);
});
if (widget.onSelectionChanged != null) {
widget.onSelectionChanged!(_selectedValues);
}
},
),
);
}
@override
Widget build(BuildContext context) {
return Container(
padding:
EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: DraggableScrollableSheet(
initialChildSize: widget.initialChildSize ?? 0.3,
minChildSize: widget.minChildSize ?? 0.3,
maxChildSize: widget.maxChildSize ?? 0.6,
expand: false,
builder: (BuildContext context, ScrollController scrollController) {
return Column(
children: [
Padding(
padding: const EdgeInsets.all(10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_showSearch
? Expanded(
child: Container(
padding: const EdgeInsets.only(left: 10),
child: TextField(
autofocus: true,
style: widget.searchTextStyle,
decoration: InputDecoration(
hintStyle: widget.searchHintStyle,
hintText: widget.searchHint ?? "Search",
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: widget.selectedColor ??
Theme.of(context).primaryColor),
),
),
onChanged: (val) {
List<MultiSelectItem<T>> filteredList = [];
filteredList = widget.updateSearchQuery(
val, widget.items);
setState(() {
if (widget.separateSelectedItems) {
_items =
widget.separateSelected(filteredList);
} else {
_items = filteredList;
}
});
},
),
),
)
: widget.title ??
Text(
"Select",
style: TextStyle(fontSize: 18),
),
widget.searchable
? IconButton(
icon: _showSearch
? widget.closeSearchIcon ?? Icon(Icons.close)
: widget.searchIcon ?? Icon(Icons.search),
onPressed: () {
setState(() {
_showSearch = !_showSearch;
if (!_showSearch) {
if (widget.separateSelectedItems) {
_items =
widget.separateSelected(widget.items);
} else {
_items = widget.items;
}
}
});
},
)
: Padding(
padding: EdgeInsets.all(15),
),
],
),
),
Expanded(
child: widget.listType == null ||
widget.listType == MultiSelectListType.LIST
? ListView.builder(
controller: scrollController,
itemCount: _items.length,
itemBuilder: (context, index) {
return _buildListItem(_items[index]);
},
)
: SingleChildScrollView(
controller: scrollController,
child: Container(
padding: EdgeInsets.all(10),
child: Wrap(
children: _items.map(_buildChipItem).toList(),
),
),
),
),
Container(
padding: EdgeInsets.all(2),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: TextButton(
onPressed: () {
widget.onCancelTap(context, widget.initialValue);
},
child: widget.cancelText ??
Text(
"CANCEL",
style: TextStyle(
color: (widget.selectedColor != null &&
widget.selectedColor !=
Colors.transparent)
? widget.selectedColor!.withOpacity(1)
: Theme.of(context).primaryColor,
),
),
),
),
SizedBox(width: 10),
Expanded(
child: TextButton(
onPressed: () {
widget.onConfirmTap(
context, _selectedValues, widget.onConfirm);
},
child: widget.confirmText ??
Text(
"OK",
style: TextStyle(
color: (widget.selectedColor != null &&
widget.selectedColor !=
Colors.transparent)
? widget.selectedColor!.withOpacity(1)
: Theme.of(context).primaryColor,
),
),
),
),
],
),
),
],
);
},
),
);
}
}
================================================
FILE: lib/bottom_sheet/multi_select_bottom_sheet_field.dart
================================================
import 'package:collection/collection.dart' show IterableExtension;
import 'package:flutter/material.dart';
import '../util/multi_select_list_type.dart';
import '../chip_display/multi_select_chip_display.dart';
import '../util/multi_select_item.dart';
import 'multi_select_bottom_sheet.dart';
/// A customizable InkWell widget that opens the MultiSelectBottomSheet
// ignore: must_be_immutable
class MultiSelectBottomSheetField<V> extends FormField<List<V>> {
/// Style the Container that makes up the field.
final BoxDecoration? decoration;
/// Set text that is displayed on the button.
final Text? buttonText;
/// Specify the button icon.
final Icon? buttonIcon;
/// List of items to select from.
final List<MultiSelectItem<V>> items;
/// The list of selected values before interaction.
final List<V> initialValue;
/// The text at the top of the dialog.
final Widget? title;
/// Fires when the an item is selected / unselected.
final void Function(List<V>)? onSelectionChanged;
/// Fires when confirm is tapped.
final void Function(List<V>) onConfirm;
/// Toggles search functionality.
final bool searchable;
/// Text on the confirm button.
final Text? confirmText;
/// Text on the cancel button.
final Text? cancelText;
/// An enum that determines which type of list to render.
final MultiSelectListType? listType;
/// Sets the color of the checkbox or chip body when selected.
final Color? selectedColor;
/// Set the hint text of the search field.
final String? searchHint;
/// Set the initial height of the BottomSheet.
final double? initialChildSize;
/// Set the minimum height threshold of the BottomSheet before it closes.
final double? minChildSize;
/// Set the maximum height of the BottomSheet.
final double? maxChildSize;
/// Apply a ShapeBorder to alter the edges of the BottomSheet.
final ShapeBorder? shape;
/// Set the color of the space outside the BottomSheet.
final Color? barrierColor;
/// Overrides the default MultiSelectChipDisplay attached to this field.
/// If you want to remove it, use MultiSelectChipDisplay.none().
final MultiSelectChipDisplay<V>? chipDisplay;
/// A function that sets the color of selected items based on their value.
/// It will either set the chip color, or the checkbox color depending on the list type.
final Color Function(V)? colorator;
/// Set the background color of the bottom sheet.
final Color? backgroundColor;
/// Color of the chip body or checkbox border while not selected.
final Color? unselectedColor;
/// Replaces the deafult search icon when searchable is true.
final Icon? searchIcon;
/// Replaces the default close search icon when searchable is true.
final Icon? closeSearchIcon;
/// The TextStyle of the items within the BottomSheet.
final TextStyle? itemsTextStyle;
/// Style the text on the selected chips or list tiles.
final TextStyle? selectedItemsTextStyle;
/// Moves the selected items to the top of the list.
final bool separateSelectedItems;
/// Style the text that is typed into the search field.
final TextStyle? searchTextStyle;
/// Style the search hint.
final TextStyle? searchHintStyle;
/// Set the color of the check in the checkbox
final Color? checkColor;
/// Whether the user can dismiss the widget by tapping outside
final bool isDismissible;
final AutovalidateMode autovalidateMode;
final FormFieldValidator<List<V>>? validator;
final FormFieldSetter<List<V>>? onSaved;
final GlobalKey<FormFieldState>? key;
FormFieldState<List<V>>? state;
MultiSelectBottomSheetField({
required this.items,
required this.onConfirm,
this.title,
this.buttonText,
this.buttonIcon,
this.listType,
this.decoration,
this.onSelectionChanged,
this.chipDisplay,
this.initialValue = const [],
this.searchable = false,
this.confirmText,
this.cancelText,
this.selectedColor,
this.initialChildSize,
this.minChildSize,
this.maxChildSize,
this.shape,
this.barrierColor,
this.searchHint,
this.colorator,
this.backgroundColor,
this.unselectedColor,
this.searchIcon,
this.closeSearchIcon,
this.itemsTextStyle,
this.searchTextStyle,
this.searchHintStyle,
this.selectedItemsTextStyle,
this.separateSelectedItems = false,
this.checkColor,
this.isDismissible = true,
this.key,
this.onSaved,
this.validator,
this.autovalidateMode = AutovalidateMode.disabled,
}) : super(
key: key,
onSaved: onSaved,
validator: validator,
autovalidateMode: autovalidateMode,
initialValue: initialValue,
builder: (FormFieldState<List<V>> state) {
_MultiSelectBottomSheetFieldView view =
_MultiSelectBottomSheetFieldView<V>(
items: items,
decoration: decoration,
unselectedColor: unselectedColor,
colorator: colorator,
itemsTextStyle: itemsTextStyle,
selectedItemsTextStyle: selectedItemsTextStyle,
backgroundColor: backgroundColor,
title: title,
initialValue: initialValue,
barrierColor: barrierColor,
buttonIcon: buttonIcon,
buttonText: buttonText,
cancelText: cancelText,
chipDisplay: chipDisplay,
closeSearchIcon: closeSearchIcon,
confirmText: confirmText,
initialChildSize: initialChildSize,
listType: listType,
maxChildSize: maxChildSize,
minChildSize: minChildSize,
onConfirm: onConfirm,
onSelectionChanged: onSelectionChanged,
searchHintStyle: searchHintStyle,
searchIcon: searchIcon,
searchHint: searchHint,
searchTextStyle: searchTextStyle,
searchable: searchable,
selectedColor: selectedColor,
separateSelectedItems: separateSelectedItems,
shape: shape,
checkColor: checkColor,
isDismissible: isDismissible,
);
return _MultiSelectBottomSheetFieldView<V?>._withState(
view as _MultiSelectBottomSheetFieldView<V?>, state);
});
}
// ignore: must_be_immutable
class _MultiSelectBottomSheetFieldView<V> extends StatefulWidget {
final BoxDecoration? decoration;
final Text? buttonText;
final Icon? buttonIcon;
final List<MultiSelectItem<V>> items;
final List<V> initialValue;
final Widget? title;
final void Function(List<V>)? onSelectionChanged;
final void Function(List<V>)? onConfirm;
final bool searchable;
final Text? confirmText;
final Text? cancelText;
final MultiSelectListType? listType;
final Color? selectedColor;
final String? searchHint;
final double? initialChildSize;
final double? minChildSize;
final double? maxChildSize;
final ShapeBorder? shape;
final Color? barrierColor;
final MultiSelectChipDisplay<V>? chipDisplay;
final Color Function(V)? colorator;
final Color? backgroundColor;
final Color? unselectedColor;
final Icon? searchIcon;
final Icon? closeSearchIcon;
final TextStyle? itemsTextStyle;
final TextStyle? selectedItemsTextStyle;
final TextStyle? searchTextStyle;
final TextStyle? searchHintStyle;
final bool separateSelectedItems;
final Color? checkColor;
final bool isDismissible;
FormFieldState<List<V>>? state;
_MultiSelectBottomSheetFieldView({
required this.items,
this.title,
this.buttonText,
this.buttonIcon,
this.listType,
this.decoration,
this.onSelectionChanged,
this.onConfirm,
this.chipDisplay,
required this.initialValue,
required this.searchable,
this.confirmText,
this.cancelText,
this.selectedColor,
this.initialChildSize,
this.minChildSize,
this.maxChildSize,
this.shape,
this.barrierColor,
this.searchHint,
this.colorator,
this.backgroundColor,
this.unselectedColor,
this.searchIcon,
this.closeSearchIcon,
this.itemsTextStyle,
this.searchTextStyle,
this.searchHintStyle,
this.selectedItemsTextStyle,
required this.separateSelectedItems,
this.checkColor,
required this.isDismissible,
});
/// This constructor allows a FormFieldState to be passed in. Called by MultiSelectBottomSheetField.
_MultiSelectBottomSheetFieldView._withState(
_MultiSelectBottomSheetFieldView<V> field, FormFieldState<List<V>> state)
: items = field.items,
title = field.title,
buttonText = field.buttonText,
buttonIcon = field.buttonIcon,
listType = field.listType,
decoration = field.decoration,
onSelectionChanged = field.onSelectionChanged,
onConfirm = field.onConfirm,
chipDisplay = field.chipDisplay,
initialValue = field.initialValue,
searchable = field.searchable,
confirmText = field.confirmText,
cancelText = field.cancelText,
selectedColor = field.selectedColor,
initialChildSize = field.initialChildSize,
minChildSize = field.minChildSize,
maxChildSize = field.maxChildSize,
shape = field.shape,
barrierColor = field.barrierColor,
searchHint = field.searchHint,
colorator = field.colorator,
backgroundColor = field.backgroundColor,
unselectedColor = field.unselectedColor,
searchIcon = field.searchIcon,
closeSearchIcon = field.closeSearchIcon,
itemsTextStyle = field.itemsTextStyle,
searchHintStyle = field.searchHintStyle,
searchTextStyle = field.searchTextStyle,
selectedItemsTextStyle = field.selectedItemsTextStyle,
separateSelectedItems = field.separateSelectedItems,
checkColor = field.checkColor,
isDismissible = field.isDismissible,
state = state;
@override
__MultiSelectBottomSheetFieldViewState createState() =>
__MultiSelectBottomSheetFieldViewState<V>();
}
class __MultiSelectBottomSheetFieldViewState<V>
extends State<_MultiSelectBottomSheetFieldView<V>> {
List<V> _selectedItems = [];
@override
void initState() {
super.initState();
_selectedItems.addAll(widget.initialValue);
}
@override
void didUpdateWidget(_MultiSelectBottomSheetFieldView<V> oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.initialValue != widget.initialValue) {
_selectedItems = [];
_selectedItems.addAll(widget.initialValue);
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.state!.didChange(_selectedItems);
});
}
}
Widget _buildInheritedChipDisplay() {
List<MultiSelectItem<V>?> chipDisplayItems = [];
chipDisplayItems = _selectedItems
.map((e) =>
widget.items.firstWhereOrNull((element) => e == element.value))
.toList();
chipDisplayItems.removeWhere((element) => element == null);
if (widget.chipDisplay != null) {
// if user has specified a chipDisplay, use its params
if (widget.chipDisplay!.disabled!) {
return Container();
} else {
return MultiSelectChipDisplay<V>(
items: chipDisplayItems,
colorator: widget.chipDisplay!.colorator ?? widget.colorator,
onTap: (item) {
List<V>? newValues;
if (widget.chipDisplay!.onTap != null) {
dynamic result = widget.chipDisplay!.onTap!(item);
if (result is List<V>) newValues = result;
}
if (newValues != null) {
_selectedItems = newValues;
if (widget.state != null) {
widget.state!.didChange(_selectedItems);
}
}
},
decoration: widget.chipDisplay!.decoration,
chipColor: widget.chipDisplay!.chipColor ??
((widget.selectedColor != null &&
widget.selectedColor != Colors.transparent)
? widget.selectedColor!.withOpacity(0.35)
: null),
alignment: widget.chipDisplay!.alignment,
textStyle: widget.chipDisplay!.textStyle,
icon: widget.chipDisplay!.icon,
shape: widget.chipDisplay!.shape,
scroll: widget.chipDisplay!.scroll,
scrollBar: widget.chipDisplay!.scrollBar,
height: widget.chipDisplay!.height,
chipWidth: widget.chipDisplay!.chipWidth,
);
}
} else {
// user didn't specify a chipDisplay, build the default
return MultiSelectChipDisplay<V>(
items: chipDisplayItems,
colorator: widget.colorator,
chipColor: (widget.selectedColor != null &&
widget.selectedColor != Colors.transparent)
? widget.selectedColor!.withOpacity(0.35)
: null,
);
}
}
_showBottomSheet(BuildContext ctx) async {
List<V>? myVar = await showModalBottomSheet<List<V>>(
isDismissible: widget.isDismissible,
backgroundColor: widget.backgroundColor,
barrierColor: widget.barrierColor,
shape: widget.shape ??
RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(15.0)),
),
isScrollControlled: true,
context: context,
builder: (context) {
return MultiSelectBottomSheet<V>(
checkColor: widget.checkColor,
selectedItemsTextStyle: widget.selectedItemsTextStyle,
searchTextStyle: widget.searchTextStyle,
searchHintStyle: widget.searchHintStyle,
itemsTextStyle: widget.itemsTextStyle,
searchIcon: widget.searchIcon,
closeSearchIcon: widget.closeSearchIcon,
unselectedColor: widget.unselectedColor,
colorator: widget.colorator,
searchHint: widget.searchHint,
selectedColor: widget.selectedColor,
listType: widget.listType,
items: widget.items,
cancelText: widget.cancelText,
confirmText: widget.confirmText,
separateSelectedItems: widget.separateSelectedItems,
initialValue: _selectedItems,
onConfirm: (selected) {
if (widget.state != null) {
widget.state!.didChange(selected);
}
_selectedItems = selected;
if (widget.onConfirm != null) widget.onConfirm!(selected);
},
onSelectionChanged: widget.onSelectionChanged,
searchable: widget.searchable,
title: widget.title,
initialChildSize: widget.initialChildSize,
minChildSize: widget.minChildSize,
maxChildSize: widget.maxChildSize,
);
});
print(myVar.toString());
_selectedItems = myVar!;
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
InkWell(
onTap: () {
_showBottomSheet(context);
},
child: Container(
decoration: widget.state != null
? widget.decoration ??
BoxDecoration(
border: Border(
bottom: BorderSide(
color: widget.state != null && widget.state!.hasError
? Colors.red.shade800.withOpacity(0.6)
: _selectedItems.isNotEmpty
? (widget.selectedColor != null &&
widget.selectedColor !=
Colors.transparent)
? widget.selectedColor!
: Theme.of(context).primaryColor
: Colors.black45,
width: _selectedItems.isNotEmpty
? (widget.state != null && widget.state!.hasError)
? 1.4
: 1.8
: 1.2,
),
),
)
: widget.decoration,
padding: EdgeInsets.all(10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
widget.buttonText ?? Text("Select"),
widget.buttonIcon ?? Icon(Icons.arrow_downward),
],
),
),
),
_buildInheritedChipDisplay(),
widget.state != null && widget.state!.hasError
? SizedBox(height: 5)
: Container(),
widget.state != null && widget.state!.hasError
? Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left: 4),
child: Text(
widget.state!.errorText!,
style: TextStyle(
color: Colors.red[800],
fontSize: 12.5,
),
),
),
],
)
: Container(),
],
);
}
}
================================================
FILE: lib/chip_display/multi_select_chip_display.dart
================================================
import 'package:flutter/material.dart';
import '../util/horizontal_scrollbar.dart';
import '../util/multi_select_item.dart';
/// A widget meant to display selected values as chips.
// ignore: must_be_immutable
class MultiSelectChipDisplay<V> extends StatelessWidget {
/// The source list of selected items.
final List<MultiSelectItem<V>?>? items;
/// Fires when a chip is tapped.
final Function(V)? onTap;
/// Set the chip color.
final Color? chipColor;
/// Change the alignment of the chips.
final Alignment? alignment;
/// Style the Container that makes up the chip display.
final BoxDecoration? decoration;
/// Style the text on the chips.
final TextStyle? textStyle;
/// A function that sets the color of selected items based on their value.
final Color? Function(V)? colorator;
/// An icon to display prior to the chip's label.
final Icon? icon;
/// Set a ShapeBorder. Typically a RoundedRectangularBorder.
final ShapeBorder? shape;
/// Enables horizontal scrolling.
final bool scroll;
/// Enables the scrollbar when scroll is `true`.
final HorizontalScrollBar? scrollBar;
final ScrollController _scrollController = ScrollController();
/// Set a fixed height.
final double? height;
/// Set the width of the chips.
final double? chipWidth;
bool? disabled;
MultiSelectChipDisplay({
this.items,
this.onTap,
this.chipColor,
this.alignment,
this.decoration,
this.textStyle,
this.colorator,
this.icon,
this.shape,
this.scroll = false,
this.scrollBar,
this.height,
this.chipWidth,
}) {
this.disabled = false;
}
MultiSelectChipDisplay.none({
this.items = const [],
this.disabled = true,
this.onTap,
this.chipColor,
this.alignment,
this.decoration,
this.textStyle,
this.colorator,
this.icon,
this.shape,
this.scroll = false,
this.scrollBar,
this.height,
this.chipWidth,
});
@override
Widget build(BuildContext context) {
if (items == null || items!.isEmpty) return Container();
return Container(
decoration: decoration,
alignment: alignment ?? Alignment.centerLeft,
padding: EdgeInsets.symmetric(horizontal: scroll ? 0 : 10),
child: scroll
? Container(
width: MediaQuery.of(context).size.width,
height: height ?? MediaQuery.of(context).size.height * 0.08,
child: scrollBar != null
? Scrollbar(
thumbVisibility: scrollBar!.isAlwaysShown,
controller: _scrollController,
child: ListView.builder(
controller: _scrollController,
scrollDirection: Axis.horizontal,
itemCount: items!.length,
itemBuilder: (ctx, index) {
return _buildItem(items![index]!, context);
},
),
)
: ListView.builder(
controller: _scrollController,
scrollDirection: Axis.horizontal,
itemCount: items!.length,
itemBuilder: (ctx, index) {
return _buildItem(items![index]!, context);
},
),
)
: Wrap(
children: items != null
? items!.map((item) => _buildItem(item!, context)).toList()
: <Widget>[
Container(),
],
),
);
}
Widget _buildItem(MultiSelectItem<V> item, BuildContext context) {
return Container(
padding: const EdgeInsets.all(2.0),
child: ChoiceChip(
shape: shape as OutlinedBorder?,
avatar: icon != null
? Icon(
icon!.icon,
color: colorator != null && colorator!(item.value) != null
? colorator!(item.value)!.withOpacity(1)
: icon!.color ?? Theme.of(context).primaryColor,
)
: null,
label: Container(
width: chipWidth,
child: Text(
item.label,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: colorator != null && colorator!(item.value) != null
? textStyle != null
? textStyle!.color ?? colorator!(item.value)
: colorator!(item.value)
: textStyle != null && textStyle!.color != null
? textStyle!.color
: chipColor != null
? chipColor!.withOpacity(1)
: null,
fontSize: textStyle != null ? textStyle!.fontSize : null,
),
),
),
selected: items!.contains(item),
selectedColor: colorator != null && colorator!(item.value) != null
? colorator!(item.value)
: chipColor != null
? chipColor
: Theme.of(context).primaryColor.withOpacity(0.33),
onSelected: (_) {
if (onTap != null) onTap!(item.value);
},
),
);
}
}
================================================
FILE: lib/chip_field/multi_select_chip_field.dart
================================================
import 'package:flutter/material.dart';
import '../multi_select_flutter.dart';
class MultiSelectChipField<V> extends FormField<List<V>> {
/// Style the Container that makes up the field.
final BoxDecoration? decoration;
/// List of items to select from.
final List<MultiSelectItem<V>> items;
/// Color of the chip while not selected.
final Color? chipColor;
/// Sets the color of the chip while selected.
final Color? selectedChipColor;
/// Style the text of the chips.
final TextStyle? textStyle;
/// Style the text of the selected chips.
final TextStyle? selectedTextStyle;
/// The icon displayed in front of text on selected chips.
final Icon? icon;
/// Replaces the deafult search icon when searchable is true.
final Icon? searchIcon;
/// Replaces the default close search icon when searchable is true.
final Icon? closeSearchIcon;
/// Set a ShapeBorder. Typically a RoundedRectangularBorder.
final ShapeBorder? chipShape;
/// Defines the header text.
final Text? title;
/// Enables horizontal scrolling. Default is true.
final bool scroll;
/// A function that sets the color of selected items based on their value.
final Color Function(V)? colorator;
/// Fires when a chip is tapped. A good time to store the selected values.
final Function(List<V>)? onTap;
/// Enables search functionality.
final bool? searchable;
/// Set the search hint.
final String? searchHint;
/// Set the TextStyle of the search hint.
final TextStyle? searchHintStyle;
/// Set the TextStyle of the text that gets typed into the search bar.
final TextStyle? searchTextStyle;
/// Set the header color.
final Color? headerColor;
/// Build a custom widget that gets created dynamically for each item.
final Widget Function(MultiSelectItem<V>, FormFieldState<List<V>>)?
itemBuilder;
/// Set the height of the selectable area.
final double? height;
/// Make use of the ScrollController to automatically scroll through the list.
final Function(ScrollController)? scrollControl;
/// Define a HorizontalScrollBar.
final HorizontalScrollBar? scrollBar;
/// Determines whether to show the header.
final bool showHeader;
/// Set the width of the chip.
final double? chipWidth;
final List<V> initialValue;
final AutovalidateMode autovalidateMode;
final FormFieldValidator<List<V>>? validator;
final FormFieldSetter<List<V>>? onSaved;
final GlobalKey<FormFieldState>? key;
MultiSelectChipField({
required this.items,
this.decoration,
this.chipColor,
this.selectedChipColor,
this.colorator,
this.textStyle,
this.selectedTextStyle,
this.icon,
this.searchIcon,
this.closeSearchIcon,
this.chipShape,
this.onTap,
this.title,
this.scroll = true,
this.searchable,
this.searchHint,
this.searchHintStyle,
this.searchTextStyle,
this.headerColor,
this.key,
this.onSaved,
this.validator,
this.autovalidateMode = AutovalidateMode.disabled,
this.initialValue = const [],
this.itemBuilder,
this.height,
this.scrollControl,
this.scrollBar,
this.showHeader = true,
this.chipWidth,
}) : super(
key: key,
onSaved: onSaved,
validator: validator,
autovalidateMode: autovalidateMode,
initialValue: initialValue,
builder: (FormFieldState<List<V>> state) {
_MultiSelectChipFieldView view = _MultiSelectChipFieldView<V>(
items: items,
decoration: decoration,
chipColor: chipColor,
selectedChipColor: selectedChipColor,
colorator: colorator,
textStyle: textStyle,
selectedTextStyle: selectedTextStyle,
icon: icon,
searchIcon: searchIcon,
closeSearchIcon: closeSearchIcon,
chipShape: chipShape,
onTap: onTap,
title: title,
scroll: scroll,
initialValue: initialValue,
searchable: searchable,
searchHint: searchHint,
searchHintStyle: searchHintStyle,
searchTextStyle: searchTextStyle,
headerColor: headerColor,
itemBuilder: itemBuilder,
height: height,
scrollControl: scrollControl,
scrollBar: scrollBar,
showHeader: showHeader,
chipWidth: chipWidth,
);
return _MultiSelectChipFieldView<V?>.withState(
view as _MultiSelectChipFieldView<V?>, state);
});
}
// ignore: must_be_immutable
class _MultiSelectChipFieldView<V> extends StatefulWidget
with MultiSelectActions {
final BoxDecoration? decoration;
final List<MultiSelectItem<V>> items;
final List<MultiSelectItem<V>>? selectedItems;
final Color? chipColor;
final Color? selectedChipColor;
final TextStyle? textStyle;
final TextStyle? selectedTextStyle;
final Icon? icon;
final Icon? searchIcon;
final Icon? closeSearchIcon;
final ShapeBorder? chipShape;
final Text? title;
final bool scroll;
final bool? searchable;
final String? searchHint;
final TextStyle? searchHintStyle;
final TextStyle? searchTextStyle;
final List<V> initialValue;
final Color? Function(V)? colorator;
final Function(List<V>)? onTap;
final Color? headerColor;
final Widget Function(MultiSelectItem<V>, FormFieldState<List<V>>)?
itemBuilder;
final double? height;
FormFieldState<List<V>>? state;
final Function(ScrollController)? scrollControl;
final HorizontalScrollBar? scrollBar;
final bool showHeader;
final double? chipWidth;
_MultiSelectChipFieldView({
required this.items,
this.selectedItems,
this.decoration,
this.chipColor,
this.selectedChipColor,
this.colorator,
this.textStyle,
this.selectedTextStyle,
this.icon,
this.chipShape,
this.onTap,
this.title,
this.scroll = true,
this.initialValue = const [],
this.searchable,
this.searchHint,
this.searchIcon,
this.closeSearchIcon,
this.searchHintStyle,
this.searchTextStyle,
this.headerColor,
this.itemBuilder,
this.height,
this.scrollControl,
this.scrollBar,
this.showHeader = true,
this.chipWidth,
});
/// This constructor allows a FormFieldState to be passed in. Called by MultiSelectChipField.
_MultiSelectChipFieldView.withState(
_MultiSelectChipFieldView<V> field, FormFieldState<List<V>> state)
: items = field.items,
title = field.title,
decoration = field.decoration,
initialValue = field.initialValue,
selectedChipColor = field.selectedChipColor,
chipShape = field.chipShape,
colorator = field.colorator,
chipColor = field.chipColor,
icon = field.icon,
closeSearchIcon = field.closeSearchIcon,
selectedItems = field.selectedItems,
textStyle = field.textStyle,
scroll = field.scroll,
selectedTextStyle = field.selectedTextStyle,
onTap = field.onTap,
searchable = field.searchable,
searchHint = field.searchHint,
searchIcon = field.searchIcon,
searchTextStyle = field.searchTextStyle,
searchHintStyle = field.searchHintStyle,
headerColor = field.headerColor,
itemBuilder = field.itemBuilder,
height = field.height,
scrollControl = field.scrollControl,
scrollBar = field.scrollBar,
showHeader = field.showHeader,
chipWidth = field.chipWidth,
state = state;
@override
__MultiSelectChipFieldViewState createState() =>
__MultiSelectChipFieldViewState<V>(items);
}
class __MultiSelectChipFieldViewState<V>
extends State<_MultiSelectChipFieldView<V?>> {
List<V?> _selectedValues = [];
bool _showSearch = false;
List<MultiSelectItem> _items;
ScrollController _scrollController = ScrollController();
__MultiSelectChipFieldViewState(this._items);
void initState() {
super.initState();
_selectedValues.addAll(widget.initialValue);
if (widget.scrollControl != null && widget.scroll)
WidgetsBinding.instance.addPostFrameCallback((_) => _scrollToPosition());
}
@override
void didUpdateWidget(_MultiSelectChipFieldView<V> oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.initialValue != widget.initialValue) {
_selectedValues = [];
_selectedValues.addAll(widget.initialValue);
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.state!.didChange(_selectedValues);
});
}
if (oldWidget.items != widget.items) {
_items = [...widget.items];
}
}
_scrollToPosition() {
widget.scrollControl!(_scrollController);
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
decoration: widget.decoration ??
BoxDecoration(
border:
Border.all(width: 1, color: Theme.of(context).primaryColor),
),
child: Column(
children: [
widget.showHeader
? Container(
color:
widget.headerColor ?? Theme.of(context).primaryColor,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_showSearch
? Expanded(
child: Container(
padding: EdgeInsets.only(left: 10),
child: TextField(
style: widget.searchTextStyle,
decoration: InputDecoration(
hintStyle: widget.searchHintStyle,
hintText: widget.searchHint ?? "Search",
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: widget.selectedChipColor ??
Theme.of(context).primaryColor,
),
),
),
onChanged: (val) {
setState(() {
_items = widget.updateSearchQuery(
val, widget.items);
});
},
),
),
)
: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: widget.title != null
? Text(
widget.title!.data!,
style: TextStyle(
color: widget.title!.style != null
? widget.title!.style!.color
: null,
fontSize:
widget.title!.style != null
? widget.title!.style!
.fontSize ??
18
: 18),
)
: Text(
"Select",
style: TextStyle(fontSize: 18),
),
),
widget.searchable != null && widget.searchable!
? IconButton(
icon: _showSearch
? widget.closeSearchIcon ??
Icon(
Icons.close,
size: 22,
)
: widget.searchIcon ??
Icon(
Icons.search,
size: 22,
),
onPressed: () {
setState(() {
_showSearch = !_showSearch;
if (!_showSearch) _items = widget.items;
});
},
)
: Padding(
padding: EdgeInsets.all(18),
),
],
),
)
: Container(),
widget.scroll
? Container(
padding: widget.itemBuilder == null
? EdgeInsets.symmetric(horizontal: 5)
: null,
width: MediaQuery.of(context).size.width,
height: widget.height ??
MediaQuery.of(context).size.height * 0.08,
child: widget.scrollBar != null
? Scrollbar(
thumbVisibility: widget.scrollBar!.isAlwaysShown,
controller: _scrollController,
child: ListView.builder(
controller: _scrollController,
scrollDirection: Axis.horizontal,
itemCount: _items.length,
itemBuilder: (ctx, index) {
return widget.itemBuilder != null
? widget.itemBuilder!(
_items[index] as MultiSelectItem<V>,
widget.state!)
: _buildItem(
_items[index] as MultiSelectItem<V?>);
},
),
)
: ListView.builder(
controller: _scrollController,
scrollDirection: Axis.horizontal,
itemCount: _items.length,
itemBuilder: (ctx, index) {
return widget.itemBuilder != null
? widget.itemBuilder!(
_items[index] as MultiSelectItem<V>,
widget.state!)
: _buildItem(
_items[index] as MultiSelectItem<V?>);
},
),
)
: Container(
height: widget.height,
alignment: Alignment.centerLeft,
padding: EdgeInsets.symmetric(horizontal: 10),
child: Wrap(
children: widget.itemBuilder != null
? _items
.map((item) => widget.itemBuilder!(
item as MultiSelectItem<V>, widget.state!))
.toList()
: _items
.map((item) =>
_buildItem(item as MultiSelectItem<V?>))
.toList(),
),
),
],
),
),
widget.state != null && widget.state!.hasError
? Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(4, 4, 0, 0),
child: Text(
widget.state!.errorText!,
style: TextStyle(
color: Colors.red[800],
fontSize: 12.5,
),
),
),
],
)
: Container(),
],
);
}
Widget _buildItem(MultiSelectItem<V?> item) {
return Container(
margin: EdgeInsets.all(0),
padding: const EdgeInsets.all(2.0),
child: ChoiceChip(
shape: widget.chipShape as OutlinedBorder? ??
RoundedRectangleBorder(
side: BorderSide(
color: widget.colorator != null &&
widget.colorator!(item.value) != null &&
_selectedValues.contains(item.value)
? widget.colorator!(item.value)!
: widget.selectedChipColor ??
Theme.of(context).primaryColor),
borderRadius: BorderRadius.vertical(
top: Radius.circular(15.0),
bottom: Radius.circular(15.0),
),
),
avatar: _selectedValues.contains(item.value)
? widget.icon != null
? Icon(
widget.icon!.icon,
color: widget.colorator != null &&
widget.colorator!(item.value) != null
? widget.colorator!(item.value)!.withOpacity(1)
: widget.icon!.color ??
widget.selectedChipColor ??
Theme.of(context).primaryColor,
)
: null
: null,
label: Container(
width: widget.chipWidth,
child: Text(
item.label,
overflow: TextOverflow.ellipsis,
style: _selectedValues.contains(item.value)
? TextStyle(
color: widget.colorator != null &&
widget.colorator!(item.value) != null
? widget.colorator!(item.value)!.withOpacity(1)
: widget.selectedTextStyle != null
? widget.selectedTextStyle!.color
: null)
: TextStyle(
color: widget.textStyle != null
? widget.textStyle!.color ?? widget.chipColor
: widget.chipColor,
fontSize: widget.textStyle != null
? widget.textStyle!.fontSize
: null,
),
),
),
selected: _selectedValues.contains(item.value),
backgroundColor: widget.chipColor ?? Colors.white70,
selectedColor:
widget.colorator != null && widget.colorator!(item.value) != null
? widget.colorator!(item.value)
: widget.selectedChipColor != null
? widget.selectedChipColor
: Theme.of(context).primaryColor.withOpacity(0.33),
onSelected: (_) {
if (_) {
_selectedValues.add(item.value);
if (widget.state != null) {
widget.state!.didChange(_selectedValues);
}
} else {
_selectedValues.remove(item.value);
if (widget.state != null) {
widget.state!.didChange(_selectedValues);
}
}
if (widget.onTap != null) widget.onTap!(_selectedValues);
},
),
);
}
}
================================================
FILE: lib/dialog/mult_select_dialog.dart
================================================
import 'package:flutter/material.dart';
import '../util/multi_select_actions.dart';
import '../util/multi_select_item.dart';
import '../util/multi_select_list_type.dart';
/// A dialog containing either a classic checkbox style list, or a chip style list.
class MultiSelectDialog<T> extends StatefulWidget with MultiSelectActions<T> {
/// List of items to select from.
final List<MultiSelectItem<T>> items;
/// The list of selected values before interaction.
final List<T> initialValue;
/// The text at the top of the dialog.
final Widget? title;
/// Fires when the an item is selected / unselected.
final void Function(List<T>)? onSelectionChanged;
/// Fires when confirm is tapped.
final void Function(List<T>)? onConfirm;
/// Toggles search functionality. Default is false.
final bool searchable;
/// Text on the confirm button.
final Text? confirmText;
/// Text on the cancel button.
final Text? cancelText;
/// An enum that determines which type of list to render.
final MultiSelectListType? listType;
/// Sets the color of the checkbox or chip when it's selected.
final Color? selectedColor;
/// Sets a fixed height on the dialog.
final double? height;
/// Sets a fixed width on the dialog.
final double? width;
/// Set the placeholder text of the search field.
final String? searchHint;
/// A function that sets the color of selected items based on their value.
/// It will either set the chip color, or the checkbox color depending on the list type.
final Color? Function(T)? colorator;
/// The background color of the dialog.
final Color? backgroundColor;
/// The color of the chip body or checkbox border while not selected.
final Color? unselectedColor;
/// Icon button that shows the search field.
final Icon? searchIcon;
/// Icon button that hides the search field
final Icon? closeSearchIcon;
/// Style the text on the chips or list tiles.
final TextStyle? itemsTextStyle;
/// Style the text on the selected chips or list tiles.
final TextStyle? selectedItemsTextStyle;
/// Style the search text.
final TextStyle? searchTextStyle;
/// Style the search hint.
final TextStyle? searchHintStyle;
/// Moves the selected items to the top of the list.
final bool separateSelectedItems;
/// Set the color of the check in the checkbox
final Color? checkColor;
MultiSelectDialog({
required this.items,
required this.initialValue,
this.title,
this.onSelectionChanged,
this.onConfirm,
this.listType,
this.searchable = false,
this.confirmText,
this.cancelText,
this.selectedColor,
this.searchHint,
this.height,
this.width,
this.colorator,
this.backgroundColor,
this.unselectedColor,
this.searchIcon,
this.closeSearchIcon,
this.itemsTextStyle,
this.searchHintStyle,
this.searchTextStyle,
this.selectedItemsTextStyle,
this.separateSelectedItems = false,
this.checkColor,
});
@override
State<StatefulWidget> createState() => _MultiSelectDialogState<T>(items);
}
class _MultiSelectDialogState<T> extends State<MultiSelectDialog<T>> {
List<T> _selectedValues = [];
bool _showSearch = false;
List<MultiSelectItem<T>> _items;
_MultiSelectDialogState(this._items);
@override
void initState() {
super.initState();
_selectedValues.addAll(widget.initialValue);
for (int i = 0; i < _items.length; i++) {
_items[i].selected = false;
if (_selectedValues.contains(_items[i].value)) {
_items[i].selected = true;
}
}
if (widget.separateSelectedItems) {
_items = widget.separateSelected(_items);
}
}
/// Returns a CheckboxListTile
Widget _buildListItem(MultiSelectItem<T> item) {
return Theme(
data: ThemeData(
unselectedWidgetColor: widget.unselectedColor ?? Colors.black54,
),
child: CheckboxListTile(
checkColor: widget.checkColor,
value: item.selected,
activeColor: widget.colorator != null
? widget.colorator!(item.value) ?? widget.selectedColor
: widget.selectedColor,
title: Text(
item.label,
style: item.selected
? widget.selectedItemsTextStyle
: widget.itemsTextStyle,
),
controlAffinity: ListTileControlAffinity.leading,
onChanged: (checked) {
setState(() {
_selectedValues = widget.onItemCheckedChange(
_selectedValues, item.value, checked!);
if (checked) {
item.selected = true;
} else {
item.selected = false;
}
if (widget.separateSelectedItems) {
_items = widget.separateSelected(_items);
}
});
if (widget.onSelectionChanged != null) {
widget.onSelectionChanged!(_selectedValues);
}
},
),
);
}
/// Returns a ChoiceChip
Widget _buildChipItem(MultiSelectItem<T> item) {
return Container(
padding: const EdgeInsets.all(2.0),
child: ChoiceChip(
backgroundColor: widget.unselectedColor,
selectedColor: widget.colorator?.call(item.value) ??
widget.selectedColor ??
Theme.of(context).primaryColor.withOpacity(0.35),
label: Text(
item.label,
style: item.selected
? TextStyle(
color: widget.selectedItemsTextStyle?.color ??
widget.colorator?.call(item.value) ??
widget.selectedColor?.withOpacity(1) ??
Theme.of(context).primaryColor,
fontSize: widget.selectedItemsTextStyle?.fontSize,
)
: widget.itemsTextStyle,
),
selected: item.selected,
onSelected: (checked) {
if (checked) {
item.selected = true;
} else {
item.selected = false;
}
setState(() {
_selectedValues = widget.onItemCheckedChange(
_selectedValues, item.value, checked);
});
if (widget.onSelectionChanged != null) {
widget.onSelectionChanged!(_selectedValues);
}
},
),
);
}
@override
Widget build(BuildContext context) {
return AlertDialog(
backgroundColor: widget.backgroundColor,
title: widget.searchable == false
? widget.title ?? const Text("Select")
: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
_showSearch
? Expanded(
child: Container(
padding: EdgeInsets.only(left: 10),
child: TextField(
style: widget.searchTextStyle,
decoration: InputDecoration(
hintStyle: widget.searchHintStyle,
hintText: widget.searchHint ?? "Search",
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: widget.selectedColor ??
Theme.of(context).primaryColor,
),
),
),
onChanged: (val) {
List<MultiSelectItem<T>> filteredList = [];
filteredList =
widget.updateSearchQuery(val, widget.items);
setState(() {
if (widget.separateSelectedItems) {
_items =
widget.separateSelected(filteredList);
} else {
_items = filteredList;
}
});
},
),
),
)
: widget.title ?? Text("Select"),
IconButton(
icon: _showSearch
? widget.closeSearchIcon ?? Icon(Icons.close)
: widget.searchIcon ?? Icon(Icons.search),
onPressed: () {
setState(() {
_showSearch = !_showSearch;
if (!_showSearch) {
if (widget.separateSelectedItems) {
_items = widget.separateSelected(widget.items);
} else {
_items = widget.items;
}
}
});
},
),
],
),
contentPadding:
widget.listType == null || widget.listType == MultiSelectListType.LIST
? EdgeInsets.only(top: 12.0)
: EdgeInsets.all(20),
content: Container(
height: widget.height,
width: widget.width ?? MediaQuery.of(context).size.width * 0.73,
child: widget.listType == null ||
widget.listType == MultiSelectListType.LIST
? ListView.builder(
itemCount: _items.length,
itemBuilder: (context, index) {
return _buildListItem(_items[index]);
},
)
: SingleChildScrollView(
child: Wrap(
children: _items.map(_buildChipItem).toList(),
),
),
),
actions: <Widget>[
TextButton(
child: widget.cancelText ??
Text(
"CANCEL",
style: TextStyle(
color: (widget.selectedColor != null &&
widget.selectedColor != Colors.transparent)
? widget.selectedColor!.withOpacity(1)
: Theme.of(context).primaryColor,
),
),
onPressed: () {
widget.onCancelTap(context, widget.initialValue);
},
),
TextButton(
child: widget.confirmText ??
Text(
'OK',
style: TextStyle(
color: (widget.selectedColor != null &&
widget.selectedColor != Colors.transparent)
? widget.selectedColor!.withOpacity(1)
: Theme.of(context).primaryColor,
),
),
onPressed: () {
widget.onConfirmTap(context, _selectedValues, widget.onConfirm);
},
)
],
);
}
}
================================================
FILE: lib/dialog/multi_select_dialog_field.dart
================================================
import 'package:collection/collection.dart' show IterableExtension;
import 'package:flutter/material.dart';
import '../util/multi_select_list_type.dart';
import '../util/multi_select_item.dart';
import '../chip_display/multi_select_chip_display.dart';
import 'mult_select_dialog.dart';
/// A customizable InkWell widget that opens the MultiSelectDialog
// ignore: must_be_immutable
class MultiSelectDialogField<V> extends FormField<List<V>> {
/// An enum that determines which type of list to render.
final MultiSelectListType? listType;
/// Style the Container that makes up the field.
final BoxDecoration? decoration;
/// Set text that is displayed on the button.
final Text? buttonText;
/// Specify the button icon.
final Icon? buttonIcon;
/// The text at the top of the dialog.
final Widget? title;
/// List of items to select from.
final List<MultiSelectItem<V>> items;
/// Fires when the an item is selected / unselected.
final void Function(List<V>)? onSelectionChanged;
/// Overrides the default MultiSelectChipDisplay attached to this field.
/// If you want to remove it, use MultiSelectChipDisplay.none().
final MultiSelectChipDisplay<V>? chipDisplay;
/// The list of selected values before interaction.
final List<V> initialValue;
/// Fires when confirm is tapped.
final void Function(List<V>) onConfirm;
/// Toggles search functionality.
final bool searchable;
/// Text on the confirm button.
final Text? confirmText;
/// Text on the cancel button.
final Text? cancelText;
/// Set the color of the space outside the BottomSheet.
final Color? barrierColor;
/// Sets the color of the checkbox or chip when it's selected.
final Color? selectedColor;
/// Sets a fixed height on the dialog.
final double? dialogHeight;
/// Sets a fixed width on the dialog.
final double? dialogWidth;
/// Set the placeholder text of the search field.
final String? searchHint;
/// A function that sets the color of selected items based on their value.
/// It will either set the chip color, or the checkbox color depending on the list type.
final Color Function(V)? colorator;
/// Set the background color of the dialog.
final Color? backgroundColor;
/// Color of the chip body or checkbox border while not selected.
final Color? unselectedColor;
/// Replaces the deafult search icon when searchable is true.
final Icon? searchIcon;
/// Replaces the default close search icon when searchable is true.
final Icon? closeSearchIcon;
/// Style the text on the chips or list tiles.
final TextStyle? itemsTextStyle;
/// Style the text on the selected chips or list tiles.
final TextStyle? selectedItemsTextStyle;
/// Style the text that is typed into the search field.
final TextStyle? searchTextStyle;
/// Style the search hint.
final TextStyle? searchHintStyle;
/// Moves the selected items to the top of the list.
final bool separateSelectedItems;
/// Set the color of the check in the checkbox
final Color? checkColor;
/// Whether the user can dismiss the widget by tapping outside
final bool isDismissible;
final AutovalidateMode autovalidateMode;
final FormFieldValidator<List<V>>? validator;
final FormFieldSetter<List<V>>? onSaved;
final GlobalKey<FormFieldState>? key;
FormFieldState<List<V>>? state;
MultiSelectDialogField({
required this.items,
required this.onConfirm,
this.title,
this.buttonText,
this.buttonIcon,
this.listType,
this.decoration,
this.onSelectionChanged,
this.chipDisplay,
this.searchable = false,
this.confirmText,
this.cancelText,
this.barrierColor,
this.selectedColor,
this.searchHint,
this.dialogHeight,
this.dialogWidth,
this.colorator,
this.backgroundColor,
this.unselectedColor,
this.searchIcon,
this.closeSearchIcon,
this.itemsTextStyle,
this.searchTextStyle,
this.searchHintStyle,
this.selectedItemsTextStyle,
this.separateSelectedItems = false,
this.checkColor,
this.isDismissible = true,
this.onSaved,
this.validator,
this.initialValue = const [],
this.autovalidateMode = AutovalidateMode.disabled,
this.key,
}) : super(
key: key,
onSaved: onSaved,
validator: validator,
autovalidateMode: autovalidateMode,
initialValue: initialValue,
builder: (FormFieldState<List<V>> state) {
_MultiSelectDialogFieldView<V> field =
_MultiSelectDialogFieldView<V>(
title: title,
items: items,
buttonText: buttonText,
buttonIcon: buttonIcon,
chipDisplay: chipDisplay,
decoration: decoration,
listType: listType,
onConfirm: onConfirm,
onSelectionChanged: onSelectionChanged,
initialValue: initialValue,
searchable: searchable,
confirmText: confirmText,
cancelText: cancelText,
barrierColor: barrierColor,
selectedColor: selectedColor,
searchHint: searchHint,
dialogHeight: dialogHeight,
dialogWidth: dialogWidth,
colorator: colorator,
backgroundColor: backgroundColor,
unselectedColor: unselectedColor,
searchIcon: searchIcon,
closeSearchIcon: closeSearchIcon,
itemsTextStyle: itemsTextStyle,
searchTextStyle: searchTextStyle,
searchHintStyle: searchHintStyle,
selectedItemsTextStyle: selectedItemsTextStyle,
separateSelectedItems: separateSelectedItems,
checkColor: checkColor,
isDismissible: isDismissible,
);
return _MultiSelectDialogFieldView<V>._withState(field, state);
});
}
// ignore: must_be_immutable
class _MultiSelectDialogFieldView<V> extends StatefulWidget {
final MultiSelectListType? listType;
final BoxDecoration? decoration;
final Text? buttonText;
final Icon? buttonIcon;
final Widget? title;
final List<MultiSelectItem<V>> items;
final void Function(List<V>)? onSelectionChanged;
final MultiSelectChipDisplay<V>? chipDisplay;
final List<V> initialValue;
final void Function(List<V>)? onConfirm;
final bool? searchable;
final Text? confirmText;
final Text? cancelText;
final Color? barrierColor;
final Color? selectedColor;
final double? dialogHeight;
final double? dialogWidth;
final String? searchHint;
final Color Function(V)? colorator;
final Color? backgroundColor;
final Color? unselectedColor;
final Icon? searchIcon;
final Icon? closeSearchIcon;
final TextStyle? itemsTextStyle;
final TextStyle? selectedItemsTextStyle;
final TextStyle? searchTextStyle;
final TextStyle? searchHintStyle;
final bool separateSelectedItems;
final Color? checkColor;
final bool isDismissible;
FormFieldState<List<V>>? state;
_MultiSelectDialogFieldView({
required this.items,
this.title,
this.buttonText,
this.buttonIcon,
this.listType,
this.decoration,
this.onSelectionChanged,
this.onConfirm,
this.chipDisplay,
this.initialValue = const [],
this.searchable,
this.confirmText,
this.cancelText,
this.barrierColor,
this.selectedColor,
this.searchHint,
this.dialogHeight,
this.dialogWidth,
this.colorator,
this.backgroundColor,
this.unselectedColor,
this.searchIcon,
this.closeSearchIcon,
this.itemsTextStyle,
this.searchTextStyle,
this.searchHintStyle,
this.selectedItemsTextStyle,
this.separateSelectedItems = false,
this.checkColor,
required this.isDismissible,
});
/// This constructor allows a FormFieldState to be passed in. Called by MultiSelectDialogField.
_MultiSelectDialogFieldView._withState(
_MultiSelectDialogFieldView<V> field, FormFieldState<List<V>> state)
: items = field.items,
title = field.title,
buttonText = field.buttonText,
buttonIcon = field.buttonIcon,
listType = field.listType,
decoration = field.decoration,
onSelectionChanged = field.onSelectionChanged,
onConfirm = field.onConfirm,
chipDisplay = field.chipDisplay,
initialValue = field.initialValue,
searchable = field.searchable,
confirmText = field.confirmText,
cancelText = field.cancelText,
barrierColor = field.barrierColor,
selectedColor = field.selectedColor,
dialogHeight = field.dialogHeight,
dialogWidth = field.dialogWidth,
searchHint = field.searchHint,
colorator = field.colorator,
backgroundColor = field.backgroundColor,
unselectedColor = field.unselectedColor,
searchIcon = field.searchIcon,
closeSearchIcon = field.closeSearchIcon,
itemsTextStyle = field.itemsTextStyle,
searchHintStyle = field.searchHintStyle,
searchTextStyle = field.searchTextStyle,
selectedItemsTextStyle = field.selectedItemsTextStyle,
separateSelectedItems = field.separateSelectedItems,
checkColor = field.checkColor,
isDismissible = field.isDismissible,
state = state;
@override
__MultiSelectDialogFieldViewState createState() =>
__MultiSelectDialogFieldViewState<V>();
}
class __MultiSelectDialogFieldViewState<V>
extends State<_MultiSelectDialogFieldView<V>> {
List<V> _selectedItems = [];
@override
void initState() {
super.initState();
_selectedItems.addAll(widget.initialValue);
}
@override
void didUpdateWidget(_MultiSelectDialogFieldView<V> oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.initialValue != widget.initialValue) {
_selectedItems = [];
_selectedItems.addAll(widget.initialValue);
WidgetsBinding.instance.addPostFrameCallback((_) {
widget.state!.didChange(_selectedItems);
});
}
}
Widget _buildInheritedChipDisplay() {
List<MultiSelectItem<V>?> chipDisplayItems = [];
chipDisplayItems = _selectedItems
.map((e) =>
widget.items.firstWhereOrNull((element) => e == element.value))
.toList();
chipDisplayItems.removeWhere((element) => element == null);
if (widget.chipDisplay != null) {
// if user has specified a chipDisplay, use its params
if (widget.chipDisplay!.disabled!) {
return Container();
} else {
return MultiSelectChipDisplay<V>(
items: chipDisplayItems,
colorator: widget.chipDisplay!.colorator ?? widget.colorator,
onTap: (item) {
List<V>? newValues;
if (widget.chipDisplay!.onTap != null) {
dynamic result = widget.chipDisplay!.onTap!(item);
if (result is List<V>) newValues = result;
}
if (newValues != null) {
_selectedItems = newValues;
if (widget.state != null) {
widget.state!.didChange(_selectedItems);
}
}
},
decoration: widget.chipDisplay!.decoration,
chipColor: widget.chipDisplay!.chipColor ??
((widget.selectedColor != null &&
widget.selectedColor != Colors.transparent)
? widget.selectedColor!.withOpacity(0.35)
: null),
alignment: widget.chipDisplay!.alignment,
textStyle: widget.chipDisplay!.textStyle,
icon: widget.chipDisplay!.icon,
shape: widget.chipDisplay!.shape,
scroll: widget.chipDisplay!.scroll,
scrollBar: widget.chipDisplay!.scrollBar,
height: widget.chipDisplay!.height,
chipWidth: widget.chipDisplay!.chipWidth,
);
}
} else {
// user didn't specify a chipDisplay, build the default
return MultiSelectChipDisplay<V>(
items: chipDisplayItems,
colorator: widget.colorator,
chipColor: (widget.selectedColor != null &&
widget.selectedColor != Colors.transparent)
? widget.selectedColor!.withOpacity(0.35)
: null,
);
}
}
/// Calls showDialog() and renders a MultiSelectDialog.
_showDialog(BuildContext ctx) async {
await showDialog(
barrierColor: widget.barrierColor,
barrierDismissible: widget.isDismissible,
context: context,
builder: (ctx) {
return MultiSelectDialog<V>(
checkColor: widget.checkColor,
selectedItemsTextStyle: widget.selectedItemsTextStyle,
searchHintStyle: widget.searchHintStyle,
searchTextStyle: widget.searchTextStyle,
itemsTextStyle: widget.itemsTextStyle,
searchIcon: widget.searchIcon,
closeSearchIcon: widget.closeSearchIcon,
unselectedColor: widget.unselectedColor,
backgroundColor: widget.backgroundColor,
colorator: widget.colorator,
searchHint: widget.searchHint,
selectedColor: widget.selectedColor,
onSelectionChanged: widget.onSelectionChanged,
height: widget.dialogHeight,
width: widget.dialogWidth,
listType: widget.listType,
items: widget.items,
title: widget.title ?? const Text("Select"),
initialValue: _selectedItems,
searchable: widget.searchable ?? false,
confirmText: widget.confirmText,
cancelText: widget.cancelText,
separateSelectedItems: widget.separateSelectedItems,
onConfirm: (selected) {
_selectedItems = selected;
if (widget.state != null) {
widget.state!.didChange(_selectedItems);
}
if (widget.onConfirm != null) widget.onConfirm!(_selectedItems);
},
);
},
);
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
InkWell(
onTap: () {
_showDialog(context);
},
child: Container(
decoration: widget.state != null
? widget.decoration ??
BoxDecoration(
border: Border(
bottom: BorderSide(
color: widget.state != null && widget.state!.hasError
? Colors.red.shade800.withOpacity(0.6)
: _selectedItems.isNotEmpty
? (widget.selectedColor != null &&
widget.selectedColor !=
Colors.transparent)
? widget.selectedColor!
: Theme.of(context).primaryColor
: Colors.black45,
width: _selectedItems.isNotEmpty
? (widget.state != null && widget.state!.hasError)
? 1.4
: 1.8
: 1.2,
),
),
)
: widget.decoration,
padding: const EdgeInsets.all(10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
widget.buttonText ?? const Text("Select"),
widget.buttonIcon ?? const Icon(Icons.arrow_downward),
],
),
),
),
_buildInheritedChipDisplay(),
widget.state != null && widget.state!.hasError
? const SizedBox(height: 5)
: Container(),
widget.state != null && widget.state!.hasError
? Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left: 4),
child: Text(
widget.state!.errorText!,
style: TextStyle(
color: Colors.red[800],
fontSize: 12.5,
),
),
),
],
)
: Container(),
],
);
}
}
================================================
FILE: lib/multi_select_flutter.dart
================================================
export 'util/multi_select_item.dart';
export 'util/multi_select_list_type.dart';
export 'util/multi_select_actions.dart';
export 'dialog/mult_select_dialog.dart';
export 'dialog/multi_select_dialog_field.dart';
export 'bottom_sheet/multi_select_bottom_sheet.dart';
export 'bottom_sheet/multi_select_bottom_sheet_field.dart';
export 'chip_display/multi_select_chip_display.dart';
export 'chip_field/multi_select_chip_field.dart';
export 'util/horizontal_scrollbar.dart';
================================================
FILE: lib/util/horizontal_scrollbar.dart
================================================
class HorizontalScrollBar {
final bool isAlwaysShown;
HorizontalScrollBar({this.isAlwaysShown = false});
}
================================================
FILE: lib/util/multi_select_actions.dart
================================================
import 'package:flutter/material.dart';
import 'multi_select_item.dart';
/// Contains common actions that are used by different multi select classes.
class MultiSelectActions<T> {
List<T> onItemCheckedChange(
List<T> selectedValues, T itemValue, bool checked) {
if (checked) {
selectedValues.add(itemValue);
} else {
selectedValues.remove(itemValue);
}
return selectedValues;
}
/// Pops the dialog from the navigation stack and returns the initially selected values.
void onCancelTap(BuildContext ctx, List<T> initiallySelectedValues) {
Navigator.pop(ctx, initiallySelectedValues);
}
/// Pops the dialog from the navigation stack and returns the selected values.
/// Calls the onConfirm function if one was provided.
void onConfirmTap(
BuildContext ctx, List<T> selectedValues, Function(List<T>)? onConfirm) {
Navigator.pop(ctx, selectedValues);
if (onConfirm != null) {
onConfirm(selectedValues);
}
}
/// Accepts the search query, and the original list of items.
/// If the search query is valid, return a filtered list, otherwise return the original list.
List<MultiSelectItem<T>> updateSearchQuery(
String? val, List<MultiSelectItem<T>> allItems) {
if (val != null && val.trim().isNotEmpty) {
List<MultiSelectItem<T>> filteredItems = [];
for (var item in allItems) {
if (item.label.toLowerCase().contains(val.toLowerCase())) {
filteredItems.add(item);
}
}
return filteredItems;
} else {
return allItems;
}
}
/// Toggles the search field.
bool onSearchTap(bool showSearch) {
return !showSearch;
}
List<MultiSelectItem<T>> separateSelected(List<MultiSelectItem<T>> list) {
List<MultiSelectItem<T>> _selectedItems = [];
List<MultiSelectItem<T>> _nonSelectedItems = [];
_nonSelectedItems.addAll(list.where((element) => !element.selected));
_nonSelectedItems.sort((a, b) => a.label.compareTo(b.label));
_selectedItems.addAll(list.where((element) => element.selected));
_selectedItems.sort((a, b) => a.label.compareTo(b.label));
return [..._selectedItems, ..._nonSelectedItems];
}
}
================================================
FILE: lib/util/multi_select_item.dart
================================================
/// A model class used to represent a selectable item.
class MultiSelectItem<T> {
final T value;
final String label;
bool selected = false;
MultiSelectItem(this.value, this.label);
}
================================================
FILE: lib/util/multi_select_list_type.dart
================================================
/// Used by MultiSelectDialog and MultiSelectBottomSheet to determine which type of list to render.
enum MultiSelectListType { LIST, CHIP }
================================================
FILE: pubspec.yaml
================================================
name: multi_select_flutter
description: A flexible multi select package for Flutter. Make multi select widgets the way you want.
version: 4.1.3
repository: https://github.com/CHB61/flutter-multi-select
issue_tracker: https://github.com/CHB61/flutter-multi-select
documentation: https://github.com/CHB61/flutter-multi-select
homepage: https://github.com/CHB61/flutter-multi-select
environment:
sdk: '>=2.12.0 <3.0.0'
dependencies:
flutter:
sdk: flutter
collection: ^1.15.0
dev_dependencies:
flutter_test:
sdk: flutter
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# To add assets to your package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/assets-and-images/#from-packages
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# To add custom fonts to your package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/custom-fonts/#from-packages
================================================
FILE: test/multi_select_flutter_test.dart
================================================
// import 'package:flutter_test/flutter_test.dart';
// import 'package:flutter_multi_select/flutter_multi_select.dart';
void main() {
}
gitextract_bazogzq2/
├── .gitignore
├── .metadata
├── CHANGELOG.md
├── LICENSE
├── README.md
├── analysis_options.yaml
├── example/
│ ├── .gitignore
│ ├── .metadata
│ ├── README.md
│ ├── android/
│ │ ├── .gitignore
│ │ ├── app/
│ │ │ ├── build.gradle
│ │ │ └── src/
│ │ │ ├── debug/
│ │ │ │ └── AndroidManifest.xml
│ │ │ ├── main/
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ ├── kotlin/
│ │ │ │ │ └── com/
│ │ │ │ │ └── example/
│ │ │ │ │ └── example/
│ │ │ │ │ └── MainActivity.kt
│ │ │ │ └── res/
│ │ │ │ ├── drawable/
│ │ │ │ │ └── launch_background.xml
│ │ │ │ └── values/
│ │ │ │ └── styles.xml
│ │ │ └── profile/
│ │ │ └── AndroidManifest.xml
│ │ ├── build.gradle
│ │ ├── gradle/
│ │ │ └── wrapper/
│ │ │ └── gradle-wrapper.properties
│ │ ├── gradle.properties
│ │ └── settings.gradle
│ ├── ios/
│ │ ├── .gitignore
│ │ ├── Flutter/
│ │ │ ├── AppFrameworkInfo.plist
│ │ │ ├── Debug.xcconfig
│ │ │ └── Release.xcconfig
│ │ ├── Runner/
│ │ │ ├── AppDelegate.swift
│ │ │ ├── Assets.xcassets/
│ │ │ │ ├── AppIcon.appiconset/
│ │ │ │ │ └── Contents.json
│ │ │ │ └── LaunchImage.imageset/
│ │ │ │ ├── Contents.json
│ │ │ │ └── README.md
│ │ │ ├── Base.lproj/
│ │ │ │ ├── LaunchScreen.storyboard
│ │ │ │ └── Main.storyboard
│ │ │ ├── Info.plist
│ │ │ └── Runner-Bridging-Header.h
│ │ ├── Runner.xcodeproj/
│ │ │ ├── project.pbxproj
│ │ │ ├── project.xcworkspace/
│ │ │ │ ├── contents.xcworkspacedata
│ │ │ │ └── xcshareddata/
│ │ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ │ └── WorkspaceSettings.xcsettings
│ │ │ └── xcshareddata/
│ │ │ └── xcschemes/
│ │ │ └── Runner.xcscheme
│ │ └── Runner.xcworkspace/
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata/
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
│ ├── lib/
│ │ └── main.dart
│ ├── pubspec.yaml
│ ├── test/
│ │ └── widget_test.dart
│ └── web/
│ ├── index.html
│ └── manifest.json
├── lib/
│ ├── bottom_sheet/
│ │ ├── multi_select_bottom_sheet.dart
│ │ └── multi_select_bottom_sheet_field.dart
│ ├── chip_display/
│ │ └── multi_select_chip_display.dart
│ ├── chip_field/
│ │ └── multi_select_chip_field.dart
│ ├── dialog/
│ │ ├── mult_select_dialog.dart
│ │ └── multi_select_dialog_field.dart
│ ├── multi_select_flutter.dart
│ └── util/
│ ├── horizontal_scrollbar.dart
│ ├── multi_select_actions.dart
│ ├── multi_select_item.dart
│ └── multi_select_list_type.dart
├── pubspec.yaml
└── test/
└── multi_select_flutter_test.dart
SYMBOL INDEX (62 symbols across 13 files)
FILE: example/lib/main.dart
function main (line 4) | void main()
class MyApp (line 8) | class MyApp extends StatelessWidget {
method build (line 10) | Widget build(BuildContext context)
class Animal (line 22) | class Animal {
class MyHomePage (line 32) | class MyHomePage extends StatefulWidget {
method createState (line 36) | _MyHomePageState createState()
class _MyHomePageState (line 39) | class _MyHomePageState extends State<MyHomePage> {
method initState (line 80) | void initState()
method build (line 86) | Widget build(BuildContext context)
FILE: example/test/widget_test.dart
function main (line 13) | void main()
FILE: lib/bottom_sheet/multi_select_bottom_sheet.dart
class MultiSelectBottomSheet (line 7) | class MultiSelectBottomSheet<T> extends StatefulWidget
method createState (line 110) | _MultiSelectBottomSheetState<T> createState()
class _MultiSelectBottomSheetState (line 114) | class _MultiSelectBottomSheetState<T> extends State<MultiSelectBottomShe...
method initState (line 122) | void initState()
method _buildListItem (line 139) | Widget _buildListItem(MultiSelectItem<T> item)
method _buildChipItem (line 180) | Widget _buildChipItem(MultiSelectItem<T> item)
method build (line 225) | Widget build(BuildContext context)
FILE: lib/bottom_sheet/multi_select_bottom_sheet_field.dart
class MultiSelectBottomSheetField (line 10) | class MultiSelectBottomSheetField<V> extends FormField<List<V>> {
class _MultiSelectBottomSheetFieldView (line 200) | class _MultiSelectBottomSheetFieldView<V> extends StatefulWidget {
method createState (line 308) | __MultiSelectBottomSheetFieldViewState createState()
class __MultiSelectBottomSheetFieldViewState (line 312) | class __MultiSelectBottomSheetFieldViewState<V>
method initState (line 317) | void initState()
method didUpdateWidget (line 323) | void didUpdateWidget(_MultiSelectBottomSheetFieldView<V> oldWidget)
method _buildInheritedChipDisplay (line 336) | Widget _buildInheritedChipDisplay()
method build (line 443) | Widget build(BuildContext context)
FILE: lib/chip_display/multi_select_chip_display.dart
class MultiSelectChipDisplay (line 7) | class MultiSelectChipDisplay<V> extends StatelessWidget {
method build (line 87) | Widget build(BuildContext context)
method _buildItem (line 129) | Widget _buildItem(MultiSelectItem<V> item, BuildContext context)
FILE: lib/chip_field/multi_select_chip_field.dart
class MultiSelectChipField (line 4) | class MultiSelectChipField<V> extends FormField<List<V>> {
class _MultiSelectChipFieldView (line 159) | class _MultiSelectChipFieldView<V> extends StatefulWidget
method createState (line 254) | __MultiSelectChipFieldViewState createState()
class __MultiSelectChipFieldViewState (line 258) | class __MultiSelectChipFieldViewState<V>
method initState (line 267) | void initState()
method didUpdateWidget (line 276) | void didUpdateWidget(_MultiSelectChipFieldView<V> oldWidget)
method build (line 297) | Widget build(BuildContext context)
method _buildItem (line 467) | Widget _buildItem(MultiSelectItem<V?> item)
FILE: lib/dialog/mult_select_dialog.dart
class MultiSelectDialog (line 7) | class MultiSelectDialog<T> extends StatefulWidget with MultiSelectAction...
method createState (line 109) | State<StatefulWidget> createState()
class _MultiSelectDialogState (line 112) | class _MultiSelectDialogState<T> extends State<MultiSelectDialog<T>> {
method initState (line 120) | void initState()
method _buildListItem (line 137) | Widget _buildListItem(MultiSelectItem<T> item)
method _buildChipItem (line 178) | Widget _buildChipItem(MultiSelectItem<T> item)
method build (line 218) | Widget build(BuildContext context)
FILE: lib/dialog/multi_select_dialog_field.dart
class MultiSelectDialogField (line 10) | class MultiSelectDialogField<V> extends FormField<List<V>> {
class _MultiSelectDialogFieldView (line 189) | class _MultiSelectDialogFieldView<V> extends StatefulWidget {
method createState (line 291) | __MultiSelectDialogFieldViewState createState()
class __MultiSelectDialogFieldViewState (line 295) | class __MultiSelectDialogFieldViewState<V>
method initState (line 300) | void initState()
method didUpdateWidget (line 306) | void didUpdateWidget(_MultiSelectDialogFieldView<V> oldWidget)
method _buildInheritedChipDisplay (line 319) | Widget _buildInheritedChipDisplay()
method build (line 420) | Widget build(BuildContext context)
FILE: lib/util/horizontal_scrollbar.dart
class HorizontalScrollBar (line 1) | class HorizontalScrollBar {
FILE: lib/util/multi_select_actions.dart
class MultiSelectActions (line 5) | class MultiSelectActions<T> {
method onItemCheckedChange (line 6) | List<T> onItemCheckedChange(
method onCancelTap (line 17) | void onCancelTap(BuildContext ctx, List<T> initiallySelectedValues)
method onConfirmTap (line 23) | void onConfirmTap(
method updateSearchQuery (line 33) | List<MultiSelectItem<T>> updateSearchQuery(
method onSearchTap (line 49) | bool onSearchTap(bool showSearch)
method separateSelected (line 53) | List<MultiSelectItem<T>> separateSelected(List<MultiSelectItem<T>> list)
FILE: lib/util/multi_select_item.dart
class MultiSelectItem (line 2) | class MultiSelectItem<T> {
FILE: lib/util/multi_select_list_type.dart
type MultiSelectListType (line 2) | enum MultiSelectListType { LIST, CHIP }
FILE: test/multi_select_flutter_test.dart
function main (line 4) | void main()
Condensed preview — 59 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (185K chars).
[
{
"path": ".gitignore",
"chars": 1551,
"preview": "# Miscellaneous\n*.class\n*.log\n*.pyc\n*.swp\n.DS_Store\n.atom/\n.buildlog/\n.history\n.svn/\n\n# IntelliJ related\n*.iml\n*.ipr\n*.i"
},
{
"path": ".metadata",
"chars": 309,
"preview": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrade"
},
{
"path": "CHANGELOG.md",
"chars": 13107,
"preview": "\n# Changelog\n\nAll notable changes to this project will be documented in this file.\n\n## [4.1.3] - 2022-11-27\n### Changed\n"
},
{
"path": "LICENSE",
"chars": 1278,
"preview": "Copyright (c) 2020, Chris Botha\n\nRedistribution and use in source and binary forms, with or without modification, are pe"
},
{
"path": "README.md",
"chars": 5942,
"preview": "\n# Multi Select Flutter\n\n[](https://pub.dev/package"
},
{
"path": "analysis_options.yaml",
"chars": 112,
"preview": "analyzer:\n exclude: [build/**]\n strong-mode:\n implicit-casts: true\n\nlinter:\n rules:\n - camel_case_types"
},
{
"path": "example/.gitignore",
"chars": 692,
"preview": "# Miscellaneous\n*.class\n*.log\n*.pyc\n*.swp\n.DS_Store\n.atom/\n.buildlog/\n.history\n.svn/\n\n# IntelliJ related\n*.iml\n*.ipr\n*.i"
},
{
"path": "example/.metadata",
"chars": 305,
"preview": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrade"
},
{
"path": "example/README.md",
"chars": 119,
"preview": "# example\n\nExample for multi_select_flutter package.\n\nCheck out some different ways to implement multi_select_flutter.\n"
},
{
"path": "example/android/.gitignore",
"chars": 110,
"preview": "gradle-wrapper.jar\n/.gradle\n/captures/\n/gradlew\n/gradlew.bat\n/local.properties\nGeneratedPluginRegistrant.java\n"
},
{
"path": "example/android/app/build.gradle",
"chars": 1766,
"preview": "def localProperties = new Properties()\ndef localPropertiesFile = rootProject.file('local.properties')\nif (localPropertie"
},
{
"path": "example/android/app/src/debug/AndroidManifest.xml",
"chars": 327,
"preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"com.example.example\">\n <!-- Flutter"
},
{
"path": "example/android/app/src/main/AndroidManifest.xml",
"chars": 2557,
"preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"com.example.example\">\n <!-- io.flut"
},
{
"path": "example/android/app/src/main/kotlin/com/example/example/MainActivity.kt",
"chars": 124,
"preview": "package com.example.example\n\nimport io.flutter.embedding.android.FlutterActivity\n\nclass MainActivity: FlutterActivity() "
},
{
"path": "example/android/app/src/main/res/drawable/launch_background.xml",
"chars": 434,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Modify this file to customize your launch splash screen -->\n<layer-list xmln"
},
{
"path": "example/android/app/src/main/res/values/styles.xml",
"chars": 951,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <!-- Theme applied to the Android Window while the process is sta"
},
{
"path": "example/android/app/src/profile/AndroidManifest.xml",
"chars": 327,
"preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"com.example.example\">\n <!-- Flutter"
},
{
"path": "example/android/build.gradle",
"chars": 582,
"preview": "buildscript {\n ext.kotlin_version = '1.3.50'\n repositories {\n google()\n jcenter()\n }\n\n depende"
},
{
"path": "example/android/gradle/wrapper/gradle-wrapper.properties",
"chars": 233,
"preview": "#Fri Jun 23 08:50:38 CEST 2017\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER"
},
{
"path": "example/android/gradle.properties",
"chars": 104,
"preview": "org.gradle.jvmargs=-Xmx1536M\nandroid.enableR8=true\nandroid.useAndroidX=true\nandroid.enableJetifier=true\n"
},
{
"path": "example/android/settings.gradle",
"chars": 627,
"preview": "// Copyright 2014 The Flutter Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style licens"
},
{
"path": "example/ios/.gitignore",
"chars": 542,
"preview": "*.mode1v3\n*.mode2v3\n*.moved-aside\n*.pbxuser\n*.perspectivev3\n**/*sync/\n.sconsign.dblite\n.tags*\n**/.vagrant/\n**/DerivedDat"
},
{
"path": "example/ios/Flutter/AppFrameworkInfo.plist",
"chars": 794,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "example/ios/Flutter/Debug.xcconfig",
"chars": 30,
"preview": "#include \"Generated.xcconfig\"\n"
},
{
"path": "example/ios/Flutter/Release.xcconfig",
"chars": 30,
"preview": "#include \"Generated.xcconfig\"\n"
},
{
"path": "example/ios/Runner/AppDelegate.swift",
"chars": 404,
"preview": "import UIKit\nimport Flutter\n\n@UIApplicationMain\n@objc class AppDelegate: FlutterAppDelegate {\n override func applicatio"
},
{
"path": "example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json",
"chars": 2519,
"preview": "{\n \"images\" : [\n {\n \"size\" : \"20x20\",\n \"idiom\" : \"iphone\",\n \"filename\" : \"Icon-App-20x20@2x.png\",\n "
},
{
"path": "example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json",
"chars": 391,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"LaunchImage.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md",
"chars": 336,
"preview": "# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in"
},
{
"path": "example/ios/Runner/Base.lproj/LaunchScreen.storyboard",
"chars": 2377,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard"
},
{
"path": "example/ios/Runner/Base.lproj/Main.storyboard",
"chars": 1605,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard"
},
{
"path": "example/ios/Runner/Info.plist",
"chars": 1526,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "example/ios/Runner/Runner-Bridging-Header.h",
"chars": 38,
"preview": "#import \"GeneratedPluginRegistrant.h\"\n"
},
{
"path": "example/ios/Runner.xcodeproj/project.pbxproj",
"chars": 19414,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
"chars": 152,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"group:Runner.xcodepr"
},
{
"path": "example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
"chars": 238,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
"chars": 226,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme",
"chars": 3291,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"1020\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "example/ios/Runner.xcworkspace/contents.xcworkspacedata",
"chars": 152,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"group:Runner.xcodepr"
},
{
"path": "example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
"chars": 238,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
"chars": 226,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "example/lib/main.dart",
"chars": 9285,
"preview": "import 'package:flutter/material.dart';\nimport 'package:multi_select_flutter/multi_select_flutter.dart';\n\nvoid main() {\n"
},
{
"path": "example/pubspec.yaml",
"chars": 2125,
"preview": "name: example\ndescription: A new Flutter project.\n\n# The following line prevents the package from being accidentally pub"
},
{
"path": "example/test/widget_test.dart",
"chars": 1046,
"preview": "// This is a basic Flutter widget test.\n//\n// To perform an interaction with a widget in your test, use the WidgetTester"
},
{
"path": "example/web/index.html",
"chars": 3926,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <!--\n If you are serving your web app in a path other than the root, change the\n h"
},
{
"path": "example/web/manifest.json",
"chars": 910,
"preview": "{\n \"name\": \"example\",\n \"short_name\": \"example\",\n \"start_url\": \".\",\n \"display\": \"standalone\",\n \"background"
},
{
"path": "lib/bottom_sheet/multi_select_bottom_sheet.dart",
"chars": 13405,
"preview": "import 'package:flutter/material.dart';\nimport '../util/multi_select_item.dart';\nimport '../util/multi_select_actions.da"
},
{
"path": "lib/bottom_sheet/multi_select_bottom_sheet_field.dart",
"chars": 17548,
"preview": "import 'package:collection/collection.dart' show IterableExtension;\nimport 'package:flutter/material.dart';\nimport '../u"
},
{
"path": "lib/chip_display/multi_select_chip_display.dart",
"chars": 5295,
"preview": "import 'package:flutter/material.dart';\nimport '../util/horizontal_scrollbar.dart';\nimport '../util/multi_select_item.da"
},
{
"path": "lib/chip_field/multi_select_chip_field.dart",
"chars": 20418,
"preview": "import 'package:flutter/material.dart';\nimport '../multi_select_flutter.dart';\n\nclass MultiSelectChipField<V> extends Fo"
},
{
"path": "lib/dialog/mult_select_dialog.dart",
"chars": 10901,
"preview": "import 'package:flutter/material.dart';\nimport '../util/multi_select_actions.dart';\nimport '../util/multi_select_item.da"
},
{
"path": "lib/dialog/multi_select_dialog_field.dart",
"chars": 16543,
"preview": "import 'package:collection/collection.dart' show IterableExtension;\nimport 'package:flutter/material.dart';\nimport '../u"
},
{
"path": "lib/multi_select_flutter.dart",
"chars": 470,
"preview": "export 'util/multi_select_item.dart';\nexport 'util/multi_select_list_type.dart';\nexport 'util/multi_select_actions.dart'"
},
{
"path": "lib/util/horizontal_scrollbar.dart",
"chars": 112,
"preview": "class HorizontalScrollBar {\n final bool isAlwaysShown;\n\n HorizontalScrollBar({this.isAlwaysShown = false});\n}\n"
},
{
"path": "lib/util/multi_select_actions.dart",
"chars": 2192,
"preview": "import 'package:flutter/material.dart';\nimport 'multi_select_item.dart';\n\n/// Contains common actions that are used by d"
},
{
"path": "lib/util/multi_select_item.dart",
"chars": 192,
"preview": "/// A model class used to represent a selectable item.\nclass MultiSelectItem<T> {\n final T value;\n final String label;"
},
{
"path": "lib/util/multi_select_list_type.dart",
"chars": 140,
"preview": "/// Used by MultiSelectDialog and MultiSelectBottomSheet to determine which type of list to render.\nenum MultiSelectList"
},
{
"path": "pubspec.yaml",
"chars": 1816,
"preview": "name: multi_select_flutter\ndescription: A flexible multi select package for Flutter. Make multi select widgets the way y"
},
{
"path": "test/multi_select_flutter_test.dart",
"chars": 138,
"preview": "// import 'package:flutter_test/flutter_test.dart';\n// import 'package:flutter_multi_select/flutter_multi_select.dart';\n"
}
]
About this extraction
This page contains the full source code of the CHB61/multi_select_flutter GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 59 files (168.5 KB), approximately 41.4k tokens, and a symbol index with 62 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.