This project demonstrates the creation of a modern, user-friendly multi-select workout selection screen in Flutter. It addresses the common problem of clunky and hard-to-scale single dropdown lists for choosing multiple items. The aim was to rebuild an old gym app's workout selection feature, transforming it into a clean, simple, and efficient interface where users can easily tap to select multiple workouts.
This repository showcases a simplified version of the solution, focusing on custom widget creation and local state management with setState.
- Custom Choice Chip Widget: A tailor-made chip widget (
ChoiceChipWidget) built from scratch for complete control over appearance and behavior.- Displays an icon and a title.
- Visually changes based on selection state (selected/unselected).
- Includes dynamic background color, content color, border color, and shadow color.
- Customizable rounded corners and border.
- Shadow effect on selection for a "pop" effect.
- Built-in ripple effect on tap using
InkWell. - Error handling for image assets.
- Multi-Select Functionality: Allows users to tap and select multiple workout options.
- Dynamic Chip Generation: Workout chips are dynamically generated from a list/map of available workouts.
- State Management for Selections:
- Uses a
Map<String, bool>to track the selection status of each workout. - Manages state using
StatefulWidgetandsetState()for UI updates upon interaction.
- Uses a
- Responsive Layout with
Wrap: Chips are displayed in aWrapwidget, allowing them to flow to the next line if horizontal space is limited.- Chips are centered within the wrap.
- Adjustable spacing between chips (
runSpacing) and internal padding for better visual appeal.
- Conditional Continue Button: An
ActionButtonis provided which:- Can be enabled/disabled based on logic.
- In this project, it's enabled only when the user has selected two or more workouts.
- Clean UI Structure: Utilizes
Scaffold,AppBar,Column,Row,Center, andSpacerfor a well-organized page layout.
Traditional single dropdown lists for multiple selections can be:
- Clunky and Hard to Use: Requiring users to scroll through long lists.
- Difficult to Scale: Adding more options makes the dropdown even more cumbersome.
- Less Engaging: Lacks immediate visual feedback for selected items.
This project provides a solution that is:
- Intuitive: Users can simply tap to select/deselect.
- Efficient: All options (or many) can be visible at once.
- Scalable: Easily handles a growing list of workout options.
- Visually Appealing: Modern look and feel with custom styling.
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes.
- Flutter SDK installed. (See Flutter documentation)
- An IDE like IntelliJ IDEA, Android Studio, or VS Code with the Flutter plugin.
-
Clone the repository:
git clone [https://github.com/YOUR_USERNAME/YOUR_REPOSITORY_NAME.git](https://github.com/YOUR_USERNAME/YOUR_REPOSITORY_NAME.git) cd YOUR_REPOSITORY_NAME -
Create an
assets/icons/folder: At the root of your Flutter project, create a folder namedassets. Insideassets, create another folder namedicons.project_root/ ├── assets/ │ └── icons/ │ └── your_icon1.png │ └── your_icon2.png │ └── ... └── lib/ └── ...- Recommendation: Place your workout icon images (e.g.,
cardio.png,strength.png) inside theassets/icons/folder. The transcript implies using PNGs, but other formats might work.
- Recommendation: Place your workout icon images (e.g.,
-
Declare assets in
pubspec.yaml: Open yourpubspec.yamlfile and add your assets folder under thefluttersection. Ensure correct indentation.flutter: uses-material-design: true assets: - assets/icons/ # Add this line
-
Install dependencies: Run the following command in your project's root directory:
flutter pub get
-
Run the project: Connect a device or start an emulator/simulator, then run:
flutter run
- A hot restart (or full stop and restart) might be necessary after adding assets for Flutter to recognize them.
The main focus of this project is the WorkoutSelectionPage and the custom ChoiceChipWidget.
This stateful widget sets up the main screen:
-
Initialization:
- A
Map<String, bool>calledworkoutSelectionsstores the available workouts and their selection state (e.g.,{'Cardio': false, 'Strength': false}). - A
Map<String, String>calledworkoutIconsstores the path to the icon for each workout (e.g.,{'Cardio': 'assets/icons/cardio.png'}). - An integer
counttracks the number of selected workouts.
- A
-
UI Structure:
- A
Scaffoldprovides the basic page layout with anAppBar. - The
bodyuses aColumnwithMainAxisAlignment.spaceBetweento distribute content. - An instructional
Textwidget ("Choose two or more to continue") is displayed at the top. - A
Centerwidget contains aWrapwidget.- Recommendation: A screenshot of the
Wrapwidget displaying multiple choice chips would be ideal here.
- Recommendation: A screenshot of the
- The
Wrapwidget dynamically generatesChoiceChipWidgetinstances based on theworkoutSelectionsmap.- Each chip's
isSelectedstate is tied to the map's boolean value. - The
onTapcallback updates theworkoutSelectionsmap and thecountusingsetState().
- Each chip's
- A
Spacerpushes the content upwards. - An
ActionButton(the "Continue" button) is at the bottom, wrapped in aRowfor centering. ItsisEnabledproperty is dynamically set based oncount > 1.
- A
This stateless widget defines the custom choice chip:
-
Parameters:
iconPath: String for the asset image.title: String for the workout name.isSelected: Boolean indicating if the chip is currently selected.onTap: Callback function executed when the chip is tapped.
-
Appearance:
- Uses an
InkWellfor tap effects, withborderRadiusto match the chip's shape. - A
ContainerwithBoxDecorationstyles the chip:- Dynamic background color, border color, and shadow based on
isSelected. - Rounded corners (
BorderRadius.circular(24)).
- Dynamic background color, border color, and shadow based on
- A
Rowarranges the icon (Image.asset) and text (Text).- Recommendation: Screenshots showing the chip in its selected and unselected states would be beneficial here. Also, a screenshot showing the fixed
InkWellshadow.
- Recommendation: Screenshots showing the chip in its selected and unselected states would be beneficial here. Also, a screenshot showing the fixed
- Uses an
- Tapping a
ChoiceChipWidgettoggles its selection state. - The
setState()call inWorkoutSelectionPagerebuilds the UI to reflect the change. - The "Continue" button becomes active only when two or more workouts are selected.
Writing tests is a crucial part of developing robust and maintainable Flutter applications. This includes:
- Widget Tests: To verify that your widgets look and behave as expected.
- Unit Tests: To test individual functions, methods, or classes.
- Integration Tests: To test complete app scenarios or larger parts of your app.
For the brevity of this demonstration project and the associated tutorial, detailed test cases have been excluded. However, in a real-world application, you would want to write comprehensive tests for the ChoiceChipWidget, the WorkoutSelectionPage, and the state management logic.
For a detailed walkthrough and live demonstration of building this project, check out the YouTube video: Watch the full video tutorial here!
Contributions are welcome! If you have suggestions for improvements or find any issues, feel free to:
- Fork the repository.
- Create a new branch (
git checkout -b feature/YourFeatureorbugfix/YourBugfix). - Make your changes.
- Commit your changes (
git commit -m 'Add some feature'). - Push to the branch (
git push origin feature/YourFeature). - Open a Pull Request.
Please make sure to update tests as appropriate if you add new features or fix bugs.
If you're working on your own Flutter project and need assistance, or if you have questions about this example, feel free to reach out. You can mention the contact method provided in the video or open an issue on this repository for project-specific questions.
This project is licensed under the MIT License.
