flutter_body_part_selector 1.1.5 copy "flutter_body_part_selector: ^1.1.5" to clipboard
flutter_body_part_selector: ^1.1.5 copied to clipboard

Interactive body selector for Flutter. Tap muscles on SVG body diagram or select programmatically with visual highlighting. Includes built-in assets.

Flutter Body Part Selector #

An interactive body selector package for Flutter that allows users to select muscles on a body diagram. Users can tap on muscles in the SVG body diagram or select them programmatically, with visual highlighting of selected muscles.

⚠️ IMPORTANT: This package includes mandatory SVG assets that must be used. Custom SVG files are not supported.

https://github.com/user-attachments/assets/8ba0b47b-fa72-4055-bee8-26f50427437c

Features #

  • 🎯 Interactive Muscle Selection: Tap on any muscle in the body diagram to select it
  • 🎨 Visual Highlighting: Selected muscles are automatically highlighted with customizable colors
  • 🔄 Front/Back Views: Toggle between front and back body views
  • 📱 Programmatic Control: Select muscles programmatically using the controller
  • 🎛️ Customizable: Customize highlight colors and base colors
  • 📦 Easy to Use: Simple API with minimal setup required - includes all required assets
  • 🎨 Built-in Assets: Package includes mandatory SVG body diagrams (front and back views)

Installation #

Add this to your package's pubspec.yaml file:

dependencies:
  flutter_body_part_selector: ^1.1.5

Then run:

flutter pub get

Usage #

The easiest way to use this package is with the InteractiveBodyWidget:

import 'package:flutter/material.dart';
import 'package:flutter_body_part_selector/flutter_body_part_selector.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: InteractiveBodyWidget(
        // Asset paths are optional - package includes default assets
        onMuscleSelected: (muscle) {
          print('Selected muscle: $muscle');
        },
      ),
    );
  }
}

Advanced Usage #

For more control, use InteractiveBodySvg with BodyMapController:

import 'package:flutter/material.dart';
import 'package:flutter_body_part_selector/flutter_body_part_selector.dart';

class BodySelectorExample extends StatefulWidget {
  @override
  State<BodySelectorExample> createState() => _BodySelectorExampleState();
}

class _BodySelectorExampleState extends State<BodySelectorExample> {
  final controller = BodyMapController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Interactive Body Selector'),
        actions: [
          IconButton(
            icon: const Icon(Icons.flip),
            onPressed: controller.toggleView,
            tooltip: 'Flip view',
          ),
        ],
      ),
      body: AnimatedBuilder(
        animation: controller,
        builder: (context, _) {
          return Column(
            children: [
              if (controller.selectedMuscles.isNotEmpty)
                Container(
                  padding: const EdgeInsets.all(16),
                  color: Colors.blue.shade900,
                  width: double.infinity,
                  child: Text(
                    'Selected: ${controller.selectedMuscles.length} muscles',
                    style: const TextStyle(color: Colors.white),
                    textAlign: TextAlign.center,
                  ),
                ),
              Expanded(
                child: InteractiveBodySvg(
                  isFront: controller.isFront,
                  selectedMuscles: controller.selectedMuscles,
                  onMuscleTap: controller.selectMuscle,
                ),
              ),
            ],
          );
        },
      ),
    );
  }
}

Using the Controller #

The BodyMapController manages the state of the body selector:

Basic Usage

final controller = BodyMapController();

// Select a muscle programmatically (toggles if already selected)
controller.selectMuscle(Muscle.bicepsLeft);

// Clear all selections
controller.clearSelection();

// Toggle between front and back view
controller.toggleView();

// Set specific view
controller.setFrontView();
controller.setBackView();

// Access current state
final selected = controller.selectedMuscles; // Returns Set<Muscle> (read-only getter)
final isFront = controller.isFront; // Writable: can be set directly

// Set entire selection using setter (convenience)
controller.selectedMuscles = {Muscle.bicepsLeft, Muscle.tricepsRight};

Initialization with Pre-selected Muscles

// Create controller with initial selection
final controller = BodyMapController(
  initialSelectedMuscles: {Muscle.bicepsLeft, Muscle.tricepsRight},
  initialDisabledMuscles: {Muscle.chestLeft}, // Optional: pre-disable muscles
  initialIsFront: true, // Optional: start with back view
);

Programmatic Selection Management

final controller = BodyMapController();

// Toggle a muscle's selection state
controller.toggleMuscle(Muscle.bicepsLeft);

// Deselect a specific muscle
controller.deselectMuscle(Muscle.bicepsLeft);

// Set entire selection (replaces current selection)
controller.setSelectedMuscles({
  Muscle.bicepsLeft,
  Muscle.tricepsRight,
  Muscle.chestLeft,
});

// Add multiple muscles to current selection (without clearing)
controller.selectMultiple({
  Muscle.bicepsLeft,
  Muscle.tricepsRight,
});

// Check if a muscle is selected
if (controller.isSelected(Muscle.bicepsLeft)) {
  print('Biceps left is selected');
}

// Get all selected muscles (read-only Set)
final selected = controller.selectedMuscles;
print('Selected ${selected.length} muscles');

Managing Disabled Muscles

final controller = BodyMapController();

// Disable a muscle (locks it, removes from selection)
controller.disableMuscle(Muscle.chestLeft);

// Enable a muscle (unlocks it)
controller.enableMuscle(Muscle.chestLeft);

// Set multiple disabled muscles at once
controller.setDisabledMuscles({
  Muscle.chestLeft,
  Muscle.chestRight,
});

// Check if a muscle is disabled
if (controller.isDisabled(Muscle.chestLeft)) {
  print('Chest left is disabled');
}

Complete Example: Programmatic Selection Management

import 'package:flutter/material.dart';
import 'package:flutter_body_part_selector/flutter_body_part_selector.dart';

class BodySelectorPage extends StatefulWidget {
  @override
  State<BodySelectorPage> createState() => _BodySelectorPageState();
}

class _BodySelectorPageState extends State<BodySelectorPage> {
  late BodyMapController controller;

  @override
  void initState() {
    super.initState();
    // Initialize with some pre-selected muscles
    controller = BodyMapController(
      initialSelectedMuscles: {Muscle.bicepsLeft, Muscle.tricepsRight},
    );
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Body Selector')),
      body: Column(
        children: [
          // Control buttons
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: Wrap(
              spacing: 8.0,
              children: [
                ElevatedButton(
                  onPressed: () {
                    // Select multiple muscles programmatically
                    controller.selectMultiple({
                      Muscle.bicepsLeft,
                      Muscle.bicepsRight,
                    });
                  },
                  child: const Text('Select Both Biceps'),
                ),
                ElevatedButton(
                  onPressed: () {
                    // Set entire selection
                    controller.setSelectedMuscles({
                      Muscle.chestLeft,
                      Muscle.chestRight,
                    });
                  },
                  child: const Text('Select Chest Only'),
                ),
                ElevatedButton(
                  onPressed: () {
                    // Clear all selections
                    controller.clearSelection();
                  },
                  child: const Text('Clear All'),
                ),
              ],
            ),
          ),
          // Display selected muscles
          AnimatedBuilder(
            animation: controller,
            builder: (context, _) {
              return Container(
                padding: const EdgeInsets.all(16),
                color: Colors.blue.shade900,
                width: double.infinity,
                child: Text(
                  'Selected: ${controller.selectedMuscles.length} muscles',
                  style: const TextStyle(color: Colors.white),
                  textAlign: TextAlign.center,
                ),
              );
            },
          ),
          // Body diagram
          Expanded(
            child: AnimatedBuilder(
              animation: controller,
              builder: (context, _) {
                return InteractiveBodySvg(
                  isFront: controller.isFront,
                  selectedMuscles: controller.selectedMuscles,
                  onMuscleTap: controller.selectMuscle,
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

Customization Options #

Colors and Styling

InteractiveBodySvg(
  isFront: true, // Use package's front body asset automatically
  selectedMuscles: controller.selectedMuscles,
  onMuscleTap: controller.selectMuscle,
  highlightColor: Colors.red.withOpacity(0.7), // Custom highlight color
  baseColor: Colors.white, // Custom base color for unselected muscles
  selectedStrokeWidth: 3.0, // Stroke width for selected muscles
  unselectedStrokeWidth: 1.0, // Stroke width for unselected muscles
)

Size and Layout

InteractiveBodySvg(
  isFront: true, // Use package's front body asset automatically
  selectedMuscles: controller.selectedMuscles,
  onMuscleTap: controller.selectMuscle,
  width: 300, // Fixed width
  height: 600, // Fixed height
  fit: BoxFit.cover, // How to fit the SVG
  alignment: Alignment.center, // Alignment within the widget
)

Selection Behavior

InteractiveBodySvg(
  isFront: true, // Use package's front body asset automatically
  selectedMuscles: controller.selectedMuscles,
  onMuscleTap: controller.selectMuscle,
  enableSelection: true, // Enable/disable tap selection
  hitTestPadding: 15.0, // Padding for hit-testing (makes taps more forgiving)
  onMuscleTapDisabled: (muscle) {
    // Called when a muscle is tapped but selection is disabled
    print('Tapped $muscle but selection is disabled');
  },
)

Using InteractiveBodyWidget

The InteractiveBodyWidget provides a complete solution with built-in UI:

InteractiveBodyWidget(
  frontAsset: 'packages/flutter_body_part_selector/assets/svg/body_front.svg',
  backAsset: 'packages/flutter_body_part_selector/assets/svg/body_back.svg',
  onMuscleSelected: (muscle) {
    print('Selected: $muscle');
  },
  highlightColor: Colors.blue,
  baseColor: Colors.white,
  showFlipButton: true, // Show flip button in app bar
  showClearButton: true, // Show clear button in app bar
  backgroundColor: Colors.black, // Background color
  selectedMuscleHeader: (muscle) {
    // Custom header widget
    return Container(
      padding: EdgeInsets.all(16),
      child: Text('Selected: $muscle'),
    );
  },
)

Available Muscles #

The package supports the following muscles:

Front View #

  • Traps (Left/Right)
  • Delts (Left/Right)
  • Chest (Left/Right)
  • Abs
  • Lats Front (Left/Right)
  • Triceps (Left/Right)
  • Biceps (Left/Right)
  • Biceps Brachialis (Left/Right)
  • Forearms (Left/Right)
  • Quads (Left/Right)
  • Calves (Left/Right)

Back View #

  • Lats Back (Left/Right)
  • Lower Lats Back (Left/Right)
  • Glutes (Left/Right)
  • Hamstrings (Left/Right)
  • Triceps (Left/Right)
  • Delts (Left/Right)
  • Traps (Left/Right)

Assets #

IMPORTANT: This package includes the required SVG body diagrams (front and back views) that are mandatory for the package to work correctly. You must use the package assets - custom SVG files are not supported.

The package assets are pre-configured with the correct muscle IDs and mappings. Using custom assets will result in incorrect behavior.

Using Package Assets #

The package includes default SVG assets that are automatically used by InteractiveBodyWidget. You don't need to specify asset paths:

// Simplest usage - assets are included automatically
InteractiveBodyWidget(
  onMuscleSelected: (muscle) {
    print('Selected: $muscle');
  },
)

For InteractiveBodySvg, you can use the simplified syntax without specifying asset paths:

InteractiveBodySvg(
  isFront: true, // Automatically uses package's front body asset
  selectedMuscles: controller.selectedMuscles,
  onMuscleTap: controller.selectMuscle,
)

Or if you need to specify asset paths explicitly:

InteractiveBodySvg(
  asset: 'packages/flutter_body_part_selector/assets/svg/body_front.svg',
  // ...
)

Note: The package assets are automatically included when you add this package to your pubspec.yaml. No additional asset configuration is required in your app's pubspec.yaml.

API Reference #

InteractiveBodyWidget #

A complete widget with built-in controller and UI. Perfect for quick integration.

Properties:

  • frontAsset (String?, optional): Path to the front body SVG. Defaults to 'packages/flutter_body_part_selector/assets/svg/body_front.svg' if not specified. Custom SVG files are not supported.
  • backAsset (String?, optional): Path to the back body SVG. Defaults to 'packages/flutter_body_part_selector/assets/svg/body_back.svg' if not specified. Custom SVG files are not supported.
  • onMuscleSelected (Function(Muscle)?, optional): Callback when a muscle is selected
  • onSelectionCleared (VoidCallback?, optional): Callback when selection is cleared
  • selectedMuscles (Set
  • initialIsFront (bool, default: true): Initial view (front or back)
  • highlightColor (Color?, optional): Color for highlighting selected muscles
  • baseColor (Color?, optional): Base color for unselected muscles
  • selectedStrokeWidth (double, default: 2.0): Stroke width for selected muscles
  • unselectedStrokeWidth (double, default: 1.0): Stroke width for unselected muscles
  • enableSelection (bool, default: true): Enable/disable selection
  • fit (BoxFit, default: BoxFit.contain): How to fit the SVG
  • hitTestPadding (double, default: 10.0): Padding for hit-testing
  • width (double?, optional): Fixed width
  • height (double?, optional): Fixed height
  • alignment (Alignment, default: Alignment.center): Alignment of SVG
  • showFlipButton (bool, default: true): Show flip button in app bar
  • showClearButton (bool, default: true): Show clear button in app bar
  • appBar (PreferredSizeWidget?, optional): Custom app bar
  • backgroundColor (Color?, optional): Background color
  • selectedMuscleHeader (Widget Function(Muscle)?, optional): Custom header widget

InteractiveBodySvg #

The core widget for displaying the interactive body diagram.

Properties:

  • asset (String?, optional): Custom asset path. If not provided, automatically uses package assets based on [isFront]. Note: Custom SVG files are not supported - only package assets work correctly.
  • isFront (bool, default: true): Whether to show front view. Used when [asset] is not provided to automatically select the correct package asset.
  • selectedMuscles (Set
  • onMuscleTap (Function(Muscle)?, optional): Callback when a muscle is tapped
  • highlightColor (Color?, optional): Color for highlighting selected muscles (default: Colors.blue with opacity)
  • baseColor (Color?, optional): Base color for unselected muscles (default: Colors.white)
  • selectedStrokeWidth (double, default: 2.0): Stroke width for selected muscles
  • unselectedStrokeWidth (double, default: 1.0): Stroke width for unselected muscles
  • enableSelection (bool, default: true): Enable/disable tap selection
  • fit (BoxFit, default: BoxFit.contain): How to fit the SVG
  • hitTestPadding (double, default: 10.0): Padding for hit-testing
  • width (double?, optional): Fixed width
  • height (double?, optional): Fixed height
  • alignment (Alignment, default: Alignment.center): Alignment of SVG
  • onMuscleTapDisabled (Function(Muscle)?, optional): Callback when muscle is tapped but selection is disabled

BodyMapController #

Controller for managing the body selector state.

Constructor:

  • BodyMapController({Set<Muscle>? initialSelectedMuscles, Set<Muscle>? initialDisabledMuscles, bool initialIsFront = true}): Create a controller with optional initial state

Selection Methods (Writable):

  • selectMuscle(Muscle): Select or toggle a muscle (if already selected, deselects it)
  • toggleMuscle(Muscle): Explicitly toggle a muscle's selection state
  • deselectMuscle(Muscle): Deselect a specific muscle
  • setSelectedMuscles(Set<Muscle>): Set the entire selection (replaces current selection)
  • selectMultiple(Set<Muscle>): Add multiple muscles to current selection (without clearing)
  • clearSelection(): Clear all selections

View Methods (Writable):

  • toggleView(): Toggle between front and back view
  • setFrontView(): Set view to front
  • setBackView(): Set view to back

Disabled Muscle Methods (Writable):

  • disableMuscle(Muscle): Disable a muscle (locks it, removes from selection)
  • enableMuscle(Muscle): Enable a muscle (unlocks it)
  • setDisabledMuscles(Set<Muscle>): Set multiple disabled muscles at once

Properties:

  • selectedMuscles (Set
  • disabledMuscles (Set
  • isFront (bool, writable): Whether showing front view. Can be set directly or use view methods.
  • isSelected(Muscle) (bool, read-only): Check if a muscle is selected
  • isDisabled(Muscle) (bool, read-only): Check if a muscle is disabled

Muscle #

Enum representing all available muscles. See the "Available Muscles" section above for the complete list.

Common Pitfalls #

❌ Don't: Try to modify selectedMuscles Set directly #

// ❌ WRONG - This won't work because the Set is unmodifiable
controller.selectedMuscles.add(Muscle.bicepsLeft); // Error!
controller.selectedMuscles.clear(); // Error!

✅ Do: Use the setter or provided methods #

// ✅ CORRECT - Use the setter (replaces entire selection)
controller.selectedMuscles = {Muscle.bicepsLeft, Muscle.tricepsRight};

// ✅ CORRECT - Or use the controller methods
controller.selectMuscle(Muscle.bicepsLeft);
controller.setSelectedMuscles({Muscle.bicepsLeft, Muscle.tricepsRight});
controller.clearSelection();

❌ Don't: Forget to listen to controller changes #

// ❌ WRONG - UI won't update when selection changes
final controller = BodyMapController();
controller.selectMuscle(Muscle.bicepsLeft);
// Widget won't rebuild automatically

✅ Do: Use AnimatedBuilder or listen to changes #

// ✅ CORRECT - Wrap with AnimatedBuilder
AnimatedBuilder(
  animation: controller,
  builder: (context, _) {
    return Text('Selected: ${controller.selectedMuscles.length}');
  },
)

❌ Don't: Create controller in build method #

// ❌ WRONG - Creates new controller on every rebuild
Widget build(BuildContext context) {
  final controller = BodyMapController(); // Don't do this!
  return InteractiveBodySvg(...);
}

✅ Do: Create controller in initState or use late initialization #

// ✅ CORRECT - Create once in initState
class _MyWidgetState extends State<MyWidget> {
  late BodyMapController controller;
  
  @override
  void initState() {
    super.initState();
    controller = BodyMapController();
  }
  
  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }
}

❌ Don't: Forget to dispose the controller #

// ❌ WRONG - Memory leak!
class _MyWidgetState extends State<MyWidget> {
  final controller = BodyMapController();
  // Missing dispose() call
}

✅ Do: Always dispose controllers #

// ✅ CORRECT - Always dispose
@override
void dispose() {
  controller.dispose();
  super.dispose();
}

❌ Don't: Try to use custom SVG assets #

// ❌ WRONG - Custom SVG files are not supported
InteractiveBodySvg(
  asset: 'assets/my_custom_body.svg', // Won't work correctly!
)

✅ Do: Use the package's included assets #

// ✅ CORRECT - Simplest way (automatically uses package assets)
InteractiveBodySvg(
  isFront: true, // Automatically uses package's front body asset
)

// ✅ CORRECT - Or specify asset path explicitly
InteractiveBodySvg(
  asset: 'packages/flutter_body_part_selector/assets/svg/body_front.svg',
)

// ✅ CORRECT - Or use InteractiveBodyWidget which uses defaults automatically
InteractiveBodyWidget(...)

Contributing #

Contributions are welcome! Please feel free to submit a Pull Request.

License #

This project is licensed under the MIT License.

Support #

If you encounter any issues or have questions, please file an issue on the GitHub repository.

1
likes
160
points
141
downloads

Publisher

unverified uploader

Weekly Downloads

Interactive body selector for Flutter. Tap muscles on SVG body diagram or select programmatically with visual highlighting. Includes built-in assets.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter, flutter_svg, xml

More

Packages that depend on flutter_body_part_selector