Skip to content

Conversation

@behnam-deriv
Copy link
Collaborator

@behnam-deriv behnam-deriv commented Jan 2, 2026

Asset Migration Guide: Moving Symbol Icons to Consumer Apps

Overview

This guide documents the migration of market selector symbol icons from the deriv_chart package to consuming applications. This change reduces the bundle size of the chart library by moving consumer-specific assets out of the library.

What Changed

Assets Moved

The following assets have been removed from the deriv_chart package and should now be provided by consuming applications:

  • assets/icons/icon_placeholder.png - Placeholder icon for symbol loading
  • assets/icons/symbols/*.png - All 108 trading symbol icons

Assets Retained

The following asset remains in the deriv_chart package as it's used internally by the library:

  • assets/fonts/quill_icons.ttf - Custom icon font for chart markers

Migration Steps for Consumers

1. Copy Assets to Your App

Copy the following folders from the chart library to your application's assets folder:

assets/
  icons/
    icon_placeholder.png
    symbols/
      *.png (all 108 symbol icons)

2. Update Your pubspec.yaml

Add the assets to your application's pubspec.yaml:

flutter:
  uses-material-design: true
  
  assets:
    - assets/icons/
    - assets/icons/symbols/

3. Update Your Code (If Needed)

The SymbolIcon widget now defaults to loading assets from the consuming application instead of the deriv_chart package.

Before (old usage):

SymbolIcon(symbolCode: 'frxeurusd')

After (no change needed):

SymbolIcon(symbolCode: 'frxeurusd')  // Still works! Now loads from your app

Advanced Usage

Custom Asset Paths

If you want to organize your assets differently, you can customize the paths:

SymbolIcon(
  symbolCode: 'frxeurusd',
  assetPathBuilder: (code) => 'my_custom_path/symbols/$code.png',
  placeholderPath: 'my_custom_path/placeholder.png',
)

Assets in a Different Package

If you have a separate package for assets:

SymbolIcon(
  symbolCode: 'frxeurusd',
  package: 'my_assets_package',
)

Custom Asset Builder

For more complex asset path logic:

SymbolIcon(
  symbolCode: 'frxeurusd',
  assetPathBuilder: (code) {
    // Your custom logic here
    final category = getCategoryForSymbol(code);
    return 'assets/$category/$code.png';
  },
)

Breaking Changes

For Library Consumers

⚠️ Breaking Change: If you're upgrading from a previous version, you must:

  1. Copy the asset files to your app
  2. Add them to your pubspec.yaml
  3. Rebuild your app

Without these steps, symbol icons will not display correctly.

For Library Maintainers

The SymbolIcon widget has been updated with new optional parameters:

  • assetPathBuilder: Custom function to build asset paths
  • placeholderPath: Custom placeholder image path
  • package: Package name for assets (defaults to null for consumer app)

Benefits

Reduced Bundle Size

  • Before: Chart library included ~110 asset files
  • After: Chart library includes only 1 font file
  • Impact: Significant reduction in library bundle size

Consumer Control

  • Consumers can now customize or replace symbol icons
  • Easier to add new symbols without updating the library
  • Better separation of concerns (library vs. consumer assets)

Flexibility

  • Support for custom asset organization
  • Support for assets in separate packages
  • Graceful fallback handling

Troubleshooting

Icons Not Displaying

Problem: Symbol icons show as placeholder or error icon.

Solution:

  1. Verify assets are copied to your app's assets/ folder
  2. Check pubspec.yaml includes the asset paths
  3. Run flutter clean and flutter pub get
  4. Rebuild your app

Wrong Asset Paths

Problem: Assets are organized differently in your app.

Solution: Use the assetPathBuilder parameter:

SymbolIcon(
  symbolCode: 'frxeurusd',
  assetPathBuilder: (code) => 'your/custom/path/$code.png',
)

Placeholder Not Found

Problem: Placeholder image not displaying correctly.

Solution: Specify custom placeholder path:

SymbolIcon(
  symbolCode: 'frxeurusd',
  placeholderPath: 'your/custom/placeholder.png',
)

Example Implementation

See the example app in the repository for a complete implementation:

  • Assets location: example/assets/icons/
  • Configuration: example/pubspec.yaml
  • Usage: example/lib/main.dart

Questions?

If you have questions or issues with the migration, please:

  1. Check the example app implementation
  2. Review the SymbolIcon documentation
  3. Open an issue on GitHub

Related Files

  • lib/src/widgets/market_selector/symbol_icon.dart - Updated widget implementation
  • example/pubspec.yaml - Example asset configuration
  • pubspec.yaml - Library configuration (assets removed)

Summary by Sourcery

Move symbol icon assets responsibility from the chart library to consuming apps and make the SymbolIcon widget configurable for external asset locations.

New Features:

  • Allow SymbolIcon to use customizable asset paths, placeholder paths, and asset packages via new optional parameters.

Enhancements:

  • Update SymbolIcon to default to loading assets from the consuming application instead of the deriv_chart package.

Build:

  • Remove symbol icon assets from the main package pubspec and configure them in the example app's pubspec instead.

@sourcery-ai
Copy link

sourcery-ai bot commented Jan 2, 2026

Reviewer's Guide

Refactors the SymbolIcon widget so that symbol PNG assets are no longer bundled with the deriv_chart package but are instead expected from consuming apps, adding configuration hooks for custom asset locations while wiring the example app to host these assets and removing them from the library’s pubspec.

Sequence diagram for SymbolIcon asset resolution and fallback

sequenceDiagram
  participant ConsumerApp
  participant SymbolIcon
  participant FlutterAssetBundle

  ConsumerApp->>SymbolIcon: create(symbolCode, assetPathBuilder?, placeholderPath?, package?)
  activate SymbolIcon

  alt custom assetPathBuilder provided
    SymbolIcon->>SymbolIcon: build path via assetPathBuilder(symbolCode)
  else default assetPathBuilder
    SymbolIcon->>SymbolIcon: build path via getSymbolAssetPath(symbolCode)
  end

  SymbolIcon->>FlutterAssetBundle: load placeholder (placeholderPath or kDefaultPlaceholderPath, package)
  SymbolIcon->>FlutterAssetBundle: load symbol image (resolved path, package)

  alt image load succeeds
    FlutterAssetBundle-->>SymbolIcon: symbol image bytes
    SymbolIcon-->>ConsumerApp: render symbol image with fade
  else image load fails
    FlutterAssetBundle-->>SymbolIcon: error
    SymbolIcon->>FlutterAssetBundle: load placeholder again (placeholderPath or kDefaultPlaceholderPath, package)
    FlutterAssetBundle-->>SymbolIcon: placeholder image bytes
    SymbolIcon-->>ConsumerApp: render placeholder image
  end

  deactivate SymbolIcon
Loading

Class diagram for updated SymbolIcon widget

classDiagram
  class SymbolIcon {
    +SymbolIcon(symbolCode:String, width:double = 32, height:double = 32, fadeDuration:Duration = 50ms, assetPathBuilder:Function(String) = null, placeholderPath:String = null, package:String = null)
  }

  class AssetImage {
  }

  class FadeInImage {
  }

  class SymbolAssetPathHelper {
    +getSymbolAssetPath(assetCode:String) String
  }

  class SymbolIconConstants {
    +kDefaultPlaceholderPath : String = assets/icons/icon_placeholder.png
  }

  SymbolIcon --|> FadeInImage
  SymbolIcon ..> AssetImage : uses
  SymbolIcon ..> SymbolAssetPathHelper : uses
  SymbolIcon ..> SymbolIconConstants : uses
Loading

File-Level Changes

Change Details Files
Make SymbolIcon load its assets from the consuming application and allow customization of asset and placeholder paths.
  • Change default symbol asset path helper to point to consumer app paths instead of package-internal assets.
  • Replace hardcoded placeholder constant with a public default placeholder path constant intended to be provided by consumers.
  • Extend the SymbolIcon constructor with optional assetPathBuilder, placeholderPath, and package parameters to customize asset resolution.
  • Update FadeInImage configuration to use the new parameters and to no longer reference the deriv_chart package by default.
  • Ensure the image error builder falls back to the configurable placeholder asset instead of the package asset.
lib/src/widgets/market_selector/symbol_icon.dart
Shift symbol icon assets from the library package into the example app configuration.
  • Add assets/icons/ and assets/icons/symbols/ entries to the example app’s flutter assets configuration so it serves symbol icons.
  • Remove assets/icons/ and assets/icons/symbols/ declarations from the main package’s flutter assets configuration, leaving only the QuillIcons font.
example/pubspec.yaml
pubspec.yaml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-actions
Copy link

github-actions bot commented Jan 2, 2026

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

OpenSSF Scorecard

PackageVersionScoreDetails

Scanned Manifest Files

@claude
Copy link

claude bot commented Jan 2, 2026

Claude finished @behnam-deriv's task —— View job


PR Review: Asset Migration to Example App

I've completed my review of this PR. Overall, this is a well-executed refactoring that successfully moves assets to consumer apps while maintaining backward compatibility through sensible defaults. Here's my detailed feedback:


✅ High Priority - Strengths

Excellent Design Decisions

  1. Backward-compatible API: The SymbolIcon widget changes are well-designed with optional parameters that default to consumer app assets
  2. Clear migration path: The PR description provides comprehensive migration guidance
  3. Flexibility: The assetPathBuilder, placeholderPath, and package parameters provide excellent customization options
  4. Bundle size reduction: Successfully removed 110 asset files from the library

Code Quality

  • Clean, well-documented code with extensive dartdoc comments
  • Follows Flutter/Dart best practices
  • Maintains immutability where appropriate
  • Good separation of concerns

⚠️ Medium Priority - Issues & Suggestions

1. Breaking Change Handling (Medium)

Issue: This IS a breaking change for existing consumers, but the migration isn't clearly surfaced.

Current behavior:

  • Existing apps using SymbolIcon will break immediately after upgrade because assets are no longer bundled
  • No deprecation period or runtime warnings

Suggestions:

// Option A: Add a debug assertion to help developers during migration
SymbolIcon({
  required String symbolCode,
  // ... other parameters
}) : super(
      // ... existing code
    ) {
  assert(
    package != null || _assetsExistInConsumerApp(),
    'SymbolIcon assets must be provided by the consuming app. '
    'See migration guide: [link]',
  );
}

Or consider a major version bump (0.5.0) to clearly signal the breaking change, given the library is still pre-1.0.

2. Error Handling (Medium)

Issue: The imageErrorBuilder falls back to the placeholder, but if the placeholder itself is missing, users get a cryptic error.

Location: lib/src/widgets/market_selector/symbol_icon.dart:107-112

Suggestion: Wrap the error builder with better error handling:

imageErrorBuilder: (context, error, stackTrace) {
  try {
    return Image.asset(
      placeholderPath ?? kDefaultPlaceholderPath,
      package: package,
      width: width,
      height: height,
    );
  } catch (e) {
    // Fallback to an Icon when assets are completely missing
    return Icon(Icons.image_not_supported, size: width);
  }
}

3. Test Coverage Gap (Medium)

Issue: The existing market_selector_test.dart tests SymbolIcon indirectly through AssetItem, but there are no tests for:

  • Loading assets from consumer app (new default behavior)
  • Custom assetPathBuilder parameter
  • Custom placeholderPath parameter
  • Custom package parameter
  • Error scenarios when assets are missing

Suggestion: Add unit tests for SymbolIcon:

// test/widgets/symbol_icon_test.dart
testWidgets('SymbolIcon loads from consumer app by default', (tester) async {
  // Test default behavior
});

testWidgets('SymbolIcon uses custom asset path builder', (tester) async {
  // Test assetPathBuilder parameter
});

testWidgets('SymbolIcon handles missing assets gracefully', (tester) async {
  // Test error scenarios
});

4. Documentation Enhancement (Medium)

Issue: The migration guide is excellent, but should be referenced in the widget's documentation.

Location: lib/src/widgets/market_selector/symbol_icon.dart:19-24

Suggestion: Add a link to the migration guide:

/// This widget expects assets to be provided by the consuming application.
/// By default, it looks for assets in the consuming app's assets folder
/// (not in the deriv_chart package).
///
/// **BREAKING CHANGE in v0.4.1**: Assets are no longer bundled with the library.
/// See the migration guide for upgrade instructions: [link to migration doc]

💡 Low Priority - Nice to Haves

1. Const Constructor (Low)

The SymbolIcon constructor could potentially be const with some refactoring, which would improve performance. However, this would require making assetPathBuilder a constant, which limits flexibility. Current approach is fine.

2. Asset Verification Script (Low)

Consider adding a script or test that verifies all 108 symbols referenced in the codebase have corresponding assets. This would help catch missing assets during development.

3. CHANGELOG Entry (Low)

Ensure this breaking change is documented in CHANGELOG.md with clear migration instructions.


🔍 Specific Code Review Notes

symbol_icon.dart Changes

Good:

  • Line 10-11: getSymbolAssetPath correctly lowercases symbol codes
  • Line 17: Good use of const for default placeholder path
  • Line 63-116: Well-structured widget with excellent documentation
  • Line 88-99: Proper use of null-aware operators for optional parameters

pubspec.yaml Changes

Good:

  • Correctly removed assets/icons/ declarations
  • Retained assets/fonts/quill_icons.ttf (used by chart markers)

example/pubspec.yaml Changes

Good:

  • Assets properly declared for example app
  • Lines 33-35: Correct asset path configuration

Existing Usage

Good:

  • asset_item.dart:80 and market_selector_button.dart:68-70 use simple default constructor
  • These will work seamlessly once consumers add assets to their apps

🎯 Summary & Recommendation

Priority Actions:

  1. Add test coverage for the new SymbolIcon parameters and error scenarios
  2. Enhance error handling to prevent cryptic errors when assets are missing
  3. Add CHANGELOG entry documenting this breaking change
  4. Consider version bump to 0.5.0 to signal breaking change

Overall Assessment:

This PR successfully achieves its goal of reducing bundle size while providing a flexible, well-documented API. The main concerns are around test coverage and helping developers discover and handle missing assets during migration.

Recommendation: ✅ Approve with suggestions - The code is production-ready, but addressing the test coverage and error handling would make this more robust.


Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `lib/src/widgets/market_selector/symbol_icon.dart:84-87` </location>
<code_context>
     required String symbolCode,
     double width = 32,
     double height = 32,
     Duration fadeDuration = const Duration(milliseconds: 50),
+    String Function(String)? assetPathBuilder,
+    String? placeholderPath,
+    String? package,
   }) : super(
           width: width,
</code_context>

<issue_to_address>
**issue:** The `fadeDuration` parameter is declared but never used in the `FadeInImage` configuration.

Since `fadeDuration` is exposed in the constructor but not passed to any `FadeInImage` property (e.g. `fadeInDuration` / `fadeOutDuration`), callers will expect it to affect the animation when it does not. Either wire it through to the relevant `FadeInImage` field(s) or remove the parameter to avoid a misleading API surface.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@claude
Copy link

claude bot commented Jan 2, 2026

Claude finished @behnam-deriv's task —— View job


PR Review: Asset Migration to Example App

I've completed a comprehensive review of this PR. This is a well-executed refactoring that successfully moves assets to consumer apps while maintaining backward compatibility. Here's my detailed analysis:

Tasks

  • Examine the current commit history
  • Review SymbolIcon widget implementation
  • Check test coverage
  • Review pubspec.yaml changes
  • Analyze migration strategy
  • Provide structured feedback

✅ Strengths

Excellent Design Decisions

  1. Backward-compatible API: The SymbolIcon widget changes are well-designed with optional parameters (assetPathBuilder, placeholderPath, package) that default to consumer app assets
  2. Clear migration path: The PR description provides comprehensive migration guidance with examples
  3. Flexibility: Parameters provide excellent customization options for different use cases
  4. Bundle size reduction: Successfully removed 110 asset files from the library (108 symbols + 1 placeholder)
  5. Clean implementation: Code follows Flutter/Dart best practices with clear documentation

Code Quality

  • Well-documented with extensive dartdoc comments
  • Good separation of concerns
  • Proper use of null-aware operators
  • Clear helper functions (getSymbolAssetPath, kDefaultPlaceholderPath)

Test Coverage Improvement

  • Added image_mock_helper.dart to properly mock image assets in tests
  • Existing tests updated to use the new helper (lines 20-26 in market_selector_test.dart)

⚠️ Issues & Recommendations

🔴 HIGH PRIORITY

1. Unused fadeDuration Parameter (Bug)

Issue: The fadeDuration parameter in SymbolIcon constructor is declared but was never actually used until the latest commit.

Location: lib/src/widgets/market_selector/symbol_icon.dart:84

Current Status: ✅ FIXED in latest commit (5c7e8b2) - The parameter is now properly connected to fadeInDuration and fadeOutDuration in lines 113-114.

Note: This was also identified by @sourcery-ai in the review comments.


🟡 MEDIUM PRIORITY

2. Error Handling for Missing Assets

Issue: If the placeholder image itself is missing, users will get a Flutter error instead of a graceful fallback.

Location: lib/src/widgets/market_selector/symbol_icon.dart:107-112

Current Code:

imageErrorBuilder: (context, error, stackTrace) =>
    Image.asset(
      placeholderPath ?? kDefaultPlaceholderPath,
      package: package,
      width: width,
      height: height,
    ),

Recommendation: Add a nested error handler:

imageErrorBuilder: (context, error, stackTrace) {
  try {
    return Image.asset(
      placeholderPath ?? kDefaultPlaceholderPath,
      package: package,
      width: width,
      height: height,
    );
  } catch (e) {
    // Fallback to a simple icon when assets are completely missing
    return Icon(Icons.image_not_supported, size: width);
  }
},

This would help developers during migration when they forget to add assets.


3. Test Coverage Gaps

Issue: While existing tests were updated with the image mock helper, there are no direct tests for the new SymbolIcon functionality:

  • Loading assets from consumer app (new default behavior)
  • Custom assetPathBuilder parameter
  • Custom placeholderPath parameter
  • Custom package parameter
  • Error scenarios when assets are missing

Recommendation: Add unit tests:

// test/widgets/symbol_icon_test.dart
testWidgets('SymbolIcon loads from consumer app by default', (tester) async {
  // Test default behavior
});

testWidgets('SymbolIcon uses custom asset path builder', (tester) async {
  // Test assetPathBuilder parameter
});

testWidgets('SymbolIcon handles missing assets gracefully', (tester) async {
  // Test error scenarios
});

4. Breaking Change Documentation

Issue: This IS a breaking change for existing consumers, but it's not clearly surfaced in the widget documentation itself.

Location: lib/src/widgets/market_selector/symbol_icon.dart:19-24

Recommendation: Add a prominent note in the dartdoc:

/// This widget expects assets to be provided by the consuming application.
/// By default, it looks for assets in the consuming app's assets folder
/// (not in the deriv_chart package).
///
/// **⚠️ BREAKING CHANGE in v0.4.1**: Assets are no longer bundled with the library.
/// See the migration guide for upgrade instructions.

Additional: Consider a major version bump (e.g., 0.5.0) to signal the breaking change since the library is pre-1.0.


🔵 LOW PRIORITY - Nice to Haves

5. CHANGELOG Entry

Recommendation: Ensure CHANGELOG.md documents this breaking change with:

  • Clear description of the change
  • Migration steps
  • Link to the migration guide in the PR description

6. Asset Verification

Suggestion: Consider adding a test or script that verifies all 108 symbols referenced in code have corresponding assets in the example app. This would catch missing assets during CI.


🔍 Detailed Code Review

symbol_icon.dart Changes ✅

  • Lines 10-11: getSymbolAssetPath correctly lowercases symbol codes
  • Line 17: Good use of const for kDefaultPlaceholderPath
  • Lines 63-116: Well-structured widget with excellent documentation
  • Lines 88-99: Proper use of null-aware operators for optional parameters
  • Lines 113-114: ✅ fadeDuration now properly connected (fixed in latest commit)

pubspec.yaml Changes ✅

  • Correctly removed assets/icons/ declarations
  • Retained assets/fonts/quill_icons.ttf (used internally by chart markers)

example/pubspec.yaml Changes ✅

  • Lines 33-35: Assets properly declared for example app
  • Correct path structure

image_mock_helper.dart

  • Clean, well-implemented test helper
  • Properly handles asset manifest mocking
  • Returns transparent 1x1 PNG for image requests
  • Good documentation

Existing Widget Usage ✅

  • asset_item.dart:80: Uses simple default constructor - will work seamlessly
  • market_selector_button.dart:68-70: Uses simple default constructor - will work seamlessly

📊 Security & Performance

Security ✅

  • No security concerns identified
  • Asset loading uses standard Flutter mechanisms
  • No dynamic code execution or injection risks

Performance ✅

  • Bundle size significantly reduced
  • No performance regressions
  • Proper use of FadeInImage for smooth loading

🎯 Summary & Recommendation

What Works Well:

  1. Clean, well-documented API changes
  2. Comprehensive migration guide in PR description
  3. Proper test helper added for image mocking
  4. Good backward compatibility through optional parameters
  5. Fixed the fadeDuration bug in the latest commit

Priority Actions Before Merge:

  1. DONE: Fix fadeDuration parameter (already fixed in commit 5c7e8b2)
  2. Consider: Enhanced error handling for missing placeholder assets
  3. Consider: Add direct unit tests for SymbolIcon widget
  4. Required: Add CHANGELOG entry documenting this breaking change
  5. Consider: Version bump to 0.5.0 to signal breaking change

Overall Assessment:

This PR successfully achieves its goal of reducing bundle size while providing a flexible, well-documented API. The code quality is high and follows Flutter/Dart best practices. The main gap is around test coverage for the new functionality and helping developers discover missing assets during migration.

Recommendation: ✅ Approve with minor suggestions - The code is production-ready. The high-priority bug was already fixed. Addressing the medium-priority suggestions (especially CHANGELOG and error handling) would make this more robust, but they're not blockers.


Great work on this refactoring! The migration guide is particularly well-written and will help consumers upgrade smoothly.


@behnam-deriv behnam-deriv merged commit f5ec43d into master Jan 2, 2026
6 checks passed
@behnam-deriv behnam-deriv deleted the move-assets-to-example-app branch January 2, 2026 08:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants