From 3b38593592fddd31c51096f57451967dafeed4cd Mon Sep 17 00:00:00 2001 From: "yuvaraj.gajaraj" Date: Thu, 24 Jun 2021 18:15:27 +0530 Subject: [PATCH] Update the source. --- .../analysis_options.yaml | 1 + .../example/linux/.gitignore | 1 + .../example/linux/CMakeLists.txt | 106 + .../example/linux/flutter/CMakeLists.txt | 91 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../linux/flutter/generated_plugins.cmake | 15 + .../example/linux/main.cc | 6 + .../example/linux/my_application.cc | 104 + .../example/linux/my_application.h | 18 + .../example/macos/.gitignore | 6 + .../macos/Flutter/Flutter-Debug.xcconfig | 1 + .../macos/Flutter/Flutter-Release.xcconfig | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 10 + .../macos/Runner.xcodeproj/project.pbxproj | 572 +++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 89 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 46993 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3276 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 1429 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 5933 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1243 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 14800 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1874 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 339 ++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../example/macos/Runner/Release.entitlements | 8 + .../example/pubspec.yaml | 7 +- .../example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/index.html | 45 + .../example/web/manifest.json | 23 + .../example/windows/.gitignore | 17 + .../example/windows/CMakeLists.txt | 95 + .../example/windows/flutter/CMakeLists.txt | 103 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../windows/flutter/generated_plugins.cmake | 15 + .../example/windows/runner/CMakeLists.txt | 18 + .../example/windows/runner/Runner.rc | 121 + .../example/windows/runner/flutter_window.cpp | 64 + .../example/windows/runner/flutter_window.h | 39 + .../example/windows/runner/main.cpp | 42 + .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../example/windows/runner/run_loop.cpp | 66 + .../example/windows/runner/run_loop.h | 40 + .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 64 + .../example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 245 ++ .../example/windows/runner/win32_window.h | 98 + .../one_dimensional/codabar_renderer.dart | 2 +- .../one_dimensional/code128_renderer.dart | 4 +- .../one_dimensional/code39_renderer.dart | 4 +- .../one_dimensional/code93_renderer.dart | 2 +- .../one_dimensional/ean13_renderer.dart | 2 +- .../one_dimensional/ean8_renderer.dart | 2 +- .../one_dimensional/upca_renderer.dart | 2 +- .../one_dimensional/upce_renderer.dart | 2 +- .../two_dimensional/datamatrix_renderer.dart | 2 +- .../two_dimensional/qr_code_renderer.dart | 43 +- .../syncfusion_flutter_barcodes/pubspec.yaml | 2 +- .../syncfusion_flutter_calendar/CHANGELOG.md | 8 + .../syncfusion_flutter_calendar/README.md | 47 +- .../example/android/.gitignore | 11 + .../res/drawable-v21/launch_background.xml | 12 + .../app/src/main/res/values-night/styles.xml | 18 + .../example/android/example_android.iml | 29 + .../example/example.iml | 18 + .../example/ios/.gitignore | 32 + .../example/lib/main.dart | 30 +- .../example/linux/.gitignore | 1 + .../example/linux/CMakeLists.txt | 106 + .../example/linux/flutter/CMakeLists.txt | 91 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../linux/flutter/generated_plugins.cmake | 15 + .../example/linux/main.cc | 6 + .../example/linux/my_application.cc | 104 + .../example/linux/my_application.h | 18 + .../example/macos/.gitignore | 6 + .../macos/Flutter/Flutter-Debug.xcconfig | 1 + .../macos/Flutter/Flutter-Release.xcconfig | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 10 + .../macos/Runner.xcodeproj/project.pbxproj | 572 +++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 89 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 46993 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3276 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 1429 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 5933 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1243 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 14800 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1874 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 339 ++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../example/macos/Runner/Release.entitlements | 8 + .../example/pubspec.yaml | 8 +- .../example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/index.html | 45 + .../example/web/manifest.json | 23 + .../example/windows/.gitignore | 17 + .../example/windows/CMakeLists.txt | 95 + .../example/windows/flutter/CMakeLists.txt | 103 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../windows/flutter/generated_plugins.cmake | 15 + .../example/windows/runner/CMakeLists.txt | 18 + .../example/windows/runner/Runner.rc | 121 + .../example/windows/runner/flutter_window.cpp | 64 + .../example/windows/runner/flutter_window.h | 39 + .../example/windows/runner/main.cpp | 42 + .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../example/windows/runner/run_loop.cpp | 66 + .../example/windows/runner/run_loop.h | 40 + .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 64 + .../example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 245 ++ .../example/windows/runner/win32_window.h | 98 + .../lib/calendar.dart | 1 + .../appointment_engine/appointment.dart | 188 +- .../appointment_helper.dart | 271 +- .../calendar_datasource.dart | 346 +- .../month_appointment_helper.dart | 15 +- .../appointment_engine/recurrence_helper.dart | 1834 +++++----- .../recurrence_properties.dart | 87 +- .../agenda_view_layout.dart | 94 +- .../allday_appointment_layout.dart | 173 +- .../appointment_layout.dart | 352 +- .../calendar/common/calendar_controller.dart | 29 +- .../calendar/common/calendar_view_helper.dart | 197 +- .../src/calendar/common/date_time_engine.dart | 122 +- .../lib/src/calendar/common/enums.dart | 24 +- .../lib/src/calendar/common/event_args.dart | 21 +- .../resource_view/calendar_resource.dart | 6 +- .../calendar/resource_view/resource_view.dart | 482 ++- .../src/calendar/settings/header_style.dart | 5 +- .../settings/month_view_settings.dart | 23 +- .../settings/resource_view_settings.dart | 9 +- .../settings/schedule_view_settings.dart | 34 +- .../src/calendar/settings/time_region.dart | 6 +- .../settings/time_slot_view_settings.dart | 13 +- .../calendar/settings/view_header_style.dart | 5 +- .../calendar/settings/week_number_style.dart | 106 + .../lib/src/calendar/sfcalendar.dart | 1306 ++++--- .../lib/src/calendar/views/calendar_view.dart | 1309 +++++-- .../lib/src/calendar/views/day_view.dart | 101 +- .../lib/src/calendar/views/month_view.dart | 409 ++- .../lib/src/calendar/views/timeline_view.dart | 184 +- .../syncfusion_flutter_calendar/pubspec.yaml | 6 +- .../syncfusion_flutter_charts/CHANGELOG.md | 42 + packages/syncfusion_flutter_charts/README.md | 17 +- .../analysis_options.yaml | 7 + .../dartdoc_options.yaml | 4 + .../example/analysis_options.yaml | 3 + .../example/lib/main.dart | 32 +- .../example/linux/.gitignore | 1 + .../example/linux/CMakeLists.txt | 106 + .../example/linux/flutter/CMakeLists.txt | 91 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../linux/flutter/generated_plugins.cmake | 15 + .../example/linux/main.cc | 6 + .../example/linux/my_application.cc | 104 + .../example/linux/my_application.h | 18 + .../example/macos/.gitignore | 6 + .../macos/Flutter/Flutter-Debug.xcconfig | 1 + .../macos/Flutter/Flutter-Release.xcconfig | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 10 + .../macos/Runner.xcodeproj/project.pbxproj | 572 +++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 89 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 46993 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3276 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 1429 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 5933 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1243 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 14800 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1874 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 339 ++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../example/macos/Runner/Release.entitlements | 8 + .../example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/index.html | 45 + .../example/web/manifest.json | 23 + .../example/windows/.gitignore | 17 + .../example/windows/CMakeLists.txt | 95 + .../example/windows/flutter/CMakeLists.txt | 103 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../windows/flutter/generated_plugins.cmake | 15 + .../example/windows/runner/CMakeLists.txt | 18 + .../example/windows/runner/Runner.rc | 121 + .../example/windows/runner/flutter_window.cpp | 64 + .../example/windows/runner/flutter_window.h | 39 + .../example/windows/runner/main.cpp | 42 + .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../example/windows/runner/run_loop.cpp | 66 + .../example/windows/runner/run_loop.h | 40 + .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 64 + .../example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 245 ++ .../example/windows/runner/win32_window.h | 98 + .../syncfusion_flutter_charts/lib/charts.dart | 7 +- .../chart/annotation/annotation_settings.dart | 38 + .../lib/src/chart/axis/axis.dart | 323 +- .../lib/src/chart/axis/axis_panel.dart | 20 +- .../lib/src/chart/axis/axis_renderer.dart | 12 +- .../lib/src/chart/axis/category_axis.dart | 156 +- .../lib/src/chart/axis/datetime_axis.dart | 211 +- .../chart/axis/datetime_category_axis.dart | 152 +- .../lib/src/chart/axis/logarithmic_axis.dart | 145 +- .../lib/src/chart/axis/numeric_axis.dart | 173 +- .../lib/src/chart/axis/plotband.dart | 106 +- .../lib/src/chart/base/chart_base.dart | 961 +++-- .../lib/src/chart/base/series_base.dart | 109 +- .../src/chart/chart_segment/bar_segment.dart | 31 +- .../box_and_whisker_segment.dart | 23 +- .../chart/chart_segment/bubble_segment.dart | 23 +- .../chart/chart_segment/candle_segment.dart | 39 +- .../chart/chart_segment/chart_segment.dart | 5 +- .../chart/chart_segment/column_segment.dart | 33 +- .../src/chart/chart_segment/hilo_segment.dart | 6 +- .../chart_segment/hiloopenclose_segment.dart | 18 +- .../chart_segment/histogram_segment.dart | 20 +- .../src/chart/chart_segment/line_segment.dart | 56 +- .../chart_segment/range_area_segment.dart | 2 +- .../chart_segment/range_column_segment.dart | 52 +- .../chart/chart_segment/scatter_segment.dart | 19 +- .../spline_range_area_segment.dart | 2 +- .../chart/chart_segment/spline_segment.dart | 7 +- .../chart_segment/stacked_area_segment.dart | 4 +- .../chart_segment/stacked_bar_segment.dart | 8 +- .../chart_segment/stacked_column_segment.dart | 8 +- .../chart_segment/stacked_line_segment.dart | 4 +- .../chart_segment/stackedarea100_segment.dart | 4 +- .../chart_segment/stackedbar100_segment.dart | 2 - .../stackedcolumn100_segment.dart | 2 - .../chart_segment/stackedline100_segment.dart | 4 +- .../chart/chart_segment/stepline_segment.dart | 4 +- .../chart_segment/waterfall_segment.dart | 52 +- .../src/chart/chart_series/area_series.dart | 114 +- .../src/chart/chart_series/bar_series.dart | 132 +- .../chart_series/box_and_whisker_series.dart | 119 +- .../src/chart/chart_series/bubble_series.dart | 118 +- .../src/chart/chart_series/candle_series.dart | 129 +- .../src/chart/chart_series/column_series.dart | 217 +- .../chart/chart_series/fastline_series.dart | 102 +- .../chart_series/financial_series_base.dart | 11 +- .../src/chart/chart_series/hilo_series.dart | 182 +- .../chart_series/hiloopenclose_series.dart | 120 +- .../chart/chart_series/histogram_series.dart | 132 +- .../src/chart/chart_series/line_series.dart | 104 +- .../chart/chart_series/range_area_series.dart | 113 +- .../chart_series/range_column_series.dart | 134 +- .../chart/chart_series/scatter_series.dart | 128 +- .../lib/src/chart/chart_series/series.dart | 148 +- .../chart_series/spline_area_series.dart | 109 +- .../spline_range_area_series.dart | 113 +- .../src/chart/chart_series/spline_series.dart | 107 +- .../chart_series/stacked_area_series.dart | 111 +- .../chart_series/stacked_bar_series.dart | 119 +- .../chart_series/stacked_column_series.dart | 119 +- .../chart_series/stacked_line_series.dart | 101 +- .../chart_series/stacked_series_base.dart | 9 +- .../chart_series/stackedarea100_series.dart | 115 +- .../chart_series/stackedbar100_series.dart | 119 +- .../chart_series/stackedcolumn100_series.dart | 119 +- .../chart_series/stackedline100_series.dart | 101 +- .../chart/chart_series/step_area_series.dart | 105 +- .../chart/chart_series/stepline_series.dart | 97 +- .../chart/chart_series/waterfall_series.dart | 125 +- .../chart/chart_series/xy_data_series.dart | 142 +- .../lib/src/chart/common/common.dart | 431 ++- .../lib/src/chart/common/data_label.dart | 63 +- .../src/chart/common/data_label_renderer.dart | 209 +- .../lib/src/chart/common/marker.dart | 95 +- .../lib/src/chart/common/renderer.dart | 49 +- .../chart/series_painter/area_painter.dart | 30 +- .../src/chart/series_painter/bar_painter.dart | 9 +- .../box_and_whisker_painter.dart | 17 +- .../chart/series_painter/bubble_painter.dart | 11 +- .../chart/series_painter/candle_painter.dart | 10 +- .../chart/series_painter/column_painter.dart | 11 +- .../series_painter/fastline_painter.dart | 12 +- .../chart/series_painter/hilo_painter.dart | 22 +- .../series_painter/hiloopenclose_painter.dart | 22 +- .../series_painter/histogram_painter.dart | 25 +- .../chart/series_painter/line_painter.dart | 14 +- .../series_painter/range_area_painter.dart | 14 +- .../series_painter/range_column_painter.dart | 11 +- .../chart/series_painter/scatter_painter.dart | 8 +- .../series_painter/spline_area_painter.dart | 35 +- .../chart/series_painter/spline_painter.dart | 26 +- .../spline_range_area_painter.dart | 55 +- .../series_painter/stacked_area_painter.dart | 2 +- .../series_painter/stacked_bar_painter.dart | 2 +- .../series_painter/stacked_line_painter.dart | 9 +- .../stackedarea100_painter.dart | 2 +- .../series_painter/stackedbar100_painter.dart | 2 +- .../stackedcolumn100_painter.dart | 2 +- .../series_painter/step_area_painter.dart | 84 +- .../series_painter/stepline_painter.dart | 82 +- .../series_painter/waterfall_painter.dart | 88 +- .../accumulation_distribution_indicator.dart | 99 +- .../technical_indicators/atr_indicator.dart | 113 +- .../bollinger_bands_indicator.dart | 158 +- .../technical_indicators/ema_indicator.dart | 143 +- .../technical_indicators/macd_indicator.dart | 202 +- .../momentum_indicator.dart | 168 +- .../technical_indicators/rsi_indicator.dart | 139 +- .../technical_indicators/sma_indicator.dart | 141 +- .../stochastic_indicator.dart | 185 +- .../technical_indicator.dart | 164 +- .../technical_indicators/tma_indicator.dart | 108 +- .../lib/src/chart/trendlines/trendlines.dart | 278 +- .../chart/trendlines/trendlines_painter.dart | 134 +- .../src/chart/user_interaction/crosshair.dart | 68 +- .../user_interaction/crosshair_painter.dart | 275 +- .../user_interaction/selection_renderer.dart | 1002 +++--- .../src/chart/user_interaction/trackball.dart | 721 ++-- .../user_interaction/trackball_painter.dart | 209 +- .../user_interaction/trackball_template.dart | 52 +- .../user_interaction/zooming_painter.dart | 101 +- .../user_interaction/zooming_panning.dart | 198 +- .../lib/src/chart/utils/enum.dart | 9 +- .../lib/src/chart/utils/helper.dart | 691 ++-- .../circular_chart/base/circular_base.dart | 695 ++-- .../src/circular_chart/base/series_base.dart | 334 +- .../renderer/circular_series.dart | 252 +- .../src/circular_chart/renderer/common.dart | 98 +- .../renderer/data_label_renderer.dart | 36 +- .../renderer/doughnut_series.dart | 125 +- .../circular_chart/renderer/pie_series.dart | 120 +- .../renderer/radial_bar_series.dart | 550 ++- .../lib/src/circular_chart/utils/helper.dart | 60 +- .../lib/src/common/common.dart | 226 +- .../lib/src/common/event_args.dart | 182 +- .../lib/src/common/legend/legend.dart | 128 +- .../lib/src/common/legend/renderer.dart | 194 +- .../lib/src/common/rendering_details.dart | 94 + .../lib/src/common/series/chart_series.dart | 23 - .../lib/src/common/template/rendering.dart | 58 +- .../common/user_interaction/selection.dart | 272 -- .../user_interaction/selection_behavior.dart | 89 +- .../src/common/user_interaction/tooltip.dart | 616 ++-- .../lib/src/common/utils/helper.dart | 570 ++- .../lib/src/common/utils/typedef.dart | 224 ++ .../src/funnel_chart/base/funnel_base.dart | 589 ++-- .../src/funnel_chart/base/series_base.dart | 185 +- .../renderer/data_label_renderer.dart | 54 +- .../funnel_chart/renderer/funnel_series.dart | 227 +- .../src/pyramid_chart/base/pyramid_base.dart | 531 ++- .../src/pyramid_chart/base/series_base.dart | 138 +- .../renderer/data_label_renderer.dart | 54 +- .../renderer/pyramid_series.dart | 224 +- .../lib/src/pyramid_chart/utils/helper.dart | 79 +- .../lib/src/sparkline/marker.dart | 3 +- .../lib/src/sparkline/plot_band.dart | 3 +- .../sparkline/renderers/renderer_base.dart | 22 +- .../renderers/spark_area_renderer.dart | 47 +- .../renderers/spark_bar_renderer.dart | 39 +- .../renderers/spark_line_renderer.dart | 18 +- .../renderers/spark_win_loss_renderer.dart | 23 +- .../src/sparkline/series/spark_area_base.dart | 2 +- .../trackball/spark_chart_trackball.dart | 3 +- .../trackball/trackball_renderer.dart | 23 +- .../lib/src/sparkline/utils/helper.dart | 49 +- .../syncfusion_flutter_charts/pubspec.yaml | 4 +- packages/syncfusion_flutter_core/README.md | 16 +- .../example/lib/main.dart | 6 +- .../example/linux/.gitignore | 1 + .../example/linux/CMakeLists.txt | 106 + .../example/linux/flutter/CMakeLists.txt | 91 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../linux/flutter/generated_plugins.cmake | 15 + .../example/linux/main.cc | 6 + .../example/linux/my_application.cc | 104 + .../example/linux/my_application.h | 18 + .../example/macos/.gitignore | 6 + .../macos/Flutter/Flutter-Debug.xcconfig | 1 + .../macos/Flutter/Flutter-Release.xcconfig | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 10 + .../macos/Runner.xcodeproj/project.pbxproj | 572 +++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 89 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 46993 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3276 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 1429 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 5933 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1243 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 14800 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1874 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 339 ++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../example/macos/Runner/Release.entitlements | 8 + .../example/pubspec.yaml | 7 +- .../example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/index.html | 45 + .../example/web/manifest.json | 23 + .../example/windows/.gitignore | 17 + .../example/windows/CMakeLists.txt | 95 + .../example/windows/flutter/CMakeLists.txt | 103 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../windows/flutter/generated_plugins.cmake | 15 + .../example/windows/runner/CMakeLists.txt | 18 + .../example/windows/runner/Runner.rc | 121 + .../example/windows/runner/flutter_window.cpp | 64 + .../example/windows/runner/flutter_window.h | 39 + .../example/windows/runner/main.cpp | 42 + .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../example/windows/runner/run_loop.cpp | 66 + .../example/windows/runner/run_loop.h | 40 + .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 64 + .../example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 245 ++ .../example/windows/runner/win32_window.h | 98 + .../lib/analysis_options.yaml | 226 +- .../syncfusion_flutter_core/lib/core.dart | 1 + .../lib/legend_internal.dart | 3 + .../lib/src/calendar/calendar_helper.dart | 15 +- .../src/calendar/custom_looping_widget.dart | 12 +- .../lib/src/calendar/hijri_date_time.dart | 24 +- .../lib/src/legend/legend.dart | 2278 ++++++++++++ .../localizations/global_localizations.dart | 5 +- .../lib/src/test/tooltip_tests.dart | 248 -- .../lib/src/theme/barcodes_theme.dart | 1 + .../lib/src/theme/calendar_theme.dart | 219 +- .../lib/src/theme/charts_theme.dart | 1 + .../lib/src/theme/datagrid_theme.dart | 60 +- .../lib/src/theme/datapager_theme.dart | 24 +- .../lib/src/theme/daterangepicker_theme.dart | 255 +- .../lib/src/theme/gauges_theme.dart | 1 + .../lib/src/theme/maps_theme.dart | 3 +- .../lib/src/theme/pdfviewer_theme.dart | 80 +- .../lib/src/theme/range_selector_theme.dart | 256 +- .../lib/src/theme/range_slider_theme.dart | 264 +- .../lib/src/theme/slider_theme.dart | 339 +- .../lib/src/theme/theme_widget.dart | 1 + .../lib/src/tooltip/tooltip.dart | 523 ++- .../lib/src/utils/helper.dart | 9 +- .../lib/src/utils/shape_helper.dart | 1486 ++++++++ .../lib/tooltip_internal.dart | 1 + packages/syncfusion_flutter_core/pubspec.yaml | 3 +- .../syncfusion_flutter_datagrid/CHANGELOG.md | 25 + .../syncfusion_flutter_datagrid/README.md | 38 +- .../example/lib/main.dart | 8 +- .../example/linux/.gitignore | 1 + .../example/linux/CMakeLists.txt | 106 + .../example/linux/flutter/CMakeLists.txt | 91 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../linux/flutter/generated_plugins.cmake | 15 + .../example/linux/main.cc | 6 + .../example/linux/my_application.cc | 104 + .../example/linux/my_application.h | 18 + .../example/macos/.gitignore | 6 + .../macos/Flutter/Flutter-Debug.xcconfig | 1 + .../macos/Flutter/Flutter-Release.xcconfig | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 10 + .../macos/Runner.xcodeproj/project.pbxproj | 572 +++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 89 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 46993 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3276 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 1429 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 5933 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1243 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 14800 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1874 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 339 ++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../example/macos/Runner/Release.entitlements | 8 + .../example/pubspec.yaml | 6 +- .../example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/index.html | 45 + .../example/web/manifest.json | 23 + .../example/windows/.gitignore | 17 + .../example/windows/CMakeLists.txt | 95 + .../example/windows/flutter/CMakeLists.txt | 103 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../windows/flutter/generated_plugins.cmake | 15 + .../example/windows/runner/CMakeLists.txt | 18 + .../example/windows/runner/Runner.rc | 121 + .../example/windows/runner/flutter_window.cpp | 64 + .../example/windows/runner/flutter_window.h | 39 + .../example/windows/runner/main.cpp | 42 + .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../example/windows/runner/run_loop.cpp | 66 + .../example/windows/runner/run_loop.h | 40 + .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 64 + .../example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 245 ++ .../example/windows/runner/win32_window.h | 98 + .../lib/datagrid.dart | 4 +- .../grid_cell_renderer_base.dart | 10 +- .../grid_cell_stacked_header_renderer.dart | 12 +- .../grid_cell_text_field_renderer.dart | 51 +- .../grid_header_cell_renderer.dart | 4 +- .../grid_virtualizing_cell_renderer_base.dart | 34 +- .../lib/src/control/callbackargs.dart | 61 + .../cell_control/grid_cell_widget.dart | 246 +- .../cell_control/grid_header_cell_widget.dart | 243 +- .../virtualizing_cells_widget.dart | 455 ++- .../lib/src/control/datagrid_datasource.dart | 456 ++- .../src/control/generator/data_cell_base.dart | 28 +- .../lib/src/control/generator/data_row.dart | 50 +- .../src/control/generator/data_row_base.dart | 40 +- .../src/control/generator/row_generator.dart | 174 +- .../control/generator/spanned_data_row.dart | 44 +- .../lib/src/control/helper/enums.dart | 71 +- .../control/helper/grid_index_resolver.dart | 103 +- .../src/control/helper/sfdatagrid_helper.dart | 210 +- .../lib/src/control/runtime/column_sizer.dart | 749 +++- .../lib/src/control/runtime/grid_column.dart | 48 +- .../src/control/runtime/stacked_header.dart | 6 +- .../scrollable_panel/scrollview_widget.dart | 437 ++- .../visual_container_helper.dart | 134 +- .../visual_container_widget.dart | 74 +- .../current_cell_manager.dart | 326 +- .../row_selection_manager.dart | 217 +- .../selected_row_info.dart | 8 +- .../selection_helper.dart | 245 +- .../selection_manager_base.dart | 36 +- .../lib/src/datapager/sfdatapager.dart | 164 +- .../grid_common/collections/tree_table.dart | 145 +- .../collections/tree_table_with_counter.dart | 39 +- .../collections/tree_table_with_summary.dart | 35 +- .../lib/src/grid_common/list_base.dart | 12 +- .../distance_range_counter_collection.dart | 70 +- .../scroll_axis_base/line_scroll_axis.dart | 34 +- .../line_size_collection.dart | 109 +- .../scroll_axis_base/line_size_host_base.dart | 4 +- .../scroll_axis_base/pixel_scroll_axis.dart | 20 +- .../scroll_axis_base/scroll_axis_base.dart | 112 +- .../scroll_axis_base/scroll_info.dart | 12 +- .../sorted_range_value_list.dart | 51 +- .../scroll_axis_base/visible_line_info.dart | 10 +- .../src/grid_common/utility/double_span.dart | 15 - .../src/grid_common/utility/int_32_span.dart | 14 - .../lib/src/sfdatagrid.dart | 712 +++- .../syncfusion_flutter_datagrid/pubspec.yaml | 6 +- .../CHANGELOG.md | 5 + .../syncfusion_flutter_datepicker/README.md | 21 +- .../analysis_options.yaml | 2 +- .../example/android/.gitignore | 11 + .../res/drawable-v21/launch_background.xml | 12 + .../app/src/main/res/values-night/styles.xml | 18 + .../example/android/example_android.iml | 29 + .../example/example.iml | 18 + .../example/ios/.gitignore | 32 + .../example/lib/main.dart | 23 +- .../example/linux/.gitignore | 1 + .../example/linux/CMakeLists.txt | 106 + .../example/linux/flutter/CMakeLists.txt | 91 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../linux/flutter/generated_plugins.cmake | 15 + .../example/linux/main.cc | 6 + .../example/linux/my_application.cc | 104 + .../example/linux/my_application.h | 18 + .../example/macos/.gitignore | 6 + .../macos/Flutter/Flutter-Debug.xcconfig | 1 + .../macos/Flutter/Flutter-Release.xcconfig | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 12 + .../macos/Runner.xcodeproj/project.pbxproj | 572 +++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 89 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 46993 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3276 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 1429 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 5933 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1243 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 14800 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1874 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 339 ++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../example/macos/Runner/Release.entitlements | 8 + .../example/pubspec.yaml | 8 +- .../example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/index.html | 45 + .../example/web/manifest.json | 23 + .../example/windows/.gitignore | 17 + .../example/windows/CMakeLists.txt | 95 + .../example/windows/flutter/CMakeLists.txt | 103 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../windows/flutter/generated_plugins.cmake | 15 + .../example/windows/runner/CMakeLists.txt | 18 + .../example/windows/runner/Runner.rc | 121 + .../example/windows/runner/flutter_window.cpp | 64 + .../example/windows/runner/flutter_window.h | 39 + .../example/windows/runner/main.cpp | 42 + .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../example/windows/runner/run_loop.cpp | 66 + .../example/windows/runner/run_loop.h | 40 + .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 64 + .../example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 245 ++ .../example/windows/runner/win32_window.h | 98 + .../lib/src/date_picker/date_picker.dart | 1089 ++++-- .../src/date_picker/date_picker_manager.dart | 282 +- .../hijri_date_picker_manager.dart | 152 +- .../lib/src/date_picker/month_view.dart | 1368 +++++++- .../lib/src/date_picker/picker_helper.dart | 190 +- .../lib/src/date_picker/year_view.dart | 816 ++++- .../pubspec.yaml | 4 +- packages/syncfusion_flutter_gauges/README.md | 13 +- .../example/linux/.gitignore | 1 + .../example/linux/CMakeLists.txt | 106 + .../example/linux/flutter/CMakeLists.txt | 91 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../linux/flutter/generated_plugins.cmake | 15 + .../example/linux/main.cc | 6 + .../example/linux/my_application.cc | 104 + .../example/linux/my_application.h | 18 + .../example/macos/.gitignore | 6 + .../macos/Flutter/Flutter-Debug.xcconfig | 1 + .../macos/Flutter/Flutter-Release.xcconfig | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 10 + .../macos/Runner.xcodeproj/project.pbxproj | 572 +++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 89 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 46993 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3276 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 1429 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 5933 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1243 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 14800 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1874 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 339 ++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../example/macos/Runner/Release.entitlements | 8 + .../example/pubspec.yaml | 7 +- .../example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/index.html | 45 + .../example/web/manifest.json | 23 + .../example/windows/.gitignore | 17 + .../example/windows/CMakeLists.txt | 95 + .../example/windows/flutter/CMakeLists.txt | 103 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../windows/flutter/generated_plugins.cmake | 15 + .../example/windows/runner/CMakeLists.txt | 18 + .../example/windows/runner/Runner.rc | 121 + .../example/windows/runner/flutter_window.cpp | 64 + .../example/windows/runner/flutter_window.h | 39 + .../example/windows/runner/main.cpp | 42 + .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../example/windows/runner/run_loop.cpp | 66 + .../example/windows/runner/run_loop.h | 40 + .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 64 + .../example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 245 ++ .../example/windows/runner/win32_window.h | 98 + .../syncfusion_flutter_gauges/lib/gauges.dart | 15 +- .../linear_gauge/axis/linear_axis_label.dart | 5 +- .../axis/linear_axis_renderer.dart | 85 +- .../axis/linear_axis_track_style.dart | 7 +- .../linear_gauge/axis/linear_tick_style.dart | 7 +- .../src/linear_gauge/gauge/linear_gauge.dart | 83 +- .../gauge/linear_gauge_render_widget.dart | 153 +- .../gauge/linear_gauge_scope.dart | 14 +- .../pointers/linear_bar_pointer.dart | 14 +- .../pointers/linear_bar_renderer.dart | 7 +- .../pointers/linear_shape_pointer.dart | 6 +- .../pointers/linear_shape_renderer.dart | 163 +- .../pointers/linear_widget_pointer.dart | 6 +- .../pointers/linear_widget_renderer.dart | 17 +- .../range/linear_gauge_range.dart | 12 +- .../range/linear_gauge_range_renderer.dart | 24 +- .../utils/linear_gauge_helper.dart | 16 +- .../annotation/gauge_annotation.dart | 100 +- .../annotation/gauge_annotation_renderer.dart | 284 ++ .../lib/src/radial_gauge/axis/gauge_axis.dart | 558 --- .../src/radial_gauge/axis/radial_axis.dart | 1066 +++++- .../radial_axis_label.dart} | 3 +- .../axis/radial_axis_parent_widget.dart | 600 ++++ .../radial_gauge/axis/radial_axis_scope.dart | 60 + .../radial_gauge/axis/radial_axis_widget.dart | 2642 ++++++++++++++ .../lib/src/radial_gauge/common/common.dart | 1076 ------ .../common/gauge_annotation_renderer.dart | 290 -- .../common/radial_gauge_renderer.dart | 1020 ------ .../common/widget_pointer_renderer.dart | 317 -- .../src/radial_gauge/gauge/radial_gauge.dart | 704 +--- .../gauge/radial_gauge_scope.dart | 40 + .../gauge_painter/marker_pointer_painter.dart | 141 - .../gauge_painter/needle_pointer_painter.dart | 150 - .../gauge_painter/radial_axis_painter.dart | 478 --- .../gauge_painter/range_painter.dart | 215 -- .../gauge_painter/range_pointer_painter.dart | 304 -- .../radial_gauge/pointers/gauge_pointer.dart | 5 +- .../radial_gauge/pointers/marker_pointer.dart | 417 ++- .../pointers/marker_pointer_renderer.dart | 934 +++++ .../radial_gauge/pointers/needle_pointer.dart | 396 ++- .../pointers/needle_pointer_renderer.dart | 690 ++++ .../pointers/pointer_painting_details.dart | 29 + .../radial_gauge/pointers/range_pointer.dart | 370 +- .../pointers/range_pointer_renderer.dart | 698 ++++ .../radial_gauge/pointers/widget_pointer.dart | 392 ++- .../pointers/widget_pointer_renderer.dart | 390 +++ .../src/radial_gauge/range/gauge_range.dart | 86 +- .../range/gauge_range_renderer.dart | 753 ++++ .../renderers/gauge_pointer_renderer.dart | 200 -- .../renderers/gauge_range_renderer.dart | 412 --- .../renderers/marker_pointer_renderer.dart | 8 +- .../marker_pointer_renderer_base.dart | 512 --- .../renderers/needle_pointer_renderer.dart | 6 +- .../needle_pointer_renderer_base.dart | 377 -- .../renderers/radial_axis_renderer.dart | 6 +- .../renderers/radial_axis_renderer_base.dart | 1283 ------- .../renderers/range_pointer_renderer.dart | 164 - .../widget_pointer_renderer_base.dart | 121 - .../styles/radial_knob_style.dart | 166 + .../styles/radial_tail_style.dart | 221 ++ .../styles/radial_text_style.dart | 67 + .../styles/radial_tick_style.dart | 380 ++ .../src/radial_gauge/title/radial_title.dart | 194 ++ .../lib/src/radial_gauge/utils/helper.dart | 58 +- .../utils/radial_callback_args.dart | 24 + .../utils/radial_gauge_typedef.dart | 14 + .../syncfusion_flutter_gauges/pubspec.yaml | 5 +- packages/syncfusion_flutter_maps/CHANGELOG.md | 7 + packages/syncfusion_flutter_maps/README.md | 59 +- .../example/lib/main.dart | 4 +- .../example/linux/.gitignore | 1 + .../example/linux/CMakeLists.txt | 116 + .../example/linux/flutter/CMakeLists.txt | 87 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../linux/flutter/generated_plugins.cmake | 15 + .../example/linux/main.cc | 6 + .../example/linux/my_application.cc | 105 + .../example/linux/my_application.h | 18 + .../example/macos/.gitignore | 6 + .../macos/Flutter/Flutter-Debug.xcconfig | 1 + .../macos/Flutter/Flutter-Release.xcconfig | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 10 + .../macos/Runner.xcodeproj/project.pbxproj | 572 +++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 89 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 46993 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3276 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 1429 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 5933 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1243 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 14800 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1874 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 339 ++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../example/macos/Runner/Release.entitlements | 8 + .../example/pubspec.yaml | 4 - .../example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/index.html | 98 + .../example/web/manifest.json | 23 + .../example/windows/.gitignore | 17 + .../example/windows/CMakeLists.txt | 95 + .../example/windows/flutter/CMakeLists.txt | 103 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../windows/flutter/generated_plugins.cmake | 15 + .../example/windows/runner/CMakeLists.txt | 18 + .../example/windows/runner/Runner.rc | 121 + .../example/windows/runner/flutter_window.cpp | 64 + .../example/windows/runner/flutter_window.h | 39 + .../example/windows/runner/main.cpp | 42 + .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../example/windows/runner/run_loop.cpp | 66 + .../example/windows/runner/run_loop.h | 40 + .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 64 + .../example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 245 ++ .../example/windows/runner/win32_window.h | 98 + .../syncfusion_flutter_maps/lib/maps.dart | 2615 +++++++++++++- .../lib/src/behavior/zoom_pan_behavior.dart | 628 ---- .../lib/src/common.dart | 11 +- ..._controller.dart => layer_controller.dart} | 4 +- .../lib/src/controller/map_controller.dart | 4 +- .../lib/src/controller/map_provider.dart | 11 +- .../lib/src/elements/bubble.dart | 40 +- .../lib/src/elements/data_label.dart | 18 +- .../lib/src/elements/legend.dart | 2003 +++-------- .../lib/src/elements/marker.dart | 101 +- .../lib/src/elements/toolbar.dart | 8 +- .../lib/src/elements/tooltip.dart | 8 +- .../lib/src/layer/layer_base.dart | 28 +- .../lib/src/layer/shape_layer.dart | 3079 +++++------------ .../lib/src/layer/tile_layer.dart | 717 +--- .../lib/src/layer/vector_layers.dart | 1020 ++++-- .../lib/src/settings.dart | 131 +- .../lib/src/utils.dart | 17 +- packages/syncfusion_flutter_maps/pubspec.yaml | 8 +- .../syncfusion_flutter_officechart/README.md | 19 +- .../analysis_options.yaml | 1 - .../example/lib/helper/save_file_mobile.dart | 39 + .../example/lib/helper/save_file_web.dart | 18 + .../example/lib/main.dart | 31 +- .../example/linux/.gitignore | 1 + .../example/linux/CMakeLists.txt | 116 + .../example/linux/flutter/CMakeLists.txt | 91 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../linux/flutter/generated_plugins.cmake | 15 + .../example/linux/main.cc | 6 + .../example/linux/my_application.cc | 105 + .../example/linux/my_application.h | 18 + .../example/macos/.gitignore | 6 + .../macos/Flutter/Flutter-Debug.xcconfig | 1 + .../macos/Flutter/Flutter-Release.xcconfig | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 14 + .../macos/Runner.xcodeproj/project.pbxproj | 572 +++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 89 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 46993 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3276 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 1429 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 5933 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1243 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 14800 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1874 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 339 ++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../example/macos/Runner/Release.entitlements | 8 + .../example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/index.html | 98 + .../example/web/manifest.json | 23 + .../example/windows/.gitignore | 17 + .../example/windows/CMakeLists.txt | 95 + .../example/windows/flutter/CMakeLists.txt | 103 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../windows/flutter/generated_plugins.cmake | 15 + .../example/windows/runner/CMakeLists.txt | 18 + .../example/windows/runner/Runner.rc | 121 + .../example/windows/runner/flutter_window.cpp | 64 + .../example/windows/runner/flutter_window.h | 39 + .../example/windows/runner/main.cpp | 42 + .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../example/windows/runner/run_loop.cpp | 66 + .../example/windows/runner/run_loop.h | 40 + .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 64 + .../example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 245 ++ .../example/windows/runner/win32_window.h | 98 + .../lib/officechart.dart | 2 +- .../lib/src/chart/chart_axis.dart | 4 +- .../lib/src/chart/chart_collection.dart | 4 +- .../lib/src/chart/chart_impl.dart | 73 +- .../lib/src/chart/chart_serialization.dart | 26 +- .../lib/src/chart/chart_serie.dart | 4 +- .../src/chart/chart_series_collection.dart | 4 +- .../lib/src/chart/chart_text_area.dart | 4 +- .../pubspec.yaml | 2 +- .../syncfusion_flutter_officecore/README.md | 19 +- .../example/lib/helper/save_file_mobile.dart | 39 + .../example/lib/helper/save_file_web.dart | 18 + .../example/lib/main.dart | 25 +- .../example/linux/.gitignore | 1 + .../example/linux/CMakeLists.txt | 116 + .../example/linux/flutter/CMakeLists.txt | 91 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../linux/flutter/generated_plugins.cmake | 15 + .../example/linux/main.cc | 6 + .../example/linux/my_application.cc | 105 + .../example/linux/my_application.h | 18 + .../example/macos/.gitignore | 6 + .../macos/Flutter/Flutter-Debug.xcconfig | 1 + .../macos/Flutter/Flutter-Release.xcconfig | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 14 + .../macos/Runner.xcodeproj/project.pbxproj | 572 +++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 89 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 46993 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3276 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 1429 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 5933 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1243 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 14800 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1874 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 339 ++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../example/macos/Runner/Release.entitlements | 8 + .../example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/index.html | 98 + .../example/web/manifest.json | 23 + .../example/windows/.gitignore | 17 + .../example/windows/CMakeLists.txt | 95 + .../example/windows/flutter/CMakeLists.txt | 103 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../windows/flutter/generated_plugins.cmake | 15 + .../example/windows/runner/CMakeLists.txt | 18 + .../example/windows/runner/Runner.rc | 121 + .../example/windows/runner/flutter_window.cpp | 64 + .../example/windows/runner/flutter_window.h | 39 + .../example/windows/runner/main.cpp | 42 + .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../example/windows/runner/run_loop.cpp | 66 + .../example/windows/runner/run_loop.h | 40 + .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 64 + .../example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 245 ++ .../example/windows/runner/win32_window.h | 98 + .../pubspec.yaml | 2 +- packages/syncfusion_flutter_pdf/CHANGELOG.md | 18 + packages/syncfusion_flutter_pdf/README.md | 19 +- .../example/lib/helper/save_file_mobile.dart | 39 + .../example/lib/helper/save_file_web.dart | 18 + .../example/lib/main.dart | 28 +- .../example/linux/.gitignore | 1 + .../example/linux/CMakeLists.txt | 116 + .../example/linux/flutter/CMakeLists.txt | 91 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../linux/flutter/generated_plugins.cmake | 15 + .../example/linux/main.cc | 6 + .../example/linux/my_application.cc | 105 + .../example/linux/my_application.h | 18 + .../example/macos/.gitignore | 6 + .../macos/Flutter/Flutter-Debug.xcconfig | 1 + .../macos/Flutter/Flutter-Release.xcconfig | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 14 + .../macos/Runner.xcodeproj/project.pbxproj | 572 +++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 89 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 46993 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3276 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 1429 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 5933 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1243 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 14800 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1874 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 339 ++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../example/macos/Runner/Release.entitlements | 8 + .../example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/index.html | 98 + .../example/web/manifest.json | 23 + .../example/windows/.gitignore | 17 + .../example/windows/CMakeLists.txt | 95 + .../example/windows/flutter/CMakeLists.txt | 103 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../windows/flutter/generated_plugins.cmake | 15 + .../example/windows/runner/CMakeLists.txt | 18 + .../example/windows/runner/Runner.rc | 121 + .../example/windows/runner/flutter_window.cpp | 64 + .../example/windows/runner/flutter_window.h | 39 + .../example/windows/runner/main.cpp | 42 + .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../example/windows/runner/run_loop.cpp | 66 + .../example/windows/runner/run_loop.h | 40 + .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 64 + .../example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 245 ++ .../example/windows/runner/win32_window.h | 98 + packages/syncfusion_flutter_pdf/lib/pdf.dart | 2 + .../actions/pdf_field_actions.dart | 4 +- .../actions/pdf_submit_action.dart | 6 +- .../appearance/pdf_appearance_state.dart | 1 + .../annotations/pdf_action_annotation.dart | 6 +- .../annotations/pdf_annotation.dart | 103 +- .../annotations/pdf_annotation_border.dart | 3 +- .../pdf_annotation_collection.dart | 14 +- .../pdf_document_link_annotation.dart | 42 +- .../annotations/pdf_ellipse_annotation.dart | 10 +- .../annotations/pdf_line_annotation.dart | 25 +- .../annotations/pdf_polygon_annotation.dart | 39 +- .../annotations/pdf_rectangle_annotation.dart | 21 +- .../annotations/pdf_text_web_link.dart | 1 + .../annotations/widget_annotation.dart | 2 +- .../annotations/widget_appearance.dart | 6 +- .../compression/compressed_stream_reader.dart | 23 +- .../decompressor_huffman_tree.dart | 4 +- .../compression/deflate/deflate_stream.dart | 8 +- .../compression/deflate/in_flatter.dart | 46 +- .../compression/pdf_zlib_compressor.dart | 12 +- .../pdf/implementation/drawing/drawing.dart | 23 +- .../pdf_text_extractor/font_file2.dart | 14 +- .../pdf_text_extractor/font_structure.dart | 253 +- .../pdf_text_extractor/image_renderer.dart | 30 +- .../pdf_text_extractor/matched_item.dart | 6 +- .../pdf_text_extractor/matrix_helper.dart | 10 +- .../page_resource_loader.dart | 26 +- .../parser/content_lexer.dart | 6 +- .../pdf_text_extractor.dart | 95 +- .../pdf_text_extractor/text_element.dart | 18 +- .../pdf_text_extractor/text_glyph.dart | 10 +- .../pdf_text_extractor/text_line.dart | 2 +- .../pdf_text_extractor/text_word.dart | 10 +- .../pdf_text_extractor/xobject_element.dart | 27 +- .../src/pdf/implementation/forms/enum.dart | 15 + .../forms/pdf_button_field.dart | 73 +- .../forms/pdf_check_box_field.dart | 39 +- .../forms/pdf_check_field_base.dart | 33 +- .../forms/pdf_combo_box_field.dart | 21 +- .../pdf/implementation/forms/pdf_field.dart | 699 +++- .../implementation/forms/pdf_field_item.dart | 2 +- .../forms/pdf_field_painter.dart | 37 +- .../pdf/implementation/forms/pdf_form.dart | 774 ++++- .../forms/pdf_form_field_collection.dart | 46 +- .../forms/pdf_list_box_field.dart | 9 +- .../implementation/forms/pdf_list_field.dart | 81 +- .../forms/pdf_list_field_item.dart | 16 +- .../forms/pdf_radio_button_list_field.dart | 73 +- .../forms/pdf_radio_button_list_item.dart | 31 +- .../forms/pdf_signature_field.dart | 21 +- .../forms/pdf_text_box_field.dart | 21 +- .../forms/pdf_xfdf_document.dart | 61 + .../general/embedded_file_specification.dart | 5 +- .../general/file_specification_base.dart | 2 +- .../general/pdf_named_destination.dart | 9 +- .../pdf_named_destination_collection.dart | 4 +- .../graphics/brushes/pdf_solid_brush.dart | 16 +- .../pdf/implementation/graphics/enums.dart | 8 +- .../graphics/figures/base/shape_layouter.dart | 6 +- .../graphics/figures/base/text_layouter.dart | 2 +- .../graphics/figures/pdf_path.dart | 3 +- .../graphics/figures/pdf_template.dart | 21 +- .../graphics/fonts/pdf_cid_font.dart | 2 +- .../pdf_cjk_font_descryptor_factory.dart | 2 +- .../graphics/fonts/pdf_cjk_standard_font.dart | 4 +- .../graphics/fonts/pdf_font.dart | 4 +- .../graphics/fonts/pdf_font_metrics.dart | 5 +- .../graphics/fonts/pdf_standard_font.dart | 8 +- .../graphics/fonts/pdf_string_format.dart | 3 +- .../graphics/fonts/pdf_string_layouter.dart | 7 +- .../graphics/fonts/pdf_true_type_font.dart | 49 +- .../graphics/fonts/string_tokenizer.dart | 4 +- .../graphics/fonts/ttf_reader.dart | 25 +- .../fonts/unicode_true_type_font.dart | 10 +- .../images/decoders/jpeg_decoder.dart | 4 +- .../graphics/images/decoders/png_decoder.dart | 21 +- .../graphics/images/pdf_bitmap.dart | 14 +- .../graphics/images/pdf_image.dart | 10 +- .../implementation/graphics/pdf_color.dart | 25 +- .../implementation/graphics/pdf_graphics.dart | 137 +- .../implementation/graphics/pdf_margins.dart | 1 + .../pdf/implementation/graphics/pdf_pen.dart | 14 +- .../pdf/implementation/graphics/pdf_pens.dart | 4 +- .../graphics/pdf_transformation_matrix.dart | 11 + .../graphics/pdf_transparency.dart | 2 +- .../pdf/implementation/io/cross_table.dart | 47 +- .../implementation/io/decode_big_endian.dart | 6 +- .../io/dictionary_properties.dart | 5 +- .../pdf/implementation/io/object_info.dart | 2 +- .../implementation/io/pdf_cross_table.dart | 31 +- .../src/pdf/implementation/io/pdf_lexer.dart | 10 +- .../io/pdf_main_object_collection.dart | 8 +- .../src/pdf/implementation/io/pdf_parser.dart | 6 +- .../src/pdf/implementation/io/pdf_reader.dart | 47 +- .../implementation/io/pdf_stream_writer.dart | 2 +- .../pdf/implementation/io/stream_reader.dart | 2 +- .../pdf/implementation/pages/pdf_layer.dart | 48 +- .../pages/pdf_layer_collection.dart | 30 +- .../pdf/implementation/pages/pdf_page.dart | 71 +- .../pages/pdf_page_collection.dart | 73 +- .../implementation/pages/pdf_page_layer.dart | 38 +- .../pages/pdf_page_layer_collection.dart | 41 +- .../pages/pdf_page_settings.dart | 1 + .../pages/pdf_page_template_element.dart | 28 +- .../pdf/implementation/pages/pdf_section.dart | 22 +- .../pages/pdf_section_template.dart | 14 +- .../pdf_attachment_collection.dart | 42 +- .../pdf_automatic_field_info.dart | 5 +- .../pdf_page_count_field.dart | 9 +- .../pdf_page_number_field.dart | 3 +- .../pdf_template_value_pair.dart | 5 +- .../pdf_document/outlines/pdf_outline.dart | 46 +- .../outlines/pdf_outline_base.dart | 7 +- .../pdf_document/pdf_catalog.dart | 8 +- .../pdf_document/pdf_catalog_names.dart | 14 +- .../pdf_document/pdf_document.dart | 34 +- .../pdf_document_information.dart | 16 +- .../implementation/primitives/pdf_array.dart | 1 + .../primitives/pdf_dictionary.dart | 20 +- .../implementation/primitives/pdf_name.dart | 6 +- .../primitives/pdf_reference_holder.dart | 17 +- .../implementation/primitives/pdf_stream.dart | 25 +- .../implementation/primitives/pdf_string.dart | 20 +- .../security/digital_signature/asn1/asn1.dart | 69 +- .../digital_signature/asn1/asn1_parser.dart | 8 +- .../digital_signature/asn1/asn1_stream.dart | 43 +- .../security/digital_signature/asn1/ber.dart | 25 +- .../security/digital_signature/asn1/der.dart | 102 +- .../cryptography/aes_cipher.dart | 36 +- .../cryptography/aes_engine.dart | 5 +- .../buffered_block_padding_base.dart | 92 +- .../cipher_block_chaining_mode.dart | 17 +- .../cryptography/cipher_utils.dart | 13 +- .../cryptography/data_ede_algorithm.dart | 12 +- .../cryptography/data_encryption.dart | 32 +- .../cryptography/pdf_cms_signer.dart | 14 +- .../cryptography/pkcs1_encoding.dart | 17 +- .../cryptography/random_array.dart | 7 +- .../cryptography/rc2_algorithm.dart | 11 +- .../cryptography/rmd_signer.dart | 2 +- .../cryptography/rsa_algorithm.dart | 19 +- .../cryptography/signature_utilities.dart | 13 +- .../digital_signature/pdf_certificate.dart | 10 +- .../pdf_external_signer.dart | 5 +- .../pdf_pkcs_certificate.dart | 92 +- .../digital_signature/pdf_signature.dart | 16 +- .../pdf_signature_dictionary.dart | 42 +- .../pkcs/password_utility.dart | 50 +- .../digital_signature/pkcs/pfx_data.dart | 38 +- .../x509/x509_certificates.dart | 36 +- .../digital_signature/x509/x509_name.dart | 149 +- .../digital_signature/x509/x509_time.dart | 10 +- .../security/pdf_encryptor.dart | 94 +- .../implementation/security/pdf_security.dart | 17 +- .../grid/layouting/pdf_grid_layouter.dart | 32 +- .../structured_elements/grid/pdf_grid.dart | 79 +- .../grid/pdf_grid_cell.dart | 55 +- .../grid/pdf_grid_row.dart | 6 +- .../grid/styles/pdf_borders.dart | 13 +- .../lists/bullets/enums.dart | 4 +- .../lists/bullets/pdf_marker.dart | 12 +- .../lists/bullets/pdf_ordered_marker.dart | 12 +- .../lists/bullets/pdf_unordered_marker.dart | 12 +- .../lists/pdf_list_item.dart | 3 +- .../lists/pdf_list_item_collection.dart | 10 +- .../lists/pdf_list_layouter.dart | 40 +- .../lists/pdf_ordered_list.dart | 2 +- .../lists/pdf_unordered_list.dart | 6 +- .../pdf/implementation/xmp/xmp_metadata.dart | 52 +- packages/syncfusion_flutter_pdf/pubspec.yaml | 11 +- .../syncfusion_flutter_pdf/test/pdf_test.dart | 67 - .../syncfusion_flutter_pdfviewer/CHANGELOG.md | 9 + .../syncfusion_flutter_pdfviewer/README.md | 28 +- .../lib/generated_plugin_registrant.dart | 15 + .../example/lib/main.dart | 4 +- .../example/macos/.gitignore | 6 + .../macos/Flutter/Flutter-Debug.xcconfig | 2 + .../macos/Flutter/Flutter-Release.xcconfig | 2 + .../Flutter/GeneratedPluginRegistrant.swift | 12 + .../macos/Runner.xcodeproj/project.pbxproj | 632 ++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 89 + .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 46993 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3276 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 1429 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 5933 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1243 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 14800 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1874 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 339 ++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 14 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../example/macos/Runner/Release.entitlements | 8 + .../example/pubspec.yaml | 3 - .../lib/src/bookmark/bookmark_item.dart | 19 +- .../lib/src/bookmark/bookmark_toolbar.dart | 39 +- .../lib/src/bookmark/bookmark_view.dart | 31 +- .../lib/src/common/mobile_helper.dart | 1 + .../lib/src/common/pdf_provider.dart | 26 +- .../lib/src/common/pdfviewer_helper.dart | 8 + .../lib/src/common/pdfviewer_plugin.dart | 38 +- .../lib/src/common/web_helper.dart | 8 +- .../src/control/interactive_scrollable.dart | 152 + .../lib/src/control/pdf_container.dart | 1297 ------- .../lib/src/control/pdf_page_view.dart | 368 +- .../lib/src/control/pdf_scrollable.dart | 380 ++ .../lib/src/control/pdftextline.dart | 2 +- .../control/pdfviewer_callback_details.dart | 3 +- .../lib/src/control/pdfviewer_canvas.dart | 365 +- .../lib/src/control/scroll_head.dart | 51 +- .../lib/src/control/scroll_head_overlay.dart | 387 ++- .../lib/src/control/scroll_status.dart | 7 +- .../lib/src/pdfviewer.dart | 1251 +++---- .../syncfusion_flutter_pdfviewer.podspec | 22 + .../syncfusion_flutter_pdfviewer/pubspec.yaml | 16 +- .../CHANGELOG.md | 3 + .../LICENSE | 12 + .../README.md | 31 + .../analysis_options.yaml | 5 +- .../example/README.md | 3 + .../example/lib/main.dart | 48 + .../macos/Flutter/Flutter-Debug.xcconfig | 2 + .../macos/Flutter/Flutter-Release.xcconfig | 2 + .../Flutter/GeneratedPluginRegistrant.swift | 12 + .../example/macos/Podfile | 40 + .../macos/Runner.xcodeproj/project.pbxproj | 632 ++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 89 + .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 46993 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3276 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 1429 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 5933 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1243 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 14800 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1874 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 339 ++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 14 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../example/macos/Runner/Release.entitlements | 8 + .../example/pubspec.yaml | 36 + .../SyncfusionFlutterPdfViewerPlugin.swift | 168 + ...syncfusion_flutter_pdfviewer_macos.podspec | 22 + .../pubspec.yaml | 21 + .../CHANGELOG.md | 2 +- .../pubspec.yaml | 5 +- .../CHANGELOG.md | 2 +- .../lib/generated_plugin_registrant.dart | 15 + .../example/pubspec.yaml | 10 +- .../example/web/index.html | 39 + .../pubspec.yaml | 6 +- .../CHANGELOG.md | 13 + .../syncfusion_flutter_signaturepad/README.md | 15 +- .../example/linux/.gitignore | 1 + .../example/linux/CMakeLists.txt | 106 + .../example/linux/flutter/CMakeLists.txt | 91 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../linux/flutter/generated_plugins.cmake | 15 + .../example/linux/main.cc | 6 + .../example/linux/my_application.cc | 104 + .../example/linux/my_application.h | 18 + .../example/macos/.gitignore | 6 + .../macos/Flutter/Flutter-Debug.xcconfig | 1 + .../macos/Flutter/Flutter-Release.xcconfig | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 10 + .../macos/Runner.xcodeproj/project.pbxproj | 572 +++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 89 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 46993 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3276 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 1429 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 5933 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1243 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 14800 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1874 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 339 ++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../example/macos/Runner/Release.entitlements | 8 + .../example/pubspec.yaml | 3 - .../example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/index.html | 45 + .../example/web/manifest.json | 23 + .../example/windows/.gitignore | 17 + .../example/windows/CMakeLists.txt | 95 + .../example/windows/flutter/CMakeLists.txt | 103 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../windows/flutter/generated_plugins.cmake | 15 + .../example/windows/runner/CMakeLists.txt | 18 + .../example/windows/runner/Runner.rc | 121 + .../example/windows/runner/flutter_window.cpp | 64 + .../example/windows/runner/flutter_window.h | 39 + .../example/windows/runner/main.cpp | 42 + .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../example/windows/runner/run_loop.cpp | 66 + .../example/windows/runner/run_loop.h | 40 + .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 64 + .../example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 245 ++ .../example/windows/runner/win32_window.h | 98 + .../lib/signaturepad.dart | 276 +- .../pubspec.yaml | 4 +- .../syncfusion_flutter_sliders/CHANGELOG.md | 124 +- packages/syncfusion_flutter_sliders/README.md | 22 +- .../example/lib/main.dart | 63 +- .../example/linux/.gitignore | 1 + .../example/linux/CMakeLists.txt | 116 + .../example/linux/flutter/CMakeLists.txt | 87 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../linux/flutter/generated_plugins.cmake | 15 + .../example/linux/main.cc | 6 + .../example/linux/my_application.cc | 105 + .../example/linux/my_application.h | 18 + .../example/macos/.gitignore | 6 + .../macos/Flutter/Flutter-Debug.xcconfig | 1 + .../macos/Flutter/Flutter-Release.xcconfig | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 10 + .../macos/Runner.xcodeproj/project.pbxproj | 572 +++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 89 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 46993 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3276 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 1429 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 5933 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1243 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 14800 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1874 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 339 ++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../example/macos/Runner/Release.entitlements | 8 + .../example/pubspec.yaml | 12 +- .../example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/index.html | 98 + .../example/web/manifest.json | 23 + .../example/windows/.gitignore | 17 + .../example/windows/CMakeLists.txt | 95 + .../example/windows/flutter/CMakeLists.txt | 103 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../windows/flutter/generated_plugins.cmake | 15 + .../example/windows/runner/CMakeLists.txt | 18 + .../example/windows/runner/Runner.rc | 121 + .../example/windows/runner/flutter_window.cpp | 64 + .../example/windows/runner/flutter_window.h | 39 + .../example/windows/runner/main.cpp | 42 + .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../example/windows/runner/run_loop.cpp | 66 + .../example/windows/runner/run_loop.h | 40 + .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 64 + .../example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 245 ++ .../example/windows/runner/win32_window.h | 98 + .../lib/src/common.dart | 14 +- .../lib/src/constants.dart | 2 + .../lib/src/range_selector.dart | 1303 ++----- .../lib/src/range_slider.dart | 1121 +----- .../lib/src/range_slider_base.dart | 992 ++++++ .../lib/src/slider.dart | 177 +- ...nder_slider_base.dart => slider_base.dart} | 217 +- .../lib/src/slider_shapes.dart | 70 +- .../syncfusion_flutter_sliders/pubspec.yaml | 4 +- .../syncfusion_flutter_treemap/CHANGELOG.md | 18 + packages/syncfusion_flutter_treemap/README.md | 141 +- .../example/lib/main.dart | 19 +- .../example/linux/.gitignore | 1 + .../example/linux/CMakeLists.txt | 116 + .../example/linux/flutter/CMakeLists.txt | 87 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../linux/flutter/generated_plugins.cmake | 15 + .../example/linux/main.cc | 6 + .../example/linux/my_application.cc | 105 + .../example/linux/my_application.h | 18 + .../example/macos/.gitignore | 6 + .../macos/Flutter/Flutter-Debug.xcconfig | 1 + .../macos/Flutter/Flutter-Release.xcconfig | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 10 + .../macos/Runner.xcodeproj/project.pbxproj | 572 +++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 89 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 46993 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3276 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 1429 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 5933 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1243 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 14800 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1874 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 339 ++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../example/macos/Runner/Release.entitlements | 8 + .../example/pubspec.yaml | 3 - .../example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/index.html | 98 + .../example/web/manifest.json | 23 + .../example/windows/.gitignore | 17 + .../example/windows/CMakeLists.txt | 95 + .../example/windows/flutter/CMakeLists.txt | 103 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../windows/flutter/generated_plugins.cmake | 15 + .../example/windows/runner/CMakeLists.txt | 18 + .../example/windows/runner/Runner.rc | 121 + .../example/windows/runner/flutter_window.cpp | 64 + .../example/windows/runner/flutter_window.h | 39 + .../example/windows/runner/main.cpp | 42 + .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../example/windows/runner/run_loop.cpp | 66 + .../example/windows/runner/run_loop.h | 40 + .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 64 + .../example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 245 ++ .../example/windows/runner/win32_window.h | 98 + .../lib/src/animated_border.dart | 79 + .../lib/src/controller.dart | 123 + .../lib/src/layouts.dart | 2155 +++++++++--- .../lib/src/legend.dart | 2115 +++-------- .../lib/src/tooltip.dart | 2 +- .../lib/treemap.dart | 963 +++++- .../syncfusion_flutter_treemap/pubspec.yaml | 2 +- .../syncfusion_flutter_xlsio/CHANGELOG.md | 15 +- packages/syncfusion_flutter_xlsio/README.md | 15 +- .../example/lib/helper/save_file_mobile.dart | 39 + .../example/lib/helper/save_file_web.dart | 18 + .../example/lib/main.dart | 26 +- .../example/linux/.gitignore | 1 + .../example/linux/CMakeLists.txt | 116 + .../example/linux/flutter/CMakeLists.txt | 91 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../linux/flutter/generated_plugins.cmake | 15 + .../example/linux/main.cc | 6 + .../example/linux/my_application.cc | 105 + .../example/linux/my_application.h | 18 + .../example/macos/.gitignore | 6 + .../macos/Flutter/Flutter-Debug.xcconfig | 1 + .../macos/Flutter/Flutter-Release.xcconfig | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 14 + .../macos/Runner.xcodeproj/project.pbxproj | 572 +++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 89 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 46993 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3276 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 1429 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 5933 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1243 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 14800 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1874 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 339 ++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../example/macos/Runner/Release.entitlements | 8 + .../example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/index.html | 98 + .../example/web/manifest.json | 23 + .../example/windows/.gitignore | 17 + .../example/windows/CMakeLists.txt | 95 + .../example/windows/flutter/CMakeLists.txt | 103 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../windows/flutter/generated_plugins.cmake | 15 + .../example/windows/runner/CMakeLists.txt | 18 + .../example/windows/runner/Runner.rc | 121 + .../example/windows/runner/flutter_window.cpp | 64 + .../example/windows/runner/flutter_window.h | 39 + .../example/windows/runner/main.cpp | 42 + .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../example/windows/runner/run_loop.cpp | 66 + .../example/windows/runner/run_loop.h | 40 + .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 64 + .../example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 245 ++ .../example/windows/runner/win32_window.h | 98 + .../lib/src/xlsio/calculate/calc_engine.dart | 723 ++-- .../lib/src/xlsio/calculate/formula_info.dart | 2 + .../xlsio/calculate/sheet_family_item.dart | 12 +- .../lib/src/xlsio/calculate/stack.dart | 4 +- .../lib/src/xlsio/cell_styles/border.dart | 7 +- .../lib/src/xlsio/cell_styles/borders.dart | 40 +- .../lib/src/xlsio/cell_styles/cell_style.dart | 14 +- .../xlsio/cell_styles/cell_style_wrapper.dart | 10 +- .../src/xlsio/cell_styles/cell_style_xfs.dart | 5 + .../lib/src/xlsio/cell_styles/font.dart | 4 +- .../xlsio/cell_styles/styles_collection.dart | 10 +- .../above_below_average.dart | 99 + .../above_below_average_impl.dart | 41 + .../above_below_average_wrapper.dart | 43 + .../color_scale/color_scale.dart | 4 +- .../color_scale/color_scale_impl.dart | 10 +- .../color_scale/color_scale_wrapper.dart | 2 +- .../condformat_collection_wrapper.dart | 2 +- .../condformat_wrapper.dart | 122 +- .../conditional_format/condition_value.dart | 24 +- .../conditional_format/conditionalformat.dart | 28 +- .../conditionalformat_collections.dart | 2 +- .../conditionalformat_impl.dart | 119 +- .../conditional_format/data_bar/data_bar.dart | 38 +- .../data_bar/data_bar_impl.dart | 14 +- .../data_bar/data_bar_wrapper.dart | 2 +- .../conditional_format/icon_set/icon_set.dart | 10 +- .../icon_set/icon_set_impl.dart | 4 +- .../icon_set/icon_set_wrapper.dart | 2 +- .../top_bottom/top_bottom.dart | 133 + .../top_bottom/top_bottom_impl.dart | 34 + .../top_bottom/top_bottom_wrapper.dart | 60 + .../lib/src/xlsio/formats/format.dart | 8 +- .../lib/src/xlsio/formats/format_parser.dart | 39 +- .../lib/src/xlsio/formats/format_section.dart | 103 +- .../formats/format_section_collection.dart | 30 +- .../formats/format_tokens/am_pm_token.dart | 30 +- .../format_tokens/character_token.dart | 23 +- .../formats/format_tokens/constants.dart | 8 - .../formats/format_tokens/day_token.dart | 25 +- .../format_tokens/decimal_point_token.dart | 20 +- .../xlsio/formats/format_tokens/enums.dart | 64 - .../format_tokens/format_token_base.dart | 16 +- .../formats/format_tokens/fraction_token.dart | 20 +- .../formats/format_tokens/hour_24_token.dart | 18 +- .../formats/format_tokens/hour_token.dart | 21 +- .../format_tokens/milli_second_token.dart | 17 +- .../formats/format_tokens/minute_token.dart | 22 +- .../formats/format_tokens/month_token.dart | 20 +- .../formats/format_tokens/second_token.dart | 31 +- .../significant_digit_token.dart | 34 +- .../formats/format_tokens/unknown_token.dart | 19 +- .../formats/format_tokens/year_token.dart | 20 +- .../src/xlsio/formats/formats_collection.dart | 39 +- .../src/xlsio/general/autofit_manager.dart | 123 +- .../lib/src/xlsio/general/culture_info.dart | 11 +- .../src/xlsio/general/serialize_workbook.dart | 448 ++- .../lib/src/xlsio/general/workbook.dart | 95 +- .../lib/src/xlsio/hyperlinks/hyperlink.dart | 14 + .../hyperlinks/hyperlink_collection.dart | 18 +- .../lib/src/xlsio/images/picture.dart | 8 +- .../src/xlsio/images/pictures_collection.dart | 4 +- .../merged_cells/merged_cell_collection.dart | 4 +- .../lib/src/xlsio/range/column.dart | 4 +- .../src/xlsio/range/column_collection.dart | 79 +- .../lib/src/xlsio/range/range.dart | 187 +- .../lib/src/xlsio/range/range_collection.dart | 10 +- .../lib/src/xlsio/range/row_collection.dart | 10 +- .../src/xlsio/security/security_helper.dart | 4 +- .../src/xlsio/worksheet/excel_data_row.dart | 33 + .../lib/src/xlsio/worksheet/worksheet.dart | 521 ++- .../xlsio/worksheet/worksheet_collection.dart | 4 +- .../syncfusion_flutter_xlsio/lib/xlsio.dart | 8 +- .../syncfusion_flutter_xlsio/pubspec.yaml | 6 +- packages/syncfusion_localizations/README.md | 13 +- .../example/lib/main.dart | 111 +- .../example/linux/.gitignore | 1 + .../example/linux/CMakeLists.txt | 106 + .../example/linux/flutter/CMakeLists.txt | 91 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../linux/flutter/generated_plugins.cmake | 15 + .../example/linux/main.cc | 6 + .../example/linux/my_application.cc | 104 + .../example/linux/my_application.h | 18 + .../example/macos/.gitignore | 6 + .../macos/Flutter/Flutter-Debug.xcconfig | 1 + .../macos/Flutter/Flutter-Release.xcconfig | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 10 + .../macos/Runner.xcodeproj/project.pbxproj | 572 +++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 89 + .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../example/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 46993 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 3276 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 1429 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 5933 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1243 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 14800 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 1874 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 339 ++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + .../example/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../example/macos/Runner/Release.entitlements | 8 + .../example/pubspec.yaml | 12 +- .../example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/index.html | 45 + .../example/web/manifest.json | 23 + .../example/windows/.gitignore | 17 + .../example/windows/CMakeLists.txt | 95 + .../example/windows/flutter/CMakeLists.txt | 103 + .../flutter/generated_plugin_registrant.cc | 9 + .../flutter/generated_plugin_registrant.h | 13 + .../windows/flutter/generated_plugins.cmake | 15 + .../example/windows/runner/CMakeLists.txt | 18 + .../example/windows/runner/Runner.rc | 121 + .../example/windows/runner/flutter_window.cpp | 64 + .../example/windows/runner/flutter_window.h | 39 + .../example/windows/runner/main.cpp | 42 + .../example/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../example/windows/runner/run_loop.cpp | 66 + .../example/windows/runner/run_loop.h | 40 + .../windows/runner/runner.exe.manifest | 20 + .../example/windows/runner/utils.cpp | 64 + .../example/windows/runner/utils.h | 19 + .../example/windows/runner/win32_window.cpp | 245 ++ .../example/windows/runner/win32_window.h | 98 + .../lib/src/global_localizations.dart | 2 +- .../syncfusion_localizations/pubspec.yaml | 4 +- 1802 files changed, 109126 insertions(+), 39297 deletions(-) create mode 100644 packages/syncfusion_flutter_barcodes/example/linux/.gitignore create mode 100644 packages/syncfusion_flutter_barcodes/example/linux/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_barcodes/example/linux/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_barcodes/example/linux/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_barcodes/example/linux/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_barcodes/example/linux/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_barcodes/example/linux/main.cc create mode 100644 packages/syncfusion_flutter_barcodes/example/linux/my_application.cc create mode 100644 packages/syncfusion_flutter_barcodes/example/linux/my_application.h create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/.gitignore create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Runner/AppDelegate.swift create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Runner/Configs/Release.xcconfig create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Runner/DebugProfile.entitlements create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Runner/Info.plist create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Runner/MainFlutterWindow.swift create mode 100644 packages/syncfusion_flutter_barcodes/example/macos/Runner/Release.entitlements create mode 100644 packages/syncfusion_flutter_barcodes/example/web/favicon.png create mode 100644 packages/syncfusion_flutter_barcodes/example/web/icons/Icon-192.png create mode 100644 packages/syncfusion_flutter_barcodes/example/web/icons/Icon-512.png create mode 100644 packages/syncfusion_flutter_barcodes/example/web/index.html create mode 100644 packages/syncfusion_flutter_barcodes/example/web/manifest.json create mode 100644 packages/syncfusion_flutter_barcodes/example/windows/.gitignore create mode 100644 packages/syncfusion_flutter_barcodes/example/windows/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_barcodes/example/windows/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_barcodes/example/windows/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_barcodes/example/windows/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_barcodes/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_barcodes/example/windows/runner/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_barcodes/example/windows/runner/Runner.rc create mode 100644 packages/syncfusion_flutter_barcodes/example/windows/runner/flutter_window.cpp create mode 100644 packages/syncfusion_flutter_barcodes/example/windows/runner/flutter_window.h create mode 100644 packages/syncfusion_flutter_barcodes/example/windows/runner/main.cpp create mode 100644 packages/syncfusion_flutter_barcodes/example/windows/runner/resource.h create mode 100644 packages/syncfusion_flutter_barcodes/example/windows/runner/resources/app_icon.ico create mode 100644 packages/syncfusion_flutter_barcodes/example/windows/runner/run_loop.cpp create mode 100644 packages/syncfusion_flutter_barcodes/example/windows/runner/run_loop.h create mode 100644 packages/syncfusion_flutter_barcodes/example/windows/runner/runner.exe.manifest create mode 100644 packages/syncfusion_flutter_barcodes/example/windows/runner/utils.cpp create mode 100644 packages/syncfusion_flutter_barcodes/example/windows/runner/utils.h create mode 100644 packages/syncfusion_flutter_barcodes/example/windows/runner/win32_window.cpp create mode 100644 packages/syncfusion_flutter_barcodes/example/windows/runner/win32_window.h create mode 100644 packages/syncfusion_flutter_calendar/example/android/.gitignore create mode 100644 packages/syncfusion_flutter_calendar/example/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 packages/syncfusion_flutter_calendar/example/android/app/src/main/res/values-night/styles.xml create mode 100644 packages/syncfusion_flutter_calendar/example/android/example_android.iml create mode 100644 packages/syncfusion_flutter_calendar/example/example.iml create mode 100644 packages/syncfusion_flutter_calendar/example/ios/.gitignore create mode 100644 packages/syncfusion_flutter_calendar/example/linux/.gitignore create mode 100644 packages/syncfusion_flutter_calendar/example/linux/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_calendar/example/linux/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_calendar/example/linux/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_calendar/example/linux/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_calendar/example/linux/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_calendar/example/linux/main.cc create mode 100644 packages/syncfusion_flutter_calendar/example/linux/my_application.cc create mode 100644 packages/syncfusion_flutter_calendar/example/linux/my_application.h create mode 100644 packages/syncfusion_flutter_calendar/example/macos/.gitignore create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Runner/AppDelegate.swift create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Runner/Configs/Release.xcconfig create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Runner/DebugProfile.entitlements create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Runner/Info.plist create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Runner/MainFlutterWindow.swift create mode 100644 packages/syncfusion_flutter_calendar/example/macos/Runner/Release.entitlements create mode 100644 packages/syncfusion_flutter_calendar/example/web/favicon.png create mode 100644 packages/syncfusion_flutter_calendar/example/web/icons/Icon-192.png create mode 100644 packages/syncfusion_flutter_calendar/example/web/icons/Icon-512.png create mode 100644 packages/syncfusion_flutter_calendar/example/web/index.html create mode 100644 packages/syncfusion_flutter_calendar/example/web/manifest.json create mode 100644 packages/syncfusion_flutter_calendar/example/windows/.gitignore create mode 100644 packages/syncfusion_flutter_calendar/example/windows/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_calendar/example/windows/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_calendar/example/windows/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_calendar/example/windows/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_calendar/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_calendar/example/windows/runner/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_calendar/example/windows/runner/Runner.rc create mode 100644 packages/syncfusion_flutter_calendar/example/windows/runner/flutter_window.cpp create mode 100644 packages/syncfusion_flutter_calendar/example/windows/runner/flutter_window.h create mode 100644 packages/syncfusion_flutter_calendar/example/windows/runner/main.cpp create mode 100644 packages/syncfusion_flutter_calendar/example/windows/runner/resource.h create mode 100644 packages/syncfusion_flutter_calendar/example/windows/runner/resources/app_icon.ico create mode 100644 packages/syncfusion_flutter_calendar/example/windows/runner/run_loop.cpp create mode 100644 packages/syncfusion_flutter_calendar/example/windows/runner/run_loop.h create mode 100644 packages/syncfusion_flutter_calendar/example/windows/runner/runner.exe.manifest create mode 100644 packages/syncfusion_flutter_calendar/example/windows/runner/utils.cpp create mode 100644 packages/syncfusion_flutter_calendar/example/windows/runner/utils.h create mode 100644 packages/syncfusion_flutter_calendar/example/windows/runner/win32_window.cpp create mode 100644 packages/syncfusion_flutter_calendar/example/windows/runner/win32_window.h create mode 100644 packages/syncfusion_flutter_calendar/lib/src/calendar/settings/week_number_style.dart create mode 100644 packages/syncfusion_flutter_charts/dartdoc_options.yaml create mode 100644 packages/syncfusion_flutter_charts/example/analysis_options.yaml create mode 100644 packages/syncfusion_flutter_charts/example/linux/.gitignore create mode 100644 packages/syncfusion_flutter_charts/example/linux/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_charts/example/linux/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_charts/example/linux/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_charts/example/linux/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_charts/example/linux/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_charts/example/linux/main.cc create mode 100644 packages/syncfusion_flutter_charts/example/linux/my_application.cc create mode 100644 packages/syncfusion_flutter_charts/example/linux/my_application.h create mode 100644 packages/syncfusion_flutter_charts/example/macos/.gitignore create mode 100644 packages/syncfusion_flutter_charts/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 packages/syncfusion_flutter_charts/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 packages/syncfusion_flutter_charts/example/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 packages/syncfusion_flutter_charts/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 packages/syncfusion_flutter_charts/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_charts/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/syncfusion_flutter_charts/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/syncfusion_flutter_charts/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_charts/example/macos/Runner/AppDelegate.swift create mode 100644 packages/syncfusion_flutter_charts/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/syncfusion_flutter_charts/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 packages/syncfusion_flutter_charts/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 packages/syncfusion_flutter_charts/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 packages/syncfusion_flutter_charts/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 packages/syncfusion_flutter_charts/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 packages/syncfusion_flutter_charts/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 packages/syncfusion_flutter_charts/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 packages/syncfusion_flutter_charts/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 packages/syncfusion_flutter_charts/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 packages/syncfusion_flutter_charts/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 packages/syncfusion_flutter_charts/example/macos/Runner/Configs/Release.xcconfig create mode 100644 packages/syncfusion_flutter_charts/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 packages/syncfusion_flutter_charts/example/macos/Runner/DebugProfile.entitlements create mode 100644 packages/syncfusion_flutter_charts/example/macos/Runner/Info.plist create mode 100644 packages/syncfusion_flutter_charts/example/macos/Runner/MainFlutterWindow.swift create mode 100644 packages/syncfusion_flutter_charts/example/macos/Runner/Release.entitlements create mode 100644 packages/syncfusion_flutter_charts/example/web/favicon.png create mode 100644 packages/syncfusion_flutter_charts/example/web/icons/Icon-192.png create mode 100644 packages/syncfusion_flutter_charts/example/web/icons/Icon-512.png create mode 100644 packages/syncfusion_flutter_charts/example/web/index.html create mode 100644 packages/syncfusion_flutter_charts/example/web/manifest.json create mode 100644 packages/syncfusion_flutter_charts/example/windows/.gitignore create mode 100644 packages/syncfusion_flutter_charts/example/windows/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_charts/example/windows/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_charts/example/windows/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_charts/example/windows/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_charts/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_charts/example/windows/runner/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_charts/example/windows/runner/Runner.rc create mode 100644 packages/syncfusion_flutter_charts/example/windows/runner/flutter_window.cpp create mode 100644 packages/syncfusion_flutter_charts/example/windows/runner/flutter_window.h create mode 100644 packages/syncfusion_flutter_charts/example/windows/runner/main.cpp create mode 100644 packages/syncfusion_flutter_charts/example/windows/runner/resource.h create mode 100644 packages/syncfusion_flutter_charts/example/windows/runner/resources/app_icon.ico create mode 100644 packages/syncfusion_flutter_charts/example/windows/runner/run_loop.cpp create mode 100644 packages/syncfusion_flutter_charts/example/windows/runner/run_loop.h create mode 100644 packages/syncfusion_flutter_charts/example/windows/runner/runner.exe.manifest create mode 100644 packages/syncfusion_flutter_charts/example/windows/runner/utils.cpp create mode 100644 packages/syncfusion_flutter_charts/example/windows/runner/utils.h create mode 100644 packages/syncfusion_flutter_charts/example/windows/runner/win32_window.cpp create mode 100644 packages/syncfusion_flutter_charts/example/windows/runner/win32_window.h create mode 100644 packages/syncfusion_flutter_charts/lib/src/common/rendering_details.dart delete mode 100644 packages/syncfusion_flutter_charts/lib/src/common/user_interaction/selection.dart create mode 100644 packages/syncfusion_flutter_charts/lib/src/common/utils/typedef.dart create mode 100644 packages/syncfusion_flutter_core/example/linux/.gitignore create mode 100644 packages/syncfusion_flutter_core/example/linux/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_core/example/linux/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_core/example/linux/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_core/example/linux/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_core/example/linux/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_core/example/linux/main.cc create mode 100644 packages/syncfusion_flutter_core/example/linux/my_application.cc create mode 100644 packages/syncfusion_flutter_core/example/linux/my_application.h create mode 100644 packages/syncfusion_flutter_core/example/macos/.gitignore create mode 100644 packages/syncfusion_flutter_core/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 packages/syncfusion_flutter_core/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 packages/syncfusion_flutter_core/example/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 packages/syncfusion_flutter_core/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 packages/syncfusion_flutter_core/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_core/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/syncfusion_flutter_core/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/syncfusion_flutter_core/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_core/example/macos/Runner/AppDelegate.swift create mode 100644 packages/syncfusion_flutter_core/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/syncfusion_flutter_core/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 packages/syncfusion_flutter_core/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 packages/syncfusion_flutter_core/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 packages/syncfusion_flutter_core/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 packages/syncfusion_flutter_core/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 packages/syncfusion_flutter_core/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 packages/syncfusion_flutter_core/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 packages/syncfusion_flutter_core/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 packages/syncfusion_flutter_core/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 packages/syncfusion_flutter_core/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 packages/syncfusion_flutter_core/example/macos/Runner/Configs/Release.xcconfig create mode 100644 packages/syncfusion_flutter_core/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 packages/syncfusion_flutter_core/example/macos/Runner/DebugProfile.entitlements create mode 100644 packages/syncfusion_flutter_core/example/macos/Runner/Info.plist create mode 100644 packages/syncfusion_flutter_core/example/macos/Runner/MainFlutterWindow.swift create mode 100644 packages/syncfusion_flutter_core/example/macos/Runner/Release.entitlements create mode 100644 packages/syncfusion_flutter_core/example/web/favicon.png create mode 100644 packages/syncfusion_flutter_core/example/web/icons/Icon-192.png create mode 100644 packages/syncfusion_flutter_core/example/web/icons/Icon-512.png create mode 100644 packages/syncfusion_flutter_core/example/web/index.html create mode 100644 packages/syncfusion_flutter_core/example/web/manifest.json create mode 100644 packages/syncfusion_flutter_core/example/windows/.gitignore create mode 100644 packages/syncfusion_flutter_core/example/windows/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_core/example/windows/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_core/example/windows/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_core/example/windows/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_core/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_core/example/windows/runner/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_core/example/windows/runner/Runner.rc create mode 100644 packages/syncfusion_flutter_core/example/windows/runner/flutter_window.cpp create mode 100644 packages/syncfusion_flutter_core/example/windows/runner/flutter_window.h create mode 100644 packages/syncfusion_flutter_core/example/windows/runner/main.cpp create mode 100644 packages/syncfusion_flutter_core/example/windows/runner/resource.h create mode 100644 packages/syncfusion_flutter_core/example/windows/runner/resources/app_icon.ico create mode 100644 packages/syncfusion_flutter_core/example/windows/runner/run_loop.cpp create mode 100644 packages/syncfusion_flutter_core/example/windows/runner/run_loop.h create mode 100644 packages/syncfusion_flutter_core/example/windows/runner/runner.exe.manifest create mode 100644 packages/syncfusion_flutter_core/example/windows/runner/utils.cpp create mode 100644 packages/syncfusion_flutter_core/example/windows/runner/utils.h create mode 100644 packages/syncfusion_flutter_core/example/windows/runner/win32_window.cpp create mode 100644 packages/syncfusion_flutter_core/example/windows/runner/win32_window.h create mode 100644 packages/syncfusion_flutter_core/lib/legend_internal.dart create mode 100644 packages/syncfusion_flutter_core/lib/src/legend/legend.dart delete mode 100644 packages/syncfusion_flutter_core/lib/src/test/tooltip_tests.dart create mode 100644 packages/syncfusion_flutter_core/lib/src/utils/shape_helper.dart create mode 100644 packages/syncfusion_flutter_datagrid/example/linux/.gitignore create mode 100644 packages/syncfusion_flutter_datagrid/example/linux/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_datagrid/example/linux/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_datagrid/example/linux/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_datagrid/example/linux/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_datagrid/example/linux/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_datagrid/example/linux/main.cc create mode 100644 packages/syncfusion_flutter_datagrid/example/linux/my_application.cc create mode 100644 packages/syncfusion_flutter_datagrid/example/linux/my_application.h create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/.gitignore create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Runner/AppDelegate.swift create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Runner/Configs/Release.xcconfig create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Runner/DebugProfile.entitlements create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Runner/Info.plist create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Runner/MainFlutterWindow.swift create mode 100644 packages/syncfusion_flutter_datagrid/example/macos/Runner/Release.entitlements create mode 100644 packages/syncfusion_flutter_datagrid/example/web/favicon.png create mode 100644 packages/syncfusion_flutter_datagrid/example/web/icons/Icon-192.png create mode 100644 packages/syncfusion_flutter_datagrid/example/web/icons/Icon-512.png create mode 100644 packages/syncfusion_flutter_datagrid/example/web/index.html create mode 100644 packages/syncfusion_flutter_datagrid/example/web/manifest.json create mode 100644 packages/syncfusion_flutter_datagrid/example/windows/.gitignore create mode 100644 packages/syncfusion_flutter_datagrid/example/windows/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_datagrid/example/windows/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_datagrid/example/windows/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_datagrid/example/windows/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_datagrid/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_datagrid/example/windows/runner/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_datagrid/example/windows/runner/Runner.rc create mode 100644 packages/syncfusion_flutter_datagrid/example/windows/runner/flutter_window.cpp create mode 100644 packages/syncfusion_flutter_datagrid/example/windows/runner/flutter_window.h create mode 100644 packages/syncfusion_flutter_datagrid/example/windows/runner/main.cpp create mode 100644 packages/syncfusion_flutter_datagrid/example/windows/runner/resource.h create mode 100644 packages/syncfusion_flutter_datagrid/example/windows/runner/resources/app_icon.ico create mode 100644 packages/syncfusion_flutter_datagrid/example/windows/runner/run_loop.cpp create mode 100644 packages/syncfusion_flutter_datagrid/example/windows/runner/run_loop.h create mode 100644 packages/syncfusion_flutter_datagrid/example/windows/runner/runner.exe.manifest create mode 100644 packages/syncfusion_flutter_datagrid/example/windows/runner/utils.cpp create mode 100644 packages/syncfusion_flutter_datagrid/example/windows/runner/utils.h create mode 100644 packages/syncfusion_flutter_datagrid/example/windows/runner/win32_window.cpp create mode 100644 packages/syncfusion_flutter_datagrid/example/windows/runner/win32_window.h create mode 100644 packages/syncfusion_flutter_datepicker/example/android/.gitignore create mode 100644 packages/syncfusion_flutter_datepicker/example/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 packages/syncfusion_flutter_datepicker/example/android/app/src/main/res/values-night/styles.xml create mode 100644 packages/syncfusion_flutter_datepicker/example/android/example_android.iml create mode 100644 packages/syncfusion_flutter_datepicker/example/example.iml create mode 100644 packages/syncfusion_flutter_datepicker/example/ios/.gitignore create mode 100644 packages/syncfusion_flutter_datepicker/example/linux/.gitignore create mode 100644 packages/syncfusion_flutter_datepicker/example/linux/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_datepicker/example/linux/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_datepicker/example/linux/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_datepicker/example/linux/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_datepicker/example/linux/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_datepicker/example/linux/main.cc create mode 100644 packages/syncfusion_flutter_datepicker/example/linux/my_application.cc create mode 100644 packages/syncfusion_flutter_datepicker/example/linux/my_application.h create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/.gitignore create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Runner/AppDelegate.swift create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Runner/Configs/Release.xcconfig create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Runner/DebugProfile.entitlements create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Runner/Info.plist create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Runner/MainFlutterWindow.swift create mode 100644 packages/syncfusion_flutter_datepicker/example/macos/Runner/Release.entitlements create mode 100644 packages/syncfusion_flutter_datepicker/example/web/favicon.png create mode 100644 packages/syncfusion_flutter_datepicker/example/web/icons/Icon-192.png create mode 100644 packages/syncfusion_flutter_datepicker/example/web/icons/Icon-512.png create mode 100644 packages/syncfusion_flutter_datepicker/example/web/index.html create mode 100644 packages/syncfusion_flutter_datepicker/example/web/manifest.json create mode 100644 packages/syncfusion_flutter_datepicker/example/windows/.gitignore create mode 100644 packages/syncfusion_flutter_datepicker/example/windows/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_datepicker/example/windows/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_datepicker/example/windows/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_datepicker/example/windows/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_datepicker/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_datepicker/example/windows/runner/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_datepicker/example/windows/runner/Runner.rc create mode 100644 packages/syncfusion_flutter_datepicker/example/windows/runner/flutter_window.cpp create mode 100644 packages/syncfusion_flutter_datepicker/example/windows/runner/flutter_window.h create mode 100644 packages/syncfusion_flutter_datepicker/example/windows/runner/main.cpp create mode 100644 packages/syncfusion_flutter_datepicker/example/windows/runner/resource.h create mode 100644 packages/syncfusion_flutter_datepicker/example/windows/runner/resources/app_icon.ico create mode 100644 packages/syncfusion_flutter_datepicker/example/windows/runner/run_loop.cpp create mode 100644 packages/syncfusion_flutter_datepicker/example/windows/runner/run_loop.h create mode 100644 packages/syncfusion_flutter_datepicker/example/windows/runner/runner.exe.manifest create mode 100644 packages/syncfusion_flutter_datepicker/example/windows/runner/utils.cpp create mode 100644 packages/syncfusion_flutter_datepicker/example/windows/runner/utils.h create mode 100644 packages/syncfusion_flutter_datepicker/example/windows/runner/win32_window.cpp create mode 100644 packages/syncfusion_flutter_datepicker/example/windows/runner/win32_window.h create mode 100644 packages/syncfusion_flutter_gauges/example/linux/.gitignore create mode 100644 packages/syncfusion_flutter_gauges/example/linux/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_gauges/example/linux/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_gauges/example/linux/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_gauges/example/linux/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_gauges/example/linux/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_gauges/example/linux/main.cc create mode 100644 packages/syncfusion_flutter_gauges/example/linux/my_application.cc create mode 100644 packages/syncfusion_flutter_gauges/example/linux/my_application.h create mode 100644 packages/syncfusion_flutter_gauges/example/macos/.gitignore create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Runner/AppDelegate.swift create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Runner/Configs/Release.xcconfig create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Runner/DebugProfile.entitlements create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Runner/Info.plist create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Runner/MainFlutterWindow.swift create mode 100644 packages/syncfusion_flutter_gauges/example/macos/Runner/Release.entitlements create mode 100644 packages/syncfusion_flutter_gauges/example/web/favicon.png create mode 100644 packages/syncfusion_flutter_gauges/example/web/icons/Icon-192.png create mode 100644 packages/syncfusion_flutter_gauges/example/web/icons/Icon-512.png create mode 100644 packages/syncfusion_flutter_gauges/example/web/index.html create mode 100644 packages/syncfusion_flutter_gauges/example/web/manifest.json create mode 100644 packages/syncfusion_flutter_gauges/example/windows/.gitignore create mode 100644 packages/syncfusion_flutter_gauges/example/windows/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_gauges/example/windows/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_gauges/example/windows/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_gauges/example/windows/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_gauges/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_gauges/example/windows/runner/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_gauges/example/windows/runner/Runner.rc create mode 100644 packages/syncfusion_flutter_gauges/example/windows/runner/flutter_window.cpp create mode 100644 packages/syncfusion_flutter_gauges/example/windows/runner/flutter_window.h create mode 100644 packages/syncfusion_flutter_gauges/example/windows/runner/main.cpp create mode 100644 packages/syncfusion_flutter_gauges/example/windows/runner/resource.h create mode 100644 packages/syncfusion_flutter_gauges/example/windows/runner/resources/app_icon.ico create mode 100644 packages/syncfusion_flutter_gauges/example/windows/runner/run_loop.cpp create mode 100644 packages/syncfusion_flutter_gauges/example/windows/runner/run_loop.h create mode 100644 packages/syncfusion_flutter_gauges/example/windows/runner/runner.exe.manifest create mode 100644 packages/syncfusion_flutter_gauges/example/windows/runner/utils.cpp create mode 100644 packages/syncfusion_flutter_gauges/example/windows/runner/utils.h create mode 100644 packages/syncfusion_flutter_gauges/example/windows/runner/win32_window.cpp create mode 100644 packages/syncfusion_flutter_gauges/example/windows/runner/win32_window.h create mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/annotation/gauge_annotation_renderer.dart delete mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/axis/gauge_axis.dart rename packages/syncfusion_flutter_gauges/lib/src/radial_gauge/{common/axis_label.dart => axis/radial_axis_label.dart} (93%) create mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/axis/radial_axis_parent_widget.dart create mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/axis/radial_axis_scope.dart create mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/axis/radial_axis_widget.dart delete mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/common/common.dart delete mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/common/gauge_annotation_renderer.dart delete mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/common/radial_gauge_renderer.dart delete mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/common/widget_pointer_renderer.dart create mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/gauge/radial_gauge_scope.dart delete mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/gauge_painter/marker_pointer_painter.dart delete mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/gauge_painter/needle_pointer_painter.dart delete mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/gauge_painter/radial_axis_painter.dart delete mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/gauge_painter/range_painter.dart delete mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/gauge_painter/range_pointer_painter.dart create mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/pointers/marker_pointer_renderer.dart create mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/pointers/needle_pointer_renderer.dart create mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/pointers/pointer_painting_details.dart create mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/pointers/range_pointer_renderer.dart create mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/pointers/widget_pointer_renderer.dart create mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/range/gauge_range_renderer.dart delete mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/renderers/gauge_pointer_renderer.dart delete mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/renderers/gauge_range_renderer.dart delete mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/renderers/marker_pointer_renderer_base.dart delete mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/renderers/needle_pointer_renderer_base.dart delete mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/renderers/radial_axis_renderer_base.dart delete mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/renderers/range_pointer_renderer.dart delete mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/renderers/widget_pointer_renderer_base.dart create mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/styles/radial_knob_style.dart create mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/styles/radial_tail_style.dart create mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/styles/radial_text_style.dart create mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/styles/radial_tick_style.dart create mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/title/radial_title.dart create mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/utils/radial_callback_args.dart create mode 100644 packages/syncfusion_flutter_gauges/lib/src/radial_gauge/utils/radial_gauge_typedef.dart create mode 100644 packages/syncfusion_flutter_maps/example/linux/.gitignore create mode 100644 packages/syncfusion_flutter_maps/example/linux/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_maps/example/linux/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_maps/example/linux/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_maps/example/linux/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_maps/example/linux/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_maps/example/linux/main.cc create mode 100644 packages/syncfusion_flutter_maps/example/linux/my_application.cc create mode 100644 packages/syncfusion_flutter_maps/example/linux/my_application.h create mode 100644 packages/syncfusion_flutter_maps/example/macos/.gitignore create mode 100644 packages/syncfusion_flutter_maps/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 packages/syncfusion_flutter_maps/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 packages/syncfusion_flutter_maps/example/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 packages/syncfusion_flutter_maps/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 packages/syncfusion_flutter_maps/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_maps/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/syncfusion_flutter_maps/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/syncfusion_flutter_maps/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_maps/example/macos/Runner/AppDelegate.swift create mode 100644 packages/syncfusion_flutter_maps/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/syncfusion_flutter_maps/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 packages/syncfusion_flutter_maps/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 packages/syncfusion_flutter_maps/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 packages/syncfusion_flutter_maps/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 packages/syncfusion_flutter_maps/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 packages/syncfusion_flutter_maps/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 packages/syncfusion_flutter_maps/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 packages/syncfusion_flutter_maps/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 packages/syncfusion_flutter_maps/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 packages/syncfusion_flutter_maps/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 packages/syncfusion_flutter_maps/example/macos/Runner/Configs/Release.xcconfig create mode 100644 packages/syncfusion_flutter_maps/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 packages/syncfusion_flutter_maps/example/macos/Runner/DebugProfile.entitlements create mode 100644 packages/syncfusion_flutter_maps/example/macos/Runner/Info.plist create mode 100644 packages/syncfusion_flutter_maps/example/macos/Runner/MainFlutterWindow.swift create mode 100644 packages/syncfusion_flutter_maps/example/macos/Runner/Release.entitlements create mode 100644 packages/syncfusion_flutter_maps/example/web/favicon.png create mode 100644 packages/syncfusion_flutter_maps/example/web/icons/Icon-192.png create mode 100644 packages/syncfusion_flutter_maps/example/web/icons/Icon-512.png create mode 100644 packages/syncfusion_flutter_maps/example/web/index.html create mode 100644 packages/syncfusion_flutter_maps/example/web/manifest.json create mode 100644 packages/syncfusion_flutter_maps/example/windows/.gitignore create mode 100644 packages/syncfusion_flutter_maps/example/windows/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_maps/example/windows/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_maps/example/windows/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_maps/example/windows/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_maps/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_maps/example/windows/runner/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_maps/example/windows/runner/Runner.rc create mode 100644 packages/syncfusion_flutter_maps/example/windows/runner/flutter_window.cpp create mode 100644 packages/syncfusion_flutter_maps/example/windows/runner/flutter_window.h create mode 100644 packages/syncfusion_flutter_maps/example/windows/runner/main.cpp create mode 100644 packages/syncfusion_flutter_maps/example/windows/runner/resource.h create mode 100644 packages/syncfusion_flutter_maps/example/windows/runner/resources/app_icon.ico create mode 100644 packages/syncfusion_flutter_maps/example/windows/runner/run_loop.cpp create mode 100644 packages/syncfusion_flutter_maps/example/windows/runner/run_loop.h create mode 100644 packages/syncfusion_flutter_maps/example/windows/runner/runner.exe.manifest create mode 100644 packages/syncfusion_flutter_maps/example/windows/runner/utils.cpp create mode 100644 packages/syncfusion_flutter_maps/example/windows/runner/utils.h create mode 100644 packages/syncfusion_flutter_maps/example/windows/runner/win32_window.cpp create mode 100644 packages/syncfusion_flutter_maps/example/windows/runner/win32_window.h delete mode 100644 packages/syncfusion_flutter_maps/lib/src/behavior/zoom_pan_behavior.dart rename packages/syncfusion_flutter_maps/lib/src/controller/{shape_layer_controller.dart => layer_controller.dart} (94%) create mode 100644 packages/syncfusion_flutter_officechart/example/lib/helper/save_file_mobile.dart create mode 100644 packages/syncfusion_flutter_officechart/example/lib/helper/save_file_web.dart create mode 100644 packages/syncfusion_flutter_officechart/example/linux/.gitignore create mode 100644 packages/syncfusion_flutter_officechart/example/linux/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_officechart/example/linux/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_officechart/example/linux/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_officechart/example/linux/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_officechart/example/linux/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_officechart/example/linux/main.cc create mode 100644 packages/syncfusion_flutter_officechart/example/linux/my_application.cc create mode 100644 packages/syncfusion_flutter_officechart/example/linux/my_application.h create mode 100644 packages/syncfusion_flutter_officechart/example/macos/.gitignore create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Runner/AppDelegate.swift create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Runner/Configs/Release.xcconfig create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Runner/DebugProfile.entitlements create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Runner/Info.plist create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Runner/MainFlutterWindow.swift create mode 100644 packages/syncfusion_flutter_officechart/example/macos/Runner/Release.entitlements create mode 100644 packages/syncfusion_flutter_officechart/example/web/favicon.png create mode 100644 packages/syncfusion_flutter_officechart/example/web/icons/Icon-192.png create mode 100644 packages/syncfusion_flutter_officechart/example/web/icons/Icon-512.png create mode 100644 packages/syncfusion_flutter_officechart/example/web/index.html create mode 100644 packages/syncfusion_flutter_officechart/example/web/manifest.json create mode 100644 packages/syncfusion_flutter_officechart/example/windows/.gitignore create mode 100644 packages/syncfusion_flutter_officechart/example/windows/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_officechart/example/windows/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_officechart/example/windows/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_officechart/example/windows/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_officechart/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_officechart/example/windows/runner/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_officechart/example/windows/runner/Runner.rc create mode 100644 packages/syncfusion_flutter_officechart/example/windows/runner/flutter_window.cpp create mode 100644 packages/syncfusion_flutter_officechart/example/windows/runner/flutter_window.h create mode 100644 packages/syncfusion_flutter_officechart/example/windows/runner/main.cpp create mode 100644 packages/syncfusion_flutter_officechart/example/windows/runner/resource.h create mode 100644 packages/syncfusion_flutter_officechart/example/windows/runner/resources/app_icon.ico create mode 100644 packages/syncfusion_flutter_officechart/example/windows/runner/run_loop.cpp create mode 100644 packages/syncfusion_flutter_officechart/example/windows/runner/run_loop.h create mode 100644 packages/syncfusion_flutter_officechart/example/windows/runner/runner.exe.manifest create mode 100644 packages/syncfusion_flutter_officechart/example/windows/runner/utils.cpp create mode 100644 packages/syncfusion_flutter_officechart/example/windows/runner/utils.h create mode 100644 packages/syncfusion_flutter_officechart/example/windows/runner/win32_window.cpp create mode 100644 packages/syncfusion_flutter_officechart/example/windows/runner/win32_window.h create mode 100644 packages/syncfusion_flutter_officecore/example/lib/helper/save_file_mobile.dart create mode 100644 packages/syncfusion_flutter_officecore/example/lib/helper/save_file_web.dart create mode 100644 packages/syncfusion_flutter_officecore/example/linux/.gitignore create mode 100644 packages/syncfusion_flutter_officecore/example/linux/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_officecore/example/linux/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_officecore/example/linux/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_officecore/example/linux/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_officecore/example/linux/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_officecore/example/linux/main.cc create mode 100644 packages/syncfusion_flutter_officecore/example/linux/my_application.cc create mode 100644 packages/syncfusion_flutter_officecore/example/linux/my_application.h create mode 100644 packages/syncfusion_flutter_officecore/example/macos/.gitignore create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Runner/AppDelegate.swift create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Runner/Configs/Release.xcconfig create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Runner/DebugProfile.entitlements create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Runner/Info.plist create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Runner/MainFlutterWindow.swift create mode 100644 packages/syncfusion_flutter_officecore/example/macos/Runner/Release.entitlements create mode 100644 packages/syncfusion_flutter_officecore/example/web/favicon.png create mode 100644 packages/syncfusion_flutter_officecore/example/web/icons/Icon-192.png create mode 100644 packages/syncfusion_flutter_officecore/example/web/icons/Icon-512.png create mode 100644 packages/syncfusion_flutter_officecore/example/web/index.html create mode 100644 packages/syncfusion_flutter_officecore/example/web/manifest.json create mode 100644 packages/syncfusion_flutter_officecore/example/windows/.gitignore create mode 100644 packages/syncfusion_flutter_officecore/example/windows/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_officecore/example/windows/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_officecore/example/windows/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_officecore/example/windows/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_officecore/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_officecore/example/windows/runner/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_officecore/example/windows/runner/Runner.rc create mode 100644 packages/syncfusion_flutter_officecore/example/windows/runner/flutter_window.cpp create mode 100644 packages/syncfusion_flutter_officecore/example/windows/runner/flutter_window.h create mode 100644 packages/syncfusion_flutter_officecore/example/windows/runner/main.cpp create mode 100644 packages/syncfusion_flutter_officecore/example/windows/runner/resource.h create mode 100644 packages/syncfusion_flutter_officecore/example/windows/runner/resources/app_icon.ico create mode 100644 packages/syncfusion_flutter_officecore/example/windows/runner/run_loop.cpp create mode 100644 packages/syncfusion_flutter_officecore/example/windows/runner/run_loop.h create mode 100644 packages/syncfusion_flutter_officecore/example/windows/runner/runner.exe.manifest create mode 100644 packages/syncfusion_flutter_officecore/example/windows/runner/utils.cpp create mode 100644 packages/syncfusion_flutter_officecore/example/windows/runner/utils.h create mode 100644 packages/syncfusion_flutter_officecore/example/windows/runner/win32_window.cpp create mode 100644 packages/syncfusion_flutter_officecore/example/windows/runner/win32_window.h create mode 100644 packages/syncfusion_flutter_pdf/example/lib/helper/save_file_mobile.dart create mode 100644 packages/syncfusion_flutter_pdf/example/lib/helper/save_file_web.dart create mode 100644 packages/syncfusion_flutter_pdf/example/linux/.gitignore create mode 100644 packages/syncfusion_flutter_pdf/example/linux/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_pdf/example/linux/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_pdf/example/linux/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_pdf/example/linux/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_pdf/example/linux/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_pdf/example/linux/main.cc create mode 100644 packages/syncfusion_flutter_pdf/example/linux/my_application.cc create mode 100644 packages/syncfusion_flutter_pdf/example/linux/my_application.h create mode 100644 packages/syncfusion_flutter_pdf/example/macos/.gitignore create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Runner/AppDelegate.swift create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Runner/Configs/Release.xcconfig create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Runner/DebugProfile.entitlements create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Runner/Info.plist create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Runner/MainFlutterWindow.swift create mode 100644 packages/syncfusion_flutter_pdf/example/macos/Runner/Release.entitlements create mode 100644 packages/syncfusion_flutter_pdf/example/web/favicon.png create mode 100644 packages/syncfusion_flutter_pdf/example/web/icons/Icon-192.png create mode 100644 packages/syncfusion_flutter_pdf/example/web/icons/Icon-512.png create mode 100644 packages/syncfusion_flutter_pdf/example/web/index.html create mode 100644 packages/syncfusion_flutter_pdf/example/web/manifest.json create mode 100644 packages/syncfusion_flutter_pdf/example/windows/.gitignore create mode 100644 packages/syncfusion_flutter_pdf/example/windows/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_pdf/example/windows/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_pdf/example/windows/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_pdf/example/windows/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_pdf/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_pdf/example/windows/runner/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_pdf/example/windows/runner/Runner.rc create mode 100644 packages/syncfusion_flutter_pdf/example/windows/runner/flutter_window.cpp create mode 100644 packages/syncfusion_flutter_pdf/example/windows/runner/flutter_window.h create mode 100644 packages/syncfusion_flutter_pdf/example/windows/runner/main.cpp create mode 100644 packages/syncfusion_flutter_pdf/example/windows/runner/resource.h create mode 100644 packages/syncfusion_flutter_pdf/example/windows/runner/resources/app_icon.ico create mode 100644 packages/syncfusion_flutter_pdf/example/windows/runner/run_loop.cpp create mode 100644 packages/syncfusion_flutter_pdf/example/windows/runner/run_loop.h create mode 100644 packages/syncfusion_flutter_pdf/example/windows/runner/runner.exe.manifest create mode 100644 packages/syncfusion_flutter_pdf/example/windows/runner/utils.cpp create mode 100644 packages/syncfusion_flutter_pdf/example/windows/runner/utils.h create mode 100644 packages/syncfusion_flutter_pdf/example/windows/runner/win32_window.cpp create mode 100644 packages/syncfusion_flutter_pdf/example/windows/runner/win32_window.h create mode 100644 packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/forms/pdf_xfdf_document.dart delete mode 100644 packages/syncfusion_flutter_pdf/test/pdf_test.dart create mode 100644 packages/syncfusion_flutter_pdfviewer/example/lib/generated_plugin_registrant.dart create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/.gitignore create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner/AppDelegate.swift create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner/Configs/Release.xcconfig create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner/DebugProfile.entitlements create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner/Info.plist create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner/MainFlutterWindow.swift create mode 100644 packages/syncfusion_flutter_pdfviewer/example/macos/Runner/Release.entitlements create mode 100644 packages/syncfusion_flutter_pdfviewer/lib/src/control/interactive_scrollable.dart delete mode 100644 packages/syncfusion_flutter_pdfviewer/lib/src/control/pdf_container.dart create mode 100644 packages/syncfusion_flutter_pdfviewer/lib/src/control/pdf_scrollable.dart create mode 100644 packages/syncfusion_flutter_pdfviewer/macos/syncfusion_flutter_pdfviewer.podspec create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/CHANGELOG.md create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/LICENSE create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/README.md rename packages/{syncfusion_flutter_pdfviewer/example => syncfusion_flutter_pdfviewer_macos}/analysis_options.yaml (50%) create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/README.md create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/lib/main.dart create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Podfile create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner/AppDelegate.swift create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner/Configs/Release.xcconfig create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner/DebugProfile.entitlements create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner/Info.plist create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner/MainFlutterWindow.swift create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/macos/Runner/Release.entitlements create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/example/pubspec.yaml create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/macos/Classes/SyncfusionFlutterPdfViewerPlugin.swift create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/macos/syncfusion_flutter_pdfviewer_macos.podspec create mode 100644 packages/syncfusion_flutter_pdfviewer_macos/pubspec.yaml create mode 100644 packages/syncfusion_flutter_pdfviewer_web/example/lib/generated_plugin_registrant.dart create mode 100644 packages/syncfusion_flutter_pdfviewer_web/example/web/index.html create mode 100644 packages/syncfusion_flutter_signaturepad/example/linux/.gitignore create mode 100644 packages/syncfusion_flutter_signaturepad/example/linux/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_signaturepad/example/linux/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_signaturepad/example/linux/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_signaturepad/example/linux/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_signaturepad/example/linux/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_signaturepad/example/linux/main.cc create mode 100644 packages/syncfusion_flutter_signaturepad/example/linux/my_application.cc create mode 100644 packages/syncfusion_flutter_signaturepad/example/linux/my_application.h create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/.gitignore create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Runner/AppDelegate.swift create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Runner/Configs/Release.xcconfig create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Runner/DebugProfile.entitlements create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Runner/Info.plist create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Runner/MainFlutterWindow.swift create mode 100644 packages/syncfusion_flutter_signaturepad/example/macos/Runner/Release.entitlements create mode 100644 packages/syncfusion_flutter_signaturepad/example/web/favicon.png create mode 100644 packages/syncfusion_flutter_signaturepad/example/web/icons/Icon-192.png create mode 100644 packages/syncfusion_flutter_signaturepad/example/web/icons/Icon-512.png create mode 100644 packages/syncfusion_flutter_signaturepad/example/web/index.html create mode 100644 packages/syncfusion_flutter_signaturepad/example/web/manifest.json create mode 100644 packages/syncfusion_flutter_signaturepad/example/windows/.gitignore create mode 100644 packages/syncfusion_flutter_signaturepad/example/windows/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_signaturepad/example/windows/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_signaturepad/example/windows/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_signaturepad/example/windows/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_signaturepad/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_signaturepad/example/windows/runner/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_signaturepad/example/windows/runner/Runner.rc create mode 100644 packages/syncfusion_flutter_signaturepad/example/windows/runner/flutter_window.cpp create mode 100644 packages/syncfusion_flutter_signaturepad/example/windows/runner/flutter_window.h create mode 100644 packages/syncfusion_flutter_signaturepad/example/windows/runner/main.cpp create mode 100644 packages/syncfusion_flutter_signaturepad/example/windows/runner/resource.h create mode 100644 packages/syncfusion_flutter_signaturepad/example/windows/runner/resources/app_icon.ico create mode 100644 packages/syncfusion_flutter_signaturepad/example/windows/runner/run_loop.cpp create mode 100644 packages/syncfusion_flutter_signaturepad/example/windows/runner/run_loop.h create mode 100644 packages/syncfusion_flutter_signaturepad/example/windows/runner/runner.exe.manifest create mode 100644 packages/syncfusion_flutter_signaturepad/example/windows/runner/utils.cpp create mode 100644 packages/syncfusion_flutter_signaturepad/example/windows/runner/utils.h create mode 100644 packages/syncfusion_flutter_signaturepad/example/windows/runner/win32_window.cpp create mode 100644 packages/syncfusion_flutter_signaturepad/example/windows/runner/win32_window.h create mode 100644 packages/syncfusion_flutter_sliders/example/linux/.gitignore create mode 100644 packages/syncfusion_flutter_sliders/example/linux/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_sliders/example/linux/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_sliders/example/linux/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_sliders/example/linux/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_sliders/example/linux/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_sliders/example/linux/main.cc create mode 100644 packages/syncfusion_flutter_sliders/example/linux/my_application.cc create mode 100644 packages/syncfusion_flutter_sliders/example/linux/my_application.h create mode 100644 packages/syncfusion_flutter_sliders/example/macos/.gitignore create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Runner/AppDelegate.swift create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Runner/Configs/Release.xcconfig create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Runner/DebugProfile.entitlements create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Runner/Info.plist create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Runner/MainFlutterWindow.swift create mode 100644 packages/syncfusion_flutter_sliders/example/macos/Runner/Release.entitlements create mode 100644 packages/syncfusion_flutter_sliders/example/web/favicon.png create mode 100644 packages/syncfusion_flutter_sliders/example/web/icons/Icon-192.png create mode 100644 packages/syncfusion_flutter_sliders/example/web/icons/Icon-512.png create mode 100644 packages/syncfusion_flutter_sliders/example/web/index.html create mode 100644 packages/syncfusion_flutter_sliders/example/web/manifest.json create mode 100644 packages/syncfusion_flutter_sliders/example/windows/.gitignore create mode 100644 packages/syncfusion_flutter_sliders/example/windows/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_sliders/example/windows/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_sliders/example/windows/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_sliders/example/windows/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_sliders/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_sliders/example/windows/runner/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_sliders/example/windows/runner/Runner.rc create mode 100644 packages/syncfusion_flutter_sliders/example/windows/runner/flutter_window.cpp create mode 100644 packages/syncfusion_flutter_sliders/example/windows/runner/flutter_window.h create mode 100644 packages/syncfusion_flutter_sliders/example/windows/runner/main.cpp create mode 100644 packages/syncfusion_flutter_sliders/example/windows/runner/resource.h create mode 100644 packages/syncfusion_flutter_sliders/example/windows/runner/resources/app_icon.ico create mode 100644 packages/syncfusion_flutter_sliders/example/windows/runner/run_loop.cpp create mode 100644 packages/syncfusion_flutter_sliders/example/windows/runner/run_loop.h create mode 100644 packages/syncfusion_flutter_sliders/example/windows/runner/runner.exe.manifest create mode 100644 packages/syncfusion_flutter_sliders/example/windows/runner/utils.cpp create mode 100644 packages/syncfusion_flutter_sliders/example/windows/runner/utils.h create mode 100644 packages/syncfusion_flutter_sliders/example/windows/runner/win32_window.cpp create mode 100644 packages/syncfusion_flutter_sliders/example/windows/runner/win32_window.h create mode 100644 packages/syncfusion_flutter_sliders/lib/src/range_slider_base.dart rename packages/syncfusion_flutter_sliders/lib/src/{render_slider_base.dart => slider_base.dart} (89%) create mode 100644 packages/syncfusion_flutter_treemap/example/linux/.gitignore create mode 100644 packages/syncfusion_flutter_treemap/example/linux/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_treemap/example/linux/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_treemap/example/linux/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_treemap/example/linux/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_treemap/example/linux/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_treemap/example/linux/main.cc create mode 100644 packages/syncfusion_flutter_treemap/example/linux/my_application.cc create mode 100644 packages/syncfusion_flutter_treemap/example/linux/my_application.h create mode 100644 packages/syncfusion_flutter_treemap/example/macos/.gitignore create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Runner/AppDelegate.swift create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Runner/Configs/Release.xcconfig create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Runner/DebugProfile.entitlements create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Runner/Info.plist create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Runner/MainFlutterWindow.swift create mode 100644 packages/syncfusion_flutter_treemap/example/macos/Runner/Release.entitlements create mode 100644 packages/syncfusion_flutter_treemap/example/web/favicon.png create mode 100644 packages/syncfusion_flutter_treemap/example/web/icons/Icon-192.png create mode 100644 packages/syncfusion_flutter_treemap/example/web/icons/Icon-512.png create mode 100644 packages/syncfusion_flutter_treemap/example/web/index.html create mode 100644 packages/syncfusion_flutter_treemap/example/web/manifest.json create mode 100644 packages/syncfusion_flutter_treemap/example/windows/.gitignore create mode 100644 packages/syncfusion_flutter_treemap/example/windows/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_treemap/example/windows/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_treemap/example/windows/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_treemap/example/windows/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_treemap/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_treemap/example/windows/runner/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_treemap/example/windows/runner/Runner.rc create mode 100644 packages/syncfusion_flutter_treemap/example/windows/runner/flutter_window.cpp create mode 100644 packages/syncfusion_flutter_treemap/example/windows/runner/flutter_window.h create mode 100644 packages/syncfusion_flutter_treemap/example/windows/runner/main.cpp create mode 100644 packages/syncfusion_flutter_treemap/example/windows/runner/resource.h create mode 100644 packages/syncfusion_flutter_treemap/example/windows/runner/resources/app_icon.ico create mode 100644 packages/syncfusion_flutter_treemap/example/windows/runner/run_loop.cpp create mode 100644 packages/syncfusion_flutter_treemap/example/windows/runner/run_loop.h create mode 100644 packages/syncfusion_flutter_treemap/example/windows/runner/runner.exe.manifest create mode 100644 packages/syncfusion_flutter_treemap/example/windows/runner/utils.cpp create mode 100644 packages/syncfusion_flutter_treemap/example/windows/runner/utils.h create mode 100644 packages/syncfusion_flutter_treemap/example/windows/runner/win32_window.cpp create mode 100644 packages/syncfusion_flutter_treemap/example/windows/runner/win32_window.h create mode 100644 packages/syncfusion_flutter_treemap/lib/src/animated_border.dart create mode 100644 packages/syncfusion_flutter_treemap/lib/src/controller.dart create mode 100644 packages/syncfusion_flutter_xlsio/example/lib/helper/save_file_mobile.dart create mode 100644 packages/syncfusion_flutter_xlsio/example/lib/helper/save_file_web.dart create mode 100644 packages/syncfusion_flutter_xlsio/example/linux/.gitignore create mode 100644 packages/syncfusion_flutter_xlsio/example/linux/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_xlsio/example/linux/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_xlsio/example/linux/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_xlsio/example/linux/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_xlsio/example/linux/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_xlsio/example/linux/main.cc create mode 100644 packages/syncfusion_flutter_xlsio/example/linux/my_application.cc create mode 100644 packages/syncfusion_flutter_xlsio/example/linux/my_application.h create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/.gitignore create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Runner/AppDelegate.swift create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Runner/Configs/Release.xcconfig create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Runner/DebugProfile.entitlements create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Runner/Info.plist create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Runner/MainFlutterWindow.swift create mode 100644 packages/syncfusion_flutter_xlsio/example/macos/Runner/Release.entitlements create mode 100644 packages/syncfusion_flutter_xlsio/example/web/favicon.png create mode 100644 packages/syncfusion_flutter_xlsio/example/web/icons/Icon-192.png create mode 100644 packages/syncfusion_flutter_xlsio/example/web/icons/Icon-512.png create mode 100644 packages/syncfusion_flutter_xlsio/example/web/index.html create mode 100644 packages/syncfusion_flutter_xlsio/example/web/manifest.json create mode 100644 packages/syncfusion_flutter_xlsio/example/windows/.gitignore create mode 100644 packages/syncfusion_flutter_xlsio/example/windows/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_xlsio/example/windows/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_xlsio/example/windows/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_flutter_xlsio/example/windows/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_flutter_xlsio/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_flutter_xlsio/example/windows/runner/CMakeLists.txt create mode 100644 packages/syncfusion_flutter_xlsio/example/windows/runner/Runner.rc create mode 100644 packages/syncfusion_flutter_xlsio/example/windows/runner/flutter_window.cpp create mode 100644 packages/syncfusion_flutter_xlsio/example/windows/runner/flutter_window.h create mode 100644 packages/syncfusion_flutter_xlsio/example/windows/runner/main.cpp create mode 100644 packages/syncfusion_flutter_xlsio/example/windows/runner/resource.h create mode 100644 packages/syncfusion_flutter_xlsio/example/windows/runner/resources/app_icon.ico create mode 100644 packages/syncfusion_flutter_xlsio/example/windows/runner/run_loop.cpp create mode 100644 packages/syncfusion_flutter_xlsio/example/windows/runner/run_loop.h create mode 100644 packages/syncfusion_flutter_xlsio/example/windows/runner/runner.exe.manifest create mode 100644 packages/syncfusion_flutter_xlsio/example/windows/runner/utils.cpp create mode 100644 packages/syncfusion_flutter_xlsio/example/windows/runner/utils.h create mode 100644 packages/syncfusion_flutter_xlsio/example/windows/runner/win32_window.cpp create mode 100644 packages/syncfusion_flutter_xlsio/example/windows/runner/win32_window.h create mode 100644 packages/syncfusion_flutter_xlsio/lib/src/xlsio/conditional_format/above_below_average/above_below_average.dart create mode 100644 packages/syncfusion_flutter_xlsio/lib/src/xlsio/conditional_format/above_below_average/above_below_average_impl.dart create mode 100644 packages/syncfusion_flutter_xlsio/lib/src/xlsio/conditional_format/above_below_average/above_below_average_wrapper.dart create mode 100644 packages/syncfusion_flutter_xlsio/lib/src/xlsio/conditional_format/top_bottom/top_bottom.dart create mode 100644 packages/syncfusion_flutter_xlsio/lib/src/xlsio/conditional_format/top_bottom/top_bottom_impl.dart create mode 100644 packages/syncfusion_flutter_xlsio/lib/src/xlsio/conditional_format/top_bottom/top_bottom_wrapper.dart create mode 100644 packages/syncfusion_flutter_xlsio/lib/src/xlsio/worksheet/excel_data_row.dart create mode 100644 packages/syncfusion_localizations/example/linux/.gitignore create mode 100644 packages/syncfusion_localizations/example/linux/CMakeLists.txt create mode 100644 packages/syncfusion_localizations/example/linux/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_localizations/example/linux/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_localizations/example/linux/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_localizations/example/linux/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_localizations/example/linux/main.cc create mode 100644 packages/syncfusion_localizations/example/linux/my_application.cc create mode 100644 packages/syncfusion_localizations/example/linux/my_application.h create mode 100644 packages/syncfusion_localizations/example/macos/.gitignore create mode 100644 packages/syncfusion_localizations/example/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 packages/syncfusion_localizations/example/macos/Flutter/Flutter-Release.xcconfig create mode 100644 packages/syncfusion_localizations/example/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 packages/syncfusion_localizations/example/macos/Runner.xcodeproj/project.pbxproj create mode 100644 packages/syncfusion_localizations/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_localizations/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 packages/syncfusion_localizations/example/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/syncfusion_localizations/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 packages/syncfusion_localizations/example/macos/Runner/AppDelegate.swift create mode 100644 packages/syncfusion_localizations/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 packages/syncfusion_localizations/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 packages/syncfusion_localizations/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 packages/syncfusion_localizations/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 packages/syncfusion_localizations/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 packages/syncfusion_localizations/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 packages/syncfusion_localizations/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 packages/syncfusion_localizations/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 packages/syncfusion_localizations/example/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 packages/syncfusion_localizations/example/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 packages/syncfusion_localizations/example/macos/Runner/Configs/Debug.xcconfig create mode 100644 packages/syncfusion_localizations/example/macos/Runner/Configs/Release.xcconfig create mode 100644 packages/syncfusion_localizations/example/macos/Runner/Configs/Warnings.xcconfig create mode 100644 packages/syncfusion_localizations/example/macos/Runner/DebugProfile.entitlements create mode 100644 packages/syncfusion_localizations/example/macos/Runner/Info.plist create mode 100644 packages/syncfusion_localizations/example/macos/Runner/MainFlutterWindow.swift create mode 100644 packages/syncfusion_localizations/example/macos/Runner/Release.entitlements create mode 100644 packages/syncfusion_localizations/example/web/favicon.png create mode 100644 packages/syncfusion_localizations/example/web/icons/Icon-192.png create mode 100644 packages/syncfusion_localizations/example/web/icons/Icon-512.png create mode 100644 packages/syncfusion_localizations/example/web/index.html create mode 100644 packages/syncfusion_localizations/example/web/manifest.json create mode 100644 packages/syncfusion_localizations/example/windows/.gitignore create mode 100644 packages/syncfusion_localizations/example/windows/CMakeLists.txt create mode 100644 packages/syncfusion_localizations/example/windows/flutter/CMakeLists.txt create mode 100644 packages/syncfusion_localizations/example/windows/flutter/generated_plugin_registrant.cc create mode 100644 packages/syncfusion_localizations/example/windows/flutter/generated_plugin_registrant.h create mode 100644 packages/syncfusion_localizations/example/windows/flutter/generated_plugins.cmake create mode 100644 packages/syncfusion_localizations/example/windows/runner/CMakeLists.txt create mode 100644 packages/syncfusion_localizations/example/windows/runner/Runner.rc create mode 100644 packages/syncfusion_localizations/example/windows/runner/flutter_window.cpp create mode 100644 packages/syncfusion_localizations/example/windows/runner/flutter_window.h create mode 100644 packages/syncfusion_localizations/example/windows/runner/main.cpp create mode 100644 packages/syncfusion_localizations/example/windows/runner/resource.h create mode 100644 packages/syncfusion_localizations/example/windows/runner/resources/app_icon.ico create mode 100644 packages/syncfusion_localizations/example/windows/runner/run_loop.cpp create mode 100644 packages/syncfusion_localizations/example/windows/runner/run_loop.h create mode 100644 packages/syncfusion_localizations/example/windows/runner/runner.exe.manifest create mode 100644 packages/syncfusion_localizations/example/windows/runner/utils.cpp create mode 100644 packages/syncfusion_localizations/example/windows/runner/utils.h create mode 100644 packages/syncfusion_localizations/example/windows/runner/win32_window.cpp create mode 100644 packages/syncfusion_localizations/example/windows/runner/win32_window.h diff --git a/packages/syncfusion_flutter_barcodes/analysis_options.yaml b/packages/syncfusion_flutter_barcodes/analysis_options.yaml index 7b491de76..472dba24e 100644 --- a/packages/syncfusion_flutter_barcodes/analysis_options.yaml +++ b/packages/syncfusion_flutter_barcodes/analysis_options.yaml @@ -5,3 +5,4 @@ analyzer: include_file_not_found: ignore lines_longer_than_80_chars: ignore avoid_as: false + use_raw_strings: ignore diff --git a/packages/syncfusion_flutter_barcodes/example/linux/.gitignore b/packages/syncfusion_flutter_barcodes/example/linux/.gitignore new file mode 100644 index 000000000..d3896c984 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/packages/syncfusion_flutter_barcodes/example/linux/CMakeLists.txt b/packages/syncfusion_flutter_barcodes/example/linux/CMakeLists.txt new file mode 100644 index 000000000..290c3e841 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/linux/CMakeLists.txt @@ -0,0 +1,106 @@ +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +set(BINARY_NAME "example") +set(APPLICATION_ID "com.example.example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Application build +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) +apply_standard_settings(${BINARY_NAME}) +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) +add_dependencies(${BINARY_NAME} flutter_assemble) +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/packages/syncfusion_flutter_barcodes/example/linux/flutter/CMakeLists.txt b/packages/syncfusion_flutter_barcodes/example/linux/flutter/CMakeLists.txt new file mode 100644 index 000000000..a1da1b9e5 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,91 @@ +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) +pkg_check_modules(BLKID REQUIRED IMPORTED_TARGET blkid) +pkg_check_modules(LZMA REQUIRED IMPORTED_TARGET liblzma) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO + PkgConfig::BLKID + PkgConfig::LZMA +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + linux-x64 ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/packages/syncfusion_flutter_barcodes/example/linux/flutter/generated_plugin_registrant.cc b/packages/syncfusion_flutter_barcodes/example/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000..d38195aa0 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,9 @@ +// +// Generated file. Do not edit. +// + +#include "generated_plugin_registrant.h" + + +void fl_register_plugins(FlPluginRegistry* registry) { +} diff --git a/packages/syncfusion_flutter_barcodes/example/linux/flutter/generated_plugin_registrant.h b/packages/syncfusion_flutter_barcodes/example/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000..9bf747894 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,13 @@ +// +// Generated file. Do not edit. +// + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/syncfusion_flutter_barcodes/example/linux/flutter/generated_plugins.cmake b/packages/syncfusion_flutter_barcodes/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 000000000..51436ae8c --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,15 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/packages/syncfusion_flutter_barcodes/example/linux/main.cc b/packages/syncfusion_flutter_barcodes/example/linux/main.cc new file mode 100644 index 000000000..e7c5c5437 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/packages/syncfusion_flutter_barcodes/example/linux/my_application.cc b/packages/syncfusion_flutter_barcodes/example/linux/my_application.cc new file mode 100644 index 000000000..543eaca72 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/linux/my_application.cc @@ -0,0 +1,104 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen *screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "example"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } + else { + gtk_window_set_title(window, "example"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar ***arguments, int *exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject *object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + nullptr)); +} diff --git a/packages/syncfusion_flutter_barcodes/example/linux/my_application.h b/packages/syncfusion_flutter_barcodes/example/linux/my_application.h new file mode 100644 index 000000000..72271d5e4 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/packages/syncfusion_flutter_barcodes/example/macos/.gitignore b/packages/syncfusion_flutter_barcodes/example/macos/.gitignore new file mode 100644 index 000000000..d2fd37723 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/macos/.gitignore @@ -0,0 +1,6 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/xcuserdata/ diff --git a/packages/syncfusion_flutter_barcodes/example/macos/Flutter/Flutter-Debug.xcconfig b/packages/syncfusion_flutter_barcodes/example/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 000000000..c2efd0b60 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/syncfusion_flutter_barcodes/example/macos/Flutter/Flutter-Release.xcconfig b/packages/syncfusion_flutter_barcodes/example/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 000000000..c2efd0b60 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/syncfusion_flutter_barcodes/example/macos/Flutter/GeneratedPluginRegistrant.swift b/packages/syncfusion_flutter_barcodes/example/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 000000000..cccf817a5 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,10 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { +} diff --git a/packages/syncfusion_flutter_barcodes/example/macos/Runner.xcodeproj/project.pbxproj b/packages/syncfusion_flutter_barcodes/example/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000..cc89c8782 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,572 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* example.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 0930; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + 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_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/packages/syncfusion_flutter_barcodes/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/syncfusion_flutter_barcodes/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/syncfusion_flutter_barcodes/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/syncfusion_flutter_barcodes/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000..ae8ff59d9 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/syncfusion_flutter_barcodes/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/syncfusion_flutter_barcodes/example/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..1d526a16e --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/syncfusion_flutter_barcodes/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/syncfusion_flutter_barcodes/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/syncfusion_flutter_barcodes/example/macos/Runner/AppDelegate.swift b/packages/syncfusion_flutter_barcodes/example/macos/Runner/AppDelegate.swift new file mode 100644 index 000000000..d53ef6437 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/packages/syncfusion_flutter_barcodes/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/syncfusion_flutter_barcodes/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..a2ec33f19 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/syncfusion_flutter_barcodes/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/packages/syncfusion_flutter_barcodes/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..3c4935a7ca84f0976aca34b7f2895d65fb94d1ea GIT binary patch literal 46993 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 literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_barcodes/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/packages/syncfusion_flutter_barcodes/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..ed4cc16421680a50164ba74381b4b35ceaa0ccfc GIT binary patch literal 3276 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 N;OXk;vd$@?2>?>Ex^Vyi literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_barcodes/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/packages/syncfusion_flutter_barcodes/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 0000000000000000000000000000000000000000..bcbf36df2f2aaaa0a63c7dabc94e600184229d0d GIT binary patch literal 5933 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 f$KZHO*s&vt|9^w-6P?|#0pRK8NSwWJ?9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!ItFh?!xdN1Q+aGJ{c&& zS>O>_%)r1c48n{Iv*t(u1=&kHeO=ifbFy+6aSK)V_AxLppYn8Z42d|rc6w}vOsL55 z`t&mC&y2@JTEyg!eDiFX^k#CC!jq%>erB=yHqUP0XcDOTw6ko}L zX;EmMrq(fKk*eygEuA616;0)>@A{TK|55PV@70 z$OfzS*(VJxQev3J?yY?O=ul(v`fp}?u9z`JK3ugibK>)DyCwImZOF4d{xK%%Ks1*} zv$oa)9anR%lXIBUqYnhLmT>VOzHfNP?ZwJNZ!5$s9M08RynIvaXw>@G^T9@r9^KH1 zVy??F&uuk)bH9Y4pQY!hP58i_H6 znl-NcuCpLV6ZWU;4C zu@9exF&OZi`Bovq_m%T+WhU2kvkz@^_LpycBvqm3bMpLw8X-Or5sL>0AKE1$(k_L=_Zc=CUq#=x1-QZf)G7nHu@fmsQ1eN_N3+nTEz`4HI4Z6uVlE zJH+X&det8JU?tO?upcM4Z=cV!JV;yF>FfL5Q$M|W_2Z!P`S=}Wzp|_1^#d%e?_H`> zV@%vA$+bFVqhw9`U;TfP|5|PD{||OiYdor8P*i??|NJcb%kzT_73*7WE?Ua5hAnR2 z=7WE=PhTlJ#ZeRznjTUb;`E(wkMZrj4e|Hilz-mK>9cZHQY**5TUPw~u}k;u73KI}xAx!0m-)GVia|x^d3p~s_9gh83jA&Ra<8rM%`>U3x69t&NzbwWY}7Ar?)FK#IZ0z|d0H0EkRO w3{9;}4Xg|ebq&m|3=9_N6z8I7$jwj5OsmAL;bP(Gi$Dzwp00i_>zopr02+f8CIA2c literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_barcodes/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/packages/syncfusion_flutter_barcodes/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 0000000000000000000000000000000000000000..e71a726136a47ed24125c7efc79d68a4a01961b4 GIT binary patch literal 14800 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/syncfusion_flutter_barcodes/example/macos/Runner/Configs/AppInfo.xcconfig b/packages/syncfusion_flutter_barcodes/example/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 000000000..cf9be60ca --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = example + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.example + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2021 com.example. All rights reserved. diff --git a/packages/syncfusion_flutter_barcodes/example/macos/Runner/Configs/Debug.xcconfig b/packages/syncfusion_flutter_barcodes/example/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 000000000..36b0fd946 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/packages/syncfusion_flutter_barcodes/example/macos/Runner/Configs/Release.xcconfig b/packages/syncfusion_flutter_barcodes/example/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 000000000..dff4f4956 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/packages/syncfusion_flutter_barcodes/example/macos/Runner/Configs/Warnings.xcconfig b/packages/syncfusion_flutter_barcodes/example/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 000000000..42bcbf478 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/packages/syncfusion_flutter_barcodes/example/macos/Runner/DebugProfile.entitlements b/packages/syncfusion_flutter_barcodes/example/macos/Runner/DebugProfile.entitlements new file mode 100644 index 000000000..dddb8a30c --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/packages/syncfusion_flutter_barcodes/example/macos/Runner/Info.plist b/packages/syncfusion_flutter_barcodes/example/macos/Runner/Info.plist new file mode 100644 index 000000000..4789daa6a --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/packages/syncfusion_flutter_barcodes/example/macos/Runner/MainFlutterWindow.swift b/packages/syncfusion_flutter_barcodes/example/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 000000000..2722837ec --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/packages/syncfusion_flutter_barcodes/example/macos/Runner/Release.entitlements b/packages/syncfusion_flutter_barcodes/example/macos/Runner/Release.entitlements new file mode 100644 index 000000000..852fa1a47 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/packages/syncfusion_flutter_barcodes/example/pubspec.yaml b/packages/syncfusion_flutter_barcodes/example/pubspec.yaml index 3dc82ab5b..f5914fb0f 100644 --- a/packages/syncfusion_flutter_barcodes/example/pubspec.yaml +++ b/packages/syncfusion_flutter_barcodes/example/pubspec.yaml @@ -11,7 +11,7 @@ description: A new Flutter project. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.0.0+1 +version: 1.0.0 environment: sdk: ">=2.12.0 <3.0.0" @@ -26,11 +26,6 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 -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 diff --git a/packages/syncfusion_flutter_barcodes/example/web/favicon.png b/packages/syncfusion_flutter_barcodes/example/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_barcodes/example/web/icons/Icon-192.png b/packages/syncfusion_flutter_barcodes/example/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_barcodes/example/web/icons/Icon-512.png b/packages/syncfusion_flutter_barcodes/example/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_barcodes/example/web/index.html b/packages/syncfusion_flutter_barcodes/example/web/index.html new file mode 100644 index 000000000..1460b5e9b --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/web/index.html @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + example + + + + + + + + diff --git a/packages/syncfusion_flutter_barcodes/example/web/manifest.json b/packages/syncfusion_flutter_barcodes/example/web/manifest.json new file mode 100644 index 000000000..8c012917d --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/web/manifest.json @@ -0,0 +1,23 @@ +{ + "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" + } + ] +} diff --git a/packages/syncfusion_flutter_barcodes/example/windows/.gitignore b/packages/syncfusion_flutter_barcodes/example/windows/.gitignore new file mode 100644 index 000000000..d492d0d98 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/packages/syncfusion_flutter_barcodes/example/windows/CMakeLists.txt b/packages/syncfusion_flutter_barcodes/example/windows/CMakeLists.txt new file mode 100644 index 000000000..abf90408e --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/windows/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required(VERSION 3.15) +project(example LANGUAGES CXX) + +set(BINARY_NAME "example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() + +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build +add_subdirectory("runner") + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/packages/syncfusion_flutter_barcodes/example/windows/flutter/CMakeLists.txt b/packages/syncfusion_flutter_barcodes/example/windows/flutter/CMakeLists.txt new file mode 100644 index 000000000..b02c5485c --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.15) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/packages/syncfusion_flutter_barcodes/example/windows/flutter/generated_plugin_registrant.cc b/packages/syncfusion_flutter_barcodes/example/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000..4bfa0f3a3 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,9 @@ +// +// Generated file. Do not edit. +// + +#include "generated_plugin_registrant.h" + + +void RegisterPlugins(flutter::PluginRegistry* registry) { +} diff --git a/packages/syncfusion_flutter_barcodes/example/windows/flutter/generated_plugin_registrant.h b/packages/syncfusion_flutter_barcodes/example/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000..9846246b4 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,13 @@ +// +// Generated file. Do not edit. +// + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/syncfusion_flutter_barcodes/example/windows/flutter/generated_plugins.cmake b/packages/syncfusion_flutter_barcodes/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 000000000..4d10c2518 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,15 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/packages/syncfusion_flutter_barcodes/example/windows/runner/CMakeLists.txt b/packages/syncfusion_flutter_barcodes/example/windows/runner/CMakeLists.txt new file mode 100644 index 000000000..977e38b5d --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/windows/runner/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.15) +project(runner LANGUAGES CXX) + +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "run_loop.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) +apply_standard_settings(${BINARY_NAME}) +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/packages/syncfusion_flutter_barcodes/example/windows/runner/Runner.rc b/packages/syncfusion_flutter_barcodes/example/windows/runner/Runner.rc new file mode 100644 index 000000000..51812dcd4 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "A new Flutter project." "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2021 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "example.exe" "\0" + VALUE "ProductName", "example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/packages/syncfusion_flutter_barcodes/example/windows/runner/flutter_window.cpp b/packages/syncfusion_flutter_barcodes/example/windows/runner/flutter_window.cpp new file mode 100644 index 000000000..c42272304 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/windows/runner/flutter_window.cpp @@ -0,0 +1,64 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(RunLoop* run_loop, + const flutter::DartProject& project) + : run_loop_(run_loop), project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + run_loop_->RegisterFlutterInstance(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + run_loop_->UnregisterFlutterInstance(flutter_controller_->engine()); + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opporutunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/packages/syncfusion_flutter_barcodes/example/windows/runner/flutter_window.h b/packages/syncfusion_flutter_barcodes/example/windows/runner/flutter_window.h new file mode 100644 index 000000000..b663ddd50 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/windows/runner/flutter_window.h @@ -0,0 +1,39 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "run_loop.h" +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow driven by the |run_loop|, hosting a + // Flutter view running |project|. + explicit FlutterWindow(RunLoop* run_loop, + const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The run loop driving events for this window. + RunLoop* run_loop_; + + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/packages/syncfusion_flutter_barcodes/example/windows/runner/main.cpp b/packages/syncfusion_flutter_barcodes/example/windows/runner/main.cpp new file mode 100644 index 000000000..b637809bd --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/windows/runner/main.cpp @@ -0,0 +1,42 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "run_loop.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + RunLoop run_loop; + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(&run_loop, project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + run_loop.Run(); + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/packages/syncfusion_flutter_barcodes/example/windows/runner/resource.h b/packages/syncfusion_flutter_barcodes/example/windows/runner/resource.h new file mode 100644 index 000000000..66a65d1e4 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/packages/syncfusion_flutter_barcodes/example/windows/runner/resources/app_icon.ico b/packages/syncfusion_flutter_barcodes/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 zcmeHQc|26z|35SKE&G-*mXah&B~fFkXr)DEO&hIfqby^T&>|8^_Ub8Vp#`BLl3lbZ zvPO!8k!2X>cg~Elr=IVxo~J*a`+9wR=A83c-k-DFd(XM&UI1VKCqM@V;DDtJ09WB} zRaHKiW(GT00brH|0EeTeKVbpbGZg?nK6-j827q-+NFM34gXjqWxJ*a#{b_apGN<-L_m3#8Z26atkEn& ze87Bvv^6vVmM+p+cQ~{u%=NJF>#(d;8{7Q{^rWKWNtf14H}>#&y7$lqmY6xmZryI& z($uy?c5-+cPnt2%)R&(KIWEXww>Cnz{OUpT>W$CbO$h1= z#4BPMkFG1Y)x}Ui+WXr?Z!w!t_hjRq8qTaWpu}FH{MsHlU{>;08goVLm{V<&`itk~ zE_Ys=D(hjiy+5=?=$HGii=Y5)jMe9|wWoD_K07(}edAxh`~LBorOJ!Cf@f{_gNCC| z%{*04ViE!#>@hc1t5bb+NO>ncf@@Dv01K!NxH$3Eg1%)|wLyMDF8^d44lV!_Sr}iEWefOaL z8f?ud3Q%Sen39u|%00W<#!E=-RpGa+H8}{ulxVl4mwpjaU+%2pzmi{3HM)%8vb*~-M9rPUAfGCSos8GUXp02|o~0BTV2l#`>>aFV&_P$ejS;nGwSVP8 zMbOaG7<7eKD>c12VdGH;?2@q7535sa7MN*L@&!m?L`ASG%boY7(&L5imY#EQ$KrBB z4@_tfP5m50(T--qv1BJcD&aiH#b-QC>8#7Fx@3yXlonJI#aEIi=8&ChiVpc#N=5le zM*?rDIdcpawoc5kizv$GEjnveyrp3sY>+5_R5;>`>erS%JolimF=A^EIsAK zsPoVyyUHCgf0aYr&alx`<)eb6Be$m&`JYSuBu=p8j%QlNNp$-5C{b4#RubPb|CAIS zGE=9OFLP7?Hgc{?k45)84biT0k&-C6C%Q}aI~q<(7BL`C#<6HyxaR%!dFx7*o^laG z=!GBF^cwK$IA(sn9y6>60Rw{mYRYkp%$jH z*xQM~+bp)G$_RhtFPYx2HTsWk80+p(uqv9@I9)y{b$7NK53rYL$ezbmRjdXS?V}fj zWxX_feWoLFNm3MG7pMUuFPs$qrQWO9!l2B(SIuy2}S|lHNbHzoE+M2|Zxhjq9+Ws8c{*}x^VAib7SbxJ*Q3EnY5lgI9 z=U^f3IW6T=TWaVj+2N%K3<%Un;CF(wUp`TC&Y|ZjyFu6co^uqDDB#EP?DV5v_dw~E zIRK*BoY9y-G_ToU2V_XCX4nJ32~`czdjT!zwme zGgJ0nOk3U4@IE5JwtM}pwimLjk{ln^*4HMU%Fl4~n(cnsLB}Ja-jUM>xIB%aY;Nq8 z)Fp8dv1tkqKanv<68o@cN|%thj$+f;zGSO7H#b+eMAV8xH$hLggtt?O?;oYEgbq@= zV(u9bbd12^%;?nyk6&$GPI%|+<_mEpJGNfl*`!KV;VfmZWw{n{rnZ51?}FDh8we_L z8OI9nE31skDqJ5Oa_ybn7|5@ui>aC`s34p4ZEu6-s!%{uU45$Zd1=p$^^dZBh zu<*pDDPLW+c>iWO$&Z_*{VSQKg7=YEpS3PssPn1U!lSm6eZIho*{@&20e4Y_lRklKDTUCKI%o4Pc<|G^Xgu$J^Q|B87U;`c1zGwf^-zH*VQ^x+i^OUWE0yd z;{FJq)2w!%`x7yg@>uGFFf-XJl4H`YtUG%0slGKOlXV`q?RP>AEWg#x!b{0RicxGhS!3$p7 zij;{gm!_u@D4$Ox%>>bPtLJ> zwKtYz?T_DR1jN>DkkfGU^<#6sGz|~p*I{y`aZ>^Di#TC|Z!7j_O1=Wo8thuit?WxR zh9_S>kw^{V^|g}HRUF=dcq>?q(pHxw!8rx4dC6vbQVmIhmICF#zU!HkHpQ>9S%Uo( zMw{eC+`&pb=GZRou|3;Po1}m46H6NGd$t<2mQh}kaK-WFfmj_66_17BX0|j-E2fe3Jat}ijpc53 zJV$$;PC<5aW`{*^Z6e5##^`Ed#a0nwJDT#Qq~^e8^JTA=z^Kl>La|(UQ!bI@#ge{Dzz@61p-I)kc2?ZxFt^QQ}f%ldLjO*GPj(5)V9IyuUakJX=~GnTgZ4$5!3E=V#t`yOG4U z(gphZB6u2zsj=qNFLYShhg$}lNpO`P9xOSnO*$@@UdMYES*{jJVj|9z-}F^riksLK zbsU+4-{281P9e2UjY6tse^&a)WM1MFw;p#_dHhWI7p&U*9TR0zKdVuQed%6{otTsq z$f~S!;wg#Bd9kez=Br{m|66Wv z#g1xMup<0)H;c2ZO6su_ii&m8j&+jJz4iKnGZ&wxoQX|5a>v&_e#6WA!MB_4asTxLRGQCC5cI(em z%$ZfeqP>!*q5kU>a+BO&ln=4Jm>Ef(QE8o&RgLkk%2}4Tf}U%IFP&uS7}&|Q-)`5< z+e>;s#4cJ-z%&-^&!xsYx777Wt(wZY9(3(avmr|gRe4cD+a8&!LY`1^T?7x{E<=kdY9NYw>A;FtTvQ=Y&1M%lyZPl$ss1oY^Sl8we}n}Aob#6 zl4jERwnt9BlSoWb@3HxYgga(752Vu6Y)k4yk9u~Kw>cA5&LHcrvn1Y-HoIuFWg~}4 zEw4bR`mXZQIyOAzo)FYqg?$5W<;^+XX%Uz61{-L6@eP|lLH%|w?g=rFc;OvEW;^qh z&iYXGhVt(G-q<+_j}CTbPS_=K>RKN0&;dubh0NxJyDOHFF;<1k!{k#7b{|Qok9hac z;gHz}6>H6C6RnB`Tt#oaSrX0p-j-oRJ;_WvS-qS--P*8}V943RT6kou-G=A+7QPGQ z!ze^UGxtW3FC0$|(lY9^L!Lx^?Q8cny(rR`es5U;-xBhphF%_WNu|aO<+e9%6LuZq zt(0PoagJG<%hyuf;te}n+qIl_Ej;czWdc{LX^pS>77s9t*2b4s5dvP_!L^3cwlc)E!(!kGrg~FescVT zZCLeua3f4;d;Tk4iXzt}g}O@nlK3?_o91_~@UMIl?@77Qc$IAlLE95#Z=TES>2E%z zxUKpK{_HvGF;5%Q7n&vA?`{%8ohlYT_?(3A$cZSi)MvIJygXD}TS-3UwyUxGLGiJP znblO~G|*uA^|ac8E-w#}uBtg|s_~s&t>-g0X%zIZ@;o_wNMr_;{KDg^O=rg`fhDZu zFp(VKd1Edj%F zWHPl+)FGj%J1BO3bOHVfH^3d1F{)*PL&sRX`~(-Zy3&9UQX)Z;c51tvaI2E*E7!)q zcz|{vpK7bjxix(k&6=OEIBJC!9lTkUbgg?4-yE{9+pFS)$Ar@vrIf`D0Bnsed(Cf? zObt2CJ>BKOl>q8PyFO6w)+6Iz`LW%T5^R`U_NIW0r1dWv6OY=TVF?N=EfA(k(~7VBW(S;Tu5m4Lg8emDG-(mOSSs=M9Q&N8jc^Y4&9RqIsk(yO_P(mcCr}rCs%1MW1VBrn=0-oQN(Xj!k%iKV zb%ricBF3G4S1;+8lzg5PbZ|$Se$)I=PwiK=cDpHYdov2QO1_a-*dL4KUi|g&oh>(* zq$<`dQ^fat`+VW?m)?_KLn&mp^-@d=&7yGDt<=XwZZC=1scwxO2^RRI7n@g-1o8ps z)&+et_~)vr8aIF1VY1Qrq~Xe``KJrQSnAZ{CSq3yP;V*JC;mmCT6oRLSs7=GA?@6g zUooM}@tKtx(^|aKK8vbaHlUQqwE0}>j&~YlN3H#vKGm@u)xxS?n9XrOWUfCRa< z`20Fld2f&;gg7zpo{Adh+mqNntMc-D$N^yWZAZRI+u1T1zWHPxk{+?vcS1D>08>@6 zLhE@`gt1Y9mAK6Z4p|u(5I%EkfU7rKFSM=E4?VG9tI;a*@?6!ey{lzN5=Y-!$WFSe z&2dtO>^0@V4WRc#L&P%R(?@KfSblMS+N+?xUN$u3K4Ys%OmEh+tq}fnU}i>6YHM?< zlnL2gl~sF!j!Y4E;j3eIU-lfa`RsOL*Tt<%EFC0gPzoHfNWAfKFIKZN8}w~(Yi~=q z>=VNLO2|CjkxP}RkutxjV#4fWYR1KNrPYq5ha9Wl+u>ipsk*I(HS@iLnmGH9MFlTU zaFZ*KSR0px>o+pL7BbhB2EC1%PJ{67_ z#kY&#O4@P=OV#-79y_W>Gv2dxL*@G7%LksNSqgId9v;2xJ zrh8uR!F-eU$NMx@S*+sk=C~Dxr9Qn7TfWnTupuHKuQ$;gGiBcU>GF5sWx(~4IP3`f zWE;YFO*?jGwYh%C3X<>RKHC-DZ!*r;cIr}GLOno^3U4tFSSoJp%oHPiSa%nh=Zgn% z14+8v@ygy0>UgEN1bczD6wK45%M>psM)y^)IfG*>3ItX|TzV*0i%@>L(VN!zdKb8S?Qf7BhjNpziA zR}?={-eu>9JDcl*R=OP9B8N$IcCETXah9SUDhr{yrld{G;PnCWRsPD7!eOOFBTWUQ=LrA_~)mFf&!zJX!Oc-_=kT<}m|K52 z)M=G#;p;Rdb@~h5D{q^K;^fX-m5V}L%!wVC2iZ1uu401Ll}#rocTeK|7FAeBRhNdQ zCc2d^aQnQp=MpOmak60N$OgS}a;p(l9CL`o4r(e-nN}mQ?M&isv-P&d$!8|1D1I(3-z!wi zTgoo)*Mv`gC?~bm?S|@}I|m-E2yqPEvYybiD5azInexpK8?9q*$9Yy9-t%5jU8~ym zgZDx>!@ujQ=|HJnwp^wv-FdD{RtzO9SnyfB{mH_(c!jHL*$>0o-(h(eqe*ZwF6Lvu z{7rkk%PEqaA>o+f{H02tzZ@TWy&su?VNw43! z-X+rN`6llvpUms3ZiSt)JMeztB~>9{J8SPmYs&qohxdYFi!ra8KR$35Zp9oR)eFC4 zE;P31#3V)n`w$fZ|4X-|%MX`xZDM~gJyl2W;O$H25*=+1S#%|53>|LyH za@yh+;325%Gq3;J&a)?%7X%t@WXcWL*BaaR*7UEZad4I8iDt7^R_Fd`XeUo256;sAo2F!HcIQKk;h})QxEsPE5BcKc7WyerTchgKmrfRX z!x#H_%cL#B9TWAqkA4I$R^8{%do3Y*&(;WFmJ zU7Dih{t1<{($VtJRl9|&EB?|cJ)xse!;}>6mSO$o5XIx@V|AA8ZcoD88ZM?C*;{|f zZVmf94_l1OmaICt`2sTyG!$^UeTHx9YuUP!omj(r|7zpm5475|yXI=rR>>fteLI+| z)MoiGho0oEt=*J(;?VY0QzwCqw@cVm?d7Y!z0A@u#H?sCJ*ecvyhj& z-F77lO;SH^dmf?L>3i>?Z*U}Em4ZYV_CjgfvzYsRZ+1B!Uo6H6mbS<-FFL`ytqvb& zE7+)2ahv-~dz(Hs+f})z{*4|{)b=2!RZK;PWwOnO=hG7xG`JU5>bAvUbdYd_CjvtHBHgtGdlO+s^9ca^Bv3`t@VRX2_AD$Ckg36OcQRF zXD6QtGfHdw*hx~V(MV-;;ZZF#dJ-piEF+s27z4X1qi5$!o~xBnvf=uopcn7ftfsZc zy@(PuOk`4GL_n(H9(E2)VUjqRCk9kR?w)v@xO6Jm_Mx})&WGEl=GS0#)0FAq^J*o! zAClhvoTsNP*-b~rN{8Yym3g{01}Ep^^Omf=SKqvN?{Q*C4HNNAcrowIa^mf+3PRy! z*_G-|3i8a;+q;iP@~Of_$(vtFkB8yOyWt2*K)vAn9El>=D;A$CEx6b*XF@4y_6M+2 zpeW`RHoI_p(B{%(&jTHI->hmNmZjHUj<@;7w0mx3&koy!2$@cfX{sN19Y}euYJFn& z1?)+?HCkD0MRI$~uB2UWri})0bru_B;klFdwsLc!ne4YUE;t41JqfG# zZJq6%vbsdx!wYeE<~?>o4V`A3?lN%MnKQ`z=uUivQN^vzJ|C;sdQ37Qn?;lpzg})y z)_2~rUdH}zNwX;Tp0tJ78+&I=IwOQ-fl30R79O8@?Ub8IIA(6I`yHn%lARVL`%b8+ z4$8D-|MZZWxc_)vu6@VZN!HsI$*2NOV&uMxBNzIbRgy%ob_ zhwEH{J9r$!dEix9XM7n&c{S(h>nGm?el;gaX0@|QnzFD@bne`el^CO$yXC?BDJ|Qg z+y$GRoR`?ST1z^e*>;!IS@5Ovb7*RlN>BV_UC!7E_F;N#ky%1J{+iixp(dUJj93aK zzHNN>R-oN7>kykHClPnoPTIj7zc6KM(Pnlb(|s??)SMb)4!sMHU^-ntJwY5Big7xv zb1Ew`Xj;|D2kzGja*C$eS44(d&RMU~c_Y14V9_TLTz0J#uHlsx`S6{nhsA0dWZ#cG zJ?`fO50E>*X4TQLv#nl%3GOk*UkAgt=IY+u0LNXqeln3Z zv$~&Li`ZJOKkFuS)dJRA>)b_Da%Q~axwA_8zNK{BH{#}#m}zGcuckz}riDE-z_Ms> zR8-EqAMcfyGJCtvTpaUVQtajhUS%c@Yj}&6Zz;-M7MZzqv3kA7{SuW$oW#=0az2wQ zg-WG@Vb4|D`pl~Il54N7Hmsauc_ne-a!o5#j3WaBBh@Wuefb!QJIOn5;d)%A#s+5% zuD$H=VNux9bE-}1&bcYGZ+>1Fo;3Z@e&zX^n!?JK*adSbONm$XW9z;Q^L>9U!}Toj2WdafJ%oL#h|yWWwyAGxzfrAWdDTtaKl zK4`5tDpPg5>z$MNv=X0LZ0d6l%D{(D8oT@+w0?ce$DZ6pv>{1&Ok67Ix1 zH}3=IEhPJEhItCC8E=`T`N5(k?G=B4+xzZ?<4!~ ze~z6Wk9!CHTI(0rLJ4{JU?E-puc;xusR?>G?;4vt;q~iI9=kDL=z0Rr%O$vU`30X$ zDZRFyZ`(omOy@u|i6h;wtJlP;+}$|Ak|k2dea7n?U1*$T!sXqqOjq^NxLPMmk~&qI zYg0W?yK8T(6+Ea+$YyspKK?kP$+B`~t3^Pib_`!6xCs32!i@pqXfFV6PmBIR<-QW= zN8L{pt0Vap0x`Gzn#E@zh@H)0FfVfA_Iu4fjYZ+umO1LXIbVc$pY+E234u)ttcrl$ z>s92z4vT%n6cMb>=XT6;l0+9e(|CZG)$@C7t7Z7Ez@a)h)!hyuV&B5K%%)P5?Lk|C zZZSVzdXp{@OXSP0hoU-gF8s8Um(#xzjP2Vem zec#-^JqTa&Y#QJ>-FBxd7tf`XB6e^JPUgagB8iBSEps;92KG`!#mvVcPQ5yNC-GEG zTiHEDYfH+0O15}r^+ z#jxj=@x8iNHWALe!P3R67TwmhItn**0JwnzSV2O&KE8KcT+0hWH^OPD1pwiuyx=b@ zNf5Jh0{9X)8;~Es)$t@%(3!OnbY+`@?i{mGX7Yy}8T_*0a6g;kaFPq;*=px5EhO{Cp%1kI<0?*|h8v!6WnO3cCJRF2-CRrU3JiLJnj@6;L)!0kWYAc_}F{2P))3HmCrz zQ&N&gE70;`!6*eJ4^1IR{f6j4(-l&X!tjHxkbHA^Zhrnhr9g{exN|xrS`5Pq=#Xf& zG%P=#ra-TyVFfgW%cZo5OSIwFL9WtXAlFOa+ubmI5t*3=g#Y zF%;70p5;{ZeFL}&}yOY1N1*Q;*<(kTB!7vM$QokF)yr2FlIU@$Ph58$Bz z0J?xQG=MlS4L6jA22eS42g|9*9pX@$#*sUeM(z+t?hr@r5J&D1rx}2pW&m*_`VDCW zUYY@v-;bAO0HqoAgbbiGGC<=ryf96}3pouhy3XJrX+!!u*O_>Si38V{uJmQ&USptX zKp#l(?>%^7;2%h(q@YWS#9;a!JhKlkR#Vd)ERILlgu!Hr@jA@V;sk4BJ-H#p*4EqC zDGjC*tl=@3Oi6)Bn^QwFpul18fpkbpg0+peH$xyPBqb%`$OUhPKyWb32o7clB*9Z< zN=i~NLjavrLtwgJ01bufP+>p-jR2I95|TpmKpQL2!oV>g(4RvS2pK4*ou%m(h6r3A zX#s&`9LU1ZG&;{CkOK!4fLDTnBys`M!vuz>Q&9OZ0hGQl!~!jSDg|~s*w52opC{sB ze|Cf2luD(*G13LcOAGA!s2FjSK8&IE5#W%J25w!vM0^VyQM!t)inj&RTiJ!wXzFgz z3^IqzB7I0L$llljsGq})thBy9UOyjtFO_*hYM_sgcMk>44jeH0V1FDyELc{S1F-;A zS;T^k^~4biG&V*Irq}O;e}j$$+E_#G?HKIn05iP3j|87TkGK~SqG!-KBg5+mN(aLm z8ybhIM`%C19UX$H$KY6JgXbY$0AT%rEpHC;u`rQ$Y=rxUdsc5*Kvc8jaYaO$^)cI6){P6K0r)I6DY4Wr4&B zLQUBraey#0HV|&c4v7PVo3n$zHj99(TZO^3?Ly%C4nYvJTL9eLBLHsM3WKKD>5!B` zQ=BsR3aR6PD(Fa>327E2HAu5TM~Wusc!)>~(gM)+3~m;92Jd;FnSib=M5d6;;5{%R zb4V7DEJ0V!CP-F*oU?gkc>ksUtAYP&V4ND5J>J2^jt*vcFflQWCrB&fLdT%O59PVJ zhid#toR=FNgD!q3&r8#wEBr`!wzvQu5zX?Q>nlSJ4i@WC*CN*-xU66F^V5crWevQ9gsq$I@z1o(a=k7LL~ z7m_~`o;_Ozha1$8Q}{WBehvAlO4EL60y5}8GDrZ< zXh&F}71JbW2A~8KfEWj&UWV#4+Z4p`b{uAj4&WC zha`}X@3~+Iz^WRlOHU&KngK>#j}+_o@LdBC1H-`gT+krWX3-;!)6?{FBp~%20a}FL zFP9%Emqcwa#(`=G>BBZ0qZDQhmZKJg_g8<=bBFKWr!dyg(YkpE+|R*SGpDVU!+VlU zFC54^DLv}`qa%49T>nNiA9Q7Ips#!Xx90tCU2gvK`(F+GPcL=J^>No{)~we#o@&mUb6c$ zCc*<|NJBk-#+{j9xkQ&ujB zI~`#kN~7W!f*-}wkG~Ld!JqZ@tK}eeSnsS5J1fMFXm|`LJx&}5`@dK3W^7#Wnm+_P zBZkp&j1fa2Y=eIjJ0}gh85jt43kaIXXv?xmo@eHrka!Z|vQv12HN#+!I5E z`(fbuW>gFiJL|uXJ!vKt#z3e3HlVdboH7;e#i3(2<)Fg-I@BR!qY#eof3MFZ&*Y@l zI|KJf&ge@p2Dq09Vu$$Qxb7!}{m-iRk@!)%KL)txi3;~Z4Pb}u@GsW;ELiWeG9V51 znX#}B&4Y2E7-H=OpNE@q{%hFLxwIpBF2t{vPREa8_{linXT;#1vMRWjOzLOP$-hf( z>=?$0;~~PnkqY;~K{EM6Vo-T(0K{A0}VUGmu*hR z{tw3hvBN%N3G3Yw`X5Te+F{J`(3w1s3-+1EbnFQKcrgrX1Jqvs@ADGe%M0s$EbK$$ zK)=y=upBc6SjGYAACCcI=Y*6Fi8_jgwZlLxD26fnQfJmb8^gHRN5(TemhX@0e=vr> zg`W}6U>x6VhoA3DqsGGD9uL1DhB3!OXO=k}59TqD@(0Nb{)Ut_luTioK_>7wjc!5C zIr@w}b`Fez3)0wQfKl&bae7;PcTA7%?f2xucM0G)wt_KO!Ewx>F~;=BI0j=Fb4>pp zv}0R^xM4eti~+^+gE$6b81p(kwzuDti(-K9bc|?+pJEl@H+jSYuxZQV8rl8 zjp@M{#%qItIUFN~KcO9Hed*`$5A-2~pAo~K&<-Q+`9`$CK>rzqAI4w~$F%vs9s{~x zg4BP%Gy*@m?;D6=SRX?888Q6peF@_4Z->8wAH~Cn!R$|Hhq2cIzFYqT_+cDourHbY z0qroxJnrZ4Gh+Ay+F`_c%+KRT>y3qw{)89?=hJ@=KO=@ep)aBJ$c!JHfBMJpsP*3G za7|)VJJ8B;4?n{~ldJF7%jmb`-ftIvNd~ekoufG(`K(3=LNc;HBY& z(lp#q8XAD#cIf}k49zX_i`*fO+#!zKA&%T3j@%)R+#yag067CU%yUEe47>wzGU8^` z1EXFT^@I!{J!F8!X?S6ph8J=gUi5tl93*W>7}_uR<2N2~e}FaG?}KPyugQ=-OGEZs z!GBoyYY+H*ANn4?Z)X4l+7H%`17i5~zRlRIX?t)6_eu=g2Q`3WBhxSUeea+M-S?RL zX9oBGKn%a!H+*hx4d2(I!gsi+@SQK%<{X22M~2tMulJoa)0*+z9=-YO+;DFEm5eE1U9b^B(Z}2^9!Qk`!A$wUE z7$Ar5?NRg2&G!AZqnmE64eh^Anss3i!{}%6@Et+4rr!=}!SBF8eZ2*J3ujCWbl;3; z48H~goPSv(8X61fKKdpP!Z7$88NL^Z?j`!^*I?-P4X^pMxyWz~@$(UeAcTSDd(`vO z{~rc;9|GfMJcApU3k}22a!&)k4{CU!e_ny^Y3cO;tOvOMKEyWz!vG(Kp*;hB?d|R3`2X~=5a6#^o5@qn?J-bI8Ppip{-yG z!k|VcGsq!jF~}7DMr49Wap-s&>o=U^T0!Lcy}!(bhtYsPQy z4|EJe{12QL#=c(suQ89Mhw9<`bui%nx7Nep`C&*M3~vMEACmcRYYRGtANq$F%zh&V zc)cEVeHz*Z1N)L7k-(k3np#{GcDh2Q@ya0YHl*n7fl*ZPAsbU-a94MYYtA#&!c`xGIaV;yzsmrjfieTEtqB_WgZp2*NplHx=$O{M~2#i_vJ{ps-NgK zQsxKK_CBM2PP_je+Xft`(vYfXXgIUr{=PA=7a8`2EHk)Ym2QKIforz# tySWtj{oF3N9@_;i*Fv5S)9x^z=nlWP>jpp-9)52ZmLVA=i*%6g{{fxOO~wEK literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_barcodes/example/windows/runner/run_loop.cpp b/packages/syncfusion_flutter_barcodes/example/windows/runner/run_loop.cpp new file mode 100644 index 000000000..2d6636ab6 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/windows/runner/run_loop.cpp @@ -0,0 +1,66 @@ +#include "run_loop.h" + +#include + +#include + +RunLoop::RunLoop() {} + +RunLoop::~RunLoop() {} + +void RunLoop::Run() { + bool keep_running = true; + TimePoint next_flutter_event_time = TimePoint::clock::now(); + while (keep_running) { + std::chrono::nanoseconds wait_duration = + std::max(std::chrono::nanoseconds(0), + next_flutter_event_time - TimePoint::clock::now()); + ::MsgWaitForMultipleObjects( + 0, nullptr, FALSE, static_cast(wait_duration.count() / 1000), + QS_ALLINPUT); + bool processed_events = false; + MSG message; + // All pending Windows messages must be processed; MsgWaitForMultipleObjects + // won't return again for items left in the queue after PeekMessage. + while (::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) { + processed_events = true; + if (message.message == WM_QUIT) { + keep_running = false; + break; + } + ::TranslateMessage(&message); + ::DispatchMessage(&message); + // Allow Flutter to process messages each time a Windows message is + // processed, to prevent starvation. + next_flutter_event_time = + std::min(next_flutter_event_time, ProcessFlutterMessages()); + } + // If the PeekMessage loop didn't run, process Flutter messages. + if (!processed_events) { + next_flutter_event_time = + std::min(next_flutter_event_time, ProcessFlutterMessages()); + } + } +} + +void RunLoop::RegisterFlutterInstance( + flutter::FlutterEngine* flutter_instance) { + flutter_instances_.insert(flutter_instance); +} + +void RunLoop::UnregisterFlutterInstance( + flutter::FlutterEngine* flutter_instance) { + flutter_instances_.erase(flutter_instance); +} + +RunLoop::TimePoint RunLoop::ProcessFlutterMessages() { + TimePoint next_event_time = TimePoint::max(); + for (auto instance : flutter_instances_) { + std::chrono::nanoseconds wait_duration = instance->ProcessMessages(); + if (wait_duration != std::chrono::nanoseconds::max()) { + next_event_time = + std::min(next_event_time, TimePoint::clock::now() + wait_duration); + } + } + return next_event_time; +} diff --git a/packages/syncfusion_flutter_barcodes/example/windows/runner/run_loop.h b/packages/syncfusion_flutter_barcodes/example/windows/runner/run_loop.h new file mode 100644 index 000000000..000d36246 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/windows/runner/run_loop.h @@ -0,0 +1,40 @@ +#ifndef RUNNER_RUN_LOOP_H_ +#define RUNNER_RUN_LOOP_H_ + +#include + +#include +#include + +// A runloop that will service events for Flutter instances as well +// as native messages. +class RunLoop { + public: + RunLoop(); + ~RunLoop(); + + // Prevent copying + RunLoop(RunLoop const&) = delete; + RunLoop& operator=(RunLoop const&) = delete; + + // Runs the run loop until the application quits. + void Run(); + + // Registers the given Flutter instance for event servicing. + void RegisterFlutterInstance( + flutter::FlutterEngine* flutter_instance); + + // Unregisters the given Flutter instance from event servicing. + void UnregisterFlutterInstance( + flutter::FlutterEngine* flutter_instance); + + private: + using TimePoint = std::chrono::steady_clock::time_point; + + // Processes all currently pending messages for registered Flutter instances. + TimePoint ProcessFlutterMessages(); + + std::set flutter_instances_; +}; + +#endif // RUNNER_RUN_LOOP_H_ diff --git a/packages/syncfusion_flutter_barcodes/example/windows/runner/runner.exe.manifest b/packages/syncfusion_flutter_barcodes/example/windows/runner/runner.exe.manifest new file mode 100644 index 000000000..c977c4a42 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/packages/syncfusion_flutter_barcodes/example/windows/runner/utils.cpp b/packages/syncfusion_flutter_barcodes/example/windows/runner/utils.cpp new file mode 100644 index 000000000..d19bdbbcc --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/windows/runner/utils.cpp @@ -0,0 +1,64 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/packages/syncfusion_flutter_barcodes/example/windows/runner/utils.h b/packages/syncfusion_flutter_barcodes/example/windows/runner/utils.h new file mode 100644 index 000000000..3879d5475 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/packages/syncfusion_flutter_barcodes/example/windows/runner/win32_window.cpp b/packages/syncfusion_flutter_barcodes/example/windows/runner/win32_window.cpp new file mode 100644 index 000000000..c10f08dc7 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/windows/runner/win32_window.cpp @@ -0,0 +1,245 @@ +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/packages/syncfusion_flutter_barcodes/example/windows/runner/win32_window.h b/packages/syncfusion_flutter_barcodes/example/windows/runner/win32_window.h new file mode 100644 index 000000000..17ba43112 --- /dev/null +++ b/packages/syncfusion_flutter_barcodes/example/windows/runner/win32_window.h @@ -0,0 +1,98 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/codabar_renderer.dart b/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/codabar_renderer.dart index 61a096406..3bfe95356 100644 --- a/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/codabar_renderer.dart +++ b/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/codabar_renderer.dart @@ -85,7 +85,7 @@ class CodabarRenderer extends SymbologyRenderer { final double barHeight = size.height; final int codeLength = codeValue!.length; for (int j = 0; j < codeLength; j++) { - final bool canDraw = codeValue[j] == '1' ? true : false; + final bool canDraw = codeValue[j] == '1'; // Draws the barcode when the corresponding bar value is one if (canDraw && diff --git a/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/code128_renderer.dart b/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/code128_renderer.dart index 760fdb384..670a31e65 100644 --- a/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/code128_renderer.dart +++ b/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/code128_renderer.dart @@ -77,14 +77,14 @@ class Code128Renderer extends SymbologyRenderer { code128ACharacterSets.add(']'); code128ACharacterSets.add('^'); code128ACharacterSets.add('_'); - code128ACharacterSets.add('\0'); + code128ACharacterSets.add('0'); code128ACharacterSets.add('\u0001'); code128ACharacterSets.add('\u0002'); code128ACharacterSets.add('\u0003'); code128ACharacterSets.add('\u0004'); code128ACharacterSets.add('\u0005'); code128ACharacterSets.add('\u0006'); - code128ACharacterSets.add('\a'); + code128ACharacterSets.add('a'); code128ACharacterSets.add('\b'); code128ACharacterSets.add('\t'); code128ACharacterSets.add('\n'); diff --git a/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/code39_renderer.dart b/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/code39_renderer.dart index c2156c9c4..dd4be8899 100644 --- a/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/code39_renderer.dart +++ b/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/code39_renderer.dart @@ -139,7 +139,7 @@ class Code39Renderer extends SymbologyRenderer { final int codeLength = codeValue.length; for (int j = 0; j < codeLength; j++) { // The current bar is drawn, if its value is divisible by 2 - final bool canDraw = j % 2 == 0 ? true : false; + final bool canDraw = j.isEven; final int currentValue = int.parse(codeValue[j]); if (canDraw && (left >= barCodeRect.left && @@ -180,7 +180,7 @@ class Code39Renderer extends SymbologyRenderer { /// Represents the encoded value for provided input List getEncodedValue(String providedValue) { // ignore: avoid_as - final Code39 code39Symbology = symbology as Code39; + final Code39 code39Symbology = symbology! as Code39; if (code39Symbology.enableCheckSum) { final String checkSum = _getCheckSum(providedValue, _character); providedValue += checkSum; diff --git a/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/code93_renderer.dart b/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/code93_renderer.dart index 23e6e0c3d..04ec4e241 100644 --- a/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/code93_renderer.dart +++ b/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/code93_renderer.dart @@ -167,7 +167,7 @@ class Code93Renderer extends SymbologyRenderer { final int codeLength = codeValue.length; for (int j = 0; j < codeLength; j++) { //Draws the barcode when the corresponding bar value is one - final bool canDraw = codeValue[j] == '1' ? true : false; + final bool canDraw = codeValue[j] == '1'; if (canDraw && (left >= barCodeRect.left && left + ratio < barCodeRect.right)) { final Rect individualBarRect = Rect.fromLTRB( diff --git a/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/ean13_renderer.dart b/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/ean13_renderer.dart index fc4237c51..0f0fede08 100644 --- a/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/ean13_renderer.dart +++ b/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/ean13_renderer.dart @@ -95,7 +95,7 @@ class EAN13Renderer extends SymbologyRenderer { final int codeLength = codeValue.length; for (int j = 0; j < codeLength; j++) { // Draw the barcode when the current code value is 1 - final bool canDraw = codeValue[j] == '1' ? true : false; + final bool canDraw = codeValue[j] == '1'; if (canDraw && (left >= barCodeRect.left && left + ratio < barCodeRect.right)) { final Rect individualBarRect = Rect.fromLTRB( diff --git a/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/ean8_renderer.dart b/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/ean8_renderer.dart index 690b89b96..b368c0cc5 100644 --- a/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/ean8_renderer.dart +++ b/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/ean8_renderer.dart @@ -82,7 +82,7 @@ class EAN8Renderer extends SymbologyRenderer { final int codeLength = codeValue.length; for (int j = 0; j < codeLength; j++) { // Draw the barcode when the current code value is 1 - final bool canDraw = codeValue[j] == '1' ? true : false; + final bool canDraw = codeValue[j] == '1'; if (canDraw && (left >= barCodeRect.left && left + ratio < barCodeRect.right)) { final Rect individualBarRect = Rect.fromLTRB( diff --git a/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/upca_renderer.dart b/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/upca_renderer.dart index 08b539682..d2d20e5dd 100644 --- a/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/upca_renderer.dart +++ b/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/upca_renderer.dart @@ -105,7 +105,7 @@ class UPCARenderer extends SymbologyRenderer { : size.height; final int codeLength = codeValue.length; for (int j = 0; j < codeLength; j++) { - final bool canDraw = codeValue[j] == '1' ? true : false; + final bool canDraw = codeValue[j] == '1'; if (canDraw && (left >= barCodeRect.left && left + ratio < barCodeRect.right)) { final Rect individualBarRect = Rect.fromLTRB( diff --git a/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/upce_renderer.dart b/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/upce_renderer.dart index d97a34479..bbf2aced2 100644 --- a/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/upce_renderer.dart +++ b/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/one_dimensional/upce_renderer.dart @@ -88,7 +88,7 @@ class UPCERenderer extends SymbologyRenderer { : size.height; final int codeLength = codeValue.length; for (int j = 0; j < codeLength; j++) { - final bool canDraw = codeValue[j] == '1' ? true : false; + final bool canDraw = codeValue[j] == '1'; if (canDraw && (left >= barCodeRect.left && left + ratio < barCodeRect.right)) { final Rect individualBarRect = Rect.fromLTRB( diff --git a/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/two_dimensional/datamatrix_renderer.dart b/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/two_dimensional/datamatrix_renderer.dart index ef8972be2..b551aaad7 100644 --- a/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/two_dimensional/datamatrix_renderer.dart +++ b/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/two_dimensional/datamatrix_renderer.dart @@ -13,7 +13,7 @@ class DataMatrixRenderer extends SymbologyRenderer { /// Creates the data matrix renderer DataMatrixRenderer({Symbology? symbology}) : super(symbology: symbology) { // ignore: avoid_as - _dataMatrixSymbology = symbology as DataMatrix; + _dataMatrixSymbology = symbology! as DataMatrix; _initialize(); } diff --git a/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/two_dimensional/qr_code_renderer.dart b/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/two_dimensional/qr_code_renderer.dart index 98df28c27..e12d65c50 100644 --- a/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/two_dimensional/qr_code_renderer.dart +++ b/packages/syncfusion_flutter_barcodes/lib/src/barcode_generator/renderers/two_dimensional/qr_code_renderer.dart @@ -14,7 +14,7 @@ class QRCodeRenderer extends SymbologyRenderer { /// Creates the qr code renderer QRCodeRenderer({Symbology? symbology}) : super(symbology: symbology) { // ignore: avoid_as - _qrCodeSymbology = symbology as QRCode; + _qrCodeSymbology = symbology! as QRCode; if (_qrCodeSymbology.codeVersion != null && _qrCodeSymbology.codeVersion != QRCodeVersion.auto) { _codeVersion = _qrCodeSymbology.codeVersion!; @@ -551,15 +551,11 @@ class QRCodeRenderer extends SymbologyRenderer { for (int i = 0; i < 6; i++) { for (int j = 2; j >= 0; j--) { _moduleValues[i][_noOfModules - 9 - j].isBlack = - versionInformation != null && versionInformation[count] == 1 - ? true - : false; + versionInformation != null && versionInformation[count] == 1; _moduleValues[i][_noOfModules - 9 - j].isFilled = true; _moduleValues[_noOfModules - 9 - j][i].isBlack = - versionInformation != null && versionInformation[count++] == 1 - ? true - : false; + versionInformation != null && versionInformation[count++] == 1; _moduleValues[_noOfModules - 9 - j][i].isFilled = true; } } @@ -1099,15 +1095,13 @@ class QRCodeRenderer extends SymbologyRenderer { int count = 0; for (int i = 0; i < 7; i++) { if (i == 6) { - _moduleValues[i + 1][8].isBlack = - formatInformation![count] == 1 ? true : false; + _moduleValues[i + 1][8].isBlack = formatInformation![count] == 1; } else { - _moduleValues[i][8].isBlack = - formatInformation![count] == 1 ? true : false; + _moduleValues[i][8].isBlack = formatInformation![count] == 1; } _moduleValues[8][_noOfModules - i - 1].isBlack = - formatInformation[count++] == 1 ? true : false; + formatInformation[count++] == 1; } count = 14; @@ -1115,21 +1109,18 @@ class QRCodeRenderer extends SymbologyRenderer { for (int i = 0; i < 7; i++) { //Draw format information from 0 to 6 if (i == 6) { - _moduleValues[8][i + 1].isBlack = - formatInformation![count] == 1 ? true : false; + _moduleValues[8][i + 1].isBlack = formatInformation![count] == 1; } else { - _moduleValues[8][i].isBlack = - formatInformation![count] == 1 ? true : false; + _moduleValues[8][i].isBlack = formatInformation![count] == 1; } _moduleValues[_noOfModules - i - 1][8].isBlack = - formatInformation[count--] == 1 ? true : false; + formatInformation[count--] == 1; } //Draw format information 7 - _moduleValues[8][8].isBlack = formatInformation![7] == 1 ? true : false; - _moduleValues[8][_noOfModules - 8].isBlack = - formatInformation[7] == 1 ? true : false; + _moduleValues[8][8].isBlack = formatInformation![7] == 1; + _moduleValues[8][_noOfModules - 8].isBlack = formatInformation[7] == 1; } /// Method used for data allocation and masking @@ -1588,11 +1579,11 @@ class QRCodeRenderer extends SymbologyRenderer { List numberInBool; numberInString = numberInString! + _encodedText[i]; - if (i % 2 == 0 && i + 1 != _encodedText.length) { + if (i.isEven && i + 1 != _encodedText.length) { number = 45 * _qrCodeValues.getAlphaNumericValues(_encodedText[i])!; } - if (i % 2 == 1 && i != 0) { + if (i.isOdd && i != 0) { number += _qrCodeValues.getAlphaNumericValues(_encodedText[i])!; numberInBool = _getIntToBoolArray(number, 11); number = 0; @@ -1751,7 +1742,7 @@ class QRCodeRenderer extends SymbologyRenderer { for (int k = 0; k < totalBlockSize; k++) { for (int j = 0; j < 8; j++) { if (i < ds1[k].length) { - _encodeDataCodeWords.add(ds1[k][i]![j] == '1' ? true : false); + _encodeDataCodeWords.add(ds1[k][i]![j] == '1'); } } } @@ -1844,8 +1835,7 @@ class QRCodeRenderer extends SymbologyRenderer { for (int k = 0; k < _blocks![0]; k++) { for (int j = 0; j < 8; j++) { if (i < polynomial[k].length) { - _encodeDataCodeWords - .add(polynomial[k][i][j] == '1' ? true : false); + _encodeDataCodeWords.add(polynomial[k][i][j] == '1'); } } } @@ -1855,8 +1845,7 @@ class QRCodeRenderer extends SymbologyRenderer { for (int k = 0; k < totalBlockSize; k++) { for (int j = 0; j < 8; j++) { if (i < polynomial[k].length) { - _encodeDataCodeWords - .add(polynomial[k][i][j] == '1' ? true : false); + _encodeDataCodeWords.add(polynomial[k][i][j] == '1'); } } } diff --git a/packages/syncfusion_flutter_barcodes/pubspec.yaml b/packages/syncfusion_flutter_barcodes/pubspec.yaml index fbafdb7c1..5732c64aa 100644 --- a/packages/syncfusion_flutter_barcodes/pubspec.yaml +++ b/packages/syncfusion_flutter_barcodes/pubspec.yaml @@ -1,6 +1,6 @@ name: syncfusion_flutter_barcodes description: Flutter Barcodes generator library is used to generate and display data in the machine-readable, industry-standard 1D and 2D barcodes. -version: 19.1.54 +version: 18.3.35 homepage: https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_flutter_barcodes environment: diff --git a/packages/syncfusion_flutter_calendar/CHANGELOG.md b/packages/syncfusion_flutter_calendar/CHANGELOG.md index 9cb8c53ee..733821cd8 100644 --- a/packages/syncfusion_flutter_calendar/CHANGELOG.md +++ b/packages/syncfusion_flutter_calendar/CHANGELOG.md @@ -1,3 +1,11 @@ +## [19.2.44] +**Features** +* Provided support to display week numbers of the year. +* Provided ID, recurrence ID, and appointment type support. +* Provided builder support for the resource header view. +* Now, the occurrence appointment will contain the occurrence date details in the tap callbacks. +* Now, appointments will be displayed based on the `appointmentDisplayMode` in month view when the month cell builder is used. + ## [19.1.54] - 03/30/2021 **Bug fixes** * Now, the localization is working properly for the spanned appointment count text in Flutter event calendar. diff --git a/packages/syncfusion_flutter_calendar/README.md b/packages/syncfusion_flutter_calendar/README.md index f92876c20..88b2acf3c 100644 --- a/packages/syncfusion_flutter_calendar/README.md +++ b/packages/syncfusion_flutter_calendar/README.md @@ -81,13 +81,17 @@ The Flutter Calendar widget has built-in configurable views such as day, week, w ![month_agenda_view](https://cdn.syncfusion.com/content/images/FTControl/Flutter/Month+agenda+view+2.png) +* **Week numbers** - Display the week numbers of the year in the month, week, and work week views of the Calendar. + +![week_numbers](https://cdn.syncfusion.com/content/images/FTControl/Calendar/calendar-weeknumber.png) + * **Quick view navigation** - Navigate among calendar views easily using the header date picker views button in the calendar header and clicking month cell and view headers. ![quick_view_navigation](https://cdn.syncfusion.com/content/images/FTControl/Calendar/flutter-calendar-quickview-navigation.png) -* **Builders** - Allows you to design and set your own custom view to the month cells, month header of schedule view, special time regions, and appointments view of the calendar. +* **Builders** - Allows you to design and set your own custom view to the month cells, month header of schedule view, resource header of timeline views, special time regions, and appointments view of the calendar. -![bulders_in_calendar](https://cdn.syncfusion.com/content/images/FTControl/Calendar/builders.png) +![builders_in_calendar](https://cdn.syncfusion.com/content/images/FTControl/Calendar/calendar-builders.png) * **Appearance customization or Theming** - Provide a uniform and consistent look to the Calendar’s appearance and format. Theming support to provide a consistent look to the calendar. @@ -114,19 +118,24 @@ The Flutter Calendar widget has built-in configurable views such as day, week, w ## Coming soon - Drag and drop -- Remainder +- Reminder ## Get the demo application Explore the full capabilities of our Flutter widgets on your device by installing our sample browser applications from the below app stores, and view samples code in GitHub.

- - + + + +

+

+ + +

- - +

## Other useful links @@ -237,18 +246,18 @@ You can also map custom appointment data to our calendar. Widget build(BuildContext context) { return Scaffold( body: SfCalendar( - view: CalendarView.month, - dataSource: MeetingDataSource(_getDataSource()), - monthViewSettings: MonthViewSettings( - appointmentDisplayMode: MonthAppointmentDisplayMode.appointment), - )); + view: CalendarView.month, + dataSource: MeetingDataSource(_getDataSource()), + monthViewSettings: MonthViewSettings( + appointmentDisplayMode: MonthAppointmentDisplayMode.appointment), + )); } List _getDataSource() { - meetings = []; + final List meetings = []; final DateTime today = DateTime.now(); final DateTime startTime = - DateTime(today.year, today.month, today.day, 9, 0, 0); + DateTime(today.year, today.month, today.day, 9, 0, 0); final DateTime endTime = startTime.add(const Duration(hours: 2)); meetings.add( Meeting('Conference', startTime, endTime, const Color(0xFF0F8644), false)); @@ -263,27 +272,27 @@ class MeetingDataSource extends CalendarDataSource { @override DateTime getStartTime(int index) { - return appointments[index].from; + return appointments![index].from; } @override DateTime getEndTime(int index) { - return appointments[index].to; + return appointments![index].to; } @override String getSubject(int index) { - return appointments[index].eventName; + return appointments![index].eventName; } @override Color getColor(int index) { - return appointments[index].background; + return appointments![index].background; } @override bool isAllDay(int index) { - return appointments[index].isAllDay; + return appointments![index].isAllDay; } } diff --git a/packages/syncfusion_flutter_calendar/example/android/.gitignore b/packages/syncfusion_flutter_calendar/example/android/.gitignore new file mode 100644 index 000000000..0a741cb43 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/android/.gitignore @@ -0,0 +1,11 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties diff --git a/packages/syncfusion_flutter_calendar/example/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/syncfusion_flutter_calendar/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 000000000..f74085f3f --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/packages/syncfusion_flutter_calendar/example/android/app/src/main/res/values-night/styles.xml b/packages/syncfusion_flutter_calendar/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 000000000..449a9f930 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/packages/syncfusion_flutter_calendar/example/android/example_android.iml b/packages/syncfusion_flutter_calendar/example/android/example_android.iml new file mode 100644 index 000000000..18999696a --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/android/example_android.iml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/syncfusion_flutter_calendar/example/example.iml b/packages/syncfusion_flutter_calendar/example/example.iml new file mode 100644 index 000000000..e5c837191 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/example.iml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/syncfusion_flutter_calendar/example/ios/.gitignore b/packages/syncfusion_flutter_calendar/example/ios/.gitignore new file mode 100644 index 000000000..e96ef602b --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/ios/.gitignore @@ -0,0 +1,32 @@ +*.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 diff --git a/packages/syncfusion_flutter_calendar/example/lib/main.dart b/packages/syncfusion_flutter_calendar/example/lib/main.dart index 995905492..ebc62d744 100644 --- a/packages/syncfusion_flutter_calendar/example/lib/main.dart +++ b/packages/syncfusion_flutter_calendar/example/lib/main.dart @@ -9,22 +9,20 @@ void main() { class CalendarApp extends StatelessWidget { @override Widget build(BuildContext context) { - return MaterialApp(title: 'Calendar Demo', home: MyHomePage()); + return const MaterialApp(title: 'Calendar Demo', home: MyHomePage()); } } /// The hove page which hosts the calendar class MyHomePage extends StatefulWidget { /// Creates the home page to display teh calendar widget. - const MyHomePage({Key key}) : super(key: key); + const MyHomePage({Key? key}) : super(key: key); @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { - List meetings; - @override Widget build(BuildContext context) { return Scaffold( @@ -34,13 +32,13 @@ class _MyHomePageState extends State { // by default the month appointment display mode set as Indicator, we can // change the display mode as appointment using the appointment display // mode property - monthViewSettings: MonthViewSettings( + monthViewSettings: const MonthViewSettings( appointmentDisplayMode: MonthAppointmentDisplayMode.appointment), )); } List _getDataSource() { - meetings = []; + final List meetings = []; final DateTime today = DateTime.now(); final DateTime startTime = DateTime(today.year, today.month, today.day, 9, 0, 0); @@ -63,27 +61,37 @@ class MeetingDataSource extends CalendarDataSource { @override DateTime getStartTime(int index) { - return appointments[index].from; + return _getMeetingData(index).from; } @override DateTime getEndTime(int index) { - return appointments[index].to; + return _getMeetingData(index).to; } @override String getSubject(int index) { - return appointments[index].eventName; + return _getMeetingData(index).eventName; } @override Color getColor(int index) { - return appointments[index].background; + return _getMeetingData(index).background; } @override bool isAllDay(int index) { - return appointments[index].isAllDay; + return _getMeetingData(index).isAllDay; + } + + Meeting _getMeetingData(int index) { + final dynamic meeting = appointments![index]; + late final Meeting meetingData; + if (meeting is Meeting) { + meetingData = meeting; + } + + return meetingData; } } diff --git a/packages/syncfusion_flutter_calendar/example/linux/.gitignore b/packages/syncfusion_flutter_calendar/example/linux/.gitignore new file mode 100644 index 000000000..d3896c984 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/packages/syncfusion_flutter_calendar/example/linux/CMakeLists.txt b/packages/syncfusion_flutter_calendar/example/linux/CMakeLists.txt new file mode 100644 index 000000000..290c3e841 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/linux/CMakeLists.txt @@ -0,0 +1,106 @@ +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +set(BINARY_NAME "example") +set(APPLICATION_ID "com.example.example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Application build +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) +apply_standard_settings(${BINARY_NAME}) +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) +add_dependencies(${BINARY_NAME} flutter_assemble) +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/packages/syncfusion_flutter_calendar/example/linux/flutter/CMakeLists.txt b/packages/syncfusion_flutter_calendar/example/linux/flutter/CMakeLists.txt new file mode 100644 index 000000000..a1da1b9e5 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,91 @@ +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) +pkg_check_modules(BLKID REQUIRED IMPORTED_TARGET blkid) +pkg_check_modules(LZMA REQUIRED IMPORTED_TARGET liblzma) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO + PkgConfig::BLKID + PkgConfig::LZMA +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + linux-x64 ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/packages/syncfusion_flutter_calendar/example/linux/flutter/generated_plugin_registrant.cc b/packages/syncfusion_flutter_calendar/example/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000..d38195aa0 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,9 @@ +// +// Generated file. Do not edit. +// + +#include "generated_plugin_registrant.h" + + +void fl_register_plugins(FlPluginRegistry* registry) { +} diff --git a/packages/syncfusion_flutter_calendar/example/linux/flutter/generated_plugin_registrant.h b/packages/syncfusion_flutter_calendar/example/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000..9bf747894 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,13 @@ +// +// Generated file. Do not edit. +// + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/syncfusion_flutter_calendar/example/linux/flutter/generated_plugins.cmake b/packages/syncfusion_flutter_calendar/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 000000000..51436ae8c --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,15 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/packages/syncfusion_flutter_calendar/example/linux/main.cc b/packages/syncfusion_flutter_calendar/example/linux/main.cc new file mode 100644 index 000000000..e7c5c5437 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/packages/syncfusion_flutter_calendar/example/linux/my_application.cc b/packages/syncfusion_flutter_calendar/example/linux/my_application.cc new file mode 100644 index 000000000..543eaca72 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/linux/my_application.cc @@ -0,0 +1,104 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen *screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "example"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } + else { + gtk_window_set_title(window, "example"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar ***arguments, int *exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject *object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + nullptr)); +} diff --git a/packages/syncfusion_flutter_calendar/example/linux/my_application.h b/packages/syncfusion_flutter_calendar/example/linux/my_application.h new file mode 100644 index 000000000..72271d5e4 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/packages/syncfusion_flutter_calendar/example/macos/.gitignore b/packages/syncfusion_flutter_calendar/example/macos/.gitignore new file mode 100644 index 000000000..d2fd37723 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/macos/.gitignore @@ -0,0 +1,6 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/xcuserdata/ diff --git a/packages/syncfusion_flutter_calendar/example/macos/Flutter/Flutter-Debug.xcconfig b/packages/syncfusion_flutter_calendar/example/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 000000000..c2efd0b60 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/syncfusion_flutter_calendar/example/macos/Flutter/Flutter-Release.xcconfig b/packages/syncfusion_flutter_calendar/example/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 000000000..c2efd0b60 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/syncfusion_flutter_calendar/example/macos/Flutter/GeneratedPluginRegistrant.swift b/packages/syncfusion_flutter_calendar/example/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 000000000..cccf817a5 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,10 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { +} diff --git a/packages/syncfusion_flutter_calendar/example/macos/Runner.xcodeproj/project.pbxproj b/packages/syncfusion_flutter_calendar/example/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000..cc89c8782 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,572 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* example.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 0930; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + 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_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/packages/syncfusion_flutter_calendar/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/syncfusion_flutter_calendar/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/syncfusion_flutter_calendar/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/syncfusion_flutter_calendar/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000..ae8ff59d9 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/syncfusion_flutter_calendar/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/syncfusion_flutter_calendar/example/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..1d526a16e --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/syncfusion_flutter_calendar/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/syncfusion_flutter_calendar/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/syncfusion_flutter_calendar/example/macos/Runner/AppDelegate.swift b/packages/syncfusion_flutter_calendar/example/macos/Runner/AppDelegate.swift new file mode 100644 index 000000000..d53ef6437 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/packages/syncfusion_flutter_calendar/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/syncfusion_flutter_calendar/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..a2ec33f19 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/syncfusion_flutter_calendar/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/packages/syncfusion_flutter_calendar/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..3c4935a7ca84f0976aca34b7f2895d65fb94d1ea GIT binary patch literal 46993 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 literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_calendar/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/packages/syncfusion_flutter_calendar/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..ed4cc16421680a50164ba74381b4b35ceaa0ccfc GIT binary patch literal 3276 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 N;OXk;vd$@?2>?>Ex^Vyi literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_calendar/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/packages/syncfusion_flutter_calendar/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 0000000000000000000000000000000000000000..bcbf36df2f2aaaa0a63c7dabc94e600184229d0d GIT binary patch literal 5933 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 f$KZHO*s&vt|9^w-6P?|#0pRK8NSwWJ?9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!ItFh?!xdN1Q+aGJ{c&& zS>O>_%)r1c48n{Iv*t(u1=&kHeO=ifbFy+6aSK)V_AxLppYn8Z42d|rc6w}vOsL55 z`t&mC&y2@JTEyg!eDiFX^k#CC!jq%>erB=yHqUP0XcDOTw6ko}L zX;EmMrq(fKk*eygEuA616;0)>@A{TK|55PV@70 z$OfzS*(VJxQev3J?yY?O=ul(v`fp}?u9z`JK3ugibK>)DyCwImZOF4d{xK%%Ks1*} zv$oa)9anR%lXIBUqYnhLmT>VOzHfNP?ZwJNZ!5$s9M08RynIvaXw>@G^T9@r9^KH1 zVy??F&uuk)bH9Y4pQY!hP58i_H6 znl-NcuCpLV6ZWU;4C zu@9exF&OZi`Bovq_m%T+WhU2kvkz@^_LpycBvqm3bMpLw8X-Or5sL>0AKE1$(k_L=_Zc=CUq#=x1-QZf)G7nHu@fmsQ1eN_N3+nTEz`4HI4Z6uVlE zJH+X&det8JU?tO?upcM4Z=cV!JV;yF>FfL5Q$M|W_2Z!P`S=}Wzp|_1^#d%e?_H`> zV@%vA$+bFVqhw9`U;TfP|5|PD{||OiYdor8P*i??|NJcb%kzT_73*7WE?Ua5hAnR2 z=7WE=PhTlJ#ZeRznjTUb;`E(wkMZrj4e|Hilz-mK>9cZHQY**5TUPw~u}k;u73KI}xAx!0m-)GVia|x^d3p~s_9gh83jA&Ra<8rM%`>U3x69t&NzbwWY}7Ar?)FK#IZ0z|d0H0EkRO w3{9;}4Xg|ebq&m|3=9_N6z8I7$jwj5OsmAL;bP(Gi$Dzwp00i_>zopr02+f8CIA2c literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_calendar/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/packages/syncfusion_flutter_calendar/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 0000000000000000000000000000000000000000..e71a726136a47ed24125c7efc79d68a4a01961b4 GIT binary patch literal 14800 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/syncfusion_flutter_calendar/example/macos/Runner/Configs/AppInfo.xcconfig b/packages/syncfusion_flutter_calendar/example/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 000000000..cf9be60ca --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = example + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.example + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2021 com.example. All rights reserved. diff --git a/packages/syncfusion_flutter_calendar/example/macos/Runner/Configs/Debug.xcconfig b/packages/syncfusion_flutter_calendar/example/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 000000000..36b0fd946 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/packages/syncfusion_flutter_calendar/example/macos/Runner/Configs/Release.xcconfig b/packages/syncfusion_flutter_calendar/example/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 000000000..dff4f4956 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/packages/syncfusion_flutter_calendar/example/macos/Runner/Configs/Warnings.xcconfig b/packages/syncfusion_flutter_calendar/example/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 000000000..42bcbf478 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/packages/syncfusion_flutter_calendar/example/macos/Runner/DebugProfile.entitlements b/packages/syncfusion_flutter_calendar/example/macos/Runner/DebugProfile.entitlements new file mode 100644 index 000000000..dddb8a30c --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/packages/syncfusion_flutter_calendar/example/macos/Runner/Info.plist b/packages/syncfusion_flutter_calendar/example/macos/Runner/Info.plist new file mode 100644 index 000000000..4789daa6a --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/packages/syncfusion_flutter_calendar/example/macos/Runner/MainFlutterWindow.swift b/packages/syncfusion_flutter_calendar/example/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 000000000..2722837ec --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/packages/syncfusion_flutter_calendar/example/macos/Runner/Release.entitlements b/packages/syncfusion_flutter_calendar/example/macos/Runner/Release.entitlements new file mode 100644 index 000000000..852fa1a47 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/packages/syncfusion_flutter_calendar/example/pubspec.yaml b/packages/syncfusion_flutter_calendar/example/pubspec.yaml index cc3132b1f..8c6a7935b 100644 --- a/packages/syncfusion_flutter_calendar/example/pubspec.yaml +++ b/packages/syncfusion_flutter_calendar/example/pubspec.yaml @@ -3,20 +3,16 @@ description: Syncfusion calendar example. version: 1.0.0+1 environment: - sdk: ">=2.7.0 <3.0.0" + sdk: ">=2.12.0 <3.0.0" dependencies: flutter: sdk: flutter - cupertino_icons: ^0.1.3 + cupertino_icons: ^1.0.2 syncfusion_flutter_calendar: path: ../../syncfusion_flutter_calendar -dev_dependencies: - flutter_test: - sdk: flutter - flutter: uses-material-design: true \ No newline at end of file diff --git a/packages/syncfusion_flutter_calendar/example/web/favicon.png b/packages/syncfusion_flutter_calendar/example/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_calendar/example/web/icons/Icon-192.png b/packages/syncfusion_flutter_calendar/example/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_calendar/example/web/icons/Icon-512.png b/packages/syncfusion_flutter_calendar/example/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_calendar/example/web/index.html b/packages/syncfusion_flutter_calendar/example/web/index.html new file mode 100644 index 000000000..1460b5e9b --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/web/index.html @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + example + + + + + + + + diff --git a/packages/syncfusion_flutter_calendar/example/web/manifest.json b/packages/syncfusion_flutter_calendar/example/web/manifest.json new file mode 100644 index 000000000..8c012917d --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/web/manifest.json @@ -0,0 +1,23 @@ +{ + "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" + } + ] +} diff --git a/packages/syncfusion_flutter_calendar/example/windows/.gitignore b/packages/syncfusion_flutter_calendar/example/windows/.gitignore new file mode 100644 index 000000000..d492d0d98 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/packages/syncfusion_flutter_calendar/example/windows/CMakeLists.txt b/packages/syncfusion_flutter_calendar/example/windows/CMakeLists.txt new file mode 100644 index 000000000..abf90408e --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/windows/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required(VERSION 3.15) +project(example LANGUAGES CXX) + +set(BINARY_NAME "example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() + +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build +add_subdirectory("runner") + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/packages/syncfusion_flutter_calendar/example/windows/flutter/CMakeLists.txt b/packages/syncfusion_flutter_calendar/example/windows/flutter/CMakeLists.txt new file mode 100644 index 000000000..b02c5485c --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.15) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/packages/syncfusion_flutter_calendar/example/windows/flutter/generated_plugin_registrant.cc b/packages/syncfusion_flutter_calendar/example/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000..4bfa0f3a3 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,9 @@ +// +// Generated file. Do not edit. +// + +#include "generated_plugin_registrant.h" + + +void RegisterPlugins(flutter::PluginRegistry* registry) { +} diff --git a/packages/syncfusion_flutter_calendar/example/windows/flutter/generated_plugin_registrant.h b/packages/syncfusion_flutter_calendar/example/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000..9846246b4 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,13 @@ +// +// Generated file. Do not edit. +// + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/syncfusion_flutter_calendar/example/windows/flutter/generated_plugins.cmake b/packages/syncfusion_flutter_calendar/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 000000000..4d10c2518 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,15 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/packages/syncfusion_flutter_calendar/example/windows/runner/CMakeLists.txt b/packages/syncfusion_flutter_calendar/example/windows/runner/CMakeLists.txt new file mode 100644 index 000000000..977e38b5d --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/windows/runner/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.15) +project(runner LANGUAGES CXX) + +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "run_loop.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) +apply_standard_settings(${BINARY_NAME}) +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/packages/syncfusion_flutter_calendar/example/windows/runner/Runner.rc b/packages/syncfusion_flutter_calendar/example/windows/runner/Runner.rc new file mode 100644 index 000000000..51812dcd4 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "A new Flutter project." "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2021 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "example.exe" "\0" + VALUE "ProductName", "example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/packages/syncfusion_flutter_calendar/example/windows/runner/flutter_window.cpp b/packages/syncfusion_flutter_calendar/example/windows/runner/flutter_window.cpp new file mode 100644 index 000000000..c42272304 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/windows/runner/flutter_window.cpp @@ -0,0 +1,64 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(RunLoop* run_loop, + const flutter::DartProject& project) + : run_loop_(run_loop), project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + run_loop_->RegisterFlutterInstance(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + run_loop_->UnregisterFlutterInstance(flutter_controller_->engine()); + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opporutunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/packages/syncfusion_flutter_calendar/example/windows/runner/flutter_window.h b/packages/syncfusion_flutter_calendar/example/windows/runner/flutter_window.h new file mode 100644 index 000000000..b663ddd50 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/windows/runner/flutter_window.h @@ -0,0 +1,39 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "run_loop.h" +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow driven by the |run_loop|, hosting a + // Flutter view running |project|. + explicit FlutterWindow(RunLoop* run_loop, + const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The run loop driving events for this window. + RunLoop* run_loop_; + + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/packages/syncfusion_flutter_calendar/example/windows/runner/main.cpp b/packages/syncfusion_flutter_calendar/example/windows/runner/main.cpp new file mode 100644 index 000000000..b637809bd --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/windows/runner/main.cpp @@ -0,0 +1,42 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "run_loop.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + RunLoop run_loop; + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(&run_loop, project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + run_loop.Run(); + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/packages/syncfusion_flutter_calendar/example/windows/runner/resource.h b/packages/syncfusion_flutter_calendar/example/windows/runner/resource.h new file mode 100644 index 000000000..66a65d1e4 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/packages/syncfusion_flutter_calendar/example/windows/runner/resources/app_icon.ico b/packages/syncfusion_flutter_calendar/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_calendar/example/windows/runner/run_loop.cpp b/packages/syncfusion_flutter_calendar/example/windows/runner/run_loop.cpp new file mode 100644 index 000000000..2d6636ab6 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/windows/runner/run_loop.cpp @@ -0,0 +1,66 @@ +#include "run_loop.h" + +#include + +#include + +RunLoop::RunLoop() {} + +RunLoop::~RunLoop() {} + +void RunLoop::Run() { + bool keep_running = true; + TimePoint next_flutter_event_time = TimePoint::clock::now(); + while (keep_running) { + std::chrono::nanoseconds wait_duration = + std::max(std::chrono::nanoseconds(0), + next_flutter_event_time - TimePoint::clock::now()); + ::MsgWaitForMultipleObjects( + 0, nullptr, FALSE, static_cast(wait_duration.count() / 1000), + QS_ALLINPUT); + bool processed_events = false; + MSG message; + // All pending Windows messages must be processed; MsgWaitForMultipleObjects + // won't return again for items left in the queue after PeekMessage. + while (::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) { + processed_events = true; + if (message.message == WM_QUIT) { + keep_running = false; + break; + } + ::TranslateMessage(&message); + ::DispatchMessage(&message); + // Allow Flutter to process messages each time a Windows message is + // processed, to prevent starvation. + next_flutter_event_time = + std::min(next_flutter_event_time, ProcessFlutterMessages()); + } + // If the PeekMessage loop didn't run, process Flutter messages. + if (!processed_events) { + next_flutter_event_time = + std::min(next_flutter_event_time, ProcessFlutterMessages()); + } + } +} + +void RunLoop::RegisterFlutterInstance( + flutter::FlutterEngine* flutter_instance) { + flutter_instances_.insert(flutter_instance); +} + +void RunLoop::UnregisterFlutterInstance( + flutter::FlutterEngine* flutter_instance) { + flutter_instances_.erase(flutter_instance); +} + +RunLoop::TimePoint RunLoop::ProcessFlutterMessages() { + TimePoint next_event_time = TimePoint::max(); + for (auto instance : flutter_instances_) { + std::chrono::nanoseconds wait_duration = instance->ProcessMessages(); + if (wait_duration != std::chrono::nanoseconds::max()) { + next_event_time = + std::min(next_event_time, TimePoint::clock::now() + wait_duration); + } + } + return next_event_time; +} diff --git a/packages/syncfusion_flutter_calendar/example/windows/runner/run_loop.h b/packages/syncfusion_flutter_calendar/example/windows/runner/run_loop.h new file mode 100644 index 000000000..000d36246 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/windows/runner/run_loop.h @@ -0,0 +1,40 @@ +#ifndef RUNNER_RUN_LOOP_H_ +#define RUNNER_RUN_LOOP_H_ + +#include + +#include +#include + +// A runloop that will service events for Flutter instances as well +// as native messages. +class RunLoop { + public: + RunLoop(); + ~RunLoop(); + + // Prevent copying + RunLoop(RunLoop const&) = delete; + RunLoop& operator=(RunLoop const&) = delete; + + // Runs the run loop until the application quits. + void Run(); + + // Registers the given Flutter instance for event servicing. + void RegisterFlutterInstance( + flutter::FlutterEngine* flutter_instance); + + // Unregisters the given Flutter instance from event servicing. + void UnregisterFlutterInstance( + flutter::FlutterEngine* flutter_instance); + + private: + using TimePoint = std::chrono::steady_clock::time_point; + + // Processes all currently pending messages for registered Flutter instances. + TimePoint ProcessFlutterMessages(); + + std::set flutter_instances_; +}; + +#endif // RUNNER_RUN_LOOP_H_ diff --git a/packages/syncfusion_flutter_calendar/example/windows/runner/runner.exe.manifest b/packages/syncfusion_flutter_calendar/example/windows/runner/runner.exe.manifest new file mode 100644 index 000000000..c977c4a42 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/packages/syncfusion_flutter_calendar/example/windows/runner/utils.cpp b/packages/syncfusion_flutter_calendar/example/windows/runner/utils.cpp new file mode 100644 index 000000000..d19bdbbcc --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/windows/runner/utils.cpp @@ -0,0 +1,64 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/packages/syncfusion_flutter_calendar/example/windows/runner/utils.h b/packages/syncfusion_flutter_calendar/example/windows/runner/utils.h new file mode 100644 index 000000000..3879d5475 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/packages/syncfusion_flutter_calendar/example/windows/runner/win32_window.cpp b/packages/syncfusion_flutter_calendar/example/windows/runner/win32_window.cpp new file mode 100644 index 000000000..c10f08dc7 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/windows/runner/win32_window.cpp @@ -0,0 +1,245 @@ +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/packages/syncfusion_flutter_calendar/example/windows/runner/win32_window.h b/packages/syncfusion_flutter_calendar/example/windows/runner/win32_window.h new file mode 100644 index 000000000..17ba43112 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/example/windows/runner/win32_window.h @@ -0,0 +1,98 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/syncfusion_flutter_calendar/lib/calendar.dart b/packages/syncfusion_flutter_calendar/lib/calendar.dart index 574814340..c63c1a418 100644 --- a/packages/syncfusion_flutter_calendar/lib/calendar.dart +++ b/packages/syncfusion_flutter_calendar/lib/calendar.dart @@ -23,5 +23,6 @@ export 'src/calendar/settings/schedule_view_settings.dart'; export 'src/calendar/settings/time_region.dart'; export 'src/calendar/settings/time_slot_view_settings.dart'; export 'src/calendar/settings/view_header_style.dart'; +export 'src/calendar/settings/week_number_style.dart'; export 'src/calendar/sfcalendar.dart'; diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/appointment.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/appointment.dart index 825df74b4..4d6d3ff53 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/appointment.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/appointment.dart @@ -3,6 +3,9 @@ import 'package:flutter/material.dart'; import 'package:syncfusion_flutter_datepicker/datepicker.dart' show IterableDiagnostics; +import '../../../calendar.dart'; +import '../common/enums.dart'; + /// Appointment data for calendar. /// /// An object that contains properties to hold the detailed information about @@ -57,12 +60,18 @@ class Appointment with Diagnosticable { this.notes, this.location, this.resourceIds, + this.recurrenceId, + this.id, required this.startTime, required this.endTime, this.subject = '', this.color = Colors.lightBlue, this.recurrenceExceptionDates, - }); + }) { + recurrenceRule = recurrenceId != null ? null : recurrenceRule; + _appointmentType = _getAppointmentType(); + id = id ?? hashCode; + } /// The start time for an [Appointment] in [SfCalendar]. /// @@ -195,7 +204,7 @@ class Appointment with Diagnosticable { /// return DataSource(appointments); /// } /// ``` - bool isAllDay = false; + bool isAllDay; /// The subject for the [Appointment] in [SfCalendar]. /// @@ -398,7 +407,8 @@ class Appointment with Diagnosticable { /// /// DataSource _getCalendarDataSource() { /// List appointments = []; - /// RecurrenceProperties recurrence = new RecurrenceProperties(); + /// RecurrenceProperties recurrence = + /// RecurrenceProperties(startDate: DateTime.now()); /// recurrence.recurrenceType = RecurrenceType.daily; /// recurrence.interval = 2; /// recurrence.recurrenceRange = RecurrenceRange.count; @@ -411,7 +421,7 @@ class Appointment with Diagnosticable { /// color: Colors.blue, /// startTimeZone: '', /// endTimeZone: '', - /// recurrenceRule: SfCalendar.rRuleGenerator(recurrence, + /// recurrenceRule: SfCalendar.generateRRule(recurrence, /// DateTime.now(), DateTime.now().add(Duration(hours: 2))))); /// /// return DataSource(appointments); @@ -449,7 +459,8 @@ class Appointment with Diagnosticable { /// /// DataSource _getCalendarDataSource() { /// List appointments = []; - /// RecurrenceProperties recurrence = new RecurrenceProperties(); + /// RecurrenceProperties recurrence = + /// RecurrenceProperties(startDate: DateTime.now()); /// recurrence.recurrenceType = RecurrenceType.daily; /// recurrence.interval = 2; /// recurrence.recurrenceRange = RecurrenceRange.noEndDate; @@ -466,7 +477,7 @@ class Appointment with Diagnosticable { /// notes: '', /// location: '', /// endTimeZone: '', - /// recurrenceRule: RecurrenceHelper.rRuleGenerator( + /// recurrenceRule: SfCalendar.generateRRule( /// recurrence, DateTime.now(), DateTime.now().add( /// Duration(hours: 2))), /// recurrenceExceptionDates: [ @@ -504,7 +515,8 @@ class Appointment with Diagnosticable { /// /// DataSource _getCalendarDataSource() { /// List appointments = []; - /// RecurrenceProperties recurrence = new RecurrenceProperties(); + /// RecurrenceProperties recurrence = + /// RecurrenceProperties(startDate: DateTime.now()); /// recurrence.recurrenceType = RecurrenceType.daily; /// recurrence.interval = 2; /// recurrence.recurrenceRange = RecurrenceRange.noEndDate; @@ -521,7 +533,7 @@ class Appointment with Diagnosticable { /// notes: '', /// location: '', /// endTimeZone: '', - /// recurrenceRule: RecurrenceHelper.rRuleGenerator( + /// recurrenceRule: SfCalendar.generateRRule( /// recurrence, DateTime.now(), DateTime.now().add( /// Duration(hours: 2))), /// recurrenceExceptionDates: [ @@ -559,7 +571,8 @@ class Appointment with Diagnosticable { /// /// DataSource _getCalendarDataSource() { /// List appointments = []; - /// RecurrenceProperties recurrence = new RecurrenceProperties(); + /// RecurrenceProperties recurrence = + /// RecurrenceProperties(startDate: DateTime.now()); /// recurrence.recurrenceType = RecurrenceType.daily; /// recurrence.interval = 2; /// recurrence.recurrenceRange = RecurrenceRange.noEndDate; @@ -576,7 +589,7 @@ class Appointment with Diagnosticable { /// notes: '', /// location: '', /// endTimeZone: '', - /// recurrenceRule: RecurrenceHelper.rRuleGenerator( + /// recurrenceRule: SfCalendar.generateRRule( /// recurrence, DateTime.now(), DateTime.now().add( /// Duration(hours: 2))), /// recurrenceExceptionDates: [ @@ -648,7 +661,142 @@ class Appointment with Diagnosticable { /// ``` List? resourceIds; + /// Defines the recurrence id for an [Appointment] in [SfCalendar], + /// The recurrence id is used to create an exception appointment + /// in a recurrence series. + /// + /// Defaults to null. + /// The [recurrenceId] of the exception appointment and the [id] of + /// the pattern appointment should be same. The [recurrenceId] should be + /// specified only for exception appointments. It is not required for + /// occurrence, and normal appointments. + /// + /// _Note:_ The exception appointment should be a normal appointment and + /// should not be created as recurring appointment, since its occurrence + /// from recurrence pattern. + /// + /// Exception recurrence appointment should not have a RRule. If the exception + /// appointment has RRule, it will not be considered. + /// + /// ```dart + ///AppointmentDataSource _getDataSource() { + /// List appointments = []; + /// final DateTime exceptionDate = DateTime(2021, 04, 20); + /// + /// final Appointment recurrenceAppointment = Appointment( + /// startTime: DateTime(2021, 04, 12, 10), + /// endTime: DateTime(2021, 04, 12, 12), + /// subject: 'Scrum meeting', + /// id: '01', + /// recurrenceRule: 'FREQ=DAILY;INTERVAL=1;COUNT=10', + /// recurrenceExceptionDates: [exceptionDate], + /// ); + /// appointments.add(recurrenceAppointment); + /// + /// final Appointment exceptionAppointment = Appointment( + /// startTime: exceptionDate.add(Duration(hours: 10)), + /// endTime: exceptionDate.add(Duration(hours: 12)), + /// subject: 'Meeting', + /// id: '02', + /// recurrenceId: recurrenceAppointment.id); + /// + /// appointments.add(exceptionAppointment); + /// + /// return AppointmentDataSource(appointments); + /// } + /// + /// ``` + Object? recurrenceId; + + /// Defines the id for an [Appointment] in [SfCalendar]. + /// + /// The unique identifier for the appointment. It must be mentioned to add + /// an exception appointment to the recurrence series. + /// + /// Defaults to the hash code of the appointment. + /// [id] can be set to all the appointment types where the internally + /// created occurrence appointment will have the same [id] value as + /// the pattern appointment. + /// + /// The exception appointment should have the different [id] value compared + /// to the pattern appointment. But the [recurrenceId] of the exception + /// appointment and the [id] value of the pattern appointment should be same. + /// ```dart + /// AppointmentDataSource _getDataSource() { + /// List appointments = []; + /// + /// appointments.add(Appointment( + /// startTime: DateTime(2021, 04, 12, 10), + /// endTime: DateTime(2021, 04, 12, 12), + /// subject: 'Scrum meeting', + /// id: '01', + /// recurrenceRule: 'FREQ=DAILY;INTERVAL=1;COUNT=10', + /// )); + /// appointments.add(Appointment( + /// startTime: DateTime(2021, 04, 12, 16), + /// endTime: DateTime(2021, 04, 12, 17), + /// subject: 'Meeting', + /// id: '02', + /// )); + /// appointments.add(Appointment( + /// startTime: DateTime(2021, 04, 13, 09), + /// endTime: DateTime(2021, 04, 13, 10), + /// subject: 'Discussion', + /// )); + /// + /// return AppointmentDataSource(appointments); + /// } + /// + /// ``` + Object? id; + + AppointmentType _appointmentType = AppointmentType.normal; + + ///Specifies the appointment type, which is used to distinguish appointments + /// based on their functionality. + /// This is read-only. + /// Defines the appointment type for the [Appointment]. + /// + /// Also refer: [AppointmentType]. + /// + /// ``` dart + ///Widget build(BuildContext context) { + /// return Container( + /// child: SfCalendar( + /// view: CalendarView.day, + /// dataSource: dataSource, + /// onTap: (CalendarTapDetails details){ + /// for(int i=0; i _appointmentType; + + /// Here we used isOccurrenceAppointment keyword to identify the + /// occurrence appointment When we clone the pattern appointment for + /// occurrence appointment we have append the string in the notes and here we + /// identify based on the string and removed the appended string. + AppointmentType _getAppointmentType() { + if (recurrenceId != null) { + return AppointmentType.changedOccurrence; + } else if (recurrenceRule != null && recurrenceRule!.isNotEmpty) { + if (notes != null && notes!.contains('isOccurrenceAppointment')) { + notes = notes!.replaceAll('isOccurrenceAppointment', ''); + return AppointmentType.occurrence; + } + + return AppointmentType.pattern; + } + return AppointmentType.normal; + } + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(dynamic other) { if (identical(this, other)) { return true; @@ -657,7 +805,10 @@ class Appointment with Diagnosticable { return false; } - final Appointment otherStyle = other; + late final Appointment otherStyle; + if (other is Appointment) { + otherStyle = other; + } return otherStyle.startTime == startTime && otherStyle.endTime == endTime && otherStyle.startTimeZone == startTimeZone && @@ -668,19 +819,26 @@ class Appointment with Diagnosticable { otherStyle.resourceIds == resourceIds && otherStyle.subject == subject && otherStyle.color == color && - otherStyle.recurrenceExceptionDates == recurrenceExceptionDates; + otherStyle.recurrenceExceptionDates == recurrenceExceptionDates && + otherStyle.recurrenceId == recurrenceId && + otherStyle.id == id && + otherStyle.appointmentType == appointmentType; } @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes int get hashCode { return hashValues( startTimeZone, endTimeZone, recurrenceRule, - isAllDay = false, + isAllDay, notes, location, hashList(resourceIds), + recurrenceId, + id, + appointmentType, startTime, endTime, subject, @@ -699,6 +857,10 @@ class Appointment with Diagnosticable { properties.add(StringProperty('location', location)); properties.add(StringProperty('subject', subject)); properties.add(ColorProperty('color', color)); + properties.add(DiagnosticsProperty('recurrenceId', recurrenceId)); + properties.add(DiagnosticsProperty('id', id)); + properties + .add(EnumProperty('appointmentType', appointmentType)); properties.add(DiagnosticsProperty('startTime', startTime)); properties.add(DiagnosticsProperty('endTime', endTime)); properties.add(IterableDiagnostics(recurrenceExceptionDates) diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/appointment_helper.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/appointment_helper.dart index afa72350c..94cd9a1af 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/appointment_helper.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/appointment_helper.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart' show DateFormat; +import 'package:syncfusion_flutter_calendar/src/calendar/common/date_time_engine.dart'; import 'package:syncfusion_flutter_core/core.dart'; import 'package:syncfusion_flutter_core/localizations.dart'; import 'package:timezone/timezone.dart'; @@ -30,13 +31,14 @@ class AppointmentHelper { /// Return the end date of the month specified in date. static DateTime getMonthEndDate(DateTime date) { - return addDays(getNextMonthDate(date), -1); + return DateTimeHelper.getDateTimeValue(addDays(getNextMonthDate(date), -1)); } /// Return the date time value by adding the days in date. static DateTime addDaysWithTime( DateTime date, int days, int hour, int minute, int second) { - final DateTime newDate = addDays(date, days); + final DateTime newDate = + DateTimeHelper.getDateTimeValue(addDays(date, days)); return DateTime( newDate.year, newDate.month, newDate.day, hour, minute, second); } @@ -121,8 +123,10 @@ class AppointmentHelper { } /// Returns recurrence icon details for appointment view. - static TextSpan getRecurrenceIcon(Color color, double textSize) { - final IconData recurrenceIconData = Icons.autorenew; + static TextSpan getRecurrenceIcon( + Color color, double textSize, bool isRecurrenceApointment) { + final IconData recurrenceIconData = + isRecurrenceApointment ? Icons.autorenew : Icons.sync_disabled; return TextSpan( text: String.fromCharCode(recurrenceIconData.codePoint), style: TextStyle( @@ -155,16 +159,16 @@ class AppointmentHelper { /// appointment static String getSpanAppointmentText(CalendarAppointment appointment, DateTime date, SfLocalizations localization) { + final DateTime exactStartTime = + convertToStartTime(appointment.exactStartTime); final String totalDays = (convertToEndTime(appointment.exactEndTime) - .difference(convertToStartTime(appointment.exactStartTime)) - .inDays + - 1) - .toString(); - final String currentDate = (convertToEndTime(date) - .difference(convertToStartTime(appointment.exactStartTime)) + .difference(exactStartTime) .inDays + 1) .toString(); + final String currentDate = + (convertToEndTime(date).difference(exactStartTime).inDays + 1) + .toString(); return appointment.subject + ' (' + @@ -260,7 +264,9 @@ class AppointmentHelper { notes: appointment.notes, location: appointment.location, isSpanned: appointment.isSpanned, - resourceIds: appointment.resourceIds); + resourceIds: appointment.resourceIds, + recurrenceId: appointment.recurrenceId, + id: appointment.id); copyAppointment.actualStartTime = appointment.actualStartTime; copyAppointment.actualEndTime = appointment.actualEndTime; copyAppointment.data = appointment.data; @@ -451,9 +457,9 @@ class AppointmentHelper { final String? timeZone = olsonWindowsTimes[windowsTimeZoneId]; if (timeZone != null) { return getLocation(timeZone); - } else { - return getLocation(windowsTimeZoneId); } + + return getLocation(windowsTimeZoneId); } /// Resets the appointment views used on appointment layout rendering. @@ -479,12 +485,11 @@ class AppointmentHelper { calendar.timeSlotViewSettings)) * timeIntervalHeight) .toDouble(); - final int hour = date.hour; - final int minute = date.minute; - final int seconds = date.second; - final double startHour = calendar.timeSlotViewSettings.startHour; - return ((hour + (minute / 60).toDouble() + (seconds / 3600).toDouble()) * + final double startHour = calendar.timeSlotViewSettings.startHour; + return ((date.hour + + (date.minute / 60).toDouble() + + (date.second / 3600).toDouble()) * singleIntervalHeightForAnHour) - (startHour * singleIntervalHeightForAnHour).toDouble(); } @@ -504,112 +509,81 @@ class AppointmentHelper { return minimumDuration.inMinutes * (hourHeight / 60); } - /// Returns the minimum height for the appointment view passed, to render the - /// appointment view within this height. - static double _getAppointmentMinHeight( - SfCalendar calendar, AppointmentView appView, double timeIntervalHeight) { - double minHeight; - - // Appointment Default Bottom Position without considering MinHeight - final double defaultAppHeight = timeToPosition( - calendar, appView.appointment!.actualEndTime, timeIntervalHeight) - - timeToPosition( - calendar, appView.appointment!.actualStartTime, timeIntervalHeight); - - minHeight = getAppointmentHeightFromDuration( - calendar.timeSlotViewSettings.minimumAppointmentDuration, - calendar, - timeIntervalHeight); - - // Appointment Default Bottom Position - Default value as double.NaN - if (minHeight == 0) { - return defaultAppHeight; - } else if ((minHeight < defaultAppHeight) || - (timeIntervalHeight < defaultAppHeight)) { - // Appointment Minimum Height is smaller than default Appointment Height - // Appointment default Height is greater than TimeIntervalHeight - return defaultAppHeight; - } else if (minHeight > timeIntervalHeight) { - // Appointment Minimum Height is greater than Interval Height - return timeIntervalHeight; - } else { - // Appointment with proper MinHeight and within Interval - return minHeight; //appView.Appointment.MinHeight; - } - } - static bool _isIntersectingAppointmentInDayView( SfCalendar calendar, CalendarView view, CalendarAppointment currentApp, AppointmentView appView, CalendarAppointment appointment, - bool isAllDay, - double timeIntervalHeight) { + int timeIntervalMinutes) { if (currentApp == appointment) { return false; } - if (currentApp.actualStartTime.isBefore(appointment.actualEndTime) && - currentApp.actualStartTime.isAfter(appointment.actualStartTime)) { - return true; - } + final DateTime currentAppointmentStartTime = currentApp.actualStartTime; + DateTime currentAppointmentEndTime = currentApp.actualEndTime; + final DateTime appointmentStartTime = appointment.actualStartTime; + DateTime appointmentEndTime = appointment.actualEndTime; + final bool isTimelineMonth = view == CalendarView.timelineMonth; + int minimumAppointmentMinutes = + calendar.timeSlotViewSettings.minimumAppointmentDuration != null + ? calendar + .timeSlotViewSettings.minimumAppointmentDuration!.inMinutes + : 0; + minimumAppointmentMinutes = minimumAppointmentMinutes > timeIntervalMinutes + ? timeIntervalMinutes + : minimumAppointmentMinutes; + if (minimumAppointmentMinutes > 0 && !isTimelineMonth) { + final int timeIntervalMinutes = + calendar.timeSlotViewSettings.timeInterval.inMinutes; + minimumAppointmentMinutes = + minimumAppointmentMinutes > timeIntervalMinutes + ? timeIntervalMinutes + : minimumAppointmentMinutes; + if (currentAppointmentEndTime + .difference(currentAppointmentStartTime) + .inMinutes < + minimumAppointmentMinutes) { + currentAppointmentEndTime = currentAppointmentStartTime + .add(Duration(minutes: minimumAppointmentMinutes)); + } - if (currentApp.actualEndTime.isAfter(appointment.actualStartTime) && - currentApp.actualEndTime.isBefore(appointment.actualEndTime)) { - return true; + if (appointmentEndTime.difference(appointmentStartTime).inMinutes < + minimumAppointmentMinutes) { + appointmentEndTime = appointmentStartTime + .add(Duration(minutes: minimumAppointmentMinutes)); + } } - if (currentApp.actualEndTime.isAfter(appointment.actualEndTime) && - currentApp.actualStartTime.isBefore(appointment.actualStartTime)) { + if (currentAppointmentStartTime.isBefore(appointmentEndTime) && + currentAppointmentStartTime.isAfter(appointmentStartTime)) { return true; } - if (CalendarViewHelper.isSameTimeSlot( - currentApp.actualStartTime, appointment.actualStartTime) || - CalendarViewHelper.isSameTimeSlot( - currentApp.actualEndTime, appointment.actualEndTime)) { + if (currentAppointmentEndTime.isAfter(appointmentStartTime) && + currentAppointmentEndTime.isBefore(appointmentEndTime)) { return true; } - if (isAllDay) { - return false; + if (currentAppointmentEndTime.isAfter(appointmentEndTime) && + currentAppointmentStartTime.isBefore(appointmentStartTime)) { + return true; } - /// For timeline month view, the intercepting appointments muse be + /// For timeline month view, the intercepting appointments must be /// calculated based on the date instead of the time, hence added this /// condition and returned that it's a intercept appointment or not. - if (view == CalendarView.timelineMonth) { + if (isTimelineMonth) { return isSameDate( currentApp.actualStartTime, appointment.actualStartTime) || isSameDate(currentApp.actualEndTime, appointment.actualEndTime); } - // Intersecting appointments by comparing appointments MinHeight instead of - // Start and EndTime - if (calendar.timeSlotViewSettings.minimumAppointmentDuration != null && - calendar.timeSlotViewSettings.minimumAppointmentDuration!.inMinutes > - 0 && - view != CalendarView.timelineMonth) { - // Comparing appointments rendered in different dates - if (!isSameDate( - currentApp.actualStartTime, appointment.actualStartTime)) { - return false; - } - - // Comparing appointments rendered in the same date - final double appTopPos = timeToPosition( - calendar, appointment.actualStartTime, timeIntervalHeight); - final double currentAppTopPos = timeToPosition( - calendar, currentApp.actualStartTime, timeIntervalHeight); - final double appHeight = - _getAppointmentMinHeight(calendar, appView, timeIntervalHeight); - // Height difference between previous and current appointment from top - // position - final double heightDiff = currentAppTopPos - appTopPos; - if (appTopPos != currentAppTopPos && appHeight > heightDiff) { - return true; - } + if (CalendarViewHelper.isSameTimeSlot( + currentAppointmentStartTime, appointmentStartTime) || + CalendarViewHelper.isSameTimeSlot( + currentAppointmentEndTime, appointmentEndTime)) { + return true; } return false; @@ -703,7 +677,6 @@ class AppointmentHelper { CalendarView view, List visibleAppointments, bool isAllDay, - double timeIntervalHeight, [int? resourceIndex]) { final bool isTimeline = CalendarViewHelper.isTimelineView(view); final List normalAppointments = visibleAppointments @@ -733,6 +706,8 @@ class AppointmentHelper { final List processedViews = []; int maxColsCount = 1; + final int timeIntervalMinutes = + CalendarViewHelper.getTimeInterval(calendar.timeSlotViewSettings); for (int count = 0; count < normalAppointments.length; count++) { final CalendarAppointment currentAppointment = normalAppointments[count]; if ((view == CalendarView.workWeek || @@ -752,7 +727,6 @@ class AppointmentHelper { bool isIntersecting = false; for (int j = 0; j < processedViews.length; j++) { final AppointmentView previousApp = processedViews[j]; - if (previousApp.position != position) { continue; } @@ -763,8 +737,7 @@ class AppointmentHelper { currentAppointment, previousApp, previousApp.appointment!, - isAllDay, - timeIntervalHeight)) { + timeIntervalMinutes)) { isIntersecting = true; if (intersectingApps == null) { @@ -1101,7 +1074,8 @@ class AppointmentHelper { if (i == 0) { spannedAppointment.actualStartTime = appointment.actualStartTime; - final DateTime date = addDays(startDate, -1); + final DateTime date = + DateTimeHelper.getDateTimeValue(addDays(startDate, -1)); spannedAppointment.actualEndTime = DateTime(date.year, date.month, date.day, 23, 59, 59); } else { @@ -1140,7 +1114,8 @@ class AppointmentHelper { final CalendarAppointment spannedAppointment = _copy(appointment); if (i == 0) { - final DateTime date = addDays(startDate, -1); + final DateTime date = + DateTimeHelper.getDateTimeValue(addDays(startDate, -1)); spannedAppointment.actualEndTime = DateTime(date.year, date.month, date.day, 23, 59, 59); } else if (i == 1) { @@ -1149,7 +1124,8 @@ class AppointmentHelper { spannedAppointment.actualEndTime = DateTime( endDate.year, endDate.month, endDate.day, 23, 59, 59); } else { - final DateTime date = addDays(endDate, 1); + final DateTime date = + DateTimeHelper.getDateTimeValue(addDays(endDate, 1)); spannedAppointment.actualStartTime = DateTime(date.year, date.month, date.day, 0, 0, 0); } @@ -1203,7 +1179,6 @@ class AppointmentHelper { static CalendarAppointment _cloneRecurrenceAppointment( CalendarAppointment appointment, - int recurrenceIndex, DateTime recursiveDate, String? calendarTimeZone) { final CalendarAppointment occurrenceAppointment = _copy(appointment); @@ -1218,8 +1193,9 @@ class AppointmentHelper { final int minutes = appointment.actualEndTime .difference(appointment.actualStartTime) .inMinutes; - occurrenceAppointment.actualEndTime = addDuration( - occurrenceAppointment.actualStartTime, Duration(minutes: minutes)); + occurrenceAppointment.actualEndTime = DateTimeHelper.getDateTimeValue( + addDuration( + occurrenceAppointment.actualStartTime, Duration(minutes: minutes))); occurrenceAppointment.endTime = occurrenceAppointment.isAllDay ? occurrenceAppointment.actualEndTime : convertTimeToAppointmentTimeZone(occurrenceAppointment.actualEndTime, @@ -1254,7 +1230,11 @@ class AppointmentHelper { if (dataSource.isNotEmpty && dataSource[0] is CalendarAppointment) { for (int i = 0; i < dataSource.length; i++) { - final CalendarAppointment item = dataSource[i]; + final dynamic dataSourceItem = dataSource[i]; + late final CalendarAppointment item; + if (dataSourceItem is CalendarAppointment) { + item = dataSourceItem; + } final DateTime appStartTime = item.startTime; final DateTime appEndTime = item.endTime; item.data = item; @@ -1305,7 +1285,9 @@ class AppointmentHelper { endTimeZone: appointmentObject.endTimeZone, recurrenceRule: appointmentObject.recurrenceRule, recurrenceExceptionDates: appointmentObject.recurrenceExceptionDates, - resourceIds: appointmentObject.resourceIds); + resourceIds: appointmentObject.resourceIds, + recurrenceId: appointmentObject.recurrenceId, + id: appointmentObject.id); } else { final int index = calendarData.appointments!.indexOf(appointmentObject); app = CalendarAppointment( @@ -1321,7 +1303,9 @@ class AppointmentHelper { recurrenceRule: calendarData.getRecurrenceRule(index), recurrenceExceptionDates: calendarData.getRecurrenceExceptionDates(index), - resourceIds: calendarData.getResourceIds(index)); + resourceIds: calendarData.getResourceIds(index), + recurrenceId: calendarData.getRecurrenceId(index), + id: calendarData.getId(index)); } app.data = appointmentObject; @@ -1359,7 +1343,6 @@ class AppointmentHelper { DateTime visibleEndDate, String? scheduleTimeZone) { final DateTime appStartTime = appointment.actualStartTime; - int recurrenceIndex = 0; if (appStartTime.isAfter(visibleEndDate)) { return; } @@ -1372,39 +1355,13 @@ class AppointmentHelper { rule = rule + newSubString; } - List recursiveDates; - final List ruleSeparator = ['=', ';', ',']; - final List rRule = - RecurrenceHelper.splitRule(recurrenceRule, ruleSeparator); - if (recurrenceRule.contains('UNTIL')) { - final String untilValue = rRule[rRule.indexOf('UNTIL') + 1]; - DateTime endDate = DateTime.parse(untilValue); - endDate = addDuration(endDate, - appointment.actualEndTime.difference(appointment.actualStartTime)); - endDate = DateTime(endDate.year, endDate.month, endDate.day, 23, 59, 59); - if (!(appStartTime.isBefore(visibleEndDate) && - visibleStartDate.isBefore(endDate))) { - return; - } - } else if (recurrenceRule.contains('COUNT')) { - recursiveDates = RecurrenceHelper.getRecurrenceDateTimeCollection( - recurrenceRule, appointment.actualStartTime); - DateTime endDate = recursiveDates.last; - endDate = addDuration(endDate, - appointment.actualEndTime.difference(appointment.actualStartTime)); - endDate = DateTime(endDate.year, endDate.month, endDate.day, 23, 59, 59); - if (!(appStartTime.isBefore(visibleEndDate) && - visibleStartDate.isBefore(endDate))) { - return; - } - } - - recursiveDates = RecurrenceHelper.getRecurrenceDateTimeCollection( - rule, appointment.actualStartTime, - recurrenceDuration: - appointment.actualEndTime.difference(appointment.actualStartTime), - specificStartDate: visibleStartDate, - specificEndDate: visibleEndDate); + final List recursiveDates = + RecurrenceHelper.getRecurrenceDateTimeCollection( + rule, appointment.actualStartTime, + recurrenceDuration: + appointment.exactEndTime.difference(appointment.exactStartTime), + specificStartDate: visibleStartDate, + specificEndDate: visibleEndDate); for (int j = 0; j < recursiveDates.length; j++) { final DateTime recursiveDate = recursiveDates[j]; @@ -1427,8 +1384,32 @@ class AppointmentHelper { final CalendarAppointment occurrenceAppointment = _cloneRecurrenceAppointment( - appointment, recurrenceIndex, recursiveDate, scheduleTimeZone); - recurrenceIndex++; + appointment, recursiveDate, scheduleTimeZone); + String recurrenceRule = appointment.recurrenceRule!; + + /// To check whether the appointment is pattern or not, we need to get + /// the first appointment of the rrule, hence added count as 1 in rrule, + /// if count not given in the rrule, here we didn't change + /// the appointment's rrule we used a separate property internally + /// for our purpose. + if (!recurrenceRule.contains('COUNT')) { + recurrenceRule = recurrenceRule + ';COUNT=1'; + } + final List recDates = + RecurrenceHelper.getRecurrenceDateTimeCollection( + recurrenceRule, appointment.actualStartTime, + specificStartDate: appointment.startTime); + + /// Here we used isOccurrenceAppointment keyword to identify the + /// occurrence appointment When we clone the pattern appointment for + /// occurrence appointment we have append the string in the notes and + /// here we identify based on the string and removed the appended string. + occurrenceAppointment.notes = recDates.isNotEmpty && + isSameDate(occurrenceAppointment.startTime, recDates[0]) + ? appointment.notes + : appointment.notes == null + ? 'isOccurrenceAppointment' + : appointment.notes! + 'isOccurrenceAppointment'; appointments.add(occurrenceAppointment); } } diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/calendar_datasource.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/calendar_datasource.dart index cb248280d..3abd351e6 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/calendar_datasource.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/calendar_datasource.dart @@ -2,10 +2,12 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:syncfusion_flutter_calendar/src/calendar/appointment_engine/appointment_helper.dart'; import 'package:syncfusion_flutter_calendar/src/calendar/common/calendar_view_helper.dart'; +import 'package:syncfusion_flutter_core/core.dart'; import 'package:syncfusion_flutter_datepicker/datepicker.dart' show IterableDiagnostics; import '../../../calendar.dart'; +import '../common/calendar_view_helper.dart'; import '../common/enums.dart'; import '../resource_view/calendar_resource.dart'; @@ -35,62 +37,61 @@ import '../resource_view/calendar_resource.dart'; /// /// @override /// DateTime getStartTime(int index) { -/// return appointments[index].from; +/// return appointments![index].from; /// } /// /// @override /// DateTime getEndTime(int index) { -/// return appointments[index].to; +/// return appointments![index].to; /// } /// /// @override /// Color getColor(int index) { -/// return appointments[index].background; +/// return appointments![index].background; /// } /// /// @override /// String getEndTimeZone(int index) { -/// return appointments[index].toZone; +/// return appointments![index].toZone; /// } /// /// @override /// List getRecurrenceExceptionDates(int index) { -/// return appointments[index].exceptionDates; +/// return appointments![index].exceptionDates; /// } /// /// @override /// String getRecurrenceRule(int index) { -/// return appointments[index].recurrenceRule; +/// return appointments![index].recurrenceRule; /// } /// /// @override /// String getStartTimeZone(int index) { -/// return appointments[index].fromZone; +/// return appointments![index].fromZone; /// } /// /// @override /// String getSubject(int index) { -/// return appointments[index].title; +/// return appointments![index].title; /// } /// /// @override /// bool isAllDay(int index) { -/// return appointments[index].isAllDay; +/// return appointments![index].isAllDay; /// } /// } /// -/// class Meeting { -/// Meeting( -/// {this.from, -/// this.to, -/// this.title, -/// this.isAllDay, -/// this.background, -/// this.fromZone, -/// this.toZone, +///class _Meeting { +/// _Meeting( +/// {required this.from, +/// required this.to, +/// this.title='', +/// this.isAllDay=false, +/// required this.background, +/// this.fromZone='', +/// this.toZone='', /// this.exceptionDates, -/// this.recurrenceRule}); -/// +/// this.recurrenceRule=''}); /// DateTime from; /// DateTime to; /// String title; @@ -99,13 +100,13 @@ import '../resource_view/calendar_resource.dart'; /// String fromZone; /// String toZone; /// String recurrenceRule; -/// List exceptionDates; +/// List? exceptionDates; /// } /// /// final DateTime date = DateTime.now(); /// MeetingDataSource _getCalendarDataSource() { -/// List appointments = []; -/// appointments.add(Meeting( +/// List<_Meeting> appointments = <_Meeting>[]; +/// appointments.add(_Meeting( /// from: date, /// to: date.add(const Duration(hours: 1)), /// title: 'General Meeting', @@ -147,13 +148,12 @@ abstract class CalendarDataSource extends CalendarDataSourceChangeNotifier { /// /// class MyAppState extends State { /// - /// CalendarController _calendarController; - /// _AppointmentDataSource _dataSource; + /// CalendarController _calendarController = CalendarController(); + /// late _AppointmentDataSource _dataSource; /// /// @override /// initState() { - /// _calendarController = CalendarController(); - /// _dataSource = _getCalendarDataSource(); + /// _dataSource = _getCalendarDataSource(); /// super.initState(); /// } /// @@ -199,7 +199,7 @@ abstract class CalendarDataSource extends CalendarDataSourceChangeNotifier { /// isAllDay: true /// )); /// return _AppointmentDataSource(appointments); - /// }} + /// } /// /// class _AppointmentDataSource extends CalendarDataSource { /// _AppointmentDataSource(List source) { @@ -233,6 +233,232 @@ abstract class CalendarDataSource extends CalendarDataSourceChangeNotifier { return visibleAppointments; } + /// Returns the occurrence appointment for the + /// given pattern appointment at the specified date. + /// + /// If there is no appointment occurring on + /// the date specified, null is returned. + /// + /// patternAppointment - required - The pattern appointment is + /// the start appointment in a recurrence series from which the occurrence + /// appointments are cloned with pattern appointment characteristics. + /// + /// date - required - The date on which the + /// occurrence appointment is requested. + /// + /// ```dart + /// + /// class MyAppState extends State{ + /// + /// CalendarController _calendarController; + /// _AppointmentDataSource _dataSource; + /// Appointment recurrenceApp; + /// @override + /// initState(){ + /// _calendarController = CalendarController(); + /// _dataSource = _getCalendarDataSource(); + /// super.initState(); + /// } + /// + /// @override + /// Widget build(BuildContext context) { + /// return MaterialApp( + /// home: Scaffold( + /// body: SfCalendar( + /// view: CalendarView.month, + /// controller: _calendarController, + /// dataSource: _dataSource, + /// onTap: (CalendarTapDetails details) { + /// DateTime date = details.date; + /// String calendarTimeZone = ''; + /// Appointment appointment = _dataSource.getOccurrenceAppointment( + /// recurrenceApp, date, calendarTimeZone); + /// }, + /// ), + /// ), + /// ); + /// } + ///} + /// + /// _AppointmentDataSource _getCalendarDataSource() { + /// List appointments = []; + /// recurrenceApp = appointments.add(Appointment( + /// startTime: DateTime(2020,11,27,9), + /// endTime: DateTime(2020,11,27,9).add(Duration(hours: 2)), + /// subject: 'Meeting', + /// color: Colors.cyanAccent, + /// startTimeZone: '', + /// endTimeZone: '', + /// recurrenceRule: 'FREQ=DAILY;INTERVAL=2;COUNT=5', + /// )); + /// appointments.add(recurrenceApp); + /// appointments.add(Appointment( + /// startTime: DateTime(2020,11,28,5), + /// endTime: DateTime(2020,11,30,7), + /// subject: 'Discussion', + /// color: Colors.orangeAccent, + /// startTimeZone: '', + /// endTimeZone: '', + /// isAllDay: true + /// )); + /// return _AppointmentDataSource(appointments); + /// } + /// } + /// + /// class _AppointmentDataSource extends CalendarDataSource { + /// _AppointmentDataSource(List source){ + /// appointments = source; + /// } + ///} + /// ``` + Appointment? getOccurrenceAppointment( + Object? patternAppointment, DateTime date, String calendarTimeZone) { + if (patternAppointment == null) { + return null; + } + + final List patternAppointmentColl = [patternAppointment]; + final List patternAppointments = + AppointmentHelper.generateCalendarAppointments( + this, calendarTimeZone, patternAppointmentColl); + final CalendarAppointment patternCalendarAppointment = + patternAppointments[0]; + + if (patternCalendarAppointment.recurrenceRule == null || + patternCalendarAppointment.recurrenceRule!.isEmpty) { + return null; + } else if (CalendarViewHelper.isDateInDateCollection( + patternCalendarAppointment.recurrenceExceptionDates, date)) { + final List dataSourceAppointments = + AppointmentHelper.generateCalendarAppointments( + this, calendarTimeZone); + for (int i = 0; i < dataSourceAppointments.length; i++) { + final CalendarAppointment dataSourceAppointment = + dataSourceAppointments[i]; + if (patternCalendarAppointment.id == + dataSourceAppointment.recurrenceId && + (isSameDate(dataSourceAppointment.startTime, date))) { + return dataSourceAppointment.convertToCalendarAppointment(); + } + } + } else { + final List occurrenceAppointments = + AppointmentHelper.getVisibleAppointments( + date, date, patternAppointments, calendarTimeZone, false, + canCreateNewAppointment: false); + + if (occurrenceAppointments.isEmpty) { + return null; + } + + return occurrenceAppointments[0].convertToCalendarAppointment(); + } + } + + /// Returns the Pattern appointment for the provided occurrence appointment. + /// + /// occurrenceAppointment - required - The occurrence appointment for which + /// the Pattern appointment is obtained. + /// + /// ```dart + /// + /// class MyAppState extends State{ + /// + /// CalendarController _calendarController; + /// _AppointmentDataSource _dataSource; + /// Appointment recurrenceApp; + /// @override + /// initState(){ + /// _calendarController = CalendarController(); + /// _dataSource = _getCalendarDataSource(); + /// super.initState(); + /// } + /// + /// @override + /// Widget build(BuildContext context) { + /// return MaterialApp( + /// home: Scaffold( + /// body: SfCalendar( + /// view: CalendarView.month, + /// controller: _calendarController, + /// dataSource: _dataSource, + /// onTap: (CalendarTapDetails details) { + /// DateTime date = details.date; + /// String calendarTimeZone = ''; + /// Appointment appointment = _dataSource.getPatternAppointment( + /// occurrenceAppointment, calendarTimeZone); + /// }, + /// ), + /// ), + /// ); + /// } + ///} + /// + /// _AppointmentDataSource _getCalendarDataSource() { + /// List appointments = []; + /// recurrenceApp = appointments.add(Appointment( + /// startTime: DateTime(2020,11,27,9), + /// endTime: DateTime(2020,11,27,9).add(Duration(hours: 2)), + /// subject: 'Meeting', + /// color: Colors.cyanAccent, + /// startTimeZone: '', + /// endTimeZone: '', + /// recurrenceRule: 'FREQ=DAILY;INTERVAL=2;COUNT=5', + /// )); + /// appointments.add(recurrenceApp); + /// appointments.add(Appointment( + /// startTime: DateTime(2020,11,28,5), + /// endTime: DateTime(2020,11,30,7), + /// subject: 'Discussion', + /// color: Colors.orangeAccent, + /// startTimeZone: '', + /// endTimeZone: '', + /// isAllDay: true + /// )); + /// return _AppointmentDataSource(appointments); + /// } + /// } + /// + /// class _AppointmentDataSource extends CalendarDataSource { + /// _AppointmentDataSource(List source){ + /// appointments = source; + /// } + ///} + /// ``` + Object? getPatternAppointment( + Object? occurrenceAppointment, String calendarTimeZone) { + if (occurrenceAppointment == null) { + return null; + } + final List occurrenceAppointmentColl = [ + occurrenceAppointment + ]; + final List occurrenceAppointments = + AppointmentHelper.generateCalendarAppointments( + this, calendarTimeZone, occurrenceAppointmentColl); + final CalendarAppointment occurrenceCalendarAppointment = + occurrenceAppointments[0]; + if ((occurrenceCalendarAppointment.recurrenceRule == null || + occurrenceCalendarAppointment.recurrenceRule!.isEmpty) && + occurrenceCalendarAppointment.recurrenceId == null) { + return null; + } + final List dataSourceAppointments = + AppointmentHelper.generateCalendarAppointments( + this, calendarTimeZone, appointments); + + for (int i = 0; i < dataSourceAppointments.length; i++) { + final CalendarAppointment dataSourceAppointment = + dataSourceAppointments[i]; + if ((dataSourceAppointment.id == + occurrenceCalendarAppointment.recurrenceId) || + (dataSourceAppointment.id == occurrenceCalendarAppointment.id)) { + return dataSourceAppointment.data; + } + } + return null; + } + /// The collection of resource to be displayed in the timeline views of /// [SfCalendar]. /// @@ -267,7 +493,7 @@ abstract class CalendarDataSource extends CalendarDataSourceChangeNotifier { /// ```dart /// @override /// DateTime getStartTime(int index) { - /// return appointments[index].from; + /// return appointments![index].from; /// } /// ``` DateTime getStartTime(int index) => DateTime.now(); @@ -288,7 +514,7 @@ abstract class CalendarDataSource extends CalendarDataSourceChangeNotifier { /// ```dart /// @override /// DateTime getEndTime(int index) { - /// return appointments[index].to; + /// return appointments![index].to; /// } /// ``` DateTime getEndTime(int index) => DateTime.now(); @@ -306,7 +532,7 @@ abstract class CalendarDataSource extends CalendarDataSourceChangeNotifier { /// ```dart /// @override /// DateTime getSubject(int index) { - /// return appointments[index].title; + /// return appointments![index].title; /// } /// ``` String getSubject(int index) => ''; @@ -324,7 +550,7 @@ abstract class CalendarDataSource extends CalendarDataSourceChangeNotifier { /// ```dart /// @override /// DateTime isAllDay(int index) { - /// return appointments[index].isAllDay; + /// return appointments![index].isAllDay; /// } /// ``` bool isAllDay(int index) => false; @@ -342,7 +568,7 @@ abstract class CalendarDataSource extends CalendarDataSourceChangeNotifier { /// ```dart /// @override /// DateTime getColor(int index) { - /// return appointments[index].background; + /// return appointments![index].background; /// } /// ``` Color getColor(int index) => Colors.lightBlue; @@ -360,7 +586,7 @@ abstract class CalendarDataSource extends CalendarDataSourceChangeNotifier { /// ```dart /// @override /// DateTime getNotes(int index) { - /// return appointments[index].notes; + /// return appointments![index].notes; /// } /// ``` String? getNotes(int index) => null; @@ -378,7 +604,7 @@ abstract class CalendarDataSource extends CalendarDataSourceChangeNotifier { /// ```dart /// @override /// DateTime getLocation(int index) { - /// return appointments[index].place; + /// return appointments![index].place; /// } /// ``` String? getLocation(int index) => null; @@ -396,7 +622,7 @@ abstract class CalendarDataSource extends CalendarDataSourceChangeNotifier { /// ```dart /// @override /// DateTime getStartTimeZone(int index) { - /// return appointments[index].fromZone; + /// return appointments![index].fromZone; /// } /// ``` String? getStartTimeZone(int index) => null; @@ -414,7 +640,7 @@ abstract class CalendarDataSource extends CalendarDataSourceChangeNotifier { /// ```dart /// @override /// DateTime getEndTimeZone(int index) { - /// return appointments[index].toZone; + /// return appointments![index].toZone; /// } /// ``` String? getEndTimeZone(int index) => null; @@ -432,7 +658,7 @@ abstract class CalendarDataSource extends CalendarDataSourceChangeNotifier { /// ```dart /// @override /// DateTime getRecurrenceRule(int index) { - /// return appointments[index].recurrenceRule; + /// return appointments![index].recurrenceRule; /// } /// ``` String? getRecurrenceRule(int index) => null; @@ -450,7 +676,7 @@ abstract class CalendarDataSource extends CalendarDataSourceChangeNotifier { /// ```dart /// @override /// DateTime getRecurrenceExceptionDates(int index) { - /// return appointments[index].exceptionDates; + /// return appointments![index].exceptionDates; /// } /// ``` List? getRecurrenceExceptionDates(int index) => null; @@ -469,11 +695,47 @@ abstract class CalendarDataSource extends CalendarDataSourceChangeNotifier { /// ```dart /// @override /// DateTime getResourceIds(int index) { - /// return appointments[index].resourceIds; + /// return appointments![index].resourceIds; /// } /// ``` List? getResourceIds(int index) => null; + /// Maps the custom appointments recurrence id to the [Appointment]. + /// + /// Allows to set the custom appointments corresponding property to the + /// [Appointment]'s recurrenceId property. + /// + /// _Note:_ It is applicable only when the custom appointments set to the + /// [appointments]. + /// + /// See also: [Appointment.recurrenceId]. + /// + /// ```dart + /// @override + /// DateTime getRecurrenceId(int index) { + /// return appointments[index].recurrenceId; + /// } + /// ``` + Object? getRecurrenceId(int index) => null; + + /// Maps the custom appointments id to the [Appointment]. + /// + /// Allows to set the custom appointments corresponding property to the + /// [Appointment]'s id property. + /// + /// _Note:_ It is applicable only when the custom appointments set to the + /// [appointments]. + /// + /// See also: [Appointment.id]. + /// + /// ```dart + /// @override + /// DateTime getId(int index) { + /// return appointments[index].id; + /// } + /// ``` + Object? getId(int index) => null; + /// Called when loadMoreAppointments function is called from the /// loadMoreWidgetBuilder. /// Call the [notifyListeners] to notify the calendar for data source changes. @@ -482,7 +744,7 @@ abstract class CalendarDataSource extends CalendarDataSourceChangeNotifier { /// /// ```dart /// @override - /// void handleLoadMore(DateTime startDate, DateTime endDate){ + /// Future handleLoadMore(DateTime startDate, DateTime endDate) async { /// await Future.delayed(Duration(seconds: 5)); /// List newColl = []; /// for (DateTime date = startDate; @@ -496,7 +758,7 @@ abstract class CalendarDataSource extends CalendarDataSourceChangeNotifier { /// )); /// } /// - /// appointments.addAll(newColl); + /// appointments!.addAll(newColl); /// notifyListeners(CalendarDataSourceAction.add, newColl); /// } /// ``` diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/month_appointment_helper.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/month_appointment_helper.dart index 5453f6efd..40596647b 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/month_appointment_helper.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/month_appointment_helper.dart @@ -1,3 +1,4 @@ +import 'package:syncfusion_flutter_calendar/src/calendar/common/date_time_engine.dart'; import 'package:syncfusion_flutter_core/core.dart'; import '../common/calendar_view_helper.dart'; @@ -295,14 +296,7 @@ class MonthAppointmentHelper { for (int i = count - DateTime.daysPerWeek; i >= 0; i -= DateTime.daysPerWeek) { - DateTime currentDate = visibleDates[i]; - currentDate = DateTime( - currentDate.year, - currentDate.month, - currentDate.day, - currentDate.hour, - currentDate.minute, - currentDate.second); + final DateTime currentDate = visibleDates[i]; if (currentDate.isBefore(date) || (currentDate.day == date.day && currentDate.month == date.month && @@ -313,7 +307,8 @@ class MonthAppointmentHelper { } } - final DateTime endDateTime = addDays(dateTime, 6); + final DateTime endDateTime = + DateTimeHelper.getDateTimeValue(addDays(dateTime, 6)); int currentViewIndex = 0; while ( dateTime.isBefore(endDateTime) || isSameDate(dateTime, endDateTime)) { @@ -322,7 +317,7 @@ class MonthAppointmentHelper { } currentViewIndex++; - dateTime = addDays(dateTime, 1); + dateTime = DateTimeHelper.getDateTimeValue(addDays(dateTime, 1)); } return -1; diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/recurrence_helper.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/recurrence_helper.dart index aedb0436e..949e92566 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/recurrence_helper.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/recurrence_helper.dart @@ -1,4 +1,5 @@ import 'package:intl/intl.dart' show DateFormat; +import 'package:syncfusion_flutter_calendar/src/calendar/common/date_time_engine.dart'; import 'package:syncfusion_flutter_core/core.dart'; import '../common/enums.dart' show RecurrenceType, RecurrenceRange, WeekDays; @@ -10,7 +11,8 @@ class RecurrenceHelper { /// Check the recurrence appointment in between the visible date range. static bool _isRecurrenceInBetweenSpecificRange(DateTime appointmentDate, Duration duration, DateTime visibleStartDate, DateTime visibleEndTime) { - final DateTime appointmentEndDate = addDuration(appointmentDate, duration); + final DateTime appointmentEndDate = + DateTimeHelper.getDateTimeValue(addDuration(appointmentDate, duration)); /// ignore: lines_longer_than_80_chars return isDateWithInDateRange(visibleStartDate, visibleEndTime, appointmentDate) || @@ -20,412 +22,934 @@ class RecurrenceHelper { appointmentDate, appointmentEndDate, visibleStartDate); } - /// Returns the date time collection of recurring appointment. - static List getRecurrenceDateTimeCollection( + static List _getDailyRecurrenceDateTimeCollection( String rRule, DateTime recurrenceStartDate, {Duration? recurrenceDuration, DateTime? specificStartDate, DateTime? specificEndDate}) { + final List recDateCollection = []; + if (specificEndDate != null) { specificEndDate = DateTime(specificEndDate.year, specificEndDate.month, specificEndDate.day, 23, 59, 59); } recurrenceDuration ??= const Duration(); + final bool isSpecificDateRange = + specificStartDate != null && specificEndDate != null; + + if (isSpecificDateRange && recurrenceStartDate.isAfter(specificEndDate)) { + return recDateCollection; + } + + final int recurrenceStartHour = recurrenceStartDate.hour; + final int recurrenceStartMinute = recurrenceStartDate.minute; + final int recurrenceStartSecond = recurrenceStartDate.second; + + const List ruleSeparator = ['=', ';', ',']; + final List ruleArray = splitRule(rRule, ruleSeparator); + if (ruleArray.isEmpty) { + return recDateCollection; + } + + int recurrenceCount = 0; + final List values = _findKeyIndex(ruleArray); + + /// Assign only daily recurrence needed values. + final String recurrenceCountString = values[0]; + final String intervalCountString = values[8]; + final String untilValueString = values[10]; + if (recurrenceCountString.isNotEmpty) { + recurrenceCount = int.parse(recurrenceCountString); + } + + final int dailyDayGap = + rRule.contains('INTERVAL') ? int.parse(intervalCountString) : 1; + DateTime? endDate; + if (rRule.contains('UNTIL')) { + /// Set the end date value from until date value. + endDate = DateTime.parse(untilValueString); + endDate = DateTime(endDate.year, endDate.month, endDate.day, 23, 59, 59); + if (isSpecificDateRange) { + final DateTime startTime = DateTime( + endDate.year, + endDate.month, + endDate.day, + recurrenceStartHour, + recurrenceStartMinute, + recurrenceStartSecond); + final DateTime endTime = startTime.add(recurrenceDuration); + + /// Check the visible start date after of recurrence end date, if + /// true then recurrence the empty appointment collection. + if (specificStartDate.isAfter(endTime) && + !isSameDate(specificStartDate, endTime)) { + return recDateCollection; + } + } + } else if (rRule.contains('COUNT')) { + /// Set the end date value from recurrence start date with + /// count and interval values. + endDate = AppointmentHelper.addDaysWithTime( + recurrenceStartDate, + (recurrenceCount - 1) * dailyDayGap, + recurrenceStartHour, + recurrenceStartMinute, + recurrenceStartSecond); + + /// Return empty collection when the recurrence end date after of visible + /// start date. + final DateTime recurrenceEndDate = endDate.add(recurrenceDuration); + if (isSpecificDateRange && + specificStartDate.isAfter(recurrenceEndDate) && + !isSameDate(specificStartDate, recurrenceEndDate)) { + return recDateCollection; + } + + endDate = DateTime(endDate.year, endDate.month, endDate.day, 23, 59, 59); + } + + /// NoEndDate specified rule returns empty collection issue fix. + if (isSpecificDateRange) { + endDate = endDate == null || endDate.isAfter(specificEndDate) + ? specificEndDate + : endDate; + } + + int recurrenceIncrementCount = 0; + DateTime addDate = recurrenceStartDate; + + /// Calculate the initial start date when visible start date specified and + /// recurrence start date is before of visible start date + if (isSpecificDateRange && + recurrenceStartDate.isBefore(specificStartDate)) { + final DateTime recurrenceInitialDate = DateTime(recurrenceStartDate.year, + recurrenceStartDate.month, recurrenceStartDate.day); + final DateTime visibleInitialDate = DateTime(specificStartDate.year, + specificStartDate.month, specificStartDate.day); + + /// Total days difference between the visible start date and recurrence + /// start date. + final int difference = + visibleInitialDate.difference(recurrenceInitialDate).inDays; + final int dayDifference = difference % dailyDayGap; + + /// Valid recurrences in between the visible start date and recurrence + /// start date. + int incrementCount = difference ~/ dailyDayGap; + + /// If day difference is 0 then initial start date is visible start date + /// and check the previous recurrence date have long recurrence duration. + if (dayDifference == 0) { + addDate = DateTime( + specificStartDate.year, + specificStartDate.month, + specificStartDate.day, + recurrenceStartHour, + recurrenceStartMinute, + recurrenceStartSecond); + } else { + /// If day difference is not 0 then initial start date is after the + /// visible start date so calculate the recurrence from the date before + /// the visible start date. check the previous recurrence date have + /// long recurrence duration. + addDate = AppointmentHelper.addDaysWithTime( + visibleInitialDate, + -dayDifference, + recurrenceStartHour, + recurrenceStartMinute, + recurrenceStartSecond); + } + + final DateTime recurrenceEndDate = addDate.add(recurrenceDuration); + if (incrementCount > 0 && !isSameDate(addDate, recurrenceEndDate)) { + final int durationDifference = recurrenceEndDate.hour > addDate.hour + ? recurrenceDuration.inDays + : recurrenceDuration.inDays + 1; + final int intervalCount = + ((durationDifference ~/ dailyDayGap) * dailyDayGap) + + (durationDifference % dailyDayGap == 0 ? 0 : dailyDayGap); + addDate = AppointmentHelper.addDaysWithTime(addDate, -intervalCount, + recurrenceStartHour, recurrenceStartMinute, recurrenceStartSecond); + incrementCount -= intervalCount ~/ dailyDayGap; + } + + recurrenceIncrementCount = incrementCount; + + /// Reset to recurrence start date when the initial recurrence start date + /// before the recurrence start date + if (addDate.isBefore(recurrenceStartDate)) { + addDate = recurrenceStartDate; + } + + /// Reset the recurrence count to 0 when its value less than 0. + if (recurrenceIncrementCount < 0) { + recurrenceIncrementCount = 0; + } + } + + while (recurrenceIncrementCount < recurrenceCount || + (endDate != null && + (addDate.isBefore(endDate) || isSameDate(addDate, endDate)))) { + if (isSpecificDateRange) { + if (_isRecurrenceInBetweenSpecificRange( + addDate, recurrenceDuration, specificStartDate, specificEndDate)) { + recDateCollection.add(addDate); + } + + if (addDate.isAfter(specificEndDate)) { + break; + } + } else { + recDateCollection.add(addDate); + } + + addDate = AppointmentHelper.addDaysWithTime(addDate, dailyDayGap, + recurrenceStartHour, recurrenceStartMinute, recurrenceStartSecond); + recurrenceIncrementCount++; + } + + return recDateCollection; + } + + static List _getWeeklyRecurrenceDateTimeCollection( + String rRule, DateTime recurrenceStartDate, + {Duration? recurrenceDuration, + DateTime? specificStartDate, + DateTime? specificEndDate}) { final List recDateCollection = []; + + if (specificEndDate != null) { + specificEndDate = DateTime(specificEndDate.year, specificEndDate.month, + specificEndDate.day, 23, 59, 59); + } + + recurrenceDuration ??= const Duration(); final bool isSpecificDateRange = specificStartDate != null && specificEndDate != null; - final List ruleSeparator = ['=', ';', ',']; + + if (isSpecificDateRange && recurrenceStartDate.isAfter(specificEndDate)) { + return recDateCollection; + } + + const List ruleSeparator = ['=', ';', ',']; const String weeklySeparator = ';'; + const List weekDaysString = [ + 'SU', + 'MO', + 'TU', + 'WE', + 'TH', + 'FR', + 'SA' + ]; + final List ruleArray = splitRule(rRule, ruleSeparator); + if (ruleArray.isEmpty) { + return recDateCollection; + } + final List values = _findKeyIndex(ruleArray); + final String recurrenceCountString = values[0]; + final String intervalCountString = values[8]; + final String untilValueString = values[10]; + + final List weeklyRule = rRule.split(weeklySeparator); + final List weeklyByDayRules = _findWeeklyRule(weeklyRule); + final int weeklyByDayPos = + weeklyByDayRules.isNotEmpty ? int.parse(weeklyByDayRules[1]) : -1; + + final int recurrenceStartHour = recurrenceStartDate.hour; + final int recurrenceStartMinute = recurrenceStartDate.minute; + final int recurrenceStartSecond = recurrenceStartDate.second; + final int recurrenceCount = + recurrenceCountString.isNotEmpty ? int.parse(recurrenceCountString) : 0; + + int tempCount = 0; + final int weeklyWeekGap = ruleArray.length > 4 && rRule.contains('INTERVAL') + ? int.parse(intervalCountString) + : 1; + assert(weeklyByDayPos != -1, 'Invalid weekly recurrence rule'); + final List weekDays = []; + final String ruleDaysString = weeklyRule[weeklyByDayPos]; + for (int i = 0; i < weekDaysString.length; i++) { + if (!ruleDaysString.contains(weekDaysString[i])) { + continue; + } + + weekDays.add(i); + } + + weekDays.sort(); + final int weekDaysCount = weekDays.length; + assert(weekDaysCount != 0, 'Invalid weekly recurrence rule'); + + final int weekDay = recurrenceStartDate.weekday % DateTime.daysPerWeek; + DateTime? endDate; + if (rRule.contains('UNTIL')) { + /// Set the end date value from until date value. + endDate = DateTime.parse(untilValueString); + endDate = DateTime(endDate.year, endDate.month, endDate.day, 23, 59, 59); + if (isSpecificDateRange) { + final DateTime startTime = DateTime( + endDate.year, + endDate.month, + endDate.day, + recurrenceStartHour, + recurrenceStartMinute, + recurrenceStartSecond); + final DateTime endTime = startTime.add(recurrenceDuration); + + /// Check the visible start date after of recurrence end date, if + /// true then recurrence the empty appointment collection. + if (specificStartDate.isAfter(endTime) && + !isSameDate(specificStartDate, endTime)) { + return recDateCollection; + } + } + } else if (rRule.contains('COUNT')) { + int tempRecurrenceCount = recurrenceCount; + + int initialWeekDay = weekDay; + + /// Remove the recurrence on current week. + /// Eg., Week Mar 21 - 27 and recurrence start date is Mar 26 then remove + /// recurrence placed on Mar 26, 27 and start with Mar 28. + while (initialWeekDay < DateTime.daysPerWeek) { + if (weekDays.contains(initialWeekDay)) { + tempRecurrenceCount--; + } + + initialWeekDay++; + } + + /// Calculate the full weeks(sunday to saturday) occupies the recurrences. + /// Eg., if recurrence count is 20 each week have 3 occurrence then + /// full weeks count is 6. + final int totalWeeks = tempRecurrenceCount ~/ weekDaysCount; + + /// Calculate the remaining and in between weeks occurrences. + /// Eg., if recurrence count is 20 each week have 3 occurrence then + /// remaining count is 2. + int remainingCount = tempRecurrenceCount % weekDaysCount; + + /// Calculate the total days on full weeks. + int totalDays = totalWeeks * DateTime.daysPerWeek * weeklyWeekGap; + + /// Add initial week days from recurrence start date to week end and + /// add interval time after the initial week. + totalDays += DateTime.daysPerWeek - + weekDay + + (DateTime.daysPerWeek * (weeklyWeekGap - 1)); + + /// Calculate the next week occurrences and it failed when + /// initial week have occurrences more than count value + /// some times count value less than the by day specified count + /// Eg., StartTime Apr 28, 2021(WE) by day WE, TH, SA and count is 2 then + /// recurrence end date is Apr 29, 2021. In the above case remaining + /// count value goes to negative. + if (remainingCount != 0 && tempRecurrenceCount > 0) { + /// Calculate the in between week days. + int additionalDays = 0; + while (additionalDays < DateTime.daysPerWeek && remainingCount != 0) { + if (weekDays.contains(additionalDays % DateTime.daysPerWeek)) { + remainingCount--; + } + + additionalDays++; + } + + /// Above loop add additional 1 day because condition failed after + /// increment. so decrement the days count by 1. + totalDays += additionalDays - 1; + } else if (remainingCount != 0 && tempRecurrenceCount < 0) { + /// some times count value less than the by day specified count + /// Eg., StartTime Apr 28, 2021(WE) by day WE, TH, SA and count is 2 + /// then recurrence end date is Apr 29, 2021. In the above case + /// remaining count value goes to negative. + + /// Calculate the in between week days. + int additionalDays = weekDay; + int currentRecurrenceCount = recurrenceCount; + while (additionalDays < DateTime.daysPerWeek && + currentRecurrenceCount != 0) { + if (weekDays.contains(additionalDays)) { + currentRecurrenceCount--; + } + + additionalDays++; + } + + /// Above loop add additional 1 day because condition failed after + /// increment. so decrement the days count by 1. + totalDays = additionalDays - weekDay - 1; + if (totalDays < 0) { + totalDays = 0; + } + } else { + /// Calculate the exact end date of the week. + int additionalDays = 1; + while (additionalDays <= DateTime.daysPerWeek) { + /// Decrement the day and check the date week day is weekly recurrence + /// specified week day. If true then break and additionalDays variable + /// have the difference between the initial and indexed position. + if (weekDays.contains((DateTime.daysPerWeek - additionalDays) % 7)) { + break; + } + + additionalDays++; + } + + /// Subtract the total days by additionalDays and week interval because + /// the recurrence placed on first week so the interval does not have a + /// recurrences so remove the interval. + /// Eg., If the interval is 2 then the 1st week have occurrences and + /// 2nd week does not have occurrences. + totalDays -= + additionalDays + (DateTime.daysPerWeek * (weeklyWeekGap - 1)); + } + + endDate = AppointmentHelper.addDaysWithTime( + recurrenceStartDate, + totalDays, + recurrenceStartHour, + recurrenceStartMinute, + recurrenceStartSecond); + + /// Return empty collection when the recurrence end date after of visible + /// start date. + final DateTime recurrenceEndDate = endDate.add(recurrenceDuration); + if (isSpecificDateRange && + specificStartDate.isAfter(recurrenceEndDate) && + !isSameDate(specificStartDate, recurrenceEndDate)) { + return recDateCollection; + } + + endDate = DateTime(endDate.year, endDate.month, endDate.day, 23, 59, 59); + } + + /// NoEndDate specified rule returns empty collection issue fix. + if (isSpecificDateRange) { + endDate = endDate == null || endDate.isAfter(specificEndDate) + ? specificEndDate + : endDate; + } + + DateTime addDate = recurrenceStartDate; + if (isSpecificDateRange && + recurrenceStartDate.isBefore(specificStartDate)) { + final DateTime startDate = + DateTime(addDate.year, addDate.month, addDate.day); + + /// Calculate the total days between the recurrence start and visible + /// start date. + int daysDifference = specificStartDate.difference(startDate).inDays; + final DateTime recurrenceEndDate = addDate.add(recurrenceDuration); + + /// Calculate day difference between the recurrence start and end date. + final int durationDifference = isSameDate(recurrenceEndDate, addDate) + ? 0 + : recurrenceEndDate.hour > addDate.hour + ? recurrenceDuration.inDays + : recurrenceDuration.inDays + 1; + + /// Remove the duration day difference from total days difference because + /// if the recurrence placed saturday, sunday and it duration is 2 days + /// then saturday recurrence end with monday but the visible start date + /// from sunday. When recurrence duration does not considered then the + /// recurrence dates only have sunday date and it does not consider the + /// saturday date. + daysDifference -= durationDifference; + daysDifference = daysDifference < 0 ? 0 : daysDifference; + int tempRecurrenceCount = 0; + int initialWeekDay = weekDay; + + int initialWeekDaysCount = 0; + + /// Remove the initial week recurrences and days because if the + /// recurrence start date is tuesday then calculate the occurrences + /// from tuesday to saturday and remove it from total days count. + while (initialWeekDay < DateTime.daysPerWeek && + daysDifference > initialWeekDaysCount) { + if (weekDays.contains(initialWeekDay)) { + tempRecurrenceCount++; + } + + initialWeekDay++; + initialWeekDaysCount++; + } + + final bool isOccurrenceInInitialWeek = + initialWeekDaysCount + weekDay < DateTime.daysPerWeek; + daysDifference -= isOccurrenceInInitialWeek ? 0 : initialWeekDaysCount; + + /// Calculate and remove the interval week after the initial week on + /// total days difference. + final int initialWeekGap = isOccurrenceInInitialWeek + ? 0 + : DateTime.daysPerWeek * (weeklyWeekGap - 1); + daysDifference -= initialWeekGap; + + /// Calculate the total valid weeks(sunday to saturday) in between the + /// recurrence start and visible start date. + final int totalWeeks = + daysDifference ~/ (DateTime.daysPerWeek * weeklyWeekGap); + tempRecurrenceCount += totalWeeks * weekDaysCount; + + /// Calculate the valid week start date near to visible start date + /// by adding the valid weeks in between the recurrence start and visible + /// start date(totalWeeks * DateTime.daysPerWeek * weeklyWeekGap) and + /// recurrence start date week remaining days to week end + /// (DateTime.daysPerWeek - weekDay) + initialWeekGap. + addDate = AppointmentHelper.addDaysWithTime( + addDate, + (totalWeeks * DateTime.daysPerWeek * weeklyWeekGap) + + (isOccurrenceInInitialWeek + ? daysDifference + : DateTime.daysPerWeek - weekDay) + + initialWeekGap, + recurrenceStartHour, + recurrenceStartMinute, + recurrenceStartSecond); + + tempCount = tempRecurrenceCount; + } + + final bool isWeeklySelected = weeklyRule[weeklyByDayPos].length > 6; + + /// Below code modified for fixing issue while setting rule as + /// "FREQ=WEEKLY;COUNT=10;BYDAY=MO" along with specified start and end + /// dates. + while ((tempCount < recurrenceCount && isWeeklySelected) || + (endDate != null && + (addDate.isBefore(endDate) || addDate == endDate))) { + final bool isRecurrenceDate = + weekDays.contains(addDate.weekday % DateTime.daysPerWeek); + if (isSpecificDateRange) { + if (_isRecurrenceInBetweenSpecificRange(addDate, recurrenceDuration, + specificStartDate, specificEndDate) && + isRecurrenceDate) { + recDateCollection.add(addDate); + } + + if (addDate.isAfter(specificEndDate)) { + break; + } + } else if (isRecurrenceDate) { + recDateCollection.add(addDate); + } + + if (isRecurrenceDate) { + tempCount++; + } + + addDate = addDate.weekday == DateTime.saturday + ? AppointmentHelper.addDaysWithTime( + addDate, + ((weeklyWeekGap - 1) * DateTime.daysPerWeek) + 1, + recurrenceStartHour, + recurrenceStartMinute, + recurrenceStartSecond) + : AppointmentHelper.addDaysWithTime(addDate, 1, recurrenceStartHour, + recurrenceStartMinute, recurrenceStartSecond); + } + + return recDateCollection; + } + static List _getMonthlyRecurrenceDateTimeCollection( + String rRule, DateTime recurrenceStartDate, + {Duration? recurrenceDuration, + DateTime? specificStartDate, + DateTime? specificEndDate}) { + final List recDateCollection = []; + + if (specificEndDate != null) { + specificEndDate = DateTime(specificEndDate.year, specificEndDate.month, + specificEndDate.day, 23, 59, 59); + } + + recurrenceDuration ??= const Duration(); + final bool isSpecificDateRange = + specificStartDate != null && specificEndDate != null; + + if (isSpecificDateRange && recurrenceStartDate.isAfter(specificEndDate)) { + return recDateCollection; + } + + final List ruleSeparator = ['=', ';', ',']; final List ruleArray = splitRule(rRule, ruleSeparator); - int weeklyByDayPos = -1; + if (ruleArray.isEmpty) { + return recDateCollection; + } + int recCount = 0; final List values = _findKeyIndex(ruleArray); final String recurCount = values[0]; - final String daily = values[1]; - final String weekly = values[2]; - final String monthly = values[3]; - final String yearly = values[4]; final String bySetPosCount = values[6]; - final String interval = values[7]; final String intervalCount = values[8]; final String untilValue = values[10]; final String byDay = values[12]; final String byDayValue = values[13]; final String byMonthDay = values[14]; final String byMonthDayCount = values[15]; - final String byMonthCount = values[17]; - final List weeklyRule = rRule.split(weeklySeparator); - final List weeklyRules = _findWeeklyRule(weeklyRule); - if (weeklyRules.isNotEmpty) { - weeklyByDayPos = int.parse(weeklyRules[1]); + + final int recurrenceStartHour = recurrenceStartDate.hour; + final int recurrenceStartMinute = recurrenceStartDate.minute; + final int recurrenceStartSecond = recurrenceStartDate.second; + DateTime addDate = recurrenceStartDate; + if (recurCount.isNotEmpty) { + recCount = int.parse(recurCount); } - if (ruleArray.isNotEmpty && rRule.isNotEmpty) { - final int recurrenceStartHour = recurrenceStartDate.hour; - final int recurrenceStartMinute = recurrenceStartDate.minute; - final int recurrenceStartSecond = recurrenceStartDate.second; - DateTime addDate = recurrenceStartDate; - if (recurCount.isNotEmpty) { - recCount = int.parse(recurCount); + final int monthlyMonthGap = ruleArray.length > 4 && intervalCount.isNotEmpty + ? int.parse(intervalCount) + : 1; + DateTime? endDate; + if (rRule.contains('UNTIL')) { + endDate = DateTime.parse(untilValue); + endDate = DateTime(endDate.year, endDate.month, endDate.day, 23, 59, 59); + if (isSpecificDateRange) { + final DateTime startTime = DateTime( + endDate.year, + endDate.month, + endDate.day, + recurrenceStartHour, + recurrenceStartMinute, + recurrenceStartSecond); + final DateTime endTime = startTime.add(recurrenceDuration); + + /// Check the visible start date after of recurrence end date, if + /// true then recurrence the empty appointment collection. + if (specificStartDate.isAfter(endTime) && + !isSameDate(specificStartDate, endTime)) { + return recDateCollection; + } } + } - DateTime? endDate; - if (rRule.contains('UNTIL')) { - endDate = DateTime.parse(untilValue); - endDate = DateTime(endDate.year, endDate.month, endDate.day, 23, 59, 0); - if (isSpecificDateRange) { - endDate = - (endDate.isAfter(specificEndDate) || endDate == specificEndDate) - ? specificEndDate - : endDate; + /// NoEndDate specified rule returns empty collection issue fix. + if (isSpecificDateRange && !rRule.contains('COUNT')) { + endDate = endDate == null || endDate.isAfter(specificEndDate) + ? specificEndDate + : endDate; + + final int addedDateMonth = addDate.month; + final int addedDateYear = addDate.year; + final int viewStartDateMonth = specificStartDate.month; + final int viewStartDateYear = specificStartDate.year; + if (addedDateYear < viewStartDateYear || + (viewStartDateMonth >= addedDateMonth && + viewStartDateYear == addedDateYear)) { + /// Calculate the total months between the recurrence start date and + /// visible start date. + final int totalMonths = (viewStartDateMonth - addedDateMonth) + + ((viewStartDateYear - addedDateYear) * 12); + + /// Calculate the valid month count between the recurrence start date + /// and visible start date. + final int validMonths = totalMonths ~/ monthlyMonthGap; + addDate = DateTime( + addedDateYear, addedDateMonth + (validMonths * monthlyMonthGap)); + if (addDate.isBefore(recurrenceStartDate)) { + addDate = recurrenceStartDate; } } + } - /// NoEndDate specified rule returns empty collection issue fix. - if (isSpecificDateRange && !rRule.contains('COUNT') && endDate == null) { - endDate = specificEndDate; - } - - if (daily == 'DAILY') { - if (!rRule.contains('BYDAY')) { - final int dailyDayGap = - !rRule.contains('INTERVAL') ? 1 : int.parse(intervalCount); - int tempCount = 0; - while (tempCount < recCount || - (endDate != null && - (addDate.isBefore(endDate) || - isSameDate(addDate, endDate)))) { - if (isSpecificDateRange) { - if (_isRecurrenceInBetweenSpecificRange(addDate, - recurrenceDuration, specificStartDate, specificEndDate)) { - recDateCollection.add(addDate); - } - } else { - recDateCollection.add(addDate); - } - - addDate = AppointmentHelper.addDaysWithTime( - addDate, - dailyDayGap, - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond); - tempCount++; - } - } else { - while (recDateCollection.length < recCount || - (endDate != null && - (addDate.isBefore(endDate) || addDate == endDate))) { - if (addDate.weekday != DateTime.sunday && - addDate.weekday != DateTime.saturday) { - if (isSpecificDateRange) { - if (_isRecurrenceInBetweenSpecificRange(addDate, - recurrenceDuration, specificStartDate, specificEndDate)) { - recDateCollection.add(addDate); - } - } else { - recDateCollection.add(addDate); - } - } + if (byMonthDay == 'BYMONTHDAY') { + final int monthDate = int.parse(byMonthDayCount); + final DateTime temp = DateTime(addDate.year, addDate.month, monthDate, + recurrenceStartHour, recurrenceStartMinute, recurrenceStartSecond); + + /// Check the month date greater than recurrence start date and the + /// month have the date value. + /// Eg., Recurrence start date as Feb 28 and recurrence month day as + /// 30 then check the 30 greater than 28 and feb have 30 date. + if (temp.day == monthDate && + (temp.isAfter(recurrenceStartDate) || + isSameDate(temp, recurrenceStartDate))) { + addDate = temp; + } else { + /// Check the month date less than recurrence start date or the + /// month does not have the date + /// Eg., Recurrence start date as Feb 28 and recurrence month day as + /// 30 and feb 30 does not exist so move the recurrence to next + /// month and check next month have date 30 if exists then start the + /// recurrence from mar 30. + addDate = DateTime(addDate.year, addDate.month + monthlyMonthGap, 1, + recurrenceStartHour, recurrenceStartMinute, recurrenceStartSecond); + final DateTime tempDate = DateTime( + addDate.year, + addDate.month, + monthDate, + recurrenceStartHour, + recurrenceStartMinute, + recurrenceStartSecond); + if (tempDate.day == monthDate) { + addDate = tempDate; + } + } - addDate = AppointmentHelper.addDaysWithTime( - addDate, - 1, - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond); - } + final int yearValue = addDate.year; + int monthValue = addDate.month; + int tempCount = 0; + while (tempCount < recCount || + (endDate != null && + (addDate.isBefore(endDate) || addDate == endDate))) { + if (addDate.day != monthDate) { + /// Check the month date day equal to updated date day value because + /// if we create the date time for February 30 then the date return + /// March 1 or 2 based on leap year. + monthValue += monthlyMonthGap; + addDate = DateTime( + yearValue, + monthValue, + monthDate, + recurrenceStartHour, + recurrenceStartMinute, + recurrenceStartSecond); + continue; } - } else if (weekly == 'WEEKLY') { - int tempCount = 0; - final int weeklyWeekGap = ruleArray.length > 4 && interval == 'INTERVAL' - ? int.parse(intervalCount) - : 1; - assert(weeklyByDayPos != -1, 'Invalid weekly recurrence rule'); - final bool isWeeklySelected = weeklyRule[weeklyByDayPos].length > 6; - - /// Below code modified for fixing issue while setting rule as - /// "FREQ=WEEKLY;COUNT=10;BYDAY=MO" along with specified start and end - /// dates. - while ((tempCount < recCount && isWeeklySelected) || - (endDate != null && - (addDate.isBefore(endDate) || addDate == endDate))) { - if (isSpecificDateRange) { - if (_isRecurrenceInBetweenSpecificRange(addDate, recurrenceDuration, - specificStartDate, specificEndDate)) { - _setWeeklyRecurrenceDate( - addDate, weeklyRule, weeklyByDayPos, recDateCollection); - } - if (addDate.isAfter(specificEndDate)) { - break; - } - } else { - _setWeeklyRecurrenceDate( - addDate, weeklyRule, weeklyByDayPos, recDateCollection); + if (isSpecificDateRange) { + if (_isRecurrenceInBetweenSpecificRange(addDate, recurrenceDuration, + specificStartDate, specificEndDate)) { + recDateCollection.add(addDate); } - if (_isRecurrenceDate(addDate, weeklyRule, weeklyByDayPos)) { - tempCount++; + if (addDate.isAfter(specificEndDate)) { + break; } + } else { + recDateCollection.add(addDate); + } + + monthValue += monthlyMonthGap; + addDate = DateTime(yearValue, monthValue, monthDate, + recurrenceStartHour, recurrenceStartMinute, recurrenceStartSecond); - addDate = addDate.weekday == DateTime.saturday - ? AppointmentHelper.addDaysWithTime( - addDate, - ((weeklyWeekGap - 1) * DateTime.daysPerWeek) + 1, - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond) - : AppointmentHelper.addDaysWithTime( - addDate, - 1, - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond); + tempCount++; + } + } else if (byDay == 'BYDAY') { + int tempCount = 0; + final int nthWeekDay = _getWeekDay(byDayValue); + final int bySetPosValue = int.parse(bySetPosCount); + + void _updateValidDate() { + final DateTime monthStart = DateTime(addDate.year, addDate.month, 1, + recurrenceStartHour, recurrenceStartMinute, recurrenceStartSecond); + final int monthStartWeekday = monthStart.weekday % DateTime.daysPerWeek; + final DateTime weekStartDate = AppointmentHelper.addDaysWithTime( + monthStart, + -monthStartWeekday, + recurrenceStartHour, + recurrenceStartMinute, + recurrenceStartSecond); + int nthWeek = bySetPosValue; + if (monthStartWeekday <= nthWeekDay) { + nthWeek = bySetPosValue - 1; } - } else if (monthly == 'MONTHLY') { - final int monthlyMonthGap = - ruleArray.length > 4 && interval == 'INTERVAL' - ? int.parse(intervalCount) - : 1; - if (byMonthDay == 'BYMONTHDAY') { - final int monthDate = int.parse(byMonthDayCount); - final int currDate = int.parse(recurrenceStartDate.day.toString()); - final DateTime temp = DateTime( - addDate.year, - addDate.month, - monthDate, + + if (bySetPosValue.isNegative) { + addDate = _getRecurrenceDateForNegativeValue( + bySetPosValue, monthStart, nthWeekDay); + } else { + addDate = AppointmentHelper.addDaysWithTime( + weekStartDate, + (nthWeek * DateTime.daysPerWeek) + nthWeekDay, recurrenceStartHour, recurrenceStartMinute, recurrenceStartSecond); + } + } - /// Check the month date greater than recurrence start date and the - /// month have the date value. - /// Eg., Recurrence start date as Feb 28 and recurrence month day as - /// 30 then check the 30 greater than 28 and feb have 30 date. - if (monthDate >= currDate && temp.day == monthDate) { - addDate = temp; - } else { - /// Check the month date less than recurrence start date or the - /// month does not have the date - /// Eg., Recurrence start date as Feb 28 and recurrence month day as - /// 30 and feb 30 does not exist so move the recurrence to next - /// month and check next month have date 30 if exists then start the - /// recurrence from mar 30. - addDate = DateTime( - addDate.year, - addDate.month + 1, - 1, - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond); - final DateTime tempDate = DateTime( - addDate.year, - addDate.month, - monthDate, - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond); - if (tempDate.day == monthDate) { - addDate = tempDate; - } + _updateValidDate(); + if (addDate.isBefore(recurrenceStartDate)) { + addDate = DateTime(addDate.year, addDate.month + monthlyMonthGap, 1, + recurrenceStartHour, recurrenceStartMinute, recurrenceStartSecond); + _updateValidDate(); + } + + while (tempCount < recCount || + (endDate != null && + (addDate.isBefore(endDate) || addDate == endDate))) { + if (isSpecificDateRange) { + if (_isRecurrenceInBetweenSpecificRange(addDate, recurrenceDuration, + specificStartDate, specificEndDate)) { + recDateCollection.add(addDate); } - final int yearValue = addDate.year; - int monthValue = addDate.month; - int tempCount = 0; - while (tempCount < recCount || - (endDate != null && - (addDate.isBefore(endDate) || addDate == endDate))) { - if (addDate.day != monthDate) { - monthValue += monthlyMonthGap; - addDate = DateTime( - yearValue, - monthValue, - monthDate, - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond); - continue; - } + if (addDate.isAfter(specificEndDate)) { + break; + } + } else { + recDateCollection.add(addDate); + } - if (isSpecificDateRange) { - if (_isRecurrenceInBetweenSpecificRange(addDate, - recurrenceDuration, specificStartDate, specificEndDate)) { - recDateCollection.add(addDate); - } + addDate = DateTime(addDate.year, addDate.month + monthlyMonthGap, 1, + recurrenceStartHour, recurrenceStartMinute, recurrenceStartSecond); + _updateValidDate(); + tempCount++; + } + } - if (addDate.isAfter(specificEndDate)) { - break; - } - } else { - recDateCollection.add(addDate); - } + return recDateCollection; + } - monthValue += monthlyMonthGap; - addDate = DateTime( - yearValue, - monthValue, - monthDate, - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond); + static List _getYearlyRecurrenceDateTimeCollection( + String rRule, DateTime recurrenceStartDate, + {Duration? recurrenceDuration, + DateTime? specificStartDate, + DateTime? specificEndDate}) { + final List recDateCollection = []; + + if (specificEndDate != null) { + specificEndDate = DateTime(specificEndDate.year, specificEndDate.month, + specificEndDate.day, 23, 59, 59); + } + + recurrenceDuration ??= const Duration(); + final bool isSpecificDateRange = + specificStartDate != null && specificEndDate != null; + + if (isSpecificDateRange && recurrenceStartDate.isAfter(specificEndDate)) { + return recDateCollection; + } + + final List ruleSeparator = ['=', ';', ',']; + final List ruleArray = splitRule(rRule, ruleSeparator); + if (ruleArray.isEmpty) { + return recDateCollection; + } + + int recCount = 0; + final List values = _findKeyIndex(ruleArray); + final String recurCount = values[0]; + final String bySetPosCount = values[6]; + final String intervalCount = values[8]; + final String untilValue = values[10]; + final String byDay = values[12]; + final String byDayValue = values[13]; + final String byMonthDay = values[14]; + final String byMonthDayCount = values[15]; + final String byMonthCount = values[17]; - tempCount++; - } - } else if (byDay == 'BYDAY') { - int tempRecDateCollectionCount = 0; - while (recDateCollection.length < recCount || - (endDate != null && - (addDate.isBefore(endDate) || addDate == endDate))) { - final DateTime monthStart = DateTime( - addDate.year, - addDate.month, - 1, - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond); - final DateTime weekStartDate = AppointmentHelper.addDaysWithTime( - monthStart, - -monthStart.weekday, - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond); - final int monthStartWeekday = monthStart.weekday; - final int nthWeekDay = _getWeekDay(byDayValue); - int nthWeek; - if (monthStartWeekday <= nthWeekDay) { - nthWeek = int.parse(bySetPosCount) - 1; - } else { - nthWeek = int.parse(bySetPosCount); - } + final int recurrenceStartHour = recurrenceStartDate.hour; + final int recurrenceStartMinute = recurrenceStartDate.minute; + final int recurrenceStartSecond = recurrenceStartDate.second; + DateTime addDate = recurrenceStartDate; + if (recurCount.isNotEmpty) { + recCount = int.parse(recurCount); + } - final int bySetPosValue = int.parse(bySetPosCount); - if (bySetPosValue.isNegative) { - addDate = _getRecurrenceDateForNegativeValue( - bySetPosValue, monthStart, nthWeekDay); - } else { - addDate = AppointmentHelper.addDaysWithTime( - weekStartDate, - (nthWeek * DateTime.daysPerWeek) + nthWeekDay, - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond); - } + DateTime? endDate; + if (rRule.contains('UNTIL')) { + endDate = DateTime.parse(untilValue); + endDate = DateTime(endDate.year, endDate.month, endDate.day, 23, 59, 59); + if (isSpecificDateRange) { + final DateTime startTime = DateTime( + endDate.year, + endDate.month, + endDate.day, + recurrenceStartHour, + recurrenceStartMinute, + recurrenceStartSecond); + final DateTime endTime = startTime.add(recurrenceDuration); + + /// Check the visible start date after of recurrence end date, if + /// true then recurrence the empty appointment collection. + if (specificStartDate.isAfter(endTime) && + !isSameDate(specificStartDate, endTime)) { + return recDateCollection; + } + } + } - if (addDate.isBefore(recurrenceStartDate)) { - addDate = DateTime( - addDate.year, - addDate.month + 1, - addDate.day, - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond); - continue; - } + final int yearlyYearGap = ruleArray.length > 4 && intervalCount.isNotEmpty + ? int.parse(intervalCount) + : 1; + + /// NoEndDate specified rule returns empty collection issue fix. + if (isSpecificDateRange && !rRule.contains('COUNT')) { + endDate = endDate == null || endDate.isAfter(specificEndDate) + ? specificEndDate + : endDate; + + final int addedDateYear = addDate.year; + final int viewStartDateYear = specificStartDate.year; + if (addedDateYear < viewStartDateYear) { + /// Calculate the valid year count between the recurrence start date + /// and visible start date. + final int inBetweenYears = + (viewStartDateYear - addedDateYear) ~/ yearlyYearGap; + addDate = DateTime(addedDateYear + (inBetweenYears * yearlyYearGap)); + if (addDate.isBefore(recurrenceStartDate)) { + addDate = recurrenceStartDate; + } + } + } - if (isSpecificDateRange) { - if (_isRecurrenceInBetweenSpecificRange(addDate, - recurrenceDuration, specificStartDate, specificEndDate) && - (tempRecDateCollectionCount < recCount || - rRule.contains('UNTIL'))) { - recDateCollection.add(addDate); - } + if (byMonthDay == 'BYMONTHDAY') { + final int monthIndex = int.parse(byMonthCount); + final int dayIndex = int.parse(byMonthDayCount); + if (monthIndex < 0 || monthIndex > 12) { + return recDateCollection; + } - if (addDate.isAfter(specificEndDate)) { - break; - } - } else { - recDateCollection.add(addDate); - } + final int daysInMonth = DateTimeHelper.getDateTimeValue( + addDays(DateTime(addDate.year, addDate.month + 1, 1), -1)) + .day; + if (daysInMonth < dayIndex) { + return recDateCollection; + } - addDate = DateTime( - addDate.year, - addDate.month + monthlyMonthGap, - addDate.day - (addDate.day - 1), - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond); + final DateTime specificDate = DateTime(addDate.year, monthIndex, dayIndex, + recurrenceStartHour, recurrenceStartMinute, recurrenceStartSecond); + if (specificDate.isBefore(recurrenceStartDate)) { + addDate = DateTime( + specificDate.year + yearlyYearGap, + specificDate.month, + specificDate.day, + recurrenceStartHour, + recurrenceStartMinute, + recurrenceStartSecond); + } else { + addDate = specificDate; + } - tempRecDateCollectionCount++; + int tempCount = 0; + while (tempCount < recCount || + (endDate != null && + (addDate.isBefore(endDate) || addDate == endDate))) { + if (isSpecificDateRange) { + if (_isRecurrenceInBetweenSpecificRange(addDate, recurrenceDuration, + specificStartDate, specificEndDate)) { + recDateCollection.add(addDate); } - } - } else if (yearly == 'YEARLY') { - final int yearlyYearGap = ruleArray.length > 4 && interval == 'INTERVAL' - ? int.parse(intervalCount) - : 1; - if (byMonthDay == 'BYMONTHDAY') { - final int monthIndex = int.parse(byMonthCount); - final int dayIndex = int.parse(byMonthDayCount); - if (monthIndex > 0 && monthIndex <= 12) { - final int bound = - addDays(DateTime(addDate.year, addDate.month + 1, 1), -1).day; - if (bound >= dayIndex) { - final DateTime specificDate = DateTime( - addDate.year, - monthIndex, - dayIndex, - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond); - if (specificDate.isBefore(addDate)) { - addDate = specificDate; - addDate = DateTime( - addDate.year + 1, - addDate.month, - addDate.day, - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond); - if (isSpecificDateRange) { - if (_isRecurrenceInBetweenSpecificRange(addDate, - recurrenceDuration, specificStartDate, specificEndDate)) { - recDateCollection.add(addDate); - } - } else { - recDateCollection.add(addDate); - } - } else { - addDate = specificDate; - } - int tempCount = 0; - while (tempCount < recCount || - (endDate != null && - (addDate.isBefore(endDate) || addDate == endDate))) { - if (!recDateCollection.contains(addDate)) { - if (isSpecificDateRange) { - if (_isRecurrenceInBetweenSpecificRange( - addDate, - recurrenceDuration, - specificStartDate, - specificEndDate)) { - recDateCollection.add(addDate); - } - - if (addDate.isAfter(specificEndDate)) { - break; - } - } else { - recDateCollection.add(addDate); - } - } - - addDate = DateTime( - addDate.year + yearlyYearGap, - addDate.month, - addDate.day, - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond); - tempCount++; - } - } + if (addDate.isAfter(specificEndDate)) { + break; } - } else if (byDay == 'BYDAY') { - final int monthIndex = int.parse(byMonthCount); + } else { + recDateCollection.add(addDate); + } + + addDate = DateTime( + addDate.year + yearlyYearGap, + addDate.month, + addDate.day, + recurrenceStartHour, + recurrenceStartMinute, + recurrenceStartSecond); + tempCount++; + } + } else if (byDay == 'BYDAY') { + int tempCount = 0; + final int monthIndex = int.parse(byMonthCount); + final int bySetPosValue = int.parse(bySetPosCount); + final int nthWeekDay = _getWeekDay(byDayValue); + + void _updateValidNextDate() { + while (true) { DateTime monthStart = DateTime( addDate.year, monthIndex, @@ -433,22 +957,19 @@ class RecurrenceHelper { recurrenceStartHour, recurrenceStartMinute, recurrenceStartSecond); - DateTime weekStartDate = AppointmentHelper.addDaysWithTime( + final int monthStartWeekday = + monthStart.weekday % DateTime.daysPerWeek; + final DateTime weekStartDate = AppointmentHelper.addDaysWithTime( monthStart, - -monthStart.weekday, + -monthStartWeekday, recurrenceStartHour, recurrenceStartMinute, recurrenceStartSecond); - int monthStartWeekday = monthStart.weekday; - int nthWeekDay = _getWeekDay(byDayValue); - int nthWeek; + int nthWeek = bySetPosValue; if (monthStartWeekday <= nthWeekDay) { - nthWeek = int.parse(bySetPosCount) - 1; - } else { - nthWeek = int.parse(bySetPosCount); + nthWeek = bySetPosValue - 1; } - final int bySetPosValue = int.parse(bySetPosCount); if (bySetPosValue.isNegative) { monthStart = _getRecurrenceDateForNegativeValue( bySetPosValue, monthStart, nthWeekDay); @@ -461,179 +982,110 @@ class RecurrenceHelper { recurrenceStartSecond); } - if ((monthStart.month != addDate.month && - monthStart.isBefore(addDate)) || - (monthStart.month == addDate.month && - (monthStart.isBefore(addDate) || - monthStart.isBefore(recurrenceStartDate)))) { + if (monthStart.month != monthIndex || + monthStart.isBefore(recurrenceStartDate)) { addDate = DateTime( - addDate.year + 1, - addDate.month, - addDate.day, - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond); - monthStart = DateTime( - addDate.year, - monthIndex, - 1, - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond); - weekStartDate = AppointmentHelper.addDaysWithTime( - monthStart, - -monthStart.weekday, + monthStart.year + yearlyYearGap, + monthStart.month, + monthStart.day, recurrenceStartHour, recurrenceStartMinute, recurrenceStartSecond); - monthStartWeekday = monthStart.weekday; - nthWeekDay = _getWeekDay(byDayValue); - if (monthStartWeekday <= nthWeekDay) { - nthWeek = int.parse(bySetPosCount) - 1; - } else { - nthWeek = int.parse(bySetPosCount); - } - - final int bySetPosValue = int.parse(bySetPosCount); - if (bySetPosValue.isNegative) { - monthStart = _getRecurrenceDateForNegativeValue( - bySetPosValue, monthStart, nthWeekDay); - } else { - monthStart = AppointmentHelper.addDaysWithTime( - weekStartDate, - (nthWeek * DateTime.daysPerWeek) + nthWeekDay, - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond); - } + continue; + } - addDate = monthStart; + addDate = monthStart; + break; + } + } - if (!recDateCollection.contains(addDate)) { - if (isSpecificDateRange) { - if (_isRecurrenceInBetweenSpecificRange(addDate, - recurrenceDuration, specificStartDate, specificEndDate)) { - recDateCollection.add(addDate); - } - } else { - recDateCollection.add(addDate); - } - } - } else { - addDate = monthStart; + _updateValidNextDate(); + while (tempCount < recCount || + (endDate != null && + (addDate.isBefore(endDate) || addDate == endDate))) { + if (isSpecificDateRange) { + if (_isRecurrenceInBetweenSpecificRange(addDate, recurrenceDuration, + specificStartDate, specificEndDate)) { + recDateCollection.add(addDate); } - int tempCount = 0; - while (tempCount < recCount || - (endDate != null && - (addDate.isBefore(endDate) || addDate == endDate))) { - if (!recDateCollection.contains(addDate)) { - if (isSpecificDateRange) { - if (_isRecurrenceInBetweenSpecificRange(addDate, - recurrenceDuration, specificStartDate, specificEndDate)) { - recDateCollection.add(addDate); - } - - if (addDate.isAfter(specificEndDate)) { - break; - } - } else { - recDateCollection.add(addDate); - } - } - - addDate = DateTime( - addDate.year + yearlyYearGap, - addDate.month, - addDate.day, - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond); + if (addDate.isAfter(specificEndDate)) { + break; + } + } else { + recDateCollection.add(addDate); + } - monthStart = DateTime( - addDate.year, - monthIndex, - 1, - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond); + addDate = DateTime( + addDate.year + yearlyYearGap, + addDate.month, + addDate.day, + recurrenceStartHour, + recurrenceStartMinute, + recurrenceStartSecond); - weekStartDate = AppointmentHelper.addDaysWithTime( - monthStart, - -monthStart.weekday, - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond); - monthStartWeekday = monthStart.weekday; - nthWeekDay = _getWeekDay(byDayValue); - if (monthStartWeekday <= nthWeekDay) { - nthWeek = int.parse(bySetPosCount) - 1; - } else { - nthWeek = int.parse(bySetPosCount); - } + tempCount++; + _updateValidNextDate(); + } + } - final int bySetPosValue = int.parse(bySetPosCount); - if (bySetPosValue.isNegative) { - monthStart = _getRecurrenceDateForNegativeValue( - bySetPosValue, monthStart, nthWeekDay); - } else { - monthStart = AppointmentHelper.addDaysWithTime( - weekStartDate, - (nthWeek * DateTime.daysPerWeek) + nthWeekDay, - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond); - } + return recDateCollection; + } - if (monthStart.month != addDate.month && - monthStart.isBefore(addDate)) { - addDate = monthStart; - addDate = DateTime( - addDate.year + 1, - addDate.month, - addDate.day, - recurrenceStartHour, - recurrenceStartMinute, - recurrenceStartSecond); - if (!recDateCollection.contains(addDate)) { - if (isSpecificDateRange) { - if (_isRecurrenceInBetweenSpecificRange(addDate, - recurrenceDuration, specificStartDate, specificEndDate)) { - recDateCollection.add(addDate); - } - - if (addDate.isAfter(specificEndDate)) { - break; - } - } else { - recDateCollection.add(addDate); - } - } - } else { - addDate = monthStart; - } + /// Returns the date time collection of recurring appointment. + static List getRecurrenceDateTimeCollection( + String rRule, DateTime recurrenceStartDate, + {Duration? recurrenceDuration, + DateTime? specificStartDate, + DateTime? specificEndDate}) { + /// Return empty collection when recurrence rule is empty. + if (rRule.isEmpty) { + return []; + } - tempCount++; - } - } - } + if (rRule.contains('DAILY')) { + return _getDailyRecurrenceDateTimeCollection(rRule, recurrenceStartDate, + recurrenceDuration: recurrenceDuration, + specificStartDate: specificStartDate, + specificEndDate: specificEndDate); + } else if (rRule.contains('WEEKLY')) { + return _getWeeklyRecurrenceDateTimeCollection(rRule, recurrenceStartDate, + recurrenceDuration: recurrenceDuration, + specificStartDate: specificStartDate, + specificEndDate: specificEndDate); + } else if (rRule.contains('MONTHLY')) { + return _getMonthlyRecurrenceDateTimeCollection(rRule, recurrenceStartDate, + recurrenceDuration: recurrenceDuration, + specificStartDate: specificStartDate, + specificEndDate: specificEndDate); + } else if (rRule.contains('YEARLY')) { + return _getYearlyRecurrenceDateTimeCollection(rRule, recurrenceStartDate, + recurrenceDuration: recurrenceDuration, + specificStartDate: specificStartDate, + specificEndDate: specificEndDate); } - return recDateCollection; + return []; } /// Returns the recurrence properties based on the given recurrence rule and /// the recurrence start date. static RecurrenceProperties parseRRule(String rRule, DateTime recStartDate) { - final DateTime recurrenceStartDate = recStartDate; final RecurrenceProperties recProp = RecurrenceProperties(startDate: recStartDate); + + if (rRule.isEmpty) { + return recProp; + } + final List ruleSeparator = ['=', ';', ',']; - const String weeklySeparator = ';'; final List ruleArray = splitRule(rRule, ruleSeparator); + if (ruleArray.isEmpty) { + return recProp; + } + + const String weeklySeparator = ';'; final List weeklyRule = rRule.split(weeklySeparator); - int weeklyByDayPos = -1; - int recCount = 0; final List resultList = _findKeyIndex(ruleArray); final String recurCount = resultList[0]; final String daily = resultList[1]; @@ -649,144 +1101,79 @@ class RecurrenceHelper { final String byMonthDayCount = resultList[15]; final String byMonthCount = resultList[17]; final List weeklyRules = _findWeeklyRule(weeklyRule); - if (weeklyRules.isNotEmpty) { - weeklyByDayPos = int.parse(weeklyRules[1]); + final int weeklyByDayPos = + weeklyRules.isNotEmpty ? int.parse(weeklyRules[1]) : -1; + + if (rRule.contains('COUNT')) { + recProp.recurrenceRange = RecurrenceRange.count; + recProp.recurrenceCount = + recurCount.isNotEmpty ? int.parse(recurCount) : 0; + } else if (rRule.contains('UNTIL')) { + recProp.recurrenceRange = RecurrenceRange.endDate; + final DateTime endDate = DateTime.parse(untilValue); + recProp.endDate = + DateTime(endDate.year, endDate.month, endDate.day, 23, 59, 59); + } else { + recProp.recurrenceRange = RecurrenceRange.noEndDate; } - if (ruleArray.isNotEmpty && rRule.isNotEmpty) { - DateTime addDate = recurrenceStartDate; - if (recurCount.isNotEmpty) { - recCount = int.parse(recurCount); - } - - if (!rRule.contains('COUNT') && !rRule.contains('UNTIL')) { - recProp.recurrenceRange = RecurrenceRange.noEndDate; - } else if (rRule.contains('COUNT')) { - recProp.recurrenceRange = RecurrenceRange.count; - recProp.recurrenceCount = recCount; - } else if (rRule.contains('UNTIL')) { - recProp.recurrenceRange = RecurrenceRange.endDate; - DateTime endDate = DateTime.parse(untilValue); - endDate = - DateTime(endDate.year, endDate.month, endDate.day, 23, 59, 59); - recProp.endDate = endDate; - } - - recProp.interval = - intervalCount.isNotEmpty ? int.parse(intervalCount) : 1; - if (daily == 'DAILY') { - recProp.recurrenceType = RecurrenceType.daily; - - if (rRule.contains('BYDAY')) { - recProp.weekDays = [ - WeekDays.monday, - WeekDays.tuesday, - WeekDays.wednesday, - WeekDays.thursday, - WeekDays.friday - ]; - } - } else if (weekly == 'WEEKLY') { - recProp.recurrenceType = RecurrenceType.weekly; - int i = 0; - - while (i < DateTime.daysPerWeek && weeklyByDayPos != -1) { - switch (addDate.weekday) { - case DateTime.sunday: - { - if (weeklyRule[weeklyByDayPos].contains('SU')) { - recProp.weekDays.add(WeekDays.sunday); - } - - break; - } - - case DateTime.monday: - { - if (weeklyRule[weeklyByDayPos].contains('MO')) { - recProp.weekDays.add(WeekDays.monday); - } - - break; - } - - case DateTime.tuesday: - { - if (weeklyRule[weeklyByDayPos].contains('TU')) { - recProp.weekDays.add(WeekDays.tuesday); - } - - break; - } - - case DateTime.wednesday: - { - if (weeklyRule[weeklyByDayPos].contains('WE')) { - recProp.weekDays.add(WeekDays.wednesday); - } - - break; - } + recProp.interval = intervalCount.isNotEmpty ? int.parse(intervalCount) : 1; + if (daily == 'DAILY') { + recProp.recurrenceType = RecurrenceType.daily; + } else if (weekly == 'WEEKLY') { + recProp.recurrenceType = RecurrenceType.weekly; - case DateTime.thursday: - { - if (weeklyRule[weeklyByDayPos].contains('TH')) { - recProp.weekDays.add(WeekDays.thursday); - } + if (weeklyByDayPos == -1) { + return recProp; + } - break; - } + final String weeklyByDayString = weeklyRule[weeklyByDayPos]; + if (weeklyByDayString.contains('SU')) { + recProp.weekDays.add(WeekDays.sunday); + } + if (weeklyByDayString.contains('MO')) { + recProp.weekDays.add(WeekDays.monday); + } - case DateTime.friday: - { - if (weeklyRule[weeklyByDayPos].contains('FR')) { - recProp.weekDays.add(WeekDays.friday); - } + if (weeklyByDayString.contains('TU')) { + recProp.weekDays.add(WeekDays.tuesday); + } - break; - } + if (weeklyByDayString.contains('WE')) { + recProp.weekDays.add(WeekDays.wednesday); + } - case DateTime.saturday: - { - if (weeklyRule[weeklyByDayPos].contains('SA')) { - recProp.weekDays.add(WeekDays.saturday); - } + if (weeklyByDayString.contains('TH')) { + recProp.weekDays.add(WeekDays.thursday); + } - break; - } - } + if (weeklyByDayString.contains('FR')) { + recProp.weekDays.add(WeekDays.friday); + } - addDate = addDate.weekday == 6 - ? addDays( - addDate, ((recProp.interval - 1) * DateTime.daysPerWeek) + 1) - : addDays(addDate, 1); - i = i + 1; - } - } else if (monthly == 'MONTHLY') { - recProp.recurrenceType = RecurrenceType.monthly; - if (byMonthDay == 'BYMONTHDAY') { - recProp.week = 0; - recProp.dayOfMonth = - byMonthDayCount.isNotEmpty ? int.parse(byMonthDayCount) : 1; - } else if (byDay == 'BYDAY') { - recProp.week = - bySetPosCount.isNotEmpty ? int.parse(bySetPosCount) : 0; - recProp.dayOfWeek = - byDayValue.isNotEmpty ? _getWeekDay(byDayValue) : 1; - } - } else if (yearly == 'YEARLY') { - recProp.recurrenceType = RecurrenceType.yearly; - if (byMonthDay == 'BYMONTHDAY') { - recProp.month = byMonthCount.isNotEmpty ? int.parse(byMonthCount) : 1; - recProp.dayOfMonth = - byMonthDayCount.isNotEmpty ? int.parse(byMonthDayCount) : 1; - } else if (byDay == 'BYDAY') { - recProp.month = byMonthCount.isNotEmpty ? int.parse(byMonthCount) : 1; - recProp.week = - bySetPosCount.isNotEmpty ? int.parse(bySetPosCount) : 0; - recProp.dayOfWeek = - byDayValue.isNotEmpty ? _getWeekDay(byDayValue) : 1; - } + if (weeklyByDayString.contains('SA')) { + recProp.weekDays.add(WeekDays.saturday); + } + } else if (monthly == 'MONTHLY') { + recProp.recurrenceType = RecurrenceType.monthly; + if (byMonthDay == 'BYMONTHDAY') { + recProp.week = 0; + recProp.dayOfMonth = + byMonthDayCount.isNotEmpty ? int.parse(byMonthDayCount) : 1; + } else if (byDay == 'BYDAY') { + recProp.week = bySetPosCount.isNotEmpty ? int.parse(bySetPosCount) : 0; + recProp.dayOfWeek = byDayValue.isNotEmpty ? _getWeekDay(byDayValue) : 1; + } + } else if (yearly == 'YEARLY') { + recProp.recurrenceType = RecurrenceType.yearly; + if (byMonthDay == 'BYMONTHDAY') { + recProp.month = byMonthCount.isNotEmpty ? int.parse(byMonthCount) : 1; + recProp.dayOfMonth = + byMonthDayCount.isNotEmpty ? int.parse(byMonthDayCount) : 1; + } else if (byDay == 'BYDAY') { + recProp.month = byMonthCount.isNotEmpty ? int.parse(byMonthCount) : 1; + recProp.week = bySetPosCount.isNotEmpty ? int.parse(bySetPosCount) : 0; + recProp.dayOfWeek = byDayValue.isNotEmpty ? _getWeekDay(byDayValue) : 1; } } @@ -808,25 +1195,12 @@ class RecurrenceHelper { (startDate.isBefore(endDate!) || startDate == endDate))) { rRule = 'FREQ=DAILY'; - if (recurrenceProperties.weekDays.contains(WeekDays.monday) && - recurrenceProperties.weekDays.contains(WeekDays.tuesday) && - recurrenceProperties.weekDays.contains(WeekDays.wednesday) && - recurrenceProperties.weekDays.contains(WeekDays.thursday) && - recurrenceProperties.weekDays.contains(WeekDays.friday)) { - if (diffTimeSpan.inHours > 24) { - isValidRecurrence = false; - } - - rRule = rRule + ';BYDAY=MO,TU,WE,TH,FR'; - } else { - if (diffTimeSpan.inHours >= recurrenceProperties.interval * 24) { - isValidRecurrence = false; - } + if (diffTimeSpan.inHours >= recurrenceProperties.interval * 24) { + isValidRecurrence = false; + } - if (recurrenceProperties.interval > 0) { - rRule = - rRule + ';INTERVAL=' + recurrenceProperties.interval.toString(); - } + if (recurrenceProperties.interval > 0) { + rRule = rRule + ';INTERVAL=' + recurrenceProperties.interval.toString(); } if (recurrenceProperties.recurrenceRange == RecurrenceRange.count) { @@ -1087,8 +1461,8 @@ class RecurrenceHelper { rRule = rRule + ';BYMONTHDAY=' + recurrenceProperties.dayOfMonth.toString(); } else { - final DateTime firstDate = - addDays(DateTime.now(), -(DateTime.now().weekday - 1)); + final DateTime firstDate = DateTimeHelper.getDateTimeValue( + addDays(DateTime.now(), -(DateTime.now().weekday - 1))); final List dayNames = List.generate(DateTime.daysPerWeek, (int index) => index) .map((int value) => DateFormat(DateFormat.ABBR_WEEKDAY) @@ -1160,8 +1534,8 @@ class RecurrenceHelper { ';BYMONTH=' + recurrenceProperties.month.toString(); } else { - final DateTime firstDate = - addDays(DateTime.now(), -(DateTime.now().weekday - 1)); + final DateTime firstDate = DateTimeHelper.getDateTimeValue( + addDays(DateTime.now(), -(DateTime.now().weekday - 1))); final List dayNames = List.generate(DateTime.daysPerWeek, (int index) => index) .map((int value) => DateFormat(DateFormat.ABBR_WEEKDAY) @@ -1226,7 +1600,7 @@ class RecurrenceHelper { final Duration diffTimeSpan = appEndTime.difference(appStartTime); int recCount = 0; final DateTime prevDate = DateTime.utc(1); - final bool isValidRecurrence = true; + const bool isValidRecurrence = true; recCount = recurrenceProperties.recurrenceCount; switch (recurrenceProperties.recurrenceType) { @@ -1374,8 +1748,8 @@ class RecurrenceHelper { static int _getWeekDay(String weekDay) { int index = 1; - final DateTime firstDate = - addDays(DateTime.now(), -(DateTime.now().weekday - 1)); + final DateTime firstDate = DateTimeHelper.getDateTimeValue( + addDays(DateTime.now(), -(DateTime.now().weekday - 1))); final List dayNames = List.generate(DateTime.daysPerWeek, (int index) => index) .map((int value) => DateFormat(DateFormat.ABBR_WEEKDAY) @@ -1419,148 +1793,6 @@ class RecurrenceHelper { return result; } - static bool _isRecurrenceDate( - DateTime addDate, List weeklyRule, int weeklyByDayPos) { - bool isRecurrenceDate = false; - switch (addDate.weekday) { - case DateTime.sunday: - { - if (weeklyRule[weeklyByDayPos].contains('SU')) { - isRecurrenceDate = true; - } - - break; - } - - case DateTime.monday: - { - if (weeklyRule[weeklyByDayPos].contains('MO')) { - isRecurrenceDate = true; - } - - break; - } - - case DateTime.tuesday: - { - if (weeklyRule[weeklyByDayPos].contains('TU')) { - isRecurrenceDate = true; - } - - break; - } - - case DateTime.wednesday: - { - if (weeklyRule[weeklyByDayPos].contains('WE')) { - isRecurrenceDate = true; - } - - break; - } - - case DateTime.thursday: - { - if (weeklyRule[weeklyByDayPos].contains('TH')) { - isRecurrenceDate = true; - } - - break; - } - - case DateTime.friday: - { - if (weeklyRule[weeklyByDayPos].contains('FR')) { - isRecurrenceDate = true; - } - - break; - } - - case DateTime.saturday: - { - if (weeklyRule[weeklyByDayPos].contains('SA')) { - isRecurrenceDate = true; - } - - break; - } - } - - return isRecurrenceDate; - } - - static void _setWeeklyRecurrenceDate( - DateTime addDate, - List weeklyRule, - int weeklyByDayPos, - List recDateCollection) { - switch (addDate.weekday) { - case DateTime.sunday: - { - if (weeklyRule[weeklyByDayPos].contains('SU')) { - recDateCollection.add(addDate); - } - - break; - } - - case DateTime.monday: - { - if (weeklyRule[weeklyByDayPos].contains('MO')) { - recDateCollection.add(addDate); - } - - break; - } - - case DateTime.tuesday: - { - if (weeklyRule[weeklyByDayPos].contains('TU')) { - recDateCollection.add(addDate); - } - - break; - } - - case DateTime.wednesday: - { - if (weeklyRule[weeklyByDayPos].contains('WE')) { - recDateCollection.add(addDate); - } - - break; - } - - case DateTime.thursday: - { - if (weeklyRule[weeklyByDayPos].contains('TH')) { - recDateCollection.add(addDate); - } - - break; - } - - case DateTime.friday: - { - if (weeklyRule[weeklyByDayPos].contains('FR')) { - recDateCollection.add(addDate); - } - - break; - } - - case DateTime.saturday: - { - if (weeklyRule[weeklyByDayPos].contains('SA')) { - recDateCollection.add(addDate); - } - - break; - } - } - } - /// Returns the recurrence start date for negative recurrence value. static DateTime _getRecurrenceDateForNegativeValue( int bySetPosCount, DateTime date, int weekDay) { @@ -1569,7 +1801,7 @@ class RecurrenceHelper { lastDate = AppointmentHelper.getMonthEndDate(date); } else if (bySetPosCount == -2) { lastDate = AppointmentHelper.getMonthEndDate(date) - .subtract(Duration(days: DateTime.daysPerWeek)); + .subtract(const Duration(days: DateTime.daysPerWeek)); } if (lastDate == null) { diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/recurrence_properties.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/recurrence_properties.dart index 8a0271dae..095c0a482 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/recurrence_properties.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_engine/recurrence_properties.dart @@ -2,6 +2,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:syncfusion_flutter_datepicker/datepicker.dart' show IterableDiagnostics; + +import '../common/calendar_view_helper.dart'; import '../common/enums.dart'; /// Recurrence properties allows to create recurrence rule for an [Appointment]. @@ -28,7 +30,8 @@ import '../common/enums.dart'; /// /// DataSource _getCalendarDataSource() { /// List appointments = []; -/// RecurrenceProperties recurrence = new RecurrenceProperties(); +/// RecurrenceProperties recurrence = +/// RecurrenceProperties(startDate: DateTime.now()); /// recurrence.recurrenceType = RecurrenceType.daily; /// recurrence.interval = 2; /// recurrence.recurrenceRange = RecurrenceRange.count; @@ -43,7 +46,7 @@ import '../common/enums.dart'; /// color: Colors.blue, /// startTimeZone: '', /// endTimeZone: '', -/// recurrenceRule: RecurrenceHelper.rRuleGenerator(recurrence, +/// recurrenceRule: SfCalendar.generateRRule(recurrence, /// DateTime.now(), DateTime.now().add(Duration(hours: 2))) /// )); /// @@ -58,7 +61,7 @@ class RecurrenceProperties with Diagnosticable { /// [SfCalendar.generateRRule] method. RecurrenceProperties( {this.recurrenceType = RecurrenceType.daily, - this.recurrenceCount = 1, + this.recurrenceCount = 0, required this.startDate, this.endDate, this.interval = 1, @@ -68,7 +71,17 @@ class RecurrenceProperties with Diagnosticable { this.dayOfMonth = 1, this.dayOfWeek = 1, this.month = 1}) - : this.weekDays = weekDays ?? []; + : weekDays = weekDays ?? [], + assert(recurrenceCount >= 0), + assert(endDate == null || + CalendarViewHelper.isSameOrBeforeDateTime(endDate, startDate)), + assert(endDate == null || + CalendarViewHelper.isSameOrAfterDateTime(startDate, endDate)), + assert(interval >= 1), + assert(week >= -2 && week <= 5), + assert(dayOfMonth >= 1 && dayOfMonth <= 31), + assert(dayOfWeek >= 1 && dayOfWeek <= 7), + assert(month >= 1 && month <= 12); /// Defines the recurrence type of an [Appointment]. /// @@ -94,7 +107,8 @@ class RecurrenceProperties with Diagnosticable { /// /// DataSource _getCalendarDataSource() { /// List appointments = []; - /// RecurrenceProperties recurrence = new RecurrenceProperties(); + /// RecurrenceProperties recurrence = + /// RecurrenceProperties(startDate: DateTime.now()); /// recurrence.recurrenceType = RecurrenceType.daily; /// recurrence.interval = 2; /// recurrence.recurrenceRange = RecurrenceRange.count; @@ -109,7 +123,7 @@ class RecurrenceProperties with Diagnosticable { /// color: Colors.blue, /// startTimeZone: '', /// endTimeZone: '', - /// recurrenceRule: RecurrenceHelper.rRuleGenerator(recurrence, + /// recurrenceRule: SfCalendar.generateRRule(recurrence, /// DateTime.now(), DateTime.now().add(Duration(hours: 2))) /// )); /// @@ -147,7 +161,8 @@ class RecurrenceProperties with Diagnosticable { /// /// DataSource _getCalendarDataSource() { /// List appointments = []; - /// RecurrenceProperties recurrence = new RecurrenceProperties(); + /// RecurrenceProperties recurrence = + /// RecurrenceProperties(startDate: DateTime.now()); /// recurrence.recurrenceType = RecurrenceType.daily; /// recurrence.interval = 2; /// recurrence.recurrenceRange = RecurrenceRange.count; @@ -162,7 +177,7 @@ class RecurrenceProperties with Diagnosticable { /// color: Colors.blue, /// startTimeZone: '', /// endTimeZone: '', - /// recurrenceRule: RecurrenceHelper.rRuleGenerator(recurrence, + /// recurrenceRule: SfCalendar.generateRRule(recurrence, /// DateTime.now(), DateTime.now().add(Duration(hours: 2))) /// )); /// @@ -193,7 +208,8 @@ class RecurrenceProperties with Diagnosticable { /// /// DataSource _getCalendarDataSource() { /// List appointments = []; - /// RecurrenceProperties recurrence = new RecurrenceProperties(); + /// RecurrenceProperties recurrence = + /// RecurrenceProperties(startDate: DateTime.now()); /// recurrence.recurrenceType = RecurrenceType.daily; /// recurrence.startDate = DateTime.now().add(Duration(days: 1)); /// recurrence.interval = 2; @@ -209,7 +225,7 @@ class RecurrenceProperties with Diagnosticable { /// color: Colors.blue, /// startTimeZone: '', /// endTimeZone: '', - /// recurrenceRule: RecurrenceHelper.rRuleGenerator(recurrence, + /// recurrenceRule: SfCalendar.generateRRule(recurrence, /// DateTime.now(), DateTime.now().add(Duration(hours: 2))) /// )); /// @@ -246,7 +262,8 @@ class RecurrenceProperties with Diagnosticable { /// /// DataSource _getCalendarDataSource() { /// List appointments = []; - /// RecurrenceProperties recurrence = new RecurrenceProperties(); + /// RecurrenceProperties recurrence = + /// RecurrenceProperties(startDate: DateTime.now()); /// recurrence.recurrenceType = RecurrenceType.daily; /// recurrence.interval = 2; /// recurrence.endDate = DateTime.now().add(Duration(days: 10)); @@ -261,7 +278,7 @@ class RecurrenceProperties with Diagnosticable { /// color: Colors.blue, /// startTimeZone: '', /// endTimeZone: '', - /// recurrenceRule: RecurrenceHelper.rRuleGenerator(recurrence, + /// recurrenceRule: SfCalendar.generateRRule(recurrence, /// DateTime.now(), DateTime.now().add(Duration(hours: 2))) /// )); /// @@ -295,7 +312,8 @@ class RecurrenceProperties with Diagnosticable { /// /// DataSource _getCalendarDataSource() { /// List appointments = []; - /// RecurrenceProperties recurrence = new RecurrenceProperties(); + /// RecurrenceProperties recurrence = + /// RecurrenceProperties(startDate: DateTime.now()); /// recurrence.recurrenceType = RecurrenceType.daily; /// recurrence.interval = 2; /// recurrence.endDate = DateTime.now().add(Duration(days: 10)); @@ -310,7 +328,7 @@ class RecurrenceProperties with Diagnosticable { /// color: Colors.blue, /// startTimeZone: '', /// endTimeZone: '', - /// recurrenceRule: RecurrenceHelper.rRuleGenerator(recurrence, + /// recurrenceRule: SfCalendar.generateRRule(recurrence, /// DateTime.now(), DateTime.now().add(Duration(hours: 2))) /// )); /// @@ -343,7 +361,8 @@ class RecurrenceProperties with Diagnosticable { /// /// DataSource _getCalendarDataSource() { /// List appointments = []; - /// RecurrenceProperties recurrence = new RecurrenceProperties(); + /// RecurrenceProperties recurrence = + /// RecurrenceProperties(startDate: DateTime.now()); /// recurrence.recurrenceType = RecurrenceType.daily; /// recurrence.interval = 2; /// recurrence.endDate = DateTime.now().add(Duration(days: 10)); @@ -358,7 +377,7 @@ class RecurrenceProperties with Diagnosticable { /// color: Colors.blue, /// startTimeZone: '', /// endTimeZone: '', - /// recurrenceRule: RecurrenceHelper.rRuleGenerator(recurrence, + /// recurrenceRule: SfCalendar.generateRRule(recurrence, /// DateTime.now(), DateTime.now().add(Duration(hours: 2))) /// )); /// @@ -390,8 +409,9 @@ class RecurrenceProperties with Diagnosticable { /// /// DataSource _getCalendarDataSource() { /// List appointments = []; - /// RecurrenceProperties recurrence = new RecurrenceProperties(); - /// List days = List(); + /// RecurrenceProperties recurrence = + /// RecurrenceProperties(startDate: DateTime.now()); + /// List days = []; /// days.add(WeekDays.monday); /// days.add(WeekDays.wednesday); /// days.add(WeekDays.friday); @@ -410,7 +430,7 @@ class RecurrenceProperties with Diagnosticable { /// color: Colors.blue, /// startTimeZone: '', /// endTimeZone: '', - /// recurrenceRule: RecurrenceHelper.rRuleGenerator(recurrence, + /// recurrenceRule: SfCalendar.generateRRule(recurrence, /// DateTime.now(), DateTime.now().add(Duration(hours: 2))) /// )); /// @@ -442,7 +462,8 @@ class RecurrenceProperties with Diagnosticable { /// /// DataSource _getCalendarDataSource() { /// List appointments = []; - /// RecurrenceProperties recurrence = new RecurrenceProperties(); + /// RecurrenceProperties recurrence = + /// RecurrenceProperties(startDate: DateTime.now()); /// recurrence.week = 4; /// recurrence.recurrenceType = RecurrenceType.monthly; /// recurrence.interval = 1; @@ -457,7 +478,7 @@ class RecurrenceProperties with Diagnosticable { /// color: Colors.blue, /// startTimeZone: '', /// endTimeZone: '', - /// recurrenceRule: RecurrenceHelper.rRuleGenerator(recurrence, + /// recurrenceRule: SfCalendar.generateRRule(recurrence, /// DateTime.now(), DateTime.now().add(Duration(hours: 2))) /// )); /// @@ -489,7 +510,8 @@ class RecurrenceProperties with Diagnosticable { /// /// DataSource _getCalendarDataSource() { /// List appointments = []; - /// RecurrenceProperties recurrence = new RecurrenceProperties(); + /// RecurrenceProperties recurrence = + /// RecurrenceProperties(startDate: DateTime.now()); /// recurrence.recurrenceType = RecurrenceType.monthly; /// recurrence.dayOfMonth = 15; /// recurrence.interval = 1; @@ -504,7 +526,7 @@ class RecurrenceProperties with Diagnosticable { /// color: Colors.blue, /// startTimeZone: '', /// endTimeZone: '', - /// recurrenceRule: RecurrenceHelper.rRuleGenerator(recurrence, + /// recurrenceRule: SfCalendar.generateRRule(recurrence, /// DateTime.now(), DateTime.now().add(Duration(hours: 2))) /// )); /// @@ -537,7 +559,8 @@ class RecurrenceProperties with Diagnosticable { /// /// DataSource _getCalendarDataSource() { /// List appointments = []; - /// RecurrenceProperties recurrence = new RecurrenceProperties(); + /// RecurrenceProperties recurrence = + /// RecurrenceProperties(startDate: DateTime.now()); /// recurrence.dayOfWeek = 7; /// recurrence.week = 4; /// recurrence.recurrenceType = RecurrenceType.monthly; @@ -553,7 +576,7 @@ class RecurrenceProperties with Diagnosticable { /// color: Colors.blue, /// startTimeZone: '', /// endTimeZone: '', - /// recurrenceRule: RecurrenceHelper.rRuleGenerator(recurrence, + /// recurrenceRule: SfCalendar.generateRRule(recurrence, /// DateTime.now(), DateTime.now().add(Duration(hours: 2))) /// )); /// @@ -587,7 +610,8 @@ class RecurrenceProperties with Diagnosticable { /// /// DataSource _getCalendarDataSource() { /// List appointments = []; - /// RecurrenceProperties recurrence = new RecurrenceProperties(); + /// RecurrenceProperties recurrence = + /// RecurrenceProperties(startDate: DateTime.now()); /// recurrence.recurrenceType = RecurrenceType.yearly; /// recurrence.dayOfMonth = 15; /// recurrence.month = 12; @@ -603,7 +627,7 @@ class RecurrenceProperties with Diagnosticable { /// color: Colors.blue, /// startTimeZone: '', /// endTimeZone: '', - /// recurrenceRule: RecurrenceHelper.rRuleGenerator(recurrence, + /// recurrenceRule: SfCalendar.generateRRule(recurrence, /// DateTime.now(), DateTime.now().add(Duration(hours: 2))) /// )); /// @@ -613,6 +637,7 @@ class RecurrenceProperties with Diagnosticable { int month; @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(dynamic other) { if (identical(this, other)) { return true; @@ -621,7 +646,10 @@ class RecurrenceProperties with Diagnosticable { return false; } - final RecurrenceProperties recurrenceProperties = other; + late final RecurrenceProperties recurrenceProperties; + if (other is RecurrenceProperties) { + recurrenceProperties = other; + } return recurrenceProperties.recurrenceType == recurrenceType && recurrenceProperties.recurrenceCount == recurrenceCount && recurrenceProperties.startDate == startDate && @@ -636,6 +664,7 @@ class RecurrenceProperties with Diagnosticable { } @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes int get hashCode { return hashValues( recurrenceType, @@ -644,7 +673,7 @@ class RecurrenceProperties with Diagnosticable { endDate, interval, recurrenceRange, - weekDays, + hashList(weekDays), week, dayOfMonth, dayOfWeek, diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_layout/agenda_view_layout.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_layout/agenda_view_layout.dart index 9a81ea512..38eed9c53 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_layout/agenda_view_layout.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_layout/agenda_view_layout.dart @@ -17,7 +17,7 @@ import '../settings/schedule_view_settings.dart'; class AgendaViewLayout extends StatefulWidget { /// Constructor to create the agenda appointment layout that holds the agenda /// appointment views in calendar widget. - AgendaViewLayout( + const AgendaViewLayout( this.monthViewSettings, this.scheduleViewSettings, this.selectedDate, @@ -90,11 +90,11 @@ class AgendaViewLayout extends StatefulWidget { class _AgendaViewLayoutState extends State { /// It holds the appointment views for the visible appointments. - List _appointmentCollection = []; + final List _appointmentCollection = []; /// It holds the children of the widget, it holds empty when /// appointment builder is null. - List _children = []; + final List _children = []; @override void initState() { @@ -131,7 +131,9 @@ class _AgendaViewLayoutState extends State { } final CalendarAppointmentDetails details = CalendarAppointmentDetails( widget.selectedDate!, - List.unmodifiable([view.appointment!.data ?? view.appointment!]), + List.unmodifiable([ + CalendarViewHelper.getAppointmentDetail(view.appointment!) + ]), view.appointmentRect!.outerRect); final Widget child = widget.appointmentBuilder!(context, details); _children.add(RepaintBoundary(child: child)); @@ -174,9 +176,7 @@ class _AgendaViewLayoutState extends State { } final bool isLargerScheduleUI = - widget.scheduleViewSettings == null || useMobilePlatformUI - ? false - : true; + widget.scheduleViewSettings != null && !useMobilePlatformUI; widget.appointments!.sort( (CalendarAppointment app1, CalendarAppointment app2) => @@ -315,8 +315,7 @@ class _AgendaViewRenderWidget extends MultiChildRenderObjectWidget { } } -class _AgendaViewRenderObject extends RenderBox - with ContainerRenderObjectMixin { +class _AgendaViewRenderObject extends CustomCalendarRenderObject { _AgendaViewRenderObject( this._monthViewSettings, this._scheduleViewSettings, @@ -581,8 +580,8 @@ class _AgendaViewRenderObject extends RenderBox /// Ref: assembleSemanticsNode method in RenderParagraph class /// (https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/rendering/paragraph.dart) List? _cacheNodes; - Paint _rectPainter = Paint(); - TextPainter _textPainter = TextPainter(); + final Paint _rectPainter = Paint(); + final TextPainter _textPainter = TextPainter(); /// attach will called when the render object rendered in view. @override @@ -599,15 +598,37 @@ class _AgendaViewRenderObject extends RenderBox } @override - void setupParentData(RenderObject child) { - if (child.parentData is! CalendarParentData) { - child.parentData = CalendarParentData(); + bool hitTestChildren(BoxHitTestResult result, {required Offset position}) { + RenderBox? child = firstChild; + if (child == null) { + return false; } - } - @override - bool hitTestSelf(Offset position) { - return true; + for (int i = 0; i < appointmentCollection.length; i++) { + final AppointmentView appointmentView = appointmentCollection[i]; + if (appointmentView.appointment == null || + child == null || + appointmentView.appointmentRect == null) { + continue; + } + + final Offset offset = Offset(appointmentView.appointmentRect!.left, + appointmentView.appointmentRect!.top); + final bool isHit = result.addWithPaintOffset( + offset: offset, + position: position, + hitTest: (BoxHitTestResult result, Offset? transformed) { + assert(transformed == position - offset); + return child!.hitTest(result, position: transformed!); + }, + ); + if (isHit) { + return true; + } + child = childAfter(child); + } + + return false; } @override @@ -629,6 +650,10 @@ class _AgendaViewRenderObject extends RenderBox maxHeight: appointmentView.appointmentRect!.height, minWidth: appointmentView.appointmentRect!.width, maxWidth: appointmentView.appointmentRect!.width)); + final CalendarParentData childParentData = + child.parentData! as CalendarParentData; + childParentData.offset = Offset(appointmentView.appointmentRect!.left, + appointmentView.appointmentRect!.top); child = childAfter(child); } } @@ -636,13 +661,13 @@ class _AgendaViewRenderObject extends RenderBox @override void paint(PaintingContext context, Offset offset) { RenderBox? child = firstChild; - final bool _isNeedDefaultPaint = childCount == 0; - final double totalAgendaViewWidth = size.width + _timeLabelWidth; + final bool isNeedDefaultPaint = childCount == 0; + final double totalAgendaViewWidth = size.width + timeLabelWidth; final bool useMobilePlatformUI = CalendarViewHelper.isMobileLayoutUI( totalAgendaViewWidth, isMobilePlatform); final bool isLargerScheduleUI = - scheduleViewSettings == null || useMobilePlatformUI ? false : true; - if (_isNeedDefaultPaint) { + scheduleViewSettings != null && !useMobilePlatformUI; + if (isNeedDefaultPaint) { _drawDefaultUI(context.canvas, isLargerScheduleUI, offset); } else { const double padding = 5.0; @@ -655,7 +680,7 @@ class _AgendaViewRenderObject extends RenderBox } final RRect rect = appointmentView.appointmentRect!.shift(offset); - child.paint(context, Offset(rect.left, rect.top)); + context.paintChild(child, Offset(rect.left, rect.top)); if (agendaViewNotifier.value != null && isSameDate(agendaViewNotifier.value!.hoveringDate, selectedDate)) { _addMouseHovering( @@ -724,11 +749,6 @@ class _AgendaViewRenderObject extends RenderBox _cacheNodes = null; } - @override - void visitChildrenForSemantics(RenderObjectVisitor visitor) { - return; - } - List _getSemanticsBuilder(Size size) { final List semanticsBuilder = []; @@ -785,7 +805,8 @@ class _AgendaViewRenderObject extends RenderBox final TextStyle appointmentTextStyle = monthViewSettings != null ? monthViewSettings!.agendaStyle.appointmentTextStyle ?? - TextStyle(color: Colors.white, fontSize: 13, fontFamily: 'Roboto') + const TextStyle( + color: Colors.white, fontSize: 13, fontFamily: 'Roboto') : scheduleViewSettings!.appointmentTextStyle ?? TextStyle( color: isLargerScheduleUI && @@ -827,6 +848,7 @@ class _AgendaViewRenderObject extends RenderBox final bool isRecurrenceAppointment = appointment.recurrenceRule != null && appointment.recurrenceRule!.isNotEmpty; + final double textSize = _getTextSize(rect, appointmentTextStyle, isMobilePlatform); @@ -851,7 +873,7 @@ class _AgendaViewRenderObject extends RenderBox final TextSpan icon = AppointmentHelper.getSpanIcon( appointmentTextStyle.color!, isMobilePlatform ? textSize : textSize / 1.5, - isRTL ? false : true); + !isRTL); _drawIcon(canvas, size, textSize, rect, padding, isLargerScheduleUI, rect.tlRadius, icon, appointmentHeight, topPadding, true, false); } @@ -903,9 +925,9 @@ class _AgendaViewRenderObject extends RenderBox } } - if (isRecurrenceAppointment) { + if (isRecurrenceAppointment || appointment.recurrenceId != null) { final TextSpan icon = AppointmentHelper.getRecurrenceIcon( - appointmentTextStyle.color!, textSize); + appointmentTextStyle.color!, textSize, isRecurrenceAppointment); _drawIcon( canvas, size, @@ -1090,7 +1112,7 @@ class _AgendaViewRenderObject extends RenderBox Radius cornerRadius) { final TextSpan span = TextSpan( text: AppointmentHelper.getSpanAppointmentText( - appointment, selectedDate!, _localizations), + appointment, selectedDate!, localizations), style: appointmentTextStyle); _updateTextPainterProperties(span); @@ -1121,7 +1143,7 @@ class _AgendaViewRenderObject extends RenderBox final TextSpan icon = AppointmentHelper.getSpanIcon( appointmentTextStyle.color!, isMobilePlatform ? textSize : textSize / 1.5, - isRTL ? false : true); + !isRTL); _drawIcon(canvas, size, textSize, rect, padding, isLargerScheduleUI, cornerRadius, icon, appointmentHeight, topPadding, true, false); return topPadding; @@ -1198,7 +1220,7 @@ class _AgendaViewRenderObject extends RenderBox circleWidth; } - final double topPadding = ((appointmentHeight - _textPainter.height) / 2); + final double topPadding = (appointmentHeight - _textPainter.height) / 2; _textPainter.paint( canvas, Offset(xPosition + padding, yPosition + topPadding)); final DateFormat format = @@ -1241,7 +1263,7 @@ class _AgendaViewRenderObject extends RenderBox rect.top + viewPadding, size.width - (isRTL ? viewPadding : padding), rect.height - (2 * viewPadding)), - Radius.circular(4)), + const Radius.circular(4)), _rectPainter); } else { _rectPainter.color = diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_layout/allday_appointment_layout.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_layout/allday_appointment_layout.dart index a66e9f480..bde90cc7d 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_layout/allday_appointment_layout.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_layout/allday_appointment_layout.dart @@ -16,7 +16,7 @@ import '../sfcalendar.dart'; class AllDayAppointmentLayout extends StatefulWidget { /// Constructor to create the all day appointment layout that holds the /// all day appointment views in calendar widget. - AllDayAppointmentLayout( + const AllDayAppointmentLayout( this.calendar, this.view, this.visibleDates, @@ -116,7 +116,7 @@ class _AllDayAppointmentLayoutState extends State { /// It holds the children of the widget, it holds empty when /// appointment builder is null. - List _children = []; + final List _children = []; @override void initState() { @@ -167,9 +167,9 @@ class _AllDayAppointmentLayoutState extends State { context, CalendarAppointmentDetails( date, - List.unmodifiable([ - appointmentView.appointment!.data ?? - appointmentView.appointment! + List.unmodifiable([ + CalendarViewHelper.getAppointmentDetail( + appointmentView.appointment!) ]), Rect.fromLTWH( appointmentView.appointmentRect!.left, @@ -212,7 +212,7 @@ class _AllDayAppointmentLayoutState extends State { context, CalendarAppointmentDetails( date, - List.unmodifiable( + List.unmodifiable( CalendarViewHelper.getCustomAppointments(moreAppointments)), Rect.fromLTWH( widget.isRTL @@ -266,7 +266,6 @@ class _AllDayAppointmentLayoutState extends State { _updateCalendarStateDetails.allDayAppointmentViewCollection; final double cellWidth = (widget.width - widget.timeLabelWidth) / widget.visibleDates.length; - final double cellEndPadding = widget.calendar.cellEndPadding; const double cornerRadius = (kAllDayAppointmentHeight * 0.1) > 2 ? 2 : kAllDayAppointmentHeight * 0.1; @@ -285,7 +284,7 @@ class _AllDayAppointmentLayoutState extends State { Rect.fromLTRB( ((widget.visibleDates.length - appointmentView.endIndex) * cellWidth) + - cellEndPadding, + widget.calendar.cellEndPadding, (kAllDayAppointmentHeight * appointmentView.position) .toDouble(), (widget.visibleDates.length - appointmentView.startIndex) * @@ -304,7 +303,7 @@ class _AllDayAppointmentLayoutState extends State { .toDouble(), (appointmentView.endIndex * cellWidth) + widget.timeLabelWidth - - cellEndPadding, + widget.calendar.cellEndPadding, ((kAllDayAppointmentHeight * appointmentView.position) + kAllDayAppointmentHeight - 1) @@ -506,8 +505,7 @@ class _AllDayAppointmentRenderWidget extends MultiChildRenderObjectWidget { } } -class _AllDayAppointmentRenderObject extends RenderBox - with ContainerRenderObjectMixin { +class _AllDayAppointmentRenderObject extends CustomCalendarRenderObject { _AllDayAppointmentRenderObject( this.calendar, this._view, @@ -763,26 +761,19 @@ class _AllDayAppointmentRenderObject extends RenderBox /// Ref: assembleSemanticsNode method in RenderParagraph class /// (https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/rendering/paragraph.dart) List? _cacheNodes; - Paint _rectPainter = Paint(); - TextPainter _textPainter = TextPainter( + final Paint _rectPainter = Paint(); + final TextPainter _textPainter = TextPainter( textDirection: TextDirection.ltr, maxLines: 1, textAlign: TextAlign.left, textWidthBasis: TextWidthBasis.longestLine); - TextPainter _expanderTextPainter = TextPainter( + final TextPainter _expanderTextPainter = TextPainter( textDirection: TextDirection.ltr, textAlign: TextAlign.left, maxLines: 1); late BoxPainter _boxPainter; bool _isHoveringAppointment = false; int _maxPosition = 0; double _cellWidth = 0; - @override - void setupParentData(RenderObject child) { - if (child.parentData is! CalendarParentData) { - child.parentData = CalendarParentData(); - } - } - /// attach will called when the render object rendered in view. @override void attach(PipelineOwner owner) { @@ -796,6 +787,86 @@ class _AllDayAppointmentRenderObject extends RenderBox return true; } + @override + bool hitTestChildren(BoxHitTestResult result, {required Offset position}) { + RenderBox? child = firstChild; + if (child == null) { + return false; + } + + final int maxPosition = allDayPainterHeight ~/ kAllDayAppointmentHeight; + final double maximumBottomPosition = + allDayPainterHeight - kAllDayAppointmentHeight; + for (int i = 0; i < appointmentCollection.length; i++) { + final AppointmentView appointmentView = appointmentCollection[i]; + if (appointmentView.appointment == null || + child == null || + appointmentView.appointmentRect == null) { + continue; + } + + final RRect appointmentRect = appointmentView.appointmentRect!; + if (!isRTL && + (appointmentRect.left < timeLabelWidth - 1 || + appointmentRect.right > size.width + 1 || + (appointmentRect.bottom > maximumBottomPosition && + appointmentView.maxPositions > maxPosition))) { + child = childAfter(child); + continue; + } else if (isRTL && + (appointmentRect.right > size.width - timeLabelWidth + 1 || + appointmentRect.left < 0 || + (appointmentRect.bottom > maximumBottomPosition && + appointmentView.maxPositions > maxPosition))) { + child = childAfter(child); + continue; + } + + final Offset offset = Offset(appointmentRect.left, appointmentRect.top); + final bool isHit = result.addWithPaintOffset( + offset: offset, + position: position, + hitTest: (BoxHitTestResult result, Offset? transformed) { + assert(transformed == position - offset); + return child!.hitTest(result, position: transformed!); + }, + ); + if (isHit) { + return true; + } + child = childAfter(child); + } + + _cellWidth = (size.width - timeLabelWidth) / visibleDates.length; + final List keys = moreAppointmentIndex.keys.toList(); + for (int i = 0; i < keys.length; i++) { + if (child == null) { + continue; + } + + final int index = keys[i]; + final double leftPosition = isRTL + ? ((visibleDates.length - index - 1) * _cellWidth) + + calendar.cellEndPadding + : timeLabelWidth + (index * _cellWidth); + final Offset offset = Offset(leftPosition, maximumBottomPosition); + final bool isHit = result.addWithPaintOffset( + offset: offset, + position: position, + hitTest: (BoxHitTestResult result, Offset? transformed) { + assert(transformed == position - offset); + return child!.hitTest(result, position: transformed!); + }, + ); + if (isHit) { + return true; + } + child = childAfter(child); + } + + return false; + } + /// detach will called when the render object removed from view. @override void detach() { @@ -843,11 +914,15 @@ class _AllDayAppointmentRenderObject extends RenderBox maxHeight: appointmentRect.height, minWidth: appointmentRect.width, maxWidth: appointmentRect.width)); + final CalendarParentData childParentData = + child.parentData! as CalendarParentData; + childParentData.offset = + Offset(appointmentRect.left, appointmentRect.top); child = childAfter(child); } _cellWidth = (size.width - timeLabelWidth) / visibleDates.length; - final double appointmentHeight = kAllDayAppointmentHeight - 1; + const double appointmentHeight = kAllDayAppointmentHeight - 1; final double maxAppointmentWidth = _cellWidth - calendar.cellEndPadding; final List keys = moreAppointmentIndex.keys.toList(); for (int i = 0; i < keys.length; i++) { @@ -860,6 +935,14 @@ class _AllDayAppointmentRenderObject extends RenderBox maxHeight: appointmentHeight, minWidth: maxAppointmentWidth, maxWidth: maxAppointmentWidth)); + final CalendarParentData childParentData = + child.parentData! as CalendarParentData; + final int index = keys[i]; + final double leftPosition = isRTL + ? ((visibleDates.length - index - 1) * _cellWidth) + + calendar.cellEndPadding + : timeLabelWidth + (index * _cellWidth); + childParentData.offset = Offset(leftPosition, maximumBottomPosition); child = childAfter(child); } } @@ -886,7 +969,6 @@ class _AllDayAppointmentRenderObject extends RenderBox _rectPainter.isAntiAlias = true; _cellWidth = (size.width - timeLabelWidth) / visibleDates.length; const double textPadding = 3; - final double cellEndPadding = calendar.cellEndPadding; _maxPosition = 0; if (appointmentCollection.isNotEmpty) { _maxPosition = appointmentCollection @@ -935,7 +1017,7 @@ class _AllDayAppointmentRenderObject extends RenderBox } if (child != null) { - child.paint(context, Offset(rect.left, rect.top)); + context.paintChild(child, Offset(rect.left, rect.top)); child = childAfter(child); } else { final CalendarAppointment appointment = appointmentView.appointment!; @@ -973,8 +1055,8 @@ class _AllDayAppointmentRenderObject extends RenderBox visibleDates[visibleDates.length - 1]); double? iconSize = _getTextSize( rect, - (calendar.appointmentTextStyle.fontSize! * - _textPainter.textScaleFactor)); + calendar.appointmentTextStyle.fontSize! * + _textPainter.textScaleFactor); if (AppointmentHelper.canAddForwardSpanIcon( appStartTime, appEndTime, viewStartDate, viewEndDate)) { canAddForwardIcon = true; @@ -1000,9 +1082,12 @@ class _AllDayAppointmentRenderObject extends RenderBox context.canvas, Offset( xPosition, rect.top + (rect.height - _textPainter.height) / 2)); - if (appointment.recurrenceRule != null && - appointment.recurrenceRule!.isNotEmpty) { - _addRecurrenceIcon(context.canvas, rect, textPadding); + final bool isRecurrenceAppointment = + appointment.recurrenceRule != null && + appointment.recurrenceRule!.isNotEmpty; + if (isRecurrenceAppointment || appointment.recurrenceId != null) { + _addRecurrenceIcon( + context.canvas, rect, textPadding, isRecurrenceAppointment); } if (canAddSpanIcon) { @@ -1025,6 +1110,7 @@ class _AllDayAppointmentRenderObject extends RenderBox if (selectionNotifier.value != null && selectionNotifier.value!.appointmentView != null && + selectionNotifier.value!.appointmentView == appointmentView && selectionNotifier.value!.appointmentView!.appointment != null && selectionNotifier.value!.appointmentView!.appointment == appointmentView.appointment) { @@ -1034,7 +1120,7 @@ class _AllDayAppointmentRenderObject extends RenderBox if (selectionNotifier.value != null && selectionNotifier.value!.selectedDate != null) { - _addSelectionForAllDayPanel(context.canvas, size, cellEndPadding); + _addSelectionForAllDayPanel(context.canvas, size); } if (isExpandable && _maxPosition > position && !isExpanding) { @@ -1049,9 +1135,9 @@ class _AllDayAppointmentRenderObject extends RenderBox final double xPosition = isRTL ? ((visibleDates.length - index - 1) * _cellWidth) + - cellEndPadding + calendar.cellEndPadding : timeLabelWidth + (index * _cellWidth); - child.paint(context, Offset(xPosition, endYPosition)); + context.paintChild(child, Offset(xPosition, endYPosition)); child = childAfter(child); } } else { @@ -1094,7 +1180,7 @@ class _AllDayAppointmentRenderObject extends RenderBox final double textSize = _getTextSize(rect, calendar.appointmentTextStyle.fontSize!); final TextSpan icon = AppointmentHelper.getSpanIcon( - calendar.appointmentTextStyle.color!, textSize, isRTL ? false : true); + calendar.appointmentTextStyle.color!, textSize, !isRTL); final double leftPadding = isMobilePlatform ? 1 : 2; _textPainter.text = icon; _textPainter.layout( @@ -1125,7 +1211,7 @@ class _AllDayAppointmentRenderObject extends RenderBox final double textSize = _getTextSize(rect, calendar.appointmentTextStyle.fontSize!); final TextSpan icon = AppointmentHelper.getSpanIcon( - calendar.appointmentTextStyle.color!, textSize, isRTL ? true : false); + calendar.appointmentTextStyle.color!, textSize, isRTL); final double leftPadding = isMobilePlatform ? 1 : 2; _textPainter.text = icon; _textPainter.layout( @@ -1226,8 +1312,7 @@ class _AllDayAppointmentRenderObject extends RenderBox Rect.fromLTWH(leftPosition, 0, _cellWidth, size.height), _rectPainter); } - void _addSelectionForAllDayPanel( - Canvas canvas, Size size, double appointmentEndPadding) { + void _addSelectionForAllDayPanel(Canvas canvas, Size size) { final int index = DateTimeHelper.getIndex( visibleDates, selectionNotifier.value!.selectedDate!); Decoration? selectionDecoration = calendar.selectionDecoration; @@ -1267,13 +1352,13 @@ class _AllDayAppointmentRenderObject extends RenderBox double xValue = timeLabelWidth + (index * _cellWidth); if (isRTL) { xValue = size.width - xValue - _cellWidth; - rect = Rect.fromLTRB(xValue + appointmentEndPadding, 0, + rect = Rect.fromLTRB(xValue + calendar.cellEndPadding, 0, xValue + _cellWidth, kAllDayAppointmentHeight - 1); } else { rect = Rect.fromLTRB( xValue, 0, - xValue + _cellWidth - appointmentEndPadding, + xValue + _cellWidth - calendar.cellEndPadding, kAllDayAppointmentHeight - 1); } @@ -1329,11 +1414,14 @@ class _AllDayAppointmentRenderObject extends RenderBox } } - void _addRecurrenceIcon(Canvas canvas, RRect rect, double textPadding) { + void _addRecurrenceIcon(Canvas canvas, RRect rect, double textPadding, + bool isRecurrenceAppointment) { final double textSize = _getTextSize(rect, calendar.appointmentTextStyle.fontSize!); final TextSpan icon = AppointmentHelper.getRecurrenceIcon( - calendar.appointmentTextStyle.color!, textSize); + calendar.appointmentTextStyle.color!, + textSize, + isRecurrenceAppointment); _textPainter.text = icon; _textPainter.layout( minWidth: 0, @@ -1407,11 +1495,6 @@ class _AllDayAppointmentRenderObject extends RenderBox _cacheNodes = null; } - @override - void visitChildrenForSemantics(RenderObjectVisitor visitor) { - return; - } - List _getSemanticsBuilder(Size size) { final List semanticsBuilder = []; diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_layout/appointment_layout.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_layout/appointment_layout.dart index c3cb7de5b..cb7dfda0e 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_layout/appointment_layout.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/appointment_layout/appointment_layout.dart @@ -21,7 +21,7 @@ import '../sfcalendar.dart'; class AppointmentLayout extends StatefulWidget { /// Constructor to create the appointment layout that holds the appointment /// views in calendar widget. - AppointmentLayout( + const AppointmentLayout( this.calendar, this.view, this.visibleDates, @@ -93,20 +93,30 @@ class AppointmentLayout extends StatefulWidget { /// Return the appointment view based on x and y position. AppointmentView? getAppointmentViewOnPoint(double x, double y) { // ignore: avoid_as - final GlobalKey appointmentLayoutKey = key as GlobalKey; + final GlobalKey appointmentLayoutKey = key! as GlobalKey; final _AppointmentLayoutState state = // ignore: avoid_as - appointmentLayoutKey.currentState as _AppointmentLayoutState; + appointmentLayoutKey.currentState! as _AppointmentLayoutState; return state._getAppointmentViewOnPoint(x, y); } + /// Returns the visible appointment view collection. + List getAppointmentViewCollection() { + // ignore: avoid_as + final GlobalKey appointmentLayoutKey = key! as GlobalKey; + final _AppointmentLayoutState state = + // ignore: avoid_as + appointmentLayoutKey.currentState! as _AppointmentLayoutState; + return state._appointmentCollection; + } + @override _AppointmentLayoutState createState() => _AppointmentLayoutState(); } class _AppointmentLayoutState extends State { /// It holds the appointment views for the visible appointments. - List _appointmentCollection = []; + final List _appointmentCollection = []; /// It holds the appointment list based on its visible index value. Map> _indexAppointments = @@ -117,15 +127,19 @@ class _AppointmentLayoutState extends State { /// It holds the children of the widget, it holds empty when /// appointment builder is null. - List _children = []; + final List _children = []; final UpdateCalendarStateDetails _updateCalendarStateDetails = UpdateCalendarStateDetails(); TextPainter _textPainter = TextPainter(); + late double _weekNumberPanelWidth; @override void initState() { widget.updateCalendarState(_updateCalendarStateDetails); + _weekNumberPanelWidth = CalendarViewHelper.getWeekNumberPanelWidth( + widget.calendar.showWeekNumber, widget.width, widget.isMobilePlatform); + _updateAppointmentDetails(); widget.visibleAppointments.addListener(_updateVisibleAppointment); super.initState(); @@ -142,6 +156,10 @@ class _AppointmentLayoutState extends State { (CalendarViewHelper.isTimelineView(widget.view) && (widget.resourceCollection != oldWidget.resourceCollection || widget.resourceItemHeight != oldWidget.resourceItemHeight))) { + _weekNumberPanelWidth = CalendarViewHelper.getWeekNumberPanelWidth( + widget.calendar.showWeekNumber, + widget.width, + widget.isMobilePlatform); isAppointmentDetailsUpdated = true; _updateAppointmentDetails(); } @@ -157,6 +175,16 @@ class _AppointmentLayoutState extends State { } } + if (widget.calendar.showWeekNumber != oldWidget.calendar.showWeekNumber && + widget.view == CalendarView.month) { + _weekNumberPanelWidth = CalendarViewHelper.getWeekNumberPanelWidth( + widget.calendar.showWeekNumber, + widget.width, + widget.isMobilePlatform); + + _updateAppointmentDetails(); + } + super.didUpdateWidget(oldWidget); } @@ -188,9 +216,9 @@ class _AppointmentLayoutState extends State { context, CalendarAppointmentDetails( date, - List.unmodifiable([ - appointmentView.appointment!.data ?? - appointmentView.appointment! + List.unmodifiable([ + CalendarViewHelper.getAppointmentDetail( + appointmentView.appointment!) ]), Rect.fromLTWH( appointmentView.appointmentRect!.left, @@ -227,8 +255,9 @@ class _AppointmentLayoutState extends State { context, CalendarAppointmentDetails( date, - List.unmodifiable(CalendarViewHelper.getCustomAppointments( - moreAppointments)), + List.unmodifiable( + CalendarViewHelper.getCustomAppointments( + moreAppointments)), Rect.fromLTWH(moreRegionRect.left, moreRegionRect.top, moreRegionRect.width, moreRegionRect.height), isMoreAppointmentRegion: true)); @@ -258,6 +287,7 @@ class _AppointmentLayoutState extends State { _appointmentCollection, _indexAppointments, _monthAppointmentCountViews, + _weekNumberPanelWidth, widgets: _children); } @@ -286,6 +316,7 @@ class _AppointmentLayoutState extends State { MonthAppointmentDisplayMode.appointment) { final List keys = _monthAppointmentCountViews.keys.toList(); for (int i = 0; i < keys.length; i++) { + // ignore: unnecessary_nullable_for_final_variable_declarations final RRect? rect = _monthAppointmentCountViews[keys[i]]!; if (rect != null && @@ -344,7 +375,7 @@ class _AppointmentLayoutState extends State { /// Check the span appointment is start after time slot end hour and /// end before time slot start hour then skip the rendering. if (isSameDate(appointment.actualEndTime, - appointment.actualStartTime.add(Duration(days: 1)))) { + appointment.actualStartTime.add(const Duration(days: 1)))) { final int appointmentStartMinutes = (appointment.actualStartTime.hour * 60) + appointment.actualStartTime.minute; @@ -431,16 +462,18 @@ class _AppointmentLayoutState extends State { void _updateMonthAppointmentDetails( List visibleAppointments) { - final double cellWidth = widget.width / DateTime.daysPerWeek; + final double cellWidth = + (widget.width - _weekNumberPanelWidth) / DateTime.daysPerWeek; final double cellHeight = widget.height / widget.calendar.monthViewSettings.numberOfWeeksInView; - if (widget.calendar.monthCellBuilder != null || - widget.calendar.monthViewSettings.appointmentDisplayMode != - MonthAppointmentDisplayMode.appointment) { + if (widget.calendar.monthViewSettings.appointmentDisplayMode != + MonthAppointmentDisplayMode.appointment) { return; } - double xPosition = 0; + double xPosition = widget.isRTL + ? widget.width - cellWidth - _weekNumberPanelWidth + : _weekNumberPanelWidth; double yPosition = 0; final int count = widget.visibleDates.length; DateTime visibleStartDate = @@ -448,10 +481,7 @@ class _AppointmentLayoutState extends State { DateTime visibleEndDate = AppointmentHelper.convertToEndTime(widget.visibleDates[count - 1]); int visibleStartIndex = 0; - int visibleEndIndex = - (widget.calendar.monthViewSettings.numberOfWeeksInView * - DateTime.daysPerWeek) - - 1; + int visibleEndIndex = count - 1; final bool showTrailingLeadingDates = CalendarViewHelper.isLeadingAndTrailingDatesVisible( widget.calendar.monthViewSettings.numberOfWeeksInView, @@ -518,8 +548,9 @@ class _AppointmentLayoutState extends State { cellWidth; xPosition -= appointmentWidth - cellWidth; } else { - xPosition = - (appointmentView.startIndex % DateTime.daysPerWeek) * cellWidth; + xPosition = ((appointmentView.startIndex % DateTime.daysPerWeek) * + cellWidth) + + _weekNumberPanelWidth; } yPosition = @@ -566,7 +597,8 @@ class _AppointmentLayoutState extends State { if (widget.isRTL) { xPosition = (6 - (index % DateTime.daysPerWeek)) * cellWidth; } else { - xPosition = (index % DateTime.daysPerWeek) * cellWidth; + xPosition = ((index % DateTime.daysPerWeek) * cellWidth) + + _weekNumberPanelWidth; } yPosition = ((index ~/ DateTime.daysPerWeek) * cellHeight) + @@ -595,9 +627,9 @@ class _AppointmentLayoutState extends State { widget.calendar, widget.view, visibleAppointments, - false, - widget.timeIntervalHeight); - final double cellWidth = width / widget.visibleDates.length; + false); + final int count = widget.visibleDates.length; + final double cellWidth = width / count; final double cellHeight = widget.timeIntervalHeight; double xPosition = timeLabelWidth; final double cellEndPadding = widget.calendar.cellEndPadding; @@ -617,12 +649,11 @@ class _AppointmentLayoutState extends State { final CalendarAppointment appointment = appointmentView.appointment!; int column = -1; - final int count = widget.visibleDates.length; for (int j = 0; j < count; j++) { final DateTime _date = widget.visibleDates[j]; if (isSameDate(_date, appointment.actualStartTime)) { - column = widget.isRTL ? widget.visibleDates.length - 1 - j : j; + column = widget.isRTL ? count - 1 - j : j; break; } } @@ -735,7 +766,7 @@ class _AppointmentLayoutState extends State { /// appointment collection. final List appointmentForEachResource = visibleAppointments - .where((app) => + .where((CalendarAppointment app) => app.resourceIds != null && app.resourceIds!.isNotEmpty && app.resourceIds!.contains(resource.id)) @@ -746,7 +777,6 @@ class _AppointmentLayoutState extends State { widget.view, appointmentForEachResource, false, - widget.timeIntervalHeight, i); } } else { @@ -755,17 +785,19 @@ class _AppointmentLayoutState extends State { widget.calendar, widget.view, visibleAppointments, - false, - widget.timeIntervalHeight); + false); } - final double viewWidth = widget.width / widget.visibleDates.length; + final int visibleDatesLength = widget.visibleDates.length; + final double viewWidth = widget.width / visibleDatesLength; final double cellWidth = widget.timeIntervalHeight; double xPosition = 0; double yPosition = 0; final double cellEndPadding = widget.calendar.cellEndPadding; final double slotHeight = isResourceEnabled ? widget.resourceItemHeight! : widget.height; + final double timelineAppointmentHeight = _getTimelineAppointmentHeight( + widget.calendar.timeSlotViewSettings, widget.view); for (int i = 0; i < _appointmentCollection.length; i++) { final AppointmentView appointmentView = _appointmentCollection[i]; if (appointmentView.canReuse || appointmentView.appointment == null) { @@ -782,7 +814,7 @@ class _AppointmentLayoutState extends State { index = 0; } - column = widget.isRTL ? widget.visibleDates.length - 1 - index : index; + column = widget.isRTL ? visibleDatesLength - 1 - index : index; /// For timeline day, week and work week view each column represents a /// time slots for timeline month each column represent a day, and as @@ -793,8 +825,7 @@ class _AppointmentLayoutState extends State { column += 1; } - double appointmentHeight = _getTimelineAppointmentHeight( - widget.calendar.timeSlotViewSettings, widget.view); + double appointmentHeight = timelineAppointmentHeight; if (appointmentHeight * appointmentView.maxPositions > slotHeight) { appointmentHeight = slotHeight / appointmentView.maxPositions; } @@ -854,7 +885,7 @@ class _AppointmentLayoutState extends State { /// appointment collection. final List appointmentForEachResource = visibleAppointments - .where((app) => + .where((CalendarAppointment app) => app.resourceIds != null && app.resourceIds!.isNotEmpty && app.resourceIds!.contains(resource.id)) @@ -865,7 +896,6 @@ class _AppointmentLayoutState extends State { widget.view, appointmentForEachResource, false, - widget.timeIntervalHeight, i); } } else { @@ -874,15 +904,14 @@ class _AppointmentLayoutState extends State { widget.calendar, widget.view, visibleAppointments, - false, - widget.timeIntervalHeight); + false); } - final double viewWidth = widget.width / widget.visibleDates.length; + final int count = widget.visibleDates.length; + final double viewWidth = widget.width / count; final double cellWidth = widget.timeIntervalHeight; double xPosition = 0; double yPosition = 0; - final int count = widget.visibleDates.length; final int timeInterval = CalendarViewHelper.getTimeInterval( widget.calendar.timeSlotViewSettings); final double cellEndPadding = widget.calendar.cellEndPadding; @@ -890,6 +919,10 @@ class _AppointmentLayoutState extends State { widget.calendar.timeSlotViewSettings.startHour.toInt(); final double viewStartMinutes = (widget.calendar.timeSlotViewSettings.startHour - viewStartHour) * 60; + final double timelineAppointmentHeight = _getTimelineAppointmentHeight( + widget.calendar.timeSlotViewSettings, widget.view); + final double slotHeight = + isResourceEnabled ? widget.resourceItemHeight! : widget.height; for (int i = 0; i < _appointmentCollection.length; i++) { final AppointmentView appointmentView = _appointmentCollection[i]; if (appointmentView.canReuse || appointmentView.appointment == null) { @@ -928,16 +961,22 @@ class _AppointmentLayoutState extends State { endColumn = j - 1; if (endColumn != -1) { date = widget.visibleDates[endColumn]; - endTime = DateTime(date.year, date.month, date.day, 59, 59, 0); + endTime = DateTime(date.year, date.month, date.day, 23, 59, 59); } break; } } + final DateTime visibleEndDate = widget.visibleDates[count - 1]; if (endColumn == -1 && - appointment.actualEndTime - .isAfter(widget.visibleDates[widget.visibleDates.length - 1])) { - endColumn = widget.visibleDates.length - 1; + appointment.actualEndTime.isAfter(visibleEndDate)) { + endColumn = count - 1; + + /// Assign the end date time value to visible end date and time value + /// when the appointment end date value after the visible end date + /// value. + endTime = DateTime(visibleEndDate.year, visibleEndDate.month, + visibleEndDate.day, 23, 59, 59); } if (column == -1 || endColumn == -1) { @@ -950,10 +989,7 @@ class _AppointmentLayoutState extends State { final double minuteHeight = cellWidth / timeInterval; - double appointmentHeight = _getTimelineAppointmentHeight( - widget.calendar.timeSlotViewSettings, widget.view); - final double slotHeight = - isResourceEnabled ? widget.resourceItemHeight! : widget.height; + double appointmentHeight = timelineAppointmentHeight; if (appointmentHeight * appointmentView.maxPositions > slotHeight) { appointmentHeight = slotHeight / appointmentView.maxPositions; } @@ -1054,10 +1090,12 @@ class _AppointmentRenderWidget extends MultiChildRenderObjectWidget { this.appointmentCollection, this.indexAppointments, this.monthAppointmentCountViews, + this.weekNumberPanelWidth, {List widgets = const []}) : super(children: widgets); final SfCalendar calendar; + final double weekNumberPanelWidth; final CalendarView view; final List visibleDates; final double timeIntervalHeight; @@ -1096,7 +1134,8 @@ class _AppointmentRenderWidget extends MultiChildRenderObjectWidget { localizations, appointmentCollection, indexAppointments, - monthAppointmentCountViews); + monthAppointmentCountViews, + weekNumberPanelWidth); } @override @@ -1120,7 +1159,8 @@ class _AppointmentRenderWidget extends MultiChildRenderObjectWidget { ..localizations = localizations ..appointmentCollection = appointmentCollection ..indexAppointments = indexAppointments - ..monthAppointmentCountViews = monthAppointmentCountViews; + ..monthAppointmentCountViews = monthAppointmentCountViews + ..weekNumberPanelWidth = weekNumberPanelWidth; } } @@ -1143,7 +1183,8 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { this._localizations, this.appointmentCollection, this.indexAppointments, - this.monthAppointmentCountViews); + this.monthAppointmentCountViews, + this._weekNumberPanelWidth); List? _visibleAppointments; @@ -1177,6 +1218,23 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { _appointmentHoverPosition.addListener(markNeedsPaint); } + double _weekNumberPanelWidth; + + double get weekNumberPanelWidth => _weekNumberPanelWidth; + + set weekNumberPanelWidth(double value) { + if (_weekNumberPanelWidth == value) { + return; + } + + _weekNumberPanelWidth = value; + if (childCount == 0) { + markNeedsPaint(); + } else { + markNeedsLayout(); + } + } + double _timeIntervalHeight; double get timeIntervalHeight => _timeIntervalHeight; @@ -1365,7 +1423,7 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { >{}; Map monthAppointmentCountViews = {}; - Paint _appointmentPainter = Paint(); + final Paint _appointmentPainter = Paint(); TextPainter _textPainter = TextPainter(); @override @@ -1420,7 +1478,7 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { semanticsBuilder.add(CustomPainterSemantics( rect: rect, - properties: SemanticsProperties( + properties: const SemanticsProperties( label: 'More', textDirection: TextDirection.ltr, ), @@ -1430,6 +1488,68 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { return semanticsBuilder; } + @override + bool hitTestChildren(BoxHitTestResult result, {required Offset position}) { + RenderBox? child = firstChild; + if (child == null) { + return false; + } + + for (int i = 0; i < appointmentCollection.length; i++) { + final AppointmentView appointmentView = appointmentCollection[i]; + if (appointmentView.appointment == null || + child == null || + appointmentView.appointmentRect == null) { + continue; + } + + final Offset offset = Offset(appointmentView.appointmentRect!.left, + appointmentView.appointmentRect!.top); + final bool isHit = result.addWithPaintOffset( + offset: offset, + position: position, + hitTest: (BoxHitTestResult result, Offset? transformed) { + assert(transformed == position - offset); + return child!.hitTest(result, position: transformed!); + }, + ); + if (isHit) { + return true; + } + child = childAfter(child); + } + + if (view != CalendarView.month || + calendar.monthViewSettings.appointmentDisplayMode != + MonthAppointmentDisplayMode.appointment) { + return false; + } + + final List keys = monthAppointmentCountViews.keys.toList(); + for (int i = 0; i < keys.length; i++) { + if (child == null) { + continue; + } + + final RRect moreRegionRect = monthAppointmentCountViews[keys[i]]!; + final Offset offset = Offset(moreRegionRect.left, moreRegionRect.top); + final bool isHit = result.addWithPaintOffset( + offset: offset, + position: position, + hitTest: (BoxHitTestResult result, Offset? transformed) { + assert(transformed == position - offset); + return child!.hitTest(result, position: transformed!); + }, + ); + if (isHit) { + return true; + } + child = childAfter(child); + } + + return false; + } + @override void performLayout() { final Size widgetSize = constraints.biggest; @@ -1449,6 +1569,10 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { maxHeight: appointmentView.appointmentRect!.height, minWidth: appointmentView.appointmentRect!.width, maxWidth: appointmentView.appointmentRect!.width)); + final CalendarParentData childParentData = + child.parentData! as CalendarParentData; + childParentData.offset = Offset(appointmentView.appointmentRect!.left, + appointmentView.appointmentRect!.top); child = childAfter(child); } @@ -1470,6 +1594,10 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { maxHeight: moreRegionRect.height, minWidth: moreRegionRect.width, maxWidth: moreRegionRect.width)); + + final CalendarParentData childParentData = + child.parentData! as CalendarParentData; + childParentData.offset = Offset(moreRegionRect.left, moreRegionRect.top); child = childAfter(child); } } @@ -1489,8 +1617,8 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { continue; } - child.paint( - context, + context.paintChild( + child, Offset(appointmentView.appointmentRect!.left, appointmentView.appointmentRect!.top)); _updateAppointmentHovering( @@ -1512,7 +1640,8 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { } final RRect moreRegionRect = monthAppointmentCountViews[keys[i]]!; - child.paint(context, Offset(moreRegionRect.left, moreRegionRect.top)); + context.paintChild( + child, Offset(moreRegionRect.left, moreRegionRect.top)); _updateAppointmentHovering(moreRegionRect, context.canvas); child = childAfter(child); @@ -1550,12 +1679,10 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { } void _drawMonthAppointment(Canvas canvas, Size size, Paint paint) { - final double cellWidth = size.width / DateTime.daysPerWeek; + final double cellWidth = + (size.width - weekNumberPanelWidth) / DateTime.daysPerWeek; final double cellHeight = size.height / calendar.monthViewSettings.numberOfWeeksInView; - if (calendar.monthCellBuilder != null) { - return; - } switch (calendar.monthViewSettings.appointmentDisplayMode) { case MonthAppointmentDisplayMode.none: @@ -1570,7 +1697,9 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { void _drawMonthAppointmentView(Canvas canvas, Size size, double cellWidth, double cellHeight, Paint paint) { - double xPosition = 0; + double xPosition = isRTL + ? size.width - cellWidth - weekNumberPanelWidth + : weekNumberPanelWidth; final int count = visibleDates.length; DateTime visibleStartDate = AppointmentHelper.convertToStartTime(visibleDates[0]); @@ -1668,9 +1797,10 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { xPosition += (appointmentRect.width - _textPainter.width) / 2; } - _textPainter.paint(canvas, Offset(xPosition + 2, yPosition)); + _textPainter.paint( + canvas, Offset(xPosition + (isRTL ? 0 : 2), yPosition)); - if (isRecurrenceAppointment) { + if (isRecurrenceAppointment || appointment.recurrenceId != null) { _drawRecurrenceIconForMonth( canvas, size, @@ -1679,7 +1809,8 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { appointmentRect, appointmentRect.tlRadius, paint, - useMobilePlatformUI); + useMobilePlatformUI, + isRecurrenceAppointment); } if (canAddSpanIcon) { @@ -1769,8 +1900,8 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { Radius cornerRadius, Paint paint, bool useMobilePlatformUI) { - final TextSpan icon = AppointmentHelper.getSpanIcon( - style.color!, textSize, isRTL ? false : true); + final TextSpan icon = + AppointmentHelper.getSpanIcon(style.color!, textSize, !isRTL); _textPainter.text = icon; _textPainter.layout( minWidth: 0, maxWidth: rect.width + 1 > 0 ? rect.width + 1 : 0); @@ -1792,15 +1923,15 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { void _drawBackwardSpanIconForMonth(Canvas canvas, TextStyle style, double textSize, RRect rect, Radius cornerRadius, Paint paint) { - final TextSpan icon = AppointmentHelper.getSpanIcon( - style.color!, textSize, isRTL ? true : false); + final TextSpan icon = + AppointmentHelper.getSpanIcon(style.color!, textSize, isRTL); _textPainter.text = icon; _textPainter.layout( minWidth: 0, maxWidth: rect.width + 1 > 0 ? rect.width + 1 : 0); final double yPosition = AppointmentHelper.getYPositionForSpanIcon(icon, _textPainter, rect); - final double rightPadding = 2; + const double rightPadding = 2; final double xPosition = isRTL ? rect.right - textSize - rightPadding : rect.left + rightPadding; canvas.drawRRect( @@ -1820,9 +1951,10 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { RRect rect, Radius cornerRadius, Paint paint, - bool useMobilePlatformUI) { - final TextSpan icon = - AppointmentHelper.getRecurrenceIcon(style.color!, textSize); + bool useMobilePlatformUI, + bool isRecurrenceAppointment) { + final TextSpan icon = AppointmentHelper.getRecurrenceIcon( + style.color!, textSize, isRecurrenceAppointment); _textPainter.text = icon; _textPainter.layout( minWidth: 0, maxWidth: rect.width + 1 > 0 ? rect.width + 1 : 0); @@ -1843,7 +1975,9 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { void _drawMonthAppointmentIndicator( Canvas canvas, double cellWidth, double cellHeight, Paint paint) { - double xPosition = 0; + double xPosition = isRTL + ? size.width - cellWidth - weekNumberPanelWidth + : weekNumberPanelWidth; double yPosition = 0; const double radius = 2.5; const double diameter = radius * 2; @@ -1894,7 +2028,8 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { if (isRTL) { startXPosition = (6 - (i % DateTime.daysPerWeek).toInt()) * cellWidth; } else { - startXPosition = ((i % DateTime.daysPerWeek).toInt()) * cellWidth; + startXPosition = (((i % DateTime.daysPerWeek).toInt()) * cellWidth) + + _weekNumberPanelWidth; } xPosition += startXPosition; @@ -1987,7 +2122,7 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { CalendarViewHelper.isSameTimeSlot( appointment.exactEndTime, appointment.actualEndTime)) { yPosition += _getTextSize(appointmentRect, - (calendar.appointmentTextStyle.fontSize! * textScaleFactor)); + calendar.appointmentTextStyle.fontSize! * textScaleFactor); } } @@ -2036,10 +2171,13 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { appointmentRect.width - textStartPadding - _textPainter.width; } - _textPainter.paint(canvas, - Offset(xPosition + textStartPadding, yPosition + textStartPadding)); - if (appointment.recurrenceRule != null && - appointment.recurrenceRule!.isNotEmpty) { + _textPainter.paint( + canvas, + Offset(xPosition + (isRTL ? 0 : textStartPadding), + yPosition + textStartPadding)); + final bool isRecurrenceAppointment = appointment.recurrenceRule != null && + appointment.recurrenceRule!.isNotEmpty; + if (isRecurrenceAppointment || appointment.recurrenceId != null) { _addRecurrenceIconForDay( canvas, size, @@ -2048,7 +2186,8 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { textStartPadding, paint, appointmentRect.tlRadius, - useMobilePlatformUI); + useMobilePlatformUI, + isRecurrenceAppointment); } if (canAddSpanIcon) { @@ -2087,9 +2226,9 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { final double yPosition = rect.top + bottomPadding; canvas.translate(xPosition, yPosition); - final radians = 90 * math.pi / 180; + final double radians = 90 * math.pi / 180; canvas.rotate(radians); - _textPainter.paint(canvas, Offset(0, 0)); + _textPainter.paint(canvas, const Offset(0, 0)); canvas.restore(); } @@ -2129,9 +2268,9 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { final double yPosition = rect.bottom - (textSize * textScaleFactor) - bottomPadding; canvas.translate(xPosition, yPosition); - final radians = 90 * math.pi / 180; + final double radians = 90 * math.pi / 180; canvas.rotate(radians); - _textPainter.paint(canvas, Offset(0, 0)); + _textPainter.paint(canvas, const Offset(0, 0)); canvas.restore(); } @@ -2155,7 +2294,8 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { int textPadding, Paint paint, Radius cornerRadius, - bool useMobilePlatformUI) { + bool useMobilePlatformUI, + bool isRecurrenceAppointment) { final double xPadding = useMobilePlatformUI ? 1 : 2; const double bottomPadding = 2; double textSize = calendar.appointmentTextStyle.fontSize!; @@ -2164,7 +2304,9 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { } final TextSpan icon = AppointmentHelper.getRecurrenceIcon( - calendar.appointmentTextStyle.color!, textSize); + calendar.appointmentTextStyle.color!, + textSize, + isRecurrenceAppointment); _textPainter.text = icon; double maxTextWidth = appointmentWidth - textPadding - 2; maxTextWidth = maxTextWidth > 0 ? maxTextWidth : 0; @@ -2219,7 +2361,7 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { final DateTime viewEndDate = AppointmentHelper.convertToEndTime( visibleDates[visibleDates.length - 1]); double? iconSize = _getTextSize(appointmentRect, - (calendar.appointmentTextStyle.fontSize! * textScaleFactor)) + + calendar.appointmentTextStyle.fontSize! * textScaleFactor) + textStartPadding; if (AppointmentHelper.canAddForwardSpanIcon( appStartTime, appEndTime, viewStartDate, viewEndDate)) { @@ -2281,10 +2423,19 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { canvas, Offset(xPosition + textStartPadding, appointmentRect.top + textStartPadding)); - if (appointment.recurrenceRule != null && - appointment.recurrenceRule!.isNotEmpty) { - _addRecurrenceIconForTimeline(canvas, size, appointmentRect, maxWidth, - appointmentRect.tlRadius, paint, useMobilePlatformUI); + final bool isRecurrenceAppointment = appointment.recurrenceRule != null && + appointment.recurrenceRule!.isNotEmpty; + + if (isRecurrenceAppointment || appointment.recurrenceId != null) { + _addRecurrenceIconForTimeline( + canvas, + size, + appointmentRect, + maxWidth, + appointmentRect.tlRadius, + paint, + useMobilePlatformUI, + isRecurrenceAppointment); } if (canAddSpanIcon) { @@ -2349,12 +2500,12 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { Radius cornerRadius, Paint paint, bool isMobilePlatform) { - final double xPadding = 2; + const double xPadding = 2; final double textSize = _getTextSize(rect, calendar.appointmentTextStyle.fontSize!); final TextSpan icon = AppointmentHelper.getSpanIcon( - calendar.appointmentTextStyle.color!, textSize, isRTL ? false : true); + calendar.appointmentTextStyle.color!, textSize, !isRTL); _textPainter = _updateTextPainter(icon, _textPainter, isRTL, _textScaleFactor); _textPainter.layout(minWidth: 0, maxWidth: maxWidth); @@ -2381,12 +2532,12 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { Radius cornerRadius, Paint paint, bool isMobilePlatform) { - final double xPadding = 2; + const double xPadding = 2; final double textSize = _getTextSize(rect, calendar.appointmentTextStyle.fontSize!); final TextSpan icon = AppointmentHelper.getSpanIcon( - calendar.appointmentTextStyle.color!, textSize, isRTL ? true : false); + calendar.appointmentTextStyle.color!, textSize, isRTL); _textPainter = _updateTextPainter(icon, _textPainter, isRTL, _textScaleFactor); _textPainter.layout(minWidth: 0, maxWidth: maxWidth); @@ -2412,14 +2563,17 @@ class _AppointmentRenderObject extends CustomCalendarRenderObject { double maxWidth, Radius cornerRadius, Paint paint, - bool useMobilePlatformUI) { + bool useMobilePlatformUI, + bool isRecurrenceAppointment) { final double xPadding = useMobilePlatformUI ? 1 : 2; const double bottomPadding = 2; final double textSize = _getTextSize(rect, calendar.appointmentTextStyle.fontSize!); final TextSpan icon = AppointmentHelper.getRecurrenceIcon( - calendar.appointmentTextStyle.color!, textSize); + calendar.appointmentTextStyle.color!, + textSize, + isRecurrenceAppointment); _textPainter.text = icon; _textPainter.layout(minWidth: 0, maxWidth: maxWidth); canvas.drawRRect( diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/common/calendar_controller.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/common/calendar_controller.dart index fd4e52e84..06da6e529 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/common/calendar_controller.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/common/calendar_controller.dart @@ -1,5 +1,4 @@ import 'package:flutter/foundation.dart'; -import 'package:syncfusion_flutter_core/core.dart'; import 'calendar_view_helper.dart'; import 'enums.dart'; @@ -104,10 +103,9 @@ class CalendarValueChangedNotifier with Diagnosticable { /// /// class MyAppState extends State{ /// -/// CalendarController _calendarController; +/// CalendarController _calendarController = CalendarController(); /// @override /// initState(){ -/// _calendarController = CalendarController(); /// _calendarController.selectedDate = DateTime(2022, 02, 05); /// _calendarController.displayDate = DateTime(2022, 02, 05); /// super.initState(); @@ -143,10 +141,9 @@ class CalendarController extends CalendarValueChangedNotifier { /// ```dart /// class MyAppState extends State{ /// - /// CalendarController _calendarController; + /// CalendarController _calendarController = CalendarController(); /// @override /// initState(){ - /// _calendarController = CalendarController(); /// _calendarController.selectedDate = DateTime(2022, 02, 05); /// _calendarController.displayDate = DateTime(2022, 02, 05); /// super.initState(); @@ -196,10 +193,9 @@ class CalendarController extends CalendarValueChangedNotifier { /// ```dart /// class MyAppState extends State{ /// - /// CalendarController _calendarController; + /// CalendarController _calendarController = CalendarController(); /// @override /// initState(){ - /// _calendarController = CalendarController(); /// _calendarController.selectedDate = DateTime(2022, 02, 05); /// _calendarController.displayDate = DateTime(2022, 02, 05); /// super.initState(); @@ -219,7 +215,7 @@ class CalendarController extends CalendarValueChangedNotifier { ///} /// ``` set displayDate(DateTime? date) { - if (date == null || isSameDate(_displayDate, date)) { + if (date == null) { return; } @@ -235,10 +231,9 @@ class CalendarController extends CalendarValueChangedNotifier { /// ```dart /// class MyAppState extends State{ /// - /// CalendarController _calendarController; + /// CalendarController _calendarController = CalendarController(); /// @override /// initState(){ - /// _calendarController = CalendarController(); /// _calendarController.view = CalendarView.week; /// super.initState(); /// } @@ -273,11 +268,10 @@ class CalendarController extends CalendarValueChangedNotifier { /// /// ```dart /// class MyAppState extends State { - /// CalendarController _calendarController; + /// CalendarController _calendarController = CalendarController(); /// /// @override /// initState() { - /// _calendarController = CalendarController(); /// _calendarController.selectedDate = DateTime(2022, 02, 05); /// _calendarController.displayDate = DateTime(2022, 02, 05); /// super.initState(); @@ -293,14 +287,14 @@ class CalendarController extends CalendarValueChangedNotifier { /// IconButton( /// icon: Icon(Icons.arrow_forward), /// onPressed: () { - /// _calendarController.forward(); + /// _calendarController.forward!(); /// }, /// ), /// ], /// leading: IconButton( /// icon: Icon(Icons.arrow_back), /// onPressed: () { - /// _calendarController.backward(); + /// _calendarController.backward!(); /// }, /// ), /// ), @@ -324,11 +318,10 @@ class CalendarController extends CalendarValueChangedNotifier { /// /// ```dart /// class MyAppState extends State { - /// CalendarController _calendarController; + /// CalendarController _calendarController = CalendarController(); /// /// @override /// initState() { - /// _calendarController = CalendarController(); /// _calendarController.selectedDate = DateTime(2022, 02, 05); /// _calendarController.displayDate = DateTime(2022, 02, 05); /// super.initState(); @@ -344,14 +337,14 @@ class CalendarController extends CalendarValueChangedNotifier { /// IconButton( /// icon: Icon(Icons.arrow_forward), /// onPressed: () { - /// _calendarController.forward(); + /// _calendarController.forward!(); /// }, /// ), /// ], /// leading: IconButton( /// icon: Icon(Icons.arrow_back), /// onPressed: () { - /// _calendarController.backward(); + /// _calendarController.backward!(); /// }, /// ), /// ), diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/common/calendar_view_helper.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/common/calendar_view_helper.dart index 4221a02fb..83bee303d 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/common/calendar_view_helper.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/common/calendar_view_helper.dart @@ -1,8 +1,10 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; +import 'package:flutter/services.dart'; import 'package:intl/intl.dart' show DateFormat; import 'package:syncfusion_flutter_core/core.dart'; +import 'package:syncfusion_flutter_core/theme.dart'; import '../../../calendar.dart'; import '../appointment_engine/calendar_datasource.dart'; @@ -13,6 +15,7 @@ import '../settings/schedule_view_settings.dart'; import '../settings/time_region.dart'; import '../settings/time_slot_view_settings.dart'; import '../sfcalendar.dart'; +import 'date_time_engine.dart'; import 'enums.dart'; import 'event_args.dart'; @@ -124,7 +127,7 @@ class CalendarViewHelper { /// Return the copy of list passed. static List? cloneList(List? value) { - if (value == null || value.isEmpty) { + if (value == null) { return null; } return value.sublist(0); @@ -158,32 +161,6 @@ class CalendarViewHelper { return true; } - /// Check both the resource collection resources are equal or not. - static bool isResourceCollectionEqual( - List? originalCollection, - List? copyCollection) { - if (originalCollection == copyCollection) { - return true; - } - - if (originalCollection == null || copyCollection == null) { - return false; - } - - final int datesCount = originalCollection.length; - if (datesCount != copyCollection.length) { - return false; - } - - for (int i = 0; i < datesCount; i++) { - if (originalCollection[i] != copyCollection[i]) { - return false; - } - } - - return true; - } - /// Check whether the date collection contains the date value or not. static bool isDateInDateCollection(List? dates, DateTime date) { if (dates == null || dates.isEmpty) { @@ -301,22 +278,30 @@ class CalendarViewHelper { } } + /// Get the today text color based on today highlight color and today text + /// style. + static Color? getTodayHighlightTextColor(Color? todayHighlightColor, + TextStyle? todayTextStyle, SfCalendarThemeData calendarTheme) { + Color? todayTextColor = todayHighlightColor; + if (todayTextColor != null && todayTextColor == Colors.transparent) { + todayTextColor = todayTextStyle != null + ? todayTextStyle.color + : calendarTheme.todayTextStyle.color; + } + + return todayTextColor; + } + /// Get the exact the time from the position and the date time includes /// minutes value. static double getTimeToPosition(Duration duration, TimeSlotViewSettings timeSlotViewSettings, double minuteHeight) { + final int startHour = timeSlotViewSettings.startHour.toInt(); final Duration startDuration = Duration( - hours: timeSlotViewSettings.startHour.toInt(), - minutes: ((timeSlotViewSettings.startHour - - timeSlotViewSettings.startHour.toInt()) * - 60) - .toInt()); + hours: startHour, + minutes: ((timeSlotViewSettings.startHour - startHour) * 60).toInt()); final Duration difference = duration - startDuration; - if (difference.isNegative) { - return 0; - } - - return difference.inMinutes * minuteHeight; + return difference.isNegative ? 0 : difference.inMinutes * minuteHeight; } /// Returns the time interval value based on the given start time, end time @@ -338,13 +323,14 @@ class CalendarViewHelper { totalMinutes = defaultLinesCount * 60; - if (settings.timeInterval.inMinutes >= 0 && - settings.timeInterval.inMinutes <= totalMinutes && - totalMinutes.round() % settings.timeInterval.inMinutes.round() == 0) { - return settings.timeInterval.inMinutes; - } else if (settings.timeInterval.inMinutes >= 0 && - settings.timeInterval.inMinutes <= totalMinutes) { - return _getNearestValue(settings.timeInterval.inMinutes, totalMinutes); + final int timeIntervalMinutes = settings.timeInterval.inMinutes; + if (timeIntervalMinutes >= 0 && + timeIntervalMinutes <= totalMinutes && + totalMinutes.round() % timeIntervalMinutes == 0) { + return timeIntervalMinutes; + } else if (timeIntervalMinutes >= 0 && + timeIntervalMinutes <= totalMinutes) { + return _getNearestValue(timeIntervalMinutes, totalMinutes); } else { return 60; } @@ -374,7 +360,7 @@ class CalendarViewHelper { static int _getNearestValue(int timeInterval, double totalMinutes) { timeInterval++; - if (totalMinutes.round() % timeInterval.round() == 0) { + if (totalMinutes.round() % timeInterval == 0) { return timeInterval; } @@ -477,9 +463,7 @@ class CalendarViewHelper { List? appointments, CalendarElement element, CalendarResource? resource) { - final CalendarTapDetails calendarTapDetails = - CalendarTapDetails(appointments, date, element, resource); - calendar.onTap!(calendarTapDetails); + calendar.onTap!(CalendarTapDetails(appointments, date, element, resource)); } /// Method that raise the calendar long press callback with given parameters. @@ -489,27 +473,22 @@ class CalendarViewHelper { List? appointments, CalendarElement element, CalendarResource? resource) { - final CalendarLongPressDetails calendarLongPressDetails = - CalendarLongPressDetails(appointments, date, element, resource); - calendar.onLongPress!(calendarLongPressDetails); + calendar.onLongPress!( + CalendarLongPressDetails(appointments, date, element, resource)); } /// method that raise the calendar selection changed callback /// with the given parameters static void raiseCalendarSelectionChangedCallback( SfCalendar calendar, DateTime? date, CalendarResource? resource) { - final CalendarSelectionDetails calendarSelectionDetails = - CalendarSelectionDetails(date, resource); - calendar.onSelectionChanged!(calendarSelectionDetails); + calendar.onSelectionChanged!(CalendarSelectionDetails(date, resource)); } /// method that raises the visible dates changed callback with the given /// parameters static void raiseViewChangedCallback( SfCalendar calendar, List visibleDates) { - final ViewChangedDetails viewChangedDetails = - ViewChangedDetails(visibleDates); - calendar.onViewChanged!(viewChangedDetails); + calendar.onViewChanged!(ViewChangedDetails(visibleDates)); } /// Check the calendar view is timeline view or not. @@ -534,18 +513,27 @@ class CalendarViewHelper { static List getCustomAppointments( List? appointments) { final List customAppointments = []; - if (appointments != null) { - for (int i = 0; i < appointments.length; i++) { - final CalendarAppointment appointment = appointments[i]; - customAppointments.add(appointment.data ?? appointment); - } - + if (appointments == null) { return customAppointments; } + for (int i = 0; i < appointments.length; i++) { + customAppointments.add(getAppointmentDetail(appointments[i])); + } + return customAppointments; } + /// Returns the appointment details with given appointment type. + static dynamic getAppointmentDetail(CalendarAppointment appointment) { + if (appointment.recurrenceRule != null && + appointment.recurrenceRule!.isNotEmpty) { + return appointment.convertToCalendarAppointment(); + } else { + return appointment.data; + } + } + /// Returns the index of the passed id's resource from the passed resource /// collection. static int getResourceIndex( @@ -554,7 +542,8 @@ class CalendarViewHelper { return -1; } - return resourceCollection.indexWhere((resource) => resource.id == id); + return resourceCollection + .indexWhere((CalendarResource resource) => resource.id == id); } /// Check the date in between first and last date @@ -563,7 +552,7 @@ class CalendarViewHelper { if (startDate.isAfter(endDate)) { final dynamic temp = startDate; startDate = endDate; - endDate = temp; + endDate = DateTimeHelper.getDateTimeValue(temp); } if (isSameOrBeforeDateTime(endDate, date) && @@ -593,6 +582,53 @@ class CalendarViewHelper { return CalendarViewHelper.isSameTimeSlot(firstDate, date) || firstDate.isBefore(date); } + + /// Method to switch the views based on the keyboard interaction. + static KeyEventResult handleViewSwitchKeyBoardEvent(RawKeyEvent event, + CalendarController controller, List? allowedViews) { + /// Ctrl + and Ctrl - used by browser to zoom the page, hence as referred + /// EJ2 scheduler, we have used alt + numeric to switch between views in + /// calendar web and windows + CalendarView view = controller.view!; + if (event.isAltPressed) { + if (event.logicalKey == LogicalKeyboardKey.digit1) { + view = CalendarView.day; + } else if (event.logicalKey == LogicalKeyboardKey.digit2) { + view = CalendarView.week; + } else if (event.logicalKey == LogicalKeyboardKey.digit3) { + view = CalendarView.workWeek; + } else if (event.logicalKey == LogicalKeyboardKey.digit4) { + view = CalendarView.month; + } else if (event.logicalKey == LogicalKeyboardKey.digit5) { + view = CalendarView.timelineDay; + } else if (event.logicalKey == LogicalKeyboardKey.digit6) { + view = CalendarView.timelineWeek; + } else if (event.logicalKey == LogicalKeyboardKey.digit7) { + view = CalendarView.timelineWorkWeek; + } else if (event.logicalKey == LogicalKeyboardKey.digit8) { + view = CalendarView.timelineMonth; + } else if (event.logicalKey == LogicalKeyboardKey.digit9) { + view = CalendarView.schedule; + } + } + + if (allowedViews != null && + allowedViews.isNotEmpty && + !allowedViews.contains(view)) { + return KeyEventResult.ignored; + } + + controller.view = view; + return KeyEventResult.handled; + } + + /// Check the showWeekNumber is true or not and returns the position. + static double getWeekNumberPanelWidth( + bool showWeekNumber, double width, bool isMobilePlatform) { + return showWeekNumber + ? (width / (DateTime.daysPerWeek + 1)) / (isMobilePlatform ? 1.3 : 4) + : 0; + } } /// Args to get and update the required properties from calendar state to it's @@ -679,6 +715,8 @@ class CalendarAppointment { this.notes, this.location, this.resourceIds, + this.recurrenceId, + this.id, required this.startTime, required this.endTime, this.subject = '', @@ -754,6 +792,15 @@ class CalendarAppointment { /// The ids of the [CalendarResource] that shares this [CalendarAppointment]. List? resourceIds; + /// Defines the recurrence id that + /// used to create an exception for recurrence appointment in [SfCalendar]. + Object? recurrenceId; + + /// Defines the id for [Appointment] in [SfCalendar]. + /// + /// Defaults to hashCode. + Object? id; + /// Holds the parent appointment details Object? data; @@ -793,6 +840,8 @@ class CalendarAppointment { recurrenceRule: recurrenceRule, isAllDay: isAllDay, resourceIds: resourceIds, + recurrenceId: recurrenceId, + id: id, startTimeZone: startTimeZone, endTimeZone: endTimeZone, notes: notes, @@ -801,6 +850,7 @@ class CalendarAppointment { } @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(dynamic other) { if (identical(this, other)) { return true; @@ -809,7 +859,10 @@ class CalendarAppointment { return false; } - final CalendarAppointment otherAppointment = other; + late final CalendarAppointment otherAppointment; + if (other is CalendarAppointment) { + otherAppointment = other; + } return CalendarViewHelper.isSameTimeSlot( otherAppointment.startTime, startTime) && CalendarViewHelper.isSameTimeSlot(otherAppointment.endTime, endTime) && @@ -824,6 +877,8 @@ class CalendarAppointment { otherAppointment.notes == notes && otherAppointment.location == location && otherAppointment.resourceIds == resourceIds && + otherAppointment.recurrenceId == recurrenceId && + otherAppointment.id == otherAppointment.id && otherAppointment.subject == subject && otherAppointment.color == color && CalendarViewHelper.isDateCollectionEqual( @@ -832,6 +887,7 @@ class CalendarAppointment { } @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes int get hashCode { return hashValues( startTimeZone, @@ -841,6 +897,8 @@ class CalendarAppointment { notes, location, hashList(resourceIds), + recurrenceId, + id, startTime, endTime, subject, @@ -952,6 +1010,7 @@ class CalendarTimeRegion { } @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(dynamic other) { if (identical(this, other)) { return true; @@ -960,7 +1019,10 @@ class CalendarTimeRegion { return false; } - final CalendarTimeRegion region = other; + late final CalendarTimeRegion region; + if (other is CalendarTimeRegion) { + region = other; + } return region.textStyle == textStyle && CalendarViewHelper.isSameTimeSlot(region.startTime, startTime) && CalendarViewHelper.isSameTimeSlot(region.endTime, endTime) && @@ -980,6 +1042,7 @@ class CalendarTimeRegion { } @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes int get hashCode { return hashValues( startTime, diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/common/date_time_engine.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/common/date_time_engine.dart index 13ffd9803..a5891fd90 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/common/date_time_engine.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/common/date_time_engine.dart @@ -47,24 +47,24 @@ class DateTimeHelper { CalendarView calendarView, int numberOfWeeksInView, DateTime date) { switch (calendarView) { case CalendarView.month: - case CalendarView.timelineMonth: { - /// The timeline month view renders the current month dates alone, - /// hence it doesn't support the numberOfWeekInView. - return numberOfWeeksInView == 6 || - calendarView == CalendarView.timelineMonth - ? getNextMonthDate(date) - : addDays(date, numberOfWeeksInView * DateTime.daysPerWeek); + return numberOfWeeksInView == 6 + ? DateTimeHelper.getDateTimeValue(getNextMonthDate(date)) + : DateTimeHelper.getDateTimeValue( + addDays(date, numberOfWeeksInView * DateTime.daysPerWeek)); } + case CalendarView.timelineMonth: + return DateTimeHelper.getDateTimeValue(getNextMonthDate(date)); case CalendarView.week: case CalendarView.workWeek: case CalendarView.timelineWeek: case CalendarView.timelineWorkWeek: - return addDays(date, DateTime.daysPerWeek); + return DateTimeHelper.getDateTimeValue( + addDays(date, DateTime.daysPerWeek)); case CalendarView.day: case CalendarView.timelineDay: case CalendarView.schedule: - return addDays(date, 1); + return DateTimeHelper.getDateTimeValue(addDays(date, 1)); } } @@ -73,39 +73,43 @@ class DateTimeHelper { CalendarView calendarView, int numberOfWeeksInView, DateTime date) { switch (calendarView) { case CalendarView.month: - case CalendarView.timelineMonth: { - return numberOfWeeksInView == 6 || - calendarView == CalendarView.timelineMonth - ? getPreviousMonthDate(date) - : addDays(date, -numberOfWeeksInView * DateTime.daysPerWeek); + return numberOfWeeksInView == 6 + ? DateTimeHelper.getDateTimeValue(getPreviousMonthDate(date)) + : DateTimeHelper.getDateTimeValue( + addDays(date, -numberOfWeeksInView * DateTime.daysPerWeek)); } + case CalendarView.timelineMonth: + return DateTimeHelper.getDateTimeValue(getPreviousMonthDate(date)); case CalendarView.timelineWeek: case CalendarView.timelineWorkWeek: case CalendarView.week: case CalendarView.workWeek: - return addDays(date, -DateTime.daysPerWeek); + return DateTimeHelper.getDateTimeValue( + addDays(date, -DateTime.daysPerWeek)); case CalendarView.day: case CalendarView.timelineDay: case CalendarView.schedule: - return addDays(date, -1); + return DateTimeHelper.getDateTimeValue(addDays(date, -1)); } } static DateTime _getPreviousValidDate( DateTime prevViewDate, List nonWorkingDays) { - DateTime previousDate = addDays(prevViewDate, -1); + DateTime previousDate = + DateTimeHelper.getDateTimeValue(addDays(prevViewDate, -1)); while (nonWorkingDays.contains(previousDate.weekday)) { - previousDate = addDays(previousDate, -1); + previousDate = DateTimeHelper.getDateTimeValue(addDays(previousDate, -1)); } return previousDate; } static DateTime _getNextValidDate( DateTime nextDate, List nonWorkingDays) { - DateTime nextViewDate = addDays(nextDate, 1); + DateTime nextViewDate = + DateTimeHelper.getDateTimeValue(addDays(nextDate, 1)); while (nonWorkingDays.contains(nextViewDate.weekday)) { - nextViewDate = addDays(nextViewDate, 1); + nextViewDate = DateTimeHelper.getDateTimeValue(addDays(nextViewDate, 1)); } return nextViewDate; } @@ -168,17 +172,17 @@ class DateTimeHelper { switch (calendarView) { case CalendarView.month: - case CalendarView.timelineMonth: { - if (numberOfWeeksInView != 6 || - calendarView == CalendarView.timelineMonth) { - final DateTime prevViewDate = addDays(visibleDates[0], -1); + if (numberOfWeeksInView != 6) { + final DateTime prevViewDate = + DateTimeHelper.getDateTimeValue(addDays(visibleDates[0], -1)); if (!isSameOrAfterDate(minDate, prevViewDate)) { return false; } } else { final DateTime currentDate = visibleDates[visibleDates.length ~/ 2]; - final DateTime previousDate = getPreviousMonthDate(currentDate); + final DateTime previousDate = DateTimeHelper.getDateTimeValue( + getPreviousMonthDate(currentDate)); if ((previousDate.month < minDate.month && previousDate.year == minDate.year) || previousDate.year < minDate.year) { @@ -187,13 +191,23 @@ class DateTimeHelper { } } break; + case CalendarView.timelineMonth: + { + final DateTime prevViewDate = + DateTimeHelper.getDateTimeValue(addDays(visibleDates[0], -1)); + if (!isSameOrAfterDate(minDate, prevViewDate)) { + return false; + } + } + break; case CalendarView.day: case CalendarView.week: case CalendarView.timelineDay: case CalendarView.timelineWeek: { DateTime prevViewDate = visibleDates[0]; - prevViewDate = addDays(prevViewDate, -1); + prevViewDate = + DateTimeHelper.getDateTimeValue(addDays(prevViewDate, -1)); if (!isSameOrAfterDate(minDate, prevViewDate)) { return false; } @@ -232,18 +246,17 @@ class DateTimeHelper { switch (calendarView) { case CalendarView.month: - case CalendarView.timelineMonth: { - if (numberOfWeeksInView != 6 || - calendarView == CalendarView.timelineMonth) { - final DateTime nextViewDate = - addDays(visibleDates[visibleDates.length - 1], 1); + if (numberOfWeeksInView != 6) { + final DateTime nextViewDate = DateTimeHelper.getDateTimeValue( + addDays(visibleDates[visibleDates.length - 1], 1)); if (!isSameOrBeforeDate(maxDate, nextViewDate)) { return false; } } else { final DateTime currentDate = visibleDates[visibleDates.length ~/ 2]; - final DateTime nextDate = getNextMonthDate(currentDate); + final DateTime nextDate = + DateTimeHelper.getDateTimeValue(getNextMonthDate(currentDate)); if ((nextDate.month > maxDate.month && nextDate.year == maxDate.year) || nextDate.year > maxDate.year) { @@ -252,13 +265,22 @@ class DateTimeHelper { } } break; + case CalendarView.timelineMonth: + { + final DateTime nextViewDate = DateTimeHelper.getDateTimeValue( + addDays(visibleDates[visibleDates.length - 1], 1)); + if (!isSameOrBeforeDate(maxDate, nextViewDate)) { + return false; + } + } + break; case CalendarView.day: case CalendarView.week: case CalendarView.timelineDay: case CalendarView.timelineWeek: { - final DateTime nextViewDate = - addDays(visibleDates[visibleDates.length - 1], 1); + final DateTime nextViewDate = DateTimeHelper.getDateTimeValue( + addDays(visibleDates[visibleDates.length - 1], 1)); if (!isSameOrBeforeDate(maxDate, nextViewDate)) { return false; } @@ -280,4 +302,36 @@ class DateTimeHelper { return true; } + + /// Converts the given dynamic data into date time data. + static DateTime getDateTimeValue(dynamic date) { + late final DateTime dateTimeData; + if (date is DateTime) { + dateTimeData = date; + } + + return dateTimeData; + } + + /// Returns week number for the given date. + static int getWeekNumberOfYear(DateTime date) { + final DateTime yearEndDate = DateTime(date.year - 1, 12, 31); + final int dayOfYear = date.difference(yearEndDate).inDays; + int weekNumber = (dayOfYear - date.weekday + 10) ~/ 7; + if (weekNumber < 1) { + weekNumber = getWeeksInYear(date.year - 1); + } else if (weekNumber > getWeeksInYear(date.year)) { + weekNumber = 1; + } + return weekNumber; + } + + /// Get the weeks in year + static int getWeeksInYear(int year) { + int P(int y) => (y + (y ~/ 4) - (y ~/ 100) + (y ~/ 400)) % 7; + if (P(year) == 4 || P(year - 1) == 3) { + return 53; + } + return 52; + } } diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/common/enums.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/common/enums.dart index 74f816e13..2d77a2eec 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/common/enums.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/common/enums.dart @@ -209,11 +209,11 @@ enum CalendarDataSourceAction { /// Available view navigation modes for [SfCalendar]. enum ViewNavigationMode { - /// - ViewNavigationMode.snap, Allows to switching to previous/next views + /// - ViewNavigationMode.snap, allows to switching to previous/next views /// through swipe interaction in SfCalendar. snap, - /// - ViewNavigationMode.none, Restrict the next or previous view dates + /// - ViewNavigationMode.none, restrict the next or previous view dates /// to be shown by swipe interaction in SfCalendar. /// /// It will not impact scrolling timeslot views, @@ -221,3 +221,23 @@ enum ViewNavigationMode { /// and [showNavigationArrow]. none } + +/// Available Appointment types for [Appointment] +enum AppointmentType { + /// - AppointmentType.changedOccurrence, this specifies the appointment is + /// a changed occurrence from the recurrence series. + changedOccurrence, + + /// AppointmentType.normal, this specifies a standard appointment. + normal, + + /// - AppointmentType.occurrence, this specifies the appointment is + /// an occurrence of pattern appointment which was created based on + /// RRule of the pattern appointment. + occurrence, + + /// - AppointmentType.pattern, this specifies the pattern appointment + /// which has the RRule value and it is the pattern for the + /// occurrence appointment in the recurrence series. + pattern +} diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/common/event_args.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/common/event_args.dart index bc2ec5bee..54378f1e6 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/common/event_args.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/common/event_args.dart @@ -122,6 +122,11 @@ typedef LoadMoreWidgetBuilder = Widget Function( /// that return a [Future] to indicate when their work is complete. typedef LoadMoreCallback = Future Function(); +/// Signature for a function that creates a widget based on resource +/// header details. +typedef ResourceViewHeaderBuilder = Widget Function( + BuildContext context, ResourceViewHeaderDetails details); + /// Contains the details that needed on month cell builder. class MonthCellDetails { /// Default constructor to store the details needed in month cell builder @@ -168,7 +173,7 @@ class CalendarAppointmentDetails { /// The appointment details associated with the appointment view widget. /// It holds more appointments when it is more appointment /// region [All day panel and Month cell more region]. - final Iterable appointments; + final Iterable appointments; /// Determines whether the widget replaces the more appointment region. /// It is applicable on the day, week, workweek views all day panel and @@ -192,6 +197,20 @@ class TimeRegionDetails { final Rect bounds; } +/// Contains the details that needed on resource view header builder. +class ResourceViewHeaderDetails { + /// Default constructor to store the details needed in resource view + /// header builder. + ResourceViewHeaderDetails(this.resource, this.bounds); + + /// The resource details such as display name, color, id and image associated + /// with the resource header widget. + final CalendarResource resource; + + /// The position and size of the widget. + final Rect bounds; +} + /// Signature for callback that reports that a current view or current visible /// dates changes. /// diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/resource_view/calendar_resource.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/resource_view/calendar_resource.dart index 44fbd65d9..11b1b0cc7 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/resource_view/calendar_resource.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/resource_view/calendar_resource.dart @@ -62,6 +62,7 @@ import 'package:flutter/material.dart'; ///} /// /// ``` +@immutable class CalendarResource with Diagnosticable { /// Creates an resource data for [SfCalendar]. /// @@ -229,7 +230,10 @@ class CalendarResource with Diagnosticable { return false; } - final CalendarResource resource = other; + late final CalendarResource resource; + if (other is CalendarResource) { + resource = other; + } return resource.displayName == displayName && resource.id == id && resource.image == image && diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/resource_view/resource_view.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/resource_view/resource_view.dart index acc9ff68a..ad8d91770 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/resource_view/resource_view.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/resource_view/resource_view.dart @@ -1,15 +1,19 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; +import 'package:syncfusion_flutter_calendar/calendar.dart'; import 'package:syncfusion_flutter_calendar/src/calendar/common/calendar_view_helper.dart'; import 'package:syncfusion_flutter_core/theme.dart'; +import '../common/calendar_view_helper.dart'; +import '../common/event_args.dart'; import '../settings/resource_view_settings.dart'; import 'calendar_resource.dart'; -/// Holds the resource views of the calendar. -class ResourceContainer extends CustomPainter { - /// Constructor to create the resource views of the calendar. - ResourceContainer( +/// Used to hold the resource view on all timeline views. +class ResourceViewWidget extends StatefulWidget { + /// Constructor to create the resource view widget to holds resource view on + /// all timeline views. + const ResourceViewWidget( this.resources, this.resourceViewSettings, this.resourceItemHeight, @@ -19,8 +23,10 @@ class ResourceContainer extends CustomPainter { this.isRTL, this.textScaleFactor, this.mouseHoverPosition, - this.imagePainterCollection) - : super(repaint: notifier); + this.imagePainterCollection, + this.width, + this.panelHeight, + this.resourceViewHeaderBuilder); /// Holds the resources of the calendar. final List? resources; @@ -51,12 +57,395 @@ class ResourceContainer extends CustomPainter { /// Holds the mouse hovering position used to paint highlight. final Offset? mouseHoverPosition; - Paint _circlePainter = Paint(); - TextPainter _namePainter = TextPainter(); - final double _borderThickness = 5; + + /// Defines the width of the resource view widget. + final double width; + + /// Defines the height of the resource view widget. + final double panelHeight; + + /// Used to build the widget that replaces the resource view header. + final ResourceViewHeaderBuilder? resourceViewHeaderBuilder; + + @override + _ResourceViewWidgetState createState() => _ResourceViewWidgetState(); +} + +class _ResourceViewWidgetState extends State { + @override + Widget build(BuildContext context) { + final List children = []; + if (widget.resourceViewHeaderBuilder != null) { + double yPosition = 0; + final int resourceLength = widget.resources!.length; + for (int i = 0; i < resourceLength; i++) { + final CalendarResource currentResource = widget.resources![i]; + final Widget child = widget.resourceViewHeaderBuilder!( + context, + ResourceViewHeaderDetails( + currentResource, + Rect.fromLTWH( + 0, yPosition, widget.width, widget.resourceItemHeight))); + children.add(RepaintBoundary(child: child)); + yPosition += widget.resourceItemHeight; + } + } + + return _ResourceViewRenderObjectWidget( + widget.resources, + widget.resourceViewSettings, + widget.resourceItemHeight, + widget.cellBorderColor, + widget.calendarTheme, + widget.notifier, + widget.isRTL, + widget.textScaleFactor, + widget.mouseHoverPosition, + widget.imagePainterCollection, + widget.width, + widget.panelHeight, + children: children, + ); + } +} + +class _ResourceViewRenderObjectWidget extends MultiChildRenderObjectWidget { + _ResourceViewRenderObjectWidget( + this.resources, + this.resourceViewSettings, + this.resourceItemHeight, + this.cellBorderColor, + this.calendarTheme, + this.notifier, + this.isRTL, + this.textScaleFactor, + this.mouseHoverPosition, + this.imagePainterCollection, + this.width, + this.panelHeight, + {List children = const []}) + : super(children: children); + + final List? resources; + final ResourceViewSettings resourceViewSettings; + final double resourceItemHeight; + final Color? cellBorderColor; + final SfCalendarThemeData calendarTheme; + final ValueNotifier notifier; + final bool isRTL; + final double textScaleFactor; + final Offset? mouseHoverPosition; + final Map imagePainterCollection; + final double width; + final double panelHeight; + + @override + _ResourceViewRenderObject createRenderObject(BuildContext context) { + return _ResourceViewRenderObject( + resources, + resourceViewSettings, + resourceItemHeight, + cellBorderColor, + calendarTheme, + notifier, + isRTL, + textScaleFactor, + mouseHoverPosition, + imagePainterCollection, + width, + panelHeight); + } + + @override + void updateRenderObject( + BuildContext context, _ResourceViewRenderObject renderObject) { + renderObject + ..resources = resources + ..resourceViewSettings = resourceViewSettings + ..resourceItemHeight = resourceItemHeight + ..cellBorderColor = cellBorderColor + ..calendarTheme = calendarTheme + ..notifier = notifier + ..isRTL = isRTL + ..textScaleFactor = textScaleFactor + ..mouseHoverPosition = mouseHoverPosition + ..imagePainterCollection = imagePainterCollection + ..width = width + ..panelHeight = panelHeight; + } +} + +class _ResourceViewRenderObject extends CustomCalendarRenderObject { + _ResourceViewRenderObject( + this._resources, + this._resourceViewSettings, + this._resourceItemHeight, + this._cellBorderColor, + this._calendarTheme, + this._notifier, + this._isRTL, + this._textScaleFactor, + this._mouseHoverPosition, + this._imagePainterCollection, + this._width, + this._panelHeight); + + List? _resources; + + List? get resources => _resources; + + set resources(List? value) { + if (_resources == value) { + return; + } + + _resources = value; + if (childCount == 0) { + markNeedsPaint(); + } else { + markNeedsLayout(); + } + } + + ResourceViewSettings _resourceViewSettings; + + ResourceViewSettings get resourceViewSettings => _resourceViewSettings; + + set resourceViewSettings(ResourceViewSettings value) { + if (_resourceViewSettings == value) { + return; + } + + _resourceViewSettings = value; + markNeedsPaint(); + } + + double _resourceItemHeight; + + double get resourceItemHeight => _resourceItemHeight; + + set resourceItemHeight(double value) { + if (_resourceItemHeight == value) { + return; + } + + _resourceItemHeight = value; + if (childCount == 0) { + markNeedsPaint(); + } else { + markNeedsLayout(); + } + } + + Color? _cellBorderColor; + + Color? get cellBorderColor => _cellBorderColor; + + set cellBorderColor(Color? value) { + if (_cellBorderColor == value) { + return; + } + + _cellBorderColor = value; + if (childCount != 0) { + return; + } + + markNeedsPaint(); + } + + SfCalendarThemeData _calendarTheme; + + SfCalendarThemeData get calendarTheme => _calendarTheme; + + set calendarTheme(SfCalendarThemeData value) { + if (_calendarTheme == value) { + return; + } + + _calendarTheme = value; + if (childCount != 0) { + return; + } + + markNeedsPaint(); + } + + ValueNotifier _notifier; + + ValueNotifier get notifier => _notifier; + + set notifier(ValueNotifier value) { + if (_notifier == value) { + return; + } + + _notifier.removeListener(markNeedsPaint); + _notifier = value; + _notifier.addListener(markNeedsPaint); + } + + bool _isRTL; + + bool get isRTL => _isRTL; + + set isRTL(bool value) { + if (_isRTL == value) { + return; + } + + _isRTL = value; + if (childCount == 0) { + markNeedsPaint(); + } else { + markNeedsLayout(); + } + } + + double _textScaleFactor; + + double get textScaleFactor => _textScaleFactor; + + set textScaleFactor(double value) { + if (_textScaleFactor == value) { + return; + } + + _textScaleFactor = value; + if (childCount != 0) { + return; + } + markNeedsPaint(); + } + + Offset? _mouseHoverPosition; + + Offset? get mouseHoverPosition => _mouseHoverPosition; + + set mouseHoverPosition(Offset? value) { + if (_mouseHoverPosition == value) { + return; + } + + _mouseHoverPosition = value; + if (childCount == 0) { + markNeedsPaint(); + } else { + markNeedsLayout(); + } + } + + Map _imagePainterCollection; + + Map get imagePainterCollection => + _imagePainterCollection; + + set imagePainterCollection(Map value) { + if (_imagePainterCollection == value) { + return; + } + + _imagePainterCollection = value; + if (childCount != 0) { + return; + } + markNeedsPaint(); + } + + double _width; + + double get width => _width; + + set width(double value) { + if (_width == value) { + return; + } + + _width = value; + if (childCount == 0) { + markNeedsPaint(); + } else { + markNeedsLayout(); + } + } + + double _panelHeight; + + double get panelHeight => _panelHeight; + + set panelHeight(double value) { + if (_panelHeight == value) { + return; + } + + _panelHeight = value; + if (childCount == 0) { + markNeedsPaint(); + } else { + markNeedsLayout(); + } + } + + @override + void attach(PipelineOwner owner) { + super.attach(owner); + _notifier.addListener(markNeedsPaint); + } @override - void paint(Canvas canvas, Size size) { + void detach() { + _notifier.removeListener(markNeedsPaint); + super.detach(); + } + + @override + void performLayout() { + final Size widgetSize = constraints.biggest; + size = Size(widgetSize.width.isInfinite ? width : widgetSize.width, + widgetSize.height.isInfinite ? panelHeight : widgetSize.height); + + for (dynamic child = firstChild; child != null; child = childAfter(child)) { + child.layout(constraints.copyWith( + minWidth: width, + minHeight: resourceItemHeight, + maxWidth: width, + maxHeight: resourceItemHeight)); + } + } + + @override + void paint(PaintingContext context, Offset offset) { + final bool _isNeedCustomPaint = childCount != 0; + + if (!_isNeedCustomPaint) { + _resourceViewHeader(context.canvas, size); + } else { + double yPosition = 0; + RenderBox? child = firstChild; + final int resourceLength = resources!.length; + for (int i = 0; i < resourceLength; i++) { + context.paintChild(child!, Offset(0, yPosition)); + child = childAfter(child); + + if (mouseHoverPosition != null) { + final Color resourceHoveringColor = + (calendarTheme.brightness == Brightness.dark + ? Colors.white + : Colors.black87) + .withOpacity(0.04); + _addHovering(context.canvas, size, yPosition, resourceHoveringColor); + } + + yPosition += resourceItemHeight; + } + } + } + + final Paint _circlePainter = Paint(); + final TextPainter _namePainter = TextPainter(); + final double _borderThickness = 5; + + void _resourceViewHeader(Canvas canvas, Size size) { canvas.clipRect(Rect.fromLTWH(0, 0, size.width, size.height)); /// The circle width. @@ -69,7 +458,17 @@ class ResourceContainer extends CustomPainter { final double radius = actualItemHeight < actualItemWidth ? actualItemHeight / 2 : actualItemWidth / 2; - _circlePainter.color = cellBorderColor ?? calendarTheme.cellBorderColor; + final Color resourceCellBorderColor = + cellBorderColor ?? calendarTheme.cellBorderColor; + final Color resourceHoveringColor = + (calendarTheme.brightness == Brightness.dark + ? Colors.white + : Colors.black87) + .withOpacity(0.04); + final TextStyle displayNameTextStyle = + resourceViewSettings.displayNameTextStyle ?? + calendarTheme.displayNameTextStyle; + _circlePainter.color = resourceCellBorderColor; _circlePainter.strokeWidth = 0.5; _circlePainter.style = PaintingStyle.stroke; final double lineXPosition = isRTL ? 0.5 : size.width - 0.5; @@ -82,19 +481,19 @@ class ResourceContainer extends CustomPainter { final CalendarResource resource = resources![i]; _drawResourceBorder( resource, canvas, size, actualItemHeight, yPosition, radius); - _drawDisplayName( - resource, canvas, size, yPosition, actualItemHeight, radius); + _drawDisplayName(resource, displayNameTextStyle, canvas, size, + yPosition, actualItemHeight, radius); _circlePainter.style = PaintingStyle.fill; _drawInnerCircle(resource, canvas, size, actualItemWidth, actualItemHeight, yPosition); - _circlePainter.color = cellBorderColor ?? calendarTheme.cellBorderColor; + _circlePainter.color = resourceCellBorderColor; _circlePainter.strokeWidth = 0.5; _circlePainter.style = PaintingStyle.stroke; canvas.drawLine(Offset(0, yPosition), Offset(size.width, yPosition), _circlePainter); if (mouseHoverPosition != null) { - _addHovering(canvas, size, yPosition); + _addHovering(canvas, size, yPosition, resourceHoveringColor); } yPosition += resourceItemHeight; @@ -104,24 +503,22 @@ class ResourceContainer extends CustomPainter { for (int i = 0; i < count; i++) { final CalendarResource resource = resources![i]; _drawResourceBackground(canvas, size, resource, yPosition); - _drawDisplayName( - resource, canvas, size, yPosition, actualItemHeight, radius); - _addHovering(canvas, size, yPosition); + _drawDisplayName(resource, displayNameTextStyle, canvas, size, + yPosition, actualItemHeight, radius); + _addHovering(canvas, size, yPosition, resourceHoveringColor); yPosition += resourceItemHeight; } } } - void _addHovering(Canvas canvas, Size size, double yPosition) { + void _addHovering( + Canvas canvas, Size size, double yPosition, Color resourceHoveringColor) { if (mouseHoverPosition != null && mouseHoverPosition!.dy > yPosition && mouseHoverPosition!.dy < (yPosition + resourceItemHeight)) { _circlePainter.style = PaintingStyle.fill; - _circlePainter.color = (calendarTheme.brightness == Brightness.dark - ? Colors.white - : Colors.black87) - .withOpacity(0.04); - final double padding = 0.5; + _circlePainter.color = resourceHoveringColor; + const double padding = 0.5; canvas.drawRect( Rect.fromLTWH(0, yPosition, size.width, resourceItemHeight - padding), _circlePainter); @@ -130,7 +527,7 @@ class ResourceContainer extends CustomPainter { void _drawResourceBackground( Canvas canvas, Size size, CalendarResource resource, double yPosition) { - final double padding = 0.5; + const double padding = 0.5; _circlePainter.color = resource.color; _circlePainter.style = PaintingStyle.fill; canvas.drawRect( @@ -165,11 +562,14 @@ class ResourceContainer extends CustomPainter { } /// Draws the display name of the resource under the circle. - void _drawDisplayName(CalendarResource resource, Canvas canvas, Size size, - double yPosition, double actualItemHeight, double radius) { - final TextStyle displayNameTextStyle = - resourceViewSettings.displayNameTextStyle ?? - calendarTheme.displayNameTextStyle; + void _drawDisplayName( + CalendarResource resource, + TextStyle displayNameTextStyle, + Canvas canvas, + Size size, + double yPosition, + double actualItemHeight, + double radius) { final TextSpan span = TextSpan(text: resource.displayName, style: displayNameTextStyle); _updateNamePainter(span); @@ -242,7 +642,7 @@ class ResourceContainer extends CustomPainter { /// display name, and fills the resources color in background. void _drawInnerCircle(CalendarResource resource, Canvas canvas, Size size, double actualItemWidth, double actualItemHeight, double yPosition) { - final double padding = 0.3; + const double padding = 0.3; final double innerCircleWidth = actualItemWidth - (_borderThickness * 2) - (padding * 2); final double innerCircleHeight = @@ -270,7 +670,7 @@ class ResourceContainer extends CustomPainter { : splitName[0].substring(0, 1); final TextSpan span = TextSpan( text: shortName, - style: TextStyle( + style: const TextStyle( color: Colors.white, fontSize: 20, fontWeight: FontWeight.w500, @@ -284,16 +684,6 @@ class ResourceContainer extends CustomPainter { _namePainter.paint(canvas, Offset(startXPosition, startYPosition)); } - @override - bool shouldRepaint(ResourceContainer oldDelegate) { - final ResourceContainer oldWidget = oldDelegate; - return oldWidget.resourceItemHeight != resourceItemHeight || - !CalendarViewHelper.isResourceCollectionEqual( - oldWidget.resources, resources) || - oldWidget.resourceViewSettings != resourceViewSettings || - oldWidget.mouseHoverPosition != mouseHoverPosition; - } - List _getSemanticsBuilder(Size size) { final List semanticsBuilder = []; @@ -326,12 +716,4 @@ class ResourceContainer extends CustomPainter { return _getSemanticsBuilder(size); }; } - - @override - bool shouldRebuildSemantics(ResourceContainer oldDelegate) { - final ResourceContainer oldWidget = oldDelegate; - return oldWidget.resourceItemHeight != resourceItemHeight || - oldWidget.resources != resources || - oldWidget.resourceViewSettings != resourceViewSettings; - } } diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/header_style.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/header_style.dart index 0c2fbd02d..27d391096 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/header_style.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/header_style.dart @@ -101,7 +101,10 @@ class CalendarHeaderStyle with Diagnosticable { return false; } - final CalendarHeaderStyle otherStyle = other; + late final CalendarHeaderStyle otherStyle; + if (other is CalendarHeaderStyle) { + otherStyle = other; + } return otherStyle.textStyle == textStyle && otherStyle.textAlign == textAlign && otherStyle.backgroundColor == backgroundColor; diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/month_view_settings.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/month_view_settings.dart index 31ef396a6..6f5fa6a9a 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/month_view_settings.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/month_view_settings.dart @@ -44,7 +44,11 @@ class MonthViewSettings with Diagnosticable { this.showTrailingAndLeadingDates = true, this.agendaViewHeight = -1, this.monthCellStyle = const MonthCellStyle(), - this.agendaStyle = const AgendaStyle()}); + this.agendaStyle = const AgendaStyle()}) + : assert(appointmentDisplayCount >= 0), + assert(numberOfWeeksInView >= 1 && numberOfWeeksInView <= 6), + assert(agendaItemHeight >= -1), + assert(agendaViewHeight >= -1); /// Formats the text in the [SfCalendar] month view view header. /// @@ -237,7 +241,7 @@ class MonthViewSettings with Diagnosticable { /// )); /// /// return DataSource(appointments); - /// }S + /// } /// final MonthCellStyle monthCellStyle; @@ -465,7 +469,10 @@ class MonthViewSettings with Diagnosticable { return false; } - final MonthViewSettings otherSetting = other; + late final MonthViewSettings otherSetting; + if (other is MonthViewSettings) { + otherSetting = other; + } return otherSetting.dayFormat == dayFormat && otherSetting.monthCellStyle == monthCellStyle && otherSetting.agendaStyle == agendaStyle && @@ -739,7 +746,10 @@ class AgendaStyle with Diagnosticable { return false; } - final AgendaStyle otherStyle = other; + late final AgendaStyle otherStyle; + if (other is AgendaStyle) { + otherStyle = other; + } return otherStyle.appointmentTextStyle == appointmentTextStyle && otherStyle.dayTextStyle == dayTextStyle && otherStyle.dateTextStyle == dateTextStyle && @@ -1186,7 +1196,10 @@ class MonthCellStyle with Diagnosticable { return false; } - final MonthCellStyle otherStyle = other; + late final MonthCellStyle otherStyle; + if (other is MonthCellStyle) { + otherStyle = other; + } return otherStyle.textStyle == textStyle && otherStyle.trailingDatesTextStyle == trailingDatesTextStyle && otherStyle.leadingDatesTextStyle == leadingDatesTextStyle && diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/resource_view_settings.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/resource_view_settings.dart index 55bc3de7b..b35486033 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/resource_view_settings.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/resource_view_settings.dart @@ -68,7 +68,9 @@ class ResourceViewSettings with Diagnosticable { {this.size = 75, this.visibleResourceCount = -1, this.showAvatar = true, - this.displayNameTextStyle}); + this.displayNameTextStyle}) + : assert(size >= 0), + assert(visibleResourceCount >= -1); /// The number of resources to be displayed in the available screen height in /// [SfCalendar] @@ -210,7 +212,10 @@ class ResourceViewSettings with Diagnosticable { return false; } - final ResourceViewSettings otherStyle = other; + late final ResourceViewSettings otherStyle; + if (other is ResourceViewSettings) { + otherStyle = other; + } return otherStyle.size == size && otherStyle.visibleResourceCount == visibleResourceCount && otherStyle.showAvatar == showAvatar && diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/schedule_view_settings.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/schedule_view_settings.dart index b588e7d9c..25f0834a2 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/schedule_view_settings.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/schedule_view_settings.dart @@ -37,7 +37,8 @@ class ScheduleViewSettings with Diagnosticable { this.hideEmptyScheduleWeek = false, this.monthHeaderSettings = const MonthHeaderSettings(), this.weekHeaderSettings = const WeekHeaderSettings(), - this.dayHeaderSettings = const DayHeaderSettings()}); + this.dayHeaderSettings = const DayHeaderSettings()}) + : assert(appointmentItemHeight >= -1); /// Sets the style to customize month label in [SfCalendar] schedule view. /// @@ -123,7 +124,6 @@ class ScheduleViewSettings with Diagnosticable { /// dayHeaderSettings: DayHeaderSettings( /// dayFormat: 'EEEE', /// width: 70, - /// backgroundColor: Colors.black, /// dayTextStyle: TextStyle( /// fontSize: 10, /// fontWeight: FontWeight.w300, @@ -218,7 +218,10 @@ class ScheduleViewSettings with Diagnosticable { return false; } - final ScheduleViewSettings otherStyle = other; + late final ScheduleViewSettings otherStyle; + if (other is ScheduleViewSettings) { + otherStyle = other; + } return otherStyle.appointmentTextStyle == appointmentTextStyle && otherStyle.appointmentItemHeight == appointmentItemHeight && otherStyle.hideEmptyScheduleWeek == hideEmptyScheduleWeek && @@ -295,7 +298,8 @@ class MonthHeaderSettings with Diagnosticable { this.height = 150, this.textAlign = TextAlign.start, this.backgroundColor = const Color.fromRGBO(17, 178, 199, 1), - this.monthTextStyle}); + this.monthTextStyle}) + : assert(height >= -1); /// Formats the month label text in the month label [SfCalendar] /// schedule view. @@ -424,7 +428,10 @@ class MonthHeaderSettings with Diagnosticable { return false; } - final MonthHeaderSettings otherStyle = other; + late final MonthHeaderSettings otherStyle; + if (other is MonthHeaderSettings) { + otherStyle = other; + } return otherStyle.monthFormat == monthFormat && otherStyle.height == height && otherStyle.textAlign == textAlign && @@ -492,7 +499,8 @@ class WeekHeaderSettings with Diagnosticable { this.height = 30, this.textAlign = TextAlign.start, this.backgroundColor = Colors.transparent, - this.weekTextStyle}); + this.weekTextStyle}) + : assert(height >= -1); /// Formats the week start date text in the week label of [SfCalendar] /// schedule view. @@ -645,7 +653,10 @@ class WeekHeaderSettings with Diagnosticable { return false; } - final WeekHeaderSettings otherStyle = other; + late final WeekHeaderSettings otherStyle; + if (other is WeekHeaderSettings) { + otherStyle = other; + } return otherStyle.startDateFormat == startDateFormat && otherStyle.height == height && otherStyle.textAlign == textAlign && @@ -689,7 +700,6 @@ class WeekHeaderSettings with Diagnosticable { /// dayHeaderSettings: DayHeaderSettings( /// dayFormat: 'EEEE', /// width: 70, -/// backgroundColor: Colors.black, /// dayTextStyle: TextStyle( /// fontSize: 10, /// fontWeight: FontWeight.w300, @@ -715,7 +725,8 @@ class DayHeaderSettings with Diagnosticable { {this.dayFormat = 'EEE', this.width = -1, this.dayTextStyle, - this.dateTextStyle}); + this.dateTextStyle}) + : assert(width >= -1); /// Formats the day text in the day label of [SfCalendar] schedule view. /// @@ -828,7 +839,10 @@ class DayHeaderSettings with Diagnosticable { return false; } - final DayHeaderSettings otherStyle = other; + late final DayHeaderSettings otherStyle; + if (other is DayHeaderSettings) { + otherStyle = other; + } return otherStyle.dayFormat == dayFormat && otherStyle.width == width && otherStyle.dayTextStyle == dayTextStyle && diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/time_region.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/time_region.dart index 4e4066b11..107efe12b 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/time_region.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/time_region.dart @@ -33,6 +33,7 @@ import 'package:syncfusion_flutter_datepicker/datepicker.dart' /// } /// /// ``` +@immutable class TimeRegion with Diagnosticable { /// Creates a Time region for timeslot views in calendar. /// @@ -474,7 +475,10 @@ class TimeRegion with Diagnosticable { return false; } - final TimeRegion region = other; + late final TimeRegion region; + if (other is TimeRegion) { + region = other; + } return region.textStyle == textStyle && region.startTime == startTime && region.endTime == endTime && diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/time_slot_view_settings.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/time_slot_view_settings.dart index c7a98e991..ba46ba0a5 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/time_slot_view_settings.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/time_slot_view_settings.dart @@ -49,7 +49,13 @@ class TimeSlotViewSettings with Diagnosticable { this.dateFormat = 'd', this.dayFormat = 'EE', this.timeRulerSize = -1, - this.timeTextStyle}); + this.timeTextStyle}) + : assert(startHour >= 0 && startHour <= 24), + assert(endHour >= 0 && endHour <= 24), + assert(timeIntervalHeight >= -1), + assert(timeIntervalWidth >= -2), + assert(timelineAppointmentHeight >= -1), + assert(timeRulerSize >= -1); /// The start hour for the time slot views in [SfCalendar]. /// @@ -479,7 +485,10 @@ class TimeSlotViewSettings with Diagnosticable { return false; } - final TimeSlotViewSettings otherStyle = other; + late final TimeSlotViewSettings otherStyle; + if (other is TimeSlotViewSettings) { + otherStyle = other; + } return otherStyle.startHour == startHour && otherStyle.endHour == endHour && otherStyle.nonWorkingDays == nonWorkingDays && diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/view_header_style.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/view_header_style.dart index b41bbb61c..2a865f64d 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/view_header_style.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/view_header_style.dart @@ -112,7 +112,10 @@ class ViewHeaderStyle with Diagnosticable { return false; } - final ViewHeaderStyle otherStyle = other; + late final ViewHeaderStyle otherStyle; + if (other is ViewHeaderStyle) { + otherStyle = other; + } return otherStyle.backgroundColor == backgroundColor && otherStyle.dayTextStyle == dayTextStyle && otherStyle.dateTextStyle == dateTextStyle; diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/week_number_style.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/week_number_style.dart new file mode 100644 index 000000000..15e16ba49 --- /dev/null +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/settings/week_number_style.dart @@ -0,0 +1,106 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +/// Sets the style to customize the week number text of [SfCalendar]. +/// +/// Allows to customize the [backgroundColor], [textStyle] in +/// week number of calendar. +/// +/// ```dart +/// +///Widget build(BuildContext context) { +/// return Container( +/// child: SfCalendar( +/// view: CalendarView.month, +/// showWeekNumber: true, +/// weekNumberStyle: WeekNumberStyle( +/// backgroundColor: Colors.blue, +/// textStyle: TextStyle(color: Colors.grey, fontSize: 20), +/// ), +/// ); +/// } +/// +/// ``` +@immutable +class WeekNumberStyle with Diagnosticable { + /// Creates a week number style for calendar. + /// + /// The properties allows to customize the week number [SfCalendar]. + const WeekNumberStyle({this.backgroundColor, this.textStyle}); + + /// The color that fills the background of the week number panel. + /// + /// Defaults to null. + /// + /// Using a [SfCalendarTheme] gives more fine-grained control over the + /// appearance of various components of the calendar + /// + /// ```dart + ///Widget build(BuildContext context) { + /// return Container( + /// child: SfCalendar( + /// view: CalendarView.month, + /// showWeekNumber: true, + /// weekNumberStyle: WeekNumberStyle( + /// backgroundColor: Colors.blue, + /// textStyle: TextStyle(color: Colors.grey, fontSize: 20), + /// ), + /// ); + /// } + /// ``` + final Color? backgroundColor; + + /// The text style to customize week number text of the SfCalendar. + /// + /// Defaults to null. + /// + /// Using a [SfCalendarTheme] gives more fine-grained control over the + /// appearance of various components of the calendar. + /// + /// ```dart + ///Widget build(BuildContext context) { + /// return Container( + /// child: SfCalendar( + /// view: CalendarView.month, + /// showWeekNumber: true, + /// weekNumberStyle: WeekNumberStyle( + /// backgroundColor: Colors.blue, + /// textStyle: TextStyle(color: Colors.grey, fontSize: 20), + /// ), + /// ); + /// } + /// ``` + final TextStyle? textStyle; + + @override + bool operator ==(dynamic other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + late final WeekNumberStyle otherStyle; + if (other is WeekNumberStyle) { + otherStyle = other; + } + return otherStyle.backgroundColor == backgroundColor && + otherStyle.textStyle == textStyle; + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(ColorProperty('backgroundColor', backgroundColor)); + properties.add(DiagnosticsProperty('dayTextStyle', textStyle)); + } + + @override + int get hashCode { + return hashValues( + backgroundColor, + textStyle, + ); + } +} diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/sfcalendar.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/sfcalendar.dart index 96066d455..7196f2e0e 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/sfcalendar.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/sfcalendar.dart @@ -36,6 +36,7 @@ import 'settings/schedule_view_settings.dart'; import 'settings/time_region.dart'; import 'settings/time_slot_view_settings.dart'; import 'settings/view_header_style.dart'; +import 'settings/week_number_style.dart'; import 'views/calendar_view.dart'; /// Specifies the unconfirmed ripple animation duration used on custom splash. @@ -95,6 +96,8 @@ typedef _CalendarHeaderCallback = void Function(double width); /// [CalendarHeaderStyle][ViewHeaderStyle][MonthViewSettings] /// [TimeSlotViewSettings][MonthCellStyle], [AgendaStyle]. /// +/// {@youtube 560 315 https://www.youtube.com/watch?v=3OROjbAQS8Y} +/// /// See also: /// [SfCalendarThemeData] /// [CalendarHeaderStyle] @@ -201,7 +204,16 @@ class SfCalendar extends StatefulWidget { this.specialRegions, this.loadMoreWidgetBuilder, this.blackoutDatesTextStyle, - }) : initialDisplayDate = initialDisplayDate ?? + this.showWeekNumber = false, + this.weekNumberStyle = const WeekNumberStyle(), + this.resourceViewHeaderBuilder, + }) : assert(firstDayOfWeek >= 1 && firstDayOfWeek <= 7), + assert(headerHeight >= 0), + assert(viewHeaderHeight >= -1), + assert(minDate == null || maxDate == null || minDate.isBefore(maxDate)), + assert(minDate == null || maxDate == null || maxDate.isAfter(minDate)), + assert(cellEndPadding >= 0), + initialDisplayDate = initialDisplayDate ?? DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day, 08, 45, 0), minDate = minDate ?? DateTime(01, 01, 01), @@ -235,10 +247,10 @@ class SfCalendar extends StatefulWidget { /// home: Scaffold( /// body: SfCalendar( /// controller: _controller, - /// dataSource: _dataSource), + /// dataSource: _dataSource(), /// loadMoreWidgetBuilder: (BuildContext context, - /// CalendarLoadMoreCallback loadMoreAppointments) { - /// return FutureBuilder( + /// LoadMoreCallback loadMoreAppointments) { + /// return FutureBuilder( /// initialData: 'loading', /// future: loadMoreAppointments(), /// builder: (context, snapShot) { @@ -460,13 +472,7 @@ class SfCalendar extends StatefulWidget { /// set to appointment. /// /// ```dart - /// CalendarController _controller; - /// - /// @override - /// void initState() { - /// _controller = CalendarController(); - /// super.initState(); - /// } + /// CalendarController _controller = CalendarController(); /// /// @override /// Widget build(BuildContext context) { @@ -567,14 +573,14 @@ class SfCalendar extends StatefulWidget { /// margin: EdgeInsets.all(1), /// alignment: Alignment.center, /// child: Text( - /// timeRegionDetails.region.text, + /// timeRegionDetails.region.text!, /// style: TextStyle(color: Colors.black), /// ), /// decoration: BoxDecoration( /// shape: BoxShape.rectangle, /// borderRadius: BorderRadius.all(Radius.circular(4.0)), /// gradient: LinearGradient( - /// colors: [timeRegionDetails.region.color, Colors.cyan], + /// colors: [timeRegionDetails.region.color!, Colors.cyan], /// begin: Alignment.centerRight, /// end: Alignment.centerLeft)), /// ); @@ -770,7 +776,7 @@ class SfCalendar extends StatefulWidget { /// view: CalendarView.schedule, /// scheduleViewSettings: ScheduleViewSettings( /// appointmentItemHeight: 60, - /// weekLabelSettings: WeekLabelSettings( + /// weekHeaderSettings: WeekHeaderSettings( /// height: 40, /// textAlign: TextAlign.center, /// )), @@ -976,7 +982,7 @@ class SfCalendar extends StatefulWidget { ///Widget build(BuildContext context) { /// return Container( /// child: SfCalendar( - /// view: CalendarView.Month, + /// view: CalendarView.month, /// todayTextStyle: TextStyle(fontStyle: FontStyle.italic, /// fontSize: 17, /// color: Colors.red), @@ -1111,7 +1117,7 @@ class SfCalendar extends StatefulWidget { /// dataSource: _getCalendarDataSource(), /// resourceViewSettings: ResourceViewSettings( /// visibleResourceCount: 4, - /// resourceViewSize: 150, + /// size: 150, /// displayNameTextStyle: TextStyle( /// fontStyle: FontStyle.italic, /// fontSize: 15, @@ -1266,7 +1272,7 @@ class SfCalendar extends StatefulWidget { /// width: 2), /// borderRadius: const BorderRadius.all(Radius.circular(4)), /// shape: BoxShape.rectangle, - /// );, + /// ), /// ), /// ); /// } @@ -1395,7 +1401,7 @@ class SfCalendar extends StatefulWidget { /// child: SfCalendar( /// view: CalendarView.week, /// onTap: (CalendarTapDetails details){ - /// DateTime date = details.date; + /// DateTime date = details.date!; /// dynamic appointments = details.appointments; /// CalendarElement view = details.targetElement; /// }, @@ -1422,7 +1428,7 @@ class SfCalendar extends StatefulWidget { /// child: SfCalendar( /// view: CalendarView.week, /// onLongPress: (CalendarLongPressDetails details){ - /// DateTime date = details.date; + /// DateTime date = details.date!; /// dynamic appointments = details.appointments; /// CalendarElement view = details.targetElement; /// }, @@ -1447,8 +1453,8 @@ class SfCalendar extends StatefulWidget { /// child: SfCalendar( /// view: CalendarView.timelineDay, /// onSelectionChanged: (CalendarSelectionDetails details){ - /// DateTime date = details.date; - /// CalendarResource resource = details.resource; + /// DateTime date = details.date!; + /// CalendarResource resource = details.resource!; /// }, /// ), /// ); @@ -1476,7 +1482,7 @@ class SfCalendar extends StatefulWidget { /// view: CalendarView.week, /// dataSource: _getCalendarDataSource(), /// timeSlotViewSettings: TimeSlotViewSettings( - /// appointmentTextStyle: TextStyle( + /// timeTextStyle: TextStyle( /// fontSize: 12, /// fontWeight: FontWeight.w500, /// color: Colors.blue, @@ -1542,6 +1548,96 @@ class SfCalendar extends StatefulWidget { /// ``` final List? specialRegions; + /// Used to displays the week number of the year in the month, week and + /// work week views of the SfCalendar. + /// + /// In the month view, it is displayed at the left side as a separate column, + /// whereas in the week and work week view, it is displayed beside the + /// view header panel of the calendar. + /// + /// Defaults to false + /// + /// see also: [weekNumberStyle] + /// + /// ``` dart + /// Widget build(BuildContext context) { + /// return Scaffold( + /// body: SfCalendar( + /// view: CalendarView.month, + /// showWeekNumber: true, + /// ), + /// ); + /// } + final bool showWeekNumber; + + /// Defines the text style for the text in the week number panel of the + /// SfCalendar. + /// + /// Using a [SfCalendarTheme] gives more fine-grained control over the + /// appearance of various components of the calendar. + /// + /// Defaults to null + /// + /// see also: [showWeekNumber] + /// + /// ``` dart + /// Widget build(BuildContext context) { + /// return Scaffold( + /// body: SfCalendar( + /// view: CalendarView.month, + /// showWeekNumber: true, + /// weekNumberStyle: WeekNumberStyle( + /// backgroundColor: Colors.blue, + /// textStyle: TextStyle(color: Colors.grey, fontSize: 20), + /// ), + /// ); + /// } + final WeekNumberStyle weekNumberStyle; + + /// Defines the builder that builds a widget and replaces the header + /// view of resource in SfCalendar. + /// + /// Defaults to null. + /// + /// see also: + /// [ResourceViewSettings]. + /// [CalendarResource] + /// + /// ``` dart + /// Widget build(BuildContext context) { + /// return MaterialApp( + /// home: Scaffold( + /// appBar: AppBar( + /// title: Text('Calendar'), + /// ), + /// body: SfCalendar( + /// view: CalendarView.timelineMonth, + /// resourceViewHeaderBuilder: + /// (BuildContext context, ResourceViewHeaderDetails details) { + /// if (details.resource.image != null) { + /// return Column( + /// mainAxisAlignment: MainAxisAlignment.center, + /// crossAxisAlignment: CrossAxisAlignment.center, + /// mainAxisSize: MainAxisSize.max, + /// children: [ + /// CircleAvatar(backgroundColor: details.resource.image), + /// Text(details.resource.displayName) + /// ], + /// ); + /// } else { + /// return Container( + /// color: details.resource.color, + /// child: Text( + /// details.resource.displayName + /// ), + /// ); + /// } + /// }), + /// ), + /// ); + /// } + final ResourceViewHeaderBuilder? resourceViewHeaderBuilder; + /// An object that used for programmatic date navigation and date selection /// in [SfCalendar]. /// @@ -1632,6 +1728,14 @@ class SfCalendar extends StatefulWidget { static List getRecurrenceDateTimeCollection( String rRule, DateTime recurrenceStartDate, {DateTime? specificStartDate, DateTime? specificEndDate}) { + assert(specificStartDate == null || + specificEndDate == null || + CalendarViewHelper.isSameOrBeforeDateTime( + specificEndDate, specificStartDate)); + assert(specificStartDate == null || + specificEndDate == null || + CalendarViewHelper.isSameOrAfterDateTime( + specificStartDate, specificEndDate)); return RecurrenceHelper.getRecurrenceDateTimeCollection( rRule, recurrenceStartDate, specificStartDate: specificStartDate, specificEndDate: specificEndDate); @@ -1673,7 +1777,8 @@ class SfCalendar extends StatefulWidget { /// /// ```dart /// - /// RecurrenceProperties recurrenceProperties = RecurrenceProperties(); + /// RecurrenceProperties recurrenceProperties = + /// RecurrenceProperties(startDate: DateTime.now()); ///recurrenceProperties.recurrenceType = RecurrenceType.daily; ///recurrenceProperties.recurrenceRange = RecurrenceRange.count; ///recurrenceProperties.interval = 2; @@ -1690,6 +1795,9 @@ class SfCalendar extends StatefulWidget { /// ``` static String generateRRule(RecurrenceProperties recurrenceProperties, DateTime appStartTime, DateTime appEndTime) { + assert(CalendarViewHelper.isSameOrBeforeDateTime(appEndTime, appStartTime)); + assert(CalendarViewHelper.isSameOrAfterDateTime(appStartTime, appEndTime)); + return RecurrenceHelper.generateRRule( recurrenceProperties, appStartTime, appEndTime); } @@ -1774,6 +1882,8 @@ class SfCalendar extends StatefulWidget { .toDiagnosticsNode(name: 'allowedViews')); properties.add(IterableDiagnostics(specialRegions) .toDiagnosticsNode(name: 'specialRegions')); + properties.add(DiagnosticsProperty( + 'resourceViewHeaderBuilder', resourceViewHeaderBuilder)); } } @@ -1805,11 +1915,14 @@ class _SfCalendarState extends State late ValueNotifier _resourceImageNotifier; /// Used to assign the forward list as center of scroll view. - Key _scheduleViewKey = UniqueKey(); + final Key _scheduleViewKey = UniqueKey(); /// Used to create the new scroll view for schedule calendar view. late Key _scrollKey; + /// Used to create the custom scroll view that holds calendar views. + final GlobalKey _customScrollViewKey = GlobalKey(); + /// Used to store the visible dates before the display date late List _previousDates; @@ -1869,7 +1982,7 @@ class _SfCalendarState extends State Animation? _fadeIn; /// Opacity of widget handles by fade animation. - ValueNotifier _opacity = ValueNotifier(1); + final ValueNotifier _opacity = ValueNotifier(1); /// Used to identify whether the load more function triggered or not. bool _isLoadMoreLoaded = false; @@ -1892,14 +2005,23 @@ class _SfCalendarState extends State /// set to [_maxDate] when the load more completed. DateTime? _scheduleMaxDate; + /// Focus node to maintain the focus for schedule view, when view changed + final FocusNode _focusNode = FocusNode(); + /// Collection used to store the resource collection and check the collection /// manipulations(add, remove, reset). List? _resourceCollection; /// The image painter collection to paint the resource images in view. - Map _imagePainterCollection = + final Map _imagePainterCollection = {}; + /// Used to indicate whether the time slot views(day, week, work week, + /// timeline views) needs scrolling or not when display date changed. + /// This value maintain the time slot view scrolling when calendar view + /// changed and view navigation(forward and backward). + bool _canScrollTimeSlotView = true; + @override void initState() { _timeZoneLoaded = false; @@ -1919,8 +2041,8 @@ class _SfCalendarState extends State _selectedDate = _controller.selectedDate; _agendaSelectedDate = ValueNotifier(_selectedDate); _agendaSelectedDate.addListener(_agendaSelectedDateListener); - _currentDate = getValidDate(widget.minDate, widget.maxDate, - _controller.displayDate ?? widget.initialDisplayDate); + _currentDate = DateTimeHelper.getDateTimeValue(getValidDate(widget.minDate, + widget.maxDate, _controller.displayDate ?? widget.initialDisplayDate)); _controller.displayDate = _currentDate; _scheduleDisplayDate = _controller.displayDate!; _controller.view ??= widget.view; @@ -1947,7 +2069,7 @@ class _SfCalendarState extends State CalendarViewHelper.shouldRaiseViewChangedCallback( widget.onViewChanged)) { CalendarViewHelper.raiseViewChangedCallback( - widget, []..add(_controller.displayDate!)); + widget, [_controller.displayDate!]); } _initScheduleViewProperties(); @@ -2003,8 +2125,8 @@ class _SfCalendarState extends State _controller.view = widget.controller!.view ?? _view; } else { _controller.selectedDate = widget.initialSelectedDate; - _currentDate = getValidDate( - widget.minDate, widget.maxDate, widget.initialDisplayDate); + _currentDate = DateTimeHelper.getDateTimeValue(getValidDate( + widget.minDate, widget.maxDate, widget.initialDisplayDate)); _controller.displayDate = _currentDate; _controller.view = widget.view; } @@ -2023,14 +2145,16 @@ class _SfCalendarState extends State _view != widget.controller!.view) { final CalendarView oldView = _view; _view = _controller.view ?? widget.view; - _currentDate = getValidDate( - widget.minDate, widget.maxDate, _updateCurrentDate(oldView)); + _currentDate = DateTimeHelper.getDateTimeValue(getValidDate( + widget.minDate, widget.maxDate, _updateCurrentDate(oldView))); + _canScrollTimeSlotView = false; _controller.displayDate = _currentDate; + _canScrollTimeSlotView = true; if (_view == CalendarView.schedule) { if (CalendarViewHelper.shouldRaiseViewChangedCallback( widget.onViewChanged)) { CalendarViewHelper.raiseViewChangedCallback( - widget, []..add(_controller.displayDate!)); + widget, [_controller.displayDate!]); } _agendaScrollController?.removeListener(_handleScheduleViewScrolled); @@ -2054,8 +2178,8 @@ class _SfCalendarState extends State widget.controller != null && oldWidget.controller!.displayDate != widget.controller!.displayDate) { if (_controller.displayDate != null) { - _currentDate = getValidDate( - widget.minDate, widget.maxDate, _controller.displayDate); + _currentDate = DateTimeHelper.getDateTimeValue(getValidDate( + widget.minDate, widget.maxDate, _controller.displayDate)); } _controller.displayDate = _currentDate; @@ -2099,14 +2223,14 @@ class _SfCalendarState extends State if (widget.monthViewSettings.numberOfWeeksInView != oldWidget.monthViewSettings.numberOfWeeksInView) { - _currentDate = getValidDate( - widget.minDate, widget.maxDate, _updateCurrentDate(_view)); + _currentDate = DateTimeHelper.getDateTimeValue(getValidDate( + widget.minDate, widget.maxDate, _updateCurrentDate(_view))); _controller.displayDate = _currentDate; if (_view == CalendarView.schedule) { if (CalendarViewHelper.shouldRaiseViewChangedCallback( widget.onViewChanged)) { CalendarViewHelper.raiseViewChangedCallback( - widget, []..add(_controller.displayDate!)); + widget, [_controller.displayDate!]); } _agendaScrollController?.removeListener(_handleScheduleViewScrolled); @@ -2137,25 +2261,26 @@ class _SfCalendarState extends State } } - if (!CalendarViewHelper.isResourceCollectionEqual( + if (!CalendarViewHelper.isCollectionEqual( widget.dataSource?.resources, _resourceCollection)) { _resourceCollection = CalendarViewHelper.cloneList(widget.dataSource?.resources); } - if ((oldWidget.minDate != widget.minDate) || - (oldWidget.maxDate != widget.maxDate)) { - _currentDate = getValidDate(widget.minDate, widget.maxDate, _currentDate); + if (oldWidget.minDate != widget.minDate || + oldWidget.maxDate != widget.maxDate) { + _currentDate = DateTimeHelper.getDateTimeValue( + getValidDate(widget.minDate, widget.maxDate, _currentDate)); if (_view == CalendarView.schedule) { _minDate = null; _maxDate = null; if (widget.loadMoreWidgetBuilder != null && _scheduleMinDate != null && _scheduleMaxDate != null) { - _scheduleMinDate = - getValidDate(widget.minDate, widget.maxDate, _scheduleMinDate); - _scheduleMaxDate = - getValidDate(widget.minDate, widget.maxDate, _scheduleMaxDate); + _scheduleMinDate = DateTimeHelper.getDateTimeValue( + getValidDate(widget.minDate, widget.maxDate, _scheduleMinDate)); + _scheduleMaxDate = DateTimeHelper.getDateTimeValue( + getValidDate(widget.minDate, widget.maxDate, _scheduleMaxDate)); } } } @@ -2288,6 +2413,7 @@ class _SfCalendarState extends State _controller.removePropertyChangedListener(_calendarValueChangedListener); _viewChangeNotifier.removeListener(_updateViewChangePopup); _viewChangeNotifier.dispose(); + _focusNode.dispose(); super.dispose(); } @@ -2406,6 +2532,7 @@ class _SfCalendarState extends State /// Handle the scroll view scroll changes to update header date value. void _handleScheduleViewScrolled() { _removeDatePicker(); + double widgetPosition = 0; final double scrolledPosition = _agendaScrollController!.position.pixels; @@ -2431,11 +2558,11 @@ class _SfCalendarState extends State /// update the header view date with latest date. if (interSectionPoint != -1 && scrolledPosition >= interSectionPoint) { - date = addDays(date, 6); + date = DateTimeHelper.getDateTimeValue(addDays(date, 6)); } - final DateTime currentViewDate = - getValidDate(widget.minDate, widget.maxDate, date); + final DateTime currentViewDate = DateTimeHelper.getDateTimeValue( + getValidDate(widget.minDate, widget.maxDate, date)); _currentDate = currentViewDate; if (currentViewDate.month != _headerUpdateNotifier.value!.month || currentViewDate.year != _headerUpdateNotifier.value!.year) { @@ -2470,11 +2597,11 @@ class _SfCalendarState extends State /// update the header view date with latest date. if (interSectionPoint != -1 && -scrolledPosition <= interSectionPoint) { - date = addDays(date, 6); + date = DateTimeHelper.getDateTimeValue(addDays(date, 6)); } - final DateTime currentViewDate = - getValidDate(widget.minDate, widget.maxDate, date); + final DateTime currentViewDate = DateTimeHelper.getDateTimeValue( + getValidDate(widget.minDate, widget.maxDate, date)); _currentDate = currentViewDate; if (currentViewDate.month != _headerUpdateNotifier.value!.month || currentViewDate.year != _headerUpdateNotifier.value!.year) { @@ -2565,25 +2692,41 @@ class _SfCalendarState extends State setState(() { final CalendarView oldView = _view; _view = _controller.view!; - _currentDate = getValidDate( - widget.minDate, widget.maxDate, _updateCurrentDate(oldView)); + _currentDate = DateTimeHelper.getDateTimeValue(getValidDate( + widget.minDate, widget.maxDate, _updateCurrentDate(oldView))); if (!isSameDate(_currentDate, _controller.displayDate)) { + _canScrollTimeSlotView = false; _controller.displayDate = _currentDate; + _canScrollTimeSlotView = true; } _fadeInController!.reset(); _fadeInController!.forward(); _agendaScrollController = ScrollController(initialScrollOffset: 0); + SchedulerBinding.instance?.addPostFrameCallback((_) { + final Widget? currentWidget = _customScrollViewKey.currentWidget; + + /// When view switched from schedule view to other views we need to + /// switch the focus to the custom scrolling panel. + if (currentWidget is CustomCalendarScrollView) { + currentWidget.updateFocus(); + } + }); if (_view == CalendarView.schedule) { _scheduleDisplayDate = _controller.displayDate!; if (CalendarViewHelper.shouldRaiseViewChangedCallback( widget.onViewChanged)) { CalendarViewHelper.raiseViewChangedCallback( - widget, []..add(_controller.displayDate!)); + widget, [_controller.displayDate!]); } _agendaScrollController?.removeListener(_handleScheduleViewScrolled); _initScheduleViewProperties(); + SchedulerBinding.instance?.addPostFrameCallback((_) { + if (!_focusNode.hasFocus) { + _focusNode.requestFocus(); + } + }); } else if (CalendarViewHelper.isResourceEnabled( widget.dataSource, _view)) { _resourcePanelScrollController ??= @@ -2664,6 +2807,12 @@ class _SfCalendarState extends State _currentViewVisibleDates[0], _currentViewVisibleDates[_currentViewVisibleDates.length - 1], _controller.displayDate)) { + if (_canScrollTimeSlotView && + _customScrollViewKey.currentWidget != null) { + // ignore: avoid_as + (_customScrollViewKey.currentWidget! as CustomCalendarScrollView) + .updateScrollPosition(); + } _currentDate = _controller.displayDate!; return; } @@ -2714,7 +2863,7 @@ class _SfCalendarState extends State } if (_isNeedLoadMore || _isScheduleStartLoadMore) { - SchedulerBinding.instance?.addPostFrameCallback((timeStamp) { + SchedulerBinding.instance?.addPostFrameCallback((Duration timeStamp) { setState(() { _isNeedLoadMore = false; _isScheduleStartLoadMore = false; @@ -2770,7 +2919,7 @@ class _SfCalendarState extends State case CalendarDataSourceAction.remove: { for (int i = 0; i < data.length; i++) { - final Object appointment = data[i]; + final dynamic appointment = data[i]; for (int j = 0; j < _appointments.length; j++) { if (_appointments[j].data == appointment) { _appointments.removeAt(j); @@ -2780,7 +2929,7 @@ class _SfCalendarState extends State } for (int i = 0; i < data.length; i++) { - final Object appointment = data[i]; + final dynamic appointment = data[i]; for (int j = 0; j < visibleAppointmentCollection.length; j++) { if (visibleAppointmentCollection[j].data == appointment) { visibleAppointmentCollection.removeAt(j); @@ -2832,7 +2981,7 @@ class _SfCalendarState extends State void _updateVisibleAppointmentCollection( List visibleAppointmentCollection) { if (_view == CalendarView.schedule) { - SchedulerBinding.instance?.addPostFrameCallback((timeStamp) { + SchedulerBinding.instance?.addPostFrameCallback((Duration timeStamp) { setState(() { /// Update the view when the appointment collection changed. }); @@ -2850,7 +2999,7 @@ class _SfCalendarState extends State /// Update all day appointment related implementation in calendar, /// because time label view needs the top position. _updateAllDayAppointment(); - SchedulerBinding.instance?.addPostFrameCallback((timeStamp) { + SchedulerBinding.instance?.addPostFrameCallback((Duration timeStamp) { setState(() { /// Update the UI. }); @@ -2880,12 +3029,15 @@ class _SfCalendarState extends State return _controller.displayDate ?? _currentDate; } + final DateTime visibleStartDate = _currentViewVisibleDates[0]; + final DateTime visibleEndDate = + _currentViewVisibleDates[_currentViewVisibleDates.length - 1]; + final bool isMonthView = + view == CalendarView.month || view == CalendarView.timelineMonth; if (_selectedDate != null && isDateWithInDateRange( - _currentViewVisibleDates[0], - _currentViewVisibleDates[_currentViewVisibleDates.length - 1], - _selectedDate)) { - if (view == CalendarView.month || view == CalendarView.timelineMonth) { + visibleStartDate, visibleEndDate, _selectedDate)) { + if (isMonthView) { return DateTime( _selectedDate!.year, _selectedDate!.month, @@ -2897,9 +3049,7 @@ class _SfCalendarState extends State return _selectedDate!; } } else if (isDateWithInDateRange( - _currentViewVisibleDates[0], - _currentViewVisibleDates[_currentViewVisibleDates.length - 1], - DateTime.now())) { + visibleStartDate, visibleEndDate, DateTime.now())) { final DateTime date = DateTime.now(); return DateTime( date.year, @@ -2909,10 +3059,10 @@ class _SfCalendarState extends State _controller.displayDate!.minute, _controller.displayDate!.second); } else { - if (view == CalendarView.month || view == CalendarView.timelineMonth) { + if (isMonthView) { if (widget.monthViewSettings.numberOfWeeksInView > 0 && widget.monthViewSettings.numberOfWeeksInView < 6) { - return _currentViewVisibleDates[0]; + return visibleStartDate; } return DateTime( _currentDate.year, @@ -2922,7 +3072,7 @@ class _SfCalendarState extends State _controller.displayDate!.minute, _controller.displayDate!.second); } else { - final DateTime date = _currentViewVisibleDates[0]; + final DateTime date = visibleStartDate; return DateTime( date.year, date.month, @@ -3175,7 +3325,7 @@ class _SfCalendarState extends State } // ignore: avoid_as - final RenderBox box = context.findRenderObject() as RenderBox; + final RenderBox box = context.findRenderObject()! as RenderBox; final Offset localPosition = box.globalToLocal(globalPosition); if (localPosition.dy < widget.headerHeight) { _updateMouseHoveringForHeader(localPosition); @@ -3417,10 +3567,11 @@ class _SfCalendarState extends State } if (!isExceptionDate) { - final DateTime recurrenceEndDate = addDuration( - recurrenceDate, - appointment.actualEndTime - .difference(appointment.actualStartTime)); + final DateTime recurrenceEndDate = DateTimeHelper.getDateTimeValue( + addDuration( + recurrenceDate, + appointment.actualEndTime + .difference(appointment.actualStartTime))); if (recurrenceEndDate.isAfter(currentMaxDate)) { currentMaxDate = recurrenceEndDate; break; @@ -3600,7 +3751,7 @@ class _SfCalendarState extends State dateAppointments[startDate] = appointmentList; } - startDate = addDays(startDate, 1); + startDate = DateTimeHelper.getDateTimeValue(addDays(startDate, 1)); } return dateAppointments; @@ -3646,8 +3797,8 @@ class _SfCalendarState extends State : _minDate; _minDate = _minDate!.isBefore(widget.minDate) ? widget.minDate : _minDate; - final DateTime viewMinDate = - addDays(_minDate, -(_minDate!.weekday % DateTime.daysPerWeek)); + final DateTime viewMinDate = DateTimeHelper.getDateTimeValue( + addDays(_minDate, -(_minDate!.weekday % DateTime.daysPerWeek))); /// Get the maximum date of schedule view when it value as null /// It return max date user assigned when the [hideEmptyScheduleWeek] @@ -3683,8 +3834,8 @@ class _SfCalendarState extends State /// enabled, then hide the week when it does not have appointments. while (count < 20) { for (int i = 1; i <= 100; i++) { - final DateTime updateDate = - addDays(date, i * DateTime.daysPerWeek); + final DateTime updateDate = DateTimeHelper.getDateTimeValue( + addDays(date, i * DateTime.daysPerWeek)); /// Skip the weeks after the max date. if (!isSameOrBeforeDate(_maxDate, updateDate)) { @@ -3692,7 +3843,8 @@ class _SfCalendarState extends State break; } - final DateTime weekEndDate = addDays(updateDate, 6); + final DateTime weekEndDate = + DateTimeHelper.getDateTimeValue(addDays(updateDate, 6)); /// Skip the week date when it does not have appointments /// when [hideEmptyAgendaDays] as enabled and display date and @@ -3709,7 +3861,7 @@ class _SfCalendarState extends State } } - date = addDays(date, 700); + date = DateTimeHelper.getDateTimeValue(addDays(date, 700)); } } } else { @@ -3723,8 +3875,8 @@ class _SfCalendarState extends State /// enabled, then hide the week when it does not have appointments. while (count < 20) { for (int i = 1; i <= 100; i++) { - final DateTime updatedDate = - addDays(date, -i * DateTime.daysPerWeek); + final DateTime updatedDate = DateTimeHelper.getDateTimeValue( + addDays(date, -i * DateTime.daysPerWeek)); /// Skip the weeks before the min date. if (!isSameOrAfterDate(viewMinDate, updatedDate)) { @@ -3732,7 +3884,8 @@ class _SfCalendarState extends State break; } - final DateTime weekEndDate = addDays(updatedDate, 6); + final DateTime weekEndDate = + DateTimeHelper.getDateTimeValue(addDays(updatedDate, 6)); /// Skip the week date when it does not have appointments /// when [hideEmptyAgendaDays] as enabled and display date and @@ -3749,7 +3902,7 @@ class _SfCalendarState extends State } } - date = addDays(date, -700); + date = DateTimeHelper.getDateTimeValue(addDays(date, -700)); } } } @@ -3773,15 +3926,19 @@ class _SfCalendarState extends State /// by subtract the 7 days to get previous date. final DateTime prevDate = index == 0 ? _previousDates.isEmpty - ? addDays(startDate, -DateTime.daysPerWeek) + ? DateTimeHelper.getDateTimeValue( + addDays(startDate, -DateTime.daysPerWeek)) : _previousDates[0] : (index > 0 ? _nextDates[index - 1] : -index >= _previousDates.length - 1 - ? addDays(startDate, -DateTime.daysPerWeek) + ? DateTimeHelper.getDateTimeValue( + addDays(startDate, -DateTime.daysPerWeek)) : _previousDates[-index]); - final DateTime prevEndDate = addDays(prevDate, 6); - final DateTime endDate = addDays(startDate, 6); + final DateTime prevEndDate = + DateTimeHelper.getDateTimeValue(addDays(prevDate, 6)); + final DateTime endDate = + DateTimeHelper.getDateTimeValue(addDays(startDate, 6)); /// Get the visible week appointment and split the appointments based on /// date. @@ -3854,10 +4011,9 @@ class _SfCalendarState extends State /// Check the week date needs month header at first or before of appointment /// view. - final bool isNeedMonthBuilder = _useMobilePlatformUI - ? prevEndDate.month != startDate.month || - prevEndDate.year != startDate.year - : false; + final bool isNeedMonthBuilder = _useMobilePlatformUI && + (prevEndDate.month != startDate.month || + prevEndDate.year != startDate.year); /// Web view does not have month label. height += isNeedMonthBuilder @@ -3963,10 +4119,10 @@ class _SfCalendarState extends State /// Check the week date needs month header at in between the appointment /// views. - bool isNeedInBetweenMonthBuilder = _useMobilePlatformUI - ? startDate.month != - (isSameOrBeforeDate(_maxDate!, endDate) ? endDate : _maxDate!).month - : false; + bool isNeedInBetweenMonthBuilder = _useMobilePlatformUI && + (startDate.month != + (isSameOrBeforeDate(_maxDate!, endDate) ? endDate : _maxDate!) + .month); /// Check the end date month have appointments or not. bool isNextMonthHasNoAppointment = false; @@ -4701,7 +4857,7 @@ class _SfCalendarState extends State if (currentYPosition <= offset.dy && currentYPosition + currentAppointmentHeight > offset.dy) { final List selectedAppointment = - []..add(appointment); + [appointment]; if (isTapCallback) { CalendarViewHelper.raiseCalendarTapCallback( widget, @@ -4746,8 +4902,8 @@ class _SfCalendarState extends State return Container(); } - final DateTime scheduleDisplayDate = - getValidDate(widget.minDate, widget.maxDate, _scheduleDisplayDate); + final DateTime scheduleDisplayDate = DateTimeHelper.getDateTimeValue( + getValidDate(widget.minDate, widget.maxDate, _scheduleDisplayDate)); final DateTime scheduleCurrentDate = DateTime.now(); final DateTime currentMaxDate = scheduleDisplayDate.isAfter(scheduleCurrentDate) @@ -4775,8 +4931,8 @@ class _SfCalendarState extends State _minDate = _minDate!.isAfter(currentMinDate) ? currentMinDate : _minDate; _minDate = _minDate!.isBefore(widget.minDate) ? widget.minDate : _minDate; - final DateTime viewMinDate = - addDays(_minDate, -(_minDate!.weekday % DateTime.daysPerWeek)); + final DateTime viewMinDate = DateTimeHelper.getDateTimeValue( + addDays(_minDate, -(_minDate!.weekday % DateTime.daysPerWeek))); /// Get the maximum date of schedule view when it value as null /// It return max date user assigned when the [hideEmptyAgendaDays] @@ -4816,14 +4972,16 @@ class _SfCalendarState extends State /// collection as empty. DateTime date = _nextDates.isNotEmpty ? _nextDates[0] - : addDays(scheduleDisplayDate, value); + : DateTimeHelper.getDateTimeValue( + addDays(scheduleDisplayDate, value)); int count = 0; /// Using while for calculate dates because if [hideEmptyAgendaDays] as /// enabled, then it hides the weeks when it does not have appointments. while (count < 50) { for (int i = 1; i <= 100; i++) { - final DateTime updatedDate = addDays(date, -i * DateTime.daysPerWeek); + final DateTime updatedDate = DateTimeHelper.getDateTimeValue( + addDays(date, -i * DateTime.daysPerWeek)); /// Skip week dates before min date if (!isSameOrAfterDate(viewMinDate, updatedDate)) { @@ -4831,7 +4989,8 @@ class _SfCalendarState extends State break; } - final DateTime weekEndDate = addDays(updatedDate, 6); + final DateTime weekEndDate = + DateTimeHelper.getDateTimeValue(addDays(updatedDate, 6)); /// Skip the week date when it does not have appointments /// when [hideEmptyAgendaDays] as enabled. @@ -4870,20 +5029,22 @@ class _SfCalendarState extends State count++; } - date = addDays(date, -700); + date = DateTimeHelper.getDateTimeValue(addDays(date, -700)); } } if (_nextDates.isEmpty) { /// Calculate the start date from display date - DateTime date = addDays(scheduleDisplayDate, value); + DateTime date = + DateTimeHelper.getDateTimeValue(addDays(scheduleDisplayDate, value)); int count = 0; /// Using while for calculate dates because if [hideEmptyAgendaDays] as /// enabled, then it hides the weeks when it does not have appointments. while (count < 50) { for (int i = 0; i < 100; i++) { - final DateTime updatedDate = addDays(date, i * DateTime.daysPerWeek); + final DateTime updatedDate = DateTimeHelper.getDateTimeValue( + addDays(date, i * DateTime.daysPerWeek)); /// Skip week date after max date if (!isSameOrBeforeDate(_maxDate, updatedDate)) { @@ -4891,7 +5052,8 @@ class _SfCalendarState extends State break; } - final DateTime weekEndDate = addDays(updatedDate, 6); + final DateTime weekEndDate = + DateTimeHelper.getDateTimeValue(addDays(updatedDate, 6)); /// Skip the week date when it does not have appointments /// when [hideEmptyAgendaDays] as enabled. @@ -4909,7 +5071,7 @@ class _SfCalendarState extends State count++; } - date = addDays(date, 700); + date = DateTimeHelper.getDateTimeValue(addDays(date, 700)); } } @@ -4938,7 +5100,8 @@ class _SfCalendarState extends State /// previous view dates and calculate the same until the next view dates /// appointment fills the view port. DateTime viewStartDate = _nextDates[0]; - DateTime viewEndDate = addDays(_nextDates[_nextDates.length - 1], 6); + DateTime viewEndDate = DateTimeHelper.getDateTimeValue( + addDays(_nextDates[_nextDates.length - 1], 6)); List appointmentCollection = AppointmentHelper.getVisibleAppointments( viewStartDate, @@ -4957,7 +5120,8 @@ class _SfCalendarState extends State double labelHeight = 0; if (_useMobilePlatformUI) { - DateTime previousDate = addDays(viewStartDate, -1); + DateTime previousDate = + DateTimeHelper.getDateTimeValue(addDays(viewStartDate, -1)); for (int i = 0; i < _nextDates.length; i++) { final DateTime nextDate = _nextDates[i]; if (previousDate.month != nextDate.month) { @@ -4998,7 +5162,8 @@ class _SfCalendarState extends State isNewDatesAdded = true; viewStartDate = currentDate; - viewEndDate = addDays(currentDate, 6); + viewEndDate = + DateTimeHelper.getDateTimeValue(addDays(currentDate, 6)); /// Calculate the newly added date appointment height and add /// the height to existing appointments height. @@ -5059,189 +5224,247 @@ class _SfCalendarState extends State _agendaScrollController!.initialScrollOffset == 0 && !_agendaScrollController!.hasClients) { final DateTime viewStartDate = _nextDates[0]; - final DateTime viewEndDate = addDays(viewStartDate, 6); + final DateTime viewEndDate = + DateTimeHelper.getDateTimeValue(addDays(viewStartDate, 6)); if (viewStartDate.isBefore(scheduleDisplayDate) && !isSameDate(viewStartDate, scheduleDisplayDate) && isSameOrBeforeDate(viewEndDate, scheduleDisplayDate)) { - final DateTime viewEndDate = addDays(scheduleDisplayDate, -1); - - /// Calculate the appointment between the week start date and - /// previous date of display date to calculate the scrolling position. - final List appointmentCollection = - AppointmentHelper.getVisibleAppointments(viewStartDate, viewEndDate, - _appointments, widget.timeZone, false); - - const double padding = 5; - - /// Calculate the today date view height when today date - /// in between the week. - double todayNewEventHeight = 0; - if (viewStartDate.isBefore(scheduleCurrentDate) && - !isSameDate(viewStartDate, scheduleCurrentDate) && - isSameOrBeforeDate(viewEndDate, scheduleCurrentDate)) { - todayNewEventHeight = appointmentViewHeight + (2 * padding); + final DateTime viewEndDate = + DateTimeHelper.getDateTimeValue(addDays(scheduleDisplayDate, -1)); + + final double initialScrollPosition = _getInitialScrollPosition( + viewStartDate, + viewEndDate, + scheduleCurrentDate, + appointmentViewHeight, + allDayAppointmentHeight); + if (initialScrollPosition != 0) { + _agendaScrollController?.removeListener(_handleScheduleViewScrolled); + _agendaScrollController = + ScrollController(initialScrollOffset: initialScrollPosition) + ..addListener(_handleScheduleViewScrolled); } - - /// Skip the scrolling when the previous week dates of display date - /// does not have a appointment. - if (appointmentCollection.isNotEmpty) { - final Map> dateAppointments = - _getAppointmentCollectionOnDateBasis( - appointmentCollection, viewStartDate, viewEndDate); - final List dateAppointmentKeys = - dateAppointments.keys.toList(); - double totalAppointmentHeight = 0; - for (int i = 0; i < dateAppointmentKeys.length; i++) { - final DateTime currentDate = dateAppointmentKeys[i]; - final List currentDateAppointment = - dateAppointments[currentDate]!; - final int eventsCount = currentDateAppointment.length; - int allDayEventCount = 0; - - /// Web view does not differentiate all day and normal appointment. - if (_useMobilePlatformUI) { - allDayEventCount = _getAllDayCount(currentDateAppointment); - } - - double panelHeight = - ((eventsCount - allDayEventCount) * appointmentViewHeight) + - (allDayEventCount * allDayAppointmentHeight); - panelHeight = panelHeight > appointmentViewHeight - ? panelHeight - : appointmentViewHeight; - - /// event count + 1 denotes the appointment padding and end padding. - totalAppointmentHeight += - panelHeight + ((eventsCount + 1) * padding); - - /// Set the today date view height to 0 when - /// today date have appointments. - if (todayNewEventHeight != 0 && - isSameDate(currentDate, scheduleCurrentDate)) { - todayNewEventHeight = 0; - } + } else if (viewStartDate.isBefore(scheduleDisplayDate)) { + DateTime visibleStartDate = viewStartDate; + double initialScrollPosition = 0; + while (visibleStartDate.isBefore(scheduleDisplayDate) && + !isSameDate(visibleStartDate, scheduleDisplayDate)) { + final DateTime viewEndDate = + DateTimeHelper.getDateTimeValue(addDays(visibleStartDate, 6)); + final DateTime appStartDate = + isSameOrAfterDate(_minDate!, visibleStartDate) + ? visibleStartDate + : _minDate!; + DateTime appEndDate = isSameOrBeforeDate(_maxDate!, viewEndDate) + ? viewEndDate + : _maxDate!; + if (appEndDate.isAfter(scheduleDisplayDate) || + isSameDate(appEndDate, scheduleDisplayDate)) { + appEndDate = DateTimeHelper.getDateTimeValue( + addDays(scheduleDisplayDate, -1)); } - final double scrolledPosition = todayNewEventHeight + - totalAppointmentHeight + + initialScrollPosition += _getInitialScrollPosition( + appStartDate, + appEndDate, + scheduleCurrentDate, + appointmentViewHeight, + allDayAppointmentHeight); + visibleStartDate = DateTimeHelper.getDateTimeValue( + addDays(visibleStartDate, DateTime.daysPerWeek)); + } - /// Add the divider height when it render on web. - (!_useMobilePlatformUI ? dateAppointmentKeys.length : 0) + - (!_useMobilePlatformUI - ? 0 - : widget.scheduleViewSettings.weekHeaderSettings.height) + - (viewStartDate.month == _scheduleDisplayDate.month && - viewStartDate.day != 1 - ? 0 - : (!_useMobilePlatformUI - ? 0 - : widget.scheduleViewSettings.monthHeaderSettings.height + - padding)); + if (initialScrollPosition != 0) { _agendaScrollController?.removeListener(_handleScheduleViewScrolled); _agendaScrollController = - ScrollController(initialScrollOffset: scrolledPosition) + ScrollController(initialScrollOffset: initialScrollPosition) ..addListener(_handleScheduleViewScrolled); - } else if ((viewStartDate.month != _scheduleDisplayDate.month && - _useMobilePlatformUI) || - todayNewEventHeight != 0) { - _agendaScrollController?.removeListener(_handleScheduleViewScrolled); - _agendaScrollController = ScrollController( - initialScrollOffset: (!_useMobilePlatformUI - ? 0 - : widget.scheduleViewSettings.weekHeaderSettings.height + - padding) + - todayNewEventHeight) - ..addListener(_handleScheduleViewScrolled); } } } - return Stack(children: [ - Positioned( - top: 0, - right: 0, - left: 0, - height: widget.headerHeight, - child: GestureDetector( - child: Container( - color: widget.headerStyle.backgroundColor ?? - _calendarTheme.headerBackgroundColor, - child: _CalendarHeaderView( - _currentViewVisibleDates, - widget.headerStyle, - null, - _view, - widget.monthViewSettings.numberOfWeeksInView, - _calendarTheme, - isRTL, - _locale, - widget.showNavigationArrow, - _controller, - widget.maxDate, - widget.minDate, - _minWidth, - widget.headerHeight, - widget.timeSlotViewSettings.nonWorkingDays, - widget.monthViewSettings.navigationDirection, - widget.showDatePickerButton, - _showHeader, - widget.allowedViews, - widget.allowViewNavigation, - _localizations, - _removeDatePicker, - _headerUpdateNotifier, - _viewChangeNotifier, - _handleOnTapForHeader, - _handleOnLongPressForHeader, - widget.todayHighlightColor, - _textScaleFactor, - _isMobilePlatform, - widget.headerDateFormat, - true, - )), - ), - ), - Positioned( - top: widget.headerHeight, - left: 0, + return RawKeyboardListener( + focusNode: _focusNode, + onKey: _onKeyDown, + child: Stack(children: [ + Positioned( + top: 0, right: 0, - height: height, - child: _OpacityWidget( - opacity: _opacity, - child: CustomScrollView( - key: _scrollKey, - physics: const AlwaysScrollableScrollPhysics(), - controller: _agendaScrollController, - center: _scheduleViewKey, - slivers: [ - SliverList( - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - if (_previousDates.length <= index) { - return null; - } + left: 0, + height: widget.headerHeight, + child: GestureDetector( + child: Container( + color: widget.headerStyle.backgroundColor ?? + _calendarTheme.headerBackgroundColor, + child: _CalendarHeaderView( + _currentViewVisibleDates, + widget.headerStyle, + null, + _view, + widget.monthViewSettings.numberOfWeeksInView, + _calendarTheme, + isRTL, + _locale, + widget.showNavigationArrow, + _controller, + widget.maxDate, + widget.minDate, + _minWidth, + widget.headerHeight, + widget.timeSlotViewSettings.nonWorkingDays, + widget.monthViewSettings.navigationDirection, + widget.showDatePickerButton, + _showHeader, + widget.allowedViews, + widget.allowViewNavigation, + _localizations, + _removeDatePicker, + _headerUpdateNotifier, + _viewChangeNotifier, + _handleOnTapForHeader, + _handleOnLongPressForHeader, + widget.todayHighlightColor, + _textScaleFactor, + _isMobilePlatform, + widget.headerDateFormat, + true, + widget.todayTextStyle, + )), + ), + ), + Positioned( + top: widget.headerHeight, + left: 0, + right: 0, + height: height, + child: _OpacityWidget( + opacity: _opacity, + child: CustomScrollView( + key: _scrollKey, + physics: const AlwaysScrollableScrollPhysics(), + controller: _agendaScrollController, + center: _scheduleViewKey, + slivers: [ + SliverList( + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + if (_previousDates.length <= index) { + return null; + } + + /// Send negative index value to differentiate the + /// backward view from forward view. + return _getItem(context, -(index + 1), isRTL); + }), + ), + SliverList( + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + if (_nextDates.length <= index) { + return null; + } + + return _getItem(context, index, isRTL); + }), + key: _scheduleViewKey, + ), + ], + ))), + _addDatePicker(widget.headerHeight, isRTL), + _getCalendarViewPopup(), + ]), + ); + } - /// Send negative index value to differentiate the - /// backward view from forward view. - return _getItem(context, -(index + 1), isRTL); - }), - ), - SliverList( - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - if (_nextDates.length <= index) { - return null; - } + double _getInitialScrollPosition( + DateTime viewStartDate, + DateTime viewEndDate, + DateTime scheduleCurrentDate, + double appointmentViewHeight, + double allDayAppointmentHeight) { + double initialScrolledPosition = 0; - return _getItem(context, index, isRTL); - }), - key: _scheduleViewKey, - ), - ], - ))), - _addDatePicker(widget.headerHeight, isRTL), - _getCalendarViewPopup(), - ]); + /// Calculate the appointment between the week start date and + /// previous date of display date to calculate the scrolling position. + final List appointmentCollection = + AppointmentHelper.getVisibleAppointments( + viewStartDate, viewEndDate, _appointments, widget.timeZone, false); + + const double padding = 5; + + /// Calculate the today date view height when today date + /// in between the week. + double todayNewEventHeight = 0; + if (viewStartDate.isBefore(scheduleCurrentDate) && + !isSameDate(viewStartDate, scheduleCurrentDate) && + isSameOrBeforeDate(viewEndDate, scheduleCurrentDate)) { + todayNewEventHeight = appointmentViewHeight + (2 * padding); + } + + /// Skip the scrolling when the previous week dates of display date + /// does not have a appointment. + if (appointmentCollection.isNotEmpty) { + final Map> dateAppointments = + _getAppointmentCollectionOnDateBasis( + appointmentCollection, viewStartDate, viewEndDate); + final List dateAppointmentKeys = dateAppointments.keys.toList(); + double totalAppointmentHeight = 0; + for (int i = 0; i < dateAppointmentKeys.length; i++) { + final DateTime currentDate = dateAppointmentKeys[i]; + final List currentDateAppointment = + dateAppointments[currentDate]!; + final int eventsCount = currentDateAppointment.length; + int allDayEventCount = 0; + + /// Web view does not differentiate all day and normal appointment. + if (_useMobilePlatformUI) { + allDayEventCount = _getAllDayCount(currentDateAppointment); + } + + double panelHeight = + ((eventsCount - allDayEventCount) * appointmentViewHeight) + + (allDayEventCount * allDayAppointmentHeight); + panelHeight = panelHeight > appointmentViewHeight + ? panelHeight + : appointmentViewHeight; + + /// event count + 1 denotes the appointment padding and end padding. + totalAppointmentHeight += panelHeight + ((eventsCount + 1) * padding); + + /// Set the today date view height to 0 when + /// today date have appointments. + if (todayNewEventHeight != 0 && + isSameDate(currentDate, scheduleCurrentDate)) { + todayNewEventHeight = 0; + } + } + + initialScrolledPosition = todayNewEventHeight + + totalAppointmentHeight + + + /// Add the divider height when it render on web. + (!_useMobilePlatformUI ? dateAppointmentKeys.length : 0) + + (!_useMobilePlatformUI + ? 0 + : widget.scheduleViewSettings.weekHeaderSettings.height) + + (viewStartDate.month == _scheduleDisplayDate.month && + viewStartDate.day != 1 + ? 0 + : (!_useMobilePlatformUI + ? 0 + : widget.scheduleViewSettings.monthHeaderSettings.height + + padding)); + } else if ((viewStartDate.month != _scheduleDisplayDate.month && + _useMobilePlatformUI) || + todayNewEventHeight != 0) { + initialScrolledPosition = (!_useMobilePlatformUI + ? 0 + : widget.scheduleViewSettings.weekHeaderSettings.height + + padding) + + todayNewEventHeight; + } + + return initialScrolledPosition; } Widget addAgendaWithLoadMore(double height, bool isRTL) { @@ -5255,8 +5478,8 @@ class _SfCalendarState extends State return Container(); } - final DateTime scheduleDisplayDate = - getValidDate(widget.minDate, widget.maxDate, _scheduleDisplayDate); + final DateTime scheduleDisplayDate = DateTimeHelper.getDateTimeValue( + getValidDate(widget.minDate, widget.maxDate, _scheduleDisplayDate)); final DateTime scheduleCurrentDate = DateTime.now(); _scheduleMinDate ??= scheduleDisplayDate; @@ -5268,8 +5491,8 @@ class _SfCalendarState extends State _maxDate = _scheduleMaxDate; } - final DateTime viewMinDate = - addDays(_minDate, -(_minDate!.weekday % DateTime.daysPerWeek)); + final DateTime viewMinDate = DateTimeHelper.getDateTimeValue( + addDays(_minDate, -(_minDate!.weekday % DateTime.daysPerWeek))); final double appointmentViewHeight = CalendarViewHelper.getScheduleAppointmentHeight( @@ -5295,14 +5518,16 @@ class _SfCalendarState extends State ? _previousDates[_previousDates.length - 1] : (_nextDates.isNotEmpty ? _nextDates[0] - : addDays(scheduleDisplayDate, value)); + : DateTimeHelper.getDateTimeValue( + addDays(scheduleDisplayDate, value))); int count = 0; /// Using while for calculate dates because if [hideEmptyAgendaDays] as /// enabled, then it hides the weeks when it does not have appointments. while (count < 50) { for (int i = 1; i <= 100; i++) { - final DateTime updatedDate = addDays(date, -i * DateTime.daysPerWeek); + final DateTime updatedDate = DateTimeHelper.getDateTimeValue( + addDays(date, -i * DateTime.daysPerWeek)); /// Skip week dates before min date if (!isSameOrAfterDate(viewMinDate, updatedDate)) { @@ -5310,7 +5535,8 @@ class _SfCalendarState extends State break; } - final DateTime weekEndDate = addDays(updatedDate, 6); + final DateTime weekEndDate = + DateTimeHelper.getDateTimeValue(addDays(updatedDate, 6)); /// Skip the week date when it does not have appointments /// when [hideEmptyAgendaDays] as enabled. @@ -5349,25 +5575,28 @@ class _SfCalendarState extends State count++; } - date = addDays(date, -700); + date = DateTimeHelper.getDateTimeValue(addDays(date, -700)); } } - final DateTime viewMaxDate = addDays(_maxDate, - (DateTime.daysPerWeek - _maxDate!.weekday) % DateTime.daysPerWeek); + final DateTime viewMaxDate = DateTimeHelper.getDateTimeValue(addDays( + _maxDate, + (DateTime.daysPerWeek - _maxDate!.weekday) % DateTime.daysPerWeek)); if (_nextDates.isEmpty || !isSameDate(_nextDates[_nextDates.length - 1], viewMaxDate)) { /// Calculate the start date from display date DateTime date = _nextDates.isEmpty - ? addDays(scheduleDisplayDate, value) - : addDays(_nextDates[_nextDates.length - 1], DateTime.daysPerWeek); + ? DateTimeHelper.getDateTimeValue(addDays(scheduleDisplayDate, value)) + : DateTimeHelper.getDateTimeValue( + addDays(_nextDates[_nextDates.length - 1], DateTime.daysPerWeek)); int count = 0; /// Using while for calculate dates because if [hideEmptyAgendaDays] as /// enabled, then it hides the weeks when it does not have appointments. while (count < 50) { for (int i = 0; i < 100; i++) { - final DateTime updatedDate = addDays(date, i * DateTime.daysPerWeek); + final DateTime updatedDate = DateTimeHelper.getDateTimeValue( + addDays(date, i * DateTime.daysPerWeek)); /// Skip week date after max date if (!isSameOrBeforeDate(_maxDate, updatedDate)) { @@ -5375,7 +5604,8 @@ class _SfCalendarState extends State break; } - final DateTime weekEndDate = addDays(updatedDate, 6); + final DateTime weekEndDate = + DateTimeHelper.getDateTimeValue(addDays(updatedDate, 6)); /// Skip the week date when it does not have appointments /// when [hideEmptyAgendaDays] as enabled. @@ -5393,7 +5623,7 @@ class _SfCalendarState extends State count++; } - date = addDays(date, 700); + date = DateTimeHelper.getDateTimeValue(addDays(date, 700)); } } @@ -5422,7 +5652,8 @@ class _SfCalendarState extends State /// previous view dates and calculate the same until the next view dates /// appointment fills the view port. DateTime viewStartDate = _nextDates[0]; - DateTime viewEndDate = addDays(_nextDates[_nextDates.length - 1], 6); + DateTime viewEndDate = DateTimeHelper.getDateTimeValue( + addDays(_nextDates[_nextDates.length - 1], 6)); List appointmentCollection = AppointmentHelper.getVisibleAppointments( viewStartDate, @@ -5441,7 +5672,8 @@ class _SfCalendarState extends State double labelHeight = 0; if (_useMobilePlatformUI) { - DateTime previousDate = addDays(viewStartDate, -1); + DateTime previousDate = + DateTimeHelper.getDateTimeValue(addDays(viewStartDate, -1)); for (int i = 0; i < _nextDates.length; i++) { final DateTime nextDate = _nextDates[i]; if (previousDate.month != nextDate.month) { @@ -5482,7 +5714,8 @@ class _SfCalendarState extends State isNewDatesAdded = true; viewStartDate = currentDate; - viewEndDate = addDays(currentDate, 6); + viewEndDate = + DateTimeHelper.getDateTimeValue(addDays(currentDate, 6)); /// Calculate the newly added date appointment height and add /// the height to existing appointments height. @@ -5533,7 +5766,8 @@ class _SfCalendarState extends State final DateTime date = _nextDates[0]; _headerUpdateNotifier.value = _useMobilePlatformUI ? date - : getValidDate(_minDate, _maxDate, date); + : DateTimeHelper.getDateTimeValue( + getValidDate(_minDate, _maxDate, date)); } } @@ -5611,8 +5845,8 @@ class _SfCalendarState extends State /// While used to calculate the height of current month weeks on initial /// loading. while (isSameOrBeforeDate(_maxDate, viewStartDate)) { - final DateTime viewEndDate = - addDays(viewStartDate, DateTime.daysPerWeek - 1); + final DateTime viewEndDate = DateTimeHelper.getDateTimeValue( + addDays(viewStartDate, DateTime.daysPerWeek - 1)); final DateTime appStartDate = isSameOrAfterDate(_minDate!, viewStartDate) ? viewStartDate @@ -5640,11 +5874,10 @@ class _SfCalendarState extends State canCreateNewAppointment: false); /// Check the week date needs month header or not. - final bool isNeedMonthBuilder = _useMobilePlatformUI - ? (viewStartDate.month != appEndDate.month || + final bool isNeedMonthBuilder = _useMobilePlatformUI && + ((viewStartDate.month != appEndDate.month || viewStartDate.year != appEndDate.year) || - viewStartDate.day == 1 - : false; + viewStartDate.day == 1); /// Web view does not have month label. double currentWeekHeight = isNeedMonthBuilder @@ -5726,15 +5959,16 @@ class _SfCalendarState extends State currentWeekHeight += displayNewEventHeight; totalHeight += currentWeekHeight; heights.add(currentWeekHeight); - viewStartDate = addDays(viewStartDate, DateTime.daysPerWeek); + viewStartDate = DateTimeHelper.getDateTimeValue( + addDays(viewStartDate, DateTime.daysPerWeek)); } /// Get the current display date week index from next dates collection. int rangeIndex = -1; for (int i = 0; i < _nextDates.length; i++) { final DateTime visibleStartDate = _nextDates[i]; - final DateTime visibleEndDate = - addDays(visibleStartDate, DateTime.daysPerWeek); + final DateTime visibleEndDate = DateTimeHelper.getDateTimeValue( + addDays(visibleStartDate, DateTime.daysPerWeek)); if (!isDateWithInDateRange( visibleStartDate, visibleEndDate, scheduleDisplayDate)) { continue; @@ -5755,7 +5989,8 @@ class _SfCalendarState extends State /// Calculate the scroll position with current display date week. while (viewStartDate.isBefore(scheduleDisplayDate) && !isSameDate(viewStartDate, scheduleDisplayDate)) { - final DateTime viewEndDate = addDays(viewStartDate, 6); + final DateTime viewEndDate = + DateTimeHelper.getDateTimeValue(addDays(viewStartDate, 6)); final DateTime appStartDate = isSameOrAfterDate(_minDate!, viewStartDate) ? viewStartDate @@ -5765,12 +6000,15 @@ class _SfCalendarState extends State : _maxDate!; if (appEndDate.isAfter(scheduleDisplayDate) || isSameDate(appEndDate, scheduleDisplayDate)) { - appEndDate = addDays(scheduleDisplayDate, -1); + appEndDate = + DateTimeHelper.getDateTimeValue(addDays(scheduleDisplayDate, -1)); } /// Today date view height. double todayNewEventHeight = - isDateWithInDateRange(appStartDate, appEndDate, scheduleCurrentDate) + !isSameDate(scheduleCurrentDate, scheduleDisplayDate) && + isDateWithInDateRange( + appStartDate, appEndDate, scheduleCurrentDate) ? displayEventHeight : 0; final List appointmentCollection = @@ -5779,11 +6017,10 @@ class _SfCalendarState extends State canCreateNewAppointment: false); /// Check the week date needs month header or not. - final bool isNeedMonthBuilder = _useMobilePlatformUI - ? (viewStartDate.month != appEndDate.month || + final bool isNeedMonthBuilder = _useMobilePlatformUI || + ((viewStartDate.month != appEndDate.month || viewStartDate.year != appEndDate.year) || - viewStartDate.day == 1 - : false; + viewStartDate.day == 1); if (appointmentCollection.isNotEmpty) { /// Get the collection of appointment collection listed by date. @@ -5859,7 +6096,8 @@ class _SfCalendarState extends State todayNewEventHeight; } - viewStartDate = addDays(viewStartDate, DateTime.daysPerWeek); + viewStartDate = DateTimeHelper.getDateTimeValue( + addDays(viewStartDate, DateTime.daysPerWeek)); } if (initialScrolledPosition != 0) { @@ -5868,7 +6106,7 @@ class _SfCalendarState extends State /// Check the content height after the scroll position, if it lesser /// than view port height then reduce the scroll position. if (belowSpace < height) { - initialScrolledPosition -= (height - belowSpace); + initialScrolledPosition -= height - belowSpace; initialScrolledPosition = initialScrolledPosition > 0 ? initialScrolledPosition : 0; } @@ -5937,6 +6175,7 @@ class _SfCalendarState extends State _isMobilePlatform, widget.headerDateFormat, !_isScheduleStartLoadMore && !_isNeedLoadMore, + widget.todayTextStyle, )), ), ), @@ -6059,7 +6298,21 @@ class _SfCalendarState extends State })))); } - return Stack(children: children); + return RawKeyboardListener( + focusNode: _focusNode, + onKey: _onKeyDown, + child: Stack(children: children), + ); + } + + /// Method to handle keyboard navigation for schedule view in calendar. + void _onKeyDown(RawKeyEvent event) { + if (event.runtimeType != RawKeyDownEvent) { + return; + } + + CalendarViewHelper.handleViewSwitchKeyBoardEvent( + event, _controller, widget.allowedViews); } Future loadMoreAppointments( @@ -6093,7 +6346,7 @@ class _SfCalendarState extends State return Container(); } - final double calendarViewTextHeight = 40; + const double calendarViewTextHeight = 40; final List children = []; double width = 0; Color? headerTextColor = widget.headerStyle.textStyle != null @@ -6102,20 +6355,24 @@ class _SfCalendarState extends State headerTextColor ??= Colors.black87; final TextStyle style = TextStyle(color: headerTextColor, fontSize: 12); int selectedIndex = -1; - final Color? todayColor = - widget.todayHighlightColor ?? _calendarTheme.todayHighlightColor; + final Color? todayColor = CalendarViewHelper.getTodayHighlightTextColor( + widget.todayHighlightColor ?? _calendarTheme.todayHighlightColor, + widget.todayTextStyle, + _calendarTheme); final Map calendarViews = _getCalendarViewsText(_localizations); + final Alignment alignment = + _isRTL ? Alignment.centerRight : Alignment.centerLeft; + final int allowedViewLength = widget.allowedViews!.length; + /// Generate the calendar view pop up content views. - for (int i = 0; i < widget.allowedViews!.length; i++) { + for (int i = 0; i < allowedViewLength; i++) { final CalendarView view = widget.allowedViews![i]; + final String text = calendarViews[view]!; final double textWidth = _getTextWidgetWidth( - calendarViews[view].toString(), - calendarViewTextHeight, - _minWidth, - context, + text, calendarViewTextHeight, _minWidth, context, style: style) .width; width = width < textWidth ? textWidth : width; @@ -6130,11 +6387,11 @@ class _SfCalendarState extends State _controller.view = view; }, child: Container( - padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 0.0), + padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 0.0), height: calendarViewTextHeight, - alignment: _isRTL ? Alignment.centerRight : Alignment.centerLeft, + alignment: alignment, child: Text( - calendarViews[view].toString(), + text, style: isSelected ? style.copyWith(color: todayColor) : style, maxLines: 1, ), @@ -6143,7 +6400,7 @@ class _SfCalendarState extends State } /// Restrict the pop up height with max height(200) - double height = widget.allowedViews!.length * calendarViewTextHeight; + double height = allowedViewLength * calendarViewTextHeight; height = height > 200 ? 200 : height; double arrowWidth = 0; @@ -6275,7 +6532,7 @@ class _SfCalendarState extends State if (selectedIndex != -1) { scrollPosition = selectedIndex * calendarViewTextHeight; final double maxScrollPosition = - widget.allowedViews!.length * calendarViewTextHeight; + allowedViewLength * calendarViewTextHeight; scrollPosition = (maxScrollPosition - scrollPosition) > height ? scrollPosition : maxScrollPosition - height; @@ -6289,7 +6546,7 @@ class _SfCalendarState extends State child: _PopupWidget( alignment: popupAlignment, child: Container( - padding: EdgeInsets.all(0), + padding: const EdgeInsets.all(0), decoration: BoxDecoration( color: _calendarTheme.brightness == Brightness.dark ? Colors.grey[850] @@ -6301,7 +6558,7 @@ class _SfCalendarState extends State child: Material( type: MaterialType.transparency, child: ListView( - padding: EdgeInsets.all(0), + padding: const EdgeInsets.all(0), controller: ScrollController(initialScrollOffset: scrollPosition), children: children), @@ -6345,7 +6602,7 @@ class _SfCalendarState extends State width: resourceViewSize, top: 0, bottom: 0, - child: Stack(children: [ + child: Stack(children: [ Positioned( left: _isRTL ? 0.5 : resourceViewSize - 0.5, width: 0.5, @@ -6379,20 +6636,20 @@ class _SfCalendarState extends State controller: _resourcePanelScrollController, scrollDirection: Axis.vertical, children: [ - CustomPaint( - painter: ResourceContainer( - _resourceCollection, - widget.resourceViewSettings, - resourceItemHeight, - widget.cellBorderColor, - _calendarTheme, - _resourceImageNotifier, - isRTL, - _textScaleFactor, - _resourceHoverNotifier.value, - _imagePainterCollection), - size: Size(resourceViewSize, panelHeight), - ), + ResourceViewWidget( + _resourceCollection, + widget.resourceViewSettings, + resourceItemHeight, + widget.cellBorderColor, + _calendarTheme, + _resourceImageNotifier, + isRTL, + _textScaleFactor, + _resourceHoverNotifier.value, + _imagePainterCollection, + resourceViewSize, + panelHeight, + widget.resourceViewHeaderBuilder), ]), onTapUp: (TapUpDetails details) { _handleOnTapForResourcePanel(details, resourceItemHeight); @@ -6451,7 +6708,8 @@ class _SfCalendarState extends State if (app.resourceIds != null && app.resourceIds!.isNotEmpty && app.resourceIds!.contains(resource.id)) { - selectedResourceAppointments.add(app.data ?? app); + selectedResourceAppointments + .add(CalendarViewHelper.getAppointmentDetail(app)); } } @@ -6484,30 +6742,30 @@ class _SfCalendarState extends State child: _OpacityWidget( opacity: _opacity, child: CustomCalendarScrollView( - widget, - _view, - width - resourceViewSize, - height - agendaHeight, - _agendaSelectedDate, - isRTL, - _locale, - _calendarTheme, - _timeZoneLoaded ? widget.specialRegions : null, - _blackoutDates, - _controller, - _removeDatePicker, - _resourcePanelScrollController, - _resourceCollection, - _textScaleFactor, - _isMobilePlatform, - _fadeInController, - widget.minDate, - widget.maxDate, - _localizations, (UpdateCalendarStateDetails details) { - _updateCalendarState(details); - }, (UpdateCalendarStateDetails details) { - _getCalendarStateDetails(details); - })), + widget, + _view, + width - resourceViewSize, + height - agendaHeight, + _agendaSelectedDate, + isRTL, + _locale, + _calendarTheme, + _timeZoneLoaded ? widget.specialRegions : null, + _blackoutDates, + _controller, + _removeDatePicker, + _resourcePanelScrollController, + _resourceCollection, + _textScaleFactor, + _isMobilePlatform, + _fadeInController, + widget.minDate, + widget.maxDate, + _localizations, + _updateCalendarState, + _getCalendarStateDetails, + key: _customScrollViewKey, + )), ); } @@ -6561,6 +6819,7 @@ class _SfCalendarState extends State _isMobilePlatform, widget.headerDateFormat, !_isNeedLoadMore, + widget.todayTextStyle, )), ), _addResourcePanel(isResourceEnabled, resourceViewSize, height, isRTL), @@ -6619,6 +6878,8 @@ class _SfCalendarState extends State _calendarTheme.activeDatesTextStyle; final Color? todayColor = widget.todayHighlightColor ?? _calendarTheme.todayHighlightColor; + final Color? todayTextColor = CalendarViewHelper.getTodayHighlightTextColor( + todayColor, widget.todayTextStyle, _calendarTheme); double left = 0; if (_isMobilePlatform) { pickerWidth = _minWidth; @@ -6696,14 +6957,14 @@ class _SfCalendarState extends State height: pickerHeight, child: _PopupWidget( child: Container( - margin: EdgeInsets.all(0), - padding: EdgeInsets.all(5), + margin: const EdgeInsets.all(0), + padding: const EdgeInsets.all(5), decoration: _isMobilePlatform ? BoxDecoration( color: _calendarTheme.brightness == Brightness.dark ? Colors.grey[850] : Colors.white, - boxShadow: [ + boxShadow: const [ BoxShadow( offset: Offset(0.0, 3.0), blurRadius: 2.0, @@ -6727,7 +6988,7 @@ class _SfCalendarState extends State todayHighlightColor: todayColor, minDate: widget.minDate, maxDate: widget.maxDate, - selectionColor: todayColor, + selectionColor: todayTextColor, headerStyle: DateRangePickerHeaderStyle( textAlign: _isMobilePlatform ? TextAlign.center : TextAlign.left, @@ -6739,10 +7000,11 @@ class _SfCalendarState extends State monthCellStyle: DateRangePickerMonthCellStyle( textStyle: datePickerStyle, todayTextStyle: - datePickerStyle.copyWith(color: todayColor)), + datePickerStyle.copyWith(color: todayTextColor)), yearCellStyle: DateRangePickerYearCellStyle( textStyle: datePickerStyle, - todayTextStyle: datePickerStyle.copyWith(color: todayColor), + todayTextStyle: + datePickerStyle.copyWith(color: todayTextColor), leadingDatesTextStyle: widget.monthViewSettings .monthCellStyle.leadingDatesTextStyle ?? _calendarTheme.leadingDatesTextStyle, @@ -6814,9 +7076,11 @@ class _SfCalendarState extends State void _updateCalendarState(UpdateCalendarStateDetails details) { if (details.currentDate != null && !isSameDate(details.currentDate, _currentDate)) { - _currentDate = - getValidDate(widget.minDate, widget.maxDate, details.currentDate); + _currentDate = DateTimeHelper.getDateTimeValue( + getValidDate(widget.minDate, widget.maxDate, details.currentDate)); + _canScrollTimeSlotView = false; _controller.displayDate = _currentDate; + _canScrollTimeSlotView = true; details.currentDate = _currentDate; } @@ -6878,13 +7142,8 @@ class _SfCalendarState extends State if (_view == CalendarView.month) { return DateTime(_currentDate.year, _currentDate.month, 01, 0, 0, 0); } else { - return DateTime( - _currentViewVisibleDates[0].year, - _currentViewVisibleDates[0].month, - _currentViewVisibleDates[0].day, - 0, - 0, - 0); + final DateTime date = _currentViewVisibleDates[0]; + return DateTime(date.year, date.month, date.day, 0, 0, 0); } } @@ -7197,7 +7456,7 @@ class _SfCalendarState extends State } class _OpacityWidget extends StatefulWidget { - _OpacityWidget({required this.child, required this.opacity}); + const _OpacityWidget({required this.child, required this.opacity}); final Widget child; @@ -7243,7 +7502,8 @@ class _OpacityWidgetState extends State<_OpacityWidget> { /// Widget used to show the pop up animation to the child. class _PopupWidget extends StatefulWidget { - _PopupWidget({required this.child, this.alignment = Alignment.topCenter}); + const _PopupWidget( + {required this.child, this.alignment = Alignment.topCenter}); /// Widget that animated like popup. final Widget child; @@ -7265,8 +7525,8 @@ class _PopupWidgetState extends State<_PopupWidget> @override void initState() { - _animationController = - AnimationController(vsync: this, duration: Duration(milliseconds: 200)); + _animationController = AnimationController( + vsync: this, duration: const Duration(milliseconds: 200)); _animation = CurvedAnimation(parent: _animationController, curve: Curves.easeInOut); super.initState(); @@ -7325,9 +7585,11 @@ class _CalendarHeaderView extends StatefulWidget { this.textScaleFactor, this.isMobilePlatform, this.headerDateFormat, - this.enableInteraction); + this.enableInteraction, + this.todayTextStyle); final List visibleDates; + final TextStyle? todayTextStyle; final CalendarHeaderStyle headerStyle; final SfCalendarThemeData calendarTheme; final DateTime? currentDate; @@ -7417,13 +7679,15 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { Color? headerTextColor = widget.headerStyle.textStyle != null ? widget.headerStyle.textStyle!.color : (widget.calendarTheme.headerTextStyle.color); + final Color headerBackgroundColor = widget.headerStyle.backgroundColor ?? + widget.calendarTheme.headerBackgroundColor; headerTextColor ??= Colors.black87; final Color arrowColor = headerTextColor.withOpacity(headerTextColor.opacity * 0.6); Color prevArrowColor = arrowColor; Color nextArrowColor = arrowColor; final TextStyle style = TextStyle(color: arrowColor); - final double defaultCalendarViewTextSize = 12; + const double defaultCalendarViewTextSize = 12; Widget calendarViewIcon = Container(width: 0, height: 0); const double padding = 5; double? headerIconTextWidth = widget.headerStyle.textStyle != null @@ -7441,7 +7705,7 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { /// 5 as padding for around today text view. final Size todayButtonSize = _getTextWidgetWidth( todayText, widget.height, widget.width - totalArrowWidth, context, - style: TextStyle(fontSize: defaultCalendarViewTextSize)); + style: const TextStyle(fontSize: defaultCalendarViewTextSize)); maxHeaderHeight = todayButtonSize.height; todayIconWidth = todayButtonSize.width + padding; } @@ -7464,6 +7728,11 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { : headerTextSize.height; } + final Color? highlightColor = CalendarViewHelper.getTodayHighlightTextColor( + widget.todayHighlightColor ?? widget.calendarTheme.todayHighlightColor, + widget.todayTextStyle, + widget.calendarTheme); + if (isNeedViewSwitchOption) { calendarViewWidth = iconWidth; if (useMobilePlatformUI) { @@ -7474,6 +7743,7 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { /// Render allowed views icon on mobile view. calendarViewIcon = _getCalendarViewWidget( + headerBackgroundColor, useMobilePlatformUI, false, calendarViewWidth, @@ -7482,7 +7752,9 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { arrowColor, headerTextColor, widget.view, + // ignore: avoid_bool_literals_in_conditional_expressions widget.isMobilePlatform ? false : widget.viewChangeNotifier.value, + highlightColor, defaultCalendarViewTextSize, semanticLabel: 'CalendarView'); } else { @@ -7505,7 +7777,7 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { final CalendarView currentView = widget.allowedViews![i]; final Size calendarViewSize = _getTextWidgetWidth( _calendarViews[currentView]!, widget.height, totalWidth, context, - style: TextStyle(fontSize: defaultCalendarViewTextSize)); + style: const TextStyle(fontSize: defaultCalendarViewTextSize)); final double currentViewTextWidth = calendarViewSize.width + padding; maxCalendarViewHeight = maxCalendarViewHeight > calendarViewSize.height @@ -7527,6 +7799,7 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { for (int i = 0; i < allowedViewsLength; i++) { final CalendarView currentView = widget.allowedViews![i]; children.add(_getCalendarViewWidget( + headerBackgroundColor, useMobilePlatformUI, false, calendarViewsWidth[currentView]!, @@ -7536,6 +7809,7 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { headerTextColor, currentView, widget.view == currentView, + highlightColor, defaultCalendarViewTextSize)); } } else { @@ -7546,7 +7820,7 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { widget.height, widget.width - totalArrowWidth, context, - style: TextStyle(fontSize: defaultCalendarViewTextSize)); + style: const TextStyle(fontSize: defaultCalendarViewTextSize)); maxCalendarViewHeight = calendarViewSize.height; maxHeaderHeight = maxCalendarViewHeight > maxHeaderHeight ? maxCalendarViewHeight @@ -7556,6 +7830,7 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { calendarViewWidth = calendarViewSize.width + padding + headerIconTextWidth; children.add(_getCalendarViewWidget( + headerBackgroundColor, useMobilePlatformUI, true, calendarViewWidth, @@ -7565,6 +7840,7 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { headerTextColor, widget.view, widget.viewChangeNotifier.value, + highlightColor, defaultCalendarViewTextSize, semanticLabel: 'CalendarView')); } @@ -7634,32 +7910,27 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { return Alignment.center; } + final Color? splashColor = + !widget.showDatePickerButton || !widget.enableInteraction + ? Colors.transparent + : null; + final TextStyle headerTextStyle = + widget.headerStyle.textStyle ?? widget.calendarTheme.headerTextStyle; final Widget headerText = widget.isMobilePlatform ? Container( alignment: Alignment.center, - color: widget.headerStyle.backgroundColor ?? - widget.calendarTheme.headerBackgroundColor, + color: headerBackgroundColor, width: isCenterAlignment && headerWidth > 200 ? 200 : headerWidth, height: headerHeight, padding: const EdgeInsets.all(2), child: Material( - color: widget.headerStyle.backgroundColor ?? - widget.calendarTheme.headerBackgroundColor, + color: headerBackgroundColor, child: InkWell( //// set splash color as transparent when header does not have // date piker. - splashColor: - !widget.showDatePickerButton || !widget.enableInteraction - ? Colors.transparent - : null, - highlightColor: - !widget.showDatePickerButton || !widget.enableInteraction - ? Colors.transparent - : null, - hoverColor: - !widget.showDatePickerButton || !widget.enableInteraction - ? Colors.transparent - : null, + splashColor: splashColor, + highlightColor: splashColor, + hoverColor: splashColor, splashFactory: _CustomSplashFactory(), onTap: () { if (!widget.enableInteraction) { @@ -7683,16 +7954,14 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { : headerWidth, height: headerHeight, alignment: Alignment.centerLeft, - padding: EdgeInsets.symmetric(horizontal: 5), + padding: const EdgeInsets.symmetric(horizontal: 5), child: Row( mainAxisAlignment: _getAlignmentFromTextAlign(), children: widget.showDatePickerButton - ? [ + ? [ Flexible( child: Text(headerString, - style: widget.headerStyle.textStyle ?? - widget.calendarTheme - .headerTextStyle, + style: headerTextStyle, maxLines: 1, overflow: TextOverflow.clip, softWrap: false, @@ -7702,19 +7971,13 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { ? Icons.arrow_drop_up : Icons.arrow_drop_down, color: arrowColor, - size: (widget.headerStyle.textStyle ?? - widget.calendarTheme - .headerTextStyle) - .fontSize ?? - 14, + size: headerTextStyle.fontSize ?? 14, ) ] - : [ + : [ Flexible( child: Text(headerString, - style: widget.headerStyle.textStyle ?? - widget.calendarTheme - .headerTextStyle, + style: headerTextStyle, maxLines: 1, overflow: TextOverflow.clip, softWrap: false, @@ -7726,25 +7989,17 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { ) : Container( alignment: _getHeaderAlignment(), - color: widget.headerStyle.backgroundColor ?? - widget.calendarTheme.headerBackgroundColor, + color: headerBackgroundColor, width: isCenterAlignment && headerWidth > 200 ? 200 : headerWidth, height: headerHeight, padding: const EdgeInsets.all(2), child: Material( - color: widget.headerStyle.backgroundColor ?? - widget.calendarTheme.headerBackgroundColor, + color: headerBackgroundColor, child: InkWell( //// set splash color as transparent when header does not have // date piker. - splashColor: - !widget.showDatePickerButton || !widget.enableInteraction - ? Colors.transparent - : null, - highlightColor: - !widget.showDatePickerButton || !widget.enableInteraction - ? Colors.transparent - : null, + splashColor: splashColor, + highlightColor: splashColor, splashFactory: _CustomSplashFactory(), onTap: () { if (!widget.enableInteraction) { @@ -7766,23 +8021,20 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { color: widget.showDatePickerButton && widget.isPickerShown ? Colors.grey.withOpacity(0.3) - : widget.headerStyle.backgroundColor ?? - widget.calendarTheme.headerBackgroundColor, + : headerBackgroundColor, width: isCenterAlignment && headerTextWidth > 200 ? 200 : headerTextWidth, height: headerHeight, alignment: Alignment.center, - padding: EdgeInsets.symmetric(horizontal: 5), + padding: const EdgeInsets.symmetric(horizontal: 5), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: widget.showDatePickerButton - ? [ + ? [ Flexible( child: Text(headerString, - style: widget.headerStyle.textStyle ?? - widget.calendarTheme - .headerTextStyle, + style: headerTextStyle, maxLines: 1, overflow: TextOverflow.clip, softWrap: false, @@ -7792,19 +8044,13 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { ? Icons.arrow_drop_up : Icons.arrow_drop_down, color: arrowColor, - size: (widget.headerStyle.textStyle ?? - widget.calendarTheme - .headerTextStyle) - .fontSize ?? - 14, + size: headerTextStyle.fontSize ?? 14, ) ] - : [ + : [ Flexible( child: Text(headerString, - style: widget.headerStyle.textStyle ?? - widget.calendarTheme - .headerTextStyle, + style: headerTextStyle, maxLines: 1, overflow: TextOverflow.clip, softWrap: false, @@ -7815,30 +8061,23 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { )), ); + final Color? leftArrowSplashColor = + prevArrowColor != arrowColor || !widget.enableInteraction + ? Colors.transparent + : null; final Container leftArrow = Container( alignment: Alignment.center, - color: widget.headerStyle.backgroundColor ?? - widget.calendarTheme.headerBackgroundColor, + color: headerBackgroundColor, width: arrowWidth, height: headerHeight, padding: const EdgeInsets.all(2), child: Material( - color: widget.headerStyle.backgroundColor ?? - widget.calendarTheme.headerBackgroundColor, + color: headerBackgroundColor, child: InkWell( //// set splash color as transparent when arrow reaches min date(disabled) - splashColor: - prevArrowColor != arrowColor || !widget.enableInteraction - ? Colors.transparent - : null, - highlightColor: - prevArrowColor != arrowColor || !widget.enableInteraction - ? Colors.transparent - : null, - hoverColor: - prevArrowColor != arrowColor || !widget.enableInteraction - ? Colors.transparent - : null, + splashColor: leftArrowSplashColor, + highlightColor: leftArrowSplashColor, + hoverColor: leftArrowSplashColor, splashFactory: _CustomSplashFactory(), onTap: _backward, child: Semantics( @@ -7859,30 +8098,23 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { )), ); + final Color? rightArrowSplashColor = + nextArrowColor != arrowColor || !widget.enableInteraction + ? Colors.transparent + : null; final Container rightArrow = Container( alignment: Alignment.center, - color: widget.headerStyle.backgroundColor ?? - widget.calendarTheme.headerBackgroundColor, + color: headerBackgroundColor, width: arrowWidth, height: headerHeight, padding: const EdgeInsets.all(2), child: Material( - color: widget.headerStyle.backgroundColor ?? - widget.calendarTheme.headerBackgroundColor, + color: headerBackgroundColor, child: InkWell( //// set splash color as transparent when arrow reaches max date(disabled) - splashColor: - nextArrowColor != arrowColor || !widget.enableInteraction - ? Colors.transparent - : null, - highlightColor: - nextArrowColor != arrowColor || !widget.enableInteraction - ? Colors.transparent - : null, - hoverColor: - nextArrowColor != arrowColor || !widget.enableInteraction - ? Colors.transparent - : null, + splashColor: rightArrowSplashColor, + highlightColor: rightArrowSplashColor, + hoverColor: rightArrowSplashColor, splashFactory: _CustomSplashFactory(), onTap: _forward, child: Semantics( @@ -7903,21 +8135,20 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { )), ); + final Color? todaySplashColor = + !widget.enableInteraction ? Colors.transparent : null; final Widget todayIcon = Container( alignment: Alignment.center, - color: widget.headerStyle.backgroundColor ?? - widget.calendarTheme.headerBackgroundColor, + color: headerBackgroundColor, width: todayIconWidth, height: headerHeight, padding: const EdgeInsets.all(2), child: Material( - color: widget.headerStyle.backgroundColor ?? - widget.calendarTheme.headerBackgroundColor, + color: headerBackgroundColor, child: InkWell( - splashColor: !widget.enableInteraction ? Colors.transparent : null, - highlightColor: - !widget.enableInteraction ? Colors.transparent : null, - hoverColor: !widget.enableInteraction ? Colors.transparent : null, + splashColor: todaySplashColor, + highlightColor: todaySplashColor, + hoverColor: todaySplashColor, splashFactory: _CustomSplashFactory(), onTap: () { if (!widget.enableInteraction) { @@ -7959,12 +8190,11 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { !useMobilePlatformUI ? Container( alignment: Alignment.center, - color: widget.headerStyle.backgroundColor ?? - widget.calendarTheme.headerBackgroundColor, + color: headerBackgroundColor, width: dividerWidth, height: headerHeight, padding: const EdgeInsets.symmetric(vertical: 5), - child: VerticalDivider( + child: const VerticalDivider( color: Colors.grey, thickness: 0.5, )) @@ -8084,6 +8314,7 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { } Widget _getCalendarViewWidget( + Color headerBackgroundColor, bool useMobilePlatformUI, bool isNeedIcon, double width, @@ -8093,26 +8324,26 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { Color headerTextColor, CalendarView view, bool isHighlighted, + Color? highlightColor, double defaultCalendarViewTextSize, {String? semanticLabel}) { final String text = _calendarViews[view]!; + final Color? calendarViewSplashColor = + !widget.enableInteraction ? Colors.transparent : null; return Container( alignment: Alignment.center, - color: widget.headerStyle.backgroundColor ?? - widget.calendarTheme.headerBackgroundColor, + color: headerBackgroundColor, width: width, height: height, - padding: EdgeInsets.all(2), + padding: const EdgeInsets.all(2), child: Material( color: isHighlighted && (isNeedIcon || useMobilePlatformUI) ? Colors.grey.withOpacity(0.3) - : widget.headerStyle.backgroundColor ?? - widget.calendarTheme.headerBackgroundColor, + : headerBackgroundColor, child: InkWell( - splashColor: !widget.enableInteraction ? Colors.transparent : null, - highlightColor: - !widget.enableInteraction ? Colors.transparent : null, - hoverColor: !widget.enableInteraction ? Colors.transparent : null, + splashColor: calendarViewSplashColor, + highlightColor: calendarViewSplashColor, + hoverColor: calendarViewSplashColor, splashFactory: _CustomSplashFactory(), onTap: () { if (!widget.enableInteraction) { @@ -8144,7 +8375,7 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { alignment: Alignment.center, child: Row( mainAxisAlignment: MainAxisAlignment.center, - children: [ + children: [ Text( text, style: TextStyle( @@ -8174,8 +8405,7 @@ class _CalendarHeaderViewState extends State<_CalendarHeaderView> { text, style: TextStyle( color: isHighlighted - ? widget.todayHighlightColor ?? - widget.calendarTheme.todayHighlightColor + ? highlightColor : headerTextColor, fontSize: defaultCalendarViewTextSize), maxLines: 1, @@ -8328,8 +8558,8 @@ class _ScheduleLabelPainter extends CustomPainter { final SfCalendarThemeData calendarTheme; final bool isDisplayDate; final double textScaleFactor; - TextPainter _textPainter = TextPainter(); - Paint _backgroundPainter = Paint(); + final TextPainter _textPainter = TextPainter(); + final Paint _backgroundPainter = Paint(); @override void paint(Canvas canvas, Size size) { @@ -8391,7 +8621,8 @@ class _ScheduleLabelPainter extends CustomPainter { size.width - (isRTL ? viewPadding : padding), size.height - (2 * (viewPadding + padding))); _backgroundPainter.color = Colors.grey.withOpacity(0.1); - canvas.drawRRect(RRect.fromRectAndRadius(rect, Radius.circular(4)), + canvas.drawRRect( + RRect.fromRectAndRadius(rect, const Radius.circular(4)), _backgroundPainter); } } @@ -8469,7 +8700,8 @@ class _ScheduleLabelPainter extends CustomPainter { final TextSpan span = TextSpan( text: DateFormat(monthFormat, locale).format(startDate).toString(), style: scheduleViewSettings.monthHeaderSettings.monthTextStyle ?? - TextStyle(color: Colors.white, fontSize: 20, fontFamily: 'Roboto'), + const TextStyle( + color: Colors.white, fontSize: 20, fontFamily: 'Roboto'), ); _backgroundPainter.shader = null; _backgroundPainter.color = @@ -8609,7 +8841,7 @@ class _ScheduleAppointmentView extends Stack { super.updateRenderObject(context, renderObject); if (renderObject is _AppointmentViewHeaderRenderObject) { - renderObject..scrollableState = Scrollable.of(context); + renderObject.scrollableState = Scrollable.of(context); } } } @@ -8877,7 +9109,9 @@ class _CustomSplash extends InteractiveInkFeature { void _handleAlphaStatusChanged(AnimationStatus status) { /// Dispose inkwell animation when the animation completed. - if (status == AnimationStatus.completed) dispose(); + if (status == AnimationStatus.completed) { + dispose(); + } } @override @@ -8966,8 +9200,8 @@ class _AgendaDateTimePainter extends CustomPainter { final bool isRTL; final double textScaleFactor; final bool isMobilePlatform; - Paint _linePainter = Paint(); - TextPainter _textPainter = TextPainter(); + final Paint _linePainter = Paint(); + final TextPainter _textPainter = TextPainter(); @override void paint(Canvas canvas, Size size) { @@ -9006,27 +9240,21 @@ class _AgendaDateTimePainter extends CustomPainter { fontWeight: FontWeight.normal)); } - final Color? selectedDayTextColor = isToday - ? todayHighlightColor - : dayTextStyle.color != null - ? dayTextStyle.color - : calendarTheme.agendaDayTextStyle.color; - final Color? selectedDateTextColor = isToday - ? calendarTheme.todayTextStyle.color - : dateTextStyle.color != null - ? dateTextStyle.color - : calendarTheme.agendaDateTextStyle.color; - dayTextStyle = dayTextStyle.copyWith(color: selectedDayTextColor); - dateTextStyle = dateTextStyle.copyWith(color: selectedDateTextColor); if (isToday) { + final Color? todayTextStyleColor = todayTextStyle != null + ? todayTextStyle!.color + : calendarTheme.todayTextStyle.color; + final Color? todayTextColor = + CalendarViewHelper.getTodayHighlightTextColor( + todayHighlightColor, todayTextStyle, calendarTheme); dayTextStyle = todayTextStyle != null - ? todayTextStyle!.copyWith( - fontSize: dayTextStyle.fontSize, color: selectedDayTextColor) - : dayTextStyle; + ? todayTextStyle! + .copyWith(fontSize: dayTextStyle.fontSize, color: todayTextColor) + : dayTextStyle.copyWith(color: todayTextColor); dateTextStyle = todayTextStyle != null ? todayTextStyle!.copyWith( - fontSize: dateTextStyle.fontSize, color: selectedDateTextColor) - : dateTextStyle; + fontSize: dateTextStyle.fontSize, color: todayTextStyleColor) + : dateTextStyle.copyWith(color: todayTextStyleColor); } /// Draw day label other than web schedule view. @@ -9125,7 +9353,7 @@ class _AgendaDateTimePainter extends CustomPainter { final String dateText = selectedDate!.day.toString(); /// Calculate the date text maximum width value. - final String maxWidthDateText = '30'; + const String maxWidthDateText = '30'; final String dayText = DateFormat( isRTL ? scheduleViewSettings!.dayHeaderSettings.dayFormat + ', MMM' @@ -9290,7 +9518,9 @@ RectCallback? _getClipCallback( assert(containedInkWell); return rectCallback; } - if (containedInkWell) return () => Offset.zero & referenceBox.size; + if (containedInkWell) { + return () => Offset.zero & referenceBox.size; + } return null; } diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/views/calendar_view.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/views/calendar_view.dart index bb486d6d5..e8b6e534a 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/views/calendar_view.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/views/calendar_view.dart @@ -25,6 +25,7 @@ import '../settings/month_view_settings.dart'; import '../settings/time_region.dart'; import '../settings/time_slot_view_settings.dart'; import '../settings/view_header_style.dart'; +import '../settings/week_number_style.dart'; import '../sfcalendar.dart'; import '../views/day_view.dart'; import '../views/month_view.dart'; @@ -62,7 +63,9 @@ class CustomCalendarScrollView extends StatefulWidget { this.maxDate, this.localizations, this.updateCalendarState, - this.getCalendarState); + this.getCalendarState, + {Key? key}) + : super(key: key); /// Holds the calendar instance used to get the calendar properties. final SfCalendar calendar; @@ -132,6 +135,46 @@ class CustomCalendarScrollView extends StatefulWidget { /// Holds the localization data of the calendar widget. final SfLocalizations localizations; + /// Updates the focus to the custom scroll view element. + void updateFocus() { + if (key == null) { + return; + } + + // ignore: avoid_as + final GlobalKey scrollViewKey = key! as GlobalKey; + final Object? currentState = scrollViewKey.currentState; + if (currentState == null) { + return; + } + + final _CustomCalendarScrollViewState state = + // ignore: avoid_as + currentState as _CustomCalendarScrollViewState; + if (!state._focusNode.hasFocus) { + state._focusNode.requestFocus(); + } + } + + /// Update the scroll position when the display date time changes. + void updateScrollPosition() { + if (key == null) { + return; + } + + // ignore: avoid_as + final GlobalKey scrollViewKey = key! as GlobalKey; + final Object? currentState = scrollViewKey.currentState; + if (currentState == null) { + return; + } + + final _CustomCalendarScrollViewState state = + // ignore: avoid_as + currentState as _CustomCalendarScrollViewState; + state._updateMoveToDate(); + } + @override _CustomCalendarScrollViewState createState() => _CustomCalendarScrollViewState(); @@ -143,7 +186,7 @@ class _CustomCalendarScrollViewState extends State late _CalendarView _currentView, _nextView, _previousView; // the three children which to be added into the layout - List<_CalendarView> _children = <_CalendarView>[]; + final List<_CalendarView> _children = <_CalendarView>[]; // holds the index of the current displaying view int _currentChildIndex = 1; @@ -161,7 +204,7 @@ class _CustomCalendarScrollViewState extends State late Animation _animation; // tween animation to handle the animation - Tween _tween = Tween(begin: 0.0, end: 0.1); + final Tween _tween = Tween(begin: 0.0, end: 0.1); // Three visible dates for the three views, the dates will updated based on // the swiping in the swipe end currentViewVisibleDates which stores the @@ -173,12 +216,12 @@ class _CustomCalendarScrollViewState extends State /// keys maintained to access the data and methods from the calendar view /// class. - GlobalKey<_CalendarViewState> _previousViewKey = + final GlobalKey<_CalendarViewState> _previousViewKey = GlobalKey<_CalendarViewState>(), _currentViewKey = GlobalKey<_CalendarViewState>(), _nextViewKey = GlobalKey<_CalendarViewState>(); - UpdateCalendarStateDetails _updateCalendarStateDetails = + final UpdateCalendarStateDetails _updateCalendarStateDetails = UpdateCalendarStateDetails(); /// Collection used to store the special regions and @@ -200,7 +243,7 @@ class _CustomCalendarScrollViewState extends State /// Used to perform the drag or scroll in timeline view. Drag? _drag; - FocusNode _focusNode = FocusNode(); + final FocusScopeNode _focusNode = FocusScopeNode(); @override void initState() { @@ -294,7 +337,7 @@ class _CustomCalendarScrollViewState extends State /// Check and re renders the views if the resource collection changed. if (CalendarViewHelper.isTimelineView(widget.view) && - !CalendarViewHelper.isResourceCollectionEqual( + !CalendarViewHelper.isCollectionEqual( oldWidget.resourceCollection, widget.resourceCollection)) { _updateSelectedResourceIndex(); _position = 0; @@ -326,7 +369,8 @@ class _CustomCalendarScrollViewState extends State oldWidget.calendarTheme != widget.calendarTheme || oldWidget.locale != widget.locale || oldWidget.calendar.selectionDecoration != - widget.calendar.selectionDecoration) { + widget.calendar.selectionDecoration || + oldWidget.calendar.weekNumberStyle != widget.calendar.weekNumberStyle) { final bool isTimelineView = CalendarViewHelper.isTimelineView(widget.view); if (widget.view != CalendarView.month && @@ -424,9 +468,11 @@ class _CustomCalendarScrollViewState extends State rightPosition = 0, topPosition = 0, bottomPosition = 0; - if (widget.calendar.monthViewSettings.navigationDirection == - MonthNavigationDirection.horizontal || - widget.view != CalendarView.month) { + final bool isHorizontalNavigation = + widget.calendar.monthViewSettings.navigationDirection == + MonthNavigationDirection.horizontal || + widget.view != CalendarView.month; + if (isHorizontalNavigation) { leftPosition = -widget.width; rightPosition = -widget.width; } else { @@ -438,9 +484,7 @@ class _CustomCalendarScrollViewState extends State final Widget customScrollWidget = GestureDetector( child: CustomScrollViewerLayout( _addViews(), - widget.view != CalendarView.month || - widget.calendar.monthViewSettings.navigationDirection == - MonthNavigationDirection.horizontal + isHorizontalNavigation ? CustomScrollDirection.horizontal : CustomScrollDirection.vertical, _position, @@ -453,21 +497,9 @@ class _CustomCalendarScrollViewState extends State onHorizontalDragStart: isTimelineView ? null : _onHorizontalStart, onHorizontalDragUpdate: isTimelineView ? null : _onHorizontalUpdate, onHorizontalDragEnd: isTimelineView ? null : _onHorizontalEnd, - onVerticalDragStart: widget.view == CalendarView.month && - widget.calendar.monthViewSettings.navigationDirection == - MonthNavigationDirection.vertical - ? _onVerticalStart - : null, - onVerticalDragUpdate: widget.view == CalendarView.month && - widget.calendar.monthViewSettings.navigationDirection == - MonthNavigationDirection.vertical - ? _onVerticalUpdate - : null, - onVerticalDragEnd: widget.view == CalendarView.month && - widget.calendar.monthViewSettings.navigationDirection == - MonthNavigationDirection.vertical - ? _onVerticalEnd - : null, + onVerticalDragStart: isHorizontalNavigation ? null : _onVerticalStart, + onVerticalDragUpdate: isHorizontalNavigation ? null : _onVerticalUpdate, + onVerticalDragEnd: isHorizontalNavigation ? null : _onVerticalEnd, ); return Stack( @@ -477,23 +509,23 @@ class _CustomCalendarScrollViewState extends State right: rightPosition, bottom: bottomPosition, top: topPosition, - child: RawKeyboardListener( - focusNode: _focusNode, + child: FocusScope( + node: _focusNode, onKey: _onKeyDown, child: isTimelineView ? Listener( onPointerSignal: _handlePointerSignal, child: RawGestureDetector( - gestures: { + gestures: { HorizontalDragGestureRecognizer: GestureRecognizerFactoryWithHandlers< HorizontalDragGestureRecognizer>( () => HorizontalDragGestureRecognizer(), (HorizontalDragGestureRecognizer instance) { - instance..onUpdate = _handleDragUpdate; - instance..onStart = _handleDragStart; - instance..onEnd = _handleDragEnd; - instance..onCancel = _handleDragCancel; + instance.onUpdate = _handleDragUpdate; + instance.onStart = _handleDragStart; + instance.onEnd = _handleDragEnd; + instance.onCancel = _handleDragCancel; }, ) }, @@ -510,6 +542,7 @@ class _CustomCalendarScrollViewState extends State void dispose() { _animationController.dispose(); _animation.removeListener(animationListener); + _focusNode.dispose(); super.dispose(); } @@ -528,7 +561,7 @@ class _CustomCalendarScrollViewState extends State return null; } // ignore: avoid_as - return view.key as GlobalKey<_CalendarViewState>; + return view.key! as GlobalKey<_CalendarViewState>; } /// Handle start of the scroll, set the scroll start position and check @@ -909,40 +942,13 @@ class _CustomCalendarScrollViewState extends State rule = rule + newSubString; } - List recursiveDates; - DateTime endDate; - final List ruleSeparator = ['=', ';', ',']; - final List rRule = - RecurrenceHelper.splitRule(region.recurrenceRule!, ruleSeparator); - if (region.recurrenceRule!.contains('UNTIL')) { - final String untilValue = rRule[rRule.indexOf('UNTIL') + 1]; - endDate = DateTime.parse(untilValue); - endDate = addDuration( - endDate, region.actualEndTime.difference(region.actualStartTime)); - endDate = DateTime(endDate.year, endDate.month, endDate.day, 23, 59, 59); - if (!(regionStartDate.isBefore(visibleEndDate) && - visibleStartDate.isBefore(endDate))) { - return; - } - } else if (region.recurrenceRule!.contains('COUNT')) { - recursiveDates = RecurrenceHelper.getRecurrenceDateTimeCollection( - region.recurrenceRule!, region.actualStartTime); - endDate = recursiveDates.last; - endDate = addDuration( - endDate, region.actualEndTime.difference(region.actualStartTime)); - endDate = DateTime(endDate.year, endDate.month, endDate.day, 23, 59, 59); - if (!(regionStartDate.isBefore(visibleEndDate) && - visibleStartDate.isBefore(endDate))) { - return; - } - } - - recursiveDates = RecurrenceHelper.getRecurrenceDateTimeCollection( - rule, region.actualStartTime, - recurrenceDuration: - region.actualEndTime.difference(region.actualStartTime), - specificStartDate: visibleStartDate, - specificEndDate: visibleEndDate); + final List recursiveDates = + RecurrenceHelper.getRecurrenceDateTimeCollection( + rule, region.actualStartTime, + recurrenceDuration: + region.actualEndTime.difference(region.actualStartTime), + specificStartDate: visibleStartDate, + specificEndDate: visibleEndDate); for (int j = 0; j < recursiveDates.length; j++) { final DateTime recursiveDate = recursiveDates[j]; @@ -952,9 +958,7 @@ class _CustomCalendarScrollViewState extends State final DateTime date = AppointmentHelper.convertTimeToAppointmentTimeZone( region.recurrenceExceptionDates![i], '', calendarTimeZone); - if (date.year == recursiveDate.year && - date.month == recursiveDate.month && - date.day == recursiveDate.day) { + if (isSameDate(date, recursiveDate)) { isDateContains = true; break; } @@ -975,8 +979,8 @@ class _CustomCalendarScrollViewState extends State DateTime recursiveDate, String? calendarTimeZone) { final int minutes = region.actualEndTime.difference(region.actualStartTime).inMinutes; - final DateTime actualEndTime = - addDuration(recursiveDate, Duration(minutes: minutes)); + final DateTime actualEndTime = DateTimeHelper.getDateTimeValue( + addDuration(recursiveDate, Duration(minutes: minutes))); final DateTime startDate = AppointmentHelper.convertTimeToAppointmentTimeZone( recursiveDate, region.timeZone, calendarTimeZone); @@ -1311,9 +1315,9 @@ class _CustomCalendarScrollViewState extends State } /// Check the collections are not equal based on its length - if ((regions1 != null && regions2 == null) || - (regions1 == null && regions2 != null) || - (regions1!.length != regions2!.length)) { + if (regions2 == null || + regions1 == null || + regions1.length != regions2.length) { return false; } @@ -1337,7 +1341,7 @@ class _CustomCalendarScrollViewState extends State for (int i = 0; i < _children.length; i++) { final GlobalKey<_CalendarViewState> viewKey = // ignore: avoid_as - _children[i].key as GlobalKey<_CalendarViewState>; + _children[i].key! as GlobalKey<_CalendarViewState>; if (CalendarViewHelper.isResourceEnabled( widget.calendar.dataSource, widget.view)) { viewKey.currentState!._selectedResourceIndex = 0; @@ -1356,7 +1360,7 @@ class _CustomCalendarScrollViewState extends State for (int i = 0; i < _children.length; i++) { final GlobalKey<_CalendarViewState> viewKey = // ignore: avoid_as - _children[i].key as GlobalKey<_CalendarViewState>; + _children[i].key! as GlobalKey<_CalendarViewState>; final int selectedResourceIndex = viewKey.currentState!._selectedResourceIndex; if (selectedResourceIndex != -1) { @@ -1565,9 +1569,9 @@ class _CustomCalendarScrollViewState extends State _updateCurrentViewVisibleDates(isNextView: true); } - void _moveToPreviousViewWithAnimation() { + void _moveToPreviousViewWithAnimation({bool isScrollToEnd = false}) { if (!widget.isMobilePlatform) { - _moveToPreviousWebViewWithAnimation(); + _moveToPreviousWebViewWithAnimation(isScrollToEnd: isScrollToEnd); return; } @@ -1593,7 +1597,7 @@ class _CustomCalendarScrollViewState extends State // Handled for time line view, to move the previous and next view to it's // start and end position accordingly if (CalendarViewHelper.isTimelineView(widget.view)) { - _positionTimelineView(isScrolledToEnd: false); + _positionTimelineView(isScrolledToEnd: isScrollToEnd); } if (widget.calendar.monthViewSettings.navigationDirection == @@ -1617,7 +1621,7 @@ class _CustomCalendarScrollViewState extends State _updateCurrentViewVisibleDates(); } - void _moveToPreviousWebViewWithAnimation() { + void _moveToPreviousWebViewWithAnimation({bool isScrollToEnd = false}) { if (!DateTimeHelper.canMoveToPreviousView( widget.view, widget.calendar.monthViewSettings.numberOfWeeksInView, @@ -1638,12 +1642,12 @@ class _CustomCalendarScrollViewState extends State return; } + final bool isTimelineView = CalendarViewHelper.isTimelineView(widget.view); // Handled for time line view, to move the previous and next view to it's // start and end position accordingly - if (CalendarViewHelper.isTimelineView(widget.view)) { - _positionTimelineView(isScrolledToEnd: false); - } else if (!CalendarViewHelper.isTimelineView(widget.view) && - widget.view != CalendarView.month) { + if (isTimelineView) { + _positionTimelineView(isScrolledToEnd: isScrollToEnd); + } else if (!isTimelineView && widget.view != CalendarView.month) { _updateDayViewScrollPosition(); } @@ -1659,8 +1663,7 @@ class _CustomCalendarScrollViewState extends State /// back to the view or swipes three times will render the all day panel as /// expanded, to collapse the all day panel in day, week and work week view, /// we have added this condition and called the method. - if (widget.view != CalendarView.month && - !CalendarViewHelper.isTimelineView(widget.view)) { + if (widget.view != CalendarView.month && !isTimelineView) { _updateAllDayPanel(); } @@ -1696,12 +1699,12 @@ class _CustomCalendarScrollViewState extends State return; } + final bool isTimelineView = CalendarViewHelper.isTimelineView(widget.view); // Handled for time line view, to move the previous and next view to it's // start and end position accordingly - if (CalendarViewHelper.isTimelineView(widget.view)) { + if (isTimelineView) { _positionTimelineView(isScrolledToEnd: false); - } else if (!CalendarViewHelper.isTimelineView(widget.view) && - widget.view != CalendarView.month) { + } else if (!isTimelineView && widget.view != CalendarView.month) { _updateDayViewScrollPosition(); } @@ -1718,8 +1721,7 @@ class _CustomCalendarScrollViewState extends State /// back to the view or swipes three times will render the all day panel as /// expanded, to collapse the all day panel in day, week and work week view, /// we have added this condition and called the method. - if (widget.view != CalendarView.month && - !CalendarViewHelper.isTimelineView(widget.view)) { + if (widget.view != CalendarView.month && !isTimelineView) { _updateAllDayPanel(); } @@ -1825,26 +1827,20 @@ class _CustomCalendarScrollViewState extends State return -1; } - DateTime _updateSelectedDateForRightArrow( - _CalendarView currentView, _CalendarViewState currentViewState) { - DateTime selectedDate; - + DateTime _updateSelectedDateForRightArrow(_CalendarView currentView, + _CalendarViewState currentViewState, DateTime? selectedDate) { /// Condition added to move the view to next view when the selection reaches /// the last horizontal cell of the view in day, week, workweek, month and /// timeline month. if (!CalendarViewHelper.isTimelineView(widget.view)) { final int visibleDatesCount = currentView.visibleDates.length; - if (isSameDate(currentView.visibleDates[visibleDatesCount - 1], - currentViewState._selectionPainter!.selectedDate)) { + if (isSameDate( + currentView.visibleDates[visibleDatesCount - 1], selectedDate)) { _moveToNextViewWithAnimation(); } - selectedDate = AppointmentHelper.addDaysWithTime( - currentViewState._selectionPainter!.selectedDate!, - 1, - currentViewState._selectionPainter!.selectedDate!.hour, - currentViewState._selectionPainter!.selectedDate!.minute, - currentViewState._selectionPainter!.selectedDate!.second); + selectedDate = AppointmentHelper.addDaysWithTime(selectedDate!, 1, + selectedDate.hour, selectedDate.minute, selectedDate.second); /// Move to next view when the new selected date as next month date. if (widget.view == CalendarView.month && @@ -1861,7 +1857,7 @@ class _CustomCalendarScrollViewState extends State widget.calendar.timeSlotViewSettings.nonWorkingDays.length; i++) { if (widget.calendar.timeSlotViewSettings.nonWorkingDays - .contains(selectedDate.weekday)) { + .contains(selectedDate!.weekday)) { selectedDate = AppointmentHelper.addDaysWithTime(selectedDate, 1, selectedDate.hour, selectedDate.minute, selectedDate.second); } else { @@ -1872,9 +1868,7 @@ class _CustomCalendarScrollViewState extends State } else { final double xPosition = widget.view == CalendarView.timelineMonth ? 0 - : AppointmentHelper.timeToPosition( - widget.calendar, - currentViewState._selectionPainter!.selectedDate!, + : AppointmentHelper.timeToPosition(widget.calendar, selectedDate!, currentViewState._timeIntervalHeight); final int rowIndex = _getRowOfDate(currentView.visibleDates, currentViewState); @@ -1889,7 +1883,7 @@ class _CustomCalendarScrollViewState extends State currentViewState._timeIntervalHeight); } if (widget.view == CalendarView.timelineDay && - currentViewState._selectionPainter!.selectedDate! + selectedDate! .add(widget.calendar.timeSlotViewSettings.timeInterval) .day != currentView @@ -1910,14 +1904,10 @@ class _CustomCalendarScrollViewState extends State /// hence to update the selected date for timeline month we must add a day /// and for other timeline views we must add the given time interval. if (widget.view == CalendarView.timelineMonth) { - selectedDate = AppointmentHelper.addDaysWithTime( - currentViewState._selectionPainter!.selectedDate!, - 1, - currentViewState._selectionPainter!.selectedDate!.hour, - currentViewState._selectionPainter!.selectedDate!.minute, - currentViewState._selectionPainter!.selectedDate!.second); + selectedDate = AppointmentHelper.addDaysWithTime(selectedDate!, 1, + selectedDate.hour, selectedDate.minute, selectedDate.second); } else { - selectedDate = currentViewState._selectionPainter!.selectedDate! + selectedDate = selectedDate! .add(widget.calendar.timeSlotViewSettings.timeInterval); } if (widget.view == CalendarView.timelineWorkWeek) { @@ -1927,7 +1917,7 @@ class _CustomCalendarScrollViewState extends State widget.calendar.timeSlotViewSettings.nonWorkingDays.length; i++) { if (widget.calendar.timeSlotViewSettings.nonWorkingDays - .contains(selectedDate.weekday)) { + .contains(selectedDate!.weekday)) { selectedDate = AppointmentHelper.addDaysWithTime(selectedDate, 1, selectedDate.hour, selectedDate.minute, selectedDate.second); } else { @@ -1937,23 +1927,19 @@ class _CustomCalendarScrollViewState extends State } } - return selectedDate; + return selectedDate!; } - DateTime _updateSelectedDateForLeftArrow( - _CalendarView currentView, _CalendarViewState currentViewState) { - DateTime selectedDate; + DateTime _updateSelectedDateForLeftArrow(_CalendarView currentView, + _CalendarViewState currentViewState, DateTime? selectedDate) { if (!CalendarViewHelper.isTimelineView(widget.view)) { if (isSameDate(currentViewState.widget.visibleDates[0], currentViewState._selectionPainter!.selectedDate)) { _moveToPreviousViewWithAnimation(); } - selectedDate = AppointmentHelper.addDaysWithTime( - currentViewState._selectionPainter!.selectedDate!, - -1, - currentViewState._selectionPainter!.selectedDate!.hour, - currentViewState._selectionPainter!.selectedDate!.minute, - currentViewState._selectionPainter!.selectedDate!.second); + + selectedDate = AppointmentHelper.addDaysWithTime(selectedDate!, -1, + selectedDate.hour, selectedDate.minute, selectedDate.second); /// Move to previous view when the selected date as previous month date. if (widget.view == CalendarView.month && @@ -1971,7 +1957,7 @@ class _CustomCalendarScrollViewState extends State widget.calendar.timeSlotViewSettings.nonWorkingDays.length; i++) { if (widget.calendar.timeSlotViewSettings.nonWorkingDays - .contains(selectedDate.weekday)) { + .contains(selectedDate!.weekday)) { selectedDate = AppointmentHelper.addDaysWithTime(selectedDate, -1, selectedDate.hour, selectedDate.minute, selectedDate.second); } else { @@ -1982,9 +1968,7 @@ class _CustomCalendarScrollViewState extends State } else { final double xPosition = widget.view == CalendarView.timelineMonth ? 0 - : AppointmentHelper.timeToPosition( - widget.calendar, - currentViewState._selectionPainter!.selectedDate!, + : AppointmentHelper.timeToPosition(widget.calendar, selectedDate!, currentViewState._timeIntervalHeight); final int rowIndex = _getRowOfDate(currentView.visibleDates, currentViewState); @@ -1992,7 +1976,7 @@ class _CustomCalendarScrollViewState extends State _getSingleViewWidthForTimeLineView(currentViewState); if ((rowIndex * singleChildWidth) + xPosition == 0) { - _moveToPreviousViewWithAnimation(); + _moveToPreviousViewWithAnimation(isScrollToEnd: true); } if ((rowIndex * singleChildWidth) + xPosition <= @@ -2008,14 +1992,10 @@ class _CustomCalendarScrollViewState extends State /// a day and for other timeline views we must subtract the given time /// interval. if (widget.view == CalendarView.timelineMonth) { - selectedDate = AppointmentHelper.addDaysWithTime( - currentViewState._selectionPainter!.selectedDate!, - -1, - currentViewState._selectionPainter!.selectedDate!.hour, - currentViewState._selectionPainter!.selectedDate!.minute, - currentViewState._selectionPainter!.selectedDate!.second); + selectedDate = AppointmentHelper.addDaysWithTime(selectedDate!, -1, + selectedDate.hour, selectedDate.minute, selectedDate.second); } else { - selectedDate = currentViewState._selectionPainter!.selectedDate! + selectedDate = selectedDate! .subtract(widget.calendar.timeSlotViewSettings.timeInterval); } if (widget.view == CalendarView.timelineWorkWeek) { @@ -2025,7 +2005,7 @@ class _CustomCalendarScrollViewState extends State widget.calendar.timeSlotViewSettings.nonWorkingDays.length; i++) { if (widget.calendar.timeSlotViewSettings.nonWorkingDays - .contains(selectedDate.weekday)) { + .contains(selectedDate!.weekday)) { selectedDate = AppointmentHelper.addDaysWithTime(selectedDate, -1, selectedDate.hour, selectedDate.minute, selectedDate.second); } else { @@ -2035,24 +2015,23 @@ class _CustomCalendarScrollViewState extends State } } - return selectedDate; + return selectedDate!; } - DateTime? _updateSelectedDateForUpArrow( - _CalendarView currentView, _CalendarViewState currentViewState) { + DateTime? _updateSelectedDateForUpArrow(_CalendarView currentView, + _CalendarViewState currentViewState, DateTime? selectedDate) { if (widget.view == CalendarView.month) { final int rowIndex = _getRowOfDate(currentView.visibleDates, currentViewState); if (rowIndex == 0) { - return currentViewState._selectionPainter!.selectedDate; + return selectedDate; } - - DateTime selectedDate = AppointmentHelper.addDaysWithTime( - currentViewState._selectionPainter!.selectedDate!, + selectedDate = AppointmentHelper.addDaysWithTime( + selectedDate!, -DateTime.daysPerWeek, - currentViewState._selectionPainter!.selectedDate!.hour, - currentViewState._selectionPainter!.selectedDate!.minute, - currentViewState._selectionPainter!.selectedDate!.second); + selectedDate.hour, + selectedDate.minute, + selectedDate.second); /// Move to month start date when the new selected date as /// previous month date. @@ -2061,24 +2040,21 @@ class _CustomCalendarScrollViewState extends State widget.calendar.monthViewSettings.showTrailingAndLeadingDates, currentView.visibleDates[currentView.visibleDates.length ~/ 2].month, selectedDate)) { - selectedDate = AppointmentHelper.getMonthStartDate( - currentViewState._selectionPainter!.selectedDate!); + selectedDate = AppointmentHelper.getMonthStartDate(selectedDate); } return selectedDate; } else if (!CalendarViewHelper.isTimelineView(widget.view)) { final double yPosition = AppointmentHelper.timeToPosition( - widget.calendar, - currentViewState._selectionPainter!.selectedDate!, - currentViewState._timeIntervalHeight); + widget.calendar, selectedDate!, currentViewState._timeIntervalHeight); if (yPosition == 0) { - return currentViewState._selectionPainter!.selectedDate; + return selectedDate; } if (yPosition <= currentViewState._scrollController!.offset) { currentViewState._scrollController! .jumpTo(yPosition - currentViewState._timeIntervalHeight); } - return currentViewState._selectionPainter!.selectedDate! + return selectedDate .subtract(widget.calendar.timeSlotViewSettings.timeInterval); } else if (CalendarViewHelper.isResourceEnabled( widget.calendar.dataSource, widget.view)) { @@ -2093,7 +2069,7 @@ class _CustomCalendarScrollViewState extends State if (currentViewState._selectedResourceIndex == -1) { currentViewState._selectedResourceIndex = 0; - return currentViewState._selectionPainter!.selectedDate; + return selectedDate; } if (currentViewState._selectedResourceIndex * resourceItemHeight < @@ -2106,28 +2082,28 @@ class _CustomCalendarScrollViewState extends State .jumpTo(scrollPosition); } - return currentViewState._selectionPainter!.selectedDate; + return selectedDate; } return null; } - DateTime? _updateSelectedDateForDownArrow( - _CalendarView currentView, _CalendarViewState currentViewState) { + DateTime? _updateSelectedDateForDownArrow(_CalendarView currentView, + _CalendarViewState currentViewState, DateTime? selectedDate) { if (widget.view == CalendarView.month) { final int rowIndex = _getRowOfDate(currentView.visibleDates, currentViewState); if (rowIndex == widget.calendar.monthViewSettings.numberOfWeeksInView - 1) { - return currentViewState._selectionPainter!.selectedDate!; + return selectedDate!; } - DateTime selectedDate = AppointmentHelper.addDaysWithTime( - currentViewState._selectionPainter!.selectedDate!, + selectedDate = AppointmentHelper.addDaysWithTime( + selectedDate!, DateTime.daysPerWeek, - currentViewState._selectionPainter!.selectedDate!.hour, - currentViewState._selectionPainter!.selectedDate!.minute, - currentViewState._selectionPainter!.selectedDate!.second); + selectedDate.hour, + selectedDate.minute, + selectedDate.second); /// Move to month end date when the new selected date as next month date. if (!CalendarViewHelper.isCurrentMonthDate( @@ -2135,23 +2111,20 @@ class _CustomCalendarScrollViewState extends State widget.calendar.monthViewSettings.showTrailingAndLeadingDates, currentView.visibleDates[currentView.visibleDates.length ~/ 2].month, selectedDate)) { - selectedDate = AppointmentHelper.getMonthEndDate( - currentViewState._selectionPainter!.selectedDate!); + selectedDate = AppointmentHelper.getMonthEndDate(selectedDate); } return selectedDate; } else if (!CalendarViewHelper.isTimelineView(widget.view)) { final double viewHeaderHeight = CalendarViewHelper.getViewHeaderHeight( widget.calendar.viewHeaderHeight, widget.view); final double yPosition = AppointmentHelper.timeToPosition( - widget.calendar, - currentViewState._selectionPainter!.selectedDate!, - currentViewState._timeIntervalHeight); + widget.calendar, selectedDate!, currentViewState._timeIntervalHeight); - if (currentViewState._selectionPainter!.selectedDate! + if (selectedDate .add(widget.calendar.timeSlotViewSettings.timeInterval) .day != - currentViewState._selectionPainter!.selectedDate!.day) { - return currentViewState._selectionPainter!.selectedDate!; + selectedDate.day) { + return selectedDate; } if (yPosition + @@ -2167,7 +2140,7 @@ class _CustomCalendarScrollViewState extends State currentViewState._scrollController!.offset + currentViewState._timeIntervalHeight); } - return currentViewState._selectionPainter!.selectedDate! + return selectedDate .add(widget.calendar.timeSlotViewSettings.timeInterval); } else if (CalendarViewHelper.isResourceEnabled( widget.calendar.dataSource, widget.view)) { @@ -2180,7 +2153,7 @@ class _CustomCalendarScrollViewState extends State if (currentViewState._selectedResourceIndex == widget.calendar.dataSource!.resources!.length - 1 || currentViewState._selectedResourceIndex == -1) { - return currentViewState._selectionPainter!.selectedDate!; + return selectedDate; } currentViewState._selectedResourceIndex += 1; @@ -2202,31 +2175,62 @@ class _CustomCalendarScrollViewState extends State .jumpTo(scrollPosition); } - return currentViewState._selectionPainter!.selectedDate!; + return selectedDate!; } return null; } - DateTime? _updateSelectedDate(RawKeyEvent event, - _CalendarViewState currentViewState, _CalendarView currentView) { + DateTime? _updateSelectedDate( + RawKeyEvent event, + _CalendarViewState currentViewState, + _CalendarView currentView, + int resourceIndex) { + DateTime? selectedDate = currentViewState._selectionPainter!.selectedDate; if (event.logicalKey == LogicalKeyboardKey.arrowRight) { - return _updateSelectedDateForRightArrow(currentView, currentViewState); + do { + selectedDate = _updateSelectedDateForRightArrow( + currentView, currentViewState, selectedDate); + } while (!_isSelectedDateEnabled(selectedDate, resourceIndex, true)); + return selectedDate; } else if (event.logicalKey == LogicalKeyboardKey.arrowLeft) { - return _updateSelectedDateForLeftArrow(currentView, currentViewState); + do { + selectedDate = _updateSelectedDateForLeftArrow( + currentView, currentViewState, selectedDate); + } while (!_isSelectedDateEnabled(selectedDate, resourceIndex, true)); + return selectedDate; } else if (event.logicalKey == LogicalKeyboardKey.arrowUp) { - return _updateSelectedDateForUpArrow(currentView, currentViewState); + do { + selectedDate = _updateSelectedDateForUpArrow( + currentView, currentViewState, selectedDate); + if (resourceIndex != -1 && + currentView.regions != null && + currentView.regions!.isNotEmpty) { + resourceIndex -= 1; + } + } while (!_isSelectedDateEnabled(selectedDate!, resourceIndex, true)); + return selectedDate; } else if (event.logicalKey == LogicalKeyboardKey.arrowDown) { - return _updateSelectedDateForDownArrow(currentView, currentViewState); + do { + selectedDate = _updateSelectedDateForDownArrow( + currentView, currentViewState, selectedDate); + if (resourceIndex != -1 && + currentView.regions != null && + currentView.regions!.isNotEmpty) { + resourceIndex += 1; + } + } while (!_isSelectedDateEnabled(selectedDate!, resourceIndex, true)); + return selectedDate; } return null; } /// Checks the selected date is enabled or not. - bool _isSelectedDateEnabled(DateTime date, int resourceIndex) { - final bool isMonthView = (widget.view == CalendarView.month || - widget.view == CalendarView.timelineMonth); + bool _isSelectedDateEnabled(DateTime date, int resourceIndex, + [bool isMinMaxDate = false]) { + final bool isMonthView = widget.view == CalendarView.month || + widget.view == CalendarView.timelineMonth; final int timeInterval = CalendarViewHelper.getTimeInterval( widget.calendar.timeSlotViewSettings); if ((isMonthView && @@ -2238,7 +2242,7 @@ class _CustomCalendarScrollViewState extends State widget.calendar.maxDate, date, timeInterval))) { - return false; + return isMinMaxDate; } final List blackoutDates = []; @@ -2295,12 +2299,338 @@ class _CustomCalendarScrollViewState extends State return true; } - void _onKeyDown(RawKeyEvent event) { + /// Method to handle the page up/down key for timeslot views in calendar. + KeyEventResult _updatePageUpAndDown(RawKeyEvent event, + _CalendarViewState currentViewState, bool isResourceEnabled) { + if (widget.controller.view != CalendarView.day && + widget.controller.view != CalendarView.week && + widget.controller.view != CalendarView.workWeek && + !isResourceEnabled) { + return KeyEventResult.ignored; + } + + final ScrollController scrollController = isResourceEnabled + ? widget.resourcePanelScrollController! + : currentViewState._scrollController!; + final TargetPlatform platform = Theme.of(context).platform; + + double difference = 0; + final double scrollViewHeight = scrollController.position.maxScrollExtent + + scrollController.position.viewportDimension; + double divideValue = 0.25; + if (scrollController.position.pixels > scrollViewHeight / 2) { + divideValue = 0.5; + } + if (event.logicalKey == LogicalKeyboardKey.pageUp || + (platform == TargetPlatform.windows && + event.logicalKey.keyId == 0x10700000021)) { + if (scrollController.position.pixels == 0) { + return KeyEventResult.ignored; + } + difference = scrollController.position.pixels * divideValue; + scrollController.jumpTo(difference); + return KeyEventResult.handled; + } else if (event.logicalKey == LogicalKeyboardKey.pageDown || + (platform == TargetPlatform.windows && + event.logicalKey.keyId == 0x10700000022)) { + double viewHeaderHeight = CalendarViewHelper.getViewHeaderHeight( + widget.calendar.viewHeaderHeight, widget.controller.view!); + double allDayHeight = 0; + + if (widget.controller.view == CalendarView.day) { + allDayHeight = _kAllDayLayoutHeight; + viewHeaderHeight = 0; + } else { + allDayHeight = allDayHeight > _kAllDayLayoutHeight + ? _kAllDayLayoutHeight + : allDayHeight; + } + + final double timeRulerSize = CalendarViewHelper.getTimeLabelWidth( + widget.calendar.timeSlotViewSettings.timeRulerSize, + widget.controller.view!); + + final double viewPortHeight = isResourceEnabled + ? widget.height - viewHeaderHeight - timeRulerSize + : widget.height - allDayHeight - viewHeaderHeight; + + final double viewPortEndPosition = + scrollController.position.pixels + viewPortHeight; + if (viewPortEndPosition == scrollViewHeight) { + return KeyEventResult.ignored; + } + difference = + (scrollViewHeight - scrollController.position.pixels) * divideValue; + difference += scrollController.position.pixels; + if (difference + viewPortHeight >= scrollViewHeight) { + difference = scrollViewHeight - viewPortHeight; + } + scrollController.jumpTo(difference); + return KeyEventResult.handled; + } + + return KeyEventResult.ignored; + } + + /// Updates the appointment selection based on keyboard navigation in calendar + KeyEventResult _updateAppointmentSelection(RawKeyEvent event, + _CalendarViewState currentVisibleViewState, bool isResourceEnabled) { + if (widget.controller.view == CalendarView.schedule) { + return KeyEventResult.ignored; + } + + AppointmentView? selectedAppointment; + final AppointmentView? currentSelectedAppointment = + currentVisibleViewState._selectionPainter!.appointmentView; + final AppointmentView? currentAllDayAppointment = + currentVisibleViewState._allDaySelectionNotifier.value?.appointmentView; + bool isAllDay = currentAllDayAppointment != null; + final List appointmentCollection = currentVisibleViewState + ._appointmentLayout + .getAppointmentViewCollection(); + final List allDayAppointmentCollection = + _updateCalendarStateDetails.allDayAppointmentViewCollection; + final List tempAppColl = + isAllDay ? allDayAppointmentCollection : appointmentCollection; + if (event.isShiftPressed) { + if (event.logicalKey == LogicalKeyboardKey.tab) { + if (currentAllDayAppointment != null || + currentSelectedAppointment != null) { + int index = tempAppColl.indexOf(isAllDay + ? currentAllDayAppointment + : currentSelectedAppointment!); + index -= 1; + if (tempAppColl.length > index && !index.isNegative) { + selectedAppointment = tempAppColl[index].appointment != null + ? tempAppColl[index] + : null; + } + } + + if (currentSelectedAppointment != null && selectedAppointment == null) { + isAllDay = allDayAppointmentCollection.isNotEmpty; + selectedAppointment = isAllDay + ? allDayAppointmentCollection[ + allDayAppointmentCollection.length - 1] + : null; + } else if (currentSelectedAppointment == null && + currentAllDayAppointment == null && + selectedAppointment == null) { + selectedAppointment = + appointmentCollection[appointmentCollection.length - 1]; + } + + return _updateAppointmentSelectionOnView( + selectedAppointment, + currentVisibleViewState, + isAllDay, + isResourceEnabled, + !event.isShiftPressed); + } + } else if (event.logicalKey == LogicalKeyboardKey.tab) { + if (currentAllDayAppointment != null || + currentSelectedAppointment != null) { + int index = tempAppColl.indexOf( + isAllDay ? currentAllDayAppointment : currentSelectedAppointment!); + index += 1; + if (tempAppColl.length > index) { + selectedAppointment = tempAppColl[index].appointment != null + ? tempAppColl[index] + : null; + } + } + + if (currentAllDayAppointment != null && selectedAppointment == null) { + isAllDay = false; + selectedAppointment = appointmentCollection[0]; + } else if (currentAllDayAppointment == null && + currentSelectedAppointment == null) { + isAllDay = allDayAppointmentCollection.isNotEmpty; + selectedAppointment = isAllDay + ? allDayAppointmentCollection[0] + : appointmentCollection[0]; + } + + return _updateAppointmentSelectionOnView( + selectedAppointment, + currentVisibleViewState, + isAllDay, + isResourceEnabled, + !event.isShiftPressed); + } + + return KeyEventResult.ignored; + } + + /// Updates the selection for appointment view based on keyboard navigation + /// in Calendar. + KeyEventResult _updateAppointmentSelectionOnView( + AppointmentView? selectedAppointment, + _CalendarViewState currentVisibleViewState, + bool isAllDay, + bool isResourceEnabled, + bool isForward) { + final DateTime visibleStartDate = AppointmentHelper.convertToStartTime( + currentVisibleViewState.widget.visibleDates[0]); + final DateTime visibleEndDate = AppointmentHelper.convertToEndTime( + currentVisibleViewState.widget.visibleDates[ + currentVisibleViewState.widget.visibleDates.length - 1]); + + if (isAllDay && selectedAppointment != null) { + currentVisibleViewState._updateAllDaySelection(selectedAppointment, null); + currentVisibleViewState._selectionPainter!.appointmentView = null; + currentVisibleViewState._selectionPainter!.selectedDate = null; + currentVisibleViewState._selectionNotifier.value = + !currentVisibleViewState._selectionNotifier.value; + return KeyEventResult.handled; + } + + if (selectedAppointment != null && + AppointmentHelper.isAppointmentWithinVisibleDateRange( + selectedAppointment.appointment!, + visibleStartDate, + visibleEndDate)) { + currentVisibleViewState._allDaySelectionNotifier.value = null; + currentVisibleViewState._selectionPainter!.appointmentView = + selectedAppointment; + currentVisibleViewState._selectionPainter!.selectedDate = null; + currentVisibleViewState._selectionNotifier.value = + !currentVisibleViewState._selectionNotifier.value; + + if (widget.controller.view != CalendarView.month) { + late double offset; + late double viewPortSize; + final double scrollViewHeight = currentVisibleViewState + ._scrollController!.position.maxScrollExtent + + currentVisibleViewState + ._scrollController!.position.viewportDimension; + final double resourceViewSize = + isResourceEnabled ? widget.calendar.resourceViewSettings.size : 0; + final bool isTimeline = + CalendarViewHelper.isTimelineView(widget.controller.view!); + double viewHeaderHeight = CalendarViewHelper.getViewHeaderHeight( + widget.calendar.viewHeaderHeight, widget.controller.view!); + + if (isTimeline) { + viewPortSize = widget.width - resourceViewSize; + offset = selectedAppointment.appointmentRect!.left; + } else { + double allDayHeight = 0; + + if (widget.controller.view == CalendarView.day) { + allDayHeight = _kAllDayLayoutHeight; + viewHeaderHeight = 0; + } else { + allDayHeight = allDayHeight > _kAllDayLayoutHeight + ? _kAllDayLayoutHeight + : allDayHeight; + } + viewPortSize = widget.height - allDayHeight - viewHeaderHeight; + offset = selectedAppointment.appointmentRect!.top; + } + + _updateScrollViewToAppointment( + offset, + currentVisibleViewState._scrollController!, + viewPortSize, + scrollViewHeight); + + if (isResourceEnabled) { + final double resourcePanelHeight = widget + .resourcePanelScrollController!.position.viewportDimension + + widget.resourcePanelScrollController!.position.maxScrollExtent; + final double timeRulerSize = CalendarViewHelper.getTimeLabelWidth( + widget.calendar.timeSlotViewSettings.timeRulerSize, + widget.controller.view!), + viewPortSize = widget.height - viewHeaderHeight - timeRulerSize; + _updateScrollViewToAppointment( + selectedAppointment.appointmentRect!.top, + widget.resourcePanelScrollController!, + viewPortSize, + resourcePanelHeight); + } + } else if (widget.controller.view == CalendarView.month) { + widget.agendaSelectedDate.value = null; + } + + _updateCalendarStateDetails.selectedDate = null; + widget.updateCalendarState(_updateCalendarStateDetails); + return KeyEventResult.handled; + } else { + currentVisibleViewState._allDaySelectionNotifier.value = null; + currentVisibleViewState._selectionPainter!.appointmentView = null; + currentVisibleViewState._selectionPainter!.selectedDate = null; + currentVisibleViewState._selectionNotifier.value = + !currentVisibleViewState._selectionNotifier.value; + _updateCalendarStateDetails.selectedDate = null; + widget.updateCalendarState(_updateCalendarStateDetails); + isForward + ? FocusScope.of(context).nextFocus() + : FocusScope.of(context).previousFocus(); + return KeyEventResult.handled; + } + } + + /// Moves the scroll panel to the selected appointments position, if the + /// selected appointment doesn't falls on the view port. + void _updateScrollViewToAppointment( + double offset, + ScrollController scrollController, + double viewPortSize, + double panelHeight) { + if (offset < scrollController.position.pixels || + offset > (scrollController.position.pixels + viewPortSize)) { + if (offset + viewPortSize > panelHeight) { + offset = panelHeight - viewPortSize; + } + scrollController.jumpTo(offset); + } + } + + KeyEventResult _onKeyDown(FocusNode node, RawKeyEvent event) { + KeyEventResult result = KeyEventResult.ignored; if (event.runtimeType != RawKeyDownEvent) { - return; + return result; } widget.removePicker(); + + if (event.isControlPressed && widget.view != CalendarView.schedule) { + final bool canMoveToNextView = DateTimeHelper.canMoveToNextView( + widget.view, + widget.calendar.monthViewSettings.numberOfWeeksInView, + widget.calendar.minDate, + widget.calendar.maxDate, + _currentViewVisibleDates, + widget.calendar.timeSlotViewSettings.nonWorkingDays, + widget.isRTL); + final bool canMoveToPreviousView = DateTimeHelper.canMoveToPreviousView( + widget.view, + widget.calendar.monthViewSettings.numberOfWeeksInView, + widget.calendar.minDate, + widget.calendar.maxDate, + _currentViewVisibleDates, + widget.calendar.timeSlotViewSettings.nonWorkingDays, + widget.isRTL); + if (event.logicalKey == LogicalKeyboardKey.arrowRight && + canMoveToNextView) { + widget.isRTL + ? _moveToPreviousViewWithAnimation() + : _moveToNextViewWithAnimation(); + result = KeyEventResult.handled; + } else if (event.logicalKey == LogicalKeyboardKey.arrowLeft && + canMoveToPreviousView) { + widget.isRTL + ? _moveToNextViewWithAnimation() + : _moveToPreviousViewWithAnimation(); + result = KeyEventResult.handled; + } + result = KeyEventResult.ignored; + } + + CalendarViewHelper.handleViewSwitchKeyBoardEvent( + event, widget.controller, widget.calendar.allowedViews); + _CalendarViewState currentVisibleViewState; _CalendarView currentVisibleView; final bool isResourcesEnabled = CalendarViewHelper.isResourceEnabled( @@ -2316,6 +2646,46 @@ class _CustomCalendarScrollViewState extends State currentVisibleView = _nextView; } + result = _updatePageUpAndDown( + event, currentVisibleViewState, isResourcesEnabled); + + result = _updateAppointmentSelection( + event, currentVisibleViewState, isResourcesEnabled); + + if (event.logicalKey == LogicalKeyboardKey.enter && + CalendarViewHelper.shouldRaiseCalendarTapCallback( + widget.calendar.onTap)) { + final AppointmentView? selectedAppointment = currentVisibleViewState + ._allDaySelectionNotifier.value?.appointmentView ?? + currentVisibleViewState._selectionPainter!.appointmentView; + final List? selectedAppointments = + widget.controller.view == CalendarView.month && + selectedAppointment == null + ? AppointmentHelper.getSelectedDateAppointments( + _updateCalendarStateDetails.appointments, + widget.calendar.timeZone, + _updateCalendarStateDetails.selectedDate) + : selectedAppointment != null + ? [selectedAppointment.appointment!] + : null; + final CalendarElement tappedElement = + _updateCalendarStateDetails.selectedDate != null + ? CalendarElement.calendarCell + : CalendarElement.appointment; + + CalendarViewHelper.raiseCalendarTapCallback( + widget.calendar, + tappedElement == CalendarElement.appointment + ? selectedAppointments![0].startTime + : _updateCalendarStateDetails.selectedDate, + CalendarViewHelper.getCustomAppointments(selectedAppointments), + tappedElement, + isResourcesEnabled + ? widget.calendar.dataSource! + .resources![currentVisibleViewState._selectedResourceIndex] + : null); + } + final int previousResourceIndex = isResourcesEnabled ? currentVisibleViewState._selectedResourceIndex : -1; @@ -2325,19 +2695,21 @@ class _CustomCalendarScrollViewState extends State currentVisibleViewState.widget.visibleDates[ currentVisibleViewState.widget.visibleDates.length - 1], currentVisibleViewState._selectionPainter!.selectedDate)) { - final DateTime? selectedDate = _updateSelectedDate( - event, currentVisibleViewState, currentVisibleView); - final int resourceIndex = isResourcesEnabled ? currentVisibleViewState._selectedResourceIndex : -1; + + final DateTime? selectedDate = _updateSelectedDate( + event, currentVisibleViewState, currentVisibleView, resourceIndex); + if (selectedDate == null) { - return; + result = KeyEventResult.ignored; + return result; } if (!_isSelectedDateEnabled(selectedDate, resourceIndex)) { currentVisibleViewState._selectedResourceIndex = previousResourceIndex; - return; + return KeyEventResult.ignored; } if (widget.view == CalendarView.month) { @@ -2369,7 +2741,10 @@ class _CustomCalendarScrollViewState extends State !currentVisibleViewState._selectionNotifier.value; widget.updateCalendarState(_updateCalendarStateDetails); + result = KeyEventResult.handled; } + + return result; } void _positionTimelineView({bool isScrolledToEnd = true}) { @@ -2808,7 +3183,7 @@ class _CustomCalendarScrollViewState extends State for (int i = 0; i < _children.length; i++) { final GlobalKey<_CalendarViewState> viewKey = // ignore: avoid_as - _children[i].key as GlobalKey<_CalendarViewState>; + _children[i].key! as GlobalKey<_CalendarViewState>; if (viewKey.currentState!._selectionPainter!.selectedDate != _updateCalendarStateDetails.selectedDate) { viewKey.currentState!._selectionPainter!.selectedDate = @@ -2844,7 +3219,7 @@ class _CustomCalendarScrollViewState extends State final _CalendarView view = _children[i]; final GlobalKey<_CalendarViewState> viewKey = // ignore: avoid_as - view.key as GlobalKey<_CalendarViewState>; + view.key! as GlobalKey<_CalendarViewState>; if (widget.view == CalendarView.month && widget.calendar.monthCellBuilder != null) { if (view.visibleDates == _currentViewVisibleDates) { @@ -2958,22 +3333,23 @@ class _CalendarViewState extends State<_CalendarView> late AppointmentLayout _appointmentLayout; AnimationController? _timelineViewAnimationController; Animation? _timelineViewAnimation; - Tween _timelineViewTween = Tween(begin: 0.0, end: 0.1); + final Tween _timelineViewTween = Tween(begin: 0.0, end: 0.1); //// timeline header is used to implement the sticky view header in horizontal calendar view mode. late TimelineViewHeaderView _timelineViewHeader; _SelectionPainter? _selectionPainter; double _allDayHeight = 0; late double _timeIntervalHeight; - UpdateCalendarStateDetails _updateCalendarStateDetails = + final UpdateCalendarStateDetails _updateCalendarStateDetails = UpdateCalendarStateDetails(); ValueNotifier _allDaySelectionNotifier = ValueNotifier(null); late ValueNotifier _viewHeaderNotifier; - ValueNotifier _calendarCellNotifier = ValueNotifier(null), + final ValueNotifier _calendarCellNotifier = + ValueNotifier(null), _allDayNotifier = ValueNotifier(null), _appointmentHoverNotifier = ValueNotifier(null); - ValueNotifier _selectionNotifier = ValueNotifier(false), + final ValueNotifier _selectionNotifier = ValueNotifier(false), _timelineViewHeaderNotifier = ValueNotifier(false); late bool _isRTL; @@ -2997,7 +3373,7 @@ class _CalendarViewState extends State<_CalendarView> /// if set the appointment layout key property as new Global key when create /// the appointment layout then each of the time it creates new appointment /// layout rather than update the existing appointment layout. - GlobalKey _appointmentLayoutKey = GlobalKey(); + final GlobalKey _appointmentLayoutKey = GlobalKey(); Timer? _timer; late ValueNotifier _currentTimeNotifier; @@ -3077,18 +3453,19 @@ class _CalendarViewState extends State<_CalendarView> @override void didUpdateWidget(_CalendarView oldWidget) { + final bool isTimelineView = CalendarViewHelper.isTimelineView(widget.view); if (widget.view != CalendarView.month) { - if (!CalendarViewHelper.isTimelineView(widget.view)) { + if (!isTimelineView) { _updateTimeSlotView(oldWidget); } _updateHorizontalLineCount(oldWidget); - _scrollController = _scrollController ?? + _scrollController ??= ScrollController(initialScrollOffset: 0, keepScrollOffset: true) - ..addListener(_scrollListener); + ..addListener(_scrollListener); - if (CalendarViewHelper.isTimelineView(widget.view)) { + if (isTimelineView) { _updateTimelineViews(oldWidget); } } @@ -3108,14 +3485,20 @@ class _CalendarViewState extends State<_CalendarView> /// work week(eg., view changed to timeline week from timeline day). if ((oldWidget.view == CalendarView.month || oldWidget.view == CalendarView.schedule || - (oldWidget.view != widget.view && - CalendarViewHelper.isTimelineView(widget.view)) || + (oldWidget.view != widget.view && isTimelineView) || (CalendarViewHelper.isTimelineView(oldWidget.view) && - !CalendarViewHelper.isTimelineView(widget.view))) && + !isTimelineView)) && widget.view != CalendarView.month) { _scrollToPosition(); } + /// Method called to update all day height, when the view changed from + /// day to week views to avoid the blank space at the bottom of the view. + final bool isCurrentView = + _updateCalendarStateDetails.currentViewVisibleDates == + widget.visibleDates; + _updateAllDayHeight(isCurrentView); + _timeIntervalHeight = _getTimeIntervalHeight( widget.calendar, widget.view, @@ -3247,7 +3630,7 @@ class _CalendarViewState extends State<_CalendarView> return widget.calendar.showCurrentTimeIndicator && widget.view != CalendarView.month && widget.view != CalendarView.timelineMonth - ? Timer.periodic(Duration(seconds: 1), (Timer t) { + ? Timer.periodic(const Duration(seconds: 1), (Timer t) { final DateTime today = DateTime.now(); final DateTime viewEndDate = widget.visibleDates[widget.visibleDates.length - 1]; @@ -3333,11 +3716,45 @@ class _CalendarViewState extends State<_CalendarView> } Widget _getDayView() { - _allDayHeight = 0; - final bool isCurrentView = _updateCalendarStateDetails.currentViewVisibleDates == widget.visibleDates; + _updateAllDayHeight(isCurrentView); + + return GestureDetector( + child: MouseRegion( + onEnter: _pointerEnterEvent, + onHover: _pointerHoverEvent, + onExit: _pointerExitEvent, + child: Container( + height: widget.height, + width: widget.width, + child: _addDayView( + widget.width, + _timeIntervalHeight * _horizontalLinesCount!, + _isRTL, + widget.locale, + isCurrentView)), + ), + onTapUp: (TapUpDetails details) { + _handleOnTapForDay(details); + }, + onLongPressStart: (LongPressStartDetails details) { + _handleOnLongPressForDay(details); + }, + ); + } + + /// Method to update alldayHeight calculation for day, week and work week + /// view, based on the view also based on the timeintervalheight. + void _updateAllDayHeight(bool isCurrentView) { + if (widget.view != CalendarView.day && + widget.view != CalendarView.week && + widget.view != CalendarView.workWeek) { + return; + } + + _allDayHeight = 0; if (widget.view == CalendarView.day) { final double viewHeaderHeight = CalendarViewHelper.getViewHeaderHeight( widget.calendar.viewHeaderHeight, widget.view); @@ -3362,29 +3779,6 @@ class _CalendarViewState extends State<_CalendarView> : _updateCalendarStateDetails.allDayPanelHeight; _allDayHeight = _allDayHeight * _heightAnimation!.value; } - - return GestureDetector( - child: MouseRegion( - onEnter: _pointerEnterEvent, - onHover: _pointerHoverEvent, - onExit: _pointerExitEvent, - child: Container( - height: widget.height, - width: widget.width, - child: _addDayView( - widget.width, - _timeIntervalHeight * _horizontalLinesCount!, - _isRTL, - widget.locale, - isCurrentView)), - ), - onTapUp: (TapUpDetails details) { - _handleOnTapForDay(details); - }, - onLongPressStart: (LongPressStartDetails details) { - _handleOnLongPressForDay(details); - }, - ); } Widget _getTimelineView() { @@ -3433,11 +3827,15 @@ class _CalendarViewState extends State<_CalendarView> widget.getCalendarState(_updateCalendarStateDetails); final double scrollPosition = _getScrollPositionForCurrentDate( _updateCalendarStateDetails.currentDate!); - if (scrollPosition == -1) { + if (scrollPosition == -1 || + _scrollController!.position.pixels == scrollPosition) { return; } - _scrollController!.jumpTo(scrollPosition); + _scrollController!.jumpTo( + _scrollController!.position.maxScrollExtent > scrollPosition + ? scrollPosition + : _scrollController!.position.maxScrollExtent); }); } @@ -3678,21 +4076,20 @@ class _CalendarViewState extends State<_CalendarView> } void _updateTimelineViews(_CalendarView oldWidget) { - _timelineRulerController = _timelineRulerController ?? + _timelineRulerController ??= ScrollController(initialScrollOffset: 0, keepScrollOffset: true) - ..addListener(_timeRulerListener); + ..addListener(_timeRulerListener); - _timelineViewAnimationController = _timelineViewAnimationController ?? - AnimationController( - duration: const Duration(milliseconds: 300), - vsync: this, - animationBehavior: AnimationBehavior.normal); + _timelineViewAnimationController ??= AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + animationBehavior: AnimationBehavior.normal); - _timelineViewAnimation = _timelineViewAnimation ?? - _timelineViewTween.animate(_timelineViewAnimationController!) - ..addListener(_scrollAnimationListener); + _timelineViewAnimation ??= _timelineViewTween + .animate(_timelineViewAnimationController!) + ..addListener(_scrollAnimationListener); - _timelineViewHeaderScrollController = _timelineViewHeaderScrollController ?? + _timelineViewHeaderScrollController ??= ScrollController(initialScrollOffset: 0, keepScrollOffset: true); _timelineViewVerticalScrollController = ScrollController(initialScrollOffset: 0, keepScrollOffset: true); @@ -3806,9 +4203,8 @@ class _CalendarViewState extends State<_CalendarView> !isCurrentView ? _allDayHeight : _updateCalendarStateDetails.allDayPanelHeight, - widget.localizations, (UpdateCalendarStateDetails details) { - _getPainterProperties(details); - }), + widget.localizations, + _getPainterProperties), ], ), ), @@ -3891,7 +4287,10 @@ class _CalendarViewState extends State<_CalendarView> widget.calendar.minDate, widget.calendar.maxDate, _viewHeaderNotifier, - widget.textScaleFactor), + widget.textScaleFactor, + widget.calendar.showWeekNumber, + widget.isMobilePlatform, + widget.calendar.weekNumberStyle), ), ), ), @@ -3905,7 +4304,7 @@ class _CalendarViewState extends State<_CalendarView> child: _CalendarMultiChildContainer( width: widget.width, height: height, - children: [ + children: [ RepaintBoundary(child: _getMonthWidget(isRTL, height)), RepaintBoundary( child: _addAppointmentPainter(widget.width, height)), @@ -3955,8 +4354,9 @@ class _CalendarViewState extends State<_CalendarView> widget.calendar.monthCellBuilder, widget.width, height, + widget.calendar.weekNumberStyle, + widget.isMobilePlatform, ValueNotifier?>(visibleAppointments)); - return _monthView; } @@ -3964,8 +4364,10 @@ class _CalendarViewState extends State<_CalendarView> Widget _addDayView(double width, double height, bool isRTL, String locale, bool isCurrentView) { double viewHeaderWidth = widget.width; - double viewHeaderHeight = CalendarViewHelper.getViewHeaderHeight( - widget.calendar.viewHeaderHeight, widget.view); + final double actualViewHeaderHeight = + CalendarViewHelper.getViewHeaderHeight( + widget.calendar.viewHeaderHeight, widget.view); + double viewHeaderHeight = actualViewHeaderHeight; final double timeLabelWidth = CalendarViewHelper.getTimeLabelWidth( widget.calendar.timeSlotViewSettings.timeRulerSize, widget.view); if (widget.view == CalendarView.day) { @@ -3990,8 +4392,7 @@ class _CalendarViewState extends State<_CalendarView> left: isRTL ? widget.width - viewHeaderWidth : 0, top: 0, right: isRTL ? 0 : widget.width - viewHeaderWidth, - height: CalendarViewHelper.getViewHeaderHeight( - widget.calendar.viewHeaderHeight, widget.view), + height: actualViewHeaderHeight, child: Container( color: widget.calendar.viewHeaderStyle.backgroundColor ?? widget.calendarTheme.viewHeaderBackgroundColor, @@ -4005,8 +4406,7 @@ class _CalendarViewState extends State<_CalendarView> CalendarViewHelper.getTimeLabelWidth( widget.calendar.timeSlotViewSettings.timeRulerSize, widget.view), - CalendarViewHelper.getViewHeaderHeight( - widget.calendar.viewHeaderHeight, widget.view), + actualViewHeaderHeight, widget.calendar.monthViewSettings, isRTL, widget.locale, @@ -4018,7 +4418,10 @@ class _CalendarViewState extends State<_CalendarView> widget.calendar.minDate, widget.calendar.maxDate, _viewHeaderNotifier, - widget.textScaleFactor), + widget.textScaleFactor, + widget.calendar.showWeekNumber, + widget.isMobilePlatform, + widget.calendar.weekNumberStyle), ), ), ), @@ -4033,6 +4436,7 @@ class _CalendarViewState extends State<_CalendarView> child: Container( child: Scrollbar( controller: _scrollController, + isAlwaysShown: !widget.isMobilePlatform, child: ListView( padding: const EdgeInsets.all(0.0), controller: _scrollController, @@ -4044,7 +4448,7 @@ class _CalendarViewState extends State<_CalendarView> child: _CalendarMultiChildContainer( width: width, height: height, - children: [ + children: [ RepaintBoundary( child: TimeSlotWidget( widget.visibleDates, @@ -4132,10 +4536,11 @@ class _CalendarViewState extends State<_CalendarView> void _updateProgrammaticSelectedResourceIndex() { if (_updateCalendarStateDetails.selectedDate != null && _selectedResourceIndex == -1) { - if ((widget.view == CalendarView.timelineMonth && + final bool isTimelineMonth = widget.view == CalendarView.timelineMonth; + if ((isTimelineMonth && (isSameDate(_updateCalendarStateDetails.selectedDate, widget.calendar.initialSelectedDate))) || - (widget.view != CalendarView.timelineMonth && + (!isTimelineMonth && (CalendarViewHelper.isSameTimeSlot( _updateCalendarStateDetails.selectedDate, widget.calendar.initialSelectedDate)))) { @@ -4153,13 +4558,13 @@ class _CalendarViewState extends State<_CalendarView> final bool isResourceEnabled = CalendarViewHelper.isResourceEnabled( widget.calendar.dataSource, widget.view); double resourceItemHeight = 0; - height -= (viewHeaderHeight + timeLabelSize); + height -= viewHeaderHeight + timeLabelSize; if (isResourceEnabled) { _updateProgrammaticSelectedResourceIndex(); final double resourceViewSize = widget.calendar.resourceViewSettings.size; resourceItemHeight = CalendarViewHelper.getResourceItemHeight( resourceViewSize, - (widget.height - viewHeaderHeight - timeLabelSize), + widget.height - viewHeaderHeight - timeLabelSize, widget.calendar.resourceViewSettings, widget.calendar.dataSource!.resources!.length); height = resourceItemHeight * widget.resourceCollection!.length; @@ -4185,7 +4590,7 @@ class _CalendarViewState extends State<_CalendarView> padding: const EdgeInsets.all(0.0), controller: _timelineRulerController, scrollDirection: Axis.horizontal, - physics: _CustomNeverScrollableScrollPhysics(), + physics: const _CustomNeverScrollableScrollPhysics(), children: [ RepaintBoundary( child: CustomPaint( @@ -4211,17 +4616,19 @@ class _CalendarViewState extends State<_CalendarView> bottom: 0, child: Scrollbar( controller: _scrollController, + isAlwaysShown: !widget.isMobilePlatform, child: ListView( padding: const EdgeInsets.all(0.0), controller: _scrollController, scrollDirection: Axis.horizontal, - physics: _CustomNeverScrollableScrollPhysics(), + physics: const _CustomNeverScrollableScrollPhysics(), children: [ Container( width: width, - child: Stack(children: [ + child: Stack(children: [ Scrollbar( controller: _timelineViewVerticalScrollController, + isAlwaysShown: !widget.isMobilePlatform, child: ListView( padding: const EdgeInsets.all(0.0), scrollDirection: Axis.vertical, @@ -4230,13 +4637,13 @@ class _CalendarViewState extends State<_CalendarView> physics: isResourceEnabled ? const ClampingScrollPhysics() : const NeverScrollableScrollPhysics(), - children: [ + children: [ Stack(children: [ RepaintBoundary( child: _CalendarMultiChildContainer( width: width, height: height, - children: [ + children: [ RepaintBoundary( child: TimelineWidget( _horizontalLinesCount!, @@ -4306,6 +4713,15 @@ class _CalendarViewState extends State<_CalendarView> final double viewHeaderHeight = CalendarViewHelper.getViewHeaderHeight( widget.calendar.viewHeaderHeight, widget.view); + final double weekNumberPanelWidth = + CalendarViewHelper.getWeekNumberPanelWidth( + widget.calendar.showWeekNumber, + widget.width, + widget.isMobilePlatform); + if ((!_isRTL && xDetails < weekNumberPanelWidth) || + (_isRTL && xDetails > widget.width - weekNumberPanelWidth)) { + return; + } if (yDetails < viewHeaderHeight) { if (isTapCallback) { _handleOnTapForViewHeader(tapDetails!, widget.width); @@ -4385,8 +4801,8 @@ class _CalendarViewState extends State<_CalendarView> appointmentView == null || isMoreTapped ? _getSelectedAppointments(selectedDate) : [ - appointmentView.appointment!.data ?? - appointmentView.appointment! + CalendarViewHelper.getAppointmentDetail( + appointmentView.appointment!) ]; final CalendarElement selectedElement = appointmentView == null ? CalendarElement.calendarCell @@ -4412,13 +4828,13 @@ class _CalendarViewState extends State<_CalendarView> bool canRaiseSelectionChanged, DateTime? previousSelectedDate, [CalendarResource? selectedResource, int? previousSelectedResourceIndex]) { + final bool isMonthView = widget.view == CalendarView.month || + widget.view == CalendarView.timelineMonth; if (canRaiseSelectionChanged && - (((widget.view == CalendarView.month || - widget.view == CalendarView.timelineMonth) && + ((isMonthView && !isSameDate( previousSelectedDate, _selectionPainter!.selectedDate)) || - ((widget.view != CalendarView.month && - widget.view != CalendarView.timelineMonth) && + (!isMonthView && !CalendarViewHelper.isSameTimeSlot( previousSelectedDate, _selectionPainter!.selectedDate)) || (CalendarViewHelper.isResourceEnabled( @@ -4613,8 +5029,8 @@ class _CalendarViewState extends State<_CalendarView> widget.calendar, selectedDate, [ - appointmentView.appointment!.data ?? - appointmentView.appointment! + CalendarViewHelper.getAppointmentDetail( + appointmentView.appointment!) ], CalendarElement.appointment, selectedResource); @@ -4623,8 +5039,8 @@ class _CalendarViewState extends State<_CalendarView> widget.calendar, selectedDate, [ - appointmentView.appointment!.data ?? - appointmentView.appointment! + CalendarViewHelper.getAppointmentDetail( + appointmentView.appointment!) ], CalendarElement.appointment, selectedResource); @@ -4684,7 +5100,7 @@ class _CalendarViewState extends State<_CalendarView> } widget.getCalendarState(_updateCalendarStateDetails); - dynamic? selectedAppointment; + dynamic selectedAppointment; List? selectedAppointments; CalendarElement targetElement = CalendarElement.viewHeader; DateTime? selectedDate = _updateCalendarStateDetails.selectedDate; @@ -4774,7 +5190,7 @@ class _CalendarViewState extends State<_CalendarView> yPosition > allDayHeight - kAllDayAppointmentHeight; /// Check the tap position inside the last appointment rendering position - /// when the panel as collapsed and it does not psoition does not have + /// when the panel as collapsed and it does not position does not have /// appointment. /// Eg., If July 8 have 3 all day appointments spanned to July 9 and /// July 9 have 1 all day appointment spanned to July 10 then July 10 @@ -4954,7 +5370,7 @@ class _CalendarViewState extends State<_CalendarView> canRaiseSelectionChanged, previousSelectedDate); } else if (selectedAppointment != null) { selectedAppointments = [ - selectedAppointment.data ?? selectedAppointment + CalendarViewHelper.getAppointmentDetail(selectedAppointment) ]; /// In LTR, remove the time ruler width value from the @@ -5067,12 +5483,12 @@ class _CalendarViewState extends State<_CalendarView> return isValidRegion; } - bool _isAutoTimeIntervalHeight(SfCalendar calendar, CalendarView view) { - if (CalendarViewHelper.isTimelineView(view)) { + bool _isAutoTimeIntervalHeight(SfCalendar calendar, bool isTimelineView) { + if (isTimelineView) { return calendar.timeSlotViewSettings.timeIntervalWidth == -1; - } else { - return calendar.timeSlotViewSettings.timeIntervalHeight == -1; } + + return calendar.timeSlotViewSettings.timeIntervalHeight == -1; } /// Returns the default time interval width for timeline views. @@ -5106,7 +5522,7 @@ class _CalendarViewState extends State<_CalendarView> view, width, isMobilePlatform) : calendar.timeSlotViewSettings.timeIntervalHeight; - if (!_isAutoTimeIntervalHeight(calendar, view)) { + if (!_isAutoTimeIntervalHeight(calendar, isTimelineView)) { return timeIntervalHeight; } @@ -5237,10 +5653,11 @@ class _CalendarViewState extends State<_CalendarView> int index = 0; final double timeLabelViewWidth = CalendarViewHelper.getTimeLabelWidth( widget.calendar.timeSlotViewSettings.timeRulerSize, widget.view); + final int visibleDatesLength = widget.visibleDates.length; if (!CalendarViewHelper.isTimelineView(widget.view)) { double cellWidth = 0; if (widget.view != CalendarView.month) { - cellWidth = (width - timeLabelViewWidth) / widget.visibleDates.length; + cellWidth = (width - timeLabelViewWidth) / visibleDatesLength; /// Set index value as 0 when calendar view as day because day view hold /// single visible date. @@ -5259,12 +5676,12 @@ class _CalendarViewState extends State<_CalendarView> /// Calculate the RTL based value of index when the widget direction as /// RTL. if (_isRTL && widget.view != CalendarView.month) { - index = widget.visibleDates.length - index - 1; + index = visibleDatesLength - index - 1; } else if (_isRTL && widget.view == CalendarView.month) { index = DateTime.daysPerWeek - index - 1; } - if (index < 0 || index >= widget.visibleDates.length) { + if (index < 0 || index >= visibleDatesLength) { return null; } @@ -5278,7 +5695,7 @@ class _CalendarViewState extends State<_CalendarView> _getSingleViewWidthForTimeLineView(this)) .truncate(); - if (index < 0 || index >= widget.visibleDates.length) { + if (index < 0 || index >= visibleDatesLength) { return null; } @@ -5452,7 +5869,7 @@ class _CalendarViewState extends State<_CalendarView> } // ignore: avoid_as - final RenderBox box = context.findRenderObject() as RenderBox; + final RenderBox box = context.findRenderObject()! as RenderBox; final Offset localPosition = box.globalToLocal(globalPosition); double viewHeaderHeight = CalendarViewHelper.getViewHeaderHeight( widget.calendar.viewHeaderHeight, widget.view); @@ -5541,6 +5958,24 @@ class _CalendarViewState extends State<_CalendarView> } else { xPosition = localPosition.dx; + /// Remove the hovering when the position not in week number panel. + if (widget.calendar.showWeekNumber && widget.view == CalendarView.month) { + final double weekNumberPanelWidth = + CalendarViewHelper.getWeekNumberPanelWidth( + widget.calendar.showWeekNumber, + widget.width, + widget.isMobilePlatform); + if ((!_isRTL && xPosition < weekNumberPanelWidth) || + (_isRTL && xPosition > widget.width - weekNumberPanelWidth)) { + _hoveringDate = null; + _calendarCellNotifier.value = null; + _viewHeaderNotifier.value = null; + _appointmentHoverNotifier.value = null; + _allDayNotifier.value = null; + return; + } + } + /// Update the x position value with scroller offset and the value /// assigned to mouse hover position. /// mouse hover position value used for highlight the position @@ -5910,11 +6345,26 @@ class _CalendarViewState extends State<_CalendarView> return null; case CalendarView.month: { - if (x > width || x < 0) { + /// Remove the selection when the position is to week number panel. + final double weekNumberPanelWidth = + CalendarViewHelper.getWeekNumberPanelWidth( + widget.calendar.showWeekNumber, + widget.width, + widget.isMobilePlatform); + if (x > widget.width || + (!_isRTL && x < weekNumberPanelWidth) || + (_isRTL && x > widget.width - weekNumberPanelWidth)) { return null; } - cellWidth = width / DateTime.daysPerWeek; + /// In RTL the week number panel will render on the right side hence, + /// we didn't consider the week number panel width in rtl. + if (!_isRTL) { + x -= weekNumberPanelWidth; + } + + cellWidth = + (widget.width - weekNumberPanelWidth) / DateTime.daysPerWeek; cellHeight = (widget.height - CalendarViewHelper.getViewHeaderHeight( widget.calendar.viewHeaderHeight, widget.view)) / @@ -5992,8 +6442,7 @@ class _CalendarViewState extends State<_CalendarView> return; } - if ((widget.view == CalendarView.month || - widget.view == CalendarView.timelineMonth) && + if (isMonthView && CalendarViewHelper.isDateInDateCollection( widget.blackoutDates, selectedDate)) { return; @@ -6029,19 +6478,23 @@ class _CalendarViewState extends State<_CalendarView> } _selectionPainter = _SelectionPainter( - widget.calendar, - widget.view, - widget.visibleDates, - _updateCalendarStateDetails.selectedDate, - widget.calendar.selectionDecoration, - _timeIntervalHeight, - widget.calendarTheme, - _selectionNotifier, - _isRTL, - _selectedResourceIndex, - resourceItemHeight, (UpdateCalendarStateDetails details) { - _getPainterProperties(details); - }); + widget.calendar, + widget.view, + widget.visibleDates, + _updateCalendarStateDetails.selectedDate, + widget.calendar.selectionDecoration, + _timeIntervalHeight, + widget.calendarTheme, + _selectionNotifier, + _isRTL, + _selectedResourceIndex, + resourceItemHeight, + widget.calendar.showWeekNumber, + widget.isMobilePlatform, + (UpdateCalendarStateDetails details) { + _getPainterProperties(details); + }, + ); if (appointmentView != null && _updateCalendarStateDetails.visibleAppointments @@ -6106,7 +6559,10 @@ class _ViewHeaderViewPainter extends CustomPainter { this.minDate, this.maxDate, this.viewHeaderNotifier, - this.textScaleFactor) + this.textScaleFactor, + this.showWeekNumber, + this.isMobilePlatform, + this.weekNumberStyle) : super(repaint: viewHeaderNotifier); final CalendarView view; @@ -6126,13 +6582,22 @@ class _ViewHeaderViewPainter extends CustomPainter { final DateTime maxDate; final ValueNotifier viewHeaderNotifier; final double textScaleFactor; - Paint _circlePainter = Paint(); - TextPainter _dayTextPainter = TextPainter(), _dateTextPainter = TextPainter(); + final Paint _circlePainter = Paint(); + final TextPainter _dayTextPainter = TextPainter(), + _dateTextPainter = TextPainter(); + final bool showWeekNumber; + final bool isMobilePlatform; + final WeekNumberStyle weekNumberStyle; @override void paint(Canvas canvas, Size size) { canvas.clipRect(Rect.fromLTWH(0, 0, size.width, size.height)); - double width = size.width; + final double weekNumberPanelWidth = + CalendarViewHelper.getWeekNumberPanelWidth( + showWeekNumber, size.width, isMobilePlatform); + double width = view == CalendarView.month + ? size.width - weekNumberPanelWidth + : size.width; width = _getViewHeaderWidth(width); /// Initializes the default text style for the texts in view header of @@ -6148,19 +6613,31 @@ class _ViewHeaderViewPainter extends CustomPainter { canvas, size, viewHeaderDayStyle, viewHeaderDateStyle, width, today); } else { _addViewHeaderForMonthView( - canvas, size, viewHeaderDayStyle, width, today); + canvas, size, viewHeaderDayStyle, width, today, weekNumberPanelWidth); } } - void _addViewHeaderForMonthView(Canvas canvas, Size size, - TextStyle viewHeaderDayStyle, double width, DateTime today) { + void _addViewHeaderForMonthView( + Canvas canvas, + Size size, + TextStyle viewHeaderDayStyle, + double width, + DateTime today, + double weekNumberPanelWidth) { TextStyle dayTextStyle = viewHeaderDayStyle; - double xPosition = 0; + double xPosition = isRTL + ? size.width - width - weekNumberPanelWidth + : weekNumberPanelWidth; double yPosition = 0; - if (isRTL) { - xPosition = size.width - width; + final int visibleDatesLength = visibleDates.length; + bool hasToday = monthViewSettings.numberOfWeeksInView > 0 && + monthViewSettings.numberOfWeeksInView < 6 || + visibleDates[visibleDatesLength ~/ 2].month == today.month; + if (hasToday) { + hasToday = isDateWithInDateRange( + visibleDates[0], visibleDates[visibleDatesLength - 1], today); } - bool hasToday = false; + for (int i = 0; i < DateTime.daysPerWeek; i++) { final DateTime currentDate = visibleDates[i]; String dayText = DateFormat(monthViewSettings.dayFormat, locale) @@ -6170,22 +6647,15 @@ class _ViewHeaderViewPainter extends CustomPainter { dayText = _updateViewHeaderFormat(monthViewSettings.dayFormat, dayText); - hasToday = monthViewSettings.numberOfWeeksInView > 0 && - monthViewSettings.numberOfWeeksInView < 6 - ? true - : visibleDates[visibleDates.length ~/ 2].month == today.month - ? true - : false; - - if (hasToday && - isDateWithInDateRange( - visibleDates[0], visibleDates[visibleDates.length - 1], today) && - currentDate.weekday == today.weekday) { + if (hasToday && currentDate.weekday == today.weekday) { + final Color? todayTextColor = + CalendarViewHelper.getTodayHighlightTextColor( + todayHighlightColor, todayTextStyle, calendarTheme); + dayTextStyle = todayTextStyle != null ? todayTextStyle!.copyWith( - fontSize: viewHeaderDayStyle.fontSize, - color: todayHighlightColor) - : viewHeaderDayStyle.copyWith(color: todayHighlightColor); + fontSize: viewHeaderDayStyle.fontSize, color: todayTextColor) + : viewHeaderDayStyle.copyWith(color: todayTextColor); } else { dayTextStyle = viewHeaderDayStyle; } @@ -6230,14 +6700,17 @@ class _ViewHeaderViewPainter extends CustomPainter { width = labelWidth; } + final Paint _linePainter = Paint(); xPosition = view == CalendarView.day ? 0 : timeLabelWidth; yPosition = 2; - final double cellWidth = width / visibleDates.length; + final int visibleDatesLength = visibleDates.length; + final double cellWidth = width / visibleDatesLength; if (isRTL && view != CalendarView.day) { xPosition = size.width - timeLabelWidth - cellWidth; } - for (int i = 0; i < visibleDates.length; i++) { + for (int i = 0; i < visibleDatesLength; i++) { final DateTime currentDate = visibleDates[i]; + String dayText = DateFormat(timeSlotViewSettings.dayFormat, locale) .format(currentDate) .toString() @@ -6251,15 +6724,19 @@ class _ViewHeaderViewPainter extends CustomPainter { .toString(); final bool isToday = isSameDate(currentDate, today); if (isToday) { + final Color? todayTextStyleColor = todayTextStyle != null + ? todayTextStyle!.color + : calendarTheme.todayTextStyle.color; + final Color? todayTextColor = + CalendarViewHelper.getTodayHighlightTextColor( + todayHighlightColor, todayTextStyle, calendarTheme); dayTextStyle = todayTextStyle != null ? todayTextStyle!.copyWith( - fontSize: viewHeaderDayStyle.fontSize, - color: todayHighlightColor) - : viewHeaderDayStyle.copyWith(color: todayHighlightColor); + fontSize: viewHeaderDayStyle.fontSize, color: todayTextColor) + : viewHeaderDayStyle.copyWith(color: todayTextColor); dateTextStyle = todayTextStyle != null ? todayTextStyle!.copyWith(fontSize: viewHeaderDateStyle.fontSize) - : viewHeaderDateStyle.copyWith( - color: calendarTheme.todayTextStyle.color); + : viewHeaderDateStyle.copyWith(color: todayTextStyleColor); } else { dayTextStyle = viewHeaderDayStyle; dateTextStyle = viewHeaderDateStyle; @@ -6328,6 +6805,54 @@ class _ViewHeaderViewPainter extends CustomPainter { topPadding + _dayTextPainter.height + inBetweenPadding)); + if (showWeekNumber && + ((currentDate.weekday == DateTime.monday) || + (view == CalendarView.workWeek && + timeSlotViewSettings.nonWorkingDays + .contains(DateTime.monday) && + i == visibleDatesLength ~/ 2))) { + final String weekNumber = + DateTimeHelper.getWeekNumberOfYear(currentDate).toString(); + final TextStyle weekNumberTextStyle = + weekNumberStyle.textStyle ?? calendarTheme.weekNumberTextStyle; + final TextSpan dayTextSpan = TextSpan( + text: weekNumber, + style: weekNumberTextStyle, + ); + _dateTextPainter.text = dayTextSpan; + _dateTextPainter.textDirection = TextDirection.ltr; + _dateTextPainter.textAlign = TextAlign.left; + _dateTextPainter.textWidthBasis = TextWidthBasis.longestLine; + _dateTextPainter.textScaleFactor = textScaleFactor; + _dateTextPainter.layout(minWidth: 0, maxWidth: timeLabelWidth); + final double weekNumberPosition = isRTL + ? (size.width - timeLabelWidth) + + ((timeLabelWidth - _dateTextPainter.width) / 2) + : (timeLabelWidth - _dateTextPainter.width) / 2; + final double weekNumberYPosition = size.height / 2 - + (_dayTextPainter.height + + topPadding + + _dateTextPainter.height + + inBetweenPadding) / + 2 + + topPadding + + _dayTextPainter.height + + inBetweenPadding; + const double padding = 10; + final Rect rect = Rect.fromLTRB( + weekNumberPosition - padding, + weekNumberYPosition - (padding / 2), + weekNumberPosition + _dateTextPainter.width + padding, + weekNumberYPosition + _dateTextPainter.height + (padding / 2)); + _linePainter.style = PaintingStyle.fill; + _linePainter.color = weekNumberStyle.backgroundColor ?? + calendarTheme.weekNumberBackgroundColor!; + final RRect roundedRect = + RRect.fromRectAndRadius(rect, const Radius.circular(padding / 2)); + canvas.drawRRect(roundedRect, _linePainter); + _dateTextPainter.paint( + canvas, Offset(weekNumberPosition, weekNumberYPosition)); + } if (isRTL) { xPosition -= cellWidth; @@ -6457,7 +6982,9 @@ class _ViewHeaderViewPainter extends CustomPainter { oldWidget.isRTL != isRTL || oldWidget.locale != locale || oldWidget.todayTextStyle != todayTextStyle || - oldWidget.textScaleFactor != textScaleFactor; + oldWidget.textScaleFactor != textScaleFactor || + oldWidget.weekNumberStyle != weekNumberStyle || + oldWidget.showWeekNumber != showWeekNumber; } //// draw today highlight circle in view header. @@ -6545,6 +7072,23 @@ class _ViewHeaderViewPainter extends CustomPainter { left = view == CalendarView.day ? 0 : timeLabelWidth; } for (int i = 0; i < visibleDates.length; i++) { + final DateTime visibleDate = visibleDates[i]; + if (showWeekNumber && + ((visibleDate.weekday == DateTime.monday && + view != CalendarView.day) || + (view == CalendarView.workWeek && + timeSlotViewSettings.nonWorkingDays + .contains(DateTime.monday) && + i == visibleDates.length ~/ 2))) { + final int weekNumber = DateTimeHelper.getWeekNumberOfYear(visibleDate); + semanticsBuilder.add(CustomPainterSemantics( + rect: Rect.fromLTWH(isRTL ? (size.width - timeLabelWidth) : 0, 0, + isRTL ? size.width : timeLabelWidth, viewHeaderHeight), + properties: SemanticsProperties( + label: 'week' + weekNumber.toString(), + textDirection: TextDirection.ltr, + ))); + } semanticsBuilder.add(CustomPainterSemantics( rect: Rect.fromLTWH(left, top, cellWidth, size.height), properties: SemanticsProperties( @@ -6593,6 +7137,8 @@ class _SelectionPainter extends CustomPainter { this.isRTL, this.selectedResourceIndex, this.resourceItemHeight, + this.showWeekNumber, + this.isMobilePlatform, this.getCalendarState) : super(repaint: repaintNotifier); @@ -6608,12 +7154,14 @@ class _SelectionPainter extends CustomPainter { int selectedResourceIndex; final double? resourceItemHeight; - BoxPainter? _boxPainter; + late BoxPainter _boxPainter; AppointmentView? appointmentView; double _cellWidth = 0, _cellHeight = 0, _xPosition = 0, _yPosition = 0; final ValueNotifier repaintNotifier; final UpdateCalendarStateDetails _updateCalendarStateDetails = UpdateCalendarStateDetails(); + final bool showWeekNumber; + final bool isMobilePlatform; @override void paint(Canvas canvas, Size size) { @@ -6627,7 +7175,7 @@ class _SelectionPainter extends CustomPainter { getCalendarState(_updateCalendarStateDetails); selectedDate = _updateCalendarStateDetails.selectedDate; final bool isMonthView = - (view == CalendarView.month || view == CalendarView.timelineMonth); + view == CalendarView.month || view == CalendarView.timelineMonth; final int timeInterval = CalendarViewHelper.getTimeInterval(calendar.timeSlotViewSettings); if (selectedDate != null && @@ -6732,13 +7280,19 @@ class _SelectionPainter extends CustomPainter { } } + @override + bool? hitTest(Offset position) { + return false; + } + void _drawMonthSelection(Canvas canvas, Size size, double width) { + final int visibleDatesLength = visibleDates.length; if (!isDateWithInDateRange( - visibleDates[0], visibleDates[visibleDates.length - 1], selectedDate)) { + visibleDates[0], visibleDates[visibleDatesLength - 1], selectedDate)) { return; } - final int currentMonth = visibleDates[visibleDates.length ~/ 2].month; + final int currentMonth = visibleDates[visibleDatesLength ~/ 2].month; /// Check the selected cell date as trailing or leading date when /// [SfCalendar] month not shown leading and trailing dates. @@ -6755,15 +7309,19 @@ class _SelectionPainter extends CustomPainter { return; } - for (int i = 0; i < visibleDates.length; i++) { + for (int i = 0; i < visibleDatesLength; i++) { if (isSameDate(visibleDates[i], selectedDate)) { + final double weekNumberPanelWidth = + CalendarViewHelper.getWeekNumberPanelWidth( + showWeekNumber, width, isMobilePlatform); + _cellWidth = (size.width - weekNumberPanelWidth) / DateTime.daysPerWeek; final int columnIndex = (i / DateTime.daysPerWeek).truncate(); _yPosition = columnIndex * _cellHeight; final int rowIndex = i % DateTime.daysPerWeek; if (isRTL) { _xPosition = (DateTime.daysPerWeek - 1 - rowIndex) * _cellWidth; } else { - _xPosition = rowIndex * _cellWidth; + _xPosition = rowIndex * _cellWidth + weekNumberPanelWidth; } _drawSlotSelection(width, size.height, canvas); break; @@ -6819,19 +7377,19 @@ class _SelectionPainter extends CustomPainter { void _drawWeekSelection( Canvas canvas, Size size, double timeLabelWidth, double width) { + final int visibleDatesLength = visibleDates.length; if (isDateWithInDateRange( - visibleDates[0], visibleDates[visibleDates.length - 1], selectedDate)) { - for (int i = 0; i < visibleDates.length; i++) { + visibleDates[0], visibleDates[visibleDatesLength - 1], selectedDate)) { + for (int i = 0; i < visibleDatesLength; i++) { if (isSameDate(selectedDate, visibleDates[i])) { final int rowIndex = i; if (isRTL) { - _xPosition = _cellWidth * (visibleDates.length - 1 - rowIndex); + _xPosition = _cellWidth * (visibleDatesLength - 1 - rowIndex); } else { _xPosition = timeLabelWidth + _cellWidth * rowIndex; } selectedDate = _updateSelectedDate(); - _yPosition = AppointmentHelper.timeToPosition( calendar, selectedDate!, timeIntervalHeight); _drawSlotSelection(width + timeLabelWidth, size.height, canvas); @@ -6921,7 +7479,7 @@ class _SelectionPainter extends CustomPainter { rect = Rect.fromLTRB(rect.left, rect.top, rect.right, rect.bottom); _boxPainter = selectionDecoration! .createBoxPainter(_updateSelectionDecorationPainter); - _boxPainter!.paint(canvas, Offset(rect.left, rect.top), + _boxPainter.paint(canvas, Offset(rect.left, rect.top), ImageConfiguration(size: rect.size)); } @@ -6934,8 +7492,7 @@ class _SelectionPainter extends CustomPainter { void _drawSlotSelection(double width, double height, Canvas canvas) { //// padding used to avoid first, last row and column selection clipping. const double padding = 0.5; - Rect rect; - rect = Rect.fromLTRB( + final Rect rect = Rect.fromLTRB( _xPosition == 0 ? _xPosition + padding : _xPosition, _yPosition == 0 ? _yPosition + padding : _yPosition, _xPosition + _cellWidth == width @@ -6947,7 +7504,7 @@ class _SelectionPainter extends CustomPainter { _boxPainter = selectionDecoration! .createBoxPainter(_updateSelectionDecorationPainter); - _boxPainter!.paint(canvas, Offset(rect.left, rect.top), + _boxPainter.paint(canvas, Offset(rect.left, rect.top), ImageConfiguration(size: rect.size, textDirection: TextDirection.ltr)); } @@ -6986,8 +7543,8 @@ class _TimeRulerView extends CustomPainter { final bool isTimelineView; final List visibleDates; final double textScaleFactor; - Paint _linePainter = Paint(); - TextPainter _textPainter = TextPainter(); + final Paint _linePainter = Paint(); + final TextPainter _textPainter = TextPainter(); @override void paint(Canvas canvas, Size size) { @@ -7018,7 +7575,7 @@ class _TimeRulerView extends CustomPainter { timeSlotViewSettings.startHour.toInt()) * 60; if (isTimelineView) { - canvas.drawLine(Offset(0, 0), Offset(size.width, 0), _linePainter); + canvas.drawLine(const Offset(0, 0), Offset(size.width, 0), _linePainter); final double timelineViewWidth = timeIntervalHeight * horizontalLinesCount; for (int i = 0; i < visibleDates.length; i++) { @@ -7040,7 +7597,9 @@ class _TimeRulerView extends CustomPainter { /// calendar. void _drawTimeLabels(Canvas canvas, Size size, DateTime date, double hour, double xPosition, double yPosition, TextStyle timeTextStyle) { - final int padding = 5; + const int padding = 5; + final int timeInterval = + CalendarViewHelper.getTimeInterval(timeSlotViewSettings); /// For timeline view we will draw 24 lines where as in day, week and work /// week view we will draw 23 lines excluding the 12 AM, hence to rectify @@ -7057,8 +7616,7 @@ class _TimeRulerView extends CustomPainter { Offset(xPosition, 0), Offset(xPosition, size.height), _linePainter); } - final double minute = - (i * CalendarViewHelper.getTimeInterval(timeSlotViewSettings)) + hour; + final double minute = (i * timeInterval) + hour; date = DateTime(date.day, date.month, date.year, timeSlotViewSettings.startHour.toInt(), minute.toInt()); final String time = DateFormat(timeSlotViewSettings.timeFormat, locale) @@ -7258,7 +7816,7 @@ class _MultiChildContainerRenderObject extends RenderStack { final Size widgetSize = constraints.biggest; size = Size(widgetSize.width.isInfinite ? width : widgetSize.width, widgetSize.height.isInfinite ? height : widgetSize.height); - for (var child = firstChild; child != null; child = childAfter(child)) { + for (dynamic child = firstChild; child != null; child = childAfter(child)) { child.layout(constraints); } } @@ -7335,7 +7893,7 @@ class _MultiChildContainerRenderObject extends RenderStack { semantics.addAll(painter!.semanticsBuilder!(size)); } // ignore: avoid_as - for (RenderRepaintBoundary? child = firstChild as RenderRepaintBoundary; + for (RenderRepaintBoundary? child = firstChild! as RenderRepaintBoundary; child != null; // ignore: avoid_as child = childAfter(child) as RenderRepaintBoundary?) { @@ -7345,7 +7903,7 @@ class _MultiChildContainerRenderObject extends RenderStack { final CustomCalendarRenderObject appointmentRenderObject = // ignore: avoid_as - child.child as CustomCalendarRenderObject; + child.child! as CustomCalendarRenderObject; semantics.addAll(appointmentRenderObject.semanticsBuilder!(size)); } @@ -7366,8 +7924,8 @@ class _CustomNeverScrollableScrollPhysics extends NeverScrollableScrollPhysics { /// physics, because flutter framework set different parent physics /// based on platform(iOS, Android, etc.,) return _CustomNeverScrollableScrollPhysics( - parent: buildParent( - ClampingScrollPhysics(parent: RangeMaintainingScrollPhysics()))); + parent: buildParent(const ClampingScrollPhysics( + parent: RangeMaintainingScrollPhysics()))); } } @@ -7453,6 +8011,11 @@ class _CurrentTimeIndicator extends CustomPainter { } } + @override + bool? hitTest(Offset position) { + return false; + } + @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return true; diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/views/day_view.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/views/day_view.dart index ad7fc70a9..f3ce39dda 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/views/day_view.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/views/day_view.dart @@ -14,7 +14,7 @@ import '../settings/time_slot_view_settings.dart'; class TimeSlotWidget extends StatefulWidget { /// Constructor to create the time slot widget to holds time slots view for /// day, week, workweek views. - TimeSlotWidget( + const TimeSlotWidget( this.visibleDates, this.horizontalLinesCount, this.timeIntervalHeight, @@ -85,7 +85,7 @@ class TimeSlotWidget extends StatefulWidget { } class _TimeSlotWidgetState extends State { - List _children = []; + final List _children = []; List _specialRegionViews = []; @override @@ -162,10 +162,11 @@ class _TimeSlotWidgetState extends State { CalendarViewHelper.getTimeInterval(widget.timeSlotViewSettings); final DateTime startDate = AppointmentHelper.convertToStartTime(widget.visibleDates[0]); + final int visibleDatesLength = widget.visibleDates.length; final DateTime endDate = AppointmentHelper.convertToEndTime( - widget.visibleDates[widget.visibleDates.length - 1]); + widget.visibleDates[visibleDatesLength - 1]); final double width = widget.width - widget.timeLabelWidth; - final double cellWidth = width / widget.visibleDates.length; + final double cellWidth = width / visibleDatesLength; for (int i = 0; i < widget.specialRegion!.length; i++) { final CalendarTimeRegion region = widget.specialRegion![i]; final DateTime regionStartTime = region.actualStartTime; @@ -203,7 +204,7 @@ class _TimeSlotWidgetState extends State { startIndex = 0; } else { /// Find the next index when the start date as non working date. - for (int k = 1; k < widget.visibleDates.length; k++) { + for (int k = 1; k < visibleDatesLength; k++) { final DateTime currentDate = widget.visibleDates[k]; if (currentDate.isBefore(regionStartTime)) { continue; @@ -230,7 +231,7 @@ class _TimeSlotWidgetState extends State { if (endIndex == -1) { /// Find the previous index when the end date as non working date. if (endDate.isAfter(regionEndTime)) { - for (int k = widget.visibleDates.length - 2; k >= 0; k--) { + for (int k = visibleDatesLength - 2; k >= 0; k--) { final DateTime currentDate = widget.visibleDates[k]; if (currentDate.isAfter(regionEndTime)) { continue; @@ -241,12 +242,12 @@ class _TimeSlotWidgetState extends State { } if (endIndex == -1) { - endIndex = widget.visibleDates.length - 1; + endIndex = visibleDatesLength - 1; } } else { /// Set index as visible date end date index when the /// region end date before the visible end date - endIndex = widget.visibleDates.length - 1; + endIndex = visibleDatesLength - 1; } /// End date as non working day and its index as previous date index. @@ -601,7 +602,7 @@ class _TimeSlotRenderObject extends CustomCalendarRenderObject { List specialRegionBounds; late double _cellWidth; - Paint _linePainter = Paint(); + final Paint _linePainter = Paint(); @override bool get isRepaintBoundary => true; @@ -651,8 +652,9 @@ class _TimeSlotRenderObject extends CustomCalendarRenderObject { RenderBox? child = firstChild; final bool isNeedDefaultPaint = childCount == 0; final double width = size.width - timeLabelWidth; - _cellWidth = width / visibleDates.length; - _minMaxExceeds(minDate, maxDate, context.canvas); + final int visibleDatesCount = visibleDates.length; + _cellWidth = width / visibleDatesCount; + _minMaxExceeds(minDate, maxDate, context.canvas, visibleDatesCount); if (isNeedDefaultPaint) { _addSpecialRegions(context.canvas); } else { @@ -667,32 +669,33 @@ class _TimeSlotRenderObject extends CustomCalendarRenderObject { continue; } final Rect rect = view.bound; - child.paint(context, Offset(rect.left, rect.top)); + context.paintChild(child, Offset(rect.left, rect.top)); child = childAfter(child); } } - _drawTimeSlots(context.canvas); + _drawTimeSlots(context.canvas, visibleDatesCount); } - void _minMaxExceeds(DateTime minDate, DateTime maxDate, Canvas canvas) { + void _minMaxExceeds(DateTime minDate, DateTime maxDate, Canvas canvas, + int visibleDatesCount) { final DateTime visibleStartDate = visibleDates[0]; - final DateTime visibleEndDate = visibleDates[visibleDates.length - 1]; - final DateTime maxEndDate = AppointmentHelper.convertToEndTime( - visibleDates[visibleDates.length - 1]); + final DateTime visibleEndDate = visibleDates[visibleDatesCount - 1]; + final DateTime maxEndDate = + AppointmentHelper.convertToEndTime(visibleDates[visibleDatesCount - 1]); if (isDateWithInDateRange(visibleStartDate, visibleEndDate, minDate)) { - _drawDisabledDate(visibleStartDate, minDate, canvas); + _drawDisabledDate(visibleStartDate, minDate, canvas, visibleDatesCount); } if (isDateWithInDateRange(visibleStartDate, visibleEndDate, maxDate)) { - _drawDisabledDate(maxDate, maxEndDate, canvas); + _drawDisabledDate(maxDate, maxEndDate, canvas, visibleDatesCount); } } - void _drawDisabledDate( - DateTime disabledStartDate, DateTime disabledEndDate, canvas) { + void _drawDisabledDate(DateTime disabledStartDate, DateTime disabledEndDate, + Canvas canvas, int visibleDatesCount) { final double minuteHeight = timeIntervalHeight / CalendarViewHelper.getTimeInterval(timeSlotViewSettings); final double viewWidth = width - timeLabelWidth; - final double cellWidth = viewWidth / visibleDates.length; + final double cellWidth = viewWidth / visibleDatesCount; final int startIndex = DateTimeHelper.getVisibleDateIndex(visibleDates, disabledStartDate); @@ -732,19 +735,19 @@ class _TimeSlotRenderObject extends CustomCalendarRenderObject { } } - void _drawTimeSlots(Canvas canvas) { - double x, y; - y = timeIntervalHeight; + void _drawTimeSlots(Canvas canvas, int visibleDatesCount) { + double y = timeIntervalHeight; _linePainter.style = PaintingStyle.stroke; _linePainter.strokeWidth = 0.5; _linePainter.strokeCap = StrokeCap.round; _linePainter.color = cellBorderColor ?? calendarTheme.cellBorderColor; + final double startXPosition = isRTL ? 0 : timeLabelWidth; + final double endXPosition = + isRTL ? size.width - timeLabelWidth : size.width; for (int i = 1; i <= horizontalLinesCount; i++) { - final Offset start = Offset(isRTL ? 0 : timeLabelWidth, y); - final Offset end = - Offset(isRTL ? size.width - timeLabelWidth : size.width, y); - canvas.drawLine(start, end, _linePainter); + canvas.drawLine( + Offset(startXPosition, y), Offset(endXPosition, y), _linePainter); y += timeIntervalHeight; if (y == size.height) { @@ -752,16 +755,9 @@ class _TimeSlotRenderObject extends CustomCalendarRenderObject { } } - if (isRTL) { - x = _cellWidth; - } else { - x = timeLabelWidth + _cellWidth; - } - - for (int i = 0; i < visibleDates.length - 1; i++) { - final Offset start = Offset(x, 0); - final Offset end = Offset(x, size.height); - canvas.drawLine(start, end, _linePainter); + double x = isRTL ? _cellWidth : timeLabelWidth + _cellWidth; + for (int i = 0; i < visibleDatesCount - 1; i++) { + canvas.drawLine(Offset(x, 0), Offset(x, size.height), _linePainter); x += _cellWidth; } @@ -771,15 +767,15 @@ class _TimeSlotRenderObject extends CustomCalendarRenderObject { } void _addMouseHoveringForTimeSlot(Canvas canvas, Size size) { - final double padding = 0.5; + const double padding = 0.5; double left = (calendarCellNotifier.value!.dx ~/ _cellWidth) * _cellWidth; double top = (calendarCellNotifier.value!.dy ~/ timeIntervalHeight) * timeIntervalHeight; _linePainter.style = PaintingStyle.stroke; _linePainter.strokeWidth = 2; _linePainter.color = calendarTheme.selectionBorderColor!.withOpacity(0.4); - left += (isRTL ? 0 : timeLabelWidth); - top = top == 0 ? top + padding : top; + left += isRTL ? 0 : timeLabelWidth; + top = top == 0 ? padding : top; double height = timeIntervalHeight; if (top == padding) { height -= padding; @@ -808,16 +804,16 @@ class _TimeSlotRenderObject extends CustomCalendarRenderObject { _linePainter.style = PaintingStyle.fill; final int count = specialRegionBounds.length; + final TextStyle defaultTextStyle = TextStyle( + color: calendarTheme.brightness == Brightness.dark + ? Colors.white54 + : Colors.black45); for (int i = 0; i < count; i++) { final TimeRegionView view = specialRegionBounds[i]; final CalendarTimeRegion region = view.region; _linePainter.color = region.color ?? Colors.grey.withOpacity(0.2); - final TextStyle textStyle = region.textStyle ?? - TextStyle( - color: calendarTheme.brightness == Brightness.dark - ? Colors.white54 - : Colors.black45); + final TextStyle textStyle = region.textStyle ?? defaultTextStyle; final Rect rect = view.bound; canvas.drawRect(rect, _linePainter); if ((region.text == null || region.text!.isEmpty) && @@ -852,17 +848,16 @@ class _TimeSlotRenderObject extends CustomCalendarRenderObject { (size.width - timeLabelWidth) / visibleDates.length; left = isRTL ? (size.width - timeLabelWidth) - cellWidth : timeLabelWidth; final double cellHeight = timeIntervalHeight; - final double hour = (timeSlotViewSettings.startHour - - timeSlotViewSettings.startHour.toInt()) * - 60; + final int startHour = timeSlotViewSettings.startHour.toInt(); + final int hour = + ((timeSlotViewSettings.startHour - startHour) * 60).toInt(); final int timeInterval = CalendarViewHelper.getTimeInterval(timeSlotViewSettings); for (int j = 0; j < visibleDates.length; j++) { DateTime date = visibleDates[j]; for (int i = 0; i < horizontalLinesCount; i++) { - final double minute = (i * timeInterval) + hour; - date = DateTime(date.year, date.month, date.day, - timeSlotViewSettings.startHour.toInt(), minute.toInt()); + final int minute = (i * timeInterval) + hour; + date = DateTime(date.year, date.month, date.day, startHour, minute); semanticsBuilder.add(CustomPainterSemantics( rect: Rect.fromLTWH(left, top, cellWidth, cellHeight), properties: SemanticsProperties( diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/views/month_view.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/views/month_view.dart index b696f23eb..8d68dc6dd 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/views/month_view.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/views/month_view.dart @@ -9,13 +9,14 @@ import '../common/calendar_view_helper.dart'; import '../common/date_time_engine.dart'; import '../common/event_args.dart'; import '../settings/month_view_settings.dart'; +import '../settings/week_number_style.dart'; import '../sfcalendar.dart'; /// Used to hold the month cell views on calendar month view. class MonthViewWidget extends StatefulWidget { /// Constructor to create the month view widget to holds month cells for /// calendar month view. - MonthViewWidget( + const MonthViewWidget( this.visibleDates, this.rowCount, this.monthCellStyle, @@ -35,6 +36,8 @@ class MonthViewWidget extends StatefulWidget { this.builder, this.width, this.height, + this.weekNumberStyle, + this.isMobilePlatform, this.visibleAppointmentNotifier); /// Defines the row count for the month view. @@ -91,6 +94,12 @@ class MonthViewWidget extends StatefulWidget { /// Defines the height of the month view widget. final double height; + /// Defines the text style of the week number. + final WeekNumberStyle weekNumberStyle; + + /// Defines the current platform is mobile platform or not. + final bool isMobilePlatform; + /// Used to build the widget that replaces the month cell. final MonthCellBuilder? builder; @@ -129,11 +138,17 @@ class _MonthViewWidgetState extends State { @override Widget build(BuildContext context) { final List children = []; + final double weekNumberPanelWidth = + CalendarViewHelper.getWeekNumberPanelWidth( + widget.calendar.showWeekNumber, + widget.width, + widget.isMobilePlatform); if (widget.builder != null) { final int visibleDatesCount = widget.visibleDates.length; - final double cellWidth = widget.width / DateTime.daysPerWeek; + final double cellWidth = + (widget.width - weekNumberPanelWidth) / DateTime.daysPerWeek; final double cellHeight = widget.height / widget.rowCount; - double xPosition = 0, yPosition = 0; + double xPosition = weekNumberPanelWidth, yPosition = 0; final int currentMonth = widget.visibleDates[visibleDatesCount ~/ 2].month; final bool showTrailingLeadingDates = @@ -145,7 +160,7 @@ class _MonthViewWidgetState extends State { currentMonth != currentVisibleDate.month) { xPosition += cellWidth; if (xPosition + 1 >= widget.width) { - xPosition = 0; + xPosition = weekNumberPanelWidth; yPosition += cellHeight; } @@ -169,8 +184,8 @@ class _MonthViewWidgetState extends State { context, MonthCellDetails( currentVisibleDate, - List.unmodifiable(monthCellAppointment), - List.unmodifiable(widget.visibleDates), + List.unmodifiable(monthCellAppointment), + List.unmodifiable(widget.visibleDates), Rect.fromLTWH( widget.isRTL ? widget.width - xPosition - cellWidth @@ -182,7 +197,7 @@ class _MonthViewWidgetState extends State { xPosition += cellWidth; if (xPosition + 1 >= widget.width) { - xPosition = 0; + xPosition = weekNumberPanelWidth; yPosition += cellHeight; } } @@ -207,6 +222,9 @@ class _MonthViewWidgetState extends State { widget.textScaleFactor, widget.width, widget.height, + widget.calendar.weekNumberStyle, + weekNumberPanelWidth, + widget.isMobilePlatform, children: children, ); } @@ -238,6 +256,9 @@ class _MonthViewRenderObjectWidget extends MultiChildRenderObjectWidget { this.textScaleFactor, this.width, this.height, + this.weekNumberStyle, + this.weekNumberPanelWidth, + this.isMobilePlatform, {List children = const []}) : super(children: children); @@ -259,6 +280,9 @@ class _MonthViewRenderObjectWidget extends MultiChildRenderObjectWidget { final double textScaleFactor; final double width; final double height; + final WeekNumberStyle weekNumberStyle; + final double weekNumberPanelWidth; + final bool isMobilePlatform; @override _MonthViewRenderObject createRenderObject(BuildContext context) { @@ -280,7 +304,10 @@ class _MonthViewRenderObjectWidget extends MultiChildRenderObjectWidget { showTrailingAndLeadingDates, textScaleFactor, width, - height); + height, + weekNumberStyle, + weekNumberPanelWidth, + isMobilePlatform); } @override @@ -304,7 +331,10 @@ class _MonthViewRenderObjectWidget extends MultiChildRenderObjectWidget { ..showTrailingAndLeadingDates = showTrailingAndLeadingDates ..textScaleFactor = textScaleFactor ..width = width - ..height = height; + ..height = height + ..weekNumberPanelWidth = weekNumberPanelWidth + ..weekNumberStyle = weekNumberStyle + ..isMobilePlatform = isMobilePlatform; } } @@ -327,7 +357,22 @@ class _MonthViewRenderObject extends CustomCalendarRenderObject { this._showTrailingAndLeadingDates, this._textScaleFactor, this._width, - this._height); + this._height, + this._weekNumberStyle, + this._weekNumberPanelWidth, + this._isMobilePlatform); + + bool _isMobilePlatform; + + bool get isMobilePlatform => _isMobilePlatform; + + set isMobilePlatform(bool value) { + if (_isMobilePlatform == value) { + return; + } + + _isMobilePlatform = value; + } double _height; @@ -607,6 +652,40 @@ class _MonthViewRenderObject extends CustomCalendarRenderObject { _calendarCellNotifier.addListener(markNeedsPaint); } + WeekNumberStyle _weekNumberStyle; + + WeekNumberStyle get weekNumberStyle => _weekNumberStyle; + + set weekNumberStyle(WeekNumberStyle value) { + if (_weekNumberStyle == value) { + return; + } + + _weekNumberStyle = value; + if (childCount == 0) { + markNeedsPaint(); + } else { + markNeedsLayout(); + } + } + + double _weekNumberPanelWidth; + + double get weekNumberPanelWidth => _weekNumberPanelWidth; + + set weekNumberPanelWidth(double value) { + if (_weekNumberPanelWidth == value) { + return; + } + + _weekNumberPanelWidth = value; + if (childCount == 0) { + markNeedsPaint(); + } else { + markNeedsLayout(); + } + } + /// attach will called when the render object rendered in view. @override void attach(PipelineOwner owner) { @@ -628,7 +707,7 @@ class _MonthViewRenderObject extends CustomCalendarRenderObject { widgetSize.height.isInfinite ? height : widgetSize.height); final double cellWidth = size.width / DateTime.daysPerWeek; final double cellHeight = size.height / rowCount; - for (var child = firstChild; child != null; child = childAfter(child)) { + for (dynamic child = firstChild; child != null; child = childAfter(child)) { child.layout(constraints.copyWith( minWidth: cellWidth, minHeight: cellHeight, @@ -640,54 +719,92 @@ class _MonthViewRenderObject extends CustomCalendarRenderObject { @override void paint(PaintingContext context, Offset offset) { final bool _isNeedCustomPaint = childCount != 0; - final double cellWidth = size.width / DateTime.daysPerWeek; - final double cellHeight = size.height / rowCount; + if (_blackoutDatesIndex.isEmpty) { + _updateBlackoutDatesIndex(); + } + if (!_isNeedCustomPaint) { _drawMonthCells(context.canvas, size); } else { - double xPosition = 0, yPosition = 0; + final double cellWidth = + (size.width - weekNumberPanelWidth) / DateTime.daysPerWeek; + final double cellHeight = size.height / rowCount; + double xPosition = weekNumberPanelWidth, yPosition = 0; RenderBox? child = firstChild; final int visibleDatesCount = visibleDates.length; final int currentMonth = visibleDates[visibleDatesCount ~/ 2].month; final bool showTrailingLeadingDates = CalendarViewHelper.isLeadingAndTrailingDatesVisible( rowCount, showTrailingAndLeadingDates); + _drawWeekNumberPanel(context.canvas, cellHeight); for (int i = 0; i < visibleDatesCount; i++) { final DateTime currentVisibleDate = visibleDates[i]; + + if (weekNumberPanelWidth != 0 && + !showTrailingAndLeadingDates && + ((i <= DateTime.daysPerWeek && + visibleDates[DateTime.daysPerWeek].month == currentMonth) || + (i > DateTime.daysPerWeek && + i <= (DateTime.daysPerWeek * 2) && + visibleDates[DateTime.daysPerWeek * 2].month == + currentMonth) || + (i > visibleDatesCount - (DateTime.daysPerWeek * 2) && + (i < visibleDatesCount - DateTime.daysPerWeek) && + visibleDates[visibleDatesCount - (DateTime.daysPerWeek * 2)] + .month == + currentMonth) || + ((i >= visibleDatesCount - DateTime.daysPerWeek) && + visibleDates[visibleDatesCount - DateTime.daysPerWeek] + .month == + currentMonth)) && + currentVisibleDate.weekday == DateTime.monday) { + _drawWeekNumber( + context.canvas, size, currentVisibleDate, cellHeight, yPosition); + } + if (!showTrailingLeadingDates && currentMonth != currentVisibleDate.month) { xPosition += cellWidth; if (xPosition + 1 >= size.width) { - xPosition = 0; + xPosition = weekNumberPanelWidth; yPosition += cellHeight; } continue; } - child!.paint( - context, + context.paintChild( + child!, Offset(isRTL ? size.width - xPosition - cellWidth : xPosition, yPosition)); child = childAfter(child); if (calendarCellNotifier.value != null && - !CalendarViewHelper.isDateInDateCollection( - blackoutDates, currentVisibleDate)) { + !_blackoutDatesIndex.contains(i)) { _addMouseHovering(context.canvas, size, cellWidth, cellHeight, - xPosition, yPosition); + isRTL ? xPosition - weekNumberPanelWidth : xPosition, yPosition); + } + + if (weekNumberPanelWidth != 0 && + currentVisibleDate.weekday == DateTime.monday && + (showTrailingAndLeadingDates || + (!showTrailingAndLeadingDates && + (i >= (DateTime.daysPerWeek * 2)) && + (i <= visibleDatesCount - (DateTime.daysPerWeek * 2))))) { + _drawWeekNumber( + context.canvas, size, currentVisibleDate, cellHeight, yPosition); } xPosition += cellWidth; if (xPosition + 1 >= size.width) { - xPosition = 0; + xPosition = weekNumberPanelWidth; yPosition += cellHeight; } } } } - Paint _linePainter = Paint(); - TextPainter _textPainter = TextPainter(); + final Paint _linePainter = Paint(); + final TextPainter _textPainter = TextPainter(); static const double linePadding = 0.5; List _blackoutDatesIndex = []; @@ -706,27 +823,78 @@ class _MonthViewRenderObject extends CustomCalendarRenderObject { } } - void _drawMonthCells(Canvas canvas, Size size) { - if (_blackoutDatesIndex.isEmpty) { - _updateBlackoutDatesIndex(); + void _drawWeekNumber(Canvas canvas, Size size, DateTime date, + double cellHeight, double yPosition) { + final String weekNumber = + DateTimeHelper.getWeekNumberOfYear(date).toString(); + double xPosition = isRTL ? size.width - weekNumberPanelWidth : 0; + final TextStyle weekNumberTextStyle = + weekNumberStyle.textStyle ?? calendarTheme.weekNumberTextStyle; + final TextSpan textSpan = + TextSpan(text: weekNumber, style: weekNumberTextStyle); + + _textPainter.text = textSpan; + _textPainter.textDirection = TextDirection.ltr; + _textPainter.textWidthBasis = TextWidthBasis.longestLine; + _textPainter.textScaleFactor = textScaleFactor; + + const double topPadding = 4; + _textPainter.layout(minWidth: 0, maxWidth: weekNumberPanelWidth); + xPosition += (weekNumberPanelWidth - _textPainter.width) / 2; + _textPainter.paint(canvas, Offset(xPosition, yPosition + topPadding)); + } + + void _drawWeekNumberPanel(Canvas canvas, double cellHeight) { + if (weekNumberPanelWidth == 0) { + return; } - double xPosition, yPosition; - final double cellWidth = size.width / DateTime.daysPerWeek; - final double cellHeight = size.height / rowCount; - xPosition = isRTL ? size.width - cellWidth : 0; + final double xPosition = isRTL ? size.width - weekNumberPanelWidth : 0; + final double padding = isMobilePlatform ? 5 : 0; + final double left = xPosition + padding; + final double right = (xPosition + weekNumberPanelWidth) - padding; + final Rect rect = + Rect.fromLTRB(left, padding, right, size.height - padding); + _linePainter.style = PaintingStyle.fill; + _linePainter.color = weekNumberStyle.backgroundColor ?? + calendarTheme.weekNumberBackgroundColor!; + final RRect roundedRect = + RRect.fromRectAndRadius(rect, Radius.circular(padding)); + canvas.drawRRect(roundedRect, _linePainter); + + if (isMobilePlatform) { + double yPosition = cellHeight; + _linePainter.strokeWidth = linePadding; + _linePainter.color = cellBorderColor ?? calendarTheme.cellBorderColor; + for (int i = 0; i < rowCount - 1; i++) { + canvas.drawLine( + Offset(left, yPosition), Offset(right, yPosition), _linePainter); + yPosition += cellHeight; + } + } + } + + void _drawMonthCells(Canvas canvas, Size size) { const double viewPadding = 5; const double circlePadding = 4; - yPosition = viewPadding; + final double cellWidth = + (size.width - weekNumberPanelWidth) / DateTime.daysPerWeek; + final double cellHeight = size.height / rowCount; + double xPosition = isRTL + ? size.width - cellWidth - weekNumberPanelWidth + : weekNumberPanelWidth; + double yPosition = viewPadding; _textPainter.textDirection = TextDirection.ltr; _textPainter.textWidthBasis = TextWidthBasis.longestLine; _textPainter.textScaleFactor = textScaleFactor; - TextStyle textStyle = - monthCellStyle.textStyle ?? calendarTheme.activeDatesTextStyle; final int visibleDatesCount = visibleDates.length; final DateTime currentMonthDate = visibleDates[visibleDatesCount ~/ 2]; - final int nextMonth = getNextMonthDate(currentMonthDate).month; - final int previousMonth = getPreviousMonthDate(currentMonthDate).month; + final int nextMonth = + DateTimeHelper.getDateTimeValue(getNextMonthDate(currentMonthDate)) + .month; + final int previousMonth = + DateTimeHelper.getDateTimeValue(getPreviousMonthDate(currentMonthDate)) + .month; final DateTime today = DateTime.now(); bool isCurrentDate; @@ -741,30 +909,70 @@ class _MonthViewRenderObject extends CustomCalendarRenderObject { calendarTheme.leadingDatesTextStyle; final TextStyle? blackoutDatesStyle = blackoutDatesTextStyle ?? calendarTheme.blackoutDatesTextStyle; + final TextStyle disabledTextStyle = TextStyle( + color: calendarTheme.brightness == Brightness.light + ? Colors.black26 + : Colors.white38, + fontSize: 13, + fontFamily: 'Roboto'); final bool showTrailingLeadingDates = CalendarViewHelper.isLeadingAndTrailingDatesVisible( rowCount, showTrailingAndLeadingDates); + final Color currentMonthBackgroundColor = monthCellStyle.backgroundColor ?? + calendarTheme.activeDatesBackgroundColor; + final Color nextMonthBackgroundColor = + monthCellStyle.leadingDatesBackgroundColor ?? + calendarTheme.leadingDatesBackgroundColor; + final Color previousMonthBackgroundColor = + monthCellStyle.trailingDatesBackgroundColor ?? + calendarTheme.trailingDatesBackgroundColor; + final Color todayBackgroundColor = monthCellStyle.todayBackgroundColor ?? + calendarTheme.todayBackgroundColor; + + TextStyle textStyle = currentMonthTextStyle; + _drawWeekNumberPanel(canvas, cellHeight); for (int i = 0; i < visibleDatesCount; i++) { isCurrentDate = false; final DateTime currentVisibleDate = visibleDates[i]; + if (weekNumberPanelWidth != 0 && + !showTrailingAndLeadingDates && + ((i <= DateTime.daysPerWeek && + visibleDates[DateTime.daysPerWeek].month == + currentMonthDate.month) || + (i > DateTime.daysPerWeek && + i <= (DateTime.daysPerWeek * 2) && + visibleDates[DateTime.daysPerWeek * 2].month == + currentMonthDate.month) || + (i > visibleDatesCount - (DateTime.daysPerWeek * 2) && + (i < visibleDatesCount - DateTime.daysPerWeek) && + visibleDates[visibleDatesCount - (DateTime.daysPerWeek * 2)] + .month == + currentMonthDate.month) || + ((i >= visibleDatesCount - DateTime.daysPerWeek) && + visibleDates[visibleDatesCount - DateTime.daysPerWeek] + .month == + currentMonthDate.month)) && + currentVisibleDate.weekday == DateTime.monday) { + _drawWeekNumber( + canvas, size, currentVisibleDate, cellHeight, yPosition); + } + textStyle = currentMonthTextStyle; + _linePainter.color = currentMonthBackgroundColor; if (currentVisibleDate.month == nextMonth) { if (!showTrailingLeadingDates) { if (isRTL) { - if (xPosition.round() == cellWidth.round()) { - xPosition = 0; - } else { - xPosition -= cellWidth; - } - if (xPosition < 0) { - xPosition = size.width - cellWidth; + if (xPosition - 1 < 0) { + xPosition = size.width; yPosition += cellHeight; } + + xPosition -= cellWidth; } else { xPosition += cellWidth; - if (xPosition.round() >= size.width.round()) { - xPosition = 0; + if (xPosition + 1 >= size.width) { + xPosition = weekNumberPanelWidth; yPosition += cellHeight; } } @@ -773,24 +981,20 @@ class _MonthViewRenderObject extends CustomCalendarRenderObject { } textStyle = nextMonthTextStyle; - _linePainter.color = monthCellStyle.leadingDatesBackgroundColor ?? - calendarTheme.leadingDatesBackgroundColor; + _linePainter.color = nextMonthBackgroundColor; } else if (currentVisibleDate.month == previousMonth) { if (!showTrailingLeadingDates) { if (isRTL) { - if (xPosition.round() == cellWidth.round()) { - xPosition = 0; - } else { - xPosition -= cellWidth; - } - if (xPosition < 0) { - xPosition = size.width - cellWidth; + if (xPosition - 1 < 0) { + xPosition = size.width; yPosition += cellHeight; } + + xPosition -= cellWidth; } else { xPosition += cellWidth; - if (xPosition.round() >= size.width.round()) { - xPosition = 0; + if (xPosition + 1 >= size.width) { + xPosition = weekNumberPanelWidth; yPosition += cellHeight; } } @@ -799,12 +1003,7 @@ class _MonthViewRenderObject extends CustomCalendarRenderObject { } textStyle = previousMonthTextStyle; - _linePainter.color = monthCellStyle.trailingDatesBackgroundColor ?? - calendarTheme.trailingDatesBackgroundColor; - } else { - textStyle = currentMonthTextStyle; - _linePainter.color = monthCellStyle.backgroundColor ?? - calendarTheme.activeDatesBackgroundColor; + _linePainter.color = previousMonthBackgroundColor; } if (rowCount <= 4) { @@ -812,20 +1011,13 @@ class _MonthViewRenderObject extends CustomCalendarRenderObject { } if (isSameDate(currentVisibleDate, today)) { - _linePainter.color = monthCellStyle.todayBackgroundColor ?? - calendarTheme.todayBackgroundColor; + _linePainter.color = todayBackgroundColor; textStyle = todayStyle; isCurrentDate = true; } if (!isDateWithInDateRange(minDate, maxDate, currentVisibleDate)) { - if (calendarTheme.brightness == Brightness.light) { - textStyle = TextStyle( - color: Colors.black26, fontSize: 13, fontFamily: 'Roboto'); - } else { - textStyle = TextStyle( - color: Colors.white38, fontSize: 13, fontFamily: 'Roboto'); - } + textStyle = disabledTextStyle; } final bool isBlackoutDate = _blackoutDatesIndex.contains(i); @@ -876,20 +1068,28 @@ class _MonthViewRenderObject extends CustomCalendarRenderObject { canvas, Offset(xPosition + (cellWidth / 2 - _textPainter.width / 2), yPosition + circlePadding)); + + if (weekNumberPanelWidth != 0 && + currentVisibleDate.weekday == DateTime.monday && + (showTrailingAndLeadingDates || + (!showTrailingAndLeadingDates && + (i >= (DateTime.daysPerWeek * 2)) && + (i <= visibleDatesCount - (DateTime.daysPerWeek * 2))))) { + _drawWeekNumber( + canvas, size, currentVisibleDate, cellHeight, yPosition); + } + if (isRTL) { - if (xPosition.round() == cellWidth.round()) { - xPosition = 0; - } else { - xPosition -= cellWidth; - } - if (xPosition < 0) { - xPosition = size.width - cellWidth; + if (xPosition - 1 < 0) { + xPosition = size.width - weekNumberPanelWidth; yPosition += cellHeight; } + + xPosition -= cellWidth; } else { xPosition += cellWidth; - if (xPosition.round() >= size.width.round()) { - xPosition = 0; + if (xPosition + 1 >= size.width) { + xPosition = weekNumberPanelWidth; yPosition += cellHeight; } } @@ -927,20 +1127,39 @@ class _MonthViewRenderObject extends CustomCalendarRenderObject { yPosition = cellHeight; _linePainter.strokeWidth = linePadding; _linePainter.color = cellBorderColor ?? calendarTheme.cellBorderColor; - canvas.drawLine(const Offset(0, linePadding), - Offset(size.width, linePadding), _linePainter); + xPosition = isRTL ? 0 : weekNumberPanelWidth; + final double finalXPosition = + isRTL ? size.width - weekNumberPanelWidth : size.width; + canvas.drawLine(Offset(xPosition, linePadding), + Offset(finalXPosition, linePadding), _linePainter); for (int i = 0; i < rowCount - 1; i++) { canvas.drawLine( - Offset(0, yPosition), Offset(size.width, yPosition), _linePainter); + Offset( + isMobilePlatform + ? isRTL + ? 0 + : weekNumberPanelWidth + : 0, + yPosition), + Offset( + isMobilePlatform + ? isRTL + ? size.width - weekNumberPanelWidth + : size.width + : size.width, + yPosition), + _linePainter); yPosition += cellHeight; } canvas.drawLine(Offset(0, size.height - linePadding), Offset(size.width, size.height - linePadding), _linePainter); - xPosition = cellWidth; + xPosition = + weekNumberPanelWidth != 0 && !isRTL ? weekNumberPanelWidth : cellWidth; canvas.drawLine(const Offset(linePadding, 0), Offset(linePadding, size.height), _linePainter); - for (int i = 0; i < 6; i++) { + final int count = weekNumberPanelWidth == 0 ? 6 : 7; + for (int i = 0; i < count; i++) { canvas.drawLine( Offset(xPosition, 0), Offset(xPosition, size.height), _linePainter); xPosition += cellWidth; @@ -964,8 +1183,12 @@ class _MonthViewRenderObject extends CustomCalendarRenderObject { List _getSemanticsBuilder(Size size) { final List semanticsBuilder = []; - double left = 0, top = 0; - final double cellWidth = size.width / DateTime.daysPerWeek; + final double cellWidth = + (size.width - weekNumberPanelWidth) / DateTime.daysPerWeek; + double left = isRTL + ? size.width - cellWidth - weekNumberPanelWidth + : weekNumberPanelWidth, + top = 0; final double cellHeight = size.height / rowCount; final bool showTrailingLeadingDates = CalendarViewHelper.isLeadingAndTrailingDatesVisible( @@ -973,6 +1196,18 @@ class _MonthViewRenderObject extends CustomCalendarRenderObject { final int currentMonth = visibleDates[visibleDates.length ~/ 2].month; for (int i = 0; i < visibleDates.length; i++) { final DateTime currentVisibleDate = visibleDates[i]; + if (weekNumberPanelWidth != 0 && + currentVisibleDate.weekday == DateTime.monday) { + final int weekNumber = + DateTimeHelper.getWeekNumberOfYear(currentVisibleDate); + semanticsBuilder.add(CustomPainterSemantics( + rect: Rect.fromLTWH(isRTL ? (size.width - left - cellWidth) : 0, + top, weekNumberPanelWidth, cellHeight), + properties: SemanticsProperties( + label: 'week' + weekNumber.toString(), + textDirection: TextDirection.ltr, + ))); + } if (showTrailingLeadingDates || currentMonth == currentVisibleDate.month) { semanticsBuilder.add(CustomPainterSemantics( @@ -988,7 +1223,7 @@ class _MonthViewRenderObject extends CustomCalendarRenderObject { left += cellWidth; if (left + 1 >= size.width) { top += cellHeight; - left = 0; + left = weekNumberPanelWidth; } } diff --git a/packages/syncfusion_flutter_calendar/lib/src/calendar/views/timeline_view.dart b/packages/syncfusion_flutter_calendar/lib/src/calendar/views/timeline_view.dart index 81deeb05b..3ea08740d 100644 --- a/packages/syncfusion_flutter_calendar/lib/src/calendar/views/timeline_view.dart +++ b/packages/syncfusion_flutter_calendar/lib/src/calendar/views/timeline_view.dart @@ -18,7 +18,7 @@ import '../settings/view_header_style.dart'; class TimelineWidget extends StatefulWidget { /// Constructor to create the timeline widget to holds time slots view for /// timeline views. - TimelineWidget( + const TimelineWidget( this.horizontalLinesCountPerView, this.visibleDates, this.timeSlotViewSettings, @@ -106,7 +106,7 @@ class TimelineWidget extends StatefulWidget { } class _TimelineWidgetState extends State { - List _children = []; + final List _children = []; List _specialRegionViews = []; @override @@ -179,7 +179,8 @@ class _TimelineWidgetState extends State { void _updateSpecialRegionDetails() { _specialRegionViews = []; - if (widget.visibleDates.length > DateTime.daysPerWeek || + final int visibleDatesCount = widget.visibleDates.length; + if (visibleDatesCount > DateTime.daysPerWeek || widget.specialRegion == null || widget.specialRegion!.isEmpty) { return; @@ -190,8 +191,8 @@ class _TimelineWidgetState extends State { final DateTime startDate = AppointmentHelper.convertToStartTime(widget.visibleDates[0]); final DateTime endDate = AppointmentHelper.convertToEndTime( - widget.visibleDates[widget.visibleDates.length - 1]); - final double viewWidth = widget.width / widget.visibleDates.length; + widget.visibleDates[visibleDatesCount - 1]); + final double viewWidth = widget.width / visibleDatesCount; final bool isResourceEnabled = widget.resourceCollection != null && widget.resourceCollection!.isNotEmpty; for (int i = 0; i < widget.specialRegion!.length; i++) { @@ -231,7 +232,7 @@ class _TimelineWidgetState extends State { startIndex = 0; } else { /// Find the next index when the start date as non working date. - for (int k = 1; k < widget.visibleDates.length; k++) { + for (int k = 1; k < visibleDatesCount; k++) { final DateTime currentDate = widget.visibleDates[k]; if (currentDate.isBefore(regionStartTime)) { continue; @@ -258,7 +259,7 @@ class _TimelineWidgetState extends State { if (endIndex == -1) { /// Find the previous index when the end date as non working date. if (endDate.isAfter(regionEndTime)) { - for (int k = widget.visibleDates.length - 2; k >= 0; k--) { + for (int k = visibleDatesCount - 2; k >= 0; k--) { final DateTime _currentDate = widget.visibleDates[k]; if (_currentDate.isAfter(regionEndTime)) { continue; @@ -269,12 +270,12 @@ class _TimelineWidgetState extends State { } if (endIndex == -1) { - endIndex = widget.visibleDates.length - 1; + endIndex = visibleDatesCount - 1; } } else { /// Set index as visible date end date index when the /// region end date before the visible end date - endIndex = widget.visibleDates.length - 1; + endIndex = visibleDatesCount - 1; } /// End date as non working day and its index as previous date index. @@ -698,7 +699,7 @@ class _TimelineRenderObject extends CustomCalendarRenderObject { late ScrollController scrollController; late bool isMobilePlatform; - Paint _linePainter = Paint(); + final Paint _linePainter = Paint(); @override bool get isRepaintBoundary => true; @@ -749,9 +750,12 @@ class _TimelineRenderObject extends CustomCalendarRenderObject { final bool isNeedDefaultPaint = childCount == 0; final bool isResourceEnabled = resourceCollection != null && resourceCollection!.isNotEmpty; - _minMaxExceeds(minDate, maxDate, blackoutDates, context.canvas); + final int visibleDatesCount = visibleDates.length; + final bool isTimelineMonth = visibleDatesCount > DateTime.daysPerWeek; + _minMaxExceeds(visibleDatesCount, isTimelineMonth, minDate, maxDate, + blackoutDates, context.canvas); if (isNeedDefaultPaint) { - _addSpecialRegion(context.canvas, isResourceEnabled); + _addSpecialRegion(context.canvas, isResourceEnabled, isTimelineMonth); } else { if (specialRegion == null || specialRegion!.isEmpty) { return; @@ -764,47 +768,64 @@ class _TimelineRenderObject extends CustomCalendarRenderObject { continue; } final Rect rect = view.bound; - child.paint(context, Offset(rect.left, rect.top)); + context.paintChild(child, Offset(rect.left, rect.top)); child = childAfter(child); } } - _drawTimeline(context.canvas, isResourceEnabled); + _drawTimeline(context.canvas, isResourceEnabled, visibleDatesCount); } void _minMaxExceeds( - DateTime minDate, DateTime maxDate, blackoutDates, Canvas canvas) { + int visibleDatesCount, + bool isTimelineMonth, + DateTime minDate, + DateTime maxDate, + List? blackoutDates, + Canvas canvas) { final DateTime visibleStartDate = visibleDates[0]; - final DateTime visibleEndDate = visibleDates[visibleDates.length - 1]; - final bool isTimelineMonth = visibleDates.length > DateTime.daysPerWeek; + final DateTime visibleEndDate = visibleDates[visibleDatesCount - 1]; + final int timeInterval = + CalendarViewHelper.getTimeInterval(timeSlotViewSettings); if (isDateWithInDateRange(visibleStartDate, visibleEndDate, minDate)) { - _drawDisabledDate(minDate, false, false, canvas, isTimelineMonth); + _drawDisabledDate(minDate, false, false, canvas, isTimelineMonth, + timeInterval, visibleDatesCount); } if (isDateWithInDateRange(visibleStartDate, visibleEndDate, maxDate)) { - _drawDisabledDate(maxDate, true, false, canvas, isTimelineMonth); + _drawDisabledDate(maxDate, true, false, canvas, isTimelineMonth, + timeInterval, visibleDatesCount); } - if (blackoutDates != null && isTimelineMonth) { - final int count = blackoutDates.length; - for (int i = 0; i < count; i++) { - final DateTime blackoutDate = blackoutDates[i]; - if (isDateWithInDateRange( - visibleStartDate, visibleEndDate, blackoutDate)) { - _drawDisabledDate(blackoutDate, false, true, canvas, isTimelineMonth); - } + + if (blackoutDates == null || !isTimelineMonth) { + return; + } + + final int count = blackoutDates.length; + for (int i = 0; i < count; i++) { + final DateTime blackoutDate = blackoutDates[i]; + if (isDateWithInDateRange( + visibleStartDate, visibleEndDate, blackoutDate)) { + _drawDisabledDate(blackoutDate, false, true, canvas, isTimelineMonth, + timeInterval, visibleDatesCount); } } } - void _drawDisabledDate(DateTime disabledDate, isMaxDate, isBlackOutDate, - canvas, bool isTimelineMonth) { - final double minuteHeight = timeIntervalWidth / - CalendarViewHelper.getTimeInterval(timeSlotViewSettings); - final double viewWidth = width / visibleDates.length; + void _drawDisabledDate( + DateTime disabledDate, + bool isMaxDate, + bool isBlackOutDate, + Canvas canvas, + bool isTimelineMonth, + int timeInterval, + int visibleDatesCount) { + final double minuteHeight = timeIntervalWidth / timeInterval; + final double viewWidth = width / visibleDatesCount; final int dateIndex = DateTimeHelper.getVisibleDateIndex(visibleDates, disabledDate); double leftPosition = 0; - final double topPosition = 0; + const double topPosition = 0; final double xPosition = isTimelineMonth ? 0 @@ -813,14 +834,14 @@ class _TimelineRenderObject extends CustomCalendarRenderObject { timeSlotViewSettings, minuteHeight); double rightPosition = (dateIndex * viewWidth) + xPosition; - if (isMaxDate) { + if (isMaxDate == true) { leftPosition = (dateIndex * viewWidth) + (isTimelineMonth ? viewWidth : xPosition); rightPosition = size.width; } final double bottomPosition = topPosition + height; - if (isBlackOutDate) { - leftPosition = (dateIndex * timeIntervalWidth); + if (isBlackOutDate == true) { + leftPosition = dateIndex * timeIntervalWidth; rightPosition = leftPosition + timeIntervalWidth; } Rect rect; @@ -835,7 +856,8 @@ class _TimelineRenderObject extends CustomCalendarRenderObject { canvas.drawRect(rect, _linePainter); } - void _drawTimeline(Canvas canvas, bool isResourceEnabled) { + void _drawTimeline( + Canvas canvas, bool isResourceEnabled, int visibleDatesCount) { _linePainter.strokeWidth = 0.5; _linePainter.strokeCap = StrokeCap.round; _linePainter.color = cellBorderColor ?? calendarTheme.cellBorderColor; @@ -858,9 +880,7 @@ class _TimelineRenderObject extends CustomCalendarRenderObject { } final List points = []; - for (int i = 0; - i < horizontalLinesCountPerView * visibleDates.length; - i++) { + for (int i = 0; i < horizontalLinesCountPerView * visibleDatesCount; i++) { if (isMobilePlatform) { points.add(Offset(startXPosition, startYPosition)); points.add(Offset(endXPosition, endYPosition)); @@ -940,13 +960,12 @@ class _TimelineRenderObject extends CustomCalendarRenderObject { /// Calculate the position for special regions and draw the special regions /// in the timeline views . - void _addSpecialRegion(Canvas canvas, bool isResourceEnabled) { + void _addSpecialRegion( + Canvas canvas, bool isResourceEnabled, bool isTimelineMonth) { /// Condition added to check and add the special region for timeline day, /// timeline week and timeline work week view only, since the special region /// support not applicable for timeline month view. - if (visibleDates.length > DateTime.daysPerWeek || - _specialRegion == null || - _specialRegion!.isEmpty) { + if (isTimelineMonth || _specialRegion == null || _specialRegion!.isEmpty) { return; } @@ -959,15 +978,15 @@ class _TimelineRenderObject extends CustomCalendarRenderObject { _linePainter.style = PaintingStyle.fill; final int count = specialRegionBounds.length; + final TextStyle defaultTextStyle = TextStyle( + color: calendarTheme.brightness == Brightness.dark + ? Colors.white54 + : Colors.black45); for (int i = 0; i < count; i++) { final TimeRegionView view = specialRegionBounds[i]; final CalendarTimeRegion region = view.region; _linePainter.color = region.color ?? Colors.grey.withOpacity(0.2); - final TextStyle textStyle = region.textStyle ?? - TextStyle( - color: calendarTheme.brightness == Brightness.dark - ? Colors.white54 - : Colors.black45); + final TextStyle textStyle = region.textStyle ?? defaultTextStyle; final Rect rect = view.bound; canvas.drawRect(rect, _linePainter); if ((region.text == null || region.text!.isEmpty) && @@ -1018,17 +1037,16 @@ class _TimelineRenderObject extends CustomCalendarRenderObject { double height, List semanticsBuilder, [CalendarResource? resource]) { double left = isRTL ? size.width - timeIntervalWidth : 0; + final int startHour = timeSlotViewSettings.startHour.toInt(); + final int startHourMinutes = + ((timeSlotViewSettings.startHour - startHour) * 60).toInt(); + final int timeInterval = + CalendarViewHelper.getTimeInterval(timeSlotViewSettings); for (int j = 0; j < visibleDates.length; j++) { DateTime date = visibleDates[j]; - final double hour = (timeSlotViewSettings.startHour - - timeSlotViewSettings.startHour.toInt()) * - 60; for (int i = 0; i < horizontalLinesCountPerView; i++) { - final double minute = - (i * CalendarViewHelper.getTimeInterval(timeSlotViewSettings)) + - hour; - date = DateTime(date.year, date.month, date.day, - timeSlotViewSettings.startHour.toInt(), minute.toInt()); + final int minute = (i * timeInterval) + startHourMinutes; + date = DateTime(date.year, date.month, date.day, startHour, minute); semanticsBuilder.add(CustomPainterSemantics( rect: Rect.fromLTWH(left, top, timeIntervalWidth, height), properties: SemanticsProperties( @@ -1050,7 +1068,7 @@ class _TimelineRenderObject extends CustomCalendarRenderObject { String _getAccessibilityText(DateTime date, [CalendarResource? resource]) { String dateText; if (visibleDates.length > DateTime.daysPerWeek) { - dateText = DateFormat('EEEEE, dd\MMMM\yyyy').format(date).toString(); + dateText = DateFormat('EEEEE, dd/MMMM/yyyy').format(date).toString(); } dateText = DateFormat('h a, dd/MMMM/yyyy').format(date).toString(); @@ -1146,15 +1164,17 @@ class TimelineViewHeaderView extends CustomPainter { final double textScaleFactor; final double _padding = 5; double _xPosition = 0; - TextPainter _dayTextPainter = TextPainter(), _dateTextPainter = TextPainter(); - Paint _hoverPainter = Paint(); + final TextPainter _dayTextPainter = TextPainter(), + _dateTextPainter = TextPainter(); + final Paint _hoverPainter = Paint(); @override void paint(Canvas canvas, Size size) { canvas.clipRect(Rect.fromLTWH(0, 0, size.width, size.height)); - final bool isTimelineMonth = visibleDates.length > DateTime.daysPerWeek; + final int visibleDatesLength = visibleDates.length; + final bool isTimelineMonth = visibleDatesLength > DateTime.daysPerWeek; final DateTime today = DateTime.now(); - final double childWidth = size.width / visibleDates.length; + final double childWidth = size.width / visibleDatesLength; final int index = isTimelineMonth ? 0 : timelineViewHeaderScrollController.offset ~/ childWidth; @@ -1166,12 +1186,12 @@ class TimelineViewHeaderView extends CustomPainter { TextStyle viewHeaderDayTextStyle = calendarTheme.brightness == Brightness.light - ? TextStyle( + ? const TextStyle( color: Colors.black87, fontSize: 11, fontWeight: FontWeight.w400, fontFamily: 'Roboto') - : TextStyle( + : const TextStyle( color: Colors.white, fontSize: 11, fontWeight: FontWeight.w400, @@ -1183,7 +1203,7 @@ class TimelineViewHeaderView extends CustomPainter { fontSize: 15, fontWeight: FontWeight.w400, fontFamily: 'Roboto') - : TextStyle( + : const TextStyle( color: Colors.white, fontSize: 15, fontWeight: FontWeight.w400, @@ -1211,14 +1231,17 @@ class TimelineViewHeaderView extends CustomPainter { TextStyle dayTextStyle = viewHeaderDayStyle; TextStyle dateTextStyle = viewHeaderDateStyle; + final Color? todayTextColor = CalendarViewHelper.getTodayHighlightTextColor( + todayHighlightColor, todayTextStyle, calendarTheme); + if (isTimelineMonth) { _hoverPainter.strokeWidth = 0.5; _hoverPainter.strokeCap = StrokeCap.round; _hoverPainter.color = cellBorderColor ?? calendarTheme.cellBorderColor; - canvas.drawLine(Offset(0, 0), Offset(size.width, 0), _hoverPainter); + canvas.drawLine(const Offset(0, 0), Offset(size.width, 0), _hoverPainter); } - for (int i = 0; i < visibleDates.length; i++) { + for (int i = 0; i < visibleDatesLength; i++) { if (i < index && !isTimelineMonth) { continue; } @@ -1242,14 +1265,12 @@ class TimelineViewHeaderView extends CustomPainter { if (isSameDate(currentDate, today)) { dayTextStyle = todayTextStyle != null ? todayTextStyle!.copyWith( - fontSize: viewHeaderDayStyle.fontSize, - color: todayHighlightColor) - : viewHeaderDayStyle.copyWith(color: todayHighlightColor); + fontSize: viewHeaderDayStyle.fontSize, color: todayTextColor) + : viewHeaderDayStyle.copyWith(color: todayTextColor); dateTextStyle = todayTextStyle != null ? todayTextStyle!.copyWith( - fontSize: viewHeaderDateStyle.fontSize, - color: todayHighlightColor) - : viewHeaderDateStyle.copyWith(color: todayHighlightColor); + fontSize: viewHeaderDateStyle.fontSize, color: todayTextColor) + : viewHeaderDateStyle.copyWith(color: todayTextColor); } else { dateTextStyle = viewHeaderDateStyle; dayTextStyle = viewHeaderDayStyle; @@ -1423,8 +1444,8 @@ class TimelineViewHeaderView extends CustomPainter { @override bool shouldRepaint(TimelineViewHeaderView oldDelegate) { final TimelineViewHeaderView oldWidget = oldDelegate; + final bool isTimelineMonth = visibleDates.length > DateTime.daysPerWeek; return oldWidget.visibleDates != visibleDates || - oldWidget._xPosition != _xPosition || oldWidget.viewHeaderStyle != viewHeaderStyle || oldWidget.timeSlotViewSettings != timeSlotViewSettings || oldWidget.viewHeaderHeight != viewHeaderHeight || @@ -1434,17 +1455,21 @@ class TimelineViewHeaderView extends CustomPainter { oldWidget.viewHeaderNotifier.value != viewHeaderNotifier.value || oldWidget.todayTextStyle != todayTextStyle || oldWidget.textScaleFactor != textScaleFactor || - !CalendarViewHelper.isDateCollectionEqual( - oldWidget.blackoutDates, blackoutDates); + oldWidget.calendarTheme != calendarTheme || + (isTimelineMonth && + (oldWidget.blackoutDatesTextStyle != blackoutDatesTextStyle || + !CalendarViewHelper.isDateCollectionEqual( + oldWidget.blackoutDates, blackoutDates))); } List _getSemanticsBuilder(Size size) { final List semanticsBuilder = []; - final double cellWidth = size.width / visibleDates.length; + final int visibleDatesLength = visibleDates.length; + final double cellWidth = size.width / visibleDatesLength; double left = isRTL ? size.width - cellWidth : 0; const double top = 0; - for (int i = 0; i < visibleDates.length; i++) { + for (int i = 0; i < visibleDatesLength; i++) { semanticsBuilder.add(CustomPainterSemantics( rect: Rect.fromLTWH(left, top, cellWidth, size.height), properties: SemanticsProperties( @@ -1490,7 +1515,6 @@ class TimelineViewHeaderView extends CustomPainter { @override bool shouldRebuildSemantics(TimelineViewHeaderView oldDelegate) { final TimelineViewHeaderView oldWidget = oldDelegate; - return oldWidget.visibleDates != visibleDates || - oldWidget.calendarTheme != calendarTheme; + return oldWidget.visibleDates != visibleDates; } } diff --git a/packages/syncfusion_flutter_calendar/pubspec.yaml b/packages/syncfusion_flutter_calendar/pubspec.yaml index 7fbc22060..28304f923 100644 --- a/packages/syncfusion_flutter_calendar/pubspec.yaml +++ b/packages/syncfusion_flutter_calendar/pubspec.yaml @@ -1,6 +1,6 @@ name: syncfusion_flutter_calendar description: The Flutter Calendar widget has nine built-in configurable views that provide basic functionalities for scheduling and representing appointments/events efficiently. -version: 19.1.54 +version: 18.3.35 homepage: https://github.com/syncfusion/flutter-examples environment: @@ -9,13 +9,13 @@ environment: dependencies: flutter: sdk: flutter - timezone: 0.7.0-nullsafety.0 + timezone: 0.7.0 syncfusion_flutter_core: path: ../syncfusion_flutter_core syncfusion_flutter_datepicker: path: ../syncfusion_flutter_datepicker - + intl: ">=0.15.0 <0.20.0" flutter: diff --git a/packages/syncfusion_flutter_charts/CHANGELOG.md b/packages/syncfusion_flutter_charts/CHANGELOG.md index 67054834a..610771062 100644 --- a/packages/syncfusion_flutter_charts/CHANGELOG.md +++ b/packages/syncfusion_flutter_charts/CHANGELOG.md @@ -1,3 +1,45 @@ +## Unreleased + +**Bugs** +* The axis interval, zoom factor and zoom position will be maintained properly when enabled auto-scrolling. +* Now, no exception will be thrown while adding multiple indicators and enabling the legend. + +**Features** +* Provided milliseconds interval support for date time and date time category axis. +* Provided support to place the legend anywhere at the top of the chart. +* Provided support to decide whether to deselect or let the data point remain selected on tapping the selected data point in the chart. +* Provided overfilled radial bar support which indicates the value that is above the maximum value. +* Provided support to trigger an event when long-pressing or double-tapping the data points. +* Now all the internally calculated indicator values can be retrieved for further use in the application. + +**Breaking changes** + +* `onPointTapped` callback has been deprecated, instead use `onPointTap` callback in Series class to get the tapped data point details. +* `onIndicatorRender` callback has been deprecated, instead use ` onRenderDetailsUpdate` callback in TechnicalIndicators class to get the indicator details. + + +## [19.1.54] - 03/30/2021 + +**Bugs** +* The annotation will not flicker on zooming or panning and will get positioned properly in the plot area of the chart. + +**Features** +* Provided on-demand data loading support to load more data lazily. +* Provided auto-scrolling support to display a fixed number of data points in the visible range and can view the remaining data by panning. +* Implemented a new x-axis type named DateTimeCategory axis, which is a mixture of date-time and category axis. +* Provided support to fill the circular charts with gradient and image shader. +* Provided support to switch the circular charts rendering mode as gradient instead of solid colors. +* Now, the trackball tooltip can be rendered along with markers alike the series tooltip. +* The swiping gesture has been added to the chart to achieve pagination functionality. +* Provided support to change the trackball/crosshair position even after the touch interaction leaves the chart area. + +**Breaking changes** + +* `onAxisLabelRender` callback has been deprecated, instead use `labelFormatter` callback to customize the axis labels. +* Hereafter initialize the chart behaviors in the `initState` method instead of `build method. +* Now, the marker will be displayed in the trackball tooltip by default. + + ## [18.4.44] - 02/23/2021 **Bugs** diff --git a/packages/syncfusion_flutter_charts/README.md b/packages/syncfusion_flutter_charts/README.md index 03320e01e..c997ac6cb 100644 --- a/packages/syncfusion_flutter_charts/README.md +++ b/packages/syncfusion_flutter_charts/README.md @@ -83,12 +83,17 @@ Spark charts (Sparkline charts) which is also known as micro charts are lightwei Explore the full capabilities of our Flutter widgets on your device by installing our sample browser applications from the below app stores, and view samples code in GitHub.

- - + + +

- - + + + +

+

+

## Other useful links @@ -295,9 +300,9 @@ Widget build(BuildContext context) { activationMode: SparkChartActivationMode.tap), //Enable marker marker: SparkChartMarker( - displayMode: MarkerDisplayMode.all), + displayMode: SparkChartMarkerDisplayMode.all), //Enable data label - labelDisplayMode: LabelDisplayMode.all, + labelDisplayMode: SparkChartLabelDisplayMode.all, data: [ 1, 5, -6, 0, 1, -2, 7, -7, -4, -10, 13, -6, 7, 5, 11, 5, 3 ], diff --git a/packages/syncfusion_flutter_charts/analysis_options.yaml b/packages/syncfusion_flutter_charts/analysis_options.yaml index 67178baca..9bde33a57 100644 --- a/packages/syncfusion_flutter_charts/analysis_options.yaml +++ b/packages/syncfusion_flutter_charts/analysis_options.yaml @@ -5,4 +5,11 @@ analyzer: include_file_not_found: ignore lines_longer_than_80_chars: ignore avoid_as: ignore + cast_nullable_to_non_nullable: ignore + implicit_dynamic_parameter: ignore + field_initializer_not_assignable: ignore + invalid_assignment: ignore unnecessary_null_comparison: ignore + unnecessary_nullable_for_final_variable_declarations: ignore + must_be_immutable: ignore + avoid_equals_and_hash_code_on_mutable_classes: ignore \ No newline at end of file diff --git a/packages/syncfusion_flutter_charts/dartdoc_options.yaml b/packages/syncfusion_flutter_charts/dartdoc_options.yaml new file mode 100644 index 000000000..2fd85ba9b --- /dev/null +++ b/packages/syncfusion_flutter_charts/dartdoc_options.yaml @@ -0,0 +1,4 @@ +dartdoc: + ignore: + - broken-link + - missing-from-search-index \ No newline at end of file diff --git a/packages/syncfusion_flutter_charts/example/analysis_options.yaml b/packages/syncfusion_flutter_charts/example/analysis_options.yaml new file mode 100644 index 000000000..b08414c80 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/analysis_options.yaml @@ -0,0 +1,3 @@ +analyzer: + errors: + invalid_dependency: ignore \ No newline at end of file diff --git a/packages/syncfusion_flutter_charts/example/lib/main.dart b/packages/syncfusion_flutter_charts/example/lib/main.dart index bc37090a3..9cec76cdd 100644 --- a/packages/syncfusion_flutter_charts/example/lib/main.dart +++ b/packages/syncfusion_flutter_charts/example/lib/main.dart @@ -57,21 +57,23 @@ class _MyHomePageState extends State<_MyHomePage> { // Enable data label dataLabelSettings: DataLabelSettings(isVisible: true)) ]), - Padding( - padding: const EdgeInsets.all(8.0), - //Initialize the spark charts widget - child: SfSparkLineChart.custom( - //Enable the trackball - trackball: SparkChartTrackball( - activationMode: SparkChartActivationMode.tap), - //Enable marker - marker: SparkChartMarker( - displayMode: SparkChartMarkerDisplayMode.all), - //Enable data label - labelDisplayMode: SparkChartLabelDisplayMode.all, - xValueMapper: (int index) => data[index].year, - yValueMapper: (int index) => data[index].sales, - dataCount: 5, + Expanded( + child: Padding( + padding: const EdgeInsets.all(8.0), + //Initialize the spark charts widget + child: SfSparkLineChart.custom( + //Enable the trackball + trackball: SparkChartTrackball( + activationMode: SparkChartActivationMode.tap), + //Enable marker + marker: SparkChartMarker( + displayMode: SparkChartMarkerDisplayMode.all), + //Enable data label + labelDisplayMode: SparkChartLabelDisplayMode.all, + xValueMapper: (int index) => data[index].year, + yValueMapper: (int index) => data[index].sales, + dataCount: 5, + ), ), ) ])); diff --git a/packages/syncfusion_flutter_charts/example/linux/.gitignore b/packages/syncfusion_flutter_charts/example/linux/.gitignore new file mode 100644 index 000000000..d3896c984 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/packages/syncfusion_flutter_charts/example/linux/CMakeLists.txt b/packages/syncfusion_flutter_charts/example/linux/CMakeLists.txt new file mode 100644 index 000000000..290c3e841 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/linux/CMakeLists.txt @@ -0,0 +1,106 @@ +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +set(BINARY_NAME "example") +set(APPLICATION_ID "com.example.example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Application build +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) +apply_standard_settings(${BINARY_NAME}) +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) +add_dependencies(${BINARY_NAME} flutter_assemble) +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/packages/syncfusion_flutter_charts/example/linux/flutter/CMakeLists.txt b/packages/syncfusion_flutter_charts/example/linux/flutter/CMakeLists.txt new file mode 100644 index 000000000..a1da1b9e5 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,91 @@ +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) +pkg_check_modules(BLKID REQUIRED IMPORTED_TARGET blkid) +pkg_check_modules(LZMA REQUIRED IMPORTED_TARGET liblzma) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO + PkgConfig::BLKID + PkgConfig::LZMA +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + linux-x64 ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/packages/syncfusion_flutter_charts/example/linux/flutter/generated_plugin_registrant.cc b/packages/syncfusion_flutter_charts/example/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000..d38195aa0 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,9 @@ +// +// Generated file. Do not edit. +// + +#include "generated_plugin_registrant.h" + + +void fl_register_plugins(FlPluginRegistry* registry) { +} diff --git a/packages/syncfusion_flutter_charts/example/linux/flutter/generated_plugin_registrant.h b/packages/syncfusion_flutter_charts/example/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000..9bf747894 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,13 @@ +// +// Generated file. Do not edit. +// + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/syncfusion_flutter_charts/example/linux/flutter/generated_plugins.cmake b/packages/syncfusion_flutter_charts/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 000000000..51436ae8c --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,15 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/packages/syncfusion_flutter_charts/example/linux/main.cc b/packages/syncfusion_flutter_charts/example/linux/main.cc new file mode 100644 index 000000000..e7c5c5437 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/packages/syncfusion_flutter_charts/example/linux/my_application.cc b/packages/syncfusion_flutter_charts/example/linux/my_application.cc new file mode 100644 index 000000000..543eaca72 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/linux/my_application.cc @@ -0,0 +1,104 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen *screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "example"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } + else { + gtk_window_set_title(window, "example"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar ***arguments, int *exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject *object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + nullptr)); +} diff --git a/packages/syncfusion_flutter_charts/example/linux/my_application.h b/packages/syncfusion_flutter_charts/example/linux/my_application.h new file mode 100644 index 000000000..72271d5e4 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/packages/syncfusion_flutter_charts/example/macos/.gitignore b/packages/syncfusion_flutter_charts/example/macos/.gitignore new file mode 100644 index 000000000..d2fd37723 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/macos/.gitignore @@ -0,0 +1,6 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/xcuserdata/ diff --git a/packages/syncfusion_flutter_charts/example/macos/Flutter/Flutter-Debug.xcconfig b/packages/syncfusion_flutter_charts/example/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 000000000..c2efd0b60 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/syncfusion_flutter_charts/example/macos/Flutter/Flutter-Release.xcconfig b/packages/syncfusion_flutter_charts/example/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 000000000..c2efd0b60 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/syncfusion_flutter_charts/example/macos/Flutter/GeneratedPluginRegistrant.swift b/packages/syncfusion_flutter_charts/example/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 000000000..cccf817a5 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,10 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { +} diff --git a/packages/syncfusion_flutter_charts/example/macos/Runner.xcodeproj/project.pbxproj b/packages/syncfusion_flutter_charts/example/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000..cc89c8782 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,572 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* example.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 0930; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + 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_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/packages/syncfusion_flutter_charts/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/syncfusion_flutter_charts/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/syncfusion_flutter_charts/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/syncfusion_flutter_charts/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000..ae8ff59d9 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/syncfusion_flutter_charts/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/syncfusion_flutter_charts/example/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..1d526a16e --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/syncfusion_flutter_charts/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/syncfusion_flutter_charts/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/syncfusion_flutter_charts/example/macos/Runner/AppDelegate.swift b/packages/syncfusion_flutter_charts/example/macos/Runner/AppDelegate.swift new file mode 100644 index 000000000..d53ef6437 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/packages/syncfusion_flutter_charts/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/syncfusion_flutter_charts/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..a2ec33f19 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/syncfusion_flutter_charts/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/packages/syncfusion_flutter_charts/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..3c4935a7ca84f0976aca34b7f2895d65fb94d1ea GIT binary patch literal 46993 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 literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_charts/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/packages/syncfusion_flutter_charts/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..ed4cc16421680a50164ba74381b4b35ceaa0ccfc GIT binary patch literal 3276 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 N;OXk;vd$@?2>?>Ex^Vyi literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_charts/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/packages/syncfusion_flutter_charts/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 0000000000000000000000000000000000000000..bcbf36df2f2aaaa0a63c7dabc94e600184229d0d GIT binary patch literal 5933 zcmZ{Idpwix|Np(&m_yAF>K&UIn{t*2ZOdsShYs(MibU!|=pZCJq~7E>B$QJr)hC5| zmk?V?ES039lQ~RC!kjkl-TU4?|NZ{>J$CPLUH9vHy`Hbhhnc~SD_vpzBp6Xw4`$%jfmPw(;etLCccvfU-s)1A zLl8-RiSx!#?Kwzd0E&>h;Fc z^;S84cUH7gMe#2}MHYcDXgbkI+Qh^X4BV~6y<@s`gMSNX!4@g8?ojjj5hZj5X4g9D zavr_NoeZ=4vim%!Y`GnF-?2_Gb)g$xAo>#zCOLB-jPww8a%c|r&DC=eVdE;y+HwH@ zy`JK(oq+Yw^-hLvWO4B8orWwLiKT!hX!?xw`kz%INd5f)>k1PZ`ZfM&&Ngw)HiXA| ze=+%KkiLe1hd>h!ZO2O$45alH0O|E+>G2oCiJ|3y2c$;XedBozx93BprOr$#d{W5sb*hQQ~M@+v_m!8s?9+{Q0adM?ip3qQ*P5$R~dFvP+5KOH_^A+l-qu5flE*KLJp!rtjqTVqJsmpc1 zo>T>*ja-V&ma7)K?CE9RTsKQKk7lhx$L`9d6-Gq`_zKDa6*>csToQ{&0rWf$mD7x~S3{oA z1wUZl&^{qbX>y*T71~3NWd1Wfgjg)<~BnK96Ro#om&~8mU{}D!Fu# zTrKKSM8gY^*47b2Vr|ZZe&m9Y`n+Y8lHvtlBbIjNl3pGxU{!#Crl5RPIO~!L5Y({ym~8%Ox-9g>IW8 zSz2G6D#F|L^lcotrZx4cFdfw6f){tqITj6>HSW&ijlgTJTGbc7Q#=)*Be0-s0$fCk z^YaG;7Q1dfJq#p|EJ~YYmqjs`M0jPl=E`Id{+h%Lo*|8xp6K7yfgjqiH7{61$4x~A zNnH+65?QCtL;_w(|mDNJXybin=rOy-i7A@lXEu z&jY(5jhjlP{TsjMe$*b^2kp8LeAXu~*q&5;|3v|4w4Ij_4c{4GG8={;=K#lh{#C8v z&t9d7bf{@9aUaE94V~4wtQ|LMT*Ruuu0Ndjj*vh2pWW@|KeeXi(vt!YXi~I6?r5PG z$_{M*wrccE6x42nPaJUO#tBu$l#MInrZhej_Tqki{;BT0VZeb$Ba%;>L!##cvieb2 zwn(_+o!zhMk@l~$$}hivyebloEnNQmOy6biopy`GL?=hN&2)hsA0@fj=A^uEv~TFE z<|ZJIWplBEmufYI)<>IXMv(c+I^y6qBthESbAnk?0N(PI>4{ASayV1ErZ&dsM4Z@E-)F&V0>tIF+Oubl zin^4Qx@`Un4kRiPq+LX5{4*+twI#F~PE7g{FpJ`{)K()FH+VG^>)C-VgK>S=PH!m^ zE$+Cfz!Ja`s^Vo(fd&+U{W|K$e(|{YG;^9{D|UdadmUW;j;&V!rU)W_@kqQj*Frp~ z7=kRxk)d1$$38B03-E_|v=<*~p3>)2w*eXo(vk%HCXeT5lf_Z+D}(Uju=(WdZ4xa( zg>98lC^Z_`s-=ra9ZC^lAF?rIvQZpAMz8-#EgX;`lc6*53ckpxG}(pJp~0XBd9?RP zq!J-f`h0dC*nWxKUh~8YqN{SjiJ6vLBkMRo?;|eA(I!akhGm^}JXoL_sHYkGEQWWf zTR_u*Ga~Y!hUuqb`h|`DS-T)yCiF#s<KR}hC~F%m)?xjzj6w#Za%~XsXFS@P0E3t*qs)tR43%!OUxs(|FTR4Sjz(N zppN>{Ip2l3esk9rtB#+To92s~*WGK`G+ECt6D>Bvm|0`>Img`jUr$r@##&!1Ud{r| zgC@cPkNL_na`74%fIk)NaP-0UGq`|9gB}oHRoRU7U>Uqe!U61fY7*Nj(JiFa-B7Av z;VNDv7Xx&CTwh(C2ZT{ot`!E~1i1kK;VtIh?;a1iLWifv8121n6X!{C%kw|h-Z8_U z9Y8M38M2QG^=h+dW*$CJFmuVcrvD*0hbFOD=~wU?C5VqNiIgAs#4axofE*WFYd|K;Et18?xaI|v-0hN#D#7j z5I{XH)+v0)ZYF=-qloGQ>!)q_2S(Lg3<=UsLn%O)V-mhI-nc_cJZu(QWRY)*1il%n zOR5Kdi)zL-5w~lOixilSSF9YQ29*H+Br2*T2lJ?aSLKBwv7}*ZfICEb$t>z&A+O3C z^@_rpf0S7MO<3?73G5{LWrDWfhy-c7%M}E>0!Q(Iu71MYB(|gk$2`jH?!>ND0?xZu z1V|&*VsEG9U zm)!4#oTcgOO6Hqt3^vcHx>n}%pyf|NSNyTZX*f+TODT`F%IyvCpY?BGELP#s<|D{U z9lUTj%P6>^0Y$fvIdSj5*=&VVMy&nms=!=2y<5DP8x;Z13#YXf7}G)sc$_TQQ=4BD zQ1Le^y+BwHl7T6)`Q&9H&A2fJ@IPa;On5n!VNqWUiA*XXOnvoSjEIKW<$V~1?#zts>enlSTQaG2A|Ck4WkZWQoeOu(te znV;souKbA2W=)YWldqW@fV^$6EuB`lFmXYm%WqI}X?I1I7(mQ8U-pm+Ya* z|7o6wac&1>GuQfIvzU7YHIz_|V;J*CMLJolXMx^9CI;I+{Nph?sf2pX@%OKT;N@Uz9Y zzuNq11Ccdwtr(TDLx}N!>?weLLkv~i!xfI0HGWff*!12E*?7QzzZT%TX{5b7{8^*A z3ut^C4uxSDf=~t4wZ%L%gO_WS7SR4Ok7hJ;tvZ9QBfVE%2)6hE>xu9y*2%X5y%g$8 z*8&(XxwN?dO?2b4VSa@On~5A?zZZ{^s3rXm54Cfi-%4hBFSk|zY9u(3d1ButJuZ1@ zfOHtpSt)uJnL`zg9bBvUkjbPO0xNr{^{h0~$I$XQzel_OIEkgT5L!dW1uSnKsEMVp z9t^dfkxq=BneR9`%b#nWSdj)u1G=Ehv0$L@xe_eG$Ac%f7 zy`*X(p0r3FdCTa1AX^BtmPJNR4%S1nyu-AM-8)~t-KII9GEJU)W^ng7C@3%&3lj$2 z4niLa8)fJ2g>%`;;!re+Vh{3V^}9osx@pH8>b0#d8p`Dgm{I?y@dUJ4QcSB<+FAuT)O9gMlwrERIy z6)DFLaEhJkQ7S4^Qr!JA6*SYni$THFtE)0@%!vAw%X7y~!#k0?-|&6VIpFY9>5GhK zr;nM-Z`Omh>1>7;&?VC5JQoKi<`!BU_&GLzR%92V$kMohNpMDB=&NzMB&w-^SF~_# zNsTca>J{Y555+z|IT75yW;wi5A1Z zyzv|4l|xZ-Oy8r8_c8X)h%|a8#(oWcgS5P6gtuCA_vA!t=)IFTL{nnh8iW!B$i=Kd zj1ILrL;ht_4aRKF(l1%^dUyVxgK!2QsL)-{x$`q5wWjjN6B!Cj)jB=bii;9&Ee-;< zJfVk(8EOrbM&5mUciP49{Z43|TLoE#j(nQN_MaKt16dp#T6jF7z?^5*KwoT-Y`rs$ z?}8)#5Dg-Rx!PTa2R5; zx0zhW{BOpx_wKPlTu;4ev-0dUwp;g3qqIi|UMC@A?zEb3RXY`z_}gbwju zzlNht0WR%g@R5CVvg#+fb)o!I*Zpe?{_+oGq*wOmCWQ=(Ra-Q9mx#6SsqWAp*-Jzb zKvuPthpH(Fn_k>2XPu!=+C{vZsF8<9p!T}U+ICbNtO}IAqxa57*L&T>M6I0ogt&l> z^3k#b#S1--$byAaU&sZL$6(6mrf)OqZXpUPbVW%T|4T}20q9SQ&;3?oRz6rSDP4`b z(}J^?+mzbp>MQDD{ziSS0K(2^V4_anz9JV|Y_5{kF3spgW%EO6JpJ(rnnIN%;xkKf zn~;I&OGHKII3ZQ&?sHlEy)jqCyfeusjPMo7sLVr~??NAknqCbuDmo+7tp8vrKykMb z(y`R)pVp}ZgTErmi+z`UyQU*G5stQRsx*J^XW}LHi_af?(bJ8DPho0b)^PT|(`_A$ zFCYCCF={BknK&KYTAVaHE{lqJs4g6B@O&^5oTPLkmqAB#T#m!l9?wz!C}#a6w)Z~Z z6jx{dsXhI(|D)x%Yu49%ioD-~4}+hCA8Q;w_A$79%n+X84jbf?Nh?kRNRzyAi{_oV zU)LqH-yRdPxp;>vBAWqH4E z(WL)}-rb<_R^B~fI%ddj?Qxhp^5_~)6-aB`D~Nd$S`LY_O&&Fme>Id)+iI>%9V-68 z3crl=15^%0qA~}ksw@^dpZ`p;m=ury;-OV63*;zQyRs4?1?8lbUL!bR+C~2Zz1O+E@6ZQW!wvv z|NLqSP0^*J2Twq@yws%~V0^h05B8BMNHv_ZZT+=d%T#i{faiqN+ut5Bc`uQPM zgO+b1uj;)i!N94RJ>5RjTNXN{gAZel|L8S4r!NT{7)_=|`}D~ElU#2er}8~UE$Q>g zZryBhOd|J-U72{1q;Lb!^3mf+H$x6(hJHn$ZJRqCp^In_PD+>6KWnCnCXA35(}g!X z;3YI1luR&*1IvESL~*aF8(?4deU`9!cxB{8IO?PpZ{O5&uY<0DIERh2wEoAP@bayv z#$WTjR*$bN8^~AGZu+85uHo&AulFjmh*pupai?o?+>rZ7@@Xk4muI}ZqH`n&<@_Vn zvT!GF-_Ngd$B7kLge~&3qC;TE=tEid(nQB*qzXI0m46ma*2d(Sd*M%@Zc{kCFcs;1 zky%U)Pyg3wm_g12J`lS4n+Sg=L)-Y`bU705E5wk&zVEZw`eM#~AHHW96@D>bz#7?- zV`xlac^e`Zh_O+B5-kO=$04{<cKUG?R&#bnF}-?4(Jq+?Ph!9g zx@s~F)Uwub>Ratv&v85!6}3{n$bYb+p!w(l8Na6cSyEx#{r7>^YvIj8L?c*{mcB^x zqnv*lu-B1ORFtrmhfe}$I8~h*3!Ys%FNQv!P2tA^wjbH f$KZHO*s&vt|9^w-6P?|#0pRK8NSwWJ?9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!ItFh?!xdN1Q+aGJ{c&& zS>O>_%)r1c48n{Iv*t(u1=&kHeO=ifbFy+6aSK)V_AxLppYn8Z42d|rc6w}vOsL55 z`t&mC&y2@JTEyg!eDiFX^k#CC!jq%>erB=yHqUP0XcDOTw6ko}L zX;EmMrq(fKk*eygEuA616;0)>@A{TK|55PV@70 z$OfzS*(VJxQev3J?yY?O=ul(v`fp}?u9z`JK3ugibK>)DyCwImZOF4d{xK%%Ks1*} zv$oa)9anR%lXIBUqYnhLmT>VOzHfNP?ZwJNZ!5$s9M08RynIvaXw>@G^T9@r9^KH1 zVy??F&uuk)bH9Y4pQY!hP58i_H6 znl-NcuCpLV6ZWU;4C zu@9exF&OZi`Bovq_m%T+WhU2kvkz@^_LpycBvqm3bMpLw8X-Or5sL>0AKE1$(k_L=_Zc=CUq#=x1-QZf)G7nHu@fmsQ1eN_N3+nTEz`4HI4Z6uVlE zJH+X&det8JU?tO?upcM4Z=cV!JV;yF>FfL5Q$M|W_2Z!P`S=}Wzp|_1^#d%e?_H`> zV@%vA$+bFVqhw9`U;TfP|5|PD{||OiYdor8P*i??|NJcb%kzT_73*7WE?Ua5hAnR2 z=7WE=PhTlJ#ZeRznjTUb;`E(wkMZrj4e|Hilz-mK>9cZHQY**5TUPw~u}k;u73KI}xAx!0m-)GVia|x^d3p~s_9gh83jA&Ra<8rM%`>U3x69t&NzbwWY}7Ar?)FK#IZ0z|d0H0EkRO w3{9;}4Xg|ebq&m|3=9_N6z8I7$jwj5OsmAL;bP(Gi$Dzwp00i_>zopr02+f8CIA2c literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_charts/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/packages/syncfusion_flutter_charts/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 0000000000000000000000000000000000000000..e71a726136a47ed24125c7efc79d68a4a01961b4 GIT binary patch literal 14800 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/syncfusion_flutter_charts/example/macos/Runner/Configs/AppInfo.xcconfig b/packages/syncfusion_flutter_charts/example/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 000000000..cf9be60ca --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = example + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.example + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2021 com.example. All rights reserved. diff --git a/packages/syncfusion_flutter_charts/example/macos/Runner/Configs/Debug.xcconfig b/packages/syncfusion_flutter_charts/example/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 000000000..36b0fd946 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/packages/syncfusion_flutter_charts/example/macos/Runner/Configs/Release.xcconfig b/packages/syncfusion_flutter_charts/example/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 000000000..dff4f4956 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/packages/syncfusion_flutter_charts/example/macos/Runner/Configs/Warnings.xcconfig b/packages/syncfusion_flutter_charts/example/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 000000000..42bcbf478 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/packages/syncfusion_flutter_charts/example/macos/Runner/DebugProfile.entitlements b/packages/syncfusion_flutter_charts/example/macos/Runner/DebugProfile.entitlements new file mode 100644 index 000000000..dddb8a30c --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/packages/syncfusion_flutter_charts/example/macos/Runner/Info.plist b/packages/syncfusion_flutter_charts/example/macos/Runner/Info.plist new file mode 100644 index 000000000..4789daa6a --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/packages/syncfusion_flutter_charts/example/macos/Runner/MainFlutterWindow.swift b/packages/syncfusion_flutter_charts/example/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 000000000..2722837ec --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/packages/syncfusion_flutter_charts/example/macos/Runner/Release.entitlements b/packages/syncfusion_flutter_charts/example/macos/Runner/Release.entitlements new file mode 100644 index 000000000..852fa1a47 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/packages/syncfusion_flutter_charts/example/web/favicon.png b/packages/syncfusion_flutter_charts/example/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_charts/example/web/icons/Icon-192.png b/packages/syncfusion_flutter_charts/example/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_charts/example/web/icons/Icon-512.png b/packages/syncfusion_flutter_charts/example/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 zcmd5=2T+s!lYZ%-(h(2@5fr2dC?F^$C=i-}R6$UX8af(!je;W5yC_|HmujSgN*6?W z3knF*TL1$|?oD*=zPbBVex*RUIKsL<(&Rj9%^UD2IK3W?2j>D?eWQgvS-HLymHo9%~|N2Q{~j za?*X-{b9JRowv_*Mh|;*-kPFn>PI;r<#kFaxFqbn?aq|PduQg=2Q;~Qc}#z)_T%x9 zE|0!a70`58wjREmAH38H1)#gof)U3g9FZ^ zF7&-0^Hy{4XHWLoC*hOG(dg~2g6&?-wqcpf{ z&3=o8vw7lMi22jCG9RQbv8H}`+}9^zSk`nlR8?Z&G2dlDy$4#+WOlg;VHqzuE=fM@ z?OI6HEJH4&tA?FVG}9>jAnq_^tlw8NbjNhfqk2rQr?h(F&WiKy03Sn=-;ZJRh~JrD zbt)zLbnabttEZ>zUiu`N*u4sfQaLE8-WDn@tHp50uD(^r-}UsUUu)`!Rl1PozAc!a z?uj|2QDQ%oV-jxUJmJycySBINSKdX{kDYRS=+`HgR2GO19fg&lZKyBFbbXhQV~v~L za^U944F1_GtuFXtvDdDNDvp<`fqy);>Vw=ncy!NB85Tw{&sT5&Ox%-p%8fTS;OzlRBwErvO+ROe?{%q-Zge=%Up|D4L#>4K@Ke=x%?*^_^P*KD zgXueMiS63!sEw@fNLB-i^F|@Oib+S4bcy{eu&e}Xvb^(mA!=U=Xr3||IpV~3K zQWzEsUeX_qBe6fky#M zzOJm5b+l;~>=sdp%i}}0h zO?B?i*W;Ndn02Y0GUUPxERG`3Bjtj!NroLoYtyVdLtl?SE*CYpf4|_${ku2s`*_)k zN=a}V8_2R5QANlxsq!1BkT6$4>9=-Ix4As@FSS;1q^#TXPrBsw>hJ}$jZ{kUHoP+H zvoYiR39gX}2OHIBYCa~6ERRPJ#V}RIIZakUmuIoLF*{sO8rAUEB9|+A#C|@kw5>u0 zBd=F!4I)Be8ycH*)X1-VPiZ+Ts8_GB;YW&ZFFUo|Sw|x~ZajLsp+_3gv((Q#N>?Jz zFBf`~p_#^${zhPIIJY~yo!7$-xi2LK%3&RkFg}Ax)3+dFCjGgKv^1;lUzQlPo^E{K zmCnrwJ)NuSaJEmueEPO@(_6h3f5mFffhkU9r8A8(JC5eOkux{gPmx_$Uv&|hyj)gN zd>JP8l2U&81@1Hc>#*su2xd{)T`Yw< zN$dSLUN}dfx)Fu`NcY}TuZ)SdviT{JHaiYgP4~@`x{&h*Hd>c3K_To9BnQi@;tuoL z%PYQo&{|IsM)_>BrF1oB~+`2_uZQ48z9!)mtUR zdfKE+b*w8cPu;F6RYJiYyV;PRBbThqHBEu_(U{(gGtjM}Zi$pL8Whx}<JwE3RM0F8x7%!!s)UJVq|TVd#hf1zVLya$;mYp(^oZQ2>=ZXU1c$}f zm|7kfk>=4KoQoQ!2&SOW5|JP1)%#55C$M(u4%SP~tHa&M+=;YsW=v(Old9L3(j)`u z2?#fK&1vtS?G6aOt@E`gZ9*qCmyvc>Ma@Q8^I4y~f3gs7*d=ATlP>1S zyF=k&6p2;7dn^8?+!wZO5r~B+;@KXFEn^&C=6ma1J7Au6y29iMIxd7#iW%=iUzq&C=$aPLa^Q zncia$@TIy6UT@69=nbty5epP>*fVW@5qbUcb2~Gg75dNd{COFLdiz3}kODn^U*=@E z0*$7u7Rl2u)=%fk4m8EK1ctR!6%Ve`e!O20L$0LkM#f+)n9h^dn{n`T*^~d+l*Qlx z$;JC0P9+en2Wlxjwq#z^a6pdnD6fJM!GV7_%8%c)kc5LZs_G^qvw)&J#6WSp< zmsd~1-(GrgjC56Pdf6#!dt^y8Rg}!#UXf)W%~PeU+kU`FeSZHk)%sFv++#Dujk-~m zFHvVJC}UBn2jN& zs!@nZ?e(iyZPNo`p1i#~wsv9l@#Z|ag3JR>0#u1iW9M1RK1iF6-RbJ4KYg?B`dET9 zyR~DjZ>%_vWYm*Z9_+^~hJ_|SNTzBKx=U0l9 z9x(J96b{`R)UVQ$I`wTJ@$_}`)_DyUNOso6=WOmQKI1e`oyYy1C&%AQU<0-`(ow)1 zT}gYdwWdm4wW6|K)LcfMe&psE0XGhMy&xS`@vLi|1#Za{D6l@#D!?nW87wcscUZgELT{Cz**^;Zb~7 z(~WFRO`~!WvyZAW-8v!6n&j*PLm9NlN}BuUN}@E^TX*4Or#dMMF?V9KBeLSiLO4?B zcE3WNIa-H{ThrlCoN=XjOGk1dT=xwwrmt<1a)mrRzg{35`@C!T?&_;Q4Ce=5=>z^*zE_c(0*vWo2_#TD<2)pLXV$FlwP}Ik74IdDQU@yhkCr5h zn5aa>B7PWy5NQ!vf7@p_qtC*{dZ8zLS;JetPkHi>IvPjtJ#ThGQD|Lq#@vE2xdl%`x4A8xOln}BiQ92Po zW;0%A?I5CQ_O`@Ad=`2BLPPbBuPUp@Hb%a_OOI}y{Rwa<#h z5^6M}s7VzE)2&I*33pA>e71d78QpF>sNK;?lj^Kl#wU7G++`N_oL4QPd-iPqBhhs| z(uVM}$ItF-onXuuXO}o$t)emBO3Hjfyil@*+GF;9j?`&67GBM;TGkLHi>@)rkS4Nj zAEk;u)`jc4C$qN6WV2dVd#q}2X6nKt&X*}I@jP%Srs%%DS92lpDY^K*Sx4`l;aql$ zt*-V{U&$DM>pdO?%jt$t=vg5|p+Rw?SPaLW zB6nvZ69$ne4Z(s$3=Rf&RX8L9PWMV*S0@R zuIk&ba#s6sxVZ51^4Kon46X^9`?DC9mEhWB3f+o4#2EXFqy0(UTc>GU| zGCJmI|Dn-dX#7|_6(fT)>&YQ0H&&JX3cTvAq(a@ydM4>5Njnuere{J8p;3?1az60* z$1E7Yyxt^ytULeokgDnRVKQw9vzHg1>X@@jM$n$HBlveIrKP5-GJq%iWH#odVwV6cF^kKX(@#%%uQVb>#T6L^mC@)%SMd4DF? zVky!~ge27>cpUP1Vi}Z32lbLV+CQy+T5Wdmva6Fg^lKb!zrg|HPU=5Qu}k;4GVH+x z%;&pN1LOce0w@9i1Mo-Y|7|z}fbch@BPp2{&R-5{GLoeu8@limQmFF zaJRR|^;kW_nw~0V^ zfTnR!Ni*;-%oSHG1yItARs~uxra|O?YJxBzLjpeE-=~TO3Dn`JL5Gz;F~O1u3|FE- zvK2Vve`ylc`a}G`gpHg58Cqc9fMoy1L}7x7T>%~b&irrNMo?np3`q;d3d;zTK>nrK zOjPS{@&74-fA7j)8uT9~*g23uGnxwIVj9HorzUX#s0pcp2?GH6i}~+kv9fWChtPa_ z@T3m+$0pbjdQw7jcnHn;Pi85hk_u2-1^}c)LNvjdam8K-XJ+KgKQ%!?2n_!#{$H|| zLO=%;hRo6EDmnOBKCL9Cg~ETU##@u^W_5joZ%Et%X_n##%JDOcsO=0VL|Lkk!VdRJ z^|~2pB@PUspT?NOeO?=0Vb+fAGc!j%Ufn-cB`s2A~W{Zj{`wqWq_-w0wr@6VrM zbzni@8c>WS!7c&|ZR$cQ;`niRw{4kG#e z70e!uX8VmP23SuJ*)#(&R=;SxGAvq|&>geL&!5Z7@0Z(No*W561n#u$Uc`f9pD70# z=sKOSK|bF~#khTTn)B28h^a1{;>EaRnHj~>i=Fnr3+Fa4 z`^+O5_itS#7kPd20rq66_wH`%?HNzWk@XFK0n;Z@Cx{kx==2L22zWH$Yg?7 zvDj|u{{+NR3JvUH({;b*$b(U5U z7(lF!1bz2%06+|-v(D?2KgwNw7( zJB#Tz+ZRi&U$i?f34m7>uTzO#+E5cbaiQ&L}UxyOQq~afbNB4EI{E04ZWg53w0A{O%qo=lF8d zf~ktGvIgf-a~zQoWf>loF7pOodrd0a2|BzwwPDV}ShauTK8*fmF6NRbO>Iw9zZU}u zw8Ya}?seBnEGQDmH#XpUUkj}N49tP<2jYwTFp!P+&Fd(%Z#yo80|5@zN(D{_pNow*&4%ql zW~&yp@scb-+Qj-EmErY+Tu=dUmf@*BoXY2&oKT8U?8?s1d}4a`Aq>7SV800m$FE~? zjmz(LY+Xx9sDX$;vU`xgw*jLw7dWOnWWCO8o|;}f>cu0Q&`0I{YudMn;P;L3R-uz# zfns_mZED_IakFBPP2r_S8XM$X)@O-xVKi4`7373Jkd5{2$M#%cRhWer3M(vr{S6>h zj{givZJ3(`yFL@``(afn&~iNx@B1|-qfYiZu?-_&Z8+R~v`d6R-}EX9IVXWO-!hL5 z*k6T#^2zAXdardU3Ao~I)4DGdAv2bx{4nOK`20rJo>rmk3S2ZDu}))8Z1m}CKigf0 z3L`3Y`{huj`xj9@`$xTZzZc3je?n^yG<8sw$`Y%}9mUsjUR%T!?k^(q)6FH6Af^b6 zlPg~IEwg0y;`t9y;#D+uz!oE4VP&Je!<#q*F?m5L5?J3i@!0J6q#eu z!RRU`-)HeqGi_UJZ(n~|PSNsv+Wgl{P-TvaUQ9j?ZCtvb^37U$sFpBrkT{7Jpd?HpIvj2!}RIq zH{9~+gErN2+}J`>Jvng2hwM`=PLNkc7pkjblKW|+Fk9rc)G1R>Ww>RC=r-|!m-u7( zc(a$9NG}w#PjWNMS~)o=i~WA&4L(YIW25@AL9+H9!?3Y}sv#MOdY{bb9j>p`{?O(P zIvb`n?_(gP2w3P#&91JX*md+bBEr%xUHMVqfB;(f?OPtMnAZ#rm5q5mh;a2f_si2_ z3oXWB?{NF(JtkAn6F(O{z@b76OIqMC$&oJ_&S|YbFJ*)3qVX_uNf5b8(!vGX19hsG z(OP>RmZp29KH9Ge2kKjKigUmOe^K_!UXP`von)PR8Qz$%=EmOB9xS(ZxE_tnyzo}7 z=6~$~9k0M~v}`w={AeqF?_)9q{m8K#6M{a&(;u;O41j)I$^T?lx5(zlebpY@NT&#N zR+1bB)-1-xj}R8uwqwf=iP1GbxBjneCC%UrSdSxK1vM^i9;bUkS#iRZw2H>rS<2<$ zNT3|sDH>{tXb=zq7XZi*K?#Zsa1h1{h5!Tq_YbKFm_*=A5-<~j63he;4`77!|LBlo zR^~tR3yxcU=gDFbshyF6>o0bdp$qmHS7D}m3;^QZq9kBBU|9$N-~oU?G5;jyFR7>z hN`IR97YZXIo@y!QgFWddJ3|0`sjFx!m))><{BI=FK%f8s literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_charts/example/web/index.html b/packages/syncfusion_flutter_charts/example/web/index.html new file mode 100644 index 000000000..1460b5e9b --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/web/index.html @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + example + + + + + + + + diff --git a/packages/syncfusion_flutter_charts/example/web/manifest.json b/packages/syncfusion_flutter_charts/example/web/manifest.json new file mode 100644 index 000000000..8c012917d --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/web/manifest.json @@ -0,0 +1,23 @@ +{ + "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" + } + ] +} diff --git a/packages/syncfusion_flutter_charts/example/windows/.gitignore b/packages/syncfusion_flutter_charts/example/windows/.gitignore new file mode 100644 index 000000000..d492d0d98 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/packages/syncfusion_flutter_charts/example/windows/CMakeLists.txt b/packages/syncfusion_flutter_charts/example/windows/CMakeLists.txt new file mode 100644 index 000000000..abf90408e --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/windows/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required(VERSION 3.15) +project(example LANGUAGES CXX) + +set(BINARY_NAME "example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() + +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build +add_subdirectory("runner") + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/packages/syncfusion_flutter_charts/example/windows/flutter/CMakeLists.txt b/packages/syncfusion_flutter_charts/example/windows/flutter/CMakeLists.txt new file mode 100644 index 000000000..b02c5485c --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.15) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/packages/syncfusion_flutter_charts/example/windows/flutter/generated_plugin_registrant.cc b/packages/syncfusion_flutter_charts/example/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000..4bfa0f3a3 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,9 @@ +// +// Generated file. Do not edit. +// + +#include "generated_plugin_registrant.h" + + +void RegisterPlugins(flutter::PluginRegistry* registry) { +} diff --git a/packages/syncfusion_flutter_charts/example/windows/flutter/generated_plugin_registrant.h b/packages/syncfusion_flutter_charts/example/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000..9846246b4 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,13 @@ +// +// Generated file. Do not edit. +// + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/syncfusion_flutter_charts/example/windows/flutter/generated_plugins.cmake b/packages/syncfusion_flutter_charts/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 000000000..4d10c2518 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,15 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/packages/syncfusion_flutter_charts/example/windows/runner/CMakeLists.txt b/packages/syncfusion_flutter_charts/example/windows/runner/CMakeLists.txt new file mode 100644 index 000000000..977e38b5d --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/windows/runner/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.15) +project(runner LANGUAGES CXX) + +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "run_loop.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) +apply_standard_settings(${BINARY_NAME}) +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/packages/syncfusion_flutter_charts/example/windows/runner/Runner.rc b/packages/syncfusion_flutter_charts/example/windows/runner/Runner.rc new file mode 100644 index 000000000..51812dcd4 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "A new Flutter project." "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2021 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "example.exe" "\0" + VALUE "ProductName", "example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/packages/syncfusion_flutter_charts/example/windows/runner/flutter_window.cpp b/packages/syncfusion_flutter_charts/example/windows/runner/flutter_window.cpp new file mode 100644 index 000000000..c42272304 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/windows/runner/flutter_window.cpp @@ -0,0 +1,64 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(RunLoop* run_loop, + const flutter::DartProject& project) + : run_loop_(run_loop), project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + run_loop_->RegisterFlutterInstance(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + run_loop_->UnregisterFlutterInstance(flutter_controller_->engine()); + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opporutunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/packages/syncfusion_flutter_charts/example/windows/runner/flutter_window.h b/packages/syncfusion_flutter_charts/example/windows/runner/flutter_window.h new file mode 100644 index 000000000..b663ddd50 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/windows/runner/flutter_window.h @@ -0,0 +1,39 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "run_loop.h" +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow driven by the |run_loop|, hosting a + // Flutter view running |project|. + explicit FlutterWindow(RunLoop* run_loop, + const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The run loop driving events for this window. + RunLoop* run_loop_; + + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/packages/syncfusion_flutter_charts/example/windows/runner/main.cpp b/packages/syncfusion_flutter_charts/example/windows/runner/main.cpp new file mode 100644 index 000000000..b637809bd --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/windows/runner/main.cpp @@ -0,0 +1,42 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "run_loop.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + RunLoop run_loop; + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(&run_loop, project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + run_loop.Run(); + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/packages/syncfusion_flutter_charts/example/windows/runner/resource.h b/packages/syncfusion_flutter_charts/example/windows/runner/resource.h new file mode 100644 index 000000000..66a65d1e4 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/packages/syncfusion_flutter_charts/example/windows/runner/resources/app_icon.ico b/packages/syncfusion_flutter_charts/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_charts/example/windows/runner/run_loop.cpp b/packages/syncfusion_flutter_charts/example/windows/runner/run_loop.cpp new file mode 100644 index 000000000..2d6636ab6 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/windows/runner/run_loop.cpp @@ -0,0 +1,66 @@ +#include "run_loop.h" + +#include + +#include + +RunLoop::RunLoop() {} + +RunLoop::~RunLoop() {} + +void RunLoop::Run() { + bool keep_running = true; + TimePoint next_flutter_event_time = TimePoint::clock::now(); + while (keep_running) { + std::chrono::nanoseconds wait_duration = + std::max(std::chrono::nanoseconds(0), + next_flutter_event_time - TimePoint::clock::now()); + ::MsgWaitForMultipleObjects( + 0, nullptr, FALSE, static_cast(wait_duration.count() / 1000), + QS_ALLINPUT); + bool processed_events = false; + MSG message; + // All pending Windows messages must be processed; MsgWaitForMultipleObjects + // won't return again for items left in the queue after PeekMessage. + while (::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) { + processed_events = true; + if (message.message == WM_QUIT) { + keep_running = false; + break; + } + ::TranslateMessage(&message); + ::DispatchMessage(&message); + // Allow Flutter to process messages each time a Windows message is + // processed, to prevent starvation. + next_flutter_event_time = + std::min(next_flutter_event_time, ProcessFlutterMessages()); + } + // If the PeekMessage loop didn't run, process Flutter messages. + if (!processed_events) { + next_flutter_event_time = + std::min(next_flutter_event_time, ProcessFlutterMessages()); + } + } +} + +void RunLoop::RegisterFlutterInstance( + flutter::FlutterEngine* flutter_instance) { + flutter_instances_.insert(flutter_instance); +} + +void RunLoop::UnregisterFlutterInstance( + flutter::FlutterEngine* flutter_instance) { + flutter_instances_.erase(flutter_instance); +} + +RunLoop::TimePoint RunLoop::ProcessFlutterMessages() { + TimePoint next_event_time = TimePoint::max(); + for (auto instance : flutter_instances_) { + std::chrono::nanoseconds wait_duration = instance->ProcessMessages(); + if (wait_duration != std::chrono::nanoseconds::max()) { + next_event_time = + std::min(next_event_time, TimePoint::clock::now() + wait_duration); + } + } + return next_event_time; +} diff --git a/packages/syncfusion_flutter_charts/example/windows/runner/run_loop.h b/packages/syncfusion_flutter_charts/example/windows/runner/run_loop.h new file mode 100644 index 000000000..000d36246 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/windows/runner/run_loop.h @@ -0,0 +1,40 @@ +#ifndef RUNNER_RUN_LOOP_H_ +#define RUNNER_RUN_LOOP_H_ + +#include + +#include +#include + +// A runloop that will service events for Flutter instances as well +// as native messages. +class RunLoop { + public: + RunLoop(); + ~RunLoop(); + + // Prevent copying + RunLoop(RunLoop const&) = delete; + RunLoop& operator=(RunLoop const&) = delete; + + // Runs the run loop until the application quits. + void Run(); + + // Registers the given Flutter instance for event servicing. + void RegisterFlutterInstance( + flutter::FlutterEngine* flutter_instance); + + // Unregisters the given Flutter instance from event servicing. + void UnregisterFlutterInstance( + flutter::FlutterEngine* flutter_instance); + + private: + using TimePoint = std::chrono::steady_clock::time_point; + + // Processes all currently pending messages for registered Flutter instances. + TimePoint ProcessFlutterMessages(); + + std::set flutter_instances_; +}; + +#endif // RUNNER_RUN_LOOP_H_ diff --git a/packages/syncfusion_flutter_charts/example/windows/runner/runner.exe.manifest b/packages/syncfusion_flutter_charts/example/windows/runner/runner.exe.manifest new file mode 100644 index 000000000..c977c4a42 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/packages/syncfusion_flutter_charts/example/windows/runner/utils.cpp b/packages/syncfusion_flutter_charts/example/windows/runner/utils.cpp new file mode 100644 index 000000000..d19bdbbcc --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/windows/runner/utils.cpp @@ -0,0 +1,64 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/packages/syncfusion_flutter_charts/example/windows/runner/utils.h b/packages/syncfusion_flutter_charts/example/windows/runner/utils.h new file mode 100644 index 000000000..3879d5475 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/packages/syncfusion_flutter_charts/example/windows/runner/win32_window.cpp b/packages/syncfusion_flutter_charts/example/windows/runner/win32_window.cpp new file mode 100644 index 000000000..c10f08dc7 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/windows/runner/win32_window.cpp @@ -0,0 +1,245 @@ +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/packages/syncfusion_flutter_charts/example/windows/runner/win32_window.h b/packages/syncfusion_flutter_charts/example/windows/runner/win32_window.h new file mode 100644 index 000000000..17ba43112 --- /dev/null +++ b/packages/syncfusion_flutter_charts/example/windows/runner/win32_window.h @@ -0,0 +1,98 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/syncfusion_flutter_charts/lib/charts.dart b/packages/syncfusion_flutter_charts/lib/charts.dart index e04941b7d..031704fb5 100644 --- a/packages/syncfusion_flutter_charts/lib/charts.dart +++ b/packages/syncfusion_flutter_charts/lib/charts.dart @@ -3,6 +3,8 @@ /// /// To use, import `package:syncfusion_flutter_charts/charts.dart`. /// +/// {@youtube 560 315 https://www.youtube.com/watch?v=JAAnmOfoqg8} +/// /// See also: /// * [Syncfusion Flutter Charts product page](https://www.syncfusion.com/flutter-widgets/flutter-charts) /// * [User guide documentation](https://help.syncfusion.com/flutter/chart/overview) @@ -23,6 +25,8 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter/foundation.dart'; +//ignore: implementation_imports +import 'package:flutter/src/foundation/platform.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:intl/intl.dart' show DateFormat; import 'package:intl/intl.dart' show NumberFormat; @@ -198,12 +202,13 @@ part './src/chart/utils/helper.dart'; // export common library part './src/common/common.dart'; part './src/common/event_args.dart'; +part './src/common/rendering_details.dart'; part './src/common/legend/legend.dart'; part './src/common/legend/renderer.dart'; part './src/common/series/chart_series.dart'; part './src/common/template/rendering.dart'; -part './src/common/user_interaction/selection.dart'; part './src/common/user_interaction/selection_behavior.dart'; part './src/common/user_interaction/tooltip.dart'; part './src/common/utils/enum.dart'; part './src/common/utils/helper.dart'; +part './src/common/utils/typedef.dart'; \ No newline at end of file diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/annotation/annotation_settings.dart b/packages/syncfusion_flutter_charts/lib/src/chart/annotation/annotation_settings.dart index 331e5543c..2c51306e3 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/annotation/annotation_settings.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/annotation/annotation_settings.dart @@ -11,6 +11,7 @@ part of charts; /// /// Provides the options x, y, coordinateUnit, and widget to customize the cartesian chart annotation. /// +@immutable class CartesianChartAnnotation { /// Creating an argument constructor of CartesianChartAnnotation class. const CartesianChartAnnotation( @@ -254,4 +255,41 @@ class CartesianChartAnnotation { ///} ///``` final ChartAlignment verticalAlignment; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is CartesianChartAnnotation && + other.widget == widget && + other.coordinateUnit == coordinateUnit && + other.region == region && + other.horizontalAlignment == horizontalAlignment && + other.verticalAlignment == verticalAlignment && + other.x == x && + other.y == y && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName; + } + + @override + int get hashCode { + final List values = [ + widget, + coordinateUnit, + region, + horizontalAlignment, + verticalAlignment, + x, + y, + xAxisName, + yAxisName + ]; + return hashList(values); + } } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/axis/axis.dart b/packages/syncfusion_flutter_charts/lib/src/chart/axis/axis.dart index 93aa044c3..1a20c11af 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/axis/axis.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/axis/axis.dart @@ -43,13 +43,13 @@ abstract class ChartAxis { bool? placeLabelsNearAxisLine, List? plotBands, this.rangeController, - double? maximumLabelWidth, - double? labelsExtent, - int? autoScrollingDelta, + this.maximumLabelWidth, + this.labelsExtent, + this.autoScrollingDelta, AutoScrollingMode? autoScrollingMode, }) : isVisible = isVisible ?? true, anchorRangeToVisiblePoints = anchorRangeToVisiblePoints ?? true, - interactiveTooltip = interactiveTooltip ?? InteractiveTooltip(), + interactiveTooltip = interactiveTooltip ?? const InteractiveTooltip(), isInversed = isInversed ?? false, plotOffset = plotOffset ?? 0, placeLabelsNearAxisLine = placeLabelsNearAxisLine ?? true, @@ -70,19 +70,16 @@ abstract class ChartAxis { fontWeight: FontWeight.normal, fontFamily: 'Normal'), title = title ?? AxisTitle(), - axisLine = axisLine ?? AxisLine(), - majorTickLines = majorTickLines ?? MajorTickLines(), - minorTickLines = minorTickLines ?? MinorTickLines(), - majorGridLines = majorGridLines ?? MajorGridLines(), - minorGridLines = minorGridLines ?? MinorGridLines(), + axisLine = axisLine ?? const AxisLine(), + majorTickLines = majorTickLines ?? const MajorTickLines(), + minorTickLines = minorTickLines ?? const MinorTickLines(), + majorGridLines = majorGridLines ?? const MajorGridLines(), + minorGridLines = minorGridLines ?? const MinorGridLines(), edgeLabelPlacement = edgeLabelPlacement ?? EdgeLabelPlacement.none, zoomFactor = zoomFactor ?? 1, zoomPosition = zoomPosition ?? 0, enableAutoIntervalOnZooming = enableAutoIntervalOnZooming ?? true, plotBands = plotBands ?? [], - maximumLabelWidth = maximumLabelWidth, - labelsExtent = labelsExtent, - autoScrollingDelta = autoScrollingDelta, autoScrollingMode = autoScrollingMode ?? AutoScrollingMode.end; ///Toggles the visibility of the axis. @@ -148,7 +145,7 @@ abstract class ChartAxis { ///Widget build(BuildContext context) { /// return Container( /// child: SfCartesianChart( - /// primaryXAxis: NumericAxis(majorTickLines: MajorTickLines(width: 2)), + /// primaryXAxis: NumericAxis(majorTickLines: const MajorTickLines(width: 2)), /// )); ///} ///``` @@ -157,7 +154,7 @@ abstract class ChartAxis { ///Customizes the appearance of the minor tick lines. /// /// Minor ticks are small lines - ///used to indicate the minor intervals between a major interval + ///used to indicate the minor intervals between a major interval. /// ///```dart ///Widget build(BuildContext context) { @@ -380,7 +377,7 @@ abstract class ChartAxis { ///Alignment of the labels. /// - ///Axis labels can be placed either start or + ///Axis labels can be placed either to the start, ///end or center of the grid lines. /// ///Defaults to `LabelAlignment.start`. @@ -542,7 +539,7 @@ abstract class ChartAxis { ///``` final InteractiveTooltip interactiveTooltip; - ///Customize to place the axis crossing on another axis based on the value + ///Customization to place the axis crossing on another axis based on the value /// ///```dart ///Widget build(BuildContext context) { @@ -733,6 +730,7 @@ abstract class ChartAxis { /// /// Provides options for label style, label size, text, and value to customize the appearance. /// +@immutable class AxisLabel { /// Creating an argument constructor of AxisLabel class. AxisLabel(this.labelStyle, this.labelSize, this.text, this.value, @@ -768,6 +766,37 @@ class AxisLabel { /// Holds the value of the visible range of the axis. num value; + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is AxisLabel && + other.labelStyle == labelStyle && + other.labelSize == labelSize && + other.text == text && + other.trimmedText == trimmedText && + other.renderText == renderText && + other.value == value; + } + + @override + int get hashCode { + final List values = [ + labelStyle, + labelSize, + text, + trimmedText, + renderText, + value + ]; + return hashList(values); + } + List? _labelCollection; int _index = 1; @@ -802,7 +831,7 @@ class MajorTickLines { /// return Container( /// child: SfCartesianChart( /// primaryXAxis: NumericAxis( - /// majorTickLines: MajorTickLines( + /// majorTickLines: const MajorTickLines( /// size: 6 /// ) /// ), @@ -820,7 +849,7 @@ class MajorTickLines { /// return Container( /// child: SfCartesianChart( /// primaryXAxis: NumericAxis( - /// majorTickLines: MajorTickLines( + /// majorTickLines: const MajorTickLines( /// width: 2 /// ) /// ), @@ -837,7 +866,7 @@ class MajorTickLines { /// return Container( /// child: SfCartesianChart( /// primaryXAxis: NumericAxis( - /// majorTickLines: MajorTickLines( + /// majorTickLines: const MajorTickLines( /// color: Colors.black /// ) /// ), @@ -845,6 +874,27 @@ class MajorTickLines { ///} ///``` final Color? color; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is MajorTickLines && + other.size == size && + other.width == width && + other.color == color; + } + + @override + int get hashCode { + final List values = [size, width, color]; + return hashList(values); + } } /// This class has the properties of minor tick lines. @@ -854,6 +904,7 @@ class MajorTickLines { /// /// Provides the color option to change the [color] of the tick line for the customization. /// +@immutable class MinorTickLines { /// Creating an argument constructor of MinorTickLines class. const MinorTickLines({this.size = 3, this.width = 0.7, this.color}); @@ -914,6 +965,27 @@ class MinorTickLines { ///} ///``` final Color? color; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is MinorTickLines && + other.size == size && + other.width == width && + other.color == color; + } + + @override + int get hashCode { + final List values = [size, width, color]; + return hashList(values); + } } /// Customizes the major grid lines. @@ -925,6 +997,7 @@ class MinorTickLines { /// /// Provides options for [color], [width], and [dashArray] to customize the appearance. /// +@immutable class MajorGridLines { /// Creating an argument constructor of MajorGridLines class. const MajorGridLines({this.width = 0.7, this.color, this.dashArray}); @@ -979,6 +1052,27 @@ class MajorGridLines { ///} ///``` final Color? color; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is MajorGridLines && + other.dashArray == dashArray && + other.width == width && + other.color == color; + } + + @override + int get hashCode { + final List values = [dashArray, width, color]; + return hashList(values); + } } /// Customizes the minor grid lines. @@ -1048,6 +1142,27 @@ class MinorGridLines { ///} ///``` final Color? color; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is MinorGridLines && + other.dashArray == dashArray && + other.width == width && + other.color == color; + } + + @override + int get hashCode { + final List values = [dashArray, width, color]; + return hashList(values); + } } /// This class holds the property of the axis title. @@ -1132,6 +1247,27 @@ class AxisTitle { ///} ///``` final ChartAlignment alignment; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is AxisTitle && + other.text == text && + other.textStyle == textStyle && + other.alignment == alignment; + } + + @override + int get hashCode { + final List values = [text, textStyle, alignment]; + return hashList(values); + } } /// This class consists of axis line properties. @@ -1197,12 +1333,33 @@ class AxisLine { ///} ///``` final List? dashArray; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is AxisLine && + other.dashArray == dashArray && + other.width == width && + other.color == color; + } + + @override + int get hashCode { + final List values = [dashArray, width, color]; + return hashList(values); + } } ///calculate visible range based on min, max values class _VisibleRange { _VisibleRange(dynamic min, dynamic max) { - if (min < max) { + if ((min < max) == true) { minimum = min; maximum = max; } else { @@ -1240,6 +1397,7 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { late ChartAxis _axis; ChartAxis? _oldAxis; late SfCartesianChartState _chartState; + _RenderingDetails get _renderingDetails => _chartState._renderingDetails; late SfCartesianChart _chart; //ignore: prefer_final_fields bool _isStack100 = false; @@ -1298,6 +1456,8 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { num? _visibleMinimum, _visibleMaximum; + int? _scrollingDelta; + @override Color? getAxisLineColor(ChartAxis axis) => axis.axisLine.color; @override @@ -1373,7 +1533,7 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { final _CustomPaintStyle paintStyle = _CustomPaintStyle( axisRenderer.getAxisLineWidth(axis), axisRenderer.getAxisLineColor(axis) ?? - _chartState._chartTheme.axisLineColor, + _renderingDetails.chartTheme.axisLineColor, PaintingStyle.stroke); _drawDashedPath(canvas, paintStyle, Offset(rect.left, rect.top), Offset(rect.left + rect.width, rect.top), axis.axisLine.dashArray); @@ -1394,7 +1554,7 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { final _CustomPaintStyle paintStyle = _CustomPaintStyle( axisRenderer.getAxisLineWidth(axis), axisRenderer.getAxisLineColor(axis) ?? - _chartState._chartTheme.axisLineColor, + _renderingDetails.chartTheme.axisLineColor, PaintingStyle.stroke); _drawDashedPath(canvas, paintStyle, Offset(rect.left, rect.top), Offset(rect.left, rect.top + rect.height), axis.axisLine.dashArray); @@ -1445,9 +1605,9 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { } if (axisBounds.left.roundToDouble() <= pointX && axisBounds.right.roundToDouble() >= pointX) { - if (axis.majorGridLines.width > 0 && + if ((axis.majorGridLines.width > 0) == true && renderType == 'outside' && - (axis.plotOffset > 0 || + ((axis.plotOffset > 0) == true || (i != 0 && (isBetweenTicks ? i != length - 1 : i != length)) || (axisBounds.left <= pointX && @@ -1461,7 +1621,8 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { i, chart); } - if (axis.minorGridLines.width > 0 || axis.minorTickLines.width > 0) { + if ((axis.minorGridLines.width > 0) == true || + (axis.minorTickLines.width > 0) == true) { num? nextValue = isBetweenTicks ? (tempInterval + axisRenderer._visibleRange!.interval) : i == length - 1 @@ -1477,7 +1638,7 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { } } } - if (axis.majorTickLines.width > 0 && + if (axis.majorTickLines.width > 0 == true && (axisBounds.left <= pointX && axisBounds.right.roundToDouble() >= pointX) && renderType == axis.tickPosition.toString().split('.')[1]) { @@ -1486,12 +1647,12 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { _CustomPaintStyle( axisRenderer.getAxisMajorTickWidth(axis, i), axisRenderer.getAxisMajorTickColor(axis, i) ?? - _chartState._chartTheme.majorTickLineColor, + _renderingDetails.chartTheme.majorTickLineColor, PaintingStyle.stroke), Offset(pointX, pointY), Offset( pointX, - !axis.opposedPosition + axis.opposedPosition == false ? (axisRenderer._isInsideTickPosition! ? pointY - ticks.size : pointY + ticks.size) @@ -1515,7 +1676,7 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { final _CustomPaintStyle paintStyle = _CustomPaintStyle( axisRenderer.getAxisMajorGridWidth(axisRenderer._axis, index), axisRenderer.getAxisMajorGridColor(axisRenderer._axis, index) ?? - _chartState._chartTheme.majorGridLineColor, + _renderingDetails.chartTheme.majorGridLineColor, PaintingStyle.stroke); _drawDashedPath( canvas, @@ -1559,7 +1720,7 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { axisRenderer._axis, index, i), axisRenderer.getAxisMinorGridColor( axisRenderer._axis, index, i) ?? - _chartState._chartTheme.minorGridLineColor, + _renderingDetails.chartTheme.minorGridLineColor, PaintingStyle.stroke), Offset(position, _chartState._chartAxis._axisClipRect.top), Offset( @@ -1578,7 +1739,7 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { _CustomPaintStyle( axisRenderer.getAxisMinorTickWidth(axis, index, i), axisRenderer.getAxisMinorTickColor(axis, index, i) ?? - _chartState._chartTheme.minorTickLineColor, + _renderingDetails.chartTheme.minorTickLineColor, PaintingStyle.stroke), Offset(position, pointY), Offset( @@ -1638,13 +1799,13 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { : pointY; } if (pointY >= axisBounds.top && pointY <= axisBounds.bottom) { - if (axis.majorGridLines.width > 0 && + if (axis.majorGridLines.width > 0 == true && renderType == 'outside' && - (axis.plotOffset > 0 || + (axis.plotOffset > 0 == true || ((i == 0 || i == length - 1) && chart.plotAreaBorderWidth == 0) || - (((i == 0 && !axis.opposedPosition) || - (i == length - 1 && axis.opposedPosition)) && + (((i == 0 && axis.opposedPosition == false) || + (i == length - 1 && axis.opposedPosition == true)) && axis.axisLine.width == 0) || (axisBounds.top < pointY - axis.majorGridLines.width && axisBounds.bottom > pointY + axis.majorGridLines.width))) { @@ -1656,22 +1817,23 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { i, chart); } - if (axis.minorGridLines.width > 0 || axis.minorTickLines.width > 0) { + if (axis.minorGridLines.width > 0 == true || + axis.minorTickLines.width > 0 == true) { axisRenderer.drawVerticalAxesMinorTickLines(canvas, axisRenderer, tempInterval, axisBounds, i, chart, renderType!); } - if (axis.majorTickLines.width > 0 && + if (axis.majorTickLines.width > 0 == true && renderType == axis.tickPosition.toString().split('.')[1]) { _drawDashedPath( canvas, _CustomPaintStyle( axisRenderer.getAxisMajorTickWidth(axis, i), axisRenderer.getAxisMajorTickColor(axis, i) ?? - _chartState._chartTheme.majorTickLineColor, + _renderingDetails.chartTheme.majorTickLineColor, PaintingStyle.stroke), Offset(pointX, pointY), Offset( - !axis.opposedPosition + axis.opposedPosition == false ? (axisRenderer._isInsideTickPosition! ? pointX + axis.majorTickLines.size : pointX - axis.majorTickLines.size) @@ -1696,7 +1858,7 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { final _CustomPaintStyle paintStyle = _CustomPaintStyle( axisRenderer.getAxisMajorGridWidth(axisRenderer._axis, index), axisRenderer.getAxisMajorGridColor(axisRenderer._axis, index) ?? - _chartState._chartTheme.majorGridLineColor, + _renderingDetails.chartTheme.majorGridLineColor, PaintingStyle.stroke); if (_chartState._chartAxis._primaryXAxisRenderer._xAxisStart != Offset(_chartState._chartAxis._axisClipRect.left, point.dy) && @@ -1748,7 +1910,7 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { _CustomPaintStyle( axisRenderer.getAxisMinorGridWidth(axis, index, i), axisRenderer.getAxisMinorGridColor(axis, index, i) ?? - _chartState._chartTheme.minorGridLineColor, + _renderingDetails.chartTheme.minorGridLineColor, PaintingStyle.stroke), Offset(_chartState._chartAxis._axisClipRect.left, position), Offset( @@ -1764,7 +1926,7 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { _CustomPaintStyle( axisRenderer.getAxisMinorTickWidth(axis, index, i), axisRenderer.getAxisMinorTickColor(axis, index, i) ?? - _chartState._chartTheme.minorTickLineColor, + _renderingDetails.chartTheme.minorTickLineColor, PaintingStyle.stroke), Offset(rect.left, position), Offset( @@ -1806,7 +1968,7 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { textStyle = _getTextStyle( textStyle: textStyle, fontColor: - textStyle.color ?? _chartState._chartTheme.axisLabelColor); + textStyle.color ?? _renderingDetails.chartTheme.axisLabelColor); tempInterval = label.value.toDouble(); angle = axisRenderer.getAxisLabelAngle(axisRenderer, labelText, i); @@ -1925,7 +2087,7 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { textStyle = _getTextStyle( textStyle: textStyle, fontColor: - textStyle.color ?? _chartState._chartTheme.axisLabelColor); + textStyle.color ?? _renderingDetails.chartTheme.axisLabelColor); tempInterval = visibleLabels[i].value.toDouble(); final Size textSize = measureText(labelText, textStyle, 0); pointY = (_valueToCoefficient(tempInterval, axisRenderer) * @@ -2022,7 +2184,7 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { final Rect bounds = axisRenderer._bounds; final ChartAxis axis = axisRenderer._axis; textSize ??= const Size(0, 0); - if (oldAxisRenderer._visibleRange!.minimum > value) { + if (oldAxisRenderer._visibleRange!.minimum > value == false) { location = axisRenderer._orientation == AxisOrientation.vertical ? (axis.isInversed ? ((bounds.top + bounds.height) - @@ -2043,7 +2205,7 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { : ((_valueToCoefficient(value, oldAxisRenderer) * bounds.width) - bounds.left) .roundToDouble()); - } else if (oldAxisRenderer._visibleRange!.maximum < value) { + } else if (oldAxisRenderer._visibleRange!.maximum < value == false) { location = axisRenderer._orientation == AxisOrientation.vertical ? (axis.isInversed ? (bounds.bottom - @@ -2250,7 +2412,7 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { TextStyle style = axis.title.textStyle; style = _getTextStyle( textStyle: style, - fontColor: style.color ?? _chartState._chartTheme.axisTitleColor); + fontColor: style.color ?? _renderingDetails.chartTheme.axisTitleColor); final Size textSize = measureText(title, style); double top; if (axis.labelPosition == ChartDataLabelPosition.inside) { @@ -2313,7 +2475,7 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { TextStyle style = axis.title.textStyle; style = _getTextStyle( textStyle: style, - fontColor: style.color ?? _chartState._chartTheme.axisTitleColor); + fontColor: style.color ?? _renderingDetails.chartTheme.axisTitleColor); final Size textSize = measureText(title, style); double left; if (axis.labelPosition == ChartDataLabelPosition.inside) { @@ -2916,6 +3078,10 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { oldAxisRenderer._axis.zoomPosition != axis.zoomPosition ? axis.zoomPosition : axisRenderer._zoomPosition; + if (axisRenderer._axis.autoScrollingDelta == + oldAxisRenderer._axis.autoScrollingDelta) { + axisRenderer._scrollingDelta = oldAxisRenderer._scrollingDelta; + } } final _VisibleRange baseRange = axisRenderer._visibleRange!; @@ -2950,14 +3116,18 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { } else { didUpdateAxis = false; } - for (final zoomedAxisRenderer in _chartState._zoomedAxisRendererStates) { + for (final ChartAxisRenderer zoomedAxisRenderer + in _chartState._zoomedAxisRendererStates) { if (zoomedAxisRenderer._name == _name) { if (didUpdateAxis) { zoomedAxisRenderer._zoomFactor = _zoomFactor; zoomedAxisRenderer._zoomPosition = _zoomPosition; } else { - axisRenderer._zoomFactor = zoomedAxisRenderer._zoomFactor; - axisRenderer._zoomPosition = zoomedAxisRenderer._zoomPosition; + if (_axis.autoScrollingDelta == null || + _scrollingDelta != zoomedAxisRenderer._visibleRange!.delta) { + axisRenderer._zoomFactor = zoomedAxisRenderer._zoomFactor; + axisRenderer._zoomPosition = zoomedAxisRenderer._zoomPosition; + } } break; } @@ -2966,11 +3136,12 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { /// To provide chart changes to range controller void _setRangeControllerValues(ChartAxisRenderer _axisRenderer) { - if (_axisRenderer is DateTimeAxisRenderer) { + if (_axisRenderer is DateTimeAxisRenderer || + _axisRenderer is DateTimeCategoryAxisRenderer) { _axis.rangeController!.start = DateTime.fromMillisecondsSinceEpoch( - _axisRenderer._visibleRange!.minimum); + _axisRenderer._visibleRange!.minimum.toInt()); _axis.rangeController!.end = DateTime.fromMillisecondsSinceEpoch( - _axisRenderer._visibleRange!.maximum); + _axisRenderer._visibleRange!.maximum.toInt()); } else { _axis.rangeController!.start = _axisRenderer._visibleRange!.minimum; _axis.rangeController!.end = _axisRenderer._visibleRange!.maximum; @@ -2979,7 +3150,10 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { /// To change chart based on range controller void _updateRangeControllerValues(ChartAxisRenderer _axisRenderer) { - if (_axisRenderer is DateTimeAxisRenderer) { + _chartState._zoomProgress = false; + _chartState._isRedrawByZoomPan = false; + if (_axisRenderer is DateTimeAxisRenderer || + _axisRenderer is DateTimeCategoryAxisRenderer) { _axisRenderer._rangeMinimum = _axis.rangeController!.start.millisecondsSinceEpoch; _axisRenderer._rangeMaximum = @@ -2990,9 +3164,48 @@ abstract class ChartAxisRenderer with _CustomizeAxisElements { } } + void _setOldRangeFromRangeController() { + if (!_chartState._renderingDetails.initialRender! && + _axis.rangeController != null && + !_chartState._canSetRangeController) { + final ChartAxisRenderer? oldrenderer = + _getOldAxisRenderer(this, _chartState._oldAxisRenderers); + if (oldrenderer != null) { + _visibleMinimum = _rangeMinimum = oldrenderer._rangeMinimum; + _visibleMaximum = _rangeMaximum = oldrenderer._rangeMaximum; + } + } + } + + void _setZoomValuesFromRangeController() { + if (!(_chartState._isRedrawByZoomPan || + _chartState._canSetRangeController)) { + if (_chartState._rangeChangeBySlider && + !_chartState._canSetRangeController && + _rangeMinimum != null && + _rangeMaximum != null) { + _visibleRange!.delta = _visibleRange!.maximum - _visibleRange!.minimum; + if (this is! DateTimeCategoryAxisRenderer) { + _visibleRange!.interval = this is LogarithmicAxisRenderer + ? (this as LogarithmicAxisRenderer) + .calculateLogNiceInterval(_visibleRange!.delta) + : calculateInterval(_visibleRange!, _axisSize); + } + _visibleRange!.interval = + _actualRange!.interval != null && _actualRange!.interval % 1 != 0 + ? _actualRange!.interval + : _visibleRange!.interval; + _zoomFactor = _visibleRange!.delta / (_actualRange!.delta); + _zoomPosition = (_visibleRange!.minimum - _actualRange!.minimum) / + _actualRange!.delta; + } + } + } + /// Auto scrolling feature void _updateAutoScrollingDelta( int scrollingDelta, ChartAxisRenderer _axisRenderer) { + _axisRenderer._scrollingDelta = scrollingDelta; switch (_axis.autoScrollingMode) { case AutoScrollingMode.start: final _VisibleRange autoScrollRange = _VisibleRange( diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/axis/axis_panel.dart b/packages/syncfusion_flutter_charts/lib/src/chart/axis/axis_panel.dart index ecdc90825..0b7671ea5 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/axis/axis_panel.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/axis/axis_panel.dart @@ -98,7 +98,7 @@ class _ChartAxis { } else if (targetAxisRenderer is NumericAxisRenderer) { targetAxisRenderer._calculateRangeAndInterval(_chartState, 'AxisCross'); } - if (!value.isNaN) { + if (value.isNaN == false) { currentAxisRenderer._crossValue = _updateCrossValue(value, targetAxisRenderer._visibleRange!); currentAxisRenderer._crossRange = targetAxisRenderer._visibleRange; @@ -126,9 +126,8 @@ class _ChartAxis { axisIndex++) { final dynamic axisRenderer = _verticalAxisRenderers[axisIndex]; assert( - axisRenderer._axis.interval != null - ? axisRenderer._axis.interval > 0 - : true, + !(axisRenderer._axis.interval != null) || + (axisRenderer._axis.interval > 0) == true, 'The vertical axis interval value must be greater than 0.'); axisRenderer._calculateRangeAndInterval(_chartState); _getAxisCrossingValue(axisRenderer); @@ -143,9 +142,8 @@ class _ChartAxis { final dynamic axisRenderer = _horizontalAxisRenderers[axisIndex]; _calculateLabelRotationAngle(axisRenderer); assert( - axisRenderer._axis.interval != null - ? axisRenderer._axis.interval > 0 - : true, + !(axisRenderer._axis.interval != null) || + (axisRenderer._axis.interval > 0) == true, 'The horizontal axis interval value must be greater than 0.'); axisRenderer._calculateRangeAndInterval(_chartState); _getAxisCrossingValue(axisRenderer); @@ -165,7 +163,7 @@ class _ChartAxis { titleSize = measureText(axis.title.text!, axis.title.textStyle).height + _axisPadding; } - final Rect rect = _chartState._containerRect; + final Rect rect = _chartState._renderingDetails.chartContainerRect; final int axisIndex = _getAxisIndex(axisRenderer); final double tickSize = (axisIndex == 0 && axis.tickPosition == TickPosition.inside) @@ -315,7 +313,7 @@ class _ChartAxis { /// Calculate series clip rect size void _calculateSeriesClipRect() { - final Rect containerRect = _chartState._containerRect; + final Rect containerRect = _chartState._renderingDetails.chartContainerRect; final num padding = _chartWidget.title.text.isNotEmpty ? 10 : 0; _chartState._chartAxis._axisClipRect = Rect.fromLTWH( containerRect.left + _leftSize, @@ -636,7 +634,7 @@ class _ChartAxis { final CartesianSeriesRenderer seriesRenderer = visibleSeriesRenderer[seriesIndex]; final XyDataSeries series = - seriesRenderer._series as XyDataSeries; + seriesRenderer._series as XyDataSeries; if ((axisRenderer._name != null && axisRenderer._name == series.xAxisName) || (series.xAxisName == null && @@ -689,7 +687,7 @@ class _ChartAxis { ? _verticalAxisRenderers.add(axisRenderer) : _horizontalAxisRenderers.add(axisRenderer); } - axisRenderer._oldAxis = _chartState._widgetNeedUpdate + axisRenderer._oldAxis = _chartState._renderingDetails.widgetNeedUpdate ? _getOldAxisRenderer(axisRenderer, _chartState._oldAxisRenderers) ?._axis : null; diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/axis/axis_renderer.dart b/packages/syncfusion_flutter_charts/lib/src/chart/axis/axis_renderer.dart index fe039ae3a..1d16b48b1 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/axis/axis_renderer.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/axis/axis_renderer.dart @@ -194,7 +194,7 @@ class _CartesianAxisRendererState extends State<_CartesianAxisRenderer> oldAxisRenderer = _getOldAxisRenderer( axisRenderer, widget.chartState._oldAxisRenderers); if (oldAxisRenderer != null && - (oldAxisRenderer._axis.visibleMinimum != null || + (oldAxisRenderer._axis.visibleMinimum != null && oldAxisRenderer._axis.visibleMaximum != null)) { needAnimate = axisRenderer.runtimeType == oldAxisRenderer.runtimeType && @@ -238,6 +238,7 @@ class _CartesianAxisRendererState extends State<_CartesianAxisRenderer> } if (axisRenderer is DateTimeCategoryAxisRenderer) { axisRenderer._labels.clear(); + //ignore: prefer_foreach for (final CartesianSeriesRenderer seriesRenderer in axisRenderer._seriesRenderers) { widget.chartState._chartSeries @@ -321,9 +322,10 @@ class _CartesianAxesPainter extends CustomPainter { /// To draw a plot area border of a container void _drawPlotAreaBorder(Canvas canvas) { final Rect axisClipRect = chartState._chartAxis._axisClipRect; + final _RenderingDetails renderingDetails = chartState._renderingDetails; final Paint paint = Paint(); - paint.color = - chart.plotAreaBorderColor ?? chartState._chartTheme.plotAreaBorderColor; + paint.color = chart.plotAreaBorderColor ?? + renderingDetails.chartTheme.plotAreaBorderColor; paint.style = PaintingStyle.stroke; paint.strokeWidth = chart.plotAreaBorderWidth; chart.plotAreaBorderWidth == 0 @@ -335,7 +337,7 @@ class _CartesianAxesPainter extends CustomPainter { axisClipRect, Paint() ..color = chart.plotAreaBackgroundColor ?? - chartState._chartTheme.plotAreaBackgroundColor + renderingDetails.chartTheme.plotAreaBackgroundColor ..style = PaintingStyle.fill); } @@ -400,7 +402,7 @@ class _CartesianAxesPainter extends CustomPainter { chartState._chartAxis._axisRenderersCollection[axisIndex]; final ChartAxis axis = axisRenderer._axis; axisRenderer._isInsideTickPosition = - (axis.tickPosition == TickPosition.inside) ? true : false; + axis.tickPosition == TickPosition.inside; ChartAxisRenderer? oldAxisRenderer; bool needAnimate = false; // ignore: unnecessary_null_comparison diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/axis/category_axis.dart b/packages/syncfusion_flutter_charts/lib/src/chart/axis/category_axis.dart index 24a2f6731..2017cbe91 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/axis/category_axis.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/axis/category_axis.dart @@ -5,8 +5,8 @@ part of charts; /// Category axis displays text labels instead of numbers. When the string values are bound to x values, then the x-axis /// must be initialized with CategoryAxis. /// -/// Provides the options for Label placement, arrange by index and interval are used to customize the appearance. -/// +/// Provides the options for Label placement, arrange by index and interval used to customize the appearance. +@immutable class CategoryAxis extends ChartAxis { /// Creating an argument constructor of CategoryAxis class. CategoryAxis({ @@ -41,7 +41,7 @@ class CategoryAxis extends ChartAxis { double? interval, this.visibleMinimum, this.visibleMaximum, - dynamic? crossesAt, + dynamic crossesAt, String? associatedAxisName, bool? placeLabelsNearAxisLine, List? plotBands, @@ -185,15 +185,116 @@ class CategoryAxis extends ChartAxis { ///} ///``` final double? visibleMaximum; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is CategoryAxis && + other.name == name && + other.isVisible == isVisible && + other.title == title && + other.axisLine == axisLine && + other.arrangeByIndex == arrangeByIndex && + other.rangePadding == rangePadding && + other.labelPlacement == labelPlacement && + other.edgeLabelPlacement == edgeLabelPlacement && + other.labelPosition == labelPosition && + other.tickPosition == tickPosition && + other.labelRotation == labelRotation && + other.labelIntersectAction == labelIntersectAction && + other.labelAlignment == labelAlignment && + other.isInversed == isInversed && + other.opposedPosition == opposedPosition && + other.minorTicksPerInterval == minorTicksPerInterval && + other.maximumLabels == maximumLabels && + other.majorTickLines == majorTickLines && + other.minorTickLines == minorTickLines && + other.majorGridLines == majorGridLines && + other.minorGridLines == minorGridLines && + other.labelStyle == labelStyle && + other.plotOffset == plotOffset && + other.zoomFactor == zoomFactor && + other.zoomPosition == zoomPosition && + other.interactiveTooltip == interactiveTooltip && + other.minimum == minimum && + other.maximum == maximum && + other.interval == interval && + other.visibleMinimum == visibleMinimum && + other.visibleMaximum == visibleMaximum && + other.crossesAt == crossesAt && + other.associatedAxisName == associatedAxisName && + other.placeLabelsNearAxisLine == placeLabelsNearAxisLine && + other.plotBands == plotBands && + other.desiredIntervals == desiredIntervals && + other.rangeController == rangeController && + other.maximumLabelWidth == maximumLabelWidth && + other.labelsExtent == labelsExtent && + other.autoScrollingDelta == autoScrollingDelta && + other.autoScrollingMode == autoScrollingMode; + } + + @override + int get hashCode { + final List values = [ + name, + isVisible, + title, + axisLine, + arrangeByIndex, + rangePadding, + labelPlacement, + edgeLabelPlacement, + labelPosition, + tickPosition, + labelRotation, + labelIntersectAction, + labelAlignment, + isInversed, + opposedPosition, + minorTicksPerInterval, + maximumLabels, + majorTickLines, + minorTickLines, + majorGridLines, + minorGridLines, + labelStyle, + plotOffset, + zoomFactor, + zoomPosition, + interactiveTooltip, + minimum, + maximum, + interval, + visibleMinimum, + visibleMaximum, + crossesAt, + associatedAxisName, + placeLabelsNearAxisLine, + plotBands, + desiredIntervals, + rangeController, + maximumLabelWidth, + labelsExtent, + autoScrollingDelta, + autoScrollingMode + ]; + return hashList(values); + } } /// Creates an axis renderer for Category axis class CategoryAxisRenderer extends ChartAxisRenderer { /// Creating an argument constructor of CategoryAxisRenderer class. CategoryAxisRenderer(this._categoryAxis) : super(_categoryAxis) { - _labels = []; + _labels = []; } - dynamic _labels; + late List _labels; late Rect _rect; final CategoryAxis _categoryAxis; @@ -201,12 +302,13 @@ class CategoryAxisRenderer extends ChartAxisRenderer { CartesianChartPoint point, int pointIndex, int dataLength, [bool? isXVisibleRange, bool? isYVisibleRange]) { if (_categoryAxis.arrangeByIndex) { + // ignore: unnecessary_null_comparison pointIndex < _labels.length && _labels[pointIndex] != null ? _labels[pointIndex] += ', ' + point.x : _labels.add(point.x.toString()); point.xValue = pointIndex; } else { - if (_labels.indexOf(point.x.toString()) < 0) { + if (!_labels.contains(point.x.toString())) { _labels.add(point.x.toString()); } point.xValue = _labels.indexOf(point.x.toString()); @@ -218,8 +320,10 @@ class CategoryAxisRenderer extends ChartAxisRenderer { /// Listener for range controller void _controlListener() { + _chartState._canSetRangeController = false; if (_axis.rangeController != null && !_chartState._rangeChangedByChart) { _updateRangeControllerValues(this); + _chartState._rangeChangeBySlider = true; _chartState._redrawByRangeChange(); } } @@ -233,9 +337,10 @@ class CategoryAxisRenderer extends ChartAxisRenderer { _chartState._rangeChangeBySlider = true; _axis.rangeController!.addListener(_controlListener); } - final Rect containerRect = _chartState._containerRect; + final Rect containerRect = _chartState._renderingDetails.chartContainerRect; _rect = Rect.fromLTWH(containerRect.left, containerRect.top, containerRect.width, containerRect.height); + _axisSize = Size(_rect.width, _rect.height); calculateRange(this); _calculateActualRange(); if (_actualRange != null) { @@ -250,19 +355,13 @@ class CategoryAxisRenderer extends ChartAxisRenderer { void _calculateActualRange() { if (_min != null && _max != null) { _actualRange = _VisibleRange( - _chartState._rangeChangeBySlider && - _categoryAxis.rangeController != null - ? _rangeMinimum ?? _categoryAxis.rangeController!.start - : _categoryAxis.minimum ?? _min, - _chartState._rangeChangeBySlider && - _categoryAxis.rangeController != null - ? _rangeMaximum ?? _categoryAxis.rangeController!.end - : _categoryAxis.maximum ?? _max); + _categoryAxis.minimum ?? _min, _categoryAxis.maximum ?? _max); final List seriesRenderers = _seriesRenderers; CartesianSeriesRenderer seriesRenderer; for (int i = 0; i < seriesRenderers.length; i++) { seriesRenderer = seriesRenderers[i]; - if (_actualRange!.maximum > seriesRenderer._dataPoints.length - 1) { + if ((_actualRange!.maximum > seriesRenderer._dataPoints.length - 1) == + true) { for (int i = _labels.length; i < _actualRange!.maximum + 1; i++) { _labels.add(i.toString()); } @@ -286,7 +385,12 @@ class CategoryAxisRenderer extends ChartAxisRenderer { /// Calculates the visible range for an axis in chart. @override void calculateVisibleRange(Size availableSize) { - _visibleRange = _VisibleRange(_actualRange!.minimum, _actualRange!.maximum); + _setOldRangeFromRangeController(); + _visibleRange = _chartState._rangeChangeBySlider && + _rangeMinimum != null && + _rangeMaximum != null + ? _VisibleRange(_rangeMinimum, _rangeMaximum) + : _VisibleRange(_actualRange!.minimum, _actualRange!.maximum); _visibleRange!.delta = _actualRange!.delta; _visibleRange!.interval = _actualRange!.interval; bool canAutoScroll = false; @@ -296,17 +400,26 @@ class CategoryAxisRenderer extends ChartAxisRenderer { canAutoScroll = true; super._updateAutoScrollingDelta(_categoryAxis.autoScrollingDelta!, this); } - if (!canAutoScroll) { + if ((!canAutoScroll || _chartState._zoomedState == true) && + !(_chartState._rangeChangeBySlider && + !_chartState._canSetRangeController)) { _setZoomFactorAndPosition(this, _chartState._zoomedAxisRendererStates); } - if (_zoomFactor < 1 || _zoomPosition > 0) { + if (_zoomFactor < 1 || + _zoomPosition > 0 || + (_axis.rangeController != null && + !_chartState._renderingDetails.initialRender!)) { _chartState._zoomProgress = true; _calculateZoomRange(this, availableSize); - if (_axis.rangeController != null) { + if (_axis.rangeController != null && + _chartState._isRedrawByZoomPan && + _chartState._canSetRangeController && + _chartState._zoomProgress) { _chartState._rangeChangedByChart = true; _setRangeControllerValues(this); } } + _setZoomValuesFromRangeController(); } /// Applies range padding @@ -375,7 +488,7 @@ class CategoryAxisRenderer extends ChartAxisRenderer { @override void generateVisibleLabels() { num tempInterval = _visibleRange!.minimum.ceil(); - num position; + int position; String labelText; _visibleLabels = []; for (; @@ -386,6 +499,7 @@ class CategoryAxisRenderer extends ChartAxisRenderer { if (position <= -1 || (_labels.isNotEmpty && position >= _labels.length)) { continue; + // ignore: unnecessary_null_comparison } else if (_labels.isNotEmpty && _labels[position] != null) { labelText = _labels[position]; } else { diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/axis/datetime_axis.dart b/packages/syncfusion_flutter_charts/lib/src/chart/axis/datetime_axis.dart index c9b396b8d..3c90b1bfa 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/axis/datetime_axis.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/axis/datetime_axis.dart @@ -8,6 +8,7 @@ part of charts; /// /// Provides the options for range padding, interval, date format for customizing the appearance. /// +@immutable class DateTimeAxis extends ChartAxis { /// Creating an argument constructor of DateTimeAxis class. DateTimeAxis({ @@ -44,7 +45,7 @@ class DateTimeAxis extends ChartAxis { double? interval, this.visibleMinimum, this.visibleMaximum, - dynamic? crossesAt, + dynamic crossesAt, String? associatedAxisName, bool? placeLabelsNearAxisLine, List? plotBands, @@ -123,7 +124,7 @@ class DateTimeAxis extends ChartAxis { /// ///Defaults to `DateTimeIntervalType.auto` /// - ///Also refer DateTimeIntervalType + ///Also refer [DateTimeIntervalType] ///```dart ///Widget build(BuildContext context) { /// return Container( @@ -204,6 +205,111 @@ class DateTimeAxis extends ChartAxis { ///} ///``` final DateTimeIntervalType autoScrollingDeltaType; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is DateTimeAxis && + other.name == name && + other.isVisible == isVisible && + other.title == title && + other.axisLine == axisLine && + other.rangePadding == rangePadding && + other.edgeLabelPlacement == edgeLabelPlacement && + other.labelPosition == labelPosition && + other.tickPosition == tickPosition && + other.labelRotation == labelRotation && + other.labelIntersectAction == labelIntersectAction && + other.labelAlignment == labelAlignment && + other.isInversed == isInversed && + other.opposedPosition == opposedPosition && + other.minorTicksPerInterval == minorTicksPerInterval && + other.maximumLabels == maximumLabels && + other.majorTickLines == majorTickLines && + other.minorTickLines == minorTickLines && + other.majorGridLines == majorGridLines && + other.minorGridLines == minorGridLines && + other.labelStyle == labelStyle && + other.plotOffset == plotOffset && + other.zoomFactor == zoomFactor && + other.zoomPosition == zoomPosition && + other.interactiveTooltip == interactiveTooltip && + other.minimum == minimum && + other.maximum == maximum && + other.interval == interval && + other.visibleMinimum == visibleMinimum && + other.visibleMaximum == visibleMaximum && + other.crossesAt == crossesAt && + other.associatedAxisName == associatedAxisName && + other.placeLabelsNearAxisLine == placeLabelsNearAxisLine && + other.plotBands == plotBands && + other.desiredIntervals == desiredIntervals && + other.rangeController == rangeController && + other.maximumLabelWidth == maximumLabelWidth && + other.labelsExtent == labelsExtent && + other.dateFormat == dateFormat && + other.intervalType == intervalType && + other.autoScrollingDelta == autoScrollingDelta && + other.enableAutoIntervalOnZooming == enableAutoIntervalOnZooming && + other.autoScrollingMode == autoScrollingMode; + } + + @override + int get hashCode { + final List values = [ + name, + isVisible, + title, + axisLine, + rangePadding, + labelIntersectAction, + labelPosition, + tickPosition, + edgeLabelPlacement, + zoomFactor, + zoomPosition, + enableAutoIntervalOnZooming, + labelRotation, + isInversed, + opposedPosition, + minorTicksPerInterval, + maximumLabels, + plotOffset, + majorTickLines, + minorTickLines, + majorGridLines, + minorGridLines, + labelStyle, + dateFormat, + intervalType, + interactiveTooltip, + labelFormat, + minimum, + maximum, + labelAlignment, + interval, + visibleMinimum, + visibleMaximum, + crossesAt, + associatedAxisName, + placeLabelsNearAxisLine, + plotBands, + rangeController, + desiredIntervals, + maximumLabelWidth, + labelsExtent, + autoScrollingDeltaType, + autoScrollingDelta, + autoScrollingMode + ]; + return hashList(values); + } } /// Creates an axis renderer for Datetime axis @@ -284,7 +390,7 @@ class DateTimeAxisRenderer extends ChartAxisRenderer { seriesRenderer._minimumY = math.min(seriesRenderer._minimumY ?? point.yValue, point.yValue); seriesRenderer._maximumY = math.max( - (seriesRenderer._maximumY ?? point.maxYValue), point.maxYValue); + seriesRenderer._maximumY ?? point.maxYValue, point.maxYValue); } } if (pointIndex >= dataLength - 1) { @@ -306,8 +412,10 @@ class DateTimeAxisRenderer extends ChartAxisRenderer { /// Listener for range controller void _controlListener() { + _chartState._canSetRangeController = false; if (_axis.rangeController != null && !_chartState._rangeChangedByChart) { _updateRangeControllerValues(this); + _chartState._rangeChangeBySlider = true; _chartState._redrawByRangeChange(); } } @@ -321,7 +429,7 @@ class DateTimeAxisRenderer extends ChartAxisRenderer { _chartState._rangeChangeBySlider = true; _axis.rangeController!.addListener(_controlListener); } - final Rect containerRect = _chartState._containerRect; + final Rect containerRect = _chartState._renderingDetails.chartContainerRect; final Rect rect = Rect.fromLTWH(containerRect.left, containerRect.top, containerRect.width, containerRect.height); _axisSize = Size(rect.width, rect.height); @@ -341,20 +449,12 @@ class DateTimeAxisRenderer extends ChartAxisRenderer { _min ??= 2717008000; _max ??= 13085008000; _actualRange = _VisibleRange( - _chartState._rangeChangeBySlider && - _dateTimeAxis.rangeController != null - ? _rangeMinimum ?? - _dateTimeAxis.rangeController!.start.millisecondsSinceEpoch - : _dateTimeAxis.minimum != null - ? _dateTimeAxis.minimum!.millisecondsSinceEpoch - : _min, - _chartState._rangeChangeBySlider && - _dateTimeAxis.rangeController != null - ? _rangeMaximum ?? - _dateTimeAxis.rangeController!.end.millisecondsSinceEpoch - : _dateTimeAxis.maximum != null - ? _dateTimeAxis.maximum!.millisecondsSinceEpoch - : _max); + _dateTimeAxis.minimum != null + ? _dateTimeAxis.minimum!.millisecondsSinceEpoch + : _min, + _dateTimeAxis.maximum != null + ? _dateTimeAxis.maximum!.millisecondsSinceEpoch + : _max); if (_actualRange!.minimum == _actualRange!.maximum) { _actualRange!.minimum = _actualRange!.minimum - 2592000000; _actualRange!.maximum = _actualRange!.maximum + 2592000000; @@ -373,33 +473,39 @@ class DateTimeAxisRenderer extends ChartAxisRenderer { case DateTimeIntervalType.years: final int year = ((dateTime.year / interval).floor() * interval).floor(); - dateTime = DateTime(year, dateTime.month, dateTime.day, 0, 0, 0); + dateTime = DateTime(year, dateTime.month, dateTime.day, 0, 0, 0, 0); break; case DateTimeIntervalType.months: final int month = ((dateTime.month / interval) * interval).floor(); - dateTime = DateTime(dateTime.year, month, dateTime.day, 0, 0, 0); + dateTime = DateTime(dateTime.year, month, dateTime.day, 0, 0, 0, 0); break; case DateTimeIntervalType.days: final int day = ((dateTime.day / interval) * interval).floor(); - dateTime = DateTime(dateTime.year, dateTime.month, day, 0, 0, 0); + dateTime = DateTime(dateTime.year, dateTime.month, day, 0, 0, 0, 0); break; case DateTimeIntervalType.hours: final int hour = ((dateTime.hour / interval).floor() * interval).floor(); - dateTime = - DateTime(dateTime.year, dateTime.month, dateTime.day, hour, 0, 0); + dateTime = DateTime( + dateTime.year, dateTime.month, dateTime.day, hour, 0, 0, 0); break; case DateTimeIntervalType.minutes: final int minute = ((dateTime.minute / interval).floor() * interval).floor(); dateTime = DateTime(dateTime.year, dateTime.month, dateTime.day, - dateTime.hour, minute, 0); + dateTime.hour, minute, 0, 0); break; case DateTimeIntervalType.seconds: final int second = ((dateTime.second / interval).floor() * interval).floor(); dateTime = DateTime(dateTime.year, dateTime.month, dateTime.day, - dateTime.hour, dateTime.minute, second); + dateTime.hour, dateTime.minute, second, 0); + break; + case DateTimeIntervalType.milliseconds: + final int millisecond = + ((dateTime.millisecond / interval).floor() * interval).floor(); + dateTime = DateTime(dateTime.year, dateTime.month, dateTime.day, + dateTime.hour, dateTime.minute, dateTime.second, millisecond, 0); break; case DateTimeIntervalType.auto: break; @@ -409,7 +515,7 @@ class DateTimeAxisRenderer extends ChartAxisRenderer { /// Increase the range interval based on actual interval type DateTime _increaseDateTimeInterval( - DateTimeAxisRenderer axisRenderer, int value, dynamic dateInterval) { + DateTimeAxisRenderer axisRenderer, int value, num dateInterval) { DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(value); axisRenderer._visibleRange!.interval = dateInterval; final bool isIntervalDecimal = dateInterval % 1 == 0; @@ -447,6 +553,16 @@ class DateTimeAxisRenderer extends ChartAxisRenderer { dateTime = DateTime(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, dateTime.second + interval, 0); break; + case DateTimeIntervalType.milliseconds: + dateTime = DateTime( + dateTime.year, + dateTime.month, + dateTime.day, + dateTime.hour, + dateTime.minute, + dateTime.second, + dateTime.millisecond + interval); + break; case DateTimeIntervalType.auto: break; } @@ -512,6 +628,16 @@ class DateTimeAxisRenderer extends ChartAxisRenderer { dateTime.second, (interval * 1000).floor()); break; + case DateTimeIntervalType.milliseconds: + dateTime = DateTime( + dateTime.year, + dateTime.month, + dateTime.day, + dateTime.hour, + dateTime.minute, + dateTime.second, + dateTime.millisecond + interval.floor()); + break; case DateTimeIntervalType.auto: break; } @@ -640,6 +766,30 @@ class DateTimeAxisRenderer extends ChartAxisRenderer { } } + /// Calculate millisecond + void _calculateMilliSecond(DateTime minimum, DateTime maximum, + ChartRangePadding rangePadding, int interval) { + final int startMilliSecond = + ((minimum.millisecond / interval) * interval).toInt(); + final int endMilliSecond = + maximum.millisecond + (minimum.millisecond - startMilliSecond).toInt(); + if (rangePadding == ChartRangePadding.round) { + _min = DateTime(minimum.year, minimum.month, minimum.day, minimum.hour, + minimum.minute, minimum.second, startMilliSecond) + .millisecondsSinceEpoch; + _max = DateTime(maximum.year, maximum.month, maximum.day, maximum.hour, + maximum.minute, maximum.second, endMilliSecond) + .millisecondsSinceEpoch; + } else { + _min = DateTime(minimum.year, minimum.month, minimum.day, minimum.hour, + minimum.minute, minimum.second, startMilliSecond + (-interval)) + .millisecondsSinceEpoch; + _max = DateTime(maximum.year, maximum.month, maximum.day, maximum.hour, + maximum.minute, maximum.second, endMilliSecond + interval) + .millisecondsSinceEpoch; + } + } + /// Applies range padding to auto, normal, additional, round, and none types. @override void applyRangePadding(_VisibleRange range, num? interval) { @@ -677,6 +827,10 @@ class DateTimeAxisRenderer extends ChartAxisRenderer { case DateTimeIntervalType.seconds: _calculateSecond(minimum, maximum, rangePadding, interval!.toInt()); break; + case DateTimeIntervalType.milliseconds: + _calculateMilliSecond( + minimum, maximum, rangePadding, interval!.toInt()); + break; case DateTimeIntervalType.auto: break; } @@ -823,6 +977,11 @@ class DateTimeAxisRenderer extends ChartAxisRenderer { scrollingDelta = Duration(seconds: _dateTimeAxis.autoScrollingDelta!) .inMilliseconds; break; + case DateTimeIntervalType.milliseconds: + scrollingDelta = + Duration(milliseconds: _dateTimeAxis.autoScrollingDelta!) + .inMilliseconds; + break; case DateTimeIntervalType.auto: scrollingDelta = _dateTimeAxis.autoScrollingDelta!; break; diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/axis/datetime_category_axis.dart b/packages/syncfusion_flutter_charts/lib/src/chart/axis/datetime_category_axis.dart index 26d20dd35..f2287aca9 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/axis/datetime_category_axis.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/axis/datetime_category_axis.dart @@ -12,6 +12,7 @@ part of charts; /// employee for a month by excluding the weekends. /// ///Provides options for label placement, interval, date format for customizing the appearance. +@immutable class DateTimeCategoryAxis extends ChartAxis { /// Creating an argument constructor of DateTimeCategoryAxis class. DateTimeCategoryAxis({ @@ -44,7 +45,7 @@ class DateTimeCategoryAxis extends ChartAxis { double? interval, this.visibleMinimum, this.visibleMaximum, - dynamic? crossesAt, + dynamic crossesAt, String? associatedAxisName, bool? placeLabelsNearAxisLine, List? plotBands, @@ -225,6 +226,109 @@ class DateTimeCategoryAxis extends ChartAxis { ///} ///``` final DateTimeIntervalType autoScrollingDeltaType; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is DateTimeCategoryAxis && + other.name == name && + other.isVisible == isVisible && + other.title == title && + other.axisLine == axisLine && + other.rangePadding == rangePadding && + other.labelPlacement == labelPlacement && + other.edgeLabelPlacement == edgeLabelPlacement && + other.labelPosition == labelPosition && + other.tickPosition == tickPosition && + other.labelRotation == labelRotation && + other.labelIntersectAction == labelIntersectAction && + other.labelAlignment == labelAlignment && + other.isInversed == isInversed && + other.opposedPosition == opposedPosition && + other.minorTicksPerInterval == minorTicksPerInterval && + other.maximumLabels == maximumLabels && + other.majorTickLines == majorTickLines && + other.minorTickLines == minorTickLines && + other.majorGridLines == majorGridLines && + other.minorGridLines == minorGridLines && + other.labelStyle == labelStyle && + other.plotOffset == plotOffset && + other.zoomFactor == zoomFactor && + other.zoomPosition == zoomPosition && + other.interactiveTooltip == interactiveTooltip && + other.minimum == minimum && + other.maximum == maximum && + other.interval == interval && + other.visibleMinimum == visibleMinimum && + other.visibleMaximum == visibleMaximum && + other.crossesAt == crossesAt && + other.associatedAxisName == associatedAxisName && + other.placeLabelsNearAxisLine == placeLabelsNearAxisLine && + other.plotBands == plotBands && + other.desiredIntervals == desiredIntervals && + other.rangeController == rangeController && + other.maximumLabelWidth == maximumLabelWidth && + other.labelsExtent == labelsExtent && + other.autoScrollingDelta == autoScrollingDelta && + other.autoScrollingMode == autoScrollingMode && + other.intervalType == intervalType && + other.dateFormat == dateFormat; + } + + @override + int get hashCode { + final List values = [ + name, + isVisible, + title, + axisLine, + rangePadding, + labelPlacement, + edgeLabelPlacement, + labelPosition, + tickPosition, + labelRotation, + labelIntersectAction, + labelAlignment, + isInversed, + opposedPosition, + minorTicksPerInterval, + maximumLabels, + majorTickLines, + minorTickLines, + majorGridLines, + minorGridLines, + labelStyle, + plotOffset, + zoomFactor, + zoomPosition, + interactiveTooltip, + minimum, + maximum, + interval, + visibleMinimum, + visibleMaximum, + crossesAt, + associatedAxisName, + placeLabelsNearAxisLine, + plotBands, + desiredIntervals, + rangeController, + maximumLabelWidth, + labelsExtent, + autoScrollingDelta, + autoScrollingMode, + intervalType, + dateFormat + ]; + return hashList(values); + } } /// Creates an axis renderer for Category axis @@ -234,7 +338,7 @@ class DateTimeCategoryAxisRenderer extends ChartAxisRenderer { : super(_dateTimeCategoryAxis) { _labels = []; } - dynamic _labels; + late List _labels; late Rect _rect; final DateTimeCategoryAxis _dateTimeCategoryAxis; late DateTimeIntervalType _actualIntervalType; @@ -274,8 +378,10 @@ class DateTimeCategoryAxisRenderer extends ChartAxisRenderer { /// Listener for range controller void _controlListener() { + _chartState._canSetRangeController = false; if (_axis.rangeController != null && !_chartState._rangeChangedByChart) { _updateRangeControllerValues(this); + _chartState._rangeChangeBySlider = true; _chartState._redrawByRangeChange(); } } @@ -289,7 +395,7 @@ class DateTimeCategoryAxisRenderer extends ChartAxisRenderer { _chartState._rangeChangeBySlider = true; _axis.rangeController!.addListener(_controlListener); } - final Rect containerRect = _chartState._containerRect; + final Rect containerRect = _chartState._renderingDetails.chartContainerRect; _rect = Rect.fromLTWH(containerRect.left, containerRect.top, containerRect.width, containerRect.height); _axisSize = Size(_rect.width, _rect.height); @@ -311,22 +417,12 @@ class DateTimeCategoryAxisRenderer extends ChartAxisRenderer { _min ??= 0; _max ??= 5; _actualRange = _VisibleRange( - _chartState._rangeChangeBySlider && - _dateTimeCategoryAxis.rangeController != null - ? _rangeMinimum ?? - _dateTimeCategoryAxis - .rangeController!.start.millisecondsSinceEpoch - : _dateTimeCategoryAxis.minimum != null - ? _getEffectiveRange(_dateTimeCategoryAxis.minimum, true) - : _min, - _chartState._rangeChangeBySlider && - _dateTimeCategoryAxis.rangeController != null - ? _rangeMaximum ?? - _dateTimeCategoryAxis - .rangeController!.end.millisecondsSinceEpoch - : _dateTimeCategoryAxis.maximum != null - ? _getEffectiveRange(_dateTimeCategoryAxis.maximum, false) - : _max); + _dateTimeCategoryAxis.minimum != null + ? _getEffectiveRange(_dateTimeCategoryAxis.minimum, true) + : _min, + _dateTimeCategoryAxis.maximum != null + ? _getEffectiveRange(_dateTimeCategoryAxis.maximum, false) + : _max); ///Below condition is for checking the min, max value is equal if ((_actualRange!.minimum == _actualRange!.maximum) && @@ -422,8 +518,12 @@ class DateTimeCategoryAxisRenderer extends ChartAxisRenderer { rangeChangedArgs.visibleMax = _visibleRange!.maximum; rangeChangedArgs.visibleInterval = _visibleRange!.interval; _chart.onActualRangeChanged!(rangeChangedArgs); - _visibleRange!.minimum = rangeChangedArgs.visibleMin; - _visibleRange!.maximum = rangeChangedArgs.visibleMax; + _visibleRange!.minimum = rangeChangedArgs.visibleMin is DateTime + ? _getEffectiveRange(rangeChangedArgs.visibleMin, true) + : rangeChangedArgs.visibleMin; + _visibleRange!.maximum = rangeChangedArgs.visibleMax is DateTime + ? _getEffectiveRange(rangeChangedArgs.visibleMax, false) + : rangeChangedArgs.visibleMax; _visibleRange!.delta = _visibleRange!.maximum - _visibleRange!.minimum; _visibleRange!.interval = rangeChangedArgs.visibleInterval; _zoomFactor = _visibleRange!.delta / (range.delta); @@ -442,7 +542,7 @@ class DateTimeCategoryAxisRenderer extends ChartAxisRenderer { @override void generateVisibleLabels() { num tempInterval = _visibleRange!.minimum.ceil(); - num position; + int position; String labelText; _visibleLabels = []; _dateTimeFormat = @@ -455,6 +555,7 @@ class DateTimeCategoryAxisRenderer extends ChartAxisRenderer { if (position <= -1 || (_labels.isNotEmpty && position >= _labels.length)) { continue; + // ignore: unnecessary_null_comparison } else if (_labels.isNotEmpty && _labels[position] != null) { labelText = _getFormattedLabel(_labels[position], _dateFormat); _labels[position] = labelText; @@ -472,9 +573,8 @@ class DateTimeCategoryAxisRenderer extends ChartAxisRenderer { .format(DateTime.fromMicrosecondsSinceEpoch(int.parse(label))); } - dynamic _getStartAndEndDate(List labels) { - final List values = [] - ..addAll(labels) + List _getStartAndEndDate(List labels) { + final List values = [...labels] ..sort((String first, String second) { return int.parse(first) < int.parse(second) ? -1 : 1; }); @@ -490,7 +590,7 @@ class DateTimeCategoryAxisRenderer extends ChartAxisRenderer { return null; } for (final String label in _labels) { - final value = int.parse(label); + final int value = int.parse(label); if (needMin) { if (value > rangeDate.microsecondsSinceEpoch) { if (!(_labels.first == label)) { diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/axis/logarithmic_axis.dart b/packages/syncfusion_flutter_charts/lib/src/chart/axis/logarithmic_axis.dart index c6c5aea21..e2b500743 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/axis/logarithmic_axis.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/axis/logarithmic_axis.dart @@ -6,6 +6,7 @@ part of charts; /// By default, the range will be calculated automatically based on the provided data. /// /// _Note:_ This is only applicable for [SfCartesianChart]. +@immutable class LogarithmicAxis extends ChartAxis { /// Creating an argument constructor of LogarithmicAxis class. LogarithmicAxis({ @@ -42,7 +43,7 @@ class LogarithmicAxis extends ChartAxis { this.visibleMinimum, this.visibleMaximum, LabelAlignment? labelAlignment, - dynamic? crossesAt, + dynamic crossesAt, String? associatedAxisName, bool? placeLabelsNearAxisLine, List? plotBands, @@ -199,6 +200,108 @@ class LogarithmicAxis extends ChartAxis { ///} ///``` final double? visibleMaximum; + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is LogarithmicAxis && + other.name == name && + other.isVisible == isVisible && + other.title == title && + other.axisLine == axisLine && + other.numberFormat == numberFormat && + other.labelFormat == labelFormat && + other.rangePadding == rangePadding && + other.logBase == logBase && + other.edgeLabelPlacement == edgeLabelPlacement && + other.labelPosition == labelPosition && + other.tickPosition == tickPosition && + other.labelRotation == labelRotation && + other.labelIntersectAction == labelIntersectAction && + other.labelAlignment == labelAlignment && + other.isInversed == isInversed && + other.opposedPosition == opposedPosition && + other.minorTicksPerInterval == minorTicksPerInterval && + other.maximumLabels == maximumLabels && + other.majorTickLines == majorTickLines && + other.minorTickLines == minorTickLines && + other.majorGridLines == majorGridLines && + other.minorGridLines == minorGridLines && + other.labelStyle == labelStyle && + other.plotOffset == plotOffset && + other.zoomFactor == zoomFactor && + other.zoomPosition == zoomPosition && + other.interactiveTooltip == interactiveTooltip && + other.minimum == minimum && + other.maximum == maximum && + other.interval == interval && + other.visibleMinimum == visibleMinimum && + other.visibleMaximum == visibleMaximum && + other.crossesAt == crossesAt && + other.associatedAxisName == associatedAxisName && + other.placeLabelsNearAxisLine == placeLabelsNearAxisLine && + other.plotBands == plotBands && + other.desiredIntervals == desiredIntervals && + other.rangeController == rangeController && + other.maximumLabelWidth == maximumLabelWidth && + other.labelsExtent == labelsExtent && + other.autoScrollingDelta == autoScrollingDelta && + other.autoScrollingMode == autoScrollingMode; + } + + @override + int get hashCode { + final List values = [ + name, + isVisible, + title, + axisLine, + numberFormat, + labelFormat, + rangePadding, + logBase, + edgeLabelPlacement, + labelPosition, + tickPosition, + labelRotation, + labelIntersectAction, + labelAlignment, + isInversed, + opposedPosition, + minorTicksPerInterval, + maximumLabels, + majorTickLines, + minorTickLines, + majorGridLines, + minorGridLines, + labelStyle, + plotOffset, + zoomFactor, + zoomPosition, + interactiveTooltip, + minimum, + maximum, + interval, + visibleMinimum, + visibleMaximum, + crossesAt, + associatedAxisName, + placeLabelsNearAxisLine, + plotBands, + desiredIntervals, + rangeController, + maximumLabelWidth, + labelsExtent, + autoScrollingDelta, + autoScrollingMode + ]; + return hashList(values); + } } /// Creates an axis renderer for Logarithmic axis @@ -272,8 +375,10 @@ class LogarithmicAxisRenderer extends ChartAxisRenderer { /// Listener for range controller void _controlListener() { + _chartState._canSetRangeController = false; if (_axis.rangeController != null && !_chartState._rangeChangedByChart) { _updateRangeControllerValues(this); + _chartState._rangeChangeBySlider = true; _chartState._redrawByRangeChange(); } } @@ -287,7 +392,7 @@ class LogarithmicAxisRenderer extends ChartAxisRenderer { _chartState._rangeChangeBySlider = true; _logarithmicAxis.rangeController!.addListener(_controlListener); } - final Rect containerRect = _chartState._containerRect; + final Rect containerRect = _chartState._renderingDetails.chartContainerRect; final Rect rect = Rect.fromLTWH(containerRect.left, containerRect.top, containerRect.width, containerRect.height); _axisSize = Size(rect.width, rect.height); @@ -342,14 +447,8 @@ class LogarithmicAxisRenderer extends ChartAxisRenderer { num logStart, logEnd; _min ??= 0; _max ??= 5; - _min = _chartState._rangeChangeBySlider && - _logarithmicAxis.rangeController != null - ? _rangeMinimum ?? _logarithmicAxis.rangeController!.start - : _logarithmicAxis.minimum ?? _min; - _max = _chartState._rangeChangeBySlider && - _logarithmicAxis.rangeController != null - ? _rangeMaximum ?? _logarithmicAxis.rangeController!.end - : _logarithmicAxis.maximum ?? _max; + _min = _logarithmicAxis.minimum ?? _min; + _max = _logarithmicAxis.maximum ?? _max; if (_axis.anchorRangeToVisiblePoints && _needCalculateYrange(_logarithmicAxis.minimum, _logarithmicAxis.maximum, _chartState, _orientation!)) { @@ -375,7 +474,7 @@ class LogarithmicAxisRenderer extends ChartAxisRenderer { /// To get the axis interval for logarithmic axis num calculateLogNiceInterval(num delta) { - final dynamic intervalDivisions = [10, 5, 2, 1]; + final List intervalDivisions = [10, 5, 2, 1]; final num actualDesiredIntervalCount = _calculateDesiredIntervalCount(_axisSize, this); num niceInterval = delta; @@ -395,7 +494,12 @@ class LogarithmicAxisRenderer extends ChartAxisRenderer { /// Calculates the visible range for an axis in chart. @override void calculateVisibleRange(Size availableSize) { - _visibleRange = _VisibleRange(_actualRange!.minimum, _actualRange!.maximum); + _setOldRangeFromRangeController(); + _visibleRange = _chartState._rangeChangeBySlider && + _rangeMinimum != null && + _rangeMaximum != null + ? _VisibleRange(_rangeMinimum, _rangeMaximum) + : _VisibleRange(_actualRange!.minimum, _actualRange!.maximum); _visibleRange!.delta = _actualRange!.delta; _visibleRange!.interval = _actualRange!.interval; bool canAutoScroll = false; @@ -406,10 +510,17 @@ class LogarithmicAxisRenderer extends ChartAxisRenderer { super._updateAutoScrollingDelta( _logarithmicAxis.autoScrollingDelta!, this); } - if (!canAutoScroll) { + if ((!canAutoScroll || _chartState._zoomedState == true) && + !(_chartState._rangeChangeBySlider && + !_chartState._canSetRangeController)) { _setZoomFactorAndPosition(this, _chartState._zoomedAxisRendererStates); } - if (_zoomFactor < 1 || _zoomPosition > 0) { + if (_zoomFactor < 1 || + _zoomPosition > 0 || + (_axis.rangeController != null && + !_chartState._renderingDetails.initialRender!) && + !(_chartState._rangeChangeBySlider || + !_chartState._canSetRangeController)) { _chartState._zoomProgress = true; _calculateZoomRange(this, availableSize); _visibleRange!.delta = _visibleRange!.maximum - _visibleRange!.minimum; @@ -420,11 +531,15 @@ class LogarithmicAxisRenderer extends ChartAxisRenderer { _visibleRange!.interval = _visibleRange!.interval.floor() == 0 ? 1 : _visibleRange!.interval.floor(); - if (_axis.rangeController != null) { + if (_axis.rangeController != null && + _chartState._isRedrawByZoomPan && + _chartState._canSetRangeController && + _chartState._zoomProgress) { _chartState._rangeChangedByChart = true; _setRangeControllerValues(this); } } + _setZoomValuesFromRangeController(); } /// Applies range padding to auto, normal, additional, round, and none types. diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/axis/numeric_axis.dart b/packages/syncfusion_flutter_charts/lib/src/chart/axis/numeric_axis.dart index a9a0f9359..e58d7decf 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/axis/numeric_axis.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/axis/numeric_axis.dart @@ -8,6 +8,7 @@ part of charts; /// Provides the options of [name], axis line, label rotation, label format, alignment and label position are /// used to customize the appearance. /// +@immutable class NumericAxis extends ChartAxis { /// Creating an argument constructor of NumericAxis class. NumericAxis({ @@ -44,7 +45,7 @@ class NumericAxis extends ChartAxis { double? interval, this.visibleMinimum, this.visibleMaximum, - dynamic? crossesAt, + dynamic crossesAt, String? associatedAxisName, bool? placeLabelsNearAxisLine, List? plotBands, @@ -201,6 +202,108 @@ class NumericAxis extends ChartAxis { ///} ///``` final int decimalPlaces; + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is NumericAxis && + other.name == name && + other.isVisible == isVisible && + other.title == title && + other.axisLine == axisLine && + other.numberFormat == numberFormat && + other.labelFormat == labelFormat && + other.rangePadding == rangePadding && + other.decimalPlaces == decimalPlaces && + other.edgeLabelPlacement == edgeLabelPlacement && + other.labelPosition == labelPosition && + other.tickPosition == tickPosition && + other.labelRotation == labelRotation && + other.labelIntersectAction == labelIntersectAction && + other.labelAlignment == labelAlignment && + other.isInversed == isInversed && + other.opposedPosition == opposedPosition && + other.minorTicksPerInterval == minorTicksPerInterval && + other.maximumLabels == maximumLabels && + other.majorTickLines == majorTickLines && + other.minorTickLines == minorTickLines && + other.majorGridLines == majorGridLines && + other.minorGridLines == minorGridLines && + other.labelStyle == labelStyle && + other.plotOffset == plotOffset && + other.zoomFactor == zoomFactor && + other.zoomPosition == zoomPosition && + other.interactiveTooltip == interactiveTooltip && + other.minimum == minimum && + other.maximum == maximum && + other.interval == interval && + other.visibleMinimum == visibleMinimum && + other.visibleMaximum == visibleMaximum && + other.crossesAt == crossesAt && + other.associatedAxisName == associatedAxisName && + other.placeLabelsNearAxisLine == placeLabelsNearAxisLine && + other.plotBands == plotBands && + other.desiredIntervals == desiredIntervals && + other.rangeController == rangeController && + other.maximumLabelWidth == maximumLabelWidth && + other.labelsExtent == labelsExtent && + other.autoScrollingDelta == autoScrollingDelta && + other.autoScrollingMode == autoScrollingMode; + } + + @override + int get hashCode { + final List values = [ + name, + isVisible, + title, + axisLine, + numberFormat, + labelFormat, + rangePadding, + decimalPlaces, + edgeLabelPlacement, + labelPosition, + tickPosition, + labelRotation, + labelIntersectAction, + labelAlignment, + isInversed, + opposedPosition, + minorTicksPerInterval, + maximumLabels, + majorTickLines, + minorTickLines, + majorGridLines, + minorGridLines, + labelStyle, + plotOffset, + zoomFactor, + zoomPosition, + interactiveTooltip, + minimum, + maximum, + interval, + visibleMinimum, + visibleMaximum, + crossesAt, + associatedAxisName, + placeLabelsNearAxisLine, + plotBands, + desiredIntervals, + rangeController, + maximumLabelWidth, + labelsExtent, + autoScrollingDelta, + autoScrollingMode + ]; + return hashList(values); + } } /// Creates an axis renderer for Numeric axis. @@ -305,8 +408,10 @@ class NumericAxisRenderer extends ChartAxisRenderer { /// Listener for range controller void _controlListener() { + _chartState._canSetRangeController = false; if (_axis.rangeController != null && !_chartState._rangeChangedByChart) { _updateRangeControllerValues(this); + _chartState._rangeChangeBySlider = true; _chartState._redrawByRangeChange(); } } @@ -320,7 +425,7 @@ class NumericAxisRenderer extends ChartAxisRenderer { _chartState._rangeChangeBySlider = true; _axis.rangeController!.addListener(_controlListener); } - final Rect containerRect = _chartState._containerRect; + final Rect containerRect = _chartState._renderingDetails.chartContainerRect; final Rect rect = Rect.fromLTWH(containerRect.left, containerRect.top, containerRect.width, containerRect.height); _axisSize = Size(rect.width, rect.height); @@ -338,13 +443,19 @@ class NumericAxisRenderer extends ChartAxisRenderer { void _calculateActualRange() { _min ??= 0; _max ??= 5; + + /// Below condition is for checking whether the min and max are equal and + /// also whether they are positive or negative in order + /// to set the min and max as zero accordingly. + if (_min == _max && _min! < 0 && _max! < 0) { + _max = 0; + } + if (_min == _max && _min! > 0 && _max! > 0) { + _min = 0; + } + _actualRange = _VisibleRange( - _chartState._rangeChangeBySlider && _numericAxis.rangeController != null - ? _rangeMinimum ?? _numericAxis.rangeController!.start - : _numericAxis.minimum ?? _min, - _chartState._rangeChangeBySlider && _numericAxis.rangeController != null - ? _rangeMaximum ?? _numericAxis.rangeController!.end - : _numericAxis.maximum ?? _max); + _numericAxis.minimum ?? _min, _numericAxis.maximum ?? _max); if (_axis.anchorRangeToVisiblePoints && _needCalculateYrange(_numericAxis.minimum, _numericAxis.maximum, _chartState, _orientation!)) { @@ -357,7 +468,7 @@ class NumericAxisRenderer extends ChartAxisRenderer { } ///Below condition is for checking the axis min value is greater than max value, then swapping min max values - else if (_actualRange!.minimum > _actualRange!.maximum) { + else if ((_actualRange!.minimum > _actualRange!.maximum) == true) { _actualRange!.minimum = _actualRange!.minimum + _actualRange!.maximum; _actualRange!.maximum = _actualRange!.minimum - _actualRange!.maximum; _actualRange!.minimum = _actualRange!.minimum - _actualRange!.maximum; @@ -422,7 +533,6 @@ class NumericAxisRenderer extends ChartAxisRenderer { void generateVisibleLabels() { num tempInterval = _visibleRange!.minimum; String text; - final String minimum = tempInterval.toString(); final num maximumVisibleRange = _visibleRange!.maximum; num interval = _visibleRange!.interval; interval = interval.toString().split('.').length >= 2 @@ -436,9 +546,8 @@ class NumericAxisRenderer extends ChartAxisRenderer { num minimumVisibleRange = tempInterval; if (minimumVisibleRange <= maximumVisibleRange && minimumVisibleRange >= _visibleRange!.minimum) { - final int fractionDigits = (minimum.split('.').length >= 2) - ? minimum.split('.')[1].toString().length - : (minimumVisibleRange.toString().split('.').length >= 2) + final int fractionDigits = + (minimumVisibleRange.toString().split('.').length >= 2) ? minimumVisibleRange.toString().split('.')[1].toString().length : 0; final int fractionDigitValue = @@ -447,9 +556,12 @@ class NumericAxisRenderer extends ChartAxisRenderer { ? minimumVisibleRange : num.tryParse( minimumVisibleRange.toStringAsFixed(fractionDigitValue))!; + if (minimumVisibleRange % 1 == 0) { + minimumVisibleRange = minimumVisibleRange.round(); + } if (minimumVisibleRange.toString().split('.').length > 1) { final String str = minimumVisibleRange.toString(); - final List? list = str.split('.'); + final List? list = str.split('.'); minimumVisibleRange = double.parse( minimumVisibleRange.toStringAsFixed(_numericAxis.decimalPlaces)); if (list != null && @@ -486,7 +598,12 @@ class NumericAxisRenderer extends ChartAxisRenderer { /// Calculates the visible range for an axis in chart. @override void calculateVisibleRange(Size availableSize) { - _visibleRange = _VisibleRange(_actualRange!.minimum, _actualRange!.maximum); + _setOldRangeFromRangeController(); + _visibleRange = _chartState._rangeChangeBySlider && + _rangeMinimum != null && + _rangeMaximum != null + ? _VisibleRange(_rangeMinimum, _rangeMaximum) + : _VisibleRange(_actualRange!.minimum, _actualRange!.maximum); _visibleRange!.delta = _actualRange!.delta; _visibleRange!.interval = _actualRange!.interval; bool canAutoScroll = false; @@ -496,21 +613,33 @@ class NumericAxisRenderer extends ChartAxisRenderer { canAutoScroll = true; super._updateAutoScrollingDelta(_numericAxis.autoScrollingDelta!, this); } - if (!canAutoScroll) { + if ((!canAutoScroll || _chartState._zoomedState == true) && + !(_chartState._rangeChangeBySlider && + !_chartState._canSetRangeController)) { _setZoomFactorAndPosition(this, _chartState._zoomedAxisRendererStates); } - if (_zoomFactor < 1 || _zoomPosition > 0) { + if (_zoomFactor < 1 || + _zoomPosition > 0 || + (_axis.rangeController != null && + !_chartState._renderingDetails.initialRender!) && + !(_chartState._rangeChangeBySlider || + !_chartState._canSetRangeController)) { _chartState._zoomProgress = true; _calculateZoomRange(this, availableSize); - _visibleRange!.interval = - _axis.enableAutoIntervalOnZooming && _chartState._zoomProgress - ? calculateInterval(_visibleRange!, _axisSize) - : _visibleRange!.interval; - if (_axis.rangeController != null) { + _visibleRange!.interval = !canAutoScroll && + _axis.enableAutoIntervalOnZooming && + _chartState._zoomProgress + ? calculateInterval(_visibleRange!, _axisSize) + : _visibleRange!.interval; + if (_axis.rangeController != null && + _chartState._isRedrawByZoomPan && + _chartState._canSetRangeController && + _chartState._zoomProgress) { _chartState._rangeChangedByChart = true; _setRangeControllerValues(this); } } + _setZoomValuesFromRangeController(); } /// Finds the interval of an axis. diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/axis/plotband.dart b/packages/syncfusion_flutter_charts/lib/src/chart/axis/plotband.dart index 4ef2c4a42..81f5cd50b 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/axis/plotband.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/axis/plotband.dart @@ -12,8 +12,10 @@ part of charts; /// Provides the property of visible, opacity, start, end, color, border color, and border width to /// customize the appearance. /// +@immutable class PlotBand { /// Creating an argument constructor of PlotBand class. + // ignore: prefer_const_constructors_in_immutables PlotBand( {this.isVisible = true, this.start, @@ -564,6 +566,72 @@ class PlotBand { /// )); ///} final String? horizontalTextPadding; + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is PlotBand && + other.isVisible == isVisible && + other.start == start && + other.end == end && + other.color == color && + other.opacity == opacity && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.text == text && + other.textStyle == textStyle && + other.isRepeatable == isRepeatable && + other.repeatEvery == repeatEvery && + other.verticalTextPadding == verticalTextPadding && + other.horizontalTextPadding == horizontalTextPadding && + other.repeatUntil == repeatUntil && + other.textAngle == textAngle && + other.shouldRenderAboveSeries == shouldRenderAboveSeries && + other.sizeType == sizeType && + other.dashArray == dashArray && + other.size == size && + other.associatedAxisStart == associatedAxisStart && + other.associatedAxisEnd == associatedAxisEnd && + other.verticalTextAlignment == verticalTextAlignment && + other.horizontalTextAlignment == horizontalTextAlignment && + other.gradient == gradient; + } + + @override + int get hashCode { + final List values = [ + isVisible, + start, + end, + color, + opacity, + borderColor, + borderWidth, + text, + textStyle, + isRepeatable, + repeatEvery, + verticalTextPadding, + horizontalTextPadding, + repeatUntil, + textAngle, + shouldRenderAboveSeries, + sizeType, + dashArray, + size, + associatedAxisStart, + associatedAxisEnd, + verticalTextAlignment, + horizontalTextAlignment, + gradient + ]; + return hashList(values); + } } class _PlotBandPainter extends CustomPainter { @@ -591,13 +659,11 @@ class _PlotBandPainter extends CustomPainter { final PlotBand plotBand = axis.plotBands[j]; if (plotBand.isVisible && shouldRenderAboveSeries != plotBand.shouldRenderAboveSeries) { - clipRect = _calculatePlotOffset( - Rect.fromLTRB( - chartState._chartAxis._axisClipRect.left, - chartState._chartAxis._axisClipRect.top, - chartState._chartAxis._axisClipRect.right, - chartState._chartAxis._axisClipRect.bottom), - Offset(axis.plotOffset, axis.plotOffset)); + clipRect = Rect.fromLTRB( + chartState._chartAxis._axisClipRect.left, + chartState._chartAxis._axisClipRect.top, + chartState._chartAxis._axisClipRect.right, + chartState._chartAxis._axisClipRect.bottom); canvas.clipRect(clipRect); _renderPlotBand(canvas, axisRenderer, plotBand); } @@ -712,27 +778,31 @@ class _PlotBandPainter extends CustomPainter { switch (intervalType) { case DateTimeIntervalType.years: date = DateTime(date.year + addValue, date.month, date.day, date.hour, - date.minute, date.second); + date.minute, date.second, date.millisecond); break; case DateTimeIntervalType.months: date = DateTime(date.year, date.month + addValue, date.day, date.hour, - date.minute, date.second); + date.minute, date.second, date.millisecond); break; case DateTimeIntervalType.days: date = DateTime(date.year, date.month, date.day + addValue, date.hour, - date.minute, date.second); + date.minute, date.second, date.millisecond); break; case DateTimeIntervalType.hours: date = DateTime(date.year, date.month, date.day, date.hour + addValue, - date.minute, date.second); + date.minute, date.second, date.millisecond); break; case DateTimeIntervalType.minutes: date = DateTime(date.year, date.month, date.day, date.hour, - date.minute + addValue, date.second); + date.minute + addValue, date.second, date.millisecond); break; case DateTimeIntervalType.seconds: date = DateTime(date.year, date.month, date.day, date.hour, - date.minute, date.second + addValue); + date.minute, date.second + addValue, date.millisecond); + break; + case DateTimeIntervalType.milliseconds: + date = DateTime(date.year, date.month, date.day, date.hour, + date.minute, date.second, date.millisecond + addValue); break; case DateTimeIntervalType.auto: break; @@ -748,11 +818,19 @@ class _PlotBandPainter extends CustomPainter { void _renderPlotBandElement(ChartAxisRenderer axisRenderer, num startValue, num endValue, PlotBand plotBand, Canvas canvas) { _ChartLocation startPoint, endPoint, segmentStartPoint, segmentEndPoint; - final Rect axisRect = chartState._chartAxis._axisClipRect; Rect plotBandRect; int textAngle; double? left, top, bottom, right; final ChartAxis axis = axisRenderer._axis; + final Rect axisRect = _calculatePlotOffset( + chartState._chartAxis._axisClipRect, + Offset( + axisRenderer._orientation == AxisOrientation.horizontal + ? axis.plotOffset + : 0, + axisRenderer._orientation == AxisOrientation.vertical + ? axis.plotOffset + : 0)); startValue = axis is LogarithmicAxis ? _calculateLogBaseValue(startValue, axis.logBase) diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/base/chart_base.dart b/packages/syncfusion_flutter_charts/lib/src/chart/base/chart_base.dart index fff4758d5..2b850194d 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/base/chart_base.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/base/chart_base.dart @@ -1,78 +1,5 @@ part of charts; -/// Returns the TooltipArgs. -typedef ChartTooltipCallback = void Function(TooltipArgs tooltipArgs); - -/// Returns the ActualRangeChangedArgs. -typedef ChartActualRangeChangedCallback = void Function( - ActualRangeChangedArgs rangeChangedArgs); - -@Deprecated('Use ChartLabelFormatterCallback instead.') - -/// Returns the AxisLabelRenderArgs. -typedef ChartAxisLabelRenderCallback = void Function( - AxisLabelRenderArgs axisLabelRenderArgs); - -///Signature for the [axisLabelFormatter] callback that returns [ChartAxisLabel] class value to -/// customize the axis label text and style. -typedef ChartLabelFormatterCallback = ChartAxisLabel Function( - AxisLabelRenderDetails axisLabelRenderArgs); - -/// Returns the DataLabelRenderArgs. -typedef ChartDataLabelRenderCallback = void Function( - DataLabelRenderArgs dataLabelArgs); - -/// Returns the LegendRenderArgs. -typedef ChartLegendRenderCallback = void Function( - LegendRenderArgs legendRenderArgs); - -/// Returns the Trendline args -typedef ChartTrendlineRenderCallback = void Function( - TrendlineRenderArgs trendlineRenderArgs); - -///Returns the TrackballArgs. -typedef ChartTrackballCallback = void Function(TrackballArgs trackballArgs); - -/// Returns the CrosshairRenderArgs -typedef ChartCrosshairCallback = void Function( - CrosshairRenderArgs crosshairArgs); - -/// Returns the ZoomPanArgs. -typedef ChartZoomingCallback = void Function(ZoomPanArgs zoomingArgs); - -/// Returns the PointTapArgs. -typedef ChartPointTapCallback = void Function(PointTapArgs pointTapArgs); - -/// Returns the AxisLabelTapArgs. -typedef ChartAxisLabelTapCallback = void Function( - AxisLabelTapArgs axisLabelTapArgs); - -/// Returns the LegendTapArgs. -typedef ChartLegendTapCallback = void Function(LegendTapArgs legendTapArgs); - -/// Returns the SelectionArgs. -typedef ChartSelectionCallback = void Function(SelectionArgs selectionArgs); - -/// Returns the offset. -typedef ChartTouchInteractionCallback = void Function( - ChartTouchInteractionArgs tapArgs); - -/// Returns the IndicatorRenderArgs. -typedef ChartIndicatorRenderCallback = void Function( - IndicatorRenderArgs indicatorRenderArgs); - -///Returns the MarkerRenderArgs. -typedef ChartMarkerRenderCallback = void Function(MarkerRenderArgs markerArgs); - -///Returns a widget which can be used to load more data to chart. -///called on dragging and when the visible range reaches the end. -typedef LoadMoreViewBuilderCallback = Widget Function( - BuildContext context, ChartSwipeDirection direction); - -/// A callback which gets called on swiping over plot area -typedef ChartPlotAreaSwipeCallback = void Function( - ChartSwipeDirection direction); - ///Renders the cartesian type charts. /// ///Cartesian charts are generally charts with horizontal and vertical axes.[SfCartesianChart] provides options to cusomize @@ -161,7 +88,7 @@ class SfCartesianChart extends StatefulWidget { this.onZoomStart, this.onZoomEnd, this.onZoomReset, - this.onPointTapped, + @deprecated this.onPointTapped, this.onAxisLabelTapped, this.onDataLabelTapped, this.onTrendlineRender, @@ -170,7 +97,6 @@ class SfCartesianChart extends StatefulWidget { this.onChartTouchInteractionUp, this.onChartTouchInteractionDown, this.onChartTouchInteractionMove, - this.onIndicatorRender, this.onMarkerRender, this.isTransposed = false, this.enableAxisAnimation = false, @@ -201,7 +127,7 @@ class SfCartesianChart extends StatefulWidget { bool? enableMultiSelection, CrosshairBehavior? crosshairBehavior, TrackballBehavior? trackballBehavior, - dynamic? series, + dynamic series, ChartTitle? title, List? axes, List>? indicators}) @@ -607,7 +533,7 @@ class SfCartesianChart extends StatefulWidget { /// print(args.seriesIndex); ///} ///``` - + @Deprecated('Use onPointTap in ChartSeries instead.') final ChartPointTapCallback? onPointTapped; ///Called when the data label is tapped. @@ -615,7 +541,7 @@ class SfCartesianChart extends StatefulWidget { ///Whenever the data label is tapped, `onDataLabelTapped` callback will be called. Provides options to /// get the position of the data label, series index, point index and its text. /// - ///_Note:_ - This callback will not be called, when the builder is specified for data label + ///_Note:_ This callback will not be called, when the builder is specified for data label /// (data label template). For this case, custom widget specified in the `DataLabelSettings.builder` property /// can be wrapped using the `GestureDetector` and this functionality can be achieved in the application level. ///```dart @@ -716,26 +642,6 @@ class SfCartesianChart extends StatefulWidget { ///``` final ChartTouchInteractionCallback? onChartTouchInteractionMove; - /// Occurs when the indicator is rendered. Here, you can get the indicatorname,, - /// seriesname, indicator index, and width of indicators. - ///```dart - ///Widget build(BuildContext context) { - /// return Container( - /// child: SfCartesianChart( - /// onIndicatorRender: (IndicatorRenderArgs args) - /// { - /// if(args.index==1) - /// { - /// args.indicatorName="changed1"; - /// args.signalLineColor=Colors.green; - /// args.signalLineWidth=10.0; - /// } - /// }, - /// )); - ///} - ///``` - final ChartIndicatorRenderCallback? onIndicatorRender; - /// Occurs when the marker is rendered. Here, you can get the marker pointIndex /// shape, height and width of data markers. ///```dart @@ -854,7 +760,7 @@ class SfCartesianChart extends StatefulWidget { /// enableMultiSelection: true, /// series: >[ /// BarSeries( - /// selectionSettings: SelectionSettings( + /// selectionBehavior: SelectionBehavior( /// selectedColor: Colors.red, /// unselectedColor: Colors.grey /// ), @@ -879,7 +785,7 @@ class SfCartesianChart extends StatefulWidget { /// selectionGesture: ActivationMode.doubleTap, /// series: >[ /// BarSeries( - /// selectionSettings: SelectionSettings( + /// selectionBehavior: SelectionBehavior( /// selectedColor: Colors.red, /// unselectedColor: Colors.grey /// ), @@ -916,7 +822,7 @@ class SfCartesianChart extends StatefulWidget { /// series: >[ /// BarSeries( /// initialSelectedDataIndexes: [2, 0], - /// selectionSettings: SelectionSettings( + /// selectionBehavior: SelectionBehavior( /// selectedColor: Colors.red, /// unselectedColor: Colors.grey /// ), @@ -994,9 +900,9 @@ class SfCartesianChart extends StatefulWidget { ///Called while rendering each axis label in the chart. /// ///Provides label text, axis name, orientation of the axis, trimmed text and text styles such as color, - /// font size, and font weight to the user using the [AxisLabelRenderDetails] class. + /// font size, and font weight to the user using the `AxisLabelRenderDetails` class. /// - ///You can customize the text and text style using the [ChartAxisLabel] class and can return it. + ///You can customize the text and text style using the `ChartAxisLabel` class and can return it. final ChartLabelFormatterCallback? axisLabelFormatter; ///Technical indicators for charts. @@ -1153,66 +1059,35 @@ class SfCartesianChart extends StatefulWidget { /// class SfCartesianChartState extends State with TickerProviderStateMixin { + /// Specifies the chart rendering details + late _RenderingDetails _renderingDetails; + /// Holds the animation controller along with their listener for all series and trenddlines late Map _controllerList; - /// Repaint notifier for zoom container - late ValueNotifier _zoomRepaintNotifier; - - /// Repaint notifier for trendline container - late ValueNotifier _trendlineRepaintNotifier; - - /// Repaint notifier for trackball container - late ValueNotifier _trackballRepaintNotifier; - - /// Repaint notifier for crosshair container - late ValueNotifier _crosshairRepaintNotifier; - - /// Repaint notifier for indicator - //ignore: unused_field - late ValueNotifier _indicatorRepaintNotifier; + late Map> _repaintNotifiers; - /// To measure legend size and position - late List<_MeasureWidgetContext> _legendWidgetContext; - - /// Chart Template info - late List<_ChartTemplateInfo> _templates; late List _zoomedAxisRendererStates; late List _oldAxisRenderers; - - /// Contains chart container size - late Rect _containerRect; - - /// Holds the information of chart theme arguments - late SfChartThemeData _chartTheme; late bool _zoomProgress; late List<_ZoomAxisRange> _zoomAxes; - bool? _initialRender; - late List<_LegendRenderContext> _legendToggleStates; late List _selectedSegments; late List _unselectedSegments; - late List<_MeasureWidgetContext> _legendToggleTemplateStates; late List _renderDatalabelRegions; - late Orientation _deviceOrientation; - late List _dataLabelTemplateRegions; late List _annotationRegions; - late bool _animateCompleted; bool _legendRefresh = false; - late bool _widgetNeedUpdate; _DataLabelRenderer? _renderDataLabel; late _CartesianAxisRenderer _renderOutsideAxis; late _CartesianAxisRenderer _renderInsideAxis; late List _oldSeriesRenderers; late List?> _oldSeriesKeys; - late bool _isLegendToggled; late List _segments; late List _oldSeriesVisible; bool? _zoomedState; late List _touchStartPositions; late List _touchMovePositions; late bool _enableDoubleTap; - Orientation? _oldDeviceOrientation; bool _legendToggling = false; dart_ui.Image? _backgroundImage; dart_ui.Image? _legendIconImage; @@ -1235,9 +1110,6 @@ class SfCartesianChartState extends State /// Holds the information of SeriesBase class late _ChartSeries _chartSeries; - /// Holds the information of LegendBase class - late _ChartLegend _chartLegend; - /// Holds the information of _ContainerArea class /// ignore: unused_field late _ContainerArea _containerArea; @@ -1254,14 +1126,10 @@ class SfCartesianChartState extends State late ZoomPanBehaviorRenderer _zoomPanBehaviorRenderer; - late TooltipBehaviorRenderer _tooltipBehaviorRenderer; - late TrackballBehaviorRenderer _trackballBehaviorRenderer; late CrosshairBehaviorRenderer _crosshairBehaviorRenderer; - late LegendRenderer _legendRenderer; - late List _technicalIndicatorRenderer; late TrackballMarkerSettingsRenderer _trackballMarkerSettingsRenderer; @@ -1292,13 +1160,18 @@ class SfCartesianChartState extends State Offset? _startOffset, _currentPosition; late bool _isRedrawByZoomPan; + late PointerDeviceKind _pointerDeviceKind; ///To check the load more widget is in progress or not late bool _isLoadMoreIndicator; + bool _canSetRangeController = false; + + late bool _enableMouseHover; + // ignore: unused_element bool get _animationCompleted { - for (final seriesRenderer in _seriesRenderers) { + for (final CartesianSeriesRenderer seriesRenderer in _seriesRenderers) { if (seriesRenderer._animationController.status == AnimationStatus.forward) { return false; @@ -1309,36 +1182,40 @@ class SfCartesianChartState extends State /// To intialize default values void _initializeDefaultValues() { + _renderingDetails = _RenderingDetails(); _chartAxis = _ChartAxis(this); _chartSeries = _ChartSeries(this); - _chartLegend = _ChartLegend(this); + _renderingDetails.chartLegend = _ChartLegend(this); _containerArea = _ContainerArea(this); _seriesRenderers = []; _controllerList = {}; - _zoomRepaintNotifier = ValueNotifier(0); - _trendlineRepaintNotifier = ValueNotifier(0); - _trackballRepaintNotifier = ValueNotifier(0); - _crosshairRepaintNotifier = ValueNotifier(0); - _indicatorRepaintNotifier = ValueNotifier(0); - _legendWidgetContext = <_MeasureWidgetContext>[]; - _templates = <_ChartTemplateInfo>[]; + _repaintNotifiers = >{ + 'zoom': ValueNotifier(0), + 'trendline': ValueNotifier(0), + 'trackball': ValueNotifier(0), + 'crosshair': ValueNotifier(0), + 'indicator': ValueNotifier(0), + }; + _renderingDetails.legendWidgetContext = <_MeasureWidgetContext>[]; + _renderingDetails.didSizeChange = false; + _renderingDetails.templates = <_ChartTemplateInfo>[]; _oldAxisRenderers = []; _zoomedAxisRendererStates = []; _zoomAxes = <_ZoomAxisRange>[]; - _containerRect = const Rect.fromLTRB(0, 0, 0, 0); + _renderingDetails.chartContainerRect = const Rect.fromLTRB(0, 0, 0, 0); _zoomProgress = false; - _legendToggleStates = <_LegendRenderContext>[]; + _renderingDetails.legendToggleStates = <_LegendRenderContext>[]; _selectedSegments = []; _unselectedSegments = []; - _legendToggleTemplateStates = <_MeasureWidgetContext>[]; + _renderingDetails.legendToggleTemplateStates = <_MeasureWidgetContext>[]; _renderDatalabelRegions = []; - _dataLabelTemplateRegions = []; + _renderingDetails.dataLabelTemplateRegions = []; _annotationRegions = []; - _animateCompleted = false; - _widgetNeedUpdate = false; + _renderingDetails.animateCompleted = false; + _renderingDetails.widgetNeedUpdate = false; _oldSeriesRenderers = []; _oldSeriesKeys = ?>[]; - _isLegendToggled = false; + _renderingDetails.isLegendToggled = false; _oldSeriesVisible = []; _touchStartPositions = []; _touchMovePositions = []; @@ -1352,10 +1229,15 @@ class SfCartesianChartState extends State _zoomPanBehaviorRenderer = ZoomPanBehaviorRenderer(this); _trackballBehaviorRenderer = TrackballBehaviorRenderer(this); _crosshairBehaviorRenderer = CrosshairBehaviorRenderer(this); - _tooltipBehaviorRenderer = TooltipBehaviorRenderer(this); - _legendRenderer = LegendRenderer(widget.legend); + _renderingDetails.tooltipBehaviorRenderer = TooltipBehaviorRenderer(this); + _renderingDetails.legendRenderer = LegendRenderer(widget.legend); _trackballMarkerSettingsRenderer = TrackballMarkerSettingsRenderer( widget.trackballBehavior.markerSettings); + final TargetPlatform platform = defaultTargetPlatform; + _enableMouseHover = kIsWeb || + platform == TargetPlatform.windows || + platform == TargetPlatform.macOS || + platform == TargetPlatform.linux; } /// Called when this object is inserted into the tree. @@ -1388,7 +1270,7 @@ class SfCartesianChartState extends State @override void didChangeDependencies() { - _chartTheme = SfChartTheme.of(context); + _renderingDetails.chartTheme = SfChartTheme.of(context); super.didChangeDependencies(); } @@ -1423,10 +1305,11 @@ class SfCartesianChartState extends State oldWidget, oldWidgetSeriesRenderers, oldWidgetOldSeriesRenderers); _needsRepaintChart( this, _chartAxis._axisRenderersCollection, oldWidgetSeriesRenderers); - _isLegendToggled = false; + _renderingDetails.isLegendToggled = false; // ignore: unnecessary_null_comparison - if (_legendWidgetContext != null && _legendWidgetContext.isNotEmpty) { - _legendWidgetContext.clear(); + if (_renderingDetails.legendWidgetContext != null && + _renderingDetails.legendWidgetContext.isNotEmpty) { + _renderingDetails.legendWidgetContext.clear(); } if (_seriesRenderers.isNotEmpty && _seriesRenderers[0]._selectionBehaviorRenderer?._selectionRenderer != @@ -1437,7 +1320,8 @@ class SfCartesianChartState extends State ?._isInteraction = false; } if (_isNeedUpdate) { - _widgetNeedUpdate = true; + _renderingDetails.widgetNeedUpdate = true; + _renderingDetails.isImageDrawn = false; _oldSeriesRenderers = oldWidgetSeriesRenderers; _getOldSeriesKeys(_oldSeriesRenderers); _oldAxisRenderers = [] @@ -1460,11 +1344,12 @@ class SfCartesianChartState extends State @override Widget build(BuildContext context) { - _oldDeviceOrientation = _oldDeviceOrientation == null - ? MediaQuery.of(context).orientation - : _deviceOrientation; - _deviceOrientation = MediaQuery.of(context).orientation; - _initialRender = _initialRender == null; + _renderingDetails.oldDeviceOrientation = + _renderingDetails.oldDeviceOrientation == null + ? MediaQuery.of(context).orientation + : _renderingDetails.deviceOrientation; + _renderingDetails.deviceOrientation = MediaQuery.of(context).orientation; + _renderingDetails.initialRender = _renderingDetails.initialRender == null; _requireInvertedAxis = false; _triggerLoaded = false; _isSeriesLoaded = _isSeriesLoaded ?? true; @@ -1474,7 +1359,8 @@ class SfCartesianChartState extends State child: _ChartContainer( child: Container( decoration: BoxDecoration( - color: widget.backgroundColor ?? _chartTheme.backgroundColor, + color: widget.backgroundColor ?? + _renderingDetails.chartTheme.backgroundColor, border: Border.all(color: widget.borderColor, width: widget.borderWidth)), child: Container( @@ -1642,7 +1528,7 @@ class SfCartesianChartState extends State } seriesRenderer._series = series; seriesRenderer._isSelectionEnable = - series.selectionBehavior.enable || series.selectionSettings.enable; + series.selectionBehavior.enable == true; seriesRenderer._visible = null; seriesRenderer._chart = widget; seriesRenderer._hasDataLabelTemplate = false; @@ -1703,9 +1589,10 @@ class SfCartesianChartState extends State /// Refresh method for axis void _refresh() { - if (_legendWidgetContext.isNotEmpty) { - for (int i = 0; i < _legendWidgetContext.length; i++) { - final _MeasureWidgetContext templateContext = _legendWidgetContext[i]; + if (_renderingDetails.legendWidgetContext.isNotEmpty) { + for (int i = 0; i < _renderingDetails.legendWidgetContext.length; i++) { + final _MeasureWidgetContext templateContext = + _renderingDetails.legendWidgetContext[i]; final RenderBox renderBox = templateContext.context!.findRenderObject() as RenderBox; templateContext.size = renderBox.size; @@ -1720,11 +1607,10 @@ class SfCartesianChartState extends State /// Redraw method for chart axis void _redraw() { _oldAxisRenderers = _chartAxis._axisRenderersCollection; - _tooltipBehaviorRenderer._timer?.cancel(); if (_trackballBehaviorRenderer._trackballPainter?.timer != null) { _trackballBehaviorRenderer._trackballPainter?.timer!.cancel(); } - if (_isLegendToggled) { + if (_renderingDetails.isLegendToggled) { _segments = []; _oldSeriesVisible = List.filled(_chartSeries.visibleSeriesRenderers.length, null); @@ -1751,7 +1637,7 @@ class SfCartesianChartState extends State } } - _widgetNeedUpdate = false; + _renderingDetails.widgetNeedUpdate = false; if (mounted) { _isRedrawByZoomPan = true; @@ -1763,6 +1649,7 @@ class SfCartesianChartState extends State } void _redrawByRangeChange() { + _oldAxisRenderers = _chartAxis._axisRenderersCollection; if (mounted) { setState(() { /// check the "mounted" property of this object and to ensure the object is still in the tree. @@ -1788,7 +1675,7 @@ class SfCartesianChartState extends State } void _repaintTrendlines() { - _trendlineRepaintNotifier.value++; + _repaintNotifiers['trendline']!.value++; } void _setPainterKey(int index, String name, bool renderComplete) { @@ -1820,8 +1707,8 @@ class SfCartesianChartState extends State final TextStyle titleStyle = _getTextStyle( textStyle: _chart.title.textStyle, background: titleBackground, - fontColor: - _chart.title.textStyle.color ?? _chartTheme.titleTextColor); + fontColor: _chart.title.textStyle.color ?? + _renderingDetails.chartTheme.titleTextColor); final TextStyle textStyle = TextStyle( color: titleStyle.color, fontSize: titleStyle.fontSize, @@ -1837,7 +1724,7 @@ class SfCartesianChartState extends State style: textStyle), decoration: BoxDecoration( color: _chart.title.backgroundColor ?? - _chartTheme.titleBackgroundColor, + _renderingDetails.chartTheme.titleBackgroundColor, border: Border.all( color: _chart.title.borderWidth == 0 ? Colors.transparent @@ -1864,19 +1751,26 @@ class SfCartesianChartState extends State if (widget.plotAreaBackgroundImage != null || widget.legend.image != null) { _calculateImage(this); } - _deviceOrientation = MediaQuery.of(context).orientation; + _renderingDetails.deviceOrientation = MediaQuery.of(context).orientation; return Expanded( child: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { Widget? element; + _renderingDetails.prevSize = + _renderingDetails.prevSize ?? constraints.biggest; + _renderingDetails.didSizeChange = + _renderingDetails.prevSize != constraints.biggest; + _renderingDetails.prevSize = constraints.biggest; final List legendTemplates = _bindCartesianLegendTemplateWidgets(); - if (legendTemplates.isNotEmpty && _legendWidgetContext.isEmpty) { + if (legendTemplates.isNotEmpty && + _renderingDetails.legendWidgetContext.isEmpty) { element = Container(child: Stack(children: legendTemplates)); SchedulerBinding.instance?.addPostFrameCallback((_) => _refresh()); } else { _initialize(constraints); - _chartLegend._calculateLegendBounds(_chartLegend.chartSize); + _renderingDetails.chartLegend + ._calculateLegendBounds(_renderingDetails.chartLegend.chartSize); element = _getElements(this, _ContainerArea(this), constraints); } return element!; @@ -1912,11 +1806,12 @@ class SfCartesianChartState extends State void _initialize(BoxConstraints constraints) { final double width = constraints.maxWidth; final double height = constraints.maxHeight; - _legendRenderer._legendPosition = + _renderingDetails.legendRenderer._legendPosition = (widget.legend.position == LegendPosition.auto) ? (height > width ? LegendPosition.bottom : LegendPosition.right) : widget.legend.position; - final LegendPosition position = _legendRenderer._legendPosition; + final LegendPosition position = + _renderingDetails.legendRenderer._legendPosition; final double widthPadding = position == LegendPosition.left || position == LegendPosition.right ? 5 @@ -1925,7 +1820,8 @@ class SfCartesianChartState extends State position == LegendPosition.top || position == LegendPosition.bottom ? 5 : 0; - _chartLegend.chartSize = Size(width - widthPadding, height - heightPadding); + _renderingDetails.chartLegend.chartSize = + Size(width - widthPadding, height - heightPadding); } /// To find the visible series @@ -1968,16 +1864,21 @@ class SfCartesianChartState extends State } } } - if (_initialRender! || - (_widgetNeedUpdate && + if (_renderingDetails.initialRender! || + (_renderingDetails.widgetNeedUpdate && !_legendToggling && - (_oldDeviceOrientation == MediaQuery.of(context).orientation))) { + (_renderingDetails.oldDeviceOrientation == + MediaQuery.of(context).orientation))) { if (_seriesRenderers[i]._oldSeries != null && (_seriesRenderers[i]._oldSeries!.isVisible == _seriesRenderers[i]._series.isVisible)) { legendCheck = true; } else { - _seriesRenderers[i]._visible = _initialRender! + if (_renderingDetails.legendToggleStates.isNotEmpty) { + _renderingDetails.legendToggleStates.clear(); + } + + _seriesRenderers[i]._visible = _renderingDetails.initialRender! ? _seriesRenderers[i]._series.isVisible : _seriesRenderers[i]._visible ?? _seriesRenderers[i]._series.isVisible; @@ -1991,7 +1892,7 @@ class SfCartesianChartState extends State (_seriesRenderers[0]._series.toString().contains('Bar') && (_seriesRenderers[i]._series.toString().contains('Bar')))) { visibleSeriesRenderers.add(_seriesRenderers[i]); - if (!_initialRender! && + if (!_renderingDetails.initialRender! && _oldSeriesVisible.isNotEmpty && i < visibleSeriesRenderers.length) { if (i < visibleSeriesRenderers.length && @@ -2006,12 +1907,12 @@ class SfCartesianChartState extends State final String? legendText = _chart.legend.legendItemBuilder != null ? visibleSeriesRenderers[index]._seriesName : visibleSeriesRenderers[index]._series.isVisibleInLegend && - _chartSeries._chartState._chartLegend.legendCollections != + _chartSeries._renderingDetails.chartLegend + .legendCollections != null && - _chartSeries._chartState._chartLegend.legendCollections! - .isNotEmpty - ? _chartSeries - ._chartState._chartLegend.legendCollections![index].text + _chartSeries._renderingDetails.chartLegend + .legendCollections!.isNotEmpty + ? _getLegendItemCollection(index)!.text : null; final String? seriesName = visibleSeriesRenderers[index]._series.name; @@ -2064,17 +1965,27 @@ class SfCartesianChartState extends State technicalIndicatorRenderer = TechnicalIndicatorsRenderer(_chart.indicators[i]); _technicalIndicatorRenderer.add(technicalIndicatorRenderer); - if (_initialRender!) { - technicalIndicatorRenderer._visible = _chart.indicators[i].isVisible; - } else { - technicalIndicatorRenderer._visible = - _checkIndicatorLegendToggleState( - visibleSeriesRenderers.length + i, - technicalIndicatorRenderer._visible ?? - _chart.indicators[i].isVisible); - } + technicalIndicatorRenderer._visible = _renderingDetails.initialRender! + ? _chart.indicators[i].isVisible + : _checkIndicatorLegendToggleState( + visibleSeriesRenderers.length + i, + technicalIndicatorRenderer._visible ?? + _chart.indicators[i].isVisible); + } + } + } + + // this method returns the legend render context of a particular series + // since there is no necessity that the series index will match with the legend index + // especially when the previous series is made invisible in legend + _LegendRenderContext? _getLegendItemCollection(int index) { + for (final _LegendRenderContext legendContext + in _renderingDetails.chartLegend.legendCollections!) { + if (legendContext.seriesIndex == index) { + return legendContext; } } + return null; } /// To check the legend toggle state @@ -2082,7 +1993,7 @@ class SfCartesianChartState extends State bool? seriesRender; if (widget.legend.legendItemBuilder != null) { final List<_MeasureWidgetContext> legendToggles = - _legendToggleTemplateStates; + _renderingDetails.legendToggleTemplateStates; if (legendToggles.isNotEmpty) { for (int j = 0; j < legendToggles.length; j++) { final _MeasureWidgetContext item = legendToggles[j]; @@ -2093,10 +2004,10 @@ class SfCartesianChartState extends State } } } else { - if (_legendToggleStates.isNotEmpty) { - for (int j = 0; j < _legendToggleStates.length; j++) { + if (_renderingDetails.legendToggleStates.isNotEmpty) { + for (int j = 0; j < _renderingDetails.legendToggleStates.length; j++) { final _LegendRenderContext legendRenderContext = - _legendToggleStates[j]; + _renderingDetails.legendToggleStates[j]; if (seriesIndex == legendRenderContext.seriesIndex) { seriesRender = false; break; @@ -2115,9 +2026,10 @@ class SfCartesianChartState extends State Trendline trendline, String text) { bool? seriesRender; - if (_legendToggleStates.isNotEmpty) { - for (int j = 0; j < _legendToggleStates.length; j++) { - final _LegendRenderContext legendRenderContext = _legendToggleStates[j]; + if (_renderingDetails.legendToggleStates.isNotEmpty) { + for (int j = 0; j < _renderingDetails.legendToggleStates.length; j++) { + final _LegendRenderContext legendRenderContext = + _renderingDetails.legendToggleStates[j]; if ((legendRenderContext.text == text && legendRenderContext.seriesIndex == seriesIndex && legendRenderContext.trendlineIndex == trendlineIndex) || @@ -2136,7 +2048,7 @@ class SfCartesianChartState extends State bool? seriesRender; if (_chart.legend.legendItemBuilder != null) { final List<_MeasureWidgetContext> legendToggles = - _legendToggleTemplateStates; + _renderingDetails.legendToggleTemplateStates; if (legendToggles.isNotEmpty) { for (int j = 0; j < legendToggles.length; j++) { final _MeasureWidgetContext item = legendToggles[j]; @@ -2147,10 +2059,10 @@ class SfCartesianChartState extends State } } } else { - if (_legendToggleStates.isNotEmpty) { - for (int j = 0; j < _legendToggleStates.length; j++) { + if (_renderingDetails.legendToggleStates.isNotEmpty) { + for (int j = 0; j < _renderingDetails.legendToggleStates.length; j++) { final _LegendRenderContext legendRenderContext = - _legendToggleStates[j]; + _renderingDetails.legendToggleStates[j]; if (seriesIndex == legendRenderContext.seriesIndex && legendRenderContext.text == text) { if (series is CartesianSeries) { @@ -2177,7 +2089,7 @@ class _ContainerArea extends StatelessWidget { //Here, we are using get keyword inorder to get the proper & updated instance of chart widget //When we initialize chart widget as a property to other classes like _ChartSeries, the chart widget is not updated properly and by using get we can rectify this. SfCartesianChart get chart => _chartState._chart; - List _chartWidgets = []; + _RenderingDetails get _renderingDetails => _chartState._renderingDetails; late RenderBox renderBox; Offset? _touchPosition; Offset? _tapDownDetails; @@ -2185,7 +2097,7 @@ class _ContainerArea extends StatelessWidget { late CartesianSeries _series; late XyDataSeriesRenderer _seriesRenderer; Offset? _zoomStartPosition; - final bool _enableMouseHover = kIsWeb; + bool get _enableMouseHover => _chartState._enableMouseHover; @override Widget build(BuildContext context) { final bool isUserInteractionEnabled = @@ -2219,6 +2131,7 @@ class _ContainerArea extends StatelessWidget { onExit: (PointerEvent event) => _performMouseExit(event), child: Listener( onPointerDown: (PointerDownEvent event) { + _chartState._pointerDeviceKind = event.kind; _performPointerDown(event); ChartTouchInteractionArgs touchArgs; if (chart.onChartTouchInteractionDown != null) { @@ -2261,6 +2174,33 @@ class _ContainerArea extends StatelessWidget { renderBox.globalToLocal(details.globalPosition); _touchPosition = position; }, + onTap: () { + final Offset position = _touchPosition!; + if (_chartState._chartSeries.visibleSeriesRenderers != + null && + _chartState._chartSeries.visibleSeriesRenderers + .isNotEmpty && + chart.selectionGesture == + ActivationMode.singleTap && + _chartState._zoomPanBehaviorRenderer._isPinching != + true) { + final CartesianSeriesRenderer + selectionSeriesRenderer = _findSeries(position)!; + final SelectionBehaviorRenderer? + selectionBehaviorRenderer = + selectionSeriesRenderer + ._selectionBehaviorRenderer; + if (selectionSeriesRenderer._isSelectionEnable && + selectionBehaviorRenderer?._selectionRenderer != + null && + !selectionSeriesRenderer._isOuterRegion) { + selectionBehaviorRenderer?._selectionRenderer + ?.seriesRenderer = selectionSeriesRenderer; + selectionBehaviorRenderer?.onTouchDown( + position.dx, position.dy); + } + } + }, onTapUp: (TapUpDetails details) { final Offset position = renderBox.globalToLocal(details.globalPosition); @@ -2268,7 +2208,12 @@ class _ContainerArea extends StatelessWidget { visibleSeriesRenderer = _chartState._chartSeries.visibleSeriesRenderers; if (chart.onPointTapped != null) { - _calculatePointSeriesIndex(chart, position); + _calculatePointSeriesIndex( + chart, _chartState, position); + } + if (_findSeries(position)!._series.onPointTap != null) { + _calculatePointSeriesIndex(chart, _chartState, + position, null, ActivationMode.singleTap); } if (chart.onAxisLabelTapped != null) { _triggerAxisLabelEvent(position); @@ -2338,7 +2283,7 @@ class _ContainerArea extends StatelessWidget { void _calculateContainerSize(BoxConstraints constraints) { final double width = constraints.maxWidth; final double height = constraints.maxHeight; - _chartState._containerRect = Rect.fromLTWH(0, 0, width, height); + _renderingDetails.chartContainerRect = Rect.fromLTWH(0, 0, width, height); } /// Calculate container bounds @@ -2366,7 +2311,7 @@ class _ContainerArea extends StatelessWidget { /// To render chart widgets Widget _renderWidgets(BoxConstraints constraints, BuildContext context) { - _chartWidgets = []; + _renderingDetails.chartWidgets = []; _chartState._renderDatalabelRegions = []; _bindAxisWidgets('outside'); _bindPlotBandWidgets(true); @@ -2381,27 +2326,28 @@ class _ContainerArea extends StatelessWidget { renderBox = context.findRenderObject()! as RenderBox; _chartState._containerArea = this; _chartState._legendRefresh = false; - return Container(child: Stack(children: _chartWidgets)); + return Container( + child: Stack( + textDirection: TextDirection.ltr, + children: _renderingDetails.chartWidgets!)); } void _bindLoadMoreIndicatorWidget() { - _chartWidgets.add(StatefulBuilder( + _renderingDetails.chartWidgets!.add(StatefulBuilder( builder: (BuildContext context, StateSetter stateSetter) { Widget renderWidget; _chartState._loadMoreViewStateSetter = stateSetter; - if (_chartState._isLoadMoreIndicator) { - renderWidget = Center( - child: _chartState._chart.loadMoreIndicatorBuilder!( - context, _chartState._swipeDirection)); - } else { - renderWidget = Container(); - } + renderWidget = _chartState._isLoadMoreIndicator + ? Center( + child: _chartState._chart.loadMoreIndicatorBuilder!( + context, _chartState._swipeDirection)) + : renderWidget = Container(); return renderWidget; })); } void _bindPlotBandWidgets(bool shouldRenderAboveSeries) { - _chartWidgets.add(RepaintBoundary( + _renderingDetails.chartWidgets!.add(RepaintBoundary( child: CustomPaint( painter: _PlotBandPainter( chartState: _chartState, @@ -2428,9 +2374,9 @@ class _ContainerArea extends StatelessWidget { _seriesRenderer._trendlineRenderer[j]; final Trendline trendline = _series.trendlines![j]; if (trendline.animationDuration > 0 && - (_chartState._oldDeviceOrientation == null || - _chartState._oldDeviceOrientation == - _chartState._deviceOrientation) && + (_renderingDetails.oldDeviceOrientation == null || + _renderingDetails.oldDeviceOrientation == + _renderingDetails.deviceOrientation) && _seriesRenderer._needsAnimation && _seriesRenderer._oldSeries == null) { trendlineRenderer._animationController.duration = @@ -2446,12 +2392,12 @@ class _ContainerArea extends StatelessWidget { } } if (isTrendline) { - _chartWidgets.add(RepaintBoundary( + _renderingDetails.chartWidgets!.add(RepaintBoundary( child: CustomPaint( painter: _TrendlinePainter( chartState: _chartState, trendlineAnimations: trendlineAnimations, - notifier: _chartState._trendlineRepaintNotifier)), + notifier: _chartState._repaintNotifiers['trendline']!)), )); } } @@ -2459,14 +2405,15 @@ class _ContainerArea extends StatelessWidget { /// To bind the widget for data label void _bindDataLabelWidgets() { _chartState._renderDataLabel = _DataLabelRenderer( - cartesianChartState: _chartState, show: _chartState._animateCompleted); - _chartWidgets.add(_chartState._renderDataLabel!); + cartesianChartState: _chartState, + show: _renderingDetails.animateCompleted); + _renderingDetails.chartWidgets!.add(_chartState._renderDataLabel!); } /// To render a template void _renderTemplates() { _chartState._annotationRegions = []; - _chartState._templates = <_ChartTemplateInfo>[]; + _renderingDetails.templates = <_ChartTemplateInfo>[]; _renderDataLabelTemplates(); if (chart.annotations != null && chart.annotations!.isNotEmpty) { for (int i = 0; i < chart.annotations!.length; i++) { @@ -2483,18 +2430,18 @@ class _ContainerArea extends StatelessWidget { verticalAlignment: annotation.verticalAlignment, horizontalAlignment: annotation.horizontalAlignment, clipRect: annotation.region == AnnotationRegion.chart - ? _chartState._containerRect + ? _renderingDetails.chartContainerRect : _chartState._chartAxis._axisClipRect, location: Offset(location.x.toDouble(), location.y.toDouble())); - _chartState._templates.add(chartTemplateInfo); + _renderingDetails.templates.add(chartTemplateInfo); } } - if (_chartState._templates.isNotEmpty) { - final int templateLength = _chartState._templates.length; - for (int i = 0; i < _chartState._templates.length; i++) { - final _ChartTemplateInfo templateInfo = _chartState._templates[i]; - _chartWidgets.add(_RenderTemplate( + if (_renderingDetails.templates.isNotEmpty) { + final int templateLength = _renderingDetails.templates.length; + for (int i = 0; i < _renderingDetails.templates.length; i++) { + final _ChartTemplateInfo templateInfo = _renderingDetails.templates[i]; + _renderingDetails.chartWidgets!.add(_RenderTemplate( template: templateInfo, templateIndex: i, templateLength: templateLength, @@ -2507,14 +2454,14 @@ class _ContainerArea extends StatelessWidget { void _renderDataLabelTemplates() { Widget? labelWidget; CartesianChartPoint point; - _chartState._dataLabelTemplateRegions = []; + _renderingDetails.dataLabelTemplateRegions = []; for (int i = 0; i < _chartState._chartSeries.visibleSeriesRenderers.length; i++) { final CartesianSeriesRenderer seriesRenderer = _chartState._chartSeries.visibleSeriesRenderers[i]; final XyDataSeries series = - seriesRenderer._series as XyDataSeries; + seriesRenderer._series as XyDataSeries; num padding; if (series.dataLabelSettings.isVisible && seriesRenderer._visible!) { for (int j = 0; j < seriesRenderer._dataPoints.length; j++) { @@ -2534,13 +2481,18 @@ class _ContainerArea extends StatelessWidget { (seriesType.contains('range') || (seriesType.contains('hilo') && !seriesType.contains('hiloopenclose'))) - ? [point.low, point.high] + ? [point.low as num, point.high as num] : (seriesType.contains('candle') || seriesType.contains('hiloopenclose')) - ? [point.low, point.high, point.open, point.close] + ? [ + point.low as num, + point.high as num, + point.open as num, + point.close as num + ] : seriesType.contains('box') - ? [point.minimum!] - : [point.y]; + ? [point.minimum!] + : [point.y as num]; for (int k = 0; k < dataLabelTemplateYValues.length; k++) { padding = (k == 0 && @@ -2568,7 +2520,7 @@ class _ContainerArea extends StatelessWidget { (series.animationDuration + 1000.0).floor(), widget: labelWidget, location: Offset(location.x, location.y + padding)); - _chartState._templates.add(templateInfo); + _renderingDetails.templates.add(templateInfo); } } } @@ -2581,7 +2533,7 @@ class _ContainerArea extends StatelessWidget { void _bindSeriesWidgets() { _chartState._painterKeys = <_PainterKey>[]; _chartState._animationCompleteCount = 0; - _chartState._animateCompleted = false; + _renderingDetails.animateCompleted = false; for (int i = 0; i < _chartState._chartSeries.visibleSeriesRenderers.length; i++) { @@ -2606,10 +2558,7 @@ class _ContainerArea extends StatelessWidget { if (_seriesRenderer != null && _seriesRenderer._visible!) { _calculateTrendlineRegion(_chartState, _seriesRenderer); _series.selectionBehavior._chartState = _chartState; - _series.selectionSettings._chartState = _chartState; - _seriesRenderer._selectionBehavior = _series.selectionBehavior.enable - ? _series.selectionBehavior - : _series.selectionSettings; + _seriesRenderer._selectionBehavior = _series.selectionBehavior; final dynamic selectionBehavior = _seriesRenderer._selectionBehavior; _seriesRenderer._selectionBehaviorRenderer = SelectionBehaviorRenderer(selectionBehavior, chart, _chartState); @@ -2651,7 +2600,7 @@ class _ContainerArea extends StatelessWidget { } int totalSelectedSegment = 0; int? selectedSeriesIndex; - if (selectionBehavior.enable && + if (selectionBehavior.enable == true && _chartState._selectedSegments.isNotEmpty && _chartState._unselectedSegments.isNotEmpty) { for (int j = 0; j < _chartState._selectedSegments.length; j++) { @@ -2668,17 +2617,16 @@ class _ContainerArea extends StatelessWidget { } } if (_chartState._isRangeSelectionSlider == false && - selectionBehavior.enable && + selectionBehavior.enable == true && (isSelecetedIndexesUpdated || - (!_chartState._initialRender! && - (totalSelectedSegment != 0 - ? (totalSelectedSegment < + (!_renderingDetails.initialRender! && + (totalSelectedSegment != 0 && + (totalSelectedSegment < _chartState - ._seriesRenderers[i]._dataPoints.length) - : false)))) { + ._seriesRenderers[i]._dataPoints.length))))) { int segmentLength = _seriesRenderer._dataPoints.length; - if (_checkSeriesType(_seriesRenderer._seriesType) || + if (_isLineTypeSeries(_seriesRenderer._seriesType) || _seriesRenderer._seriesType.contains('boxandwhisker')) { segmentLength = _seriesRenderer._dataPoints.length - 1; } @@ -2688,18 +2636,17 @@ class _ContainerArea extends StatelessWidget { segment.currentSegmentIndex = j; segment._seriesIndex = i; segment._currentPoint = _seriesRenderer._dataPoints[j]; - if ((_series.initialSelectedDataIndexes! - .contains(segment.currentSegmentIndex) && - isSelecetedIndexesUpdated) || - chart.selectionType == SelectionType.series && - selectedSeriesIndex == i) { - selectionBehaviorRenderer._selectionRenderer!.selectedSegments - .add(segment); - } else { - selectionBehaviorRenderer - ._selectionRenderer!.unselectedSegments! - .add(segment); - } + ((_series.initialSelectedDataIndexes! + .contains(segment.currentSegmentIndex) && + isSelecetedIndexesUpdated) || + chart.selectionType == SelectionType.series && + selectedSeriesIndex == i) + ? selectionBehaviorRenderer + ._selectionRenderer!.selectedSegments + .add(segment) + : selectionBehaviorRenderer + ._selectionRenderer!.unselectedSegments! + .add(segment); } _seriesRenderer._oldSelectedIndexes = [] //ignore: prefer_spread_collections @@ -2709,17 +2656,18 @@ class _ContainerArea extends StatelessWidget { // ignore: unnecessary_null_comparison if (_seriesRenderer._animationController != null && _series.animationDuration > 0 && - (_chartState._oldDeviceOrientation == null || + !_renderingDetails.didSizeChange && + (_renderingDetails.oldDeviceOrientation == null || _chartState._legendRefresh || - _chartState._oldDeviceOrientation == - _chartState._deviceOrientation) && - (_chartState._initialRender! || + _renderingDetails.oldDeviceOrientation == + _renderingDetails.deviceOrientation) && + (_renderingDetails.initialRender! || _chartState._legendRefresh || ((_seriesType == 'column' || _seriesType == 'bar') && _chartState._legendToggling) || (!_chartState._legendToggling && _seriesRenderer._needsAnimation && - _chartState._widgetNeedUpdate))) { + _renderingDetails.widgetNeedUpdate))) { if ((_seriesType == 'column' || _seriesType == 'bar') && _chartState._legendToggling) { _seriesRenderer._needAnimateSeriesElements = true; @@ -2743,21 +2691,21 @@ class _ContainerArea extends StatelessWidget { _setAnimationStatus(_chartState); } } - _chartWidgets.add(Container( + _renderingDetails.chartWidgets!.add(Container( child: RepaintBoundary( child: CustomPaint( painter: _getSeriesPainter( i, _seriesRenderer._animationController, _seriesRenderer), )))); } - _chartWidgets.add(Container( + _renderingDetails.chartWidgets!.add(Container( color: Colors.red, child: RepaintBoundary( child: CustomPaint( painter: _ZoomRectPainter( isRepaint: true, chartState: _chartState, - notifier: _chartState._zoomRepaintNotifier))))); + notifier: _chartState._repaintNotifiers['zoom']))))); _chartState._legendToggling = false; } @@ -2769,12 +2717,10 @@ class _ContainerArea extends StatelessWidget { _chartState._chartAxis._axisRenderersCollection.length > 1) { final _CartesianAxisRenderer axisWidget = _CartesianAxisRenderer( chartState: _chartState, renderType: renderType); - if (renderType == 'outside') { - _chartState._renderOutsideAxis = axisWidget; - } else { - _chartState._renderInsideAxis = axisWidget; - } - _chartWidgets.add(axisWidget); + renderType == 'outside' + ? _chartState._renderOutsideAxis = axisWidget + : _chartState._renderInsideAxis = axisWidget; + _renderingDetails.chartWidgets!.add(axisWidget); } } @@ -2815,15 +2761,20 @@ class _ContainerArea extends StatelessWidget { for (int k = _chartState._chartSeries.visibleSeriesRenderers.length - 1; k >= 0; k--) { - isSelect = - seriesRenderer._isSelectionEnable && seriesRenderer._visible! - ? selectionBehaviorRenderer!._selectionRenderer! - ._isSeriesContainsPoint( - _chartState._chartSeries.visibleSeriesRenderers[i], - position) - : false; + isSelect = seriesRenderer._isSelectionEnable && + seriesRenderer._visible! && + selectionBehaviorRenderer!._selectionRenderer! + ._isSeriesContainsPoint( + _chartState._chartSeries.visibleSeriesRenderers[i], + position); if (isSelect) { return _chartState._chartSeries.visibleSeriesRenderers[i]; + } else if (seriesRenderer._visible! && + selectionBehaviorRenderer!._selectionRenderer! + ._isSeriesContainsPoint( + _chartState._chartSeries.visibleSeriesRenderers[i], + position)) { + return _chartState._chartSeries.visibleSeriesRenderers[i]; } } } @@ -2833,7 +2784,8 @@ class _ContainerArea extends StatelessWidget { /// To perform the pointer down event void _performPointerDown(PointerDownEvent event) { - _chartState._tooltipBehaviorRenderer._isHovering = false; + _chartState._canSetRangeController = true; + _renderingDetails.tooltipBehaviorRenderer._isHovering = false; _tapDownDetails = event.position; if (chart.zoomPanBehavior.enablePinching == true) { ZoomPanArgs? zoomStartArgs; @@ -2847,8 +2799,8 @@ class _ContainerArea extends StatelessWidget { final ChartAxisRenderer axisRenderer = _chartState._chartAxis._axisRenderersCollection[axisIndex]; if (chart.onZoomStart != null) { - zoomStartArgs = _bindZoomEvent( - chart, axisRenderer, zoomStartArgs, chart.onZoomStart!); + zoomStartArgs = + _bindZoomEvent(chart, axisRenderer, chart.onZoomStart!); axisRenderer._zoomFactor = zoomStartArgs.currentZoomFactor; axisRenderer._zoomPosition = zoomStartArgs.currentZoomPosition; } @@ -2865,23 +2817,6 @@ class _ContainerArea extends StatelessWidget { final Offset position = renderBox.globalToLocal(event.position); _touchPosition = position; // ignore: unnecessary_null_comparison - if (_chartState._chartSeries.visibleSeriesRenderers != null && - _chartState._chartSeries.visibleSeriesRenderers.isNotEmpty && - chart.selectionGesture == ActivationMode.singleTap && - _chartState._zoomPanBehaviorRenderer._isPinching != true) { - final CartesianSeriesRenderer selectionSeriesRenderer = - _findSeries(position)!; - final SelectionBehaviorRenderer selectionBehaviorRenderer = - selectionSeriesRenderer._selectionBehaviorRenderer!; - if (selectionSeriesRenderer._isSelectionEnable && - selectionBehaviorRenderer._selectionRenderer != null && - !selectionSeriesRenderer._isOuterRegion) { - selectionBehaviorRenderer._selectionRenderer!.seriesRenderer = - selectionSeriesRenderer; - selectionBehaviorRenderer.onTouchDown(position.dx, position.dy); - } - } - // ignore: unnecessary_null_comparison if (chart.trackballBehavior != null && chart.trackballBehavior.enable && chart.trackballBehavior.activationMode == ActivationMode.singleTap) { @@ -2904,7 +2839,7 @@ class _ContainerArea extends StatelessWidget { /// To perform the pointer move event void _performPointerMove(PointerMoveEvent event) { - _chartState._tooltipBehaviorRenderer._isHovering = false; + _renderingDetails.tooltipBehaviorRenderer._isHovering = false; if (chart.zoomPanBehavior.enablePinching == true && _chartState._touchStartPositions.length == 2) { _chartState._zoomPanBehaviorRenderer._isPinching = true; @@ -2944,7 +2879,7 @@ class _ContainerArea extends StatelessWidget { _chartState._touchMovePositions = []; _chartState._zoomPanBehaviorRenderer._isPinching = false; _chartState._zoomPanBehaviorRenderer._delayRedraw = false; - _chartState._tooltipBehaviorRenderer._isHovering = false; + _renderingDetails.tooltipBehaviorRenderer._isHovering = false; final Offset position = renderBox.globalToLocal(event.position); // ignore: unnecessary_null_comparison if ((chart.trackballBehavior != null && @@ -2986,13 +2921,12 @@ class _ContainerArea extends StatelessWidget { if (chart.tooltipBehavior.enable && chart.tooltipBehavior.activationMode == ActivationMode.singleTap || _shouldShowAxisTooltip(_chartState)) { - _chartState._tooltipBehaviorRenderer._isInteraction = true; - if (chart.tooltipBehavior.builder != null) { - _chartState._tooltipBehaviorRenderer._showTemplateTooltip(position); - } else { - _chartState._tooltipBehaviorRenderer - .onTouchUp(position.dx, position.dy); - } + _renderingDetails.tooltipBehaviorRenderer._isInteraction = true; + chart.tooltipBehavior.builder != null + ? _renderingDetails.tooltipBehaviorRenderer + ._showTemplateTooltip(position) + : _renderingDetails.tooltipBehaviorRenderer + .onTouchUp(position.dx, position.dy); } } @@ -3011,7 +2945,7 @@ class _ContainerArea extends StatelessWidget { /// To calculate the arguments of pinch zooming event void _calculatePinchZoomingArgs() { - ZoomPanArgs? zoomEndArgs, zoomResetArgs; + ZoomPanArgs? zoomEndArgs; bool resetFlag = false; int axisIndex; for (axisIndex = 0; @@ -3020,8 +2954,7 @@ class _ContainerArea extends StatelessWidget { final ChartAxisRenderer axisRenderer = _chartState._chartAxis._axisRenderersCollection[axisIndex]; if (chart.onZoomEnd != null) { - zoomEndArgs = - _bindZoomEvent(chart, axisRenderer, zoomEndArgs, chart.onZoomEnd!); + zoomEndArgs = _bindZoomEvent(chart, axisRenderer, chart.onZoomEnd!); axisRenderer._zoomFactor = zoomEndArgs.currentZoomFactor; axisRenderer._zoomPosition = zoomEndArgs.currentZoomPosition; } @@ -3045,7 +2978,7 @@ class _ContainerArea extends StatelessWidget { index++) { final ChartAxisRenderer axisRenderer = _chartState._chartAxis._axisRenderersCollection[index]; - _bindZoomEvent(chart, axisRenderer, zoomResetArgs, chart.onZoomReset!); + _bindZoomEvent(chart, axisRenderer, chart.onZoomReset!); } } } @@ -3073,23 +3006,19 @@ class _ContainerArea extends StatelessWidget { chart.trackballBehavior.activationMode != ActivationMode.doubleTap && position != null) { if (chart.trackballBehavior.activationMode == ActivationMode.singleTap) { - if (chart.trackballBehavior.builder != null) { - _chartState._trackballBehaviorRenderer - ._showTemplateTrackball(position); - } else { - _chartState._trackballBehaviorRenderer - .onTouchMove(position.dx, position.dy); - } + chart.trackballBehavior.builder != null + ? _chartState._trackballBehaviorRenderer + ._showTemplateTrackball(position) + : _chartState._trackballBehaviorRenderer + .onTouchMove(position.dx, position.dy); } if (chart.trackballBehavior.activationMode == ActivationMode.longPress && _chartState._trackballBehaviorRenderer._isLongPressActivated) { - if (chart.trackballBehavior.builder != null) { - _chartState._trackballBehaviorRenderer - ._showTemplateTrackball(position); - } else { - _chartState._trackballBehaviorRenderer - .onTouchMove(position.dx, position.dy); - } + chart.trackballBehavior.builder != null + ? _chartState._trackballBehaviorRenderer + ._showTemplateTrackball(position) + : _chartState._trackballBehaviorRenderer + .onTouchMove(position.dx, position.dy); } } // ignore: unnecessary_null_comparison @@ -3145,17 +3074,20 @@ class _ContainerArea extends StatelessWidget { Offset? position; if (_tapDownDetails != null) { position = renderBox.globalToLocal(_tapDownDetails!); + if (_findSeries(position)!._series.onPointLongPress != null) { + _calculatePointSeriesIndex( + chart, _chartState, position, null, ActivationMode.longPress); + } if (chart.tooltipBehavior.enable && chart.tooltipBehavior.activationMode == ActivationMode.longPress || _shouldShowAxisTooltip(_chartState)) { - _chartState._tooltipBehaviorRenderer._isInteraction = true; - if (chart.tooltipBehavior.builder != null) { - _chartState._tooltipBehaviorRenderer._showTemplateTooltip(position); - } else { - _chartState._tooltipBehaviorRenderer - .onLongPress(position.dx, position.dy); - } + _renderingDetails.tooltipBehaviorRenderer._isInteraction = true; + chart.tooltipBehavior.builder != null + ? _renderingDetails.tooltipBehaviorRenderer + ._showTemplateTooltip(position) + : _renderingDetails.tooltipBehaviorRenderer + .onLongPress(position.dx, position.dy); } } // ignore: unnecessary_null_comparison @@ -3178,12 +3110,11 @@ class _ContainerArea extends StatelessWidget { position != null && _chartState._zoomPanBehaviorRenderer._isPinching != true) { _chartState._trackballBehaviorRenderer._isLongPressActivated = true; - if (chart.trackballBehavior.builder != null) { - _chartState._trackballBehaviorRenderer._showTemplateTrackball(position); - } else { - _chartState._trackballBehaviorRenderer - .onTouchDown(position.dx, position.dy); - } + chart.trackballBehavior.builder != null + ? _chartState._trackballBehaviorRenderer + ._showTemplateTrackball(position) + : _chartState._trackballBehaviorRenderer + .onTouchDown(position.dx, position.dy); } // ignore: unnecessary_null_comparison if ((chart.crosshairBehavior != null && @@ -3203,17 +3134,19 @@ class _ContainerArea extends StatelessWidget { void _performDoubleTap() { if (_tapDownDetails != null) { final Offset position = renderBox.globalToLocal(_tapDownDetails!); + if (_findSeries(position)!._series.onPointDoubleTap != null) { + _calculatePointSeriesIndex( + chart, _chartState, position, null, ActivationMode.doubleTap); + } // ignore: unnecessary_null_comparison if (chart.trackballBehavior != null && chart.trackballBehavior.enable && chart.trackballBehavior.activationMode == ActivationMode.doubleTap) { - if (chart.trackballBehavior.builder != null) { - _chartState._trackballBehaviorRenderer - ._showTemplateTrackball(position); - } else { - _chartState._trackballBehaviorRenderer - .onDoubleTap(position.dx, position.dy); - } + chart.trackballBehavior.builder != null + ? _chartState._trackballBehaviorRenderer + ._showTemplateTrackball(position) + : _chartState._trackballBehaviorRenderer + .onDoubleTap(position.dx, position.dy); _chartState._enableDoubleTap = true; _chartState._isTouchUp = true; _chartState._trackballBehaviorRenderer @@ -3238,13 +3171,12 @@ class _ContainerArea extends StatelessWidget { chart.tooltipBehavior.activationMode == ActivationMode.doubleTap || _shouldShowAxisTooltip(_chartState)) { - _chartState._tooltipBehaviorRenderer._isInteraction = true; - if (chart.tooltipBehavior.builder != null) { - _chartState._tooltipBehaviorRenderer._showTemplateTooltip(position); - } else { - _chartState._tooltipBehaviorRenderer - .onDoubleTap(position.dx, position.dy); - } + _renderingDetails.tooltipBehaviorRenderer._isInteraction = true; + chart.tooltipBehavior.builder != null + ? _renderingDetails.tooltipBehaviorRenderer + ._showTemplateTooltip(position) + : _renderingDetails.tooltipBehaviorRenderer + .onDoubleTap(position.dx, position.dy); } // ignore: unnecessary_null_comparison if (_chartState._chartSeries.visibleSeriesRenderers != null && @@ -3306,13 +3238,11 @@ class _ContainerArea extends StatelessWidget { chart.trackballBehavior.activationMode == ActivationMode.longPress && _chartState._trackballBehaviorRenderer._isLongPressActivated == true) { - if (chart.trackballBehavior.builder != null) { - _chartState._trackballBehaviorRenderer - ._showTemplateTrackball(position); - } else { - _chartState._trackballBehaviorRenderer - .onTouchMove(position.dx, position.dy); - } + chart.trackballBehavior.builder != null + ? _chartState._trackballBehaviorRenderer + ._showTemplateTrackball(position) + : _chartState._trackballBehaviorRenderer + .onTouchMove(position.dx, position.dy); } } // ignore: unnecessary_null_comparison @@ -3376,7 +3306,8 @@ class _ContainerArea extends StatelessWidget { ? _chartState._chartAxis._axisClipRect.height : _chartState._chartAxis._axisClipRect.width) * 0.1; - const double swipeMinVelocity = 240.0; + final double swipeMinVelocity = + _chartState._pointerDeviceKind == PointerDeviceKind.mouse ? 0.0 : 240; ChartSwipeDirection swipeDirection; final double dx = @@ -3386,22 +3317,33 @@ class _ContainerArea extends StatelessWidget { (_chartState._currentPosition!.dy - _chartState._startOffset!.dy) .abs(); final double velocity = details.primaryVelocity!; - if (_chartState._requireInvertedAxis && dx <= swipeMaxDistanceThreshold && dy >= swipeMinDisplacement && velocity.abs() >= swipeMinVelocity) { ///vertical - swipeDirection = - velocity < 0 ? ChartSwipeDirection.start : ChartSwipeDirection.end; + swipeDirection = _chartState._pointerDeviceKind == + PointerDeviceKind.mouse + ? (_chartState._currentPosition!.dy > _chartState._startOffset!.dy + ? ChartSwipeDirection.end + : ChartSwipeDirection.start) + : (velocity < 0 + ? ChartSwipeDirection.start + : ChartSwipeDirection.end); chart.onPlotAreaSwipe!(swipeDirection); } else if (!_chartState._requireInvertedAxis && dx >= swipeMinDisplacement && dy <= swipeMaxDistanceThreshold && velocity.abs() >= swipeMinVelocity) { ///horizontal - swipeDirection = - velocity > 0 ? ChartSwipeDirection.start : ChartSwipeDirection.end; + swipeDirection = _chartState._pointerDeviceKind == + PointerDeviceKind.mouse + ? (_chartState._currentPosition!.dx > _chartState._startOffset!.dx + ? ChartSwipeDirection.start + : ChartSwipeDirection.end) + : (velocity > 0 + ? ChartSwipeDirection.start + : ChartSwipeDirection.end); chart.onPlotAreaSwipe!(swipeDirection); } } @@ -3411,10 +3353,10 @@ class _ContainerArea extends StatelessWidget { _chartState._startOffset != null && _chartState._currentPosition != null) { final bool verticallyDragging = - ((_chartState._currentPosition!.dy - _chartState._startOffset!.dy) + (_chartState._currentPosition!.dy - _chartState._startOffset!.dy) .abs() > (_chartState._currentPosition!.dx - _chartState._startOffset!.dx) - .abs()); + .abs(); if ((!verticallyDragging && !_chartState._requireInvertedAxis) || (verticallyDragging && _chartState._requireInvertedAxis)) { bool loadMore = false; @@ -3463,36 +3405,39 @@ class _ContainerArea extends StatelessWidget { /// To perform mouse hover event void _performMouseHover(PointerEvent event) { - _chartState._tooltipBehaviorRenderer._isHovering = true; - _chartState._tooltipBehaviorRenderer._isInteraction = true; + _renderingDetails.tooltipBehaviorRenderer._isHovering = true; + _renderingDetails.tooltipBehaviorRenderer._isInteraction = true; final Offset position = renderBox.globalToLocal(event.position); - _shouldShowAxisTooltip(_chartState); - if (chart.tooltipBehavior.enable || _shouldShowAxisTooltip(_chartState)) { - if (chart.tooltipBehavior.builder != null) { - _chartState._tooltipBehaviorRenderer._showTemplateTooltip(position); - } else { - _chartState._tooltipBehaviorRenderer.onEnter(position.dx, position.dy); - } + if ((chart.tooltipBehavior.enable && + chart.tooltipBehavior.activationMode == ActivationMode.singleTap) || + _shouldShowAxisTooltip(_chartState)) { + chart.tooltipBehavior.builder != null + ? _renderingDetails.tooltipBehaviorRenderer + ._showTemplateTooltip(position) + : _renderingDetails.tooltipBehaviorRenderer + .onEnter(position.dx, position.dy); } - if (chart.trackballBehavior.enable) { - if (chart.trackballBehavior.builder != null) { - _chartState._trackballBehaviorRenderer._showTemplateTrackball(position); - } else { - _chartState._trackballBehaviorRenderer - .onEnter(position.dx, position.dy); - } + if (chart.trackballBehavior.enable && + chart.trackballBehavior.activationMode == ActivationMode.singleTap) { + chart.trackballBehavior.builder != null + ? _chartState._trackballBehaviorRenderer + ._showTemplateTrackball(position) + : _chartState._trackballBehaviorRenderer + .onEnter(position.dx, position.dy); } - if (chart.crosshairBehavior.enable) { + if (chart.crosshairBehavior.enable && + chart.crosshairBehavior.activationMode == ActivationMode.singleTap) { _chartState._crosshairBehaviorRenderer.onEnter(position.dx, position.dy); } } /// To perform the mouse exit event void _performMouseExit(PointerEvent event) { - _chartState._tooltipBehaviorRenderer._isHovering = false; + _renderingDetails.tooltipBehaviorRenderer._isHovering = false; final Offset position = renderBox.globalToLocal(event.position); if (chart.tooltipBehavior.enable || _shouldShowAxisTooltip(_chartState)) { - _chartState._tooltipBehaviorRenderer.onExit(position.dx, position.dy); + _renderingDetails.tooltipBehaviorRenderer + .onExit(position.dx, position.dy); } if (chart.crosshairBehavior.enable) { _chartState._crosshairBehaviorRenderer.onExit(position.dx, position.dy); @@ -3528,7 +3473,7 @@ class _ContainerArea extends StatelessWidget { } else { trackballPainter = _TrackballPainter( chartState: _chartState, - valueNotifier: _chartState._trackballRepaintNotifier); + valueNotifier: _chartState._repaintNotifiers['trackball']!); _chartState._trackballBehaviorRenderer._trackballPainter = trackballPainter; userInteractionWidgets.add(Container( @@ -3542,7 +3487,7 @@ class _ContainerArea extends StatelessWidget { if (chart.crosshairBehavior != null && chart.crosshairBehavior.enable) { crosshairPainter = _CrosshairPainter( chartState: _chartState, - valueNotifier: _chartState._crosshairRepaintNotifier); + valueNotifier: _chartState._repaintNotifiers['crosshair']!); _chartState._crosshairBehaviorRenderer._crosshairPainter = crosshairPainter; userInteractionWidgets.add(Container( @@ -3551,87 +3496,41 @@ class _ContainerArea extends StatelessWidget { decoration: const BoxDecoration(color: Colors.transparent), child: CustomPaint(painter: crosshairPainter))); } - final tooltip = chart.tooltipBehavior; + final TooltipBehavior tooltip = chart.tooltipBehavior; if (chart.tooltipBehavior.enable || _shouldShowAxisTooltip(_chartState)) { - _chartState._tooltipBehaviorRenderer._prevTooltipValue = - _chartState._tooltipBehaviorRenderer._currentTooltipValue = null; - _chartState._tooltipBehaviorRenderer._chartTooltip = SfTooltip( - color: tooltip.color ?? _chartState._chartTheme.tooltipColor, + _renderingDetails.tooltipBehaviorRenderer._prevTooltipValue = + _renderingDetails.tooltipBehaviorRenderer._currentTooltipValue = null; + _renderingDetails.tooltipBehaviorRenderer._chartTooltip = SfTooltip( + color: tooltip.color ?? _renderingDetails.chartTheme.tooltipColor, key: GlobalKey(), textStyle: tooltip.textStyle, animationDuration: tooltip.animationDuration, + animationCurve: const Interval(0.1, 0.8, curve: Curves.easeOutBack), enable: tooltip.enable, opacity: tooltip.opacity, borderColor: tooltip.borderColor, - borderWidth: tooltip.borderWidth, - duration: tooltip.duration, + borderWidth: tooltip.builder == null ? tooltip.borderWidth : 0, + duration: tooltip.duration.toInt(), shouldAlwaysShow: tooltip.shouldAlwaysShow, elevation: tooltip.elevation, canShowMarker: tooltip.canShowMarker, textAlignment: tooltip.textAlignment, decimalPlaces: tooltip.decimalPlaces, labelColor: tooltip.textStyle.color ?? - _chartState._chartTheme.tooltipLabelColor, + _renderingDetails.chartTheme.tooltipLabelColor, header: tooltip.header, format: tooltip.format, - builder: tooltip.builder, shadowColor: tooltip.shadowColor, onTooltipRender: chart.onTooltipRender != null - ? _chartState._tooltipBehaviorRenderer._tooltipRenderingEvent + ? _renderingDetails.tooltipBehaviorRenderer._tooltipRenderingEvent : null); - _chartWidgets.add(_chartState._tooltipBehaviorRenderer._chartTooltip!); + _renderingDetails.chartWidgets! + .add(_renderingDetails.tooltipBehaviorRenderer._chartTooltip!); } final Widget uiWidget = IgnorePointer( - ignoring: (chart.annotations != null), + ignoring: chart.annotations != null, child: Stack(children: userInteractionWidgets)); - _chartWidgets.add(uiWidget); - } - - /// Find point index for selection - void _calculatePointSeriesIndex(SfCartesianChart chart, Offset position) { - for (int i = 0; - i < _chartState._chartSeries.visibleSeriesRenderers.length; - i++) { - final CartesianSeriesRenderer seriesRenderer = - _chartState._chartSeries.visibleSeriesRenderers[i]; - final String _seriesType = seriesRenderer._seriesType; - int? pointIndex; - final double padding = (_seriesType == 'bubble') || - (_seriesType == 'scatter') || - (_seriesType == 'bar') || - (_seriesType == 'column' || - _seriesType == 'rangecolumn' || - _seriesType.contains('stackedcolumn') || - _seriesType.contains('stackedbar') || - _seriesType == 'waterfall') - ? 0 - : 15; - - /// regional padding to detect smooth touch - seriesRenderer._regionalData! - .forEach((dynamic regionRect, dynamic values) { - final Rect region = regionRect[0]; - final double left = region.left - padding; - final double right = region.right + padding; - final double top = region.top - padding; - final double bottom = region.bottom + padding; - final Rect paddedRegion = Rect.fromLTRB(left, top, right, bottom); - if (paddedRegion.contains(position)) { - pointIndex = regionRect[4].visiblePointIndex; - } - }); - - if (pointIndex != null) { - PointTapArgs pointTapArgs; - pointTapArgs = PointTapArgs( - i, - pointIndex!, - seriesRenderer._dataPoints, - seriesRenderer - ._visibleDataPoints![pointIndex!].overallDataPointIndex); - chart.onPointTapped!(pointTapArgs); - } - } + _renderingDetails.chartWidgets!.add(uiWidget); } /// Triggering onAxisLabelTapped event @@ -3672,9 +3571,7 @@ class _ContainerArea extends StatelessWidget { seriesRenderer: seriesRenderer as LineSeriesRenderer, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), painterKey: painterKey, animationController: controller, notifier: seriesRenderer._repaintNotifier); @@ -3686,9 +3583,7 @@ class _ContainerArea extends StatelessWidget { painterKey: painterKey, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), animationController: controller, notifier: seriesRenderer._repaintNotifier); break; @@ -3696,9 +3591,8 @@ class _ContainerArea extends StatelessWidget { customPainter = _ColumnChartPainter( chartState: _chartState, seriesRenderer: seriesRenderer as ColumnSeriesRenderer, - isRepaint: _chartState._zoomedState != null - ? _chartState._zoomedAxisRendererStates.isNotEmpty - : true, + isRepaint: !(_chartState._zoomedState != null) || + _chartState._zoomedAxisRendererStates.isNotEmpty, painterKey: painterKey, animationController: controller, notifier: seriesRenderer._repaintNotifier); @@ -3710,9 +3604,7 @@ class _ContainerArea extends StatelessWidget { seriesRenderer: seriesRenderer as ScatterSeriesRenderer, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), painterKey: painterKey, animationController: controller, notifier: seriesRenderer._repaintNotifier); @@ -3724,9 +3616,7 @@ class _ContainerArea extends StatelessWidget { painterKey: painterKey, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), animationController: controller, notifier: seriesRenderer._repaintNotifier); break; @@ -3736,9 +3626,7 @@ class _ContainerArea extends StatelessWidget { seriesRenderer: seriesRenderer as AreaSeriesRenderer, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), painterKey: painterKey, animationController: controller, notifier: seriesRenderer._repaintNotifier); @@ -3749,9 +3637,7 @@ class _ContainerArea extends StatelessWidget { seriesRenderer: seriesRenderer as BubbleSeriesRenderer, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), painterKey: painterKey, animationController: controller, notifier: seriesRenderer._repaintNotifier); @@ -3760,9 +3646,8 @@ class _ContainerArea extends StatelessWidget { customPainter = _BarChartPainter( chartState: _chartState, seriesRenderer: seriesRenderer as BarSeriesRenderer, - isRepaint: _chartState._zoomedState != null - ? _chartState._zoomedAxisRendererStates.isNotEmpty - : true, + isRepaint: ((_chartState._zoomedState != null) == false) || + _chartState._zoomedAxisRendererStates.isNotEmpty, painterKey: painterKey, animationController: controller, notifier: seriesRenderer._repaintNotifier); @@ -3773,9 +3658,7 @@ class _ContainerArea extends StatelessWidget { seriesRenderer: seriesRenderer as FastLineSeriesRenderer, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), painterKey: painterKey, animationController: controller, notifier: seriesRenderer._repaintNotifier); @@ -3787,9 +3670,7 @@ class _ContainerArea extends StatelessWidget { painterKey: painterKey, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), animationController: controller, notifier: seriesRenderer._repaintNotifier); break; @@ -3800,9 +3681,7 @@ class _ContainerArea extends StatelessWidget { painterKey: painterKey, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), animationController: controller, notifier: seriesRenderer._repaintNotifier); break; @@ -3813,9 +3692,7 @@ class _ContainerArea extends StatelessWidget { painterKey: painterKey, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), animationController: controller, notifier: seriesRenderer._repaintNotifier); break; @@ -3826,9 +3703,7 @@ class _ContainerArea extends StatelessWidget { painterKey: painterKey, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), animationController: controller, notifier: seriesRenderer._repaintNotifier); break; @@ -3839,9 +3714,7 @@ class _ContainerArea extends StatelessWidget { painterKey: painterKey, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), animationController: controller, notifier: seriesRenderer._repaintNotifier); break; @@ -3852,9 +3725,7 @@ class _ContainerArea extends StatelessWidget { painterKey: painterKey, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), animationController: controller, notifier: seriesRenderer._repaintNotifier); break; @@ -3865,9 +3736,7 @@ class _ContainerArea extends StatelessWidget { painterKey: painterKey, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), animationController: controller, notifier: seriesRenderer._repaintNotifier); break; @@ -3878,9 +3747,7 @@ class _ContainerArea extends StatelessWidget { painterKey: painterKey, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), animationController: controller, notifier: seriesRenderer._repaintNotifier); break; @@ -3891,9 +3758,7 @@ class _ContainerArea extends StatelessWidget { painterKey: painterKey, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), animationController: controller, notifier: seriesRenderer._repaintNotifier); break; @@ -3904,9 +3769,7 @@ class _ContainerArea extends StatelessWidget { painterKey: painterKey, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), animationController: controller, notifier: seriesRenderer._repaintNotifier); break; @@ -3917,9 +3780,7 @@ class _ContainerArea extends StatelessWidget { painterKey: painterKey, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), animationController: controller, notifier: seriesRenderer._repaintNotifier); break; @@ -3930,9 +3791,7 @@ class _ContainerArea extends StatelessWidget { painterKey: painterKey, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), animationController: controller, notifier: seriesRenderer._repaintNotifier); break; @@ -3943,9 +3802,7 @@ class _ContainerArea extends StatelessWidget { painterKey: painterKey, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), animationController: controller, notifier: seriesRenderer._repaintNotifier); break; @@ -3956,9 +3813,7 @@ class _ContainerArea extends StatelessWidget { painterKey: painterKey, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), animationController: controller, notifier: seriesRenderer._repaintNotifier); break; @@ -3970,9 +3825,7 @@ class _ContainerArea extends StatelessWidget { painterKey: painterKey, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), animationController: controller, notifier: seriesRenderer._repaintNotifier); break; @@ -3983,9 +3836,7 @@ class _ContainerArea extends StatelessWidget { painterKey: painterKey, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), animationController: controller, notifier: seriesRenderer._repaintNotifier); break; @@ -3997,9 +3848,7 @@ class _ContainerArea extends StatelessWidget { painterKey: painterKey, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), animationController: controller, notifier: seriesRenderer._repaintNotifier); break; @@ -4010,9 +3859,7 @@ class _ContainerArea extends StatelessWidget { painterKey: painterKey, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), animationController: controller, notifier: seriesRenderer._repaintNotifier); break; @@ -4023,9 +3870,7 @@ class _ContainerArea extends StatelessWidget { painterKey: painterKey, isRepaint: _chartState._zoomedState != null ? _chartState._zoomedAxisRendererStates.isNotEmpty - : (_chartState._legendToggling - ? true - : seriesRenderer._needsRepaint), + : (_chartState._legendToggling || seriesRenderer._needsRepaint), animationController: controller, notifier: seriesRenderer._repaintNotifier); break; diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/base/series_base.dart b/packages/syncfusion_flutter_charts/lib/src/chart/base/series_base.dart index 59a6b03dd..137674469 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/base/series_base.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/base/series_base.dart @@ -6,6 +6,7 @@ class _ChartSeries { //Here, we are using get keyword inorder to get the proper & updated instance of chart widget //When we initialize chart widget as a property to other classes like _ChartSeries, the chart widget is not updated properly and by using get we can rectify this. SfCartesianChart get chart => _chartState._chart; + _RenderingDetails get _renderingDetails => _chartState._renderingDetails; bool isStacked100 = false; int paletteIndex = 0; num sumOfYvalues = 0; @@ -90,7 +91,7 @@ class _ChartSeries { } if (seriesRenderer is HistogramSeriesRenderer) { final HistogramSeries series = - seriesRenderer._series as HistogramSeries; + seriesRenderer._series as HistogramSeries; for (int pointIndex = 0; pointIndex < series.dataSource.length; pointIndex++) { @@ -115,6 +116,7 @@ class _ChartSeries { dynamic yVal; num? low, high; num maxYValue = 0; + seriesRenderer._overAllDataPoints = ?>[]; for (int pointIndex = 0; pointIndex < series.dataSource.length;) { currentPoint = _getChartPoint( seriesRenderer, series.dataSource[pointIndex], pointIndex); @@ -123,6 +125,8 @@ class _ChartSeries { high = currentPoint?.high; low = currentPoint?.low; currentPoint?.overallDataPointIndex = pointIndex; + + seriesRenderer._overAllDataPoints.add(currentPoint); if (seriesRenderer is WaterfallSeriesRenderer) { yVal ??= 0; maxYValue += yVal; @@ -130,7 +134,7 @@ class _ChartSeries { } if (xVal != null) { - dynamic bubbleSize; + num bubbleSize; final dynamic xAxis = seriesRenderer._xAxisRenderer?._axis; final dynamic yAxis = seriesRenderer._yAxisRenderer?._axis; dynamic xMin = xAxis?.visibleMinimum; @@ -183,29 +187,33 @@ class _ChartSeries { } } - if ((!(isSelectionRangeChangeByEvent || + if (!(!(isSelectionRangeChangeByEvent || + _chartState._rangeChangeBySlider || _chartState._zoomedState == true || _chartState._zoomProgress) && (xMin != null || xMax != null || yMin != null || yMax != null) && - (yVal != null || (low != null && high != null))) - ? ((xMin != null && xMax != null) - ? (xPointValue >= xMin) && (xPointValue <= xMax) + (yVal != null || (low != null && high != null))) || + ((xMin != null && xMax != null) + ? (xPointValue >= xMin) == true && + (xPointValue <= xMax) == true : xMin != null ? xPointValue >= xMin : xMax != null ? xPointValue <= xMax - : false) || - ((yMin != null && yMax != null) - ? ((yVal ?? low) >= yMin) && ((yVal ?? high) <= yMax) + : false) == + true || + ((yMin != null && yMax != null) + ? ((yVal ?? low) >= yMin) == true && + ((yVal ?? high) <= yMax) == true : yMin != null ? (yVal ?? low) >= yMin : yMax != null ? (yVal ?? high) <= yMax - : false) - : true) { + : false) == + true) { _isXVisibleRange = true; _isYVisibleRange = true; seriesRenderer._dataPoints.add(currentPoint!); @@ -275,18 +283,17 @@ class _ChartSeries { seriesRenderer._needAnimateSeriesElements = true; seriesRenderer._needsAnimation = seriesRenderer._visible!; } else { - if (seriesRenderer._dataPoints.length <= - seriesRenderer._oldDataPoints!.length) { - seriesRenderer._needsAnimation = seriesRenderer._visible! && - _findChangesInPoint( - currentPoint, - seriesRenderer._oldDataPoints![ - seriesRenderer._dataPoints.length - 1], - seriesRenderer, - ); - } else { - seriesRenderer._needsAnimation = seriesRenderer._visible!; - } + seriesRenderer._needsAnimation = + (seriesRenderer._dataPoints.length <= + seriesRenderer._oldDataPoints!.length) + ? seriesRenderer._visible! && + _findChangesInPoint( + currentPoint, + seriesRenderer._oldDataPoints![ + seriesRenderer._dataPoints.length - 1], + seriesRenderer, + ) + : seriesRenderer._visible!; } } } @@ -413,17 +420,16 @@ class _ChartSeries { for (int i = 0; i < seriesRenderer._series.trendlines!.length; i++) { trendline = seriesRenderer._series.trendlines![i]; trendlineRenderer = seriesRenderer._trendlineRenderer[i]; - trendlineRenderer._isNeedRender = - trendlineRenderer._visible == true && - seriesRenderer._visible! && - (trendline.type == TrendlineType.polynomial - ? (trendline.polynomialOrder >= 2 && - trendline.polynomialOrder <= 6) - : trendline.type == TrendlineType.movingAverage - ? (trendline.period >= 2 && - trendline.period <= - seriesRenderer._series.dataSource.length - 1) - : true); + trendlineRenderer._isNeedRender = trendlineRenderer._visible == + true && + seriesRenderer._visible! && + (trendline.type == TrendlineType.polynomial + ? (trendline.polynomialOrder >= 2 && + trendline.polynomialOrder <= 6) + : !(trendline.type == TrendlineType.movingAverage) || + (trendline.period >= 2 && + trendline.period <= + seriesRenderer._series.dataSource.length - 1)); trendlineRenderer._animationController = AnimationController(vsync: _chartState) ..addListener(_chartState._repaintTrendlines); @@ -444,7 +450,7 @@ class _ChartSeries { (CartesianChartPoint firstPoint, CartesianChartPoint secondPoint) { if (seriesRenderer._series.sortingOrder == SortingOrder.ascending) { - return (firstPoint.sortValue == null) + return ((firstPoint.sortValue == null) ? -1 : (secondPoint.sortValue == null ? 1 @@ -452,10 +458,11 @@ class _ChartSeries { ? firstPoint.sortValue .toLowerCase() .compareTo(secondPoint.sortValue.toLowerCase()) - : firstPoint.sortValue.compareTo(secondPoint.sortValue))); + : firstPoint.sortValue + .compareTo(secondPoint.sortValue)))) as int; } else if (seriesRenderer._series.sortingOrder == SortingOrder.descending) { - return (firstPoint.sortValue == null) + return ((firstPoint.sortValue == null) ? 1 : (secondPoint.sortValue == null ? -1 @@ -463,7 +470,8 @@ class _ChartSeries { ? secondPoint.sortValue .toLowerCase() .compareTo(firstPoint.sortValue.toLowerCase()) - : secondPoint.sortValue.compareTo(firstPoint.sortValue))); + : secondPoint.sortValue + .compareTo(firstPoint.sortValue)))) as int; } else { return 0; } @@ -490,14 +498,13 @@ class _ChartSeries { if (seriesRenderer is _StackedSeriesRenderer && seriesRenderer._series is _StackedSeriesBase) { final _StackedSeriesBase stackedSeriesBase = - seriesRenderer._series as _StackedSeriesBase; + seriesRenderer._series as _StackedSeriesBase; if (seriesRenderer._dataPoints.isNotEmpty) { groupName = (seriesRenderer._seriesType.contains('stackedarea')) ? 'stackedareagroup' // ignore: unnecessary_null_comparison - : (stackedSeriesBase.groupName == null - ? ('series ' + i.toString()) - : stackedSeriesBase.groupName); + : stackedSeriesBase.groupName; + // : (stackedSeriesBase.groupName ?? ('series ' + i.toString())); stackedItemInfo = _StackedItemInfo(i, seriesRenderer); if (_chartState._chartSeries.clusterStackedItemInfo.isNotEmpty) { for (int k = 0; @@ -642,7 +649,7 @@ class _ChartSeries { if (seriesRenderer is _StackedSeriesRenderer && seriesRenderer._series is _StackedSeriesBase) { final _StackedSeriesBase stackedSeriesBase = - seriesRenderer._series as _StackedSeriesBase; + seriesRenderer._series as _StackedSeriesBase; if (seriesRenderer._dataPoints.isNotEmpty) { groupName = (seriesRenderer._seriesType == 'stackedarea100') ? 'stackedareagroup' @@ -779,6 +786,7 @@ class _ChartSeries { } else if (seriesRenderer is WaterfallSeriesRenderer) { seriesRenderer._seriesType = 'waterfall'; } + if (index == 0) { _chartState._requireInvertedAxis = (!chart.isTransposed && seriesRenderer._seriesType.toLowerCase().contains('bar')) || @@ -793,7 +801,7 @@ class _ChartSeries { if (chart.indicators != null && chart.indicators.isNotEmpty) { dynamic indicator; bool existField; - Map _map = Map(); + Map _map = {}; TechnicalIndicatorsRenderer technicalIndicatorsRenderer; if (!chart.legend.isVisible!) { final List textCollection = []; @@ -835,9 +843,10 @@ class _ChartSeries { : ' ' + count.toString())); } if (indicator != null && - indicator.isVisible && + indicator.isVisible == true && (indicator.dataSource != null || indicator.seriesName != null)) { - if (indicator.dataSource != null && indicator.dataSource.isNotEmpty) { + if (indicator.dataSource != null && + indicator.dataSource.isNotEmpty == true) { existField = technicalIndicatorsRenderer._indicatorType == 'SMA' || technicalIndicatorsRenderer._indicatorType == 'TMA' || technicalIndicatorsRenderer._indicatorType == 'EMA'; @@ -953,7 +962,8 @@ class _ChartSeries { technicalIndicatorsRenderer._dataPoints!.isNotEmpty) { indicator._initSeriesCollection( indicator, chart, technicalIndicatorsRenderer); - indicator._initDataSource(indicator, technicalIndicatorsRenderer); + indicator._initDataSource( + indicator, technicalIndicatorsRenderer, chart); if (technicalIndicatorsRenderer._renderPoints.isNotEmpty) { _chartState._chartSeries.visibleSeriesRenderers .addAll(technicalIndicatorsRenderer._targetSeriesRenderers); @@ -1020,13 +1030,14 @@ class _ChartSeries { seriesRenderer._xAxisRenderer as DateTimeCategoryAxisRenderer; seriesRenderer._dataPoints.sort((CartesianChartPoint point1, CartesianChartPoint point2) { - return point2.x.isAfter(point1.x) ? -1 : 1; + return point2.x.isAfter(point1.x) == true ? -1 : 1; }); axisRenderer._labels.sort((String first, String second) { return int.parse(first) < int.parse(second) ? -1 : 1; }); seriesRenderer._xValues?.sort(); - for (final CartesianChartPoint point in seriesRenderer._dataPoints) { + for (final CartesianChartPoint point + in seriesRenderer._dataPoints) { point.xValue = axisRenderer._labels .indexOf(point.x.microsecondsSinceEpoch.toString()); } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/bar_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/bar_segment.dart index e4a641438..a14aefd68 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/bar_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/bar_segment.dart @@ -51,14 +51,12 @@ class BarSegment extends ChartSegment { ..strokeWidth = _currentPoint!.isEmpty == true ? _series.emptyPointSettings.borderWidth : _strokeWidth!; - if (_series.borderGradient != null) { - strokePaint!.shader = - _series.borderGradient!.createShader(_currentPoint!.region!); - } else { - strokePaint!.color = _currentPoint!.isEmpty == true - ? _series.emptyPointSettings.borderColor - : _strokeColor!; - } + (_series.borderGradient != null) + ? strokePaint!.shader = + _series.borderGradient!.createShader(_currentPoint!.region!) + : strokePaint!.color = _currentPoint!.isEmpty == true + ? _series.emptyPointSettings.borderColor + : _strokeColor!; _series.borderWidth == 0 ? strokePaint!.color = Colors.transparent : strokePaint!.color; @@ -69,7 +67,8 @@ class BarSegment extends ChartSegment { /// Method to get series tracker fill. Paint _getTrackerFillPaint() { if (_series is BarSeries) { - final BarSeries barSeries = _series as BarSeries; + final BarSeries barSeries = + _series as BarSeries; _trackerFillPaint = Paint() ..color = barSeries.trackColor ..style = PaintingStyle.fill; @@ -79,7 +78,8 @@ class BarSegment extends ChartSegment { /// Method to get series tracker stroke color. Paint _getTrackerStrokePaint() { - final BarSeries barSeries = _series as BarSeries; + final BarSeries barSeries = + _series as BarSeries; _trackerStrokePaint = Paint() ..color = barSeries.trackBorderColor ..strokeWidth = barSeries.trackBorderWidth @@ -97,7 +97,8 @@ class BarSegment extends ChartSegment { /// Draws segment in series bounds. @override void onPaint(Canvas canvas) { - final BarSeries barSeries = _series as BarSeries; + final BarSeries barSeries = + _series as BarSeries; if (_trackerFillPaint != null && barSeries.isTrackVisible) { _drawSegmentRect(canvas, _trackBarRect, _trackerFillPaint!); } @@ -110,11 +111,9 @@ class BarSegment extends ChartSegment { _drawSegmentRect(canvas, segmentRect, fillPaint!); } if (strokePaint != null) { - if (_series.dashArray[0] != 0 && _series.dashArray[1] != 0) { - _drawDashedLine(canvas, _series.dashArray, strokePaint!, _path); - } else { - _drawSegmentRect(canvas, segmentRect, strokePaint!); - } + (_series.dashArray[0] != 0 && _series.dashArray[1] != 0) + ? _drawDashedLine(canvas, _series.dashArray, strokePaint!, _path) + : _drawSegmentRect(canvas, segmentRect, strokePaint!); } } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/box_and_whisker_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/box_and_whisker_segment.dart index 7a7272116..fffc7a8cf 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/box_and_whisker_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/box_and_whisker_segment.dart @@ -101,7 +101,7 @@ class BoxAndWhiskerSegment extends ChartSegment { /// Calculates the rendering bounds of a segment. @override void calculateSegmentPoints() { - _boxAndWhiskerSeries = _series as BoxAndWhiskerSeries; + _boxAndWhiskerSeries = _series as BoxAndWhiskerSeries; _x = _max = double.nan; _isTransposed = _seriesRenderer._chartState!._requireInvertedAxis; _minPoint = _currentPoint!.minimumPoint!; @@ -122,11 +122,10 @@ class BoxAndWhiskerSegment extends ChartSegment { _medianX = _currentPoint!.medianPoint!.x; _medianY = _currentPoint!.medianPoint!.y; - if (_lowerY > _upperY) { - _centersY = _upperY + ((_upperY - _lowerY).abs() / 2); - } else { - _centersY = _lowerY + ((_lowerY - _upperY).abs() / 2); - } + _centersY = (_lowerY > _upperY) + ? (_upperY + ((_upperY - _lowerY).abs() / 2)) + : (_lowerY + ((_lowerY - _upperY).abs() / 2)); + _topRectY = _centersY - ((_centersY - _upperY).abs() * 1); _bottomRectY = _centersY + ((_centersY - _lowerY).abs() * 1); } @@ -163,8 +162,8 @@ class BoxAndWhiskerSegment extends ChartSegment { /// To draw mean line path of box and whisker segments void _drawMeanLine( Canvas canvas, Offset position, Size size, bool isTransposed) { - final x = !isTransposed ? position.dx : position.dy; - final y = !isTransposed ? position.dy : position.dx; + final double x = !isTransposed ? position.dx : position.dy; + final double y = !isTransposed ? position.dy : position.dx; if (_series.animationDuration <= 0 || animationFactor >= _seriesRenderer._chartState!._seriesDurationFactor) { /// `0.15` is animation duration of mean point value, as like marker. @@ -208,9 +207,9 @@ class BoxAndWhiskerSegment extends ChartSegment { @override void onPaint(Canvas canvas) { if (fillPaint != null && _seriesRenderer._reAnimate || - (!(_seriesRenderer._chartState!._widgetNeedUpdate && + (!(_seriesRenderer._renderingDetails!.widgetNeedUpdate && _oldSeriesRenderer != null && - !_seriesRenderer._chartState!._isLegendToggled))) { + !_seriesRenderer._renderingDetails!.isLegendToggled))) { _path = Path(); if (!_isTransposed && _currentPoint!.lowerQuartile! > _currentPoint!.upperQuartile!) { @@ -219,7 +218,7 @@ class BoxAndWhiskerSegment extends ChartSegment { _lowerY = temp; } - if (_seriesRenderer._chartState!._isLegendToggled) { + if (_seriesRenderer._renderingDetails!.isLegendToggled) { animationFactor = 1; } if (_lowerY > _upperY) { @@ -357,7 +356,7 @@ class BoxAndWhiskerSegment extends ChartSegment { } } } - } else if (!_seriesRenderer._chartState!._isLegendToggled) { + } else if (!_seriesRenderer._renderingDetails!.isLegendToggled) { final BoxAndWhiskerSegment currentSegment = _seriesRenderer ._segments[currentSegmentIndex!] as BoxAndWhiskerSegment; final BoxAndWhiskerSegment? oldSegment = (currentSegment diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/bubble_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/bubble_segment.dart index 6e0edf389..19b1a1034 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/bubble_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/bubble_segment.dart @@ -15,7 +15,7 @@ class BubbleSegment extends ChartSegment { /// Gets the color of the series. @override Paint getFillPaint() { - final bool hasPointColor = _series.pointColorMapper != null ? true : false; + final bool hasPointColor = _series.pointColorMapper != null; if (_series.gradient == null) { if (_color != null) { fillPaint = Paint() @@ -54,14 +54,12 @@ class BubbleSegment extends ChartSegment { ..strokeWidth = _currentPoint!.isEmpty == true ? _series.emptyPointSettings.borderWidth : _strokeWidth!; - if (_series.borderGradient != null) { - _strokePaint.shader = - _series.borderGradient!.createShader(_currentPoint!.region!); - } else { - _strokePaint.color = _currentPoint!.isEmpty == true - ? _series.emptyPointSettings.borderColor - : _strokeColor!; - } + _series.borderGradient != null + ? _strokePaint.shader = + _series.borderGradient!.createShader(_currentPoint!.region!) + : _strokePaint.color = _currentPoint!.isEmpty == true + ? _series.emptyPointSettings.borderColor + : _strokeColor!; _series.borderWidth == 0 ? _strokePaint.color = Colors.transparent : _strokePaint.color; @@ -99,7 +97,8 @@ class BubbleSegment extends ChartSegment { /// To calculate and return the bubble size double _calculateBubbleRadius(BubbleSeriesRenderer _seriesRenderer) { - final BubbleSeries bubbleSeries = _series as BubbleSeries; + final BubbleSeries bubbleSeries = + _series as BubbleSeries; num bubbleRadius, sizeRange, radiusRange, maxSize, minSize; maxSize = _seriesRenderer._maxSize!; minSize = _seriesRenderer._minSize!; @@ -132,9 +131,9 @@ class BubbleSegment extends ChartSegment { @override void onPaint(Canvas canvas) { _segmentRect = RRect.fromRectAndRadius(_currentPoint!.region!, Radius.zero); - if (_seriesRenderer._chartState!._widgetNeedUpdate && + if (_seriesRenderer._renderingDetails!.widgetNeedUpdate && !_seriesRenderer._reAnimate && - !_seriesRenderer._chartState!._isLegendToggled && + !_seriesRenderer._renderingDetails!.isLegendToggled && _seriesRenderer._chartState!._oldSeriesRenderers.isNotEmpty && _oldSeriesRenderer != null && _oldSeriesRenderer!._segments.isNotEmpty && diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/candle_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/candle_segment.dart index 63d56b6b7..4301d34e9 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/candle_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/candle_segment.dart @@ -79,7 +79,7 @@ class CandleSegment extends ChartSegment { /// Calculates the rendering bounds of a segment. @override void calculateSegmentPoints() { - _candleSeries = _series as CandleSeries; + _candleSeries = _series as CandleSeries; _isBull = _currentPoint!.open < _currentPoint!.close; _x = _high = _low = double.nan; _isTransposed = _seriesRenderer._chartState!._requireInvertedAxis; @@ -184,18 +184,19 @@ class CandleSegment extends ChartSegment { void onPaint(Canvas canvas) { if (fillPaint != null && (_seriesRenderer._reAnimate || - !(_seriesRenderer._chartState!._widgetNeedUpdate && - !_seriesRenderer._chartState!._isLegendToggled))) { + !(_seriesRenderer._renderingDetails!.widgetNeedUpdate && + !_seriesRenderer._renderingDetails!.isLegendToggled))) { _path = Path(); _linePath = Path(); - if (!_isTransposed && _currentPoint!.open > _currentPoint!.close) { + if (!_isTransposed && + _currentPoint!.open > _currentPoint!.close == true) { final double temp = _closeY; _closeY = _openY; _openY = temp; } - if (_seriesRenderer._chartState!._isLegendToggled) { + if (_seriesRenderer._renderingDetails!.isLegendToggled) { animationFactor = 1; } _centersY = _closeY + ((_closeY - _openY).abs() / 2); @@ -214,7 +215,7 @@ class CandleSegment extends ChartSegment { : _topLineY; if (_isTransposed) { - _currentPoint!.open > _currentPoint!.close + _currentPoint!.open > _currentPoint!.close == true ? _calculateCandlePositions(_openX, _closeX) : _calculateCandlePositions(_closeX, _openX); @@ -266,22 +267,20 @@ class CandleSegment extends ChartSegment { } else { canvas.drawPath(_path, fillPaint!); if (fillPaint!.style == PaintingStyle.fill) { - if (_isTransposed) { - _showSameValue - ? canvas.drawLine( - Offset(_centerHighPoint.x, _centerHighPoint.y), - Offset(_centerLowPoint.x, _centerHighPoint.y), - fillPaint!) - : _drawFillLine(canvas); - } else { - _showSameValue - ? canvas.drawLine(Offset(_centerHighPoint.x, _highPoint.y), - Offset(_centerHighPoint.x, _lowPoint.y), fillPaint!) - : _drawLine(canvas); - } + _isTransposed + ? _showSameValue + ? canvas.drawLine( + Offset(_centerHighPoint.x, _centerHighPoint.y), + Offset(_centerLowPoint.x, _centerHighPoint.y), + fillPaint!) + : _drawFillLine(canvas) + : _showSameValue + ? canvas.drawLine(Offset(_centerHighPoint.x, _highPoint.y), + Offset(_centerHighPoint.x, _lowPoint.y), fillPaint!) + : _drawLine(canvas); } } - } else if (!_seriesRenderer._chartState!._isLegendToggled) { + } else if (!_seriesRenderer._renderingDetails!.isLegendToggled) { _currentSegment = _seriesRenderer._segments[currentSegmentIndex!] as CandleSegment; _oldSegment = !_seriesRenderer._reAnimate && diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/chart_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/chart_segment.dart index 16803500a..cda1cbf0c 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/chart_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/chart_segment.dart @@ -55,7 +55,8 @@ abstract class ChartSegment { Paint? _defaultFillColor, _defaultStrokeColor; /// Current index value. - int? currentSegmentIndex, _oldSegmentIndex; + int? currentSegmentIndex; + int? _oldSegmentIndex; late int _seriesIndex; CartesianChartPoint? _currentPoint, _point, _oldPoint, _nextPoint; @@ -71,4 +72,6 @@ abstract class ChartSegment { /// Cartesian chart state properties late SfCartesianChartState _chartState; + + _RenderingDetails get _renderingDetails => _chartState._renderingDetails; } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/column_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/column_segment.dart index 33cf54119..347c56683 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/column_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/column_segment.dart @@ -11,8 +11,6 @@ class ColumnSegment extends ChartSegment { /// Render path. late Path _path; late RRect _trackRect; - // @override - // CartesianChartPoint _currentPoint; Paint? _trackerFillPaint, _trackerStrokePaint; /// Rectangle of the segment this could be used to render the segment while overriding this segment @@ -54,14 +52,12 @@ class ColumnSegment extends ChartSegment { ..strokeWidth = _currentPoint!.isEmpty == true ? _series.emptyPointSettings.borderWidth : _strokeWidth!; - if (_series.borderGradient != null) { - strokePaint!.shader = - _series.borderGradient!.createShader(_currentPoint!.region!); - } else { - strokePaint!.color = _currentPoint!.isEmpty == true - ? _series.emptyPointSettings.borderColor - : _strokeColor!; - } + _series.borderGradient != null + ? strokePaint!.shader = + _series.borderGradient!.createShader(_currentPoint!.region!) + : strokePaint!.color = _currentPoint!.isEmpty == true + ? _series.emptyPointSettings.borderColor + : _strokeColor!; _series.borderWidth == 0 ? strokePaint!.color = Colors.transparent : strokePaint!.color; @@ -71,7 +67,8 @@ class ColumnSegment extends ChartSegment { /// Method to get series tracker fill. Paint _getTrackerFillPaint() { - final ColumnSeries columnSeries = _series as ColumnSeries; + final ColumnSeries columnSeries = + _series as ColumnSeries; _trackerFillPaint = Paint() ..color = columnSeries.trackColor ..style = PaintingStyle.fill; @@ -80,7 +77,8 @@ class ColumnSegment extends ChartSegment { /// Method to getseries tracker stroke color. Paint _getTrackerStrokePaint() { - final ColumnSeries columnSeries = _series as ColumnSeries; + final ColumnSeries columnSeries = + _series as ColumnSeries; _trackerStrokePaint = Paint() ..color = columnSeries.trackBorderColor ..strokeWidth = columnSeries.trackBorderWidth @@ -98,7 +96,8 @@ class ColumnSegment extends ChartSegment { /// Draws segment in series bounds. @override void onPaint(Canvas canvas) { - final ColumnSeries columnSeries = _series as ColumnSeries; + final ColumnSeries columnSeries = + _series as ColumnSeries; if (_trackerFillPaint != null && columnSeries.isTrackVisible) { _drawSegmentRect(canvas, _trackRect, _trackerFillPaint!); @@ -112,11 +111,9 @@ class ColumnSegment extends ChartSegment { _drawSegmentRect(canvas, segmentRect, fillPaint!); } if (strokePaint != null) { - if (_series.dashArray[0] != 0 && _series.dashArray[1] != 0) { - _drawDashedLine(canvas, _series.dashArray, strokePaint!, _path); - } else { - _drawSegmentRect(canvas, segmentRect, strokePaint!); - } + (_series.dashArray[0] != 0 && _series.dashArray[1] != 0) + ? _drawDashedLine(canvas, _series.dashArray, strokePaint!, _path) + : _drawSegmentRect(canvas, segmentRect, strokePaint!); } } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/hilo_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/hilo_segment.dart index 37037b01c..110fa5cba 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/hilo_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/hilo_segment.dart @@ -68,7 +68,7 @@ class HiloSegment extends ChartSegment { /// Calculates the rendering bounds of a segment. @override void calculateSegmentPoints() { - _hiloSeries = _series as HiloSeries; + _hiloSeries = _series as HiloSeries; _x = _high = _low = double.nan; _lowPoint = _currentPoint!.lowPoint!; _highPoint = _currentPoint!.highPoint!; @@ -97,8 +97,8 @@ class HiloSegment extends ChartSegment { @override void onPaint(Canvas canvas) { if (_series.animationDuration > 0 && - !_seriesRenderer._chartState!._isLegendToggled) { - if (!_seriesRenderer._chartState!._widgetNeedUpdate || + !_seriesRenderer._renderingDetails!.isLegendToggled) { + if (!_seriesRenderer._renderingDetails!.widgetNeedUpdate || _seriesRenderer._reAnimate) { if (_isTransposed) { _lowX = _lowPoint.x; diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/hiloopenclose_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/hiloopenclose_segment.dart index e67237710..dff22e210 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/hiloopenclose_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/hiloopenclose_segment.dart @@ -28,8 +28,6 @@ class HiloOpenCloseSegment extends ChartSegment { late Path _path; Color? _pointColorMapper; - // @override - // CartesianChartPoint _currentPoint; late _ChartLocation _centerLowPoint, _centerHighPoint, _lowPoint, _highPoint; late bool _showSameValue, _isTransposed, _isBull = false; late HiloOpenCloseSegment _currentSegment; @@ -82,7 +80,7 @@ class HiloOpenCloseSegment extends ChartSegment { /// Calculates the rendering bounds of a segment. @override void calculateSegmentPoints() { - _hiloOpenCloseSeries = _series as HiloOpenCloseSeries; + _hiloOpenCloseSeries = _series as HiloOpenCloseSeries; _isTransposed = _seriesRenderer._chartState!._requireInvertedAxis; _isBull = _currentPoint!.open < _currentPoint!.close; _lowPoint = _currentPoint!.lowPoint!; @@ -157,8 +155,8 @@ class HiloOpenCloseSegment extends ChartSegment { if (strokePaint != null) { _path = Path(); if (_series.animationDuration > 0 && - !_seriesRenderer._chartState!._isLegendToggled) { - if (!_seriesRenderer._chartState!._widgetNeedUpdate || + !_seriesRenderer._renderingDetails!.isLegendToggled) { + if (!_seriesRenderer._renderingDetails!.widgetNeedUpdate || _seriesRenderer._reAnimate) { if (_isTransposed) { _centerX = _highX + ((_lowX - _highX) / 2); @@ -240,12 +238,10 @@ class HiloOpenCloseSegment extends ChartSegment { _seriesRenderer); } } else { - if (_series.dashArray[0] != 0 && _series.dashArray[1] != 0) { - _drawDashedLine(canvas, _series.dashArray, strokePaint!, - _drawDashedHiloOpenClosePath(canvas)); - } else { - drawHiloOpenClosePath(canvas); - } + (_series.dashArray[0] != 0 && _series.dashArray[1] != 0) + ? _drawDashedLine(canvas, _series.dashArray, strokePaint!, + _drawDashedHiloOpenClosePath(canvas)) + : drawHiloOpenClosePath(canvas); } } } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/histogram_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/histogram_segment.dart index 0a8991b60..108f7829e 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/histogram_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/histogram_segment.dart @@ -11,8 +11,6 @@ class HistogramSegment extends ChartSegment { /// Render path. late Path _path; late RRect _trackRect; - // @override - // CartesianChartPoint _currentPoint; Paint? _trackerFillPaint, _trackerStrokePaint; //We are using `segmentRect` to draw the histogram segment in the series. @@ -80,7 +78,7 @@ class HistogramSegment extends ChartSegment { /// Method to get series tracker fill. Paint _getTrackerFillPaint() { final HistogramSeries histogramSeries = - _series as HistogramSeries; + _series as HistogramSeries; if (_color != null) { _trackerFillPaint = Paint() ..color = histogramSeries.trackColor @@ -92,7 +90,7 @@ class HistogramSegment extends ChartSegment { /// Method to get series tracker stroke color. Paint _getTrackerStrokePaint() { final HistogramSeries histogramSeries = - _series as HistogramSeries; + _series as HistogramSeries; _trackerStrokePaint = Paint() ..color = histogramSeries.trackBorderColor ..strokeWidth = histogramSeries.trackBorderWidth @@ -111,7 +109,7 @@ class HistogramSegment extends ChartSegment { @override void onPaint(Canvas canvas) { final HistogramSeries histogramSeries = - _series as HistogramSeries; + _series as HistogramSeries; if (_trackerFillPaint != null && histogramSeries.isTrackVisible) { _drawSegmentRect(_trackerFillPaint!, canvas, _trackRect); @@ -125,18 +123,16 @@ class HistogramSegment extends ChartSegment { _drawSegmentRect(fillPaint!, canvas, segmentRect); } if (strokePaint != null) { - if (_series.dashArray[0] != 0 && _series.dashArray[1] != 0) { - _drawDashedLine(canvas, _series.dashArray, strokePaint!, _path); - } else { - _drawSegmentRect(strokePaint!, canvas, segmentRect); - } + (_series.dashArray[0] != 0 && _series.dashArray[1] != 0) + ? _drawDashedLine(canvas, _series.dashArray, strokePaint!, _path) + : _drawSegmentRect(strokePaint!, canvas, segmentRect); } } /// To draw the rect of a given segment void _drawSegmentRect(Paint getPaint, Canvas canvas, RRect getRect) { - ((_chartState._initialRender! || - _chartState._isLegendToggled || + ((_renderingDetails.initialRender! || + _renderingDetails.isLegendToggled || (_series.key != null && _chartState._oldSeriesKeys.contains(_series.key))) && _series.animationDuration > 0) diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/line_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/line_segment.dart index 01f29dd61..8bb715dcf 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/line_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/line_segment.dart @@ -143,7 +143,7 @@ class LineSegment extends ChartSegment { _oldY1 = _oldSegment?._y1; _oldX2 = _oldSegment?._x2; _oldY2 = _oldSegment?._y2; - if (_oldSegment == null && _chartState._widgetNeedUpdate) { + if (_oldSegment == null && _renderingDetails.widgetNeedUpdate) { _newlyAddedSegment = true; prevX = prevSegment?._x2; prevY = prevSegment?._y2; @@ -190,34 +190,32 @@ class LineSegment extends ChartSegment { _oldY2 = _second.y; } } - if (_newlyAddedSegment) { - _animateToPoint( - canvas, - _seriesRenderer, - strokePaint!, - animationFactor, - _currentSegment._x1, - _currentSegment._y1, - _currentSegment._x2, - _currentSegment._y2, - prevX, - prevY); - } else { - _animateLineTypeSeries( - canvas, - _seriesRenderer, - strokePaint!, - animationFactor, - _currentSegment._x1, - _currentSegment._y1, - _currentSegment._x2, - _currentSegment._y2, - _oldX1, - _oldY1, - _oldX2, - _oldY2, - ); - } + _newlyAddedSegment + ? _animateToPoint( + canvas, + _seriesRenderer, + strokePaint!, + animationFactor, + _currentSegment._x1, + _currentSegment._y1, + _currentSegment._x2, + _currentSegment._y2, + prevX, + prevY) + : _animateLineTypeSeries( + canvas, + _seriesRenderer, + strokePaint!, + animationFactor, + _currentSegment._x1, + _currentSegment._y1, + _currentSegment._x2, + _currentSegment._y2, + _oldX1, + _oldY1, + _oldX2, + _oldY2, + ); } else { if (_series.dashArray[0] != 0 && _series.dashArray[1] != 0) { _path.moveTo(_x1, _y1); diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/range_area_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/range_area_segment.dart index a84e055b3..bbd724132 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/range_area_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/range_area_segment.dart @@ -69,7 +69,7 @@ class RangeAreaSegment extends ChartSegment { @override void onPaint(Canvas canvas) { final RangeAreaSeries _series = - this._series as RangeAreaSeries; + this._series as RangeAreaSeries; _pathRect = _path.getBounds(); canvas.drawPath( _path, (_series.gradient == null) ? fillPaint! : getFillPaint()); diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/range_column_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/range_column_segment.dart index 4b8b49f34..5aed9bce0 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/range_column_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/range_column_segment.dart @@ -13,8 +13,6 @@ class RangeColumnSegment extends ChartSegment { ///Path of the series late Path _path; late RRect _trackRect; - // @override - // CartesianChartPoint _currentPoint; Paint? _trackerFillPaint, _trackerStrokePaint; //We are using `segmentRect` to draw the histogram segment in the series. @@ -25,7 +23,7 @@ class RangeColumnSegment extends ChartSegment { /// Gets the color of the series. @override Paint getFillPaint() { - final bool hasPointColor = _series.pointColorMapper != null ? true : false; + final bool hasPointColor = _series.pointColorMapper != null; /// Get and set the paint options for range column series. if (_series.gradient == null) { @@ -63,14 +61,12 @@ class RangeColumnSegment extends ChartSegment { ? _series.emptyPointSettings.borderWidth : _strokeWidth!; _defaultStrokeColor = strokePaint; - if (_series.borderGradient != null) { - strokePaint!.shader = - _series.borderGradient!.createShader(_currentPoint!.region!); - } else { - strokePaint!.color = _currentPoint!.isEmpty == true - ? _series.emptyPointSettings.borderColor - : _strokeColor!; - } + _series.borderGradient != null + ? strokePaint!.shader = + _series.borderGradient!.createShader(_currentPoint!.region!) + : strokePaint!.color = _currentPoint!.isEmpty == true + ? _series.emptyPointSettings.borderColor + : _strokeColor!; _series.borderWidth == 0 ? strokePaint!.color = Colors.transparent : strokePaint!.color; @@ -80,7 +76,7 @@ class RangeColumnSegment extends ChartSegment { /// Method to get series tracker fill. Paint _getTrackerFillPaint() { final RangeColumnSeries _series = - this._series as RangeColumnSeries; + this._series as RangeColumnSeries; _trackerFillPaint = Paint() ..color = _series.trackColor @@ -92,7 +88,7 @@ class RangeColumnSegment extends ChartSegment { /// Method to get series tracker stroke color. Paint _getTrackerStrokePaint() { final RangeColumnSeries _series = - this._series as RangeColumnSeries; + this._series as RangeColumnSeries; _trackerStrokePaint = Paint() ..color = _series.trackBorderColor ..strokeWidth = _series.trackBorderWidth @@ -111,7 +107,7 @@ class RangeColumnSegment extends ChartSegment { @override void onPaint(Canvas canvas) { final RangeColumnSeries _series = - this._series as RangeColumnSeries; + this._series as RangeColumnSeries; if (_trackerFillPaint != null && _series.isTrackVisible) { canvas.drawRRect(_trackRect, _trackerFillPaint!); @@ -123,7 +119,7 @@ class RangeColumnSegment extends ChartSegment { if (fillPaint != null) { (_series.animationDuration > 0 && - !_seriesRenderer._chartState!._isLegendToggled) + !_seriesRenderer._renderingDetails!.isLegendToggled) ? _animateRangeColumn( canvas, _seriesRenderer, @@ -134,20 +130,18 @@ class RangeColumnSegment extends ChartSegment { : canvas.drawRRect(segmentRect, fillPaint!); } if (strokePaint != null) { - if (_series.dashArray[0] != 0 && _series.dashArray[1] != 0) { - _drawDashedLine(canvas, _series.dashArray, strokePaint!, _path); - } else { - (_series.animationDuration > 0 && - !_seriesRenderer._chartState!._isLegendToggled) - ? _animateRangeColumn( - canvas, - _seriesRenderer, - strokePaint!, - segmentRect, - _oldPoint != null ? _oldPoint!.region : _oldRegion, - animationFactor) - : canvas.drawRRect(segmentRect, strokePaint!); - } + (_series.dashArray[0] != 0 && _series.dashArray[1] != 0) + ? _drawDashedLine(canvas, _series.dashArray, strokePaint!, _path) + : (_series.animationDuration > 0 && + !_seriesRenderer._renderingDetails!.isLegendToggled) + ? _animateRangeColumn( + canvas, + _seriesRenderer, + strokePaint!, + segmentRect, + _oldPoint != null ? _oldPoint!.region : _oldRegion, + animationFactor) + : canvas.drawRRect(segmentRect, strokePaint!); } } } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/scatter_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/scatter_segment.dart index 9f87cf945..9f4651b57 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/scatter_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/scatter_segment.dart @@ -7,13 +7,10 @@ part of charts; /// /// Gets the path and color from the `series`. class ScatterSegment extends ChartSegment { - // @override - // CartesianChartPoint _currentPoint; - /// Gets the color of the series. @override Paint getFillPaint() { - final bool hasPointColor = _series.pointColorMapper != null ? true : false; + final bool hasPointColor = _series.pointColorMapper != null; if (_series.gradient == null) { if (_color != null) { fillPaint = Paint() @@ -74,9 +71,17 @@ class ScatterSegment extends ChartSegment { void onPaint(Canvas canvas) { if (fillPaint != null) { _series.animationDuration > 0 && - !_seriesRenderer._chartState!._isLegendToggled - ? _animateScatterSeries(_seriesRenderer, _point!, _oldPoint, - animationFactor, canvas, fillPaint!, strokePaint!) + !_seriesRenderer._renderingDetails!.isLegendToggled + ? _animateScatterSeries( + _seriesRenderer, + _point!, + _oldPoint, + animationFactor, + canvas, + fillPaint!, + strokePaint!, + currentSegmentIndex!, + this) : _seriesRenderer.drawDataMarker( currentSegmentIndex!, canvas, diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/spline_range_area_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/spline_range_area_segment.dart index 811e71bc6..865fa8a9e 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/spline_range_area_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/spline_range_area_segment.dart @@ -69,7 +69,7 @@ class SplineRangeAreaSegment extends ChartSegment { @override void onPaint(Canvas canvas) { final SplineRangeAreaSeries splineRangeAreaSeries = - _seriesRenderer._series as SplineRangeAreaSeries; + _seriesRenderer._series as SplineRangeAreaSeries; _pathRect = _path.getBounds(); canvas.drawPath( _path, (_series.gradient == null) ? fillPaint! : getFillPaint()); diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/spline_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/spline_segment.dart index 8915c2607..c324dd804 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/spline_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/spline_segment.dart @@ -22,9 +22,6 @@ class SplineSegment extends ChartSegment { /// End point Y value double? endControlY; - // @override - // CartesianChartPoint _currentPoint, _nextPoint; - Color? _pointColorMapper; late _ChartLocation _currentPointLocation, _nextPointLocation; late ChartAxisRenderer _xAxisRenderer, _yAxisRenderer; @@ -128,8 +125,8 @@ class SplineSegment extends ChartSegment { /// Draw spline series if (_series.animationDuration > 0 && !_seriesRenderer._reAnimate && - _seriesRenderer._chartState!._widgetNeedUpdate && - !_seriesRenderer._chartState!._isLegendToggled && + _seriesRenderer._renderingDetails!.widgetNeedUpdate && + !_seriesRenderer._renderingDetails!.isLegendToggled && _seriesRenderer._chartState!._oldSeriesRenderers.isNotEmpty && _oldSeries != null && _oldSeriesRenderer!._segments.isNotEmpty && diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stacked_area_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stacked_area_segment.dart index 61c4d3b71..fc74b880c 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stacked_area_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stacked_area_segment.dart @@ -66,7 +66,7 @@ class StackedAreaSegment extends ChartSegment { /// Draws segment in series bounds. @override void onPaint(Canvas canvas) { - _renderStackedAreaSeries(_seriesRenderer, fillPaint!, strokePaint!, canvas, - _seriesIndex, getFillPaint(), _path, _strokePath!); + _drawStackedAreaPath( + _path, _strokePath!, _seriesRenderer, canvas, fillPaint!, strokePaint!); } } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stacked_bar_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stacked_bar_segment.dart index a4ee1549c..def9f4fd5 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stacked_bar_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stacked_bar_segment.dart @@ -9,8 +9,6 @@ part of charts; class StackedBarSegment extends ChartSegment { /// Stacked values late double stackValues; - // @override - // CartesianChartPoint _currentPoint; /// Render path late Path _path; @@ -76,7 +74,7 @@ class StackedBarSegment extends ChartSegment { /// Method to get series tracker fill. Paint _getTrackerFillPaint() { final StackedBarSeries columnSeries = - _series as StackedBarSeries; + _series as StackedBarSeries; _trackerFillPaint = Paint() ..color = columnSeries.trackColor ..style = PaintingStyle.fill; @@ -85,7 +83,7 @@ class StackedBarSegment extends ChartSegment { /// Method to get _series tracker stroke color. Paint _getTrackerStrokePaint() { - _stackedBarSeries = _series as StackedBarSeries; + _stackedBarSeries = _series as StackedBarSeries; _trackerStrokePaint = Paint() ..color = _stackedBarSeries.trackBorderColor ..strokeWidth = _stackedBarSeries.trackBorderWidth @@ -103,7 +101,7 @@ class StackedBarSegment extends ChartSegment { /// Draws segment in series bounds. @override void onPaint(Canvas canvas) { - _stackedBarSeries = _series as StackedBarSeries; + _stackedBarSeries = _series as StackedBarSeries; if (_trackerFillPaint != null && _stackedBarSeries.isTrackVisible) { canvas.drawRRect(_trackRect, _trackerFillPaint!); } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stacked_column_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stacked_column_segment.dart index 0c18eddfe..b163fcd93 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stacked_column_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stacked_column_segment.dart @@ -9,8 +9,6 @@ part of charts; class StackedColumnSegment extends ChartSegment { /// Stack values. late double stackValues; - // @override - // CartesianChartPoint _currentPoint; /// Rendering path. late Path _path; @@ -77,7 +75,7 @@ class StackedColumnSegment extends ChartSegment { /// Method to get series tracker fill. Paint _getTrackerFillPaint() { - _stackedColumnSeries = _series as StackedColumnSeries; + _stackedColumnSeries = _series as StackedColumnSeries; _trackerFillPaint = Paint() ..color = _stackedColumnSeries.trackColor ..style = PaintingStyle.fill; @@ -86,7 +84,7 @@ class StackedColumnSegment extends ChartSegment { /// Method to get series tracker stroke color. Paint _getTrackerStrokePaint() { - _stackedColumnSeries = _series as StackedColumnSeries; + _stackedColumnSeries = _series as StackedColumnSeries; _trackerStrokePaint = Paint() ..color = _stackedColumnSeries.trackBorderColor ..strokeWidth = _stackedColumnSeries.trackBorderWidth @@ -104,7 +102,7 @@ class StackedColumnSegment extends ChartSegment { /// Draws segment in series bounds. @override void onPaint(Canvas canvas) { - _stackedColumnSeries = _series as StackedColumnSeries; + _stackedColumnSeries = _series as StackedColumnSeries; if (_trackerFillPaint != null && _stackedColumnSeries.isTrackVisible) { canvas.drawRRect(_trackRect, _trackerFillPaint!); } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stacked_line_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stacked_line_segment.dart index 5329e6f56..94b7dde46 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stacked_line_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stacked_line_segment.dart @@ -110,7 +110,7 @@ class StackedLineSegment extends ChartSegment { /// Draws segment in series bounds. @override void onPaint(Canvas canvas) { - _renderStackedLineSeries(_series as _StackedSeriesBase, canvas, - strokePaint!, _x1, _y1, _x2, _y2); + _renderStackedLineSeries(_series as _StackedSeriesBase, + canvas, strokePaint!, _x1, _y1, _x2, _y2); } } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stackedarea100_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stackedarea100_segment.dart index ba5e9c726..af9d93869 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stackedarea100_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stackedarea100_segment.dart @@ -66,7 +66,7 @@ class StackedArea100Segment extends ChartSegment { /// Draws segment in series bounds. @override void onPaint(Canvas canvas) { - _renderStackedAreaSeries(_seriesRenderer, fillPaint!, strokePaint!, canvas, - _seriesIndex, getFillPaint(), _path, _strokePath!); + _drawStackedAreaPath( + _path, _strokePath!, _seriesRenderer, canvas, fillPaint!, strokePaint!); } } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stackedbar100_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stackedbar100_segment.dart index 70d132c80..2755da27f 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stackedbar100_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stackedbar100_segment.dart @@ -9,8 +9,6 @@ part of charts; class StackedBar100Segment extends ChartSegment { /// staked value of the segment late double stackValues; - // @override - // CartesianChartPoint _currentPoint; ///Render path. late Path _path; diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stackedcolumn100_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stackedcolumn100_segment.dart index 102a4f725..ef01500cb 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stackedcolumn100_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stackedcolumn100_segment.dart @@ -9,8 +9,6 @@ part of charts; class StackedColumn100Segment extends ChartSegment { /// Stacked value. late double stackValues; - // @override - // CartesianChartPoint _currentPoint; /// Rendering path. late Path _path; diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stackedline100_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stackedline100_segment.dart index 3ae0033b5..cc2243ea9 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stackedline100_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stackedline100_segment.dart @@ -110,7 +110,7 @@ class StackedLine100Segment extends ChartSegment { /// Draws segment in series bounds. @override void onPaint(Canvas canvas) { - _renderStackedLineSeries(_series as _StackedSeriesBase, canvas, - strokePaint!, _x1, _y1, _x2, _y2); + _renderStackedLineSeries(_series as _StackedSeriesBase, + canvas, strokePaint!, _x1, _y1, _x2, _y2); } } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stepline_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stepline_segment.dart index a90bdfb23..8dd93fb84 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stepline_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/stepline_segment.dart @@ -110,8 +110,8 @@ class StepLineSegment extends ChartSegment { _path = Path(); if (_series.animationDuration > 0 && !_seriesRenderer._reAnimate && - _seriesRenderer._chartState!._widgetNeedUpdate && - !_seriesRenderer._chartState!._isLegendToggled && + _seriesRenderer._renderingDetails!.widgetNeedUpdate && + !_seriesRenderer._renderingDetails!.isLegendToggled && _seriesRenderer._chartState!._oldSeriesRenderers.isNotEmpty && _oldSeriesRenderer != null && _oldSeriesRenderer!._segments.isNotEmpty && diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/waterfall_segment.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/waterfall_segment.dart index ab130b239..ea6685207 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/waterfall_segment.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_segment/waterfall_segment.dart @@ -19,9 +19,6 @@ class WaterfallSegment extends ChartSegment { /// Colors of the negative point, intermediate point and total point. Color? _negativePointsColor, _intermediateSumColor, _totalSumColor; - // @override - // CartesianChartPoint _currentPoint; - /// We are using `segmentRect` to draw the bar segment in the series. /// we can override this class and customize the waterfall segment by getting `segmentRect`. late RRect segmentRect; @@ -29,7 +26,7 @@ class WaterfallSegment extends ChartSegment { /// Gets the color of the series. @override Paint getFillPaint() { - final bool hasPointColor = _series.pointColorMapper != null ? true : false; + final bool hasPointColor = _series.pointColorMapper != null; /// Get and set the paint options for waterfall series. if (_series.gradient == null) { @@ -40,7 +37,7 @@ class WaterfallSegment extends ChartSegment { ? _intermediateSumColor ?? _color! : _currentPoint!.isTotalSum! ? _totalSumColor ?? _color! - : _currentPoint!.yValue < 0 + : _currentPoint!.yValue < 0 == true ? _negativePointsColor ?? _color! : _color!)! ..style = PaintingStyle.fill; @@ -64,12 +61,13 @@ class WaterfallSegment extends ChartSegment { /// Get the color of connector lines. Paint _getConnectorLineStrokePaint() { - final WaterfallSeries series = _series as WaterfallSeries; + final WaterfallSeries series = + _series as WaterfallSeries; connectorLineStrokePaint = Paint() ..style = PaintingStyle.stroke ..strokeWidth = series.connectorLineSettings.width ..color = series.connectorLineSettings.color ?? - _chartState._chartTheme.waterfallConnectorLineColor; + _renderingDetails.chartTheme.waterfallConnectorLineColor; return connectorLineStrokePaint!; } @@ -80,12 +78,10 @@ class WaterfallSegment extends ChartSegment { ..style = PaintingStyle.stroke ..strokeWidth = _strokeWidth!; _defaultStrokeColor = strokePaint; - if (_series.borderGradient != null) { - strokePaint!.shader = - _series.borderGradient!.createShader(_currentPoint!.region!); - } else { - strokePaint!.color = _strokeColor!; - } + _series.borderGradient != null + ? strokePaint!.shader = + _series.borderGradient!.createShader(_currentPoint!.region!) + : strokePaint!.color = _strokeColor!; _series.borderWidth == 0 ? strokePaint!.color = Colors.transparent : strokePaint!.color; @@ -100,13 +96,13 @@ class WaterfallSegment extends ChartSegment { @override void onPaint(Canvas canvas) { final WaterfallSeries _series = - this._series as WaterfallSeries; + this._series as WaterfallSeries; CartesianChartPoint oldPaint; final Path linePath = Path(); if (fillPaint != null) { (_series.animationDuration > 0 && - !_seriesRenderer._chartState!._isLegendToggled) + !_seriesRenderer._renderingDetails!.isLegendToggled) ? _animateRangeColumn( canvas, _seriesRenderer, @@ -117,20 +113,18 @@ class WaterfallSegment extends ChartSegment { : canvas.drawRRect(segmentRect, fillPaint!); } if (strokePaint != null) { - if (_series.dashArray[0] != 0 && _series.dashArray[1] != 0) { - _drawDashedLine(canvas, _series.dashArray, strokePaint!, _path); - } else { - (_series.animationDuration > 0 && - !_seriesRenderer._chartState!._isLegendToggled) - ? _animateRangeColumn( - canvas, - _seriesRenderer, - strokePaint!, - segmentRect, - _oldPoint != null ? _oldPoint!.region : _oldRegion, - animationFactor) - : canvas.drawRRect(segmentRect, strokePaint!); - } + (_series.dashArray[0] != 0 && _series.dashArray[1] != 0) + ? _drawDashedLine(canvas, _series.dashArray, strokePaint!, _path) + : (_series.animationDuration > 0 && + !_seriesRenderer._renderingDetails!.isLegendToggled) + ? _animateRangeColumn( + canvas, + _seriesRenderer, + strokePaint!, + segmentRect, + _oldPoint != null ? _oldPoint!.region : _oldRegion, + animationFactor) + : canvas.drawRRect(segmentRect, strokePaint!); } if (connectorLineStrokePaint != null && _currentPoint!.overallDataPointIndex! > 0) { diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/area_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/area_series.dart index cfaa27b39..6bbec44db 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/area_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/area_series.dart @@ -1,6 +1,6 @@ part of charts; -/// This class Renders the area series. +/// This class renders the area series. /// /// To render an area chart, create an instance of AreaSeries, and add it to the series collection property of SfCartesianChart. /// The area chart shows the filled area to represent the data, but when there are more than a series, this may hide the other series. @@ -8,6 +8,8 @@ part of charts; /// /// It provides options for color, opacity, border color, and border width to customize the appearance. /// +/// {@youtube 560 315 https://www.youtube.com/watch?v=E_odUnOsBtQ} +@immutable class AreaSeries extends XyDataSeries { /// Creating an argument constructor of AreaSeries class. AreaSeries( @@ -36,19 +38,23 @@ class AreaSeries extends XyDataSeries { double? borderWidth, LinearGradient? gradient, LinearGradient? borderGradient, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, String? legendItemText, double? opacity, this.borderDrawMode = BorderDrawMode.top, - SeriesRendererCreatedCallback? onRendererCreated}) + SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress}) : super( key: key, onRendererCreated: onRendererCreated, onCreateRenderer: onCreateRenderer, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, xValueMapper: xValueMapper, yValueMapper: yValueMapper, sortFieldValueMapper: sortFieldValueMapper, @@ -71,7 +77,6 @@ class AreaSeries extends XyDataSeries { borderWidth: borderWidth, gradient: gradient, borderGradient: borderGradient, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, @@ -81,7 +86,7 @@ class AreaSeries extends XyDataSeries { ///Border type of area series. /// - ///It have the three types of [BorderDrawMode], + ///It has three types of [BorderDrawMode], /// ///* [BorderDrawMode.all] renders border for all the sides of area. /// @@ -120,6 +125,95 @@ class AreaSeries extends XyDataSeries { } return AreaSeriesRenderer(); } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is AreaSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.pointColorMapper == pointColorMapper && + other.dataLabelMapper == dataLabelMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.color == color && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.gradient == gradient && + other.borderGradient == borderGradient && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.borderDrawMode == borderDrawMode && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + yValueMapper, + sortFieldValueMapper, + pointColorMapper, + dataLabelMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + color, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + dashArray, + animationDuration, + borderColor, + borderWidth, + gradient, + borderGradient, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + borderDrawMode, + onRendererCreated, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } } /// Creates series renderer for Area series @@ -134,15 +228,17 @@ class AreaSeriesRenderer extends XyDataSeriesRenderer { final AreaSegment segment = createSegment(); final List oldSeriesRenderers = _chartState!._oldSeriesRenderers; - segment._series = _series as XyDataSeries; + segment._series = _series as XyDataSeries; segment.currentSegmentIndex = 0; - if (_points != null) segment.points = _points; + if (_points != null) { + segment.points = _points; + } segment._seriesRenderer = this; segment._seriesIndex = seriesIndex; segment.animationFactor = animateFactor; segment._path = path; segment._strokePath = strokePath; - if (_chartState!._widgetNeedUpdate && + if (_renderingDetails!.widgetNeedUpdate && // ignore: unnecessary_null_comparison oldSeriesRenderers != null && oldSeriesRenderers.isNotEmpty && diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/bar_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/bar_series.dart index 4c9e31e33..015e60f66 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/bar_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/bar_series.dart @@ -8,6 +8,8 @@ part of charts; /// /// Provides options for color, opacity, border color and border width to customize the appearance. /// +/// {@youtube 560 315 https://www.youtube.com/watch?v=MHQzCN_jD1Q} +@immutable class BarSeries extends XyDataSeries { /// Creating an argument constructor of BarSeries class. BarSeries( @@ -43,8 +45,6 @@ class BarSeries extends XyDataSeries { List? trendlines, Color? borderColor, double? borderWidth, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, @@ -52,12 +52,18 @@ class BarSeries extends XyDataSeries { double? opacity, List? dashArray, SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, List? initialSelectedDataIndexes}) : super( key: key, onCreateRenderer: onCreateRenderer, name: name, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, xValueMapper: xValueMapper, yValueMapper: yValueMapper, sortFieldValueMapper: sortFieldValueMapper, @@ -79,7 +85,6 @@ class BarSeries extends XyDataSeries { animationDuration: animationDuration, borderColor: borderColor, borderWidth: borderWidth, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, @@ -252,6 +257,109 @@ class BarSeries extends XyDataSeries { } return BarSeriesRenderer(); } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is BarSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.pointColorMapper == pointColorMapper && + other.dataLabelMapper == dataLabelMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.color == color && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.gradient == gradient && + other.borderGradient == borderGradient && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.trackColor == trackColor && + other.trackBorderColor == trackBorderColor && + other.trackBorderWidth == trackBorderWidth && + other.trackPadding == trackPadding && + other.spacing == spacing && + other.borderRadius == borderRadius && + other.isTrackVisible == isTrackVisible && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress && + other.initialSelectedDataIndexes == initialSelectedDataIndexes; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + yValueMapper, + sortFieldValueMapper, + pointColorMapper, + dataLabelMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + color, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + dashArray, + animationDuration, + borderColor, + borderWidth, + gradient, + borderGradient, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + trackColor, + trackBorderColor, + trackBorderWidth, + trackPadding, + spacing, + borderRadius, + isTrackVisible, + onRendererCreated, + initialSelectedDataIndexes, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } } /// Creates series renderer for Bar series @@ -268,7 +376,8 @@ class BarSeriesRenderer extends XyDataSeriesRenderer { /// To add bar segments to chart segments ChartSegment _createSegments(CartesianChartPoint currentPoint, int pointIndex, int seriesIndex, double animateFactor) { - final BarSeries _barSeries = _series as BarSeries; + final BarSeries _barSeries = + _series as BarSeries; final BarSegment segment = createSegment(); final List oldSeriesRenderers = _chartState!._oldSeriesRenderers; @@ -282,8 +391,8 @@ class BarSeriesRenderer extends XyDataSeriesRenderer { .add(Offset(currentPoint.markerPoint!.x, currentPoint.markerPoint!.y)); segment.animationFactor = animateFactor; segment._currentPoint = currentPoint; - if (_chartState!._widgetNeedUpdate && - !_chartState!._isLegendToggled && + if (_renderingDetails!.widgetNeedUpdate && + !_renderingDetails!.isLegendToggled && // ignore: unnecessary_null_comparison oldSeriesRenderers != null && oldSeriesRenderers.isNotEmpty && @@ -297,7 +406,16 @@ class BarSeriesRenderer extends XyDataSeriesRenderer { ? segment._oldSeriesRenderer!._dataPoints[pointIndex] : null; segment._oldSegmentIndex = _getOldSegmentIndex(segment); - } else if (_chartState!._isLegendToggled && + if ((_chartState!._selectedSegments.length - 1 >= pointIndex) && + _chartState?._selectedSegments[pointIndex]._oldSegmentIndex == null) { + final ChartSegment selectedSegment = + _chartState?._selectedSegments[pointIndex] as ChartSegment; + selectedSegment._oldSeriesRenderer = + oldSeriesRenderers[selectedSegment._seriesIndex]; + selectedSegment._seriesRenderer = this; + selectedSegment._oldSegmentIndex = _getOldSegmentIndex(selectedSegment); + } + } else if (_renderingDetails!.isLegendToggled && // ignore: unnecessary_null_comparison _chartState!._segments != null && _chartState!._segments.isNotEmpty) { diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/box_and_whisker_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/box_and_whisker_series.dart index e6dccd999..6c92465c7 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/box_and_whisker_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/box_and_whisker_series.dart @@ -8,6 +8,7 @@ part of charts; /// Provides options for color, opacity, border color, and border width /// to customize the appearance. /// +@immutable class BoxAndWhiskerSeries extends XyDataSeries { /// Creating an argument constructor of BoxAndWhiskerSeries class. BoxAndWhiskerSeries( @@ -35,8 +36,6 @@ class BoxAndWhiskerSeries extends XyDataSeries { double? borderWidth, LinearGradient? gradient, LinearGradient? borderGradient, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, @@ -44,6 +43,9 @@ class BoxAndWhiskerSeries extends XyDataSeries { List? dashArray, double? opacity, SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, List? trendlines, this.boxPlotMode = BoxPlotMode.normal, this.showMean = true}) @@ -52,6 +54,9 @@ class BoxAndWhiskerSeries extends XyDataSeries { onCreateRenderer: onCreateRenderer, name: name, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, dashArray: dashArray, xValueMapper: xValueMapper, yValueMapper: yValueMapper, @@ -72,7 +77,6 @@ class BoxAndWhiskerSeries extends XyDataSeries { borderWidth: borderWidth ?? 1, gradient: gradient, borderGradient: borderGradient, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, @@ -174,13 +178,106 @@ class BoxAndWhiskerSeries extends XyDataSeries { } return BoxAndWhiskerSeriesRenderer(); } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is BoxAndWhiskerSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.pointColorMapper == pointColorMapper && + other.dataLabelMapper == dataLabelMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.color == color && + other.width == width && + other.markerSettings == markerSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.gradient == gradient && + other.borderGradient == borderGradient && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.boxPlotMode == boxPlotMode && + other.showMean == showMean && + other.spacing == spacing && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + yValueMapper, + sortFieldValueMapper, + pointColorMapper, + dataLabelMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + color, + width, + markerSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + dashArray, + animationDuration, + borderColor, + borderWidth, + gradient, + borderGradient, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + boxPlotMode, + showMean, + spacing, + onRendererCreated, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } } class _BoxPlotQuartileValues { _BoxPlotQuartileValues( {this.minimum, this.maximum, - //ignore: unused_element + //ignore: unused_element, avoid_unused_constructor_parameters List? outliers, this.upperQuartile, this.lowerQuartile, @@ -219,6 +316,7 @@ class BoxAndWhiskerSeriesRenderer extends XyDataSeriesRenderer { final int yCount = yValues.length; _boxPlotQuartileValues = _BoxPlotQuartileValues(); _boxPlotQuartileValues.average = + //ignore: always_specify_types (yValues.fold(0, (num x, y) => (x.toDouble()) + y!)) / yCount; if (mode == BoxPlotMode.exclusive) { _boxPlotQuartileValues.lowerQuartile = @@ -284,9 +382,8 @@ class BoxAndWhiskerSeriesRenderer extends XyDataSeriesRenderer { final num rank = percentile * (count - 1); final int integerRank = (rank.abs()).floor(); final num fractionRank = rank - integerRank; - value = - (fractionRank * (yValues[integerRank + 1]! - yValues[integerRank]!) + - yValues[integerRank]!); + value = fractionRank * (yValues[integerRank + 1]! - yValues[integerRank]!) + + yValues[integerRank]!; return value.toDouble(); } @@ -305,7 +402,7 @@ class BoxAndWhiskerSeriesRenderer extends XyDataSeriesRenderer { if (count == 1) { _boxPlotQuartileValues.lowerQuartile = yValues[0]; _boxPlotQuartileValues.upperQuartile = yValues[0]; - return null; + return; } final bool isEvenList = count % 2 == 0; final num halfLength = count ~/ 2; @@ -349,12 +446,12 @@ class BoxAndWhiskerSeriesRenderer extends XyDataSeriesRenderer { _segment._seriesIndex = seriesIndex; _segment.currentSegmentIndex = pointIndex; _segment._seriesRenderer = this; - _segment._series = _series as XyDataSeries; + _segment._series = _series as XyDataSeries; _segment.animationFactor = animateFactor; _segment._pointColorMapper = currentPoint.pointColorMapper; _segment._currentPoint = currentPoint; - if (_chartState!._widgetNeedUpdate && - !_chartState!._isLegendToggled && + if (_renderingDetails!.widgetNeedUpdate && + !_renderingDetails!.isLegendToggled && _oldSeriesRenderers != null && _oldSeriesRenderers!.isNotEmpty && _oldSeriesRenderers!.length - 1 >= _segment._seriesIndex && diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/bubble_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/bubble_series.dart index 409472dc1..0809aec30 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/bubble_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/bubble_series.dart @@ -7,6 +7,7 @@ part of charts; /// /// Provide the options for color, opacity, border color, and border width to customize the appearance. /// +@immutable class BubbleSeries extends XyDataSeries { /// Creating an argument constructor of BubbleSeries class. BubbleSeries( @@ -37,8 +38,6 @@ class BubbleSeries extends XyDataSeries { double? borderWidth, LinearGradient? gradient, LinearGradient? borderGradient, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, @@ -46,12 +45,18 @@ class BubbleSeries extends XyDataSeries { String? legendItemText, double? opacity, SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, List? initialSelectedDataIndexes}) : super( key: key, onCreateRenderer: onCreateRenderer, name: name, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, xValueMapper: xValueMapper, yValueMapper: yValueMapper, sortFieldValueMapper: sortFieldValueMapper, @@ -74,7 +79,6 @@ class BubbleSeries extends XyDataSeries { borderWidth: borderWidth, gradient: gradient, borderGradient: borderGradient, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, @@ -133,6 +137,99 @@ class BubbleSeries extends XyDataSeries { } return BubbleSeriesRenderer(); } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is BubbleSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.pointColorMapper == pointColorMapper && + other.dataLabelMapper == dataLabelMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.color == color && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.gradient == gradient && + other.borderGradient == borderGradient && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.maximumRadius == maximumRadius && + other.minimumRadius == minimumRadius && + other.initialSelectedDataIndexes == other.initialSelectedDataIndexes && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + yValueMapper, + sortFieldValueMapper, + pointColorMapper, + dataLabelMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + color, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + dashArray, + animationDuration, + borderColor, + borderWidth, + gradient, + borderGradient, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + maximumRadius, + minimumRadius, + initialSelectedDataIndexes, + onRendererCreated, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } } /// Creates series renderer for Bubble series @@ -149,7 +246,7 @@ class BubbleSeriesRenderer extends XyDataSeriesRenderer { /// To add bubble segments to segment list ChartSegment _createSegments(CartesianChartPoint currentPoint, int pointIndex, int seriesIndex, double animateFactor) { - final BubbleSegment segment = this.createSegment(); + final BubbleSegment segment = createSegment(); final List oldSeriesRenderers = _chartState!._oldSeriesRenderers; _isRectSeries = false; @@ -158,11 +255,11 @@ class BubbleSeriesRenderer extends XyDataSeriesRenderer { segment.points .add(Offset(currentPoint.markerPoint!.x, currentPoint.markerPoint!.y)); segment._seriesIndex = seriesIndex; - segment._series = _series as XyDataSeries; + segment._series = _series as XyDataSeries; segment.animationFactor = animateFactor; segment._currentPoint = currentPoint; segment._seriesRenderer = this; - if (_chartState!._widgetNeedUpdate && + if (_renderingDetails!.widgetNeedUpdate && oldSeriesRenderers.isNotEmpty && oldSeriesRenderers.length - 1 >= segment._seriesIndex && oldSeriesRenderers[segment._seriesIndex]._seriesName == @@ -173,6 +270,15 @@ class BubbleSeriesRenderer extends XyDataSeriesRenderer { ? segment._oldSeriesRenderer!._dataPoints[pointIndex] : null; segment._oldSegmentIndex = _getOldSegmentIndex(segment); + if ((_chartState!._selectedSegments.length - 1 >= pointIndex) && + _chartState?._selectedSegments[pointIndex]._oldSegmentIndex == null) { + final ChartSegment selectedSegment = + _chartState?._selectedSegments[pointIndex] as ChartSegment; + selectedSegment._oldSeriesRenderer = + oldSeriesRenderers[selectedSegment._seriesIndex]; + selectedSegment._seriesRenderer = this; + selectedSegment._oldSegmentIndex = _getOldSegmentIndex(selectedSegment); + } } segment.calculateSegmentPoints(); customizeSegment(segment); diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/candle_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/candle_series.dart index 18af490f7..dda6f7f7b 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/candle_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/candle_series.dart @@ -10,6 +10,8 @@ part of charts; /// Provides options for color, opacity, border color, and border width /// to customize the appearance. /// +/// {@youtube 560 315 https://www.youtube.com/watch?v=g5cniDExpRw} +@immutable class CandleSeries extends _FinancialSeriesBase { /// Creating an argument constructor of CandleSeries class. CandleSeries({ @@ -37,8 +39,6 @@ class CandleSeries extends _FinancialSeriesBase { bool? enableTooltip, double? animationDuration, double? borderWidth, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, @@ -46,6 +46,9 @@ class CandleSeries extends _FinancialSeriesBase { List? dashArray, double? opacity, SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, List? initialSelectedDataIndexes, bool? showIndicationForSameValues, List? trendlines, @@ -54,6 +57,9 @@ class CandleSeries extends _FinancialSeriesBase { onCreateRenderer: onCreateRenderer, name: name, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, dashArray: dashArray, xValueMapper: xValueMapper, lowValueMapper: lowValueMapper, @@ -78,7 +84,6 @@ class CandleSeries extends _FinancialSeriesBase { enableTooltip: enableTooltip, animationDuration: animationDuration, borderWidth: borderWidth ?? 2, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, @@ -104,6 +109,99 @@ class CandleSeries extends _FinancialSeriesBase { } return CandleSeriesRenderer(); } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is CandleSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.lowValueMapper == lowValueMapper && + other.highValueMapper == highValueMapper && + other.openValueMapper == openValueMapper && + other.closeValueMapper == closeValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.pointColorMapper == pointColorMapper && + other.dataLabelMapper == dataLabelMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.bearColor == bearColor && + other.bullColor == bullColor && + other.enableSolidCandles == enableSolidCandles && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.borderWidth == borderWidth && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.showIndicationForSameValues == showIndicationForSameValues && + other.initialSelectedDataIndexes == other.initialSelectedDataIndexes && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + lowValueMapper, + highValueMapper, + openValueMapper, + closeValueMapper, + sortFieldValueMapper, + pointColorMapper, + dataLabelMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + bearColor, + bullColor, + enableSolidCandles, + emptyPointSettings, + dataLabelSettings, + isVisible, + enableTooltip, + animationDuration, + borderWidth, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + dashArray, + opacity, + onRendererCreated, + initialSelectedDataIndexes, + showIndicationForSameValues, + trendlines, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } } /// Creates series renderer for Candle series @@ -112,9 +210,11 @@ class CandleSeriesRenderer extends _FinancialSerieBaseRenderer { CandleSeriesRenderer(); // Store the rect position // + @override late num _rectPosition; // Store the rect count // + @override late num _rectCount; late CandleSegment _candleSegment, _segment; @@ -136,12 +236,12 @@ class CandleSeriesRenderer extends _FinancialSerieBaseRenderer { _segment._seriesIndex = seriesIndex; _segment.currentSegmentIndex = pointIndex; _segment._seriesRenderer = this; - _segment._series = _series as XyDataSeries; + _segment._series = _series as XyDataSeries; _segment.animationFactor = animateFactor; _segment._pointColorMapper = currentPoint.pointColorMapper; _segment._currentPoint = currentPoint; - if (_chartState!._widgetNeedUpdate && - !_chartState!._isLegendToggled && + if (_renderingDetails!.widgetNeedUpdate && + !_renderingDetails!.isLegendToggled && _oldSeriesRenderers != null && _oldSeriesRenderers!.isNotEmpty && _oldSeriesRenderers!.length - 1 >= _segment._seriesIndex && @@ -175,7 +275,7 @@ class CandleSeriesRenderer extends _FinancialSerieBaseRenderer { /// Changes the series color, border color, and border width. @override void customizeSegment(ChartSegment _segment) { - _candleSeries = _series as CandleSeries; + _candleSeries = _series as CandleSeries; _candelSereisRenderer = _segment._seriesRenderer as CandleSeriesRenderer; _candleSegment = _candelSereisRenderer._candleSegment; @@ -185,14 +285,17 @@ class CandleSeriesRenderer extends _FinancialSerieBaseRenderer { ? _candleSeries.bullColor : _candleSeries.bearColor; } else { - _candleSegment._isSolid = !_candleSegment._isBull ? true : false; + _candleSegment._isSolid = !_candleSegment._isBull; _candleSegment.currentSegmentIndex! - 1 >= 0 && _candleSegment - ._seriesRenderer - ._dataPoints[_candleSegment.currentSegmentIndex! - 1] - .close > - _candleSegment._seriesRenderer - ._dataPoints[_candleSegment.currentSegmentIndex!].close + ._seriesRenderer + ._dataPoints[_candleSegment.currentSegmentIndex! - 1] + .close > + _candleSegment + ._seriesRenderer + ._dataPoints[_candleSegment.currentSegmentIndex!] + .close == + true ? _candleSegment._color = _candleSeries.bearColor : _candleSegment._color = _candleSeries.bullColor; } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/column_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/column_series.dart index 52134f58a..e9fd5c1f9 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/column_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/column_series.dart @@ -3,57 +3,58 @@ part of charts; /// This class has the properties of the column series. /// /// To render a column chart, create an instance of [ColumnSeries], and add it to the series collection property of [SfCartesianChart]. -/// The column series is a rectangular column with heights or lengths proportional to the values that they represent. it has the spacing +/// The column series is a rectangular column with heights or lengths proportional to the values that they represent. It has the spacing /// property to separate the column. /// /// Provide the options of color, opacity, border color, and border width to customize the appearance. /// class ColumnSeries extends XyDataSeries { /// Creating an argument constructor of ColumnSeries class. - ColumnSeries( - {ValueKey? key, - ChartSeriesRendererFactory? onCreateRenderer, - required List dataSource, - required ChartValueMapper xValueMapper, - required ChartValueMapper yValueMapper, - ChartValueMapper? sortFieldValueMapper, - ChartValueMapper? pointColorMapper, - ChartValueMapper? dataLabelMapper, - SortingOrder? sortingOrder, - this.isTrackVisible = false, - String? xAxisName, - String? yAxisName, - String? name, - Color? color, - double? width, - this.spacing = 0, - MarkerSettings? markerSettings, - List? trendlines, - EmptyPointSettings? emptyPointSettings, - DataLabelSettings? dataLabelSettings, - bool? isVisible, - LinearGradient? gradient, - LinearGradient? borderGradient, - this.borderRadius = const BorderRadius.all(Radius.zero), - bool? enableTooltip, - double? animationDuration, - this.trackColor = Colors.grey, - this.trackBorderColor = Colors.transparent, - this.trackBorderWidth = 1, - this.trackPadding = 0, - Color? borderColor, - double? borderWidth, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, - SelectionBehavior? selectionBehavior, - bool? isVisibleInLegend, - LegendIconType? legendIconType, - String? legendItemText, - double? opacity, - List? dashArray, - SeriesRendererCreatedCallback? onRendererCreated, - List? initialSelectedDataIndexes}) - : super( + ColumnSeries({ + ValueKey? key, + ChartSeriesRendererFactory? onCreateRenderer, + required List dataSource, + required ChartValueMapper xValueMapper, + required ChartValueMapper yValueMapper, + ChartValueMapper? sortFieldValueMapper, + ChartValueMapper? pointColorMapper, + ChartValueMapper? dataLabelMapper, + SortingOrder? sortingOrder, + this.isTrackVisible = false, + String? xAxisName, + String? yAxisName, + String? name, + Color? color, + double? width, + this.spacing = 0, + MarkerSettings? markerSettings, + List? trendlines, + EmptyPointSettings? emptyPointSettings, + DataLabelSettings? dataLabelSettings, + bool? isVisible, + LinearGradient? gradient, + LinearGradient? borderGradient, + this.borderRadius = const BorderRadius.all(Radius.zero), + bool? enableTooltip, + double? animationDuration, + this.trackColor = Colors.grey, + this.trackBorderColor = Colors.transparent, + this.trackBorderWidth = 1, + this.trackPadding = 0, + Color? borderColor, + double? borderWidth, + SelectionBehavior? selectionBehavior, + bool? isVisibleInLegend, + LegendIconType? legendIconType, + String? legendItemText, + double? opacity, + List? dashArray, + SeriesRendererCreatedCallback? onRendererCreated, + List? initialSelectedDataIndexes, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, + }) : super( key: key, onCreateRenderer: onCreateRenderer, name: name, @@ -78,7 +79,6 @@ class ColumnSeries extends XyDataSeries { animationDuration: animationDuration, borderColor: borderColor, borderWidth: borderWidth, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, @@ -87,7 +87,10 @@ class ColumnSeries extends XyDataSeries { opacity: opacity, dashArray: dashArray, onRendererCreated: onRendererCreated, - initialSelectedDataIndexes: initialSelectedDataIndexes); + initialSelectedDataIndexes: initialSelectedDataIndexes, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress); ///Color of the track. /// @@ -247,6 +250,109 @@ class ColumnSeries extends XyDataSeries { } return ColumnSeriesRenderer(); } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is ColumnSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.pointColorMapper == pointColorMapper && + other.dataLabelMapper == dataLabelMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.color == color && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.gradient == gradient && + other.borderGradient == borderGradient && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.trackColor == trackColor && + other.trackBorderColor == trackBorderColor && + other.trackBorderWidth == trackBorderWidth && + other.trackPadding == trackPadding && + other.spacing == spacing && + other.borderRadius == borderRadius && + other.isTrackVisible == isTrackVisible && + other.onRendererCreated == onRendererCreated && + other.initialSelectedDataIndexes == initialSelectedDataIndexes && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + yValueMapper, + sortFieldValueMapper, + pointColorMapper, + dataLabelMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + color, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + dashArray, + animationDuration, + borderColor, + borderWidth, + gradient, + borderGradient, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + trackColor, + trackBorderColor, + trackBorderWidth, + trackPadding, + spacing, + borderRadius, + isTrackVisible, + initialSelectedDataIndexes, + onRendererCreated, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } } /// Creates series renderer for Column series @@ -267,7 +373,7 @@ class ColumnSeriesRenderer extends XyDataSeriesRenderer { final List oldSeriesRenderers = _chartState!._oldSeriesRenderers; final ColumnSeries _columnSeries = - _series as ColumnSeries; + _series as ColumnSeries; segment._seriesRenderer = this; segment._series = _columnSeries; segment._chart = _chart; @@ -278,9 +384,9 @@ class ColumnSeriesRenderer extends XyDataSeriesRenderer { .add(Offset(currentPoint.markerPoint!.x, currentPoint.markerPoint!.y)); segment.animationFactor = animateFactor; segment._currentPoint = currentPoint; - if (_chartState!._widgetNeedUpdate && + if (_renderingDetails!.widgetNeedUpdate && _chartState!._zoomPanBehaviorRenderer._isPinching != true && - !_chartState!._isLegendToggled && + !_renderingDetails!.isLegendToggled && // ignore: unnecessary_null_comparison oldSeriesRenderers != null && oldSeriesRenderers.isNotEmpty && @@ -294,7 +400,16 @@ class ColumnSeriesRenderer extends XyDataSeriesRenderer { ? segment._oldSeriesRenderer!._dataPoints[pointIndex] : null; segment._oldSegmentIndex = _getOldSegmentIndex(segment); - } else if (_chartState!._isLegendToggled && + if ((_chartState!._selectedSegments.length - 1 >= pointIndex) && + _chartState?._selectedSegments[pointIndex]._oldSegmentIndex == null) { + final ChartSegment selectedSegment = + _chartState?._selectedSegments[pointIndex] as ChartSegment; + selectedSegment._oldSeriesRenderer = + oldSeriesRenderers[selectedSegment._seriesIndex]; + selectedSegment._seriesRenderer = this; + selectedSegment._oldSegmentIndex = _getOldSegmentIndex(selectedSegment); + } + } else if (_renderingDetails!.isLegendToggled && // ignore: unnecessary_null_comparison _chartState!._segments != null && _chartState!._segments.isNotEmpty) { diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/fastline_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/fastline_series.dart index 63d17b218..5d2a9d849 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/fastline_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/fastline_series.dart @@ -9,9 +9,9 @@ part of charts; /// /// The following properties are used to customize the appearance of fast line segment: /// -/// * color – Changes the color of the line. +/// * color - Changes the color of the line. /// * opacity - Controls the transparency of the chart series. -/// * width – Changes the stroke width of the line. +/// * width - Changes the stroke width of the line. class FastLineSeries extends XyDataSeries { /// Creating an argument constructor of FastLineSeries class. FastLineSeries( @@ -37,14 +37,15 @@ class FastLineSeries extends XyDataSeries { String? name, bool? enableTooltip, double? animationDuration, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, String? legendItemText, double? opacity, - SeriesRendererCreatedCallback? onRendererCreated}) + SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress}) : super( key: key, onCreateRenderer: onCreateRenderer, @@ -67,13 +68,15 @@ class FastLineSeries extends XyDataSeries { isVisible: isVisible, enableTooltip: enableTooltip, animationDuration: animationDuration, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, legendIconType: legendIconType, sortingOrder: sortingOrder, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, opacity: opacity); /// Create the fastline series renderer. @@ -88,6 +91,87 @@ class FastLineSeries extends XyDataSeries { } return FastLineSeriesRenderer(); } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is FastLineSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.dataLabelMapper == dataLabelMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.color == color && + other.width == width && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.gradient == gradient && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + yValueMapper, + sortFieldValueMapper, + dataLabelMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + color, + width, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + dashArray, + animationDuration, + gradient, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + onRendererCreated, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } } /// Creates series renderer for Fastline series @@ -104,11 +188,13 @@ class FastLineSeriesRenderer extends XyDataSeriesRenderer { int seriesIndex, SfCartesianChart chart, double animateFactor, [List? _points]) { final FastLineSegment segment = createSegment(); - segment._series = _series as XyDataSeries; + segment._series = _series as XyDataSeries; segment._seriesIndex = seriesIndex; segment._seriesRenderer = this; segment.animationFactor = animateFactor; - if (_points != null) segment.points = _points; + if (_points != null) { + segment.points = _points; + } segment._oldSegmentIndex = 0; customizeSegment(segment); segment._chart = chart; diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/financial_series_base.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/financial_series_base.dart index d928e30b5..3d0f75196 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/financial_series_base.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/financial_series_base.dart @@ -30,8 +30,6 @@ abstract class _FinancialSeriesBase extends XyDataSeries { bool? enableTooltip, double? animationDuration, double? borderWidth, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, List? initialSelectedDataIndexes, bool? isVisibleInLegend, @@ -43,7 +41,10 @@ abstract class _FinancialSeriesBase extends XyDataSeries { double? opacity, this.showIndicationForSameValues = false, List? trendlines, - SeriesRendererCreatedCallback? onRendererCreated}) + SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress}) : dashArray = dashArray ?? [0, 0], spacing = spacing ?? 0, super( @@ -69,7 +70,6 @@ abstract class _FinancialSeriesBase extends XyDataSeries { enableTooltip: enableTooltip, animationDuration: animationDuration, borderWidth: borderWidth, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, initialSelectedDataIndexes: initialSelectedDataIndexes, legendItemText: legendItemText, @@ -78,6 +78,9 @@ abstract class _FinancialSeriesBase extends XyDataSeries { sortingOrder: sortingOrder, opacity: opacity, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, trendlines: trendlines); final ChartIndexedValueMapper? volumeValueMapper; diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/hilo_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/hilo_series.dart index 983ef31e4..b717bef61 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/hilo_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/hilo_series.dart @@ -5,44 +5,47 @@ part of charts; ///HiLo series illustrates the price movements in stock using the high and low values. /// ///To render a HiLo chart, create an instance of HiloSeries, and add it to the series collection property of [SfCartesianChart]. +/// +/// {@youtube 560 315 https://www.youtube.com/watch?v=uSsKhlRzC2Q} class HiloSeries extends _FinancialSeriesBase { /// Creating an argument constructor of HiloSeries class. - HiloSeries( - {ValueKey? key, - ChartSeriesRendererFactory? onCreateRenderer, - required List dataSource, - required ChartValueMapper xValueMapper, - required ChartValueMapper lowValueMapper, - required ChartValueMapper highValueMapper, - ChartValueMapper? sortFieldValueMapper, - ChartValueMapper? pointColorMapper, - ChartValueMapper? dataLabelMapper, - SortingOrder? sortingOrder, - String? xAxisName, - String? yAxisName, - String? name, - Color? color, - MarkerSettings? markerSettings, - EmptyPointSettings? emptyPointSettings, - DataLabelSettings? dataLabelSettings, - bool? isVisible, - bool? enableTooltip, - double? animationDuration, - double? borderWidth, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, - SelectionBehavior? selectionBehavior, - bool? isVisibleInLegend, - LegendIconType? legendIconType, - String? legendItemText, - List? dashArray, - double? opacity, - double? spacing, - List? initialSelectedDataIndexes, - bool? showIndicationForSameValues, - List? trendlines, - SeriesRendererCreatedCallback? onRendererCreated}) - : super( + HiloSeries({ + ValueKey? key, + ChartSeriesRendererFactory? onCreateRenderer, + required List dataSource, + required ChartValueMapper xValueMapper, + required ChartValueMapper lowValueMapper, + required ChartValueMapper highValueMapper, + ChartValueMapper? sortFieldValueMapper, + ChartValueMapper? pointColorMapper, + ChartValueMapper? dataLabelMapper, + SortingOrder? sortingOrder, + String? xAxisName, + String? yAxisName, + String? name, + Color? color, + MarkerSettings? markerSettings, + EmptyPointSettings? emptyPointSettings, + DataLabelSettings? dataLabelSettings, + bool? isVisible, + bool? enableTooltip, + double? animationDuration, + double? borderWidth, + SelectionBehavior? selectionBehavior, + bool? isVisibleInLegend, + LegendIconType? legendIconType, + String? legendItemText, + List? dashArray, + double? opacity, + double? spacing, + List? initialSelectedDataIndexes, + bool? showIndicationForSameValues, + List? trendlines, + SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, + }) : super( key: key, onCreateRenderer: onCreateRenderer, name: name, @@ -65,7 +68,6 @@ class HiloSeries extends _FinancialSeriesBase { enableTooltip: enableTooltip, animationDuration: animationDuration, borderWidth: borderWidth ?? 2, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, @@ -73,6 +75,9 @@ class HiloSeries extends _FinancialSeriesBase { sortingOrder: sortingOrder, opacity: opacity, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, showIndicationForSameValues: showIndicationForSameValues ?? false, initialSelectedDataIndexes: initialSelectedDataIndexes, trendlines: trendlines); @@ -89,6 +94,95 @@ class HiloSeries extends _FinancialSeriesBase { } return HiloSeriesRenderer(); } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is HiloSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.lowValueMapper == lowValueMapper && + other.highValueMapper == highValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.pointColorMapper == pointColorMapper && + other.dataLabelMapper == dataLabelMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.bearColor == bearColor && + other.bullColor == bullColor && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.borderWidth == borderWidth && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.spacing == spacing && + other.showIndicationForSameValues == showIndicationForSameValues && + other.initialSelectedDataIndexes == other.initialSelectedDataIndexes && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + lowValueMapper, + highValueMapper, + sortFieldValueMapper, + pointColorMapper, + dataLabelMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + bearColor, + bullColor, + emptyPointSettings, + dataLabelSettings, + isVisible, + enableTooltip, + animationDuration, + borderWidth, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + dashArray, + opacity, + spacing, + onRendererCreated, + initialSelectedDataIndexes, + showIndicationForSameValues, + trendlines, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } } /// Creates series renderer for Hilo series @@ -97,9 +191,11 @@ class HiloSeriesRenderer extends _FinancialSerieBaseRenderer { HiloSeriesRenderer(); // Store the rect position // + @override late num _rectPosition; // Store the rect count // + @override late num _rectCount; /// Hilo segment is created here @@ -117,13 +213,13 @@ class HiloSeriesRenderer extends _FinancialSerieBaseRenderer { Offset(currentPoint.markerPoint!.x, currentPoint.markerPoint!.y)); segment.points.add( Offset(currentPoint.markerPoint2!.x, currentPoint.markerPoint2!.y)); - segment._series = _series as XyDataSeries; + segment._series = _series as XyDataSeries; segment._seriesRenderer = this; segment.animationFactor = animateFactor; segment._pointColorMapper = currentPoint.pointColorMapper; segment._currentPoint = currentPoint; - if (_chartState!._widgetNeedUpdate && - !_chartState!._isLegendToggled && + if (_renderingDetails!.widgetNeedUpdate && + !_renderingDetails!.isLegendToggled && // ignore: unnecessary_null_comparison oldSeriesRenderers != null && oldSeriesRenderers.isNotEmpty && @@ -151,7 +247,11 @@ class HiloSeriesRenderer extends _FinancialSerieBaseRenderer { selectionBehaviorRenderer?._selectionRenderer?._checkWithSelectionState( _segments[segment.currentSegmentIndex!], _chart); } - segment.onPaint(canvas); + if (!((segment._currentPoint?.low == segment._currentPoint?.high) && + //ignore: always_specify_types + !(_series as HiloSeries).showIndicationForSameValues)) { + segment.onPaint(canvas); + } } @override diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/hiloopenclose_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/hiloopenclose_series.dart index 6f9a51dd8..ed4dd7693 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/hiloopenclose_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/hiloopenclose_series.dart @@ -4,8 +4,10 @@ part of charts; /// /// HiLoOpenClose series is used to represent the low, high, open and closing values over time. /// -///To render a HiloOpenClose chart, create an instance of _hiloOpenCloseSeries, +///To render a HiloOpenClose chart, create an instance of HiloOpenCloseSeries, /// and add it to the series collection property of [SfCartesianChart]. +/// +/// {@youtube 560 315 https://www.youtube.com/watch?v=g5cniDExpRw} class HiloOpenCloseSeries extends _FinancialSeriesBase { /// Creating an argument constructor of HiloOpenCloseSeries class. HiloOpenCloseSeries( @@ -33,8 +35,6 @@ class HiloOpenCloseSeries extends _FinancialSeriesBase { bool? enableTooltip, double? animationDuration, double? borderWidth, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, @@ -45,7 +45,10 @@ class HiloOpenCloseSeries extends _FinancialSeriesBase { List? initialSelectedDataIndexes, bool? showIndicationForSameValues, List? trendlines, - SeriesRendererCreatedCallback? onRendererCreated}) + SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress}) : super( key: key, onCreateRenderer: onCreateRenderer, @@ -79,7 +82,6 @@ class HiloOpenCloseSeries extends _FinancialSeriesBase { enableTooltip: enableTooltip, animationDuration: animationDuration, borderWidth: borderWidth ?? 2, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, @@ -90,6 +92,9 @@ class HiloOpenCloseSeries extends _FinancialSeriesBase { bullColor: bullColor ?? Colors.green, initialSelectedDataIndexes: initialSelectedDataIndexes, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, showIndicationForSameValues: showIndicationForSameValues ?? false, trendlines: trendlines); @@ -105,6 +110,103 @@ class HiloOpenCloseSeries extends _FinancialSeriesBase { } return HiloOpenCloseSeriesRenderer(); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is HiloOpenCloseSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.lowValueMapper == lowValueMapper && + other.highValueMapper == highValueMapper && + other.openValueMapper == openValueMapper && + other.closeValueMapper == closeValueMapper && + other.volumeValueMapper == volumeValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.pointColorMapper == pointColorMapper && + other.dataLabelMapper == dataLabelMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.bearColor == bearColor && + other.bullColor == bullColor && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.borderWidth == borderWidth && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.spacing == spacing && + other.showIndicationForSameValues == showIndicationForSameValues && + other.initialSelectedDataIndexes == other.initialSelectedDataIndexes && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress; + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + lowValueMapper, + highValueMapper, + openValueMapper, + closeValueMapper, + volumeValueMapper, + sortFieldValueMapper, + pointColorMapper, + dataLabelMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + bearColor, + bullColor, + emptyPointSettings, + dataLabelSettings, + isVisible, + enableTooltip, + animationDuration, + borderWidth, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + dashArray, + opacity, + spacing, + onRendererCreated, + initialSelectedDataIndexes, + showIndicationForSameValues, + trendlines, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } } /// Creates series renderer for Hilo open close series @@ -135,12 +237,12 @@ class HiloOpenCloseSeriesRenderer extends XyDataSeriesRenderer { _segment._seriesIndex = seriesIndex; _segment.currentSegmentIndex = pointIndex; _segment._seriesRenderer = this; - _segment._series = _series as XyDataSeries; + _segment._series = _series as XyDataSeries; _segment.animationFactor = animateFactor; _segment._pointColorMapper = currentPoint.pointColorMapper; _segment._currentPoint = currentPoint; - if (_chartState!._widgetNeedUpdate && - !_chartState!._isLegendToggled && + if (_renderingDetails!.widgetNeedUpdate && + !_renderingDetails!.isLegendToggled && _oldSeriesRenderers != null && _oldSeriesRenderers!.isNotEmpty && _oldSeriesRenderers!.length - 1 >= _segment._seriesIndex && @@ -183,7 +285,7 @@ class HiloOpenCloseSeriesRenderer extends XyDataSeriesRenderer { /// Changes the series color, border color, and border width. @override void customizeSegment(ChartSegment _segment) { - _hiloOpenCloseSeries = _series as HiloOpenCloseSeries; + _hiloOpenCloseSeries = _series as HiloOpenCloseSeries; _segment._color = _segment._seriesRenderer._seriesColor; _segment._strokeColor = _segment is HiloOpenCloseSegment && _segment._isBull ? _hiloOpenCloseSeries.bullColor diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/histogram_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/histogram_series.dart index 3ea27a1ae..92c640194 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/histogram_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/histogram_series.dart @@ -3,7 +3,7 @@ part of charts; /// This class has the properties of the column series. /// /// To render a column chart, create an instance of [HistogramSeries], and add it to the series collection property of [SfCartesianChart]. -/// The column series is a rectangular column with heights or lengths proportional to the values that they represent. it has the spacing +/// The column series is a rectangular column with heights or lengths proportional to the values that they represent. It has the spacing /// property to separate the column. /// /// Provide the options of color, opacity, border color, and border width to customize the appearance. @@ -42,8 +42,6 @@ class HistogramSeries extends XyDataSeries { this.trackPadding = 0, Color? borderColor, double? borderWidth, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, @@ -55,7 +53,10 @@ class HistogramSeries extends XyDataSeries { this.curveColor = Colors.blue, this.curveWidth = 2, this.curveDashArray, - SeriesRendererCreatedCallback? onRendererCreated}) + SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress}) : super( key: key, onCreateRenderer: onCreateRenderer, @@ -80,13 +81,15 @@ class HistogramSeries extends XyDataSeries { animationDuration: animationDuration, borderColor: borderColor, borderWidth: borderWidth, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, legendIconType: legendIconType, sortingOrder: sortingOrder, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, opacity: opacity, dashArray: dashArray); @@ -345,6 +348,117 @@ class HistogramSeries extends XyDataSeries { } return HistogramSeriesRenderer(); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is HistogramSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.yValueMapper == yValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.pointColorMapper == pointColorMapper && + other.dataLabelMapper == dataLabelMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.color == color && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.gradient == gradient && + other.borderGradient == borderGradient && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.trackColor == trackColor && + other.trackBorderColor == trackBorderColor && + other.trackBorderWidth == trackBorderWidth && + other.trackPadding == trackPadding && + other.spacing == spacing && + other.binInterval == binInterval && + other.showNormalDistributionCurve == showNormalDistributionCurve && + other.curveColor == curveColor && + other.curveWidth == curveWidth && + other.curveDashArray == curveDashArray && + other.borderRadius == borderRadius && + other.isTrackVisible == isTrackVisible && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress; + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + yValueMapper, + sortFieldValueMapper, + pointColorMapper, + dataLabelMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + color, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + dashArray, + animationDuration, + borderColor, + borderWidth, + gradient, + borderGradient, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + trackColor, + trackBorderColor, + trackBorderWidth, + trackPadding, + spacing, + borderRadius, + binInterval, + showNormalDistributionCurve, + curveColor, + curveWidth, + curveDashArray, + isTrackVisible, + onRendererCreated, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } } class _HistogramValues { @@ -428,7 +542,7 @@ class HistogramSeriesRenderer extends XyDataSeriesRenderer { double animateFactor) { _segment = createSegment(); _oldSeriesRenderers = _chartState!._oldSeriesRenderers; - _histogramSeries = _series as HistogramSeries; + _histogramSeries = _series as HistogramSeries; _borderRadius = _histogramSeries.borderRadius; _segment._seriesRenderer = this; _segment._series = _histogramSeries; @@ -448,8 +562,8 @@ class HistogramSeriesRenderer extends XyDataSeriesRenderer { this, _chartState!); _segment._currentPoint = currentPoint; - if (_chartState!._widgetNeedUpdate && - !_chartState!._isLegendToggled && + if (_renderingDetails!.widgetNeedUpdate && + !_renderingDetails!.isLegendToggled && _oldSeriesRenderers != null && _oldSeriesRenderers!.isNotEmpty && _oldSeriesRenderers!.length - 1 >= _segment._seriesIndex && @@ -462,7 +576,7 @@ class HistogramSeriesRenderer extends XyDataSeriesRenderer { ? _segment._oldSeriesRenderer!._dataPoints[pointIndex] : null; _segment._oldSegmentIndex = _getOldSegmentIndex(_segment); - } else if (_chartState!._isLegendToggled && + } else if (_renderingDetails!.isLegendToggled && // ignore: unnecessary_null_comparison _chartState!._segments != null && _chartState!._segments.isNotEmpty) { diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/line_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/line_series.dart index b243bd2f7..6e60852c8 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/line_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/line_series.dart @@ -8,6 +8,8 @@ part of charts; /// to plot a point. /// /// Provide the options for color, opacity, border color, and border width to customize the appearance. +/// +/// {@youtube 560 315 https://www.youtube.com/watch?v=zhcxdh4-Jt8} class LineSeries extends XyDataSeries { /// Creating an argument constructor of LineSeries class. LineSeries( @@ -32,8 +34,6 @@ class LineSeries extends XyDataSeries { bool? enableTooltip, List? dashArray, double? animationDuration, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, @@ -41,11 +41,17 @@ class LineSeries extends XyDataSeries { String? legendItemText, double? opacity, List? initialSelectedDataIndexes, - SeriesRendererCreatedCallback? onRendererCreated}) + SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress}) : super( key: key, onRendererCreated: onRendererCreated, onCreateRenderer: onCreateRenderer, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, name: name, xValueMapper: xValueMapper, yValueMapper: yValueMapper, @@ -65,7 +71,6 @@ class LineSeries extends XyDataSeries { enableTooltip: enableTooltip, dashArray: dashArray, animationDuration: animationDuration, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, @@ -86,6 +91,93 @@ class LineSeries extends XyDataSeries { } return LineSeriesRenderer(); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is LineSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.dataLabelMapper == dataLabelMapper && + other.pointColorMapper == pointColorMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.color == color && + other.width == width && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.gradient == gradient && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress && + other.initialSelectedDataIndexes == initialSelectedDataIndexes; + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + yValueMapper, + sortFieldValueMapper, + dataLabelMapper, + pointColorMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + color, + width, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + dashArray, + animationDuration, + gradient, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + onRendererCreated, + initialSelectedDataIndexes, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } } /// Creates series renderer for Line series @@ -106,7 +198,7 @@ class LineSeriesRenderer extends XyDataSeriesRenderer { double animateFactor) { _segment = createSegment(); _oldSeriesRenderers = _chartState!._oldSeriesRenderers; - _segment._series = _series as XyDataSeries; + _segment._series = _series as XyDataSeries; _segment._seriesRenderer = this; _segment._seriesIndex = seriesIndex; _segment._currentPoint = currentPoint; @@ -116,7 +208,7 @@ class LineSeriesRenderer extends XyDataSeriesRenderer { _segment._chartState = _chartState!; _segment.animationFactor = animateFactor; _segment._pointColorMapper = currentPoint.pointColorMapper; - if (_chartState!._widgetNeedUpdate && + if (_renderingDetails!.widgetNeedUpdate && _oldSeriesRenderers != null && _oldSeriesRenderers!.isNotEmpty && _oldSeriesRenderers!.length - 1 >= _segment._seriesIndex && diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/range_area_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/range_area_series.dart index f645bac93..8fd215f00 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/range_area_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/range_area_series.dart @@ -9,6 +9,8 @@ part of charts; /// /// [highValueMapper] - Field in the data source, which is considered as high value for the data points. /// [lowValueMapper] - Field in the data source, which is considered as low value for the data points. +/// +/// {@youtube 560 315 https://www.youtube.com/watch?v=uSsKhlRzC2Q} class RangeAreaSeries extends XyDataSeries { /// Creating an argument constructor of RangeAreaSeries class. RangeAreaSeries( @@ -38,15 +40,16 @@ class RangeAreaSeries extends XyDataSeries { double? borderWidth, LinearGradient? gradient, LinearGradient? borderGradient, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, String? legendItemText, double? opacity, this.borderDrawMode = RangeAreaBorderMode.all, - SeriesRendererCreatedCallback? onRendererCreated}) + SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress}) : super( key: key, onCreateRenderer: onCreateRenderer, @@ -73,13 +76,15 @@ class RangeAreaSeries extends XyDataSeries { borderWidth: borderWidth, gradient: gradient, borderGradient: borderGradient, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, legendIconType: legendIconType, sortingOrder: sortingOrder, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, opacity: opacity); ///Border type of area series. @@ -114,6 +119,99 @@ class RangeAreaSeries extends XyDataSeries { } return RangeAreaSeriesRenderer(); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is RangeAreaSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.highValueMapper == highValueMapper && + other.lowValueMapper == lowValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.pointColorMapper == pointColorMapper && + other.dataLabelMapper == dataLabelMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.color == color && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.gradient == gradient && + other.borderGradient == borderGradient && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.borderDrawMode == borderDrawMode && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress; + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + highValueMapper, + lowValueMapper, + sortFieldValueMapper, + pointColorMapper, + dataLabelMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + color, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + dashArray, + animationDuration, + borderColor, + borderWidth, + gradient, + borderGradient, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + borderDrawMode, + onRendererCreated, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } } /// Creates series renderer for Range area series @@ -132,8 +230,11 @@ class RangeAreaSeriesRenderer extends XyDataSeriesRenderer { segment._seriesIndex = seriesIndex; segment.animationFactor = animateFactor; segment._seriesRenderer = this; - segment._series = _series as XyDataSeries; - if (_points != null) segment.points = _points; + segment._series = _series as XyDataSeries; + if (_points != null) { + segment.points = _points; + } + segment.currentSegmentIndex = 0; customizeSegment(segment); segment._chart = chart; segment.strokePaint = segment.getStrokePaint(); diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/range_column_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/range_column_series.dart index df005b865..13c5280fa 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/range_column_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/range_column_series.dart @@ -10,6 +10,9 @@ part of charts; /// /// * [highValueMapper] - Field in the data source, which is considered as high value for the data points. /// * [lowValueMapper] - Field in the data source, which is considered as low value for the data points. +/// +/// {@youtube 560 315 https://www.youtube.com/watch?v=uSsKhlRzC2Q} +@immutable class RangeColumnSeries extends XyDataSeries { /// Creating an argument constructor of RangeColumnSeries class. RangeColumnSeries( @@ -46,8 +49,6 @@ class RangeColumnSeries extends XyDataSeries { Color? borderColor, List? trendlines, double? borderWidth, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, @@ -55,6 +56,9 @@ class RangeColumnSeries extends XyDataSeries { double? opacity, List? dashArray, SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, List? initialSelectedDataIndexes}) : super( key: key, @@ -82,7 +86,6 @@ class RangeColumnSeries extends XyDataSeries { animationDuration: animationDuration, borderColor: borderColor, borderWidth: borderWidth, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, @@ -91,6 +94,9 @@ class RangeColumnSeries extends XyDataSeries { opacity: opacity, dashArray: dashArray, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, initialSelectedDataIndexes: initialSelectedDataIndexes); ///Color of the track. @@ -247,6 +253,111 @@ class RangeColumnSeries extends XyDataSeries { } return RangeColumnSeriesRenderer(); } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is RangeColumnSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.highValueMapper == highValueMapper && + other.lowValueMapper == lowValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.pointColorMapper == pointColorMapper && + other.dataLabelMapper == dataLabelMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.color == color && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.gradient == gradient && + other.borderGradient == borderGradient && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.trackColor == trackColor && + other.trackBorderColor == trackBorderColor && + other.trackBorderWidth == trackBorderWidth && + other.trackPadding == trackPadding && + other.spacing == spacing && + other.borderRadius == borderRadius && + other.isTrackVisible == isTrackVisible && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress && + other.initialSelectedDataIndexes == initialSelectedDataIndexes; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + highValueMapper, + lowValueMapper, + sortFieldValueMapper, + pointColorMapper, + dataLabelMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + color, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + dashArray, + animationDuration, + borderColor, + borderWidth, + gradient, + borderGradient, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + trackColor, + trackBorderColor, + trackBorderWidth, + trackPadding, + spacing, + borderRadius, + isTrackVisible, + onRendererCreated, + initialSelectedDataIndexes, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } } /// Creates series renderer for Range column series @@ -268,7 +379,7 @@ class RangeColumnSeriesRenderer extends XyDataSeriesRenderer { final List? oldSeriesRenderers = _chartState!._oldSeriesRenderers; final RangeColumnSeries _rangeColumnSeries = - _series as RangeColumnSeries; + _series as RangeColumnSeries; final BorderRadius borderRadius = _rangeColumnSeries.borderRadius; segment._seriesIndex = seriesIndex; segment.currentSegmentIndex = pointIndex; @@ -282,9 +393,9 @@ class RangeColumnSeriesRenderer extends XyDataSeriesRenderer { segment._chartState = _chartState!; segment.animationFactor = animateFactor; segment._currentPoint = currentPoint; - if (_chartState!._widgetNeedUpdate && + if (_renderingDetails!.widgetNeedUpdate && _chartState!._zoomPanBehaviorRenderer._isPinching != true && - !_chartState!._isLegendToggled && + !_renderingDetails!.isLegendToggled && oldSeriesRenderers != null && oldSeriesRenderers.isNotEmpty && oldSeriesRenderers.length - 1 >= segment._seriesIndex && @@ -297,7 +408,16 @@ class RangeColumnSeriesRenderer extends XyDataSeriesRenderer { ? segment._oldSeriesRenderer!._dataPoints[pointIndex] : null; segment._oldSegmentIndex = _getOldSegmentIndex(segment); - } else if (_chartState!._isLegendToggled && + if ((_chartState!._selectedSegments.length - 1 >= pointIndex) && + _chartState?._selectedSegments[pointIndex]._oldSegmentIndex == null) { + final ChartSegment selectedSegment = + _chartState?._selectedSegments[pointIndex] as ChartSegment; + selectedSegment._oldSeriesRenderer = + oldSeriesRenderers[selectedSegment._seriesIndex]; + selectedSegment._seriesRenderer = this; + selectedSegment._oldSegmentIndex = _getOldSegmentIndex(selectedSegment); + } + } else if (_renderingDetails!.isLegendToggled && // ignore: unnecessary_null_comparison _chartState!._segments != null && _chartState!._segments.isNotEmpty) { diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/scatter_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/scatter_series.dart index ae6324ff4..7897c6243 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/scatter_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/scatter_series.dart @@ -6,6 +6,7 @@ part of charts; /// /// The following properties, such as [color], [opacity], [borderWidth], [borderColor] can be used to customize the appearance of the scatter segment. +@immutable class ScatterSeries extends XyDataSeries { /// Creating an argument constructor of ScatterSeries class. ScatterSeries( @@ -32,8 +33,6 @@ class ScatterSeries extends XyDataSeries { double? borderWidth, LinearGradient? gradient, LinearGradient? borderGradient, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, @@ -41,6 +40,9 @@ class ScatterSeries extends XyDataSeries { String? legendItemText, double? opacity, SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, List? initialSelectedDataIndexes}) : super( key: key, @@ -66,7 +68,6 @@ class ScatterSeries extends XyDataSeries { trendlines: trendlines, gradient: gradient, borderGradient: borderGradient, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, @@ -74,6 +75,9 @@ class ScatterSeries extends XyDataSeries { sortingOrder: sortingOrder, opacity: opacity, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, initialSelectedDataIndexes: initialSelectedDataIndexes); /// Create the scatter series renderer. @@ -88,6 +92,93 @@ class ScatterSeries extends XyDataSeries { } return ScatterSeriesRenderer(); } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is ScatterSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.pointColorMapper == pointColorMapper && + other.dataLabelMapper == dataLabelMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.color == color && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.animationDuration == animationDuration && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.gradient == gradient && + other.borderGradient == borderGradient && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress && + other.initialSelectedDataIndexes == initialSelectedDataIndexes; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + yValueMapper, + sortFieldValueMapper, + pointColorMapper, + dataLabelMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + color, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + animationDuration, + borderColor, + borderWidth, + gradient, + borderGradient, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + onRendererCreated, + initialSelectedDataIndexes, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } } /// Creates series renderer for Scatter series @@ -100,6 +191,8 @@ class ScatterSeriesRenderer extends XyDataSeriesRenderer { final bool _isLineType = false; + ScatterSegment? _segment; + ///Adds the points to the segments . ChartSegment _createSegments(CartesianChartPoint currentPoint, int pointIndex, int seriesIndex, double animateFactor) { @@ -112,12 +205,12 @@ class ScatterSeriesRenderer extends XyDataSeriesRenderer { segment._seriesIndex = seriesIndex; segment.currentSegmentIndex = pointIndex; segment._seriesRenderer = this; - segment._series = _series as XyDataSeries; + segment._series = _series as XyDataSeries; segment.animationFactor = animateFactor; segment._point = currentPoint; segment._currentPoint = currentPoint; - if (_chartState!._widgetNeedUpdate && - !_chartState!._isLegendToggled && + if (_renderingDetails!.widgetNeedUpdate && + !_renderingDetails!.isLegendToggled && // ignore: unnecessary_null_comparison oldSeriesRenderers != null && oldSeriesRenderers.isNotEmpty && @@ -132,6 +225,17 @@ class ScatterSeriesRenderer extends XyDataSeriesRenderer { ? segment._oldSeriesRenderer!._dataPoints[pointIndex] : null; segment._oldSegmentIndex = _getOldSegmentIndex(segment); + if ((_chartState!._selectedSegments.length - 1 >= pointIndex) && + _chartState?._selectedSegments[pointIndex]._oldSegmentIndex == + null) { + final ChartSegment selectedSegment = + _chartState?._selectedSegments[pointIndex] as ChartSegment; + selectedSegment._oldSeriesRenderer = + oldSeriesRenderers[selectedSegment._seriesIndex]; + selectedSegment._seriesRenderer = this; + selectedSegment._oldSegmentIndex = + _getOldSegmentIndex(selectedSegment); + } } final _ChartLocation location = _calculatePoint( currentPoint.xValue, @@ -146,6 +250,7 @@ class ScatterSeriesRenderer extends XyDataSeriesRenderer { RRect.fromRectAndRadius(currentPoint.region!, Radius.zero); customizeSegment(segment); _segments.add(segment); + _segment = segment; } return segment; } @@ -191,8 +296,15 @@ class ScatterSeriesRenderer extends XyDataSeriesRenderer { [CartesianSeriesRenderer? seriesRenderer]) { final Size size = Size(_series.markerSettings.width, _series.markerSettings.height); - final Path markerPath = _getMarkerShapesPath(_series.markerSettings.shape, - Offset(pointX, pointY), size, seriesRenderer!, index); + final Path markerPath = _getMarkerShapesPath( + _series.markerSettings.shape, + Offset(pointX, pointY), + size, + seriesRenderer!, + index, + null, + seriesRenderer._seriesElementAnimation, + _segment); canvas.drawPath(markerPath, fillPaint); canvas.drawPath(markerPath, strokePaint); } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/series.dart index 83924af14..e1026d8a0 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/series.dart @@ -34,6 +34,9 @@ abstract class CartesianSeries extends ChartSeries { this.trendlines, this.onRendererCreated, this.onCreateRenderer, + this.onPointTap, + this.onPointDoubleTap, + this.onPointLongPress, MarkerSettings? markerSettings, bool? isVisible, bool? enableTooltip, @@ -44,24 +47,20 @@ abstract class CartesianSeries extends ChartSeries { List? initialSelectedDataIndexes, Color? borderColor, double? borderWidth, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, double? opacity, SortingOrder? sortingOrder}) : isVisible = isVisible ?? true, - markerSettings = markerSettings ?? MarkerSettings(), - dataLabelSettings = dataLabelSettings ?? DataLabelSettings(), + markerSettings = markerSettings ?? const MarkerSettings(), + dataLabelSettings = dataLabelSettings ?? const DataLabelSettings(), enableTooltip = enableTooltip ?? true, emptyPointSettings = emptyPointSettings ?? EmptyPointSettings(), dashArray = dashArray ?? [0, 0], initialSelectedDataIndexes = initialSelectedDataIndexes ?? [], animationDuration = animationDuration ?? 1500, borderColor = borderColor ?? Colors.transparent, - // ignore: deprecated_member_use_from_same_package - selectionSettings = selectionSettings ?? SelectionSettings(), selectionBehavior = selectionBehavior ?? SelectionBehavior(), legendIconType = legendIconType ?? LegendIconType.seriesType, isVisibleInLegend = isVisibleInLegend ?? true, @@ -80,7 +79,6 @@ abstract class CartesianSeries extends ChartSeries { dataLabelSettings: dataLabelSettings, enableTooltip: enableTooltip, animationDuration: animationDuration, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendIconType: legendIconType, sortingOrder: sortingOrder, @@ -177,6 +175,69 @@ abstract class CartesianSeries extends ChartSeries { ///``` final SeriesRendererCreatedCallback? onRendererCreated; + ///Called when tapped on the chart data point. + /// + ///The user can fetch the series index, point index, viewport point index and + /// data of the tapped data point. + ///```dart + ///Widget build(BuildContext context) { + /// ChartSeriesController _chartSeriesController; + /// return Container( + /// child: SfCartesianChart( + /// series: >[ + /// LineSeries( + /// onPointTap: (ChartPointDetails details) { + /// print(details.pointIndex); + /// }, + /// ), + /// ], + /// )); + ///} + ///``` + final ChartPointInteractionCallback? onPointTap; + + ///Called when double tapped on the chart data point. + /// + ///The user can fetch the series index, point index, viewport point index and + /// data of the double-tapped data point. + ///```dart + ///Widget build(BuildContext context) { + /// ChartSeriesController _chartSeriesController; + /// return Container( + /// child: SfCartesianChart( + /// series: >[ + /// LineSeries( + /// onPointDoubleTap: (ChartPointDetails details) { + /// print(details.pointIndex); + /// }, + /// ), + /// ], + /// )); + ///} + ///``` + final ChartPointInteractionCallback? onPointDoubleTap; + + ///Called when long pressed on the chart data point. + /// + ///The user can fetch the series index, point index, viewport point index and + /// data of the long-pressed data point. + ///```dart + ///Widget build(BuildContext context) { + /// ChartSeriesController _chartSeriesController; + /// return Container( + /// child: SfCartesianChart( + /// series: >[ + /// LineSeries( + /// onPointLongPress: (ChartPointDetails details) { + /// print(details.pointIndex); + /// }, + /// ), + /// ], + /// )); + ///} + ///``` + final ChartPointInteractionCallback? onPointLongPress; + ///Data required for rendering the series. /// /// If no data source is specified, empty chart will be rendered without series. @@ -439,7 +500,7 @@ abstract class CartesianSeries extends ChartSeries { /// ///This callback will be called for all the data points to check if the data is intermediate sum. /// - /// _Note:_ - This is applicable only for waterfall chart. + /// _Note:_ This is applicable only for waterfall chart. /// ///Defaults to `null`. /// @@ -477,7 +538,7 @@ abstract class CartesianSeries extends ChartSeries { /// ///This callback will be called for all the data points to check if the data is total sum. /// - /// _Note:_ - This is applicable only for waterfall chart. + /// _Note:_ This is applicable only for waterfall chart. /// ///Defaults to `null`. /// @@ -623,7 +684,7 @@ abstract class CartesianSeries extends ChartSeries { ///Indication of data points. /// - ///Mark the data point location with symbols for better + ///Marks the data point location with symbols for better ///indication. The shape, color, border, and size of the marker can be customized. /// ///```dart @@ -949,29 +1010,6 @@ abstract class CartesianSeries extends ChartSeries { @override final String? legendItemText; - ///Customizes the data points or series on selection. - /// - ///```dart - ///Widget build(BuildContext context) { - /// return Container( - /// child: SfCartesianChart( - /// series: >[ - /// BarSeries( - /// selectionSettings: SelectionSettings( - /// selectedColor: Colors.red, - /// unselectedColor: Colors.grey, - /// selectedOpacity : 0.8, - /// unselectedOpacity: 0.4 - /// ), - /// ), - /// ], - /// )); - ///} - ///``` - @override - // ignore: deprecated_member_use_from_same_package - final SelectionSettings selectionSettings; - ///Customizes the data points or series on selection. /// ///```dart @@ -1121,10 +1159,6 @@ abstract class ChartSeriesRenderer { late SfCartesianChart _chart; } -/// Called when the series renderer is created -typedef SeriesRendererCreatedCallback = void Function( - ChartSeriesController controller); - ///We can redraw the series with updating or creating new points by using this controller.If we need to access the redrawing methods ///in this before we must get the ChartSeriesController onRendererCreated event. class ChartSeriesController { @@ -1264,14 +1298,14 @@ class ChartSeriesController { final LogarithmicAxis axis = seriesRenderer._xAxisRenderer!._axis as LogarithmicAxis; currentPoint.xValue = currentPoint.x; - x = _calculateLogBaseValue(x > 1 ? x : 1, axis.logBase); + x = _calculateLogBaseValue((x > 1) == true ? x : 1, axis.logBase); } else if (seriesRenderer._xAxisRenderer is CategoryAxisRenderer) { x = index; } currentPoint.xValue ??= x; currentPoint.yValue = currentPoint.y; if (!_needXRecalculation && - (xRange.minimum >= x || xRange.maximum <= x)) { + ((xRange.minimum >= x) == true || (xRange.maximum <= x) == true)) { _needXRecalculation = true; } num minYVal = currentPoint.y ?? currentPoint.low; @@ -1285,7 +1319,8 @@ class ChartSeriesController { _calculateLogBaseValue(maxYVal > 1 ? maxYVal : 1, axis.logBase); } if (!_needYRecalculation && - (yRange.minimum >= minYVal || yRange.maximum <= maxYVal)) { + ((yRange.minimum >= minYVal) == true || + (yRange.maximum <= maxYVal) == true)) { _needYRecalculation = true; } @@ -1392,13 +1427,13 @@ class ChartSeriesController { num xValue = _pointToXValue( seriesRenderer._chartState!._requireInvertedAxis, xAxisRenderer, - xAxisRenderer._bounds, + rect, position.dx - (rect.left + xAxis.plotOffset), position.dy - (rect.top + yAxis.plotOffset)); num yValue = _pointToYValue( seriesRenderer._chartState!._requireInvertedAxis, yAxisRenderer, - yAxisRenderer._bounds, + rect, position.dx - (rect.left + xAxis.plotOffset), position.dy - (rect.top + yAxis.plotOffset)); @@ -1468,7 +1503,7 @@ class ChartSeriesController { yAxisRenderer, isInverted, series, - seriesRenderer._chartState!._containerRect); + seriesRenderer._chartState!._chartAxis._axisClipRect); return Offset(location.x, location.y); } @@ -1512,22 +1547,24 @@ class ChartSeriesController { chartState._trackballBehaviorRenderer._trackballPainter; final TrackballBehaviorRenderer trackballBehaviorRenderer = chartState._trackballBehaviorRenderer; + final _RenderingDetails renderingDetails = chartState._renderingDetails; //This hides the tooltip if rendered for this current series renderer if (tooltip.enable && (tooltip.builder != null - ? chartState._tooltipBehaviorRenderer._seriesIndex == + ? renderingDetails.tooltipBehaviorRenderer._seriesIndex == seriesRenderer._segments[0]._seriesIndex - : chartState._tooltipBehaviorRenderer._currentSeries == + : renderingDetails.tooltipBehaviorRenderer._currentSeries == seriesRenderer)) { tooltip.hide(); } //This hides the trackball if rendered for this current series renderer if (trackball.enable) { - for (final point in trackballBehaviorRenderer._chartPointInfo) { + for (final _ChartPointInfo point + in trackballBehaviorRenderer._chartPointInfo) { if (point.seriesRenderer == seriesRenderer) { if (trackballPainter != null) { - chartState._trackballRepaintNotifier.value++; + chartState._repaintNotifiers['trackball']!.value++; trackballPainter.canResetPath = true; break; } else { @@ -1543,13 +1580,13 @@ class ChartSeriesController { } seriesRenderer._reAnimate = seriesRenderer._needsAnimation = seriesRenderer._needAnimateSeriesElements = true; - chartState._initialRender = false; + renderingDetails.initialRender = false; //This repaints the datalabels for the series if renderered. chartState._renderDataLabel?.state?.repaintDataLabelElements(); //This animates the datalabel templates of the animating series. if (seriesRenderer._series.dataLabelSettings.builder != null) { - for (final _ChartTemplateInfo template in chartState._templates) { + for (final _ChartTemplateInfo template in renderingDetails.templates) { if (template.templateType == 'DataLabel' && template.animationDuration > 0 && template.seriesIndex == @@ -1663,7 +1700,7 @@ class ChartSeriesController { if (needXRecalculation || needYRecalculation) { chartState._renderOutsideAxis.state.axisRepaintNotifier.value++; chartState._renderInsideAxis.state.axisRepaintNotifier.value++; - for (final seriesRenderer + for (final CartesianSeriesRenderer seriesRenderer in chartState._chartSeries.visibleSeriesRenderers) { _repaintSeries(chartState, seriesRenderer); } @@ -1766,6 +1803,8 @@ abstract class CartesianSeriesRenderer extends ChartSeriesRenderer { /// Holds the information about chart state class SfCartesianChartState? _chartState; + _RenderingDetails? get _renderingDetails => _chartState?._renderingDetails; + /// Contains the collection of path for markers late List _markerShapes; @@ -1799,7 +1838,7 @@ abstract class CartesianSeriesRenderer extends ChartSeriesRenderer { Animation? _seriesAnimation; //ignore: prefer_final_fields - late Animation _seriesElementAnimation; + Animation? _seriesElementAnimation; //controls the animation of the corresponding series late AnimationController _animationController; @@ -1833,7 +1872,12 @@ abstract class CartesianSeriesRenderer extends ChartSeriesRenderer { // ignore: prefer_final_fields /// It specifies the side by side information of the visible range. - _VisibleRange? sideBySideInfo; + _VisibleRange? _sideBySideInfo; + + /// Holds the collection of cartesian overall data points + // ignore: prefer_final_fields + List?> _overAllDataPoints = + ?>[]; /// To create segment for series ChartSegment createSegment(); diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/spline_area_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/spline_area_series.dart index 05959366e..5731ac2ff 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/spline_area_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/spline_area_series.dart @@ -4,6 +4,7 @@ part of charts; /// /// To render a spline area chart, create an instance of SplineAreaSeries, and add it to the series collection property of [SfCartesianChart]. /// Properties such as [color], [opacity], [width] are used to customize the appearance of spline area chart. +@immutable class SplineAreaSeries extends XyDataSeries { /// Creating an argument constructor of SplineAreaSeries class. SplineAreaSeries( @@ -33,14 +34,15 @@ class SplineAreaSeries extends XyDataSeries { double? borderWidth, LinearGradient? gradient, LinearGradient? borderGradient, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, String? legendItemText, double? opacity, SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, this.borderDrawMode = BorderDrawMode.top}) : super( key: key, @@ -66,13 +68,15 @@ class SplineAreaSeries extends XyDataSeries { borderWidth: borderWidth, gradient: gradient, borderGradient: borderGradient, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, legendIconType: legendIconType, sortingOrder: sortingOrder, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, opacity: opacity); ///Border type of area series. @@ -149,6 +153,99 @@ class SplineAreaSeries extends XyDataSeries { } return SplineAreaSeriesRenderer(); } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is SplineAreaSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.pointColorMapper == pointColorMapper && + other.dataLabelMapper == dataLabelMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.color == color && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.gradient == gradient && + other.borderGradient == borderGradient && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.borderDrawMode == borderDrawMode && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress && + other.cardinalSplineTension == cardinalSplineTension && + other.splineType == splineType; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + yValueMapper, + sortFieldValueMapper, + pointColorMapper, + dataLabelMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + color, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + dashArray, + animationDuration, + borderColor, + borderWidth, + gradient, + borderGradient, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + borderDrawMode, + onRendererCreated, + cardinalSplineTension, + splineType, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } } /// Creates series renderer for Spline area series @@ -166,9 +263,11 @@ class SplineAreaSeriesRenderer extends XyDataSeriesRenderer { if (segment != null) { segment._seriesIndex = seriesIndex; segment.animationFactor = animateFactor; - segment._series = _series as XyDataSeries; + segment._series = _series as XyDataSeries; segment._seriesRenderer = this; - if (_points != null) segment.points = _points; + if (_points != null) { + segment.points = _points; + } segment._path = path; segment._strokePath = strokePath; segment._chart = chart; diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/spline_range_area_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/spline_range_area_series.dart index eaf9e29c8..d541c7400 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/spline_range_area_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/spline_range_area_series.dart @@ -4,6 +4,9 @@ part of charts; /// /// To render a spline range area chart, create an instance of SplineRangeAreaSeries, and add it to the series collection property of [SfCartesianChart]. /// Properties such as [color], [opacity], [width] are used to customize the appearance of spline area chart. +/// +/// {@youtube 560 315 https://www.youtube.com/watch?v=uSsKhlRzC2Q} +@immutable class SplineRangeAreaSeries extends XyDataSeries { /// Creating an argument constructor of SplineRangeAreaSeries class. SplineRangeAreaSeries( @@ -34,14 +37,15 @@ class SplineRangeAreaSeries extends XyDataSeries { double? borderWidth, LinearGradient? gradient, LinearGradient? borderGradient, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, String? legendItemText, double? opacity, SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, this.borderDrawMode = RangeAreaBorderMode.all}) : super( key: key, @@ -68,13 +72,15 @@ class SplineRangeAreaSeries extends XyDataSeries { borderWidth: borderWidth, gradient: gradient, borderGradient: borderGradient, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, legendIconType: legendIconType, sortingOrder: sortingOrder, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, opacity: opacity); ///Border type of the spline range area series. @@ -121,6 +127,101 @@ class SplineRangeAreaSeries extends XyDataSeries { } return SplineRangeAreaSeriesRenderer(); } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is SplineRangeAreaSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.lowValueMapper == lowValueMapper && + other.highValueMapper == highValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.pointColorMapper == pointColorMapper && + other.dataLabelMapper == dataLabelMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.color == color && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.gradient == gradient && + other.borderGradient == borderGradient && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.borderDrawMode == borderDrawMode && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress && + other.cardinalSplineTension == cardinalSplineTension && + other.splineType == splineType; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + lowValueMapper, + highValueMapper, + sortFieldValueMapper, + pointColorMapper, + dataLabelMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + color, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + dashArray, + animationDuration, + borderColor, + borderWidth, + gradient, + borderGradient, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + borderDrawMode, + onRendererCreated, + cardinalSplineTension, + splineType, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } } /// Creates series renderer for Spline range area series @@ -138,9 +239,11 @@ class SplineRangeAreaSeriesRenderer extends XyDataSeriesRenderer { if (segment != null) { segment._seriesIndex = seriesIndex; segment.animationFactor = animateFactor; - segment._series = _series as XyDataSeries; + segment._series = _series as XyDataSeries; segment._seriesRenderer = this; - if (_points != null) segment.points = _points; + if (_points != null) { + segment.points = _points; + } segment._chart = chart; segment._path = path; segment._strokePath = strokePath; diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/spline_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/spline_series.dart index 390b1706f..85396696d 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/spline_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/spline_series.dart @@ -6,6 +6,7 @@ part of charts; /// SplineSeries, and add it to the series collection property of [SfCartesianChart]. /// /// Provides options to customize the color, opacity and width of the spline series segments. +@immutable class SplineSeries extends XyDataSeries { /// Creating an argument constructor of SplineSeries class. SplineSeries( @@ -32,8 +33,6 @@ class SplineSeries extends XyDataSeries { bool? enableTooltip, List? dashArray, double? animationDuration, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, @@ -41,6 +40,9 @@ class SplineSeries extends XyDataSeries { String? legendItemText, double? opacity, SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, List? initialSelectedDataIndexes}) : super( key: key, @@ -64,7 +66,6 @@ class SplineSeries extends XyDataSeries { isVisible: isVisible, dashArray: dashArray, animationDuration: animationDuration, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, @@ -72,6 +73,9 @@ class SplineSeries extends XyDataSeries { sortingOrder: sortingOrder, opacity: opacity, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, initialSelectedDataIndexes: initialSelectedDataIndexes); ///Type of the spline curve. Various type of curves such as clamped, cardinal, @@ -128,6 +132,95 @@ class SplineSeries extends XyDataSeries { } return SplineSeriesRenderer(); } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is SplineSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.dataLabelMapper == dataLabelMapper && + other.pointColorMapper == pointColorMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.color == color && + other.width == width && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.gradient == gradient && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress && + other.initialSelectedDataIndexes == initialSelectedDataIndexes && + other.cardinalSplineTension == cardinalSplineTension && + other.splineType == splineType; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + yValueMapper, + sortFieldValueMapper, + dataLabelMapper, + pointColorMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + color, + width, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + dashArray, + animationDuration, + gradient, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + onRendererCreated, + initialSelectedDataIndexes, + cardinalSplineTension, + splineType, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } } /// Creates series renderer for Spline series @@ -159,9 +252,9 @@ class SplineSeriesRenderer extends XyDataSeriesRenderer { segment._pointColorMapper = currentPoint.pointColorMapper; segment.currentSegmentIndex = pointIndex; segment._seriesIndex = seriesIndex; - segment._series = _series as XyDataSeries; + segment._series = _series as XyDataSeries; segment._seriesRenderer = this; - if (_chartState!._widgetNeedUpdate && + if (_renderingDetails!.widgetNeedUpdate && // ignore: unnecessary_null_comparison _oldSeriesRenderers != null && _oldSeriesRenderers.isNotEmpty && @@ -169,8 +262,8 @@ class SplineSeriesRenderer extends XyDataSeriesRenderer { _oldSeriesRenderers[segment._seriesIndex]._seriesName == segment._seriesRenderer._seriesName) { segment._oldSeriesRenderer = _oldSeriesRenderers[segment._seriesIndex]; - segment._oldSeries = - segment._oldSeriesRenderer!._series as XyDataSeries; + segment._oldSeries = segment._oldSeriesRenderer!._series + as XyDataSeries; } segment.calculateSegmentPoints(); segment.points.add( diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stacked_area_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stacked_area_series.dart index d4df70774..5c5781992 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stacked_area_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stacked_area_series.dart @@ -10,6 +10,9 @@ part of charts; /// To render a stacked area chart, create an instance of StackedAreaSeries, and add it to the series collection property of [SfCartesianChart]. /// ///Provides options to customize the [color], [opacity], [borderWidth], [borderColor] of the stacked area segments. +/// +/// {@youtube 560 315 https://www.youtube.com/watch?v=NCUDBD_ClHo} +@immutable class StackedAreaSeries extends _StackedSeriesBase { /// Creating an argument constructor of StackedAreaSeries class. StackedAreaSeries( @@ -39,14 +42,15 @@ class StackedAreaSeries extends _StackedSeriesBase { double? borderWidth, LinearGradient? gradient, LinearGradient? borderGradient, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, String? legendItemText, double? opacity, SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, this.borderDrawMode = BorderDrawMode.top}) : super( key: key, @@ -73,7 +77,6 @@ class StackedAreaSeries extends _StackedSeriesBase { borderWidth: borderWidth, gradient: gradient, borderGradient: borderGradient, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, @@ -81,6 +84,9 @@ class StackedAreaSeries extends _StackedSeriesBase { sortingOrder: sortingOrder, groupName: groupName, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, opacity: opacity); ///Border type of stacked area series. @@ -103,6 +109,97 @@ class StackedAreaSeries extends _StackedSeriesBase { ///``` final BorderDrawMode borderDrawMode; + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is StackedAreaSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.dataLabelMapper == dataLabelMapper && + other.pointColorMapper == pointColorMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.groupName == groupName && + other.color == color && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.gradient == gradient && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.borderGradient == borderGradient && + other.borderDrawMode == borderDrawMode; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + yValueMapper, + sortFieldValueMapper, + dataLabelMapper, + pointColorMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + groupName, + color, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + dashArray, + animationDuration, + gradient, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + onRendererCreated, + borderColor, + borderWidth, + borderGradient, + borderDrawMode, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } + /// Create the stacked area series renderer. StackedAreaSeriesRenderer createRenderer(ChartSeries series) { StackedAreaSeriesRenderer stackedAreaSeriesRenderer; @@ -135,11 +232,13 @@ class StackedAreaSeriesRenderer extends _StackedSeriesRenderer { // ignore: unnecessary_null_comparison if (segment != null) { segment._seriesRenderer = this; - segment._series = _series as XyDataSeries; + segment._series = _series as XyDataSeries; segment._seriesIndex = seriesIndex; - if (_points != null) segment.points = _points; + if (_points != null) { + segment.points = _points; + } segment.animationFactor = animateFactor; - if (_chartState!._widgetNeedUpdate && + if (_renderingDetails!.widgetNeedUpdate && _xAxisRenderer!._zoomFactor == 1 && _yAxisRenderer!._zoomFactor == 1 && // ignore: unnecessary_null_comparison diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stacked_bar_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stacked_bar_series.dart index 6492c1b81..40c45dc3a 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stacked_bar_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stacked_bar_series.dart @@ -10,7 +10,10 @@ part of charts; /// ///Provides options to customize properties such as [color], [opacity], ///[borderWidth], [borderColor], [borderRadius] of the Stackedbar segemnts. +/// +/// {@youtube 560 315 https://www.youtube.com/watch?v=NCUDBD_ClHo} +@immutable class StackedBarSeries extends _StackedSeriesBase { /// Creating an argument constructor of StackedBarSeries class. StackedBarSeries( @@ -47,8 +50,6 @@ class StackedBarSeries extends _StackedSeriesBase { List? trendlines, Color? borderColor, double? borderWidth, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, @@ -56,6 +57,9 @@ class StackedBarSeries extends _StackedSeriesBase { List? dashArray, double? opacity, SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, List? initialSelectedDataIndexes}) : super( key: key, @@ -91,7 +95,6 @@ class StackedBarSeries extends _StackedSeriesBase { borderColor: borderColor, borderWidth: borderWidth, borderRadius: borderRadius, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, @@ -99,8 +102,116 @@ class StackedBarSeries extends _StackedSeriesBase { sortingOrder: sortingOrder, opacity: opacity, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, initialSelectedDataIndexes: initialSelectedDataIndexes); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is StackedBarSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.pointColorMapper == pointColorMapper && + other.dataLabelMapper == dataLabelMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.color == color && + other.groupName == groupName && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.gradient == gradient && + other.borderGradient == borderGradient && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.trackColor == trackColor && + other.trackBorderColor == trackBorderColor && + other.trackBorderWidth == trackBorderWidth && + other.trackPadding == trackPadding && + other.spacing == spacing && + other.borderRadius == borderRadius && + other.isTrackVisible == isTrackVisible && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress && + other.initialSelectedDataIndexes == initialSelectedDataIndexes; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + yValueMapper, + sortFieldValueMapper, + pointColorMapper, + dataLabelMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + color, + groupName, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + dashArray, + animationDuration, + borderColor, + borderWidth, + gradient, + borderGradient, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + trackColor, + trackBorderColor, + trackBorderWidth, + trackPadding, + spacing, + borderRadius, + isTrackVisible, + onRendererCreated, + initialSelectedDataIndexes, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } + /// Create the stacked bar series renderer. StackedBarSeriesRenderer createRenderer(ChartSeries series) { StackedBarSeriesRenderer stackedAreaSeriesRenderer; @@ -131,7 +242,7 @@ class StackedBarSeriesRenderer extends _StackedSeriesRenderer { int pointIndex, int seriesIndex, double animateFactor) { final StackedBarSegment segment = createSegment(); final StackedBarSeries _stackedBarSeries = - _series as StackedBarSeries; + _series as StackedBarSeries; _isRectSeries = true; // ignore: unnecessary_null_comparison if (segment != null) { diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stacked_column_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stacked_column_series.dart index 8ae0531dd..0060b07b6 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stacked_column_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stacked_column_series.dart @@ -9,6 +9,9 @@ part of charts; /// ///Provides options to customize properties such as [color], [opacity], ///[borderWidth], [borderColor], [borderRadius] of the Stackedcolumn segemnts. +/// +/// {@youtube 560 315 https://www.youtube.com/watch?v=NCUDBD_ClHo} +@immutable class StackedColumnSeries extends _StackedSeriesBase { /// Creating an argument constructor of StackedColumnSeries class. StackedColumnSeries( @@ -45,8 +48,6 @@ class StackedColumnSeries extends _StackedSeriesBase { double? trackPadding, Color? borderColor, double? borderWidth, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, @@ -54,6 +55,9 @@ class StackedColumnSeries extends _StackedSeriesBase { List? dashArray, double? opacity, SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, List? initialSelectedDataIndexes}) : super( key: key, @@ -89,7 +93,6 @@ class StackedColumnSeries extends _StackedSeriesBase { borderColor: borderColor, borderWidth: borderWidth, borderRadius: borderRadius, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, @@ -97,8 +100,116 @@ class StackedColumnSeries extends _StackedSeriesBase { sortingOrder: sortingOrder, opacity: opacity, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, initialSelectedDataIndexes: initialSelectedDataIndexes); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is StackedColumnSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.pointColorMapper == pointColorMapper && + other.dataLabelMapper == dataLabelMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.color == color && + other.groupName == groupName && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.gradient == gradient && + other.borderGradient == borderGradient && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.trackColor == trackColor && + other.trackBorderColor == trackBorderColor && + other.trackBorderWidth == trackBorderWidth && + other.trackPadding == trackPadding && + other.spacing == spacing && + other.borderRadius == borderRadius && + other.isTrackVisible == isTrackVisible && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress && + other.initialSelectedDataIndexes == initialSelectedDataIndexes; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + yValueMapper, + sortFieldValueMapper, + pointColorMapper, + dataLabelMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + color, + groupName, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + dashArray, + animationDuration, + borderColor, + borderWidth, + gradient, + borderGradient, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + trackColor, + trackBorderColor, + trackBorderWidth, + trackPadding, + spacing, + borderRadius, + isTrackVisible, + onRendererCreated, + initialSelectedDataIndexes, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } + /// Create the stacked area series renderer. StackedColumnSeriesRenderer createRenderer(ChartSeries series) { StackedColumnSeriesRenderer stackedAreaSeriesRenderer; @@ -129,7 +240,7 @@ class StackedColumnSeriesRenderer extends _StackedSeriesRenderer { int pointIndex, int seriesIndex, double animateFactor) { final StackedColumnSegment segment = createSegment(); final StackedColumnSeries _stackedColumnSeries = - _series as StackedColumnSeries; + _series as StackedColumnSeries; _isRectSeries = true; // ignore: unnecessary_null_comparison if (segment != null) { diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stacked_line_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stacked_line_series.dart index d9023fefa..fdfc76f25 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stacked_line_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stacked_line_series.dart @@ -8,6 +8,9 @@ part of charts; /// /// To render a stacked line chart, create an instance of StackedLineSeries, and add it to the series collection property of [SfCartesianChart]. /// Provides options to customise [color], [opacity], [width] of the Stacked Line segments. +/// +/// {@youtube 560 315 https://www.youtube.com/watch?v=NCUDBD_ClHo} +@immutable class StackedLineSeries extends _StackedSeriesBase { /// Creating an argument constructor of StackedLineSeries class. StackedLineSeries( @@ -33,8 +36,6 @@ class StackedLineSeries extends _StackedSeriesBase { double? animationDuration, String? groupName, List? trendlines, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, @@ -42,6 +43,9 @@ class StackedLineSeries extends _StackedSeriesBase { String? legendItemText, double? opacity, SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, List? initialSelectedDataIndexes}) : super( key: key, @@ -65,7 +69,6 @@ class StackedLineSeries extends _StackedSeriesBase { enableTooltip: enableTooltip, dashArray: dashArray, animationDuration: animationDuration, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, @@ -74,8 +77,96 @@ class StackedLineSeries extends _StackedSeriesBase { groupName: groupName, opacity: opacity, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, initialSelectedDataIndexes: initialSelectedDataIndexes); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is StackedLineSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.dataLabelMapper == dataLabelMapper && + other.pointColorMapper == pointColorMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.groupName == groupName && + other.color == color && + other.width == width && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress && + other.initialSelectedDataIndexes == initialSelectedDataIndexes; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + yValueMapper, + sortFieldValueMapper, + dataLabelMapper, + pointColorMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + groupName, + color, + width, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + dashArray, + animationDuration, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + onRendererCreated, + initialSelectedDataIndexes, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } + /// to create a Stacked line series renderer StackedLineSeriesRenderer createRenderer(ChartSeries series) { StackedLineSeriesRenderer stackedLineSeriesRenderer; @@ -113,7 +204,7 @@ class StackedLineSeriesRenderer extends _StackedSeriesRenderer { // ignore: unnecessary_null_comparison if (segment != null) { segment._seriesRenderer = this; - segment._series = _series as XyDataSeries; + segment._series = _series as XyDataSeries; segment._seriesIndex = seriesIndex; segment._currentPoint = currentPoint; segment.currentSegmentIndex = pointIndex; @@ -124,7 +215,7 @@ class StackedLineSeriesRenderer extends _StackedSeriesRenderer { segment._pointColorMapper = currentPoint.pointColorMapper; segment._currentCummulativePos = currentCummulativePos; segment._nextCummulativePos = nextCummulativePos; - if (_chartState!._widgetNeedUpdate && + if (_renderingDetails!.widgetNeedUpdate && _xAxisRenderer!._zoomFactor == 1 && _yAxisRenderer!._zoomFactor == 1 && _oldSeriesRenderers != null && diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stacked_series_base.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stacked_series_base.dart index 2644d227d..03ca5521c 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stacked_series_base.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stacked_series_base.dart @@ -36,14 +36,15 @@ abstract class _StackedSeriesBase extends XyDataSeries { double? trackPadding, Color? borderColor, double? borderWidth, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, List? initialSelectedDataIndexes, bool? isVisibleInLegend, LegendIconType? legendIconType, String? legendItemText, SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, double? opacity}) : borderRadius = borderRadius ?? const BorderRadius.all(Radius.zero), trackColor = trackColor ?? Colors.grey, @@ -79,7 +80,6 @@ abstract class _StackedSeriesBase extends XyDataSeries { animationDuration: animationDuration, borderColor: borderColor, borderWidth: borderWidth, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, initialSelectedDataIndexes: initialSelectedDataIndexes, legendItemText: legendItemText, @@ -87,6 +87,9 @@ abstract class _StackedSeriesBase extends XyDataSeries { legendIconType: legendIconType, sortingOrder: sortingOrder, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, opacity: opacity); ///Customizes the corners of the column. Each corner can be customized with a desired diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stackedarea100_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stackedarea100_series.dart index e38c099e6..eadd1a4d8 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stackedarea100_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stackedarea100_series.dart @@ -12,6 +12,9 @@ part of charts; /// /// Provides options to customize the [color], [opacity], [borderWidth], [borderColor], /// [borderDrawMode] of the stackedarea100 series segments. +/// +/// {@youtube 560 315 https://www.youtube.com/watch?v=NCUDBD_ClHo} +@immutable class StackedArea100Series extends _StackedSeriesBase { /// Creating an argument constructor of StackedArea100Series class. StackedArea100Series( @@ -41,14 +44,15 @@ class StackedArea100Series extends _StackedSeriesBase { double? borderWidth, LinearGradient? gradient, LinearGradient? borderGradient, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, String? legendItemText, double? opacity, SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, this.borderDrawMode = BorderDrawMode.top}) : super( key: key, @@ -75,7 +79,6 @@ class StackedArea100Series extends _StackedSeriesBase { borderWidth: borderWidth, gradient: gradient, borderGradient: borderGradient, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, @@ -83,6 +86,9 @@ class StackedArea100Series extends _StackedSeriesBase { sortingOrder: sortingOrder, groupName: groupName, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, opacity: opacity); ///Border type of stacked area series. @@ -105,6 +111,97 @@ class StackedArea100Series extends _StackedSeriesBase { ///``` final BorderDrawMode borderDrawMode; + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is StackedArea100Series && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.dataLabelMapper == dataLabelMapper && + other.pointColorMapper == pointColorMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.groupName == groupName && + other.color == color && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.gradient == gradient && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.borderGradient == borderGradient && + other.borderDrawMode == borderDrawMode; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + yValueMapper, + sortFieldValueMapper, + dataLabelMapper, + pointColorMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + groupName, + color, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + dashArray, + animationDuration, + gradient, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + onRendererCreated, + borderColor, + borderWidth, + borderGradient, + borderDrawMode, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } + /// Create the stacked area series renderer. StackedArea100SeriesRenderer createRenderer(ChartSeries series) { StackedArea100SeriesRenderer stackedAreaSeriesRenderer; @@ -137,11 +234,13 @@ class StackedArea100SeriesRenderer extends _StackedSeriesRenderer { // ignore: unnecessary_null_comparison if (segment != null) { segment._seriesRenderer = this; - segment._series = _series as XyDataSeries; + segment._series = _series as XyDataSeries; segment._seriesIndex = seriesIndex; - if (_points != null) segment.points = _points; + if (_points != null) { + segment.points = _points; + } segment.animationFactor = animateFactor; - if (_chartState!._widgetNeedUpdate && + if (_renderingDetails!.widgetNeedUpdate && _xAxisRenderer!._zoomFactor == 1 && _yAxisRenderer!._zoomFactor == 1 && // ignore: unnecessary_null_comparison @@ -151,8 +250,8 @@ class StackedArea100SeriesRenderer extends _StackedSeriesRenderer { _oldSeriesRenderers[segment._seriesIndex]._seriesName == segment._seriesRenderer._seriesName) { segment._oldSeriesRenderer = _oldSeriesRenderers[segment._seriesIndex]; - segment._oldSeries = - segment._oldSeriesRenderer!._series as XyDataSeries; + segment._oldSeries = segment._oldSeriesRenderer!._series + as XyDataSeries; segment._oldSegmentIndex = 0; } customizeSegment(segment); diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stackedbar100_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stackedbar100_series.dart index 473595eef..29d18d35f 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stackedbar100_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stackedbar100_series.dart @@ -10,6 +10,9 @@ part of charts; /// ///Provides options to customize properties such as [color], [opacity], ///[borderWidth], [borderColor], [borderRadius] of the Stackedbar100 segments. +/// +/// {@youtube 560 315 https://www.youtube.com/watch?v=NCUDBD_ClHo} +@immutable class StackedBar100Series extends _StackedSeriesBase { /// Creating an argument constructor of StackedBar100Series class. StackedBar100Series( @@ -41,8 +44,6 @@ class StackedBar100Series extends _StackedSeriesBase { List? trendlines, Color? borderColor, double? borderWidth, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, @@ -50,6 +51,9 @@ class StackedBar100Series extends _StackedSeriesBase { List? dashArray, double? opacity, SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, List? initialSelectedDataIndexes}) : super( key: key, @@ -80,7 +84,6 @@ class StackedBar100Series extends _StackedSeriesBase { borderColor: borderColor, borderWidth: borderWidth, borderRadius: borderRadius, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, @@ -88,8 +91,116 @@ class StackedBar100Series extends _StackedSeriesBase { sortingOrder: sortingOrder, opacity: opacity, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, initialSelectedDataIndexes: initialSelectedDataIndexes); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is StackedBar100Series && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.pointColorMapper == pointColorMapper && + other.dataLabelMapper == dataLabelMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.color == color && + other.groupName == groupName && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.gradient == gradient && + other.borderGradient == borderGradient && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.trackColor == trackColor && + other.trackBorderColor == trackBorderColor && + other.trackBorderWidth == trackBorderWidth && + other.trackPadding == trackPadding && + other.spacing == spacing && + other.borderRadius == borderRadius && + other.isTrackVisible == isTrackVisible && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress && + other.initialSelectedDataIndexes == initialSelectedDataIndexes; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + yValueMapper, + sortFieldValueMapper, + pointColorMapper, + dataLabelMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + color, + groupName, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + dashArray, + animationDuration, + borderColor, + borderWidth, + gradient, + borderGradient, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + trackColor, + trackBorderColor, + trackBorderWidth, + trackPadding, + spacing, + borderRadius, + isTrackVisible, + onRendererCreated, + initialSelectedDataIndexes, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } + /// Create the stacked area series renderer. StackedBar100SeriesRenderer createRenderer(ChartSeries series) { StackedBar100SeriesRenderer stackedBarSeriesRenderer; @@ -121,7 +232,7 @@ class StackedBar100SeriesRenderer extends _StackedSeriesRenderer { int pointIndex, int seriesIndex, double animateFactor) { final StackedBar100Segment segment = createSegment(); final StackedBar100Series _stackedBar100Series = - _series as StackedBar100Series; + _series as StackedBar100Series; _isRectSeries = true; // ignore: unnecessary_null_comparison if (segment != null) { diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stackedcolumn100_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stackedcolumn100_series.dart index 2bc8eac78..6c1540215 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stackedcolumn100_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stackedcolumn100_series.dart @@ -10,6 +10,9 @@ part of charts; /// ///Provides options to customize properties such as [color], [opacity], ///[borderWidth], [borderColor], [borderRadius] of the StackedColumn100 segemnts. +/// +/// {@youtube 560 315 https://www.youtube.com/watch?v=NCUDBD_ClHo} +@immutable class StackedColumn100Series extends _StackedSeriesBase { /// Creating an argument constructor of StackedColumn100Series class. StackedColumn100Series( @@ -41,8 +44,6 @@ class StackedColumn100Series extends _StackedSeriesBase { double? animationDuration, Color? borderColor, double? borderWidth, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, @@ -50,6 +51,9 @@ class StackedColumn100Series extends _StackedSeriesBase { List? dashArray, double? opacity, SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, List? initialSelectedDataIndexes}) : super( key: key, @@ -80,7 +84,6 @@ class StackedColumn100Series extends _StackedSeriesBase { borderColor: borderColor, borderWidth: borderWidth, borderRadius: borderRadius, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, @@ -88,8 +91,116 @@ class StackedColumn100Series extends _StackedSeriesBase { sortingOrder: sortingOrder, opacity: opacity, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, initialSelectedDataIndexes: initialSelectedDataIndexes); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is StackedColumn100Series && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.pointColorMapper == pointColorMapper && + other.dataLabelMapper == dataLabelMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.color == color && + other.groupName == groupName && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.gradient == gradient && + other.borderGradient == borderGradient && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.trackColor == trackColor && + other.trackBorderColor == trackBorderColor && + other.trackBorderWidth == trackBorderWidth && + other.trackPadding == trackPadding && + other.spacing == spacing && + other.borderRadius == borderRadius && + other.isTrackVisible == isTrackVisible && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress && + other.initialSelectedDataIndexes == initialSelectedDataIndexes; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + yValueMapper, + sortFieldValueMapper, + pointColorMapper, + dataLabelMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + color, + groupName, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + dashArray, + animationDuration, + borderColor, + borderWidth, + gradient, + borderGradient, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + trackColor, + trackBorderColor, + trackBorderWidth, + trackPadding, + spacing, + borderRadius, + isTrackVisible, + onRendererCreated, + initialSelectedDataIndexes, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } + /// Create the stacked area series renderer. StackedColumn100SeriesRenderer createRenderer(ChartSeries series) { StackedColumn100SeriesRenderer stackedAreaSeriesRenderer; @@ -121,7 +232,7 @@ class StackedColumn100SeriesRenderer extends _StackedSeriesRenderer { int pointIndex, int seriesIndex, double animateFactor) { final StackedColumn100Segment segment = createSegment(); final StackedColumn100Series _stackedColumn100Series = - _series as StackedColumn100Series; + _series as StackedColumn100Series; _isRectSeries = true; // ignore: unnecessary_null_comparison if (segment != null) { diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stackedline100_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stackedline100_series.dart index d57315c1a..5d503d177 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stackedline100_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stackedline100_series.dart @@ -7,6 +7,9 @@ part of charts; /// /// To render a 100% stacked line chart, create an instance of StackedLine100Series, and add it to the series collection property of [SfCartesianChart]. /// Provides options to customise color,opacity and width of the StackedLine100 segments. +/// +/// {@youtube 560 315 https://www.youtube.com/watch?v=NCUDBD_ClHo} +@immutable class StackedLine100Series extends _StackedSeriesBase { /// Creating an argument constructor of StackedLine100Series class. StackedLine100Series( @@ -32,8 +35,6 @@ class StackedLine100Series extends _StackedSeriesBase { double? animationDuration, List? trendlines, String? groupName, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, @@ -41,6 +42,9 @@ class StackedLine100Series extends _StackedSeriesBase { String? legendItemText, double? opacity, SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, List? initialSelectedDataIndexes}) : super( key: key, @@ -64,7 +68,6 @@ class StackedLine100Series extends _StackedSeriesBase { enableTooltip: enableTooltip, dashArray: dashArray, animationDuration: animationDuration, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, @@ -73,8 +76,96 @@ class StackedLine100Series extends _StackedSeriesBase { groupName: groupName, opacity: opacity, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, initialSelectedDataIndexes: initialSelectedDataIndexes); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is StackedLine100Series && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.dataLabelMapper == dataLabelMapper && + other.pointColorMapper == pointColorMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.groupName == groupName && + other.color == color && + other.width == width && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress && + other.initialSelectedDataIndexes == initialSelectedDataIndexes; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + yValueMapper, + sortFieldValueMapper, + dataLabelMapper, + pointColorMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + groupName, + color, + width, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + dashArray, + animationDuration, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + onRendererCreated, + initialSelectedDataIndexes, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } + /// to create a Stacked line100 series renderer StackedLine100SeriesRenderer createRenderer(ChartSeries series) { StackedLine100SeriesRenderer stackedLine100SeriesRenderer; @@ -112,7 +203,7 @@ class StackedLine100SeriesRenderer extends _StackedSeriesRenderer { // ignore: unnecessary_null_comparison if (segment != null) { segment._seriesRenderer = this; - segment._series = _series as XyDataSeries; + segment._series = _series as XyDataSeries; segment._seriesIndex = seriesIndex; segment._currentPoint = currentPoint; segment.currentSegmentIndex = pointIndex; @@ -123,7 +214,7 @@ class StackedLine100SeriesRenderer extends _StackedSeriesRenderer { segment._pointColorMapper = currentPoint.pointColorMapper; segment._currentCummulativePos = currentCummulativePos; segment._nextCummulativePos = nextCummulativePos; - if (_chartState!._widgetNeedUpdate && + if (_renderingDetails!.widgetNeedUpdate && _xAxisRenderer!._zoomFactor == 1 && _yAxisRenderer!._zoomFactor == 1 && // ignore: unnecessary_null_comparison diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/step_area_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/step_area_series.dart index af3f10154..c1ad5cb48 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/step_area_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/step_area_series.dart @@ -13,6 +13,7 @@ part of charts; ///To render a spline area chart, create an instance of StepAreaSeries, and add it to the series collection property of [SfCartesianChart]. /// ///Provides options to customize the [color], [opacity], [width] of the StepArea segments. +@immutable class StepAreaSeries extends XyDataSeries { /// Creating an argument constructor of StepAreaSeries class. StepAreaSeries( @@ -41,14 +42,15 @@ class StepAreaSeries extends XyDataSeries { double? borderWidth, LinearGradient? gradient, LinearGradient? borderGradient, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, String? legendItemText, double? opacity, SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, this.borderDrawMode = BorderDrawMode.top}) : super( key: key, @@ -75,13 +77,15 @@ class StepAreaSeries extends XyDataSeries { borderWidth: borderWidth, gradient: gradient, borderGradient: borderGradient, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, legendIconType: legendIconType, sortingOrder: sortingOrder, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, opacity: opacity); ///Border type of step area series. @@ -116,6 +120,95 @@ class StepAreaSeries extends XyDataSeries { } return StepAreaSeriesRenderer(); } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is StepAreaSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.pointColorMapper == pointColorMapper && + other.dataLabelMapper == dataLabelMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.color == color && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.gradient == gradient && + other.borderGradient == borderGradient && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.borderDrawMode == borderDrawMode && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + yValueMapper, + sortFieldValueMapper, + pointColorMapper, + dataLabelMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + color, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + dashArray, + animationDuration, + borderColor, + borderWidth, + gradient, + borderGradient, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + borderDrawMode, + onRendererCreated, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } } /// Creates series renderer for Step area series @@ -133,8 +226,10 @@ class StepAreaSeriesRenderer extends XyDataSeriesRenderer { segment._strokePath = strokePath; segment._seriesIndex = seriesIndex; segment._seriesRenderer = this; - segment._series = _series as XyDataSeries; - if (_points != null) segment.points = _points; + segment._series = _series as XyDataSeries; + if (_points != null) { + segment.points = _points; + } segment._chart = _chart; segment._chartState = _chartState!; segment.animationFactor = animateFactor; diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stepline_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stepline_series.dart index 54e2d08cc..b8a3c06b2 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stepline_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/stepline_series.dart @@ -7,6 +7,7 @@ part of charts; /// /// To render a step line chart, create an instance of StepLineSeries, and add it to the series collection property of [SfCartesianChart]. /// Provides option to customise the [color], [opacity], [width] of the stepline segments +@immutable class StepLineSeries extends XyDataSeries { /// Creating an argument constructor of StepLineSeries class. StepLineSeries( @@ -31,14 +32,15 @@ class StepLineSeries extends XyDataSeries { bool? enableTooltip, List? dashArray, double? animationDuration, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, SortingOrder? sortingOrder, bool? isVisibleInLegend, LegendIconType? legendIconType, String? legendItemText, SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, double? opacity}) : super( key: key, @@ -62,13 +64,15 @@ class StepLineSeries extends XyDataSeries { dashArray: dashArray, isVisible: isVisible, animationDuration: animationDuration, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, legendIconType: legendIconType, sortingOrder: sortingOrder, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, opacity: opacity); /// Create the stacked area series renderer. @@ -84,6 +88,89 @@ class StepLineSeries extends XyDataSeries { } return StepLineSeriesRenderer(); } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is StepLineSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper && + other.sortFieldValueMapper == sortFieldValueMapper && + other.dataLabelMapper == dataLabelMapper && + other.pointColorMapper == pointColorMapper && + other.sortingOrder == sortingOrder && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.color == color && + other.width == width && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.gradient == gradient && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + yValueMapper, + sortFieldValueMapper, + dataLabelMapper, + pointColorMapper, + sortingOrder, + xAxisName, + yAxisName, + name, + color, + width, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + isVisible, + enableTooltip, + dashArray, + animationDuration, + gradient, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + onRendererCreated, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } } /// Creates series renderer for Step line series @@ -107,7 +194,7 @@ class StepLineSeriesRenderer extends XyDataSeriesRenderer { segment.currentSegmentIndex = pointIndex; segment._seriesIndex = seriesIndex; segment._seriesRenderer = this; - segment._series = _series as XyDataSeries; + segment._series = _series as XyDataSeries; segment._currentPoint = currentPoint; segment._midX = midX; segment._midY = midY; @@ -116,7 +203,7 @@ class StepLineSeriesRenderer extends XyDataSeriesRenderer { segment._chartState = _chartState!; segment.animationFactor = animateFactor; segment._pointColorMapper = currentPoint.pointColorMapper; - if (_chartState!._widgetNeedUpdate && + if (_renderingDetails!.widgetNeedUpdate && // ignore: unnecessary_null_comparison _oldSeriesRenderers != null && _oldSeriesRenderers.isNotEmpty && diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/waterfall_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/waterfall_series.dart index 61bcabb4a..12c583c39 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/waterfall_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/waterfall_series.dart @@ -7,6 +7,7 @@ part of charts; /// WaterfallSeries is similar to range column series, /// in range column high and low value should be there, but in waterfall /// we have find the endValue and originValue of each data point. +@immutable class WaterfallSeries extends XyDataSeries { /// Creating an argument constructor of WaterfallSeries class. WaterfallSeries( @@ -42,8 +43,6 @@ class WaterfallSeries extends XyDataSeries { Color? borderColor, List? trendlines, double? borderWidth, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, @@ -51,6 +50,9 @@ class WaterfallSeries extends XyDataSeries { double? opacity, List? dashArray, SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, List? initialSelectedDataIndexes}) : super( key: key, @@ -78,7 +80,6 @@ class WaterfallSeries extends XyDataSeries { animationDuration: animationDuration, borderColor: borderColor, borderWidth: borderWidth, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendItemText: legendItemText, isVisibleInLegend: isVisibleInLegend, @@ -87,6 +88,9 @@ class WaterfallSeries extends XyDataSeries { opacity: opacity, dashArray: dashArray, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, initialSelectedDataIndexes: initialSelectedDataIndexes); ///Color of the negative data points in the series. @@ -225,6 +229,113 @@ class WaterfallSeries extends XyDataSeries { ///``` final BorderRadius borderRadius; + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is WaterfallSeries && + other.key == key && + other.onCreateRenderer == onCreateRenderer && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper && + other.intermediateSumPredicate == intermediateSumPredicate && + other.totalSumPredicate == totalSumPredicate && + other.negativePointsColor == negativePointsColor && + other.intermediateSumColor == intermediateSumColor && + other.totalSumColor == totalSumColor && + other.sortFieldValueMapper == sortFieldValueMapper && + other.dataLabelMapper == dataLabelMapper && + other.pointColorMapper == pointColorMapper && + other.sortingOrder == sortingOrder && + other.connectorLineSettings == connectorLineSettings && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.name == name && + other.spacing == spacing && + other.color == color && + other.width == width && + other.markerSettings == markerSettings && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.trendlines == trendlines && + other.gradient == gradient && + other.borderGradient == borderGradient && + other.borderRadius == borderRadius && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.isVisible == isVisible && + other.enableTooltip == enableTooltip && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.selectionBehavior == selectionBehavior && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.opacity == opacity && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress && + other.initialSelectedDataIndexes == initialSelectedDataIndexes; + } + + @override + int get hashCode { + final List values = [ + key, + onCreateRenderer, + dataSource, + xValueMapper, + yValueMapper, + intermediateSumPredicate, + totalSumPredicate, + negativePointsColor, + intermediateSumColor, + totalSumColor, + sortFieldValueMapper, + dataLabelMapper, + pointColorMapper, + sortingOrder, + connectorLineSettings, + xAxisName, + yAxisName, + name, + spacing, + color, + width, + markerSettings, + emptyPointSettings, + dataLabelSettings, + trendlines, + gradient, + borderGradient, + borderWidth, + borderRadius, + borderColor, + isVisible, + enableTooltip, + dashArray, + animationDuration, + selectionBehavior, + isVisibleInLegend, + legendIconType, + legendItemText, + opacity, + onRendererCreated, + initialSelectedDataIndexes, + onPointTap, + onPointDoubleTap, + onPointLongPress + ]; + return hashList(values); + } + /// Create the waterfall series renderer. WaterfallSeriesRenderer createRenderer(ChartSeries series) { WaterfallSeriesRenderer seriesRenderer; @@ -254,7 +365,7 @@ class WaterfallSeriesRenderer extends XyDataSeriesRenderer { final WaterfallSegment segment = createSegment(); final List? oldSeriesRenderers = _chartState!._oldSeriesRenderers; - _waterfallSeries = _series as WaterfallSeries; + _waterfallSeries = _series as WaterfallSeries; final BorderRadius borderRadius = _waterfallSeries.borderRadius; segment._seriesIndex = seriesIndex; segment.currentSegmentIndex = pointIndex; @@ -266,9 +377,9 @@ class WaterfallSeriesRenderer extends XyDataSeriesRenderer { segment._chartState = _chartState!; segment.animationFactor = animateFactor; segment._currentPoint = currentPoint; - if (_chartState!._widgetNeedUpdate && + if (_renderingDetails!.widgetNeedUpdate && _chartState!._zoomPanBehaviorRenderer._isPinching != true && - !_chartState!._isLegendToggled && + !_renderingDetails!.isLegendToggled && oldSeriesRenderers != null && oldSeriesRenderers.isNotEmpty && oldSeriesRenderers.length - 1 >= segment._seriesIndex && @@ -281,7 +392,7 @@ class WaterfallSeriesRenderer extends XyDataSeriesRenderer { ? segment._oldSeriesRenderer!._dataPoints[pointIndex] : null; segment._oldSegmentIndex = _getOldSegmentIndex(segment); - } else if (_chartState!._isLegendToggled && + } else if (_renderingDetails!.isLegendToggled && // ignore: unnecessary_null_comparison _chartState!._segments != null && _chartState!._segments.isNotEmpty) { diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/xy_data_series.dart b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/xy_data_series.dart index 98ae0145a..a136ed608 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/xy_data_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/chart_series/xy_data_series.dart @@ -9,6 +9,9 @@ abstract class XyDataSeries extends CartesianSeries { {ValueKey? key, ChartSeriesRendererFactory? onCreateRenderer, SeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, ChartValueMapper? xValueMapper, ChartValueMapper? yValueMapper, ChartValueMapper? dataLabelMapper, @@ -37,8 +40,6 @@ abstract class XyDataSeries extends CartesianSeries { List? dashArray, Color? borderColor, double? borderWidth, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, bool? isVisibleInLegend, LegendIconType? legendIconType, @@ -50,6 +51,9 @@ abstract class XyDataSeries extends CartesianSeries { key: key, onRendererCreated: onRendererCreated, onCreateRenderer: onCreateRenderer, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, isVisible: isVisible, legendItemText: legendItemText, xAxisName: xAxisName, @@ -98,7 +102,6 @@ abstract class XyDataSeries extends CartesianSeries { dataLabelSettings: dataLabelSettings, enableTooltip: enableTooltip, animationDuration: animationDuration, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendIconType: legendIconType, sortingOrder: sortingOrder, @@ -109,11 +112,6 @@ abstract class XyDataSeries extends CartesianSeries { initialSelectedDataIndexes: initialSelectedDataIndexes); } -/// Returns the widget. -typedef ChartDataLabelTemplateBuilder = Widget Function( - T data, CartesianChartPoint point, int pointIndex, - {int seriesIndex, CartesianSeries series}); - /// This class has the properties of CartesianChartPoint. /// /// Chart point is a class that is used to store the current x and y values from the datasource. @@ -178,13 +176,13 @@ class CartesianChartPoint { /// X value of the point. D? x; - /// Y value of the point + /// Y value of the point. D? y; - /// Stores the xValues of the point + /// Stores the xValues of the point. D? xValue; - /// Stores the yValues of the Point + /// Stores the yValues of the point. D? yValue; /// Sort value of the point. @@ -199,43 +197,43 @@ class CartesianChartPoint { /// Open value of the point. D? open; - /// Close value of the point + /// Close value of the point. D? close; - /// Volume value of the point + /// Volume value of the point. num? volume; /// Marker point location. _ChartLocation? markerPoint; - /// second Marker point location. + /// Second marker point location. _ChartLocation? markerPoint2; /// Size of the bubble. num? bubbleSize; - /// To set empty value + /// To set empty value. bool? isEmpty; - /// To set gap value + /// To set gap value. bool isGap = false; - /// To set the drop value + /// To set the drop value. bool isDrop = false; - /// Set the visibility of the series. + /// Sets the visibility of the series. bool isVisible = true; - /// Used to map the color value from data point. + /// Used to map the color value from data points. Color? pointColorMapper; - /// Map the datalabel value from data point. + /// Maps the datalabel value from data points. String? dataLabelMapper; - /// Store the region. + /// Stores the region rect. Rect? region; - /// Store the region for box series rect. + /// Stores the region of box series rect. Rect? boxRectRegion; /// Store the outliers region. @@ -253,10 +251,10 @@ class CartesianChartPoint { /// Outlier values of box plot series. List? outliers = []; - /// Upper quartile values of box plot series. + /// Upper quartile value of box plot series. double? upperQuartile; - /// Lower quartile values of box plot series. + /// Lower quartile value of box plot series. double? lowerQuartile; /// Average value of the given data source in box plot series. @@ -337,109 +335,109 @@ class CartesianChartPoint { /// Stores the outliers location. List<_ChartLocation> outliersPoint = <_ChartLocation>[]; - /// control points for spline series. + /// Control points for spline series. List? controlPoint; - /// control points for spline range area series. + /// Control points for spline range area series. List? controlPointshigh; - /// control points for spline range area series. + /// Control points for spline range area series. List? controlPointslow; - /// Store the List of region. + /// Stores the list of regions. List? regions; - /// store the cumulative value. + /// Stores the cumulative value. double? cumulativeValue; - /// Stores the tracker rect region + /// Stores the tracker rect region. Rect? trackerRectRegion; - /// Stores the forth data label text + /// Stores the yValue/high value data label text String? label; - /// Stores the forth data label text + /// Stores the data label text of low value. String? label2; - /// Stores the forth data label text + /// Stores the data label text of close value. String? label3; - /// Stores the forth data label text + /// Stores the data label text of open value. String? label4; - /// Stores the median data label text + /// Stores the median data label text. String? label5; - /// Stores the outliers data label text + /// Stores the outliers data label text. List outliersLabel = []; - /// Stores the forth data label Rect + /// Stores the yValue/high value data label Rect. RRect? labelFillRect; - /// Stores the forth data label Rect + /// Stores the data label text of low value Rect. RRect? labelFillRect2; - /// Stores the forth data label Rect + /// Stores the data label text of close value Rect. RRect? labelFillRect3; - /// Stores the forth data label Rect + /// Stores the data label text of open value Rect. RRect? labelFillRect4; - /// Stores the median data label Rect + /// Stores the median data label Rect. RRect? labelFillRect5; - /// Stores the outliers data label Rect + /// Stores the outliers data label Rect. List outliersFillRect = []; - /// Stores the data label location + /// Stores the data label location. _ChartLocation? labelLocation; - /// Stores the second data label location + /// Stores the second data label location. _ChartLocation? labelLocation2; - /// Stores the third data label location + /// Stores the third data label location. _ChartLocation? labelLocation3; - /// Stores the forth data label location + /// Stores the fourth data label location. _ChartLocation? labelLocation4; - /// Stores the median data label location + /// Stores the median data label location. _ChartLocation? labelLocation5; - /// Stores the outliers data label location + /// Stores the outliers data label location. List<_ChartLocation> outliersLocation = <_ChartLocation>[]; /// Data label region saturation. bool dataLabelSaturationRegionInside = false; - /// Stores the data label region + /// Stores the data label region. Rect? dataLabelRegion; - /// Stores the second data label region + /// Stores the second data label region. Rect? dataLabelRegion2; - /// Stores the third data label region + /// Stores the third data label region. Rect? dataLabelRegion3; - /// Stores the forth data label region + /// Stores the forth data label region. Rect? dataLabelRegion4; - /// Stores the median data label region + /// Stores the median data label region. Rect? dataLabelRegion5; - /// Stores the outliers data label region + /// Stores the outliers data label region. List outliersDataLabelRegion = []; - /// Stores the data point index + /// Stores the data point index. int? index; - /// Stores the data index + /// Stores the data index. int? overallDataPointIndex; - /// Store the region data of the data point. + /// Stores the region data of the data point. List? regionData; - /// Store the visible point index. + /// Stores the visible point index. int? visiblePointIndex; } @@ -611,25 +609,23 @@ abstract class XyDataSeriesRenderer extends CartesianSeriesRenderer { /// To render a series of elements for all series void _renderSeriesElements(SfCartesianChart chart, Canvas canvas, - Animation animationController) { + Animation? animationController) { _markerShapes = []; _markerShapes2 = []; assert( // ignore: unnecessary_null_comparison - _series.markerSettings.height != null - ? _series.markerSettings.height >= 0 - : true, + !(_series.markerSettings.height != null) || + _series.markerSettings.height >= 0, 'The height of the marker should be greater than or equal to 0.'); assert( // ignore: unnecessary_null_comparison - _series.markerSettings.width != null - ? _series.markerSettings.width >= 0 - : true, + !(_series.markerSettings.width != null) || + _series.markerSettings.width >= 0, 'The width of the marker must be greater than or equal to 0.'); for (int pointIndex = 0; pointIndex < _dataPoints.length; pointIndex++) { final CartesianChartPoint point = _dataPoints[pointIndex]; if ((_series.markerSettings.isVisible && - !(this is BoxAndWhiskerSeriesRenderer)) || + this is! BoxAndWhiskerSeriesRenderer) || this is ScatterSeriesRenderer) { final MarkerSettingsRenderer? markerSettingsRenderer = _markerSettingsRenderer!; @@ -650,7 +646,7 @@ abstract class XyDataSeriesRenderer extends CartesianSeriesRenderer { _chartState!._animationCompleteCount++; _setAnimationStatus(_chartState); } else if (_chartState != null && status == AnimationStatus.forward) { - _chartState!._animateCompleted = false; + _renderingDetails!.animateCompleted = false; _animationCompleted = false; } } @@ -748,13 +744,13 @@ abstract class XyDataSeriesRenderer extends CartesianSeriesRenderer { } /// side by side range calculated - seriesRenderer.sideBySideInfo = (_isRectSeries || + seriesRenderer._sideBySideInfo = (_isRectSeries || (_seriesType.contains('candle') || _seriesType.contains('hilo') || _seriesType.contains('histogram') || _seriesType.contains('box'))) ? _calculateSideBySideInfo(seriesRenderer, _chartState) - : seriesRenderer.sideBySideInfo; + : seriesRenderer._sideBySideInfo; if (_isRectSeries) { _calculateRectSeriesRegion(point, pointIndex, this, _chartState); } else if (isPointSeries) { @@ -777,7 +773,11 @@ abstract class XyDataSeriesRenderer extends CartesianSeriesRenderer { } // ignore: unnecessary_null_comparison if (_chart.tooltipBehavior != null && - (_chart.tooltipBehavior.enable || _chart.onPointTapped != null) && + (_chart.tooltipBehavior.enable || + _chart.onPointTapped != null || + seriesRenderer._series.onPointTap != null || + seriesRenderer._series.onPointDoubleTap != null || + seriesRenderer._series.onPointLongPress != null) && _seriesType != 'boxandwhisker') { _calculateTooltipRegion(point, seriesIndex, this, _chartState); } @@ -835,7 +835,7 @@ abstract class XyDataSeriesRenderer extends CartesianSeriesRenderer { regionRect.add(point.region); regionRect.add(_isRectSeries ? _seriesType == 'column' || _seriesType.contains('stackedcolumn') - ? point.yValue > 0 + ? (point.yValue > 0) == true ? point.region!.topCenter : point.region!.bottomCenter : point.region!.topCenter diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/common/common.dart b/packages/syncfusion_flutter_charts/lib/src/chart/common/common.dart index ca59ae287..2625d88dc 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/common/common.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/common/common.dart @@ -32,6 +32,7 @@ class _PainterKey { /// /// _Note:_ IntereactivetoolTip applicable for axis types and trackball. +@immutable class InteractiveTooltip { /// Creating an argument constructor of InteractiveTooltip class. const InteractiveTooltip( @@ -276,6 +277,53 @@ class InteractiveTooltip { ///} ///``` final bool canShowMarker; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is InteractiveTooltip && + other.enable == enable && + other.color == color && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.borderRadius == borderRadius && + other.arrowLength == arrowLength && + other.arrowWidth == arrowWidth && + other.format == format && + other.connectorLineColor == connectorLineColor && + other.connectorLineWidth == connectorLineWidth && + other.connectorLineDashArray == connectorLineDashArray && + other.decimalPlaces == decimalPlaces && + other.canShowMarker == canShowMarker && + other.textStyle == textStyle; + } + + @override + int get hashCode { + final List values = [ + enable, + color, + borderColor, + borderWidth, + borderRadius, + arrowLength, + arrowWidth, + format, + connectorLineColor, + connectorLineWidth, + connectorLineDashArray, + decimalPlaces, + canShowMarker, + textStyle + ]; + return hashList(values); + } } class _StackedValues { @@ -386,7 +434,7 @@ class _ChartPointInfo { ///marker to customize the appearance. class TrackballMarkerSettings extends MarkerSettings { /// Creating an argument constructor of TrackballMarkerSettings class. - TrackballMarkerSettings( + const TrackballMarkerSettings( {this.markerVisibility = TrackballVisibilityMode.auto, double? height, double? width, @@ -435,12 +483,48 @@ class TrackballMarkerSettings extends MarkerSettings { ///} ///``` final TrackballVisibilityMode markerVisibility; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is TrackballMarkerSettings && + other.markerVisibility == markerVisibility && + other.height == height && + other.width == width && + other.color == color && + other.shape == shape && + other.borderWidth == borderWidth && + other.borderColor == borderColor && + other.image == image; + } + + @override + int get hashCode { + final List values = [ + markerVisibility, + height, + width, + color, + shape, + borderWidth, + borderColor, + image + ]; + return hashList(values); + } } ///Options to show the details of the trackball template. +@immutable class TrackballDetails { ///Constructor of TrackballDetails class. - TrackballDetails( + const TrackballDetails( [this.point, this.series, this.pointIndex, @@ -461,6 +545,34 @@ class TrackballDetails { /// It specifies the trackball grouping mode info. final TrackballGroupingModeInfo? groupingModeInfo; + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is TrackballDetails && + other.point == point && + other.series == series && + other.pointIndex == pointIndex && + other.seriesIndex == seriesIndex && + other.groupingModeInfo == groupingModeInfo; + } + + @override + int get hashCode { + final List values = [ + point, + series, + pointIndex, + seriesIndex, + groupingModeInfo + ]; + return hashList(values); + } } /// To get cartesian type data label saturation color. @@ -472,6 +584,7 @@ Color _getDataLabelSaturationColor( final SfCartesianChart chart = _chartState._chart; Color color; final DataLabelSettings dataLabel = seriesRenderer._series.dataLabelSettings; + final _RenderingDetails renderingDetails = _chartState._renderingDetails; final ChartDataLabelAlignment labelPosition = (seriesRenderer._seriesType == 'rangecolumn' && (dataLabel.labelAlignment == ChartDataLabelAlignment.bottom || @@ -500,25 +613,24 @@ Color _getDataLabelSaturationColor( switch (seriesType) { case 'Line': color = _getOuterDataLabelColor(dataLabelSettingsRenderer, - chart.plotAreaBackgroundColor, _chartState._chartTheme); + chart.plotAreaBackgroundColor, renderingDetails.chartTheme); break; case 'Column': color = (!currentPoint.dataLabelSaturationRegionInside && (labelPosition == ChartDataLabelAlignment.outer || (labelPosition == ChartDataLabelAlignment.top && alignment == ChartAlignment.far) || - (seriesRenderer._seriesType == 'rangecolumn' - ? (labelPosition == ChartDataLabelAlignment.top && - alignment == ChartAlignment.near) - : false) || + seriesRenderer._seriesType == 'rangecolumn' && + (labelPosition == ChartDataLabelAlignment.top && + alignment == ChartAlignment.near) || (labelPosition == ChartDataLabelAlignment.auto && (!seriesRenderer._seriesType.contains('100') && seriesRenderer._seriesType != 'stackedbar' && seriesRenderer._seriesType != 'stackedcolumn')))) ? _getOuterDataLabelColor(dataLabelSettingsRenderer, - chart.plotAreaBackgroundColor, _chartState._chartTheme) + chart.plotAreaBackgroundColor, renderingDetails.chartTheme) : _getInnerDataLabelColor( - currentPoint, seriesRenderer, _chartState._chartTheme); + currentPoint, seriesRenderer, renderingDetails.chartTheme); break; case 'Circle': color = (labelPosition == ChartDataLabelAlignment.middle && @@ -530,18 +642,18 @@ Color _getDataLabelSaturationColor( labelPosition == ChartDataLabelAlignment.outer && alignment == ChartAlignment.near) ? _getInnerDataLabelColor( - currentPoint, seriesRenderer, _chartState._chartTheme) + currentPoint, seriesRenderer, renderingDetails.chartTheme) : _getOuterDataLabelColor(dataLabelSettingsRenderer, - chart.plotAreaBackgroundColor, _chartState._chartTheme); + chart.plotAreaBackgroundColor, renderingDetails.chartTheme); break; case 'area': color = (!currentPoint.dataLabelSaturationRegionInside && currentPoint.labelLocation!.y < currentPoint.markerPoint!.y) ? _getOuterDataLabelColor(dataLabelSettingsRenderer, - chart.plotAreaBackgroundColor, _chartState._chartTheme) + chart.plotAreaBackgroundColor, renderingDetails.chartTheme) : _getInnerDataLabelColor( - currentPoint, seriesRenderer, _chartState._chartTheme); + currentPoint, seriesRenderer, renderingDetails.chartTheme); break; default: @@ -554,8 +666,7 @@ Color _getDataLabelSaturationColor( /// Get the data label color of open-close series Color _getOpenCloseDataLabelColor(CartesianChartPoint currentPoint, CartesianSeriesRenderer seriesRenderer, SfCartesianChart chart) { - Color color; - color = seriesRenderer + final Color color = seriesRenderer ._segments[seriesRenderer._dataPoints.indexOf(currentPoint)] .fillPaint! .style == @@ -586,7 +697,9 @@ Color _getInnerDataLabelColor(CartesianChartPoint currentPoint, Color? seriesColor = seriesRenderer._series.color; if (seriesRenderer._seriesType == 'waterfall') { seriesColor = _getWaterfallSeriesColor( - seriesRenderer._series as WaterfallSeries, currentPoint, seriesColor); + seriesRenderer._series as WaterfallSeries, + currentPoint, + seriesColor); } // ignore: prefer_if_null_operators innerColor = dataLabelSettingsRenderer._color != null @@ -618,48 +731,46 @@ void _animateRectSeries( Rect? oldSegmentRect, num? oldYValue, bool? oldSeriesVisible) { - final bool comparePrev = seriesRenderer._chartState!._widgetNeedUpdate && + final bool comparePrev = seriesRenderer._renderingDetails!.widgetNeedUpdate && oldYValue != null && !seriesRenderer._reAnimate && oldSegmentRect != null; - final bool isLargePrev = oldYValue != null ? oldYValue > yPoint : false; - final bool isSingleSeries = seriesRenderer._chartState!._isLegendToggled - ? _checkSingleSeries(seriesRenderer) - : false; - if ((seriesRenderer._seriesType == 'column' && - seriesRenderer._chart.isTransposed) || - (seriesRenderer._seriesType == 'bar' && - !seriesRenderer._chart.isTransposed) || - (seriesRenderer._seriesType == 'histogram' && - seriesRenderer._chart.isTransposed)) { - _animateTransposedRectSeries( - canvas, - seriesRenderer, - fillPaint, - segmentRect, - yPoint, - animationFactor, - oldSegmentRect, - oldSeriesVisible, - comparePrev, - isLargePrev, - isSingleSeries, - oldYValue); - } else { - _animateNormalRectSeries( - canvas, - seriesRenderer, - fillPaint, - segmentRect, - yPoint, - animationFactor, - oldSegmentRect, - oldSeriesVisible, - comparePrev, - isLargePrev, - isSingleSeries, - oldYValue); - } + final bool isLargePrev = oldYValue != null && oldYValue > yPoint; + final bool isSingleSeries = + seriesRenderer._renderingDetails!.isLegendToggled && + _checkSingleSeries(seriesRenderer); + ((seriesRenderer._seriesType == 'column' && + seriesRenderer._chart.isTransposed) || + (seriesRenderer._seriesType == 'bar' && + !seriesRenderer._chart.isTransposed) || + (seriesRenderer._seriesType == 'histogram' && + seriesRenderer._chart.isTransposed)) + ? _animateTransposedRectSeries( + canvas, + seriesRenderer, + fillPaint, + segmentRect, + yPoint, + animationFactor, + oldSegmentRect, + oldSeriesVisible, + comparePrev, + isLargePrev, + isSingleSeries, + oldYValue) + : _animateNormalRectSeries( + canvas, + seriesRenderer, + fillPaint, + segmentRect, + yPoint, + animationFactor, + oldSegmentRect, + oldSeriesVisible, + comparePrev, + isLargePrev, + isSingleSeries, + oldYValue); } /// To animate transposed bar and column series @@ -681,19 +792,19 @@ void _animateTransposedRectSeries( final double top = segmentRect.top; double left = segmentRect.left, right = segmentRect.right; Rect? rect; - final num defaultCrossesAtValue = 0; + const num defaultCrossesAtValue = 0; final num crossesAt = _getCrossesAtValue(seriesRenderer, seriesRenderer._chartState!) ?? defaultCrossesAtValue; seriesRenderer._needAnimateSeriesElements = seriesRenderer._needAnimateSeriesElements || segmentRect.outerRect != oldSegmentRect; - if (!seriesRenderer._chartState!._isLegendToggled || + if (!seriesRenderer._renderingDetails!.isLegendToggled || seriesRenderer._reAnimate) { width = segmentRect.width * ((!comparePrev && !seriesRenderer._reAnimate && - !seriesRenderer._chartState!._initialRender! && + !seriesRenderer._renderingDetails!.initialRender! && oldSegmentRect == null && seriesRenderer._series.key != null && seriesRenderer._chartState!._oldSeriesKeys @@ -778,7 +889,7 @@ void _animateTransposedRectSeries( } } rect = Rect.fromLTWH(right - width, top, width, height); - } else if (seriesRenderer._chartState!._isLegendToggled && + } else if (seriesRenderer._renderingDetails!.isLegendToggled && oldSegmentRect != null) { rect = _performTransposedLegendToggleAnimation(seriesRenderer, segmentRect, oldSegmentRect, oldSeriesVisible, isSingleSeries, animationFactor); @@ -858,19 +969,19 @@ void _animateNormalRectSeries( final double left = segmentRect.left; double top = segmentRect.top, bottom; Rect? rect; - final num defaultCrossesAtValue = 0; + const num defaultCrossesAtValue = 0; final num crossesAt = _getCrossesAtValue(seriesRenderer, seriesRenderer._chartState!) ?? defaultCrossesAtValue; seriesRenderer._needAnimateSeriesElements = seriesRenderer._needAnimateSeriesElements || segmentRect.outerRect != oldSegmentRect; - if (!seriesRenderer._chartState!._isLegendToggled || + if (!seriesRenderer._renderingDetails!.isLegendToggled || seriesRenderer._reAnimate) { height = segmentRect.height * ((!comparePrev && !seriesRenderer._reAnimate && - !seriesRenderer._chartState!._initialRender! && + !seriesRenderer._renderingDetails!.initialRender! && oldSegmentRect == null && seriesRenderer._series.key != null && seriesRenderer._chartState!._oldSeriesKeys @@ -953,7 +1064,7 @@ void _animateNormalRectSeries( } } rect = Rect.fromLTWH(left, top, width, height); - } else if (seriesRenderer._chartState!._isLegendToggled && + } else if (seriesRenderer._renderingDetails!.isLegendToggled && oldSegmentRect != null && oldSeriesVisible != null) { rect = _performLegendToggleAnimation(seriesRenderer, segmentRect, @@ -1026,7 +1137,7 @@ void _animateStackedRectSeries( List seriesCollection; Rect? prevRegion; final _StackedSeriesBase series = - seriesRenderer._series as _StackedSeriesBase; + seriesRenderer._series as _StackedSeriesBase; index = seriesRenderer._dataPoints.indexOf(currentPoint); seriesCollection = _findSeriesCollection(_chartState); seriesIndex = seriesCollection.indexOf(seriesRenderer); @@ -1084,8 +1195,8 @@ void _drawAnimatedStackedRect( double top = segmentRect.top, height = segmentRect.height; double right = segmentRect.right, width = segmentRect.width; final double height1 = segmentRect.height, top1 = segmentRect.top; - final num defaultCrossesAtValue = 0; - final crossesAt = + const num defaultCrossesAtValue = 0; + final num crossesAt = _getCrossesAtValue(seriesRenderer, seriesRenderer._chartState!) ?? defaultCrossesAtValue; height = segmentRect.height * animationFactor; @@ -1095,14 +1206,14 @@ void _drawAnimatedStackedRect( (seriesRenderer._seriesType.contains('stackedbar')) && seriesRenderer._chart.isTransposed == true) { seriesRenderer._yAxisRenderer!._axis.isInversed != true - ? seriesRenderer._dataPoints[index].y > crossesAt + ? (seriesRenderer._dataPoints[index].y > crossesAt) == true ? prevRegion == null ? top = (segmentRect.top + segmentRect.height) - height : top = prevRegion.top - height : prevRegion == null ? top = segmentRect.top : top = prevRegion.bottom - : seriesRenderer._dataPoints[index].y > crossesAt + : (seriesRenderer._dataPoints[index].y > crossesAt) == true ? prevRegion == null ? top = segmentRect.top : top = prevRegion.bottom @@ -1124,14 +1235,14 @@ void _drawAnimatedStackedRect( (seriesRenderer._seriesType.contains('stackedbar')) && seriesRenderer._chart.isTransposed != true) { seriesRenderer._yAxisRenderer!._axis.isInversed != true - ? seriesRenderer._dataPoints[index].y > crossesAt + ? (seriesRenderer._dataPoints[index].y > crossesAt) == true ? prevRegion == null ? right = (segmentRect.right - segmentRect.width) + width : right = prevRegion.right + width : prevRegion == null ? right = segmentRect.right : right = prevRegion.left - : seriesRenderer._dataPoints[index].y > crossesAt + : (seriesRenderer._dataPoints[index].y > crossesAt) == true ? prevRegion == null ? right = segmentRect.right : right = prevRegion.left @@ -1152,17 +1263,19 @@ void _drawAnimatedStackedRect( } // This recursive function returns the previous region of the cluster stakced info for animation purposes -Rect? _getPrevRegion(dynamic yValue, - _ClusterStackedItemInfo clusterStackedItemInfo, int index, int k) { +Rect? _getPrevRegion(num yValue, _ClusterStackedItemInfo clusterStackedItemInfo, + int index, int k) { Rect? prevRegion; - if ((yValue > 0 && - clusterStackedItemInfo.stackedItemInfo[k - 1].seriesRenderer - ._dataPoints[index].yValue > - 0) || - (yValue < 0 && - clusterStackedItemInfo.stackedItemInfo[k - 1].seriesRenderer - ._dataPoints[index].yValue < - 0)) { + if (((yValue > 0) == true && + (clusterStackedItemInfo.stackedItemInfo[k - 1].seriesRenderer + ._dataPoints[index].yValue > + 0) == + true) || + ((yValue < 0) == true && + (clusterStackedItemInfo.stackedItemInfo[k - 1].seriesRenderer + ._dataPoints[index].yValue < + 0) == + true)) { prevRegion = clusterStackedItemInfo .stackedItemInfo[k - 1].seriesRenderer._dataPoints[index].region; } else { @@ -1184,7 +1297,7 @@ bool _checkSingleSeries(CartesianSeriesRenderer seriesRenderer) { count++; } } - return count == 1 ? true : false; + return count == 1; } /// to animate dynamic update in line, spline, stepLine series @@ -1290,14 +1403,16 @@ void _animateScatterSeries( double animationFactor, Canvas canvas, Paint fillPaint, - Paint strokePaint) { + Paint strokePaint, + int index, + ScatterSegment segment) { final CartesianSeries series = seriesRenderer._series; - final double width = series.markerSettings.width, + double width = series.markerSettings.width, height = series.markerSettings.height; - final DataMarkerType markerType = series.markerSettings.shape; + DataMarkerType markerType = series.markerSettings.shape; double x = point.markerPoint!.x; double y = point.markerPoint!.y; - if (seriesRenderer._chartState!._widgetNeedUpdate && + if (seriesRenderer._renderingDetails!.widgetNeedUpdate && _oldPoint != null && !seriesRenderer._reAnimate && _oldPoint.markerPoint != null) { @@ -1307,44 +1422,63 @@ void _animateScatterSeries( animationFactor, y, _oldPoint.markerPoint!.y, y, seriesRenderer); animationFactor = 1; } + if (seriesRenderer._chart.onMarkerRender != null) { + final MarkerRenderArgs markerRenderArgs = _triggerMarkerRenderEvent( + seriesRenderer, + Size(width, height), + markerType, + index, + seriesRenderer._seriesAnimation, + segment)!; + + width = markerRenderArgs.markerWidth; + height = markerRenderArgs.markerHeight; + markerType = markerRenderArgs.shape; + } final Path path = Path(); + final double animateWidth = (animationFactor * width) / 2; + final double animateHeight = (animationFactor * height) / 2; + final Rect rect = Rect.fromLTWH(x - animateWidth, y - animateHeight, + animationFactor * width, animationFactor * height); { switch (markerType) { case DataMarkerType.circle: - ShapeMaker.drawCircle( - path, x, y, animationFactor * width, animationFactor * height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.circle); break; case DataMarkerType.rectangle: - ShapeMaker.drawRectangle( - path, x, y, animationFactor * width, animationFactor * height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.rectangle); break; case DataMarkerType.image: _drawImageMarker(seriesRenderer, canvas, x, y); break; case DataMarkerType.pentagon: - ShapeMaker.drawPentagon( - path, x, y, animationFactor * width, animationFactor * height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.pentagon); break; case DataMarkerType.verticalLine: - ShapeMaker.drawVerticalLine( - path, x, y, animationFactor * width, animationFactor * height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.verticalLine); break; case DataMarkerType.invertedTriangle: - ShapeMaker.drawInvertedTriangle( - path, x, y, animationFactor * width, animationFactor * height); + ShapePainter.getShapesPath( + path: path, + rect: rect, + shapeType: ShapeMarkerType.invertedTriangle); break; case DataMarkerType.horizontalLine: - ShapeMaker.drawHorizontalLine( - path, x, y, animationFactor * width, animationFactor * height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.horizontalLine); break; case DataMarkerType.diamond: - ShapeMaker.drawDiamond( - path, x, y, animationFactor * width, animationFactor * height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.diamond); break; case DataMarkerType.triangle: - ShapeMaker.drawTriangle( - path, x, y, animationFactor * width, animationFactor * height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.triangle); break; case DataMarkerType.none: break; @@ -1616,19 +1750,15 @@ void _animateBoxSeries( path.moveTo(centerMax, upperQuartileY); path.lineTo(centerMax, lowerQuartileY); path.moveTo(centerMax, maxY); - if (centerMax < upperQuartileX) { - path.lineTo(upperQuartileX, maxY); - } else { - path.lineTo(upperQuartileX, maxY); - } + (centerMax < upperQuartileX) + ? path.lineTo(upperQuartileX, maxY) + : path.lineTo(upperQuartileX, maxY); path.moveTo(medianX, upperQuartileY); path.lineTo(medianX, lowerQuartileY); path.moveTo(centerMin, maxY); - if (centerMin > lowerQuartileX) { - path.lineTo(lowerQuartileX, maxY); - } else { - path.lineTo(lowerQuartileX, maxY); - } + (centerMin > lowerQuartileX) + ? path.lineTo(lowerQuartileX, maxY) + : path.lineTo(lowerQuartileX, maxY); path.moveTo(centerMin, upperQuartileY); path.lineTo(centerMin, lowerQuartileY); if (showMean) { @@ -1819,17 +1949,13 @@ void _animateCandleSeries( Offset(centerHigh, highY), Offset(centerLow, highY), paint); } else { path.moveTo(centerHigh, highY); - if (centerHigh < closeX) { - path.lineTo(closeX, highY); - } else { - path.lineTo(closeX, highY); - } + (centerHigh < closeX) + ? path.lineTo(closeX, highY) + : path.lineTo(closeX, highY); path.moveTo(centerLow, highY); - if (centerLow > openX) { - path.lineTo(openX, highY); - } else { - path.lineTo(openX, highY); - } + (centerLow > openX) + ? path.lineTo(openX, highY) + : path.lineTo(openX, highY); } if (openX == closeX) { canvas.drawLine(Offset(openX, openY), Offset(closeX, closeY), paint); @@ -1867,14 +1993,12 @@ void _animateCandleSeries( } } - if (seriesRenderer._series.dashArray[0] != 0 && - seriesRenderer._series.dashArray[1] != 0 && - paint.style != PaintingStyle.fill && - seriesRenderer._series.animationDuration <= 0) { - _drawDashedLine(canvas, seriesRenderer._series.dashArray, paint, path); - } else { - canvas.drawPath(path, paint); - } + (seriesRenderer._series.dashArray[0] != 0 && + seriesRenderer._series.dashArray[1] != 0 && + paint.style != PaintingStyle.fill && + seriesRenderer._series.animationDuration <= 0) + ? _drawDashedLine(canvas, seriesRenderer._series.dashArray, paint, path) + : canvas.drawPath(path, paint); if (paint.style == PaintingStyle.fill) { if (transposed) { if (showSameValue) { @@ -1920,12 +2044,10 @@ List>? _getNearestChartPoints( for (int i = 0; i < dataList.length; i++) { xValues.add(dataList[i].xValue); - if (cartesianSeriesRenderer is BoxAndWhiskerSeriesRenderer) { - yValues.add((dataList[i].maximum! + dataList[i].minimum!) / 2); - } else { - yValues - .add(dataList[i].yValue ?? (dataList[i].high + dataList[i].low) / 2); - } + (cartesianSeriesRenderer is BoxAndWhiskerSeriesRenderer) + ? yValues.add((dataList[i].maximum! + dataList[i].minimum!) / 2) + : yValues.add( + dataList[i].yValue ?? (dataList[i].high + dataList[i].low) / 2); } num nearPointX = dataList[0].xValue; num nearPointY = actualYAxisRenderer._visibleRange!.minimum; @@ -1979,12 +2101,9 @@ List>? _getNearestChartPoints( } /// Return the arguments for zoom event -ZoomPanArgs _bindZoomEvent( - SfCartesianChart chart, - ChartAxisRenderer axisRenderer, - ZoomPanArgs? zoomPanArgs, - ChartZoomingCallback zoomEventType) { - zoomPanArgs = ZoomPanArgs(axisRenderer._axis, +ZoomPanArgs _bindZoomEvent(SfCartesianChart chart, + ChartAxisRenderer axisRenderer, ChartZoomingCallback zoomEventType) { + final ZoomPanArgs zoomPanArgs = ZoomPanArgs(axisRenderer._axis, axisRenderer._previousZoomPosition, axisRenderer._previousZoomFactor); zoomPanArgs.currentZoomFactor = axisRenderer._zoomFactor; zoomPanArgs.currentZoomPosition = axisRenderer._zoomPosition; @@ -2034,36 +2153,38 @@ Future _getImageInfo(ImageProvider imageProvider) async { } //ignore: avoid_void_async -void _calculateImage( - [dynamic? chartState, - dynamic? seriesRenderer, +void _calculateImage(SfCartesianChartState chartState, + [CartesianSeriesRenderer? seriesRenderer, TrackballBehavior? trackballBehavior]) async { - final dynamic chart = chartState?._chart; + final SfCartesianChart chart = chartState._chart; + // ignore: unnecessary_null_comparison if (chart != null && seriesRenderer == null) { if (chart.plotAreaBackgroundImage != null) { chartState._backgroundImage = - await _getImageInfo(chart.plotAreaBackgroundImage); + await _getImageInfo(chart.plotAreaBackgroundImage!); chartState._renderOutsideAxis.state.axisRepaintNotifier.value++; } if (chart.legend.image != null) { - chartState._legendIconImage = await _getImageInfo(chart.legend.image); - chartState._chartLegend.legendRepaintNotifier.value++; + chartState._legendIconImage = await _getImageInfo(chart.legend.image!); + chartState._renderingDetails.chartLegend.legendRepaintNotifier.value++; } } else if (trackballBehavior != null && trackballBehavior.markerSettings!.image != null) { chartState._trackballMarkerSettingsRenderer._image = await _getImageInfo(trackballBehavior.markerSettings!.image!); - chartState._trackballRepaintNotifier.value++; + chartState._repaintNotifiers['trackball']!.value++; } else { - final CartesianSeries series = seriesRenderer._series; - // final MarkerSettingsRenderer markerSettingsRenderer = seriesRenderer._markerSettingsRenderer; + final CartesianSeries series = seriesRenderer!._series; if (series.markerSettings.image != null) { - seriesRenderer._markerSettingsRenderer._image = + seriesRenderer._markerSettingsRenderer!._image = await _getImageInfo(series.markerSettings.image!); - seriesRenderer._repaintNotifier.value++; + if (!chartState._renderingDetails.isImageDrawn) + seriesRenderer._repaintNotifier.value++; + chartState._renderingDetails.isImageDrawn = true; if (seriesRenderer._seriesType == 'scatter' && - seriesRenderer._chart.legend.isVisible) { - seriesRenderer._chartState!._chartLegend.legendRepaintNotifier.value++; + seriesRenderer._chart.legend.isVisible!) { + seriesRenderer + ._renderingDetails!.chartLegend.legendRepaintNotifier.value++; } } } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/common/data_label.dart b/packages/syncfusion_flutter_charts/lib/src/chart/common/data_label.dart index 3da8138e1..1c8ec86c8 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/common/data_label.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/common/data_label.dart @@ -7,9 +7,10 @@ part of charts; /// /// Provide options like color, border width, border color, alignment and data label text style for customization. /// +@immutable class DataLabelSettings { /// Creating an argument constructor of DataLabelSettings class. - DataLabelSettings( + const DataLabelSettings( {this.alignment = ChartAlignment.center, this.color, this.textStyle = const TextStyle( @@ -84,6 +85,7 @@ class DataLabelSettings { /// )); ///} ///``` + final TextStyle textStyle; ///Margin between the data label text and its shape. @@ -417,12 +419,69 @@ class DataLabelSettings { /// ///Also refer [labelAlignment]. /// - ///_Note:_ - This property is only applicable for Cartesian charts and not for + ///_Note:_ This property is only applicable for Cartesian charts and not for /// Circular, Pyramid and Funnel charts. /// ///Defaults to `null`. final Offset? offset; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is DataLabelSettings && + other.alignment == alignment && + other.color == color && + other.textStyle == textStyle && + other.margin == margin && + other.opacity == opacity && + other.labelAlignment == labelAlignment && + other.borderRadius == borderRadius && + other.isVisible == isVisible && + other.angle == angle && + other.builder == builder && + other.useSeriesColor == useSeriesColor && + other.offset == offset && + other.showCumulativeValues == showCumulativeValues && + other.showZeroValue == showZeroValue && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.labelIntersectAction == labelIntersectAction && + other.connectorLineSettings == connectorLineSettings && + other.labelPosition == labelPosition; + } + + @override + int get hashCode { + final List values = [ + alignment, + color, + textStyle, + margin, + opacity, + labelAlignment, + borderRadius, + isVisible, + angle, + builder, + useSeriesColor, + offset, + showCumulativeValues, + showZeroValue, + borderColor, + borderWidth, + labelIntersectAction, + connectorLineSettings, + labelPosition + ]; + return hashList(values); + } } ///Datalabel renderer class for mutable fields and methods diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/common/data_label_renderer.dart b/packages/syncfusion_flutter_charts/lib/src/chart/common/data_label_renderer.dart index 2a387a3ba..3c48a391d 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/common/data_label_renderer.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/common/data_label_renderer.dart @@ -19,23 +19,25 @@ void _calculateDataLabelPosition( final DataLabelSettings dataLabel = series.dataLabelSettings; Size? textSize, textSize2, textSize3, textSize4, textSize5; double? value1, value2; - final int boxPlotPadding = 8; + const int boxPlotPadding = 8; final Rect rect = _calculatePlotOffset( _chartState._chartAxis._axisClipRect, Offset(seriesRenderer._xAxisRenderer!._axis.plotOffset, seriesRenderer._yAxisRenderer!._axis.plotOffset)); if (seriesRenderer._seriesType.contains('hilo') || seriesRenderer._seriesType.contains('candle')) { - value1 = - ((point.open != null && point.close != null && point.close < point.open) - ? point.close - : point.open) - ?.toDouble(); - value2 = - ((point.open != null && point.close != null && point.close > point.open) - ? point.close - : point.open) - ?.toDouble(); + value1 = ((point.open != null && + point.close != null && + (point.close < point.open) == true) + ? point.close + : point.open) + ?.toDouble(); + value2 = ((point.open != null && + point.close != null && + (point.close > point.open) == true) + ? point.close + : point.open) + ?.toDouble(); } final bool transposed = _chartState._requireInvertedAxis; final bool inversed = seriesRenderer._yAxisRenderer!._axis.isInversed; @@ -251,7 +253,7 @@ void _calculateDataLabelPosition( if (!isBoxSeries) { point.label3 = point.dataLabelMapper ?? _getLabelText( - point.open > point.close + (point.open > point.close) == true ? !inversed ? point.close : point.open @@ -261,7 +263,7 @@ void _calculateDataLabelPosition( seriesRenderer); point.label4 = point.dataLabelMapper ?? _getLabelText( - point.open > point.close + (point.open > point.close) == true ? !inversed ? point.open : point.close @@ -297,33 +299,22 @@ void _calculateDataLabelPosition( ? measureText(point.label3!, font) : templateSize; if (seriesRenderer._seriesType.contains('hilo')) { - if (point.open > point.close) { - chartLocation3 = _ChartLocation( - point.centerClosePoint!.x + textSize3!.width, - point.closePoint!.y); - } else { - chartLocation3 = _ChartLocation( - point.centerOpenPoint!.x - textSize3!.width, - point.openPoint!.y); - } + chartLocation3 = (point.open > point.close) == true + ? _ChartLocation(point.centerClosePoint!.x + textSize3!.width, + point.closePoint!.y) + : _ChartLocation(point.centerOpenPoint!.x - textSize3!.width, + point.openPoint!.y); } else if (seriesRenderer._seriesType == 'candle' && seriesRenderer._chartState!._requireInvertedAxis) { - if (point.open > point.close) { - chartLocation3 = - _ChartLocation(point.closePoint!.x, markerPoint2.y + 1); - } else { - chartLocation3 = - _ChartLocation(point.openPoint!.x, markerPoint2.y + 1); - } + chartLocation3 = (point.open > point.close) == true + ? _ChartLocation(point.closePoint!.x, markerPoint2.y + 1) + : _ChartLocation(point.openPoint!.x, markerPoint2.y + 1); } else if (isBoxSeries) { - if (seriesRenderer._chartState!._requireInvertedAxis) { - chartLocation3 = _ChartLocation( - point.lowerQuartilePoint!.x + boxPlotPadding, - markerPoint2.y + 1); - } else { - chartLocation3 = _ChartLocation( - point.region!.topCenter.dx, markerPoint2.y - boxPlotPadding); - } + chartLocation3 = (seriesRenderer._chartState!._requireInvertedAxis) + ? _ChartLocation(point.lowerQuartilePoint!.x + boxPlotPadding, + markerPoint2.y + 1) + : _ChartLocation( + point.region!.topCenter.dx, markerPoint2.y - boxPlotPadding); } else { chartLocation3 = _ChartLocation(point.region!.topCenter.dx, markerPoint2.y); @@ -332,46 +323,33 @@ void _calculateDataLabelPosition( ? measureText(point.label4!, font) : templateSize; if (seriesRenderer._seriesType.contains('hilo')) { - if (point.open > point.close) { - chartLocation4 = _ChartLocation( - point.centerOpenPoint!.x - textSize4!.width, - point.openPoint!.y); - } else { - chartLocation4 = _ChartLocation( - point.centerClosePoint!.x + textSize4!.width, - point.closePoint!.y); - } + chartLocation4 = (point.open > point.close) == true + ? _ChartLocation(point.centerOpenPoint!.x - textSize4!.width, + point.openPoint!.y) + : _ChartLocation(point.centerClosePoint!.x + textSize4!.width, + point.closePoint!.y); } else if (seriesRenderer._seriesType == 'candle' && seriesRenderer._chartState!._requireInvertedAxis) { - if (point.open > point.close) { - chartLocation4 = - _ChartLocation(point.openPoint!.x, markerPoint3.y + 1); - } else { - chartLocation4 = - _ChartLocation(point.closePoint!.x, markerPoint3.y + 1); - } + chartLocation4 = (point.open > point.close) == true + ? _ChartLocation(point.openPoint!.x, markerPoint3.y + 1) + : _ChartLocation(point.closePoint!.x, markerPoint3.y + 1); } else if (isBoxSeries) { - if (seriesRenderer._chartState!._requireInvertedAxis) { - chartLocation4 = _ChartLocation( - point.upperQuartilePoint!.x - boxPlotPadding, - markerPoint3.y + 1); - } else { - chartLocation4 = _ChartLocation( - point.region!.bottomCenter.dx, markerPoint3.y + boxPlotPadding); - } + chartLocation4 = (seriesRenderer._chartState!._requireInvertedAxis) + ? _ChartLocation(point.upperQuartilePoint!.x - boxPlotPadding, + markerPoint3.y + 1) + : _ChartLocation(point.region!.bottomCenter.dx, + markerPoint3.y + boxPlotPadding); } else { chartLocation4 = _ChartLocation(point.region!.bottomCenter.dx, markerPoint3.y + 1); } if (isBoxSeries) { textSize5 = measureText(point.label5!, font); - if (!seriesRenderer._chartState!._requireInvertedAxis) { - chartLocation5 = _ChartLocation( - point.centerMedianPoint!.x, point.centerMedianPoint!.y); - } else { - chartLocation5 = _ChartLocation( - point.centerMedianPoint!.x, point.centerMedianPoint!.y); - } + chartLocation5 = (!seriesRenderer._chartState!._requireInvertedAxis) + ? _ChartLocation( + point.centerMedianPoint!.x, point.centerMedianPoint!.y) + : _ChartLocation( + point.centerMedianPoint!.x, point.centerMedianPoint!.y); } final List<_ChartLocation?> alignedLabelLocations2 = _getAlignedLabelLocations(_chartState, seriesRenderer, point, @@ -424,7 +402,7 @@ List<_ChartLocation?> _getAlignedLabelLocations( Size textSize) { final SfCartesianChart chart = _chartState._chart; final XyDataSeries series = - seriesRenderer._series as XyDataSeries; + seriesRenderer._series as XyDataSeries; final bool transposed = _chartState._requireInvertedAxis; final bool isRangeSeries = seriesRenderer._seriesType.contains('range') || seriesRenderer._seriesType.contains('hilo') || @@ -601,12 +579,10 @@ _ChartLocation _getSecondLabelLocation( final EdgeInsets margin = dataLabel.margin; bool minus; - if (seriesRenderer._seriesType == 'boxandwhisker') { - minus = (point.minimum! < 0 && !inversed) || - (!(point.minimum! < 0) && inversed); - } else { - minus = (point.low < 0 && !inversed) || (!(point.low < 0) && inversed); - } + minus = (seriesRenderer._seriesType == 'boxandwhisker') + ? (point.minimum! < 0 && !inversed) || (!(point.minimum! < 0) && inversed) + : ((point.low < 0) == true && !inversed) || + ((point.low < 0) == false && inversed); if (!_chartState._requireInvertedAxis) { chartLocation2.y = _calculateRectPosition( @@ -720,7 +696,7 @@ void _calculateDataLabelRegion( final Rect rect = fillRect.middleRect; if (seriesRenderer._seriesType == 'candle' && _chartState._requireInvertedAxis && - point.close > point.high) { + (point.close > point.high) == true) { point.labelLocation = _ChartLocation( rect.left - rect.width - textSize.width, rect.top + rect.height / 2 - textSize.height / 2); @@ -732,7 +708,7 @@ void _calculateDataLabelRegion( rect.top + rect.height / 2 - textSize.height / 2); } else if (seriesRenderer._seriesType == 'candle' && !_chartState._requireInvertedAxis && - point.close > point.high) { + (point.close > point.high) == true) { point.labelLocation = _ChartLocation( rect.left + rect.width / 2 - textSize.width / 2, rect.top + rect.height + textSize.height); @@ -834,7 +810,7 @@ void _calculateDataLabelRegion( } else { if (seriesRenderer._seriesType == 'candle' && _chartState._requireInvertedAxis && - point.close > point.high) { + (point.close > point.high) == true) { point.labelLocation = _ChartLocation( rect.left - rect.width - textSize.width - 2, rect.top + rect.height / 2 - textSize.height / 2); @@ -846,7 +822,7 @@ void _calculateDataLabelRegion( rect.top + rect.height / 2 - textSize.height / 2); } else if (seriesRenderer._seriesType == 'candle' && !_chartState._requireInvertedAxis && - point.close > point.high) { + (point.close > point.high) == true) { point.labelLocation = _ChartLocation( rect.left + rect.width / 2 - textSize.width / 2, rect.top + rect.height + textSize.height / 2); @@ -866,7 +842,7 @@ void _calculateDataLabelRegion( if (isRangeSeries || isBoxSeries) { if (seriesRenderer._seriesType == 'candle' && _chartState._requireInvertedAxis && - point.close > point.high) { + (point.close > point.high) == true) { point.labelLocation2 = _ChartLocation( rect2!.left + rect2.width + textSize2!.width + 2, rect2.top + rect2.height / 2 - textSize2.height / 2); @@ -878,7 +854,7 @@ void _calculateDataLabelRegion( rect2.top + rect2.height / 2 - textSize2.height / 2); } else if (seriesRenderer._seriesType == 'candle' && !_chartState._requireInvertedAxis && - point.close > point.high) { + (point.close > point.high) == true) { point.labelLocation2 = _ChartLocation( rect2!.left + rect2.width / 2 - textSize2!.width / 2, rect2.top - rect2.height - textSize2.height); @@ -935,7 +911,7 @@ double _calculatePathPosition( CartesianChartPoint currentPoint, Size markerSize) { final XyDataSeries series = - seriesRenderer._series as XyDataSeries; + seriesRenderer._series as XyDataSeries; const double padding = 5; final bool needFill = series.dataLabelSettings.color != null || series.dataLabelSettings.color != Colors.transparent || @@ -1025,7 +1001,7 @@ double _calculatePathActualPosition( CartesianChartPoint currentPoint, bool inversed) { final XyDataSeries series = - seriesRenderer._series as XyDataSeries; + seriesRenderer._series as XyDataSeries; late double yLocation; bool isBottom, isOverLap = true; Rect labelRect; @@ -1099,10 +1075,10 @@ ChartDataLabelAlignment _getActualPathDataLabelAlignment( if (!_nextPoint!.isVisible && !previousPoint!.isVisible) { position = ChartDataLabelAlignment.top; } else if (!_nextPoint.isVisible) { - position = - (_nextPoint.yValue > yValue || previousPoint!.yValue > yValue) - ? ChartDataLabelAlignment.bottom - : ChartDataLabelAlignment.top; + position = ((_nextPoint.yValue > yValue) == true || + (previousPoint!.yValue > yValue) == true) + ? ChartDataLabelAlignment.bottom + : ChartDataLabelAlignment.top; } else { final num slope = (_nextPoint.yValue - previousPoint!.yValue) / 2; final num intersectY = @@ -1205,10 +1181,10 @@ void _drawDataLabel( final bool isDatalabelCollide = (_chartState._requireInvertedAxis || (dataLabelSettingsRenderer._angle / 90) % 2 != 1) && _findingCollision(labelRect, _chartState._renderDatalabelRegions); - if (label.isNotEmpty && isDatalabelCollide + if (!(label.isNotEmpty && isDatalabelCollide) // ignore: unnecessary_null_comparison - ? dataLabel.labelIntersectAction == null - : true) { + || + dataLabel.labelIntersectAction == null) { final TextStyle _textStyle = TextStyle( color: fontColor.withOpacity(opacity), fontSize: font.fontSize, @@ -1368,14 +1344,18 @@ void _drawDataLabelRectAndText( Color? seriesColor = seriesRenderer._seriesColor!; if (seriesRenderer._seriesType == 'waterfall') { seriesColor = _getWaterfallSeriesColor( - seriesRenderer._series as WaterfallSeries, point, seriesColor); + seriesRenderer._series as WaterfallSeries, + point, + seriesColor); } final Paint paint = Paint() - ..color = (dataLabelSettingsRenderer._color ?? - (point.pointColorMapper ?? seriesColor!)) - .withOpacity((opacity - (1 - dataLabel.opacity)) < 0 - ? 0 - : opacity - (1 - dataLabel.opacity)) + ..color = dataLabelSettingsRenderer._color != Colors.transparent + ? ((dataLabelSettingsRenderer._color ?? + (point.pointColorMapper ?? seriesColor!)) + .withOpacity((opacity - (1 - dataLabel.opacity)) < 0 + ? 0 + : opacity - (1 - dataLabel.opacity))) + : Colors.transparent ..style = PaintingStyle.fill; canvas.save(); canvas.translate(point.dataLabelRegion!.center.dx + x, @@ -1516,7 +1496,7 @@ void _drawDataLabelRectAndText( final List<_ChartLocation> outliersLocation = <_ChartLocation>[]; final List outliersTextSize = []; final List outliersRect = []; - final int outlierPadding = 12; + const int outlierPadding = 12; for (int outlierIndex = 0; outlierIndex < point.outliers!.length; outlierIndex++) { @@ -1642,7 +1622,7 @@ String _getLabelText( dynamic labelValue, CartesianSeriesRenderer seriesRenderer) { if (labelValue.toString().split('.').length > 1) { final String str = labelValue.toString(); - final List list = str.split('.'); + final List list = str.split('.'); labelValue = double.parse(labelValue.toStringAsFixed(6)); if (list[1] == '0' || list[1] == '00' || @@ -1658,12 +1638,11 @@ String _getLabelText( final dynamic value = yAxis?.numberFormat != null ? yAxis.numberFormat.format(labelValue) : labelValue; - return (yAxis.labelFormat != null && yAxis.labelFormat != '') + return ((yAxis.labelFormat != null && yAxis.labelFormat != '') ? yAxis.labelFormat.replaceAll(RegExp('{value}'), value.toString()) - : value.toString(); + : value.toString()) as String; } else { - final dynamic value = labelValue; - return value.toString(); + return labelValue.toString(); } } @@ -1682,7 +1661,7 @@ double _calculateRectPosition( bool inverted, EdgeInsets margin) { final XyDataSeries series = - seriesRenderer._series as XyDataSeries; + seriesRenderer._series as XyDataSeries; double padding; padding = seriesRenderer._seriesType.contains('hilo') || seriesRenderer._seriesType.contains('candle') || @@ -1773,7 +1752,7 @@ double _calculateRectActualPosition( bool isOverLap = true; int position = 0; final XyDataSeries series = - seriesRenderer._series as XyDataSeries; + seriesRenderer._series as XyDataSeries; final int finalPosition = seriesRenderer._seriesType.contains('range') ? 2 : 4; while (isOverLap && position < finalPosition) { @@ -1816,11 +1795,9 @@ double _calculateRectActualPosition( _findingCollision( labelRect, _chartState._renderDatalabelRegions)); } - seriesRenderer - ._dataPoints[index].dataLabelSaturationRegionInside = isOverLap || - seriesRenderer._dataPoints[index].dataLabelSaturationRegionInside - ? true - : false; + seriesRenderer._dataPoints[index].dataLabelSaturationRegionInside = + isOverLap || + seriesRenderer._dataPoints[index].dataLabelSaturationRegionInside; position++; } return location; @@ -1840,7 +1817,7 @@ double _calculateTopAndOuterPosition( bool inverted, double borderWidth) { final XyDataSeries series = - seriesRenderer._series as XyDataSeries; + seriesRenderer._series as XyDataSeries; final num markerHeight = series.markerSettings.isVisible ? series.markerSettings.height / 2 : 0; if (((isMinus && !seriesRenderer._seriesType.contains('range')) && @@ -1898,15 +1875,17 @@ bool _isLabelWithinRange(CartesianSeriesRenderer seriesRenderer, CartesianChartPoint point) { bool withInRange = true; final bool isBoxSeries = seriesRenderer._seriesType.contains('boxandwhisker'); - if (!(seriesRenderer._yAxisRenderer is LogarithmicAxisRenderer)) { + if (seriesRenderer._yAxisRenderer is! LogarithmicAxisRenderer) { withInRange = _withInRange( point.xValue, seriesRenderer._xAxisRenderer!._visibleRange!) && (seriesRenderer._seriesType.contains('range') || seriesRenderer._seriesType == 'hilo' - ? (_withInRange(isBoxSeries ? point.minimum : point.low, - seriesRenderer._yAxisRenderer!._visibleRange!) || - _withInRange(isBoxSeries ? point.maximum : point.high, - seriesRenderer._yAxisRenderer!._visibleRange!)) + ? (isBoxSeries && point.minimum != null && point.maximum != null) || + (!isBoxSeries && point.low != null && point.high != null) && + (_withInRange(isBoxSeries ? point.minimum : point.low, + seriesRenderer._yAxisRenderer!._visibleRange!) || + _withInRange(isBoxSeries ? point.maximum : point.high, + seriesRenderer._yAxisRenderer!._visibleRange!)) : seriesRenderer._seriesType == 'hiloopenclose' || seriesRenderer._seriesType.contains('candle') || isBoxSeries diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/common/marker.dart b/packages/syncfusion_flutter_charts/lib/src/chart/common/marker.dart index 4c592f85a..88f30fc0a 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/common/marker.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/common/marker.dart @@ -7,6 +7,7 @@ part of charts; /// /// Provides the options of [color], border width, border color and [shape] of the marker to customize the appearance. /// +@immutable class MarkerSettings { /// Creating an argument constructor of MarkerSettings class. const MarkerSettings( @@ -180,6 +181,41 @@ class MarkerSettings { ///} ///``` final ImageProvider? image; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is MarkerSettings && + other.isVisible == isVisible && + other.height == height && + other.width == width && + other.color == color && + other.shape == shape && + other.borderWidth == borderWidth && + other.borderColor == borderColor && + other.image == image; + } + + @override + int get hashCode { + final List values = [ + isVisible, + height, + width, + color, + shape, + borderWidth, + borderColor, + image + ]; + return hashList(values); + } } /// Marker settings renderer class for mutable fields and methods @@ -216,16 +252,16 @@ class MarkerSettingsRenderer { seriesRenderer, seriesRenderer._dataPoints[markerIndex]); Paint strokePaint, fillPaint; final XyDataSeries series = - seriesRenderer._series as XyDataSeries; + seriesRenderer._series as XyDataSeries; final Size size = Size(series.markerSettings.width, series.markerSettings.height); final DataMarkerType markerType = series.markerSettings.shape; CartesianChartPoint point; - final bool hasPointColor = (series.pointColorMapper != null) ? true : false; + final bool hasPointColor = series.pointColorMapper != null; final bool isBoxSeries = seriesRenderer._seriesType.contains('boxandwhisker'); final double opacity = (animationController != null && - (seriesRenderer._chartState!._initialRender! || + (seriesRenderer._renderingDetails!.initialRender! || seriesRenderer._needAnimateSeriesElements)) ? animationController.value : 1; @@ -233,35 +269,35 @@ class MarkerSettingsRenderer { Color? seriesColor = seriesRenderer._seriesColor; if (seriesRenderer._seriesType == 'waterfall') { seriesColor = _getWaterfallSeriesColor( - seriesRenderer._series as WaterfallSeries, point, seriesColor); + seriesRenderer._series as WaterfallSeries, + point, + seriesColor); } _borderColor = series.markerSettings.borderColor ?? seriesColor; _color = series.markerSettings.color; _borderWidth = series.markerSettings.borderWidth; - if (!isBoxSeries) { - seriesRenderer._markerShapes.add(isDataPointVisible - ? _getMarkerShapesPath( - markerType, - Offset(point.markerPoint!.x, point.markerPoint!.y), - size, - seriesRenderer, - markerIndex, - null, - animationController) - : null); - } else { - seriesRenderer._markerShapes.add(isDataPointVisible - ? _getMarkerShapesPath( - markerType, - Offset(point.outliersPoint[outlierIndex!].x, - point.outliersPoint[outlierIndex].y), - size, - seriesRenderer, - markerIndex, - null, - animationController) - : null); - } + !isBoxSeries + ? seriesRenderer._markerShapes.add(isDataPointVisible + ? _getMarkerShapesPath( + markerType, + Offset(point.markerPoint!.x, point.markerPoint!.y), + size, + seriesRenderer, + markerIndex, + null, + animationController) + : null) + : seriesRenderer._markerShapes.add(isDataPointVisible + ? _getMarkerShapesPath( + markerType, + Offset(point.outliersPoint[outlierIndex!].x, + point.outliersPoint[outlierIndex].y), + size, + seriesRenderer, + markerIndex, + null, + animationController) + : null); if (seriesRenderer._seriesType.contains('range') || seriesRenderer._seriesType == 'hilo') { seriesRenderer._markerShapes2.add(isDataPointVisible @@ -320,7 +356,8 @@ class MarkerSettingsRenderer { ? series.emptyPointSettings.color : _color != Colors.transparent ? (_color ?? - (seriesRenderer._chartState!._chartTheme.brightness == + (seriesRenderer + ._renderingDetails!.chartTheme.brightness == Brightness.light ? Colors.white : Colors.black)) diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/common/renderer.dart b/packages/syncfusion_flutter_charts/lib/src/chart/common/renderer.dart index ab40efb67..aa8a4da24 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/common/renderer.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/common/renderer.dart @@ -12,6 +12,7 @@ class _DataLabelRenderer extends StatefulWidget { _DataLabelRendererState? state; @override + // ignore: no_logic_in_create_state State createState() { state = _DataLabelRendererState(); return state!; @@ -40,7 +41,10 @@ class _DataLabelRendererState extends State<_DataLabelRenderer> Widget build(BuildContext context) { widget.state = this; animationController.duration = Duration( - milliseconds: widget.cartesianChartState._initialRender! ? 500 : 0); + milliseconds: + widget.cartesianChartState._renderingDetails.initialRender! + ? 500 + : 0); final Animation dataLabelAnimation = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation( parent: animationController, @@ -110,7 +114,7 @@ class _DataLabelPainter extends CustomPainter { if (seriesRenderer._series.dataLabelSettings.isVisible && (seriesRenderer._animationCompleted || seriesRenderer._series.animationDuration == 0 || - !cartesianChartState._initialRender!) && + !cartesianChartState._renderingDetails.initialRender!) && (!seriesRenderer._needAnimateSeriesElements || (cartesianChartState._seriesDurationFactor < seriesRenderer._animationController.value || @@ -159,9 +163,9 @@ void _calculateRectSeriesRegion( final ChartAxisRenderer xAxisRenderer = seriesRenderer._xAxisRenderer!; final ChartAxisRenderer yAxisRenderer = seriesRenderer._yAxisRenderer!; final num? crossesAt = _getCrossesAtValue(seriesRenderer, _chartState); - final num sideBySideMinimumVal = seriesRenderer.sideBySideInfo!.minimum; + final num sideBySideMinimumVal = seriesRenderer._sideBySideInfo!.minimum; - final num sideBySideMaximumVal = seriesRenderer.sideBySideInfo!.maximum; + final num sideBySideMaximumVal = seriesRenderer._sideBySideInfo!.maximum; final num origin = crossesAt ?? math.max(yAxisRenderer._visibleRange!.minimum, 0); @@ -205,7 +209,7 @@ void _calculateRectSeriesRegion( if (seriesRenderer._seriesType != 'stackedcolumn100' && seriesRenderer._seriesType != 'stackedbar100' && seriesRenderer._seriesType != 'waterfall' && - series.isTrackVisible) { + series.isTrackVisible == true) { final Rect shadowPointRect = _calculateShadowRectangle( point.xValue + sideBySideMinimumVal, seriesRenderer._seriesType == 'rangecolumn' ? point.high : point.yValue, @@ -230,17 +234,17 @@ void _calculateRectSeriesRegion( } else { point.markerPoint = _chartState._requireInvertedAxis != true ? (yAxisRenderer._axis.isInversed - ? (point.yValue.isNegative + ? (point.yValue.isNegative == true ? _ChartLocation(rect.topCenter.dx, rect.topCenter.dy) : _ChartLocation(rect.bottomCenter.dx, rect.bottomCenter.dy)) - : (point.yValue.isNegative + : (point.yValue.isNegative == true ? _ChartLocation(rect.bottomCenter.dx, rect.bottomCenter.dy) : _ChartLocation(rect.topCenter.dx, rect.topCenter.dy))) : (yAxisRenderer._axis.isInversed - ? (point.yValue.isNegative + ? (point.yValue.isNegative == true ? _ChartLocation(rect.centerRight.dx, rect.centerRight.dy) : _ChartLocation(rect.centerLeft.dx, rect.centerLeft.dy)) - : (point.yValue.isNegative + : (point.yValue.isNegative == true ? _ChartLocation(rect.centerLeft.dx, rect.centerLeft.dy) : _ChartLocation(rect.centerRight.dx, rect.centerRight.dy))); } @@ -319,7 +323,8 @@ void _calculatePointSeriesRegion( currentPoint.x + series.markerSettings.width, currentPoint.y + series.markerSettings.width); } else { - final BubbleSeries bubbleSeries = series as BubbleSeries; + final BubbleSeries bubbleSeries = + series as BubbleSeries; num bubbleRadius = 0, sizeRange = 0, radiusRange, bubbleSize; if (seriesRenderer is BubbleSeriesRenderer) { sizeRange = seriesRenderer._maxSize! - seriesRenderer._minSize!; @@ -372,9 +377,9 @@ void _calculatePathSeriesRegion( num? midY]) { final ChartAxisRenderer xAxisRenderer = seriesRenderer._xAxisRenderer!; final ChartAxisRenderer yAxisRenderer = seriesRenderer._yAxisRenderer!; - final num? sideBySideMinimumVal = seriesRenderer.sideBySideInfo?.minimum; + final num? sideBySideMinimumVal = seriesRenderer._sideBySideInfo?.minimum; - final num? sideBySideMaximumVal = seriesRenderer.sideBySideInfo?.maximum; + final num? sideBySideMaximumVal = seriesRenderer._sideBySideInfo?.maximum; if (seriesRenderer._seriesType != 'rangearea' && seriesRenderer._seriesType != 'splinerangearea' && (!seriesRenderer._seriesType.contains('hilo')) && @@ -480,10 +485,14 @@ void _calculatePathSeriesRegion( point.markerPoint = currentPoint; } else { num? value1, value2; - value1 = (point.low != null && point.high != null && point.low < point.high) + value1 = (point.low != null && + point.high != null && + (point.low < point.high) == true) ? point.high : point.low; - value2 = (point.low != null && point.high != null && point.low > point.high) + value2 = (point.low != null && + point.high != null && + (point.low > point.high) == true) ? point.high : point.low; if (seriesRenderer._seriesType == 'boxandwhisker') { @@ -814,10 +823,16 @@ void _calculateTooltipRegion( if ((series.enableTooltip != null || // ignore: unnecessary_null_comparison seriesRenderer._chart.trackballBehavior != null || - chart.onPointTapped != null) && + chart.onPointTapped != null || + seriesRenderer._series.onPointTap != null || + seriesRenderer._series.onPointDoubleTap != null || + seriesRenderer._series.onPointLongPress != null) && (series.enableTooltip || seriesRenderer._chart.trackballBehavior.enable || - chart.onPointTapped != null) && + chart.onPointTapped != null || + seriesRenderer._series.onPointTap != null || + seriesRenderer._series.onPointDoubleTap != null || + seriesRenderer._series.onPointLongPress != null) && // ignore: unnecessary_null_comparison point != null && !point.isGap && @@ -906,7 +921,7 @@ void _calculateTooltipRegion( ? seriesRenderer._seriesType == 'column' || seriesRenderer._seriesType.contains('stackedcolumn') || seriesRenderer._seriesType == 'histogram' - ? (point.yValue > (crossesAt ?? 0)) + ? (point.yValue > (crossesAt ?? 0)) == true ? point.region!.topCenter : point.region!.bottomCenter : seriesRenderer._seriesType.contains('hilo') || diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/area_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/area_painter.dart index ffd01b078..3160f1b9e 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/area_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/area_painter.dart @@ -23,13 +23,14 @@ class _AreaChartPainter extends CustomPainter { final int seriesIndex = painterKey.index; Rect clipRect; final AreaSeries series = - seriesRenderer._series as AreaSeries; + seriesRenderer._series as AreaSeries; seriesRenderer._storeSeriesProperties(chartState, seriesIndex); double animationFactor; CartesianChartPoint? prevPoint, point, _point; _ChartLocation? currentPoint, originPoint, _oldPoint; final ChartAxisRenderer xAxisRenderer = seriesRenderer._xAxisRenderer!; final ChartAxisRenderer yAxisRenderer = seriesRenderer._yAxisRenderer!; + final _RenderingDetails renderingDetails = chartState._renderingDetails; CartesianSeriesRenderer? oldSeriesRenderer; final Path _path = Path(); final Path _strokePath = Path(); @@ -39,16 +40,14 @@ class _AreaChartPainter extends CustomPainter { if (seriesRenderer._visible!) { assert( // ignore: unnecessary_null_comparison - series.animationDuration != null - ? series.animationDuration >= 0 - : true, + !(series.animationDuration != null) || series.animationDuration >= 0, 'The animation duration of the area series must be greater than or equal to 0.'); final List oldSeriesRenderers = chartState._oldSeriesRenderers; final List> dataPoints = seriesRenderer._dataPoints; - final bool widgetNeedUpdate = chartState._widgetNeedUpdate; - final bool isLegendToggled = chartState._isLegendToggled; + final bool widgetNeedUpdate = renderingDetails.widgetNeedUpdate; + final bool isLegendToggled = renderingDetails.isLegendToggled; final bool isTransposed = seriesRenderer._chartState!._requireInvertedAxis; canvas.save(); @@ -106,17 +105,14 @@ class _AreaChartPainter extends CustomPainter { double y = currentPoint.y; _points.add(Offset(x, y)); final bool closed = - series.emptyPointSettings.mode == EmptyPointMode.drop - ? _getSeriesVisibility(dataPoints, pointIndex) - : false; + series.emptyPointSettings.mode == EmptyPointMode.drop && + _getSeriesVisibility(dataPoints, pointIndex); if (_oldPoint != null) { - if (isTransposed) { - x = _getAnimateValue(animationFactor, x, _oldPoint.x, - currentPoint.x, seriesRenderer); - } else { - y = _getAnimateValue(animationFactor, y, _oldPoint.y, - currentPoint.y, seriesRenderer); - } + isTransposed + ? x = _getAnimateValue(animationFactor, x, _oldPoint.x, + currentPoint.x, seriesRenderer) + : y = _getAnimateValue(animationFactor, y, _oldPoint.y, + currentPoint.y, seriesRenderer); } if (prevPoint == null || dataPoints[pointIndex - 1].isGap == true || @@ -183,7 +179,7 @@ class _AreaChartPainter extends CustomPainter { xAxisRenderer._axis.plotOffset, yAxisRenderer._axis.plotOffset)); canvas.restore(); if ((series.animationDuration <= 0 || - (!chartState._initialRender! && + (!renderingDetails.initialRender! && !seriesRenderer._needAnimateSeriesElements) || animationFactor >= chartState._seriesDurationFactor) && (series.markerSettings.isVisible || diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/bar_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/bar_painter.dart index b72e85239..36febb6b0 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/bar_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/bar_painter.dart @@ -22,16 +22,15 @@ class _BarChartPainter extends CustomPainter { void paint(Canvas canvas, Size size) { final ChartAxisRenderer xAxisRenderer = seriesRenderer._xAxisRenderer!; final ChartAxisRenderer yAxisRenderer = seriesRenderer._yAxisRenderer!; + final _RenderingDetails renderingDetails = chartState._renderingDetails; final List> dataPoints = seriesRenderer._dataPoints; final BarSeries series = - seriesRenderer._series as BarSeries; + seriesRenderer._series as BarSeries; if (seriesRenderer._visible!) { assert( // ignore: unnecessary_null_comparison - series.animationDuration != null - ? series.animationDuration >= 0 - : true, + !(series.animationDuration != null) || series.animationDuration >= 0, 'The animation duration of the bar series must be greater than or equal to 0.'); Rect axisClipRect, clipRect; double animationFactor; @@ -77,7 +76,7 @@ class _BarChartPainter extends CustomPainter { xAxisRenderer._axis.plotOffset, yAxisRenderer._axis.plotOffset)); canvas.restore(); if ((series.animationDuration <= 0 || - (!chartState._initialRender! && + (!renderingDetails.initialRender! && !seriesRenderer._needAnimateSeriesElements) || animationFactor >= chartState._seriesDurationFactor) && (series.markerSettings.isVisible || diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/box_and_whisker_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/box_and_whisker_painter.dart index d24e2a7dd..ccddbd60d 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/box_and_whisker_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/box_and_whisker_painter.dart @@ -31,16 +31,14 @@ class _BoxAndWhiskerPainter extends CustomPainter { Rect clipRect; double animationFactor; final BoxAndWhiskerSeries series = - seriesRenderer._series as BoxAndWhiskerSeries; + seriesRenderer._series as BoxAndWhiskerSeries; CartesianChartPoint? point; if (seriesRenderer._visible!) { canvas.save(); assert( // ignore: unnecessary_null_comparison - series.animationDuration != null - ? series.animationDuration >= 0 - : true, - 'The animation duration of the box and whisker series must be greater or equal to 0.'); + !(series.animationDuration != null) || series.animationDuration >= 0, + 'The animation duration of the bar series must be greater than or equal to 0.'); final int seriesIndex = painterKey.index; seriesRenderer._storeSeriesProperties(chartState, seriesIndex); final Rect axisClipRect = _calculatePlotOffset( @@ -63,8 +61,13 @@ class _BoxAndWhiskerPainter extends CustomPainter { (point.y).remove(null); (point.y).sort(); seriesRenderer._findBoxPlotValues(point.y, point, series.boxPlotMode); - seriesRenderer._calculateRegionData(chartState, seriesRenderer, - painterKey.index, point, pointIndex, seriesRenderer.sideBySideInfo); + seriesRenderer._calculateRegionData( + chartState, + seriesRenderer, + painterKey.index, + point, + pointIndex, + seriesRenderer._sideBySideInfo); if (point.isVisible && !point.isGap) { seriesRenderer._drawSegment( canvas, diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/bubble_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/bubble_painter.dart index 61cf0b424..2b2fc1be1 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/bubble_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/bubble_painter.dart @@ -22,19 +22,18 @@ class _BubbleChartPainter extends CustomPainter { void paint(Canvas canvas, Size size) { final ChartAxisRenderer xAxisRenderer = seriesRenderer._xAxisRenderer!; final ChartAxisRenderer yAxisRenderer = seriesRenderer._yAxisRenderer!; + final _RenderingDetails renderingDetails = chartState._renderingDetails; final List> dataPoints = seriesRenderer._dataPoints; double animationFactor; final BubbleSeries series = - seriesRenderer._series as BubbleSeries; + seriesRenderer._series as BubbleSeries; if (seriesRenderer._visible!) { canvas.save(); assert( // ignore: unnecessary_null_comparison - series.animationDuration != null - ? series.animationDuration >= 0 - : true, - 'The animation duration of the bubble series must be greater than or equal to 0.'); + !(series.animationDuration != null) || series.animationDuration >= 0, + 'The animation duration of the bar series must be greater than or equal to 0.'); final int seriesIndex = painterKey.index; seriesRenderer._storeSeriesProperties(chartState, seriesIndex); animationFactor = seriesRenderer._seriesAnimation != null @@ -64,7 +63,7 @@ class _BubbleChartPainter extends CustomPainter { } canvas.restore(); if ((series.animationDuration <= 0 || - (!chartState._initialRender! && + (!renderingDetails.initialRender! && !seriesRenderer._needAnimateSeriesElements) || animationFactor >= chartState._seriesDurationFactor) && (series.markerSettings.isVisible || diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/candle_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/candle_painter.dart index 96a6f8099..c26b83462 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/candle_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/candle_painter.dart @@ -28,16 +28,14 @@ class _CandlePainter extends CustomPainter { Rect clipRect; double animationFactor; final CandleSeries series = - seriesRenderer._series as CandleSeries; + seriesRenderer._series as CandleSeries; CartesianChartPoint point; if (seriesRenderer._visible!) { canvas.save(); assert( // ignore: unnecessary_null_comparison - series.animationDuration != null - ? series.animationDuration >= 0 - : true, - 'The animation duration of the candle series must be greater or equal to 0.'); + !(series.animationDuration != null) || series.animationDuration >= 0, + 'The animation duration of the bar series must be greater than or equal to 0.'); final int seriesIndex = painterKey.index; seriesRenderer._storeSeriesProperties(chartState, seriesIndex); final Rect axisClipRect = _calculatePlotOffset( @@ -64,7 +62,7 @@ class _CandlePainter extends CustomPainter { painterKey.index, point, pointIndex, - seriesRenderer.sideBySideInfo); + seriesRenderer._sideBySideInfo); if (point.isVisible && !point.isGap) { seriesRenderer._drawSegment( diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/column_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/column_painter.dart index cc1134cf8..06a23c227 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/column_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/column_painter.dart @@ -23,10 +23,11 @@ class _ColumnChartPainter extends CustomPainter { void paint(Canvas canvas, Size size) { final ChartAxisRenderer xAxisRenderer = seriesRenderer._xAxisRenderer!; final ChartAxisRenderer yAxisRenderer = seriesRenderer._yAxisRenderer!; + final _RenderingDetails renderingDetails = chartState._renderingDetails; final List> dataPoints = seriesRenderer._dataPoints; final ColumnSeries series = - seriesRenderer._series as ColumnSeries; + seriesRenderer._series as ColumnSeries; if (seriesRenderer._visible!) { Rect axisClipRect, clipRect; double animationFactor; @@ -34,10 +35,8 @@ class _ColumnChartPainter extends CustomPainter { canvas.save(); assert( // ignore: unnecessary_null_comparison - series.animationDuration != null - ? series.animationDuration >= 0 - : true, - 'The animation duration of the column series must be greater or equal to 0.'); + !(series.animationDuration != null) || series.animationDuration >= 0, + 'The animation duration of the bar series must be greater than or equal to 0.'); final int seriesIndex = painterKey.index; seriesRenderer._storeSeriesProperties(chartState, seriesIndex); axisClipRect = _calculatePlotOffset( @@ -78,7 +77,7 @@ class _ColumnChartPainter extends CustomPainter { xAxisRenderer._axis.plotOffset, yAxisRenderer._axis.plotOffset)); canvas.restore(); if ((series.animationDuration <= 0 || - (!chartState._initialRender! && + (!renderingDetails.initialRender! && !seriesRenderer._needAnimateSeriesElements) || animationFactor >= chartState._seriesDurationFactor) && (series.markerSettings.isVisible || diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/fastline_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/fastline_painter.dart index 60579a5f9..cb3a11c93 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/fastline_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/fastline_painter.dart @@ -23,7 +23,7 @@ class _FastLineChartPainter extends CustomPainter { Rect clipRect; double animationFactor; final FastLineSeries series = - seriesRenderer._series as FastLineSeries; + seriesRenderer._series as FastLineSeries; final ChartAxisRenderer xAxisRenderer = seriesRenderer._xAxisRenderer!; final ChartAxisRenderer yAxisRenderer = seriesRenderer._yAxisRenderer!; final List _points = []; @@ -31,9 +31,7 @@ class _FastLineChartPainter extends CustomPainter { canvas.save(); assert( // ignore: unnecessary_null_comparison - series.animationDuration != null - ? series.animationDuration >= 0 - : true, + !(series.animationDuration != null) || series.animationDuration >= 0, 'The animation duration of the fast line series must be greater or equal to 0.'); final int seriesIndex = painterKey.index; seriesRenderer._storeSeriesProperties(chartState, seriesIndex); @@ -47,7 +45,7 @@ class _FastLineChartPainter extends CustomPainter { canvas.clipRect(axisClipRect); if (seriesRenderer._reAnimate || (series.animationDuration > 0 && - !seriesRenderer._chartState!._isLegendToggled)) { + !seriesRenderer._renderingDetails!.isLegendToggled)) { seriesRenderer._needAnimateSeriesElements = seriesRenderer._needsAnimation; _performLinearAnimation( @@ -68,13 +66,13 @@ class _FastLineChartPainter extends CustomPainter { num prevXValue = (seriesPoints.isNotEmpty && // ignore: unnecessary_null_comparison seriesPoints[0] != null && - seriesPoints[0].xValue > xTolerance) + (seriesPoints[0].xValue > xTolerance) == true) ? 0 : xTolerance; num prevYValue = (seriesPoints.isNotEmpty && // ignore: unnecessary_null_comparison seriesPoints[0] != null && - seriesPoints[0].yValue > yTolerance) + (seriesPoints[0].yValue > yTolerance) == true) ? 0 : yTolerance; num xVal = 0; diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/hilo_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/hilo_painter.dart index 4d2917f08..7f08487cf 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/hilo_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/hilo_painter.dart @@ -28,16 +28,14 @@ class _HiloPainter extends CustomPainter { Rect clipRect; double animationFactor; final HiloSeries series = - seriesRenderer._series as HiloSeries; + seriesRenderer._series as HiloSeries; CartesianChartPoint point; if (seriesRenderer._visible!) { canvas.save(); assert( // ignore: unnecessary_null_comparison - series.animationDuration != null - ? series.animationDuration >= 0 - : true, - 'The animation duration of the Hilo series must be greater or equal to 0.'); + !(series.animationDuration != null) || series.animationDuration >= 0, + 'The animation duration of the fast line series must be greater or equal to 0.'); final int seriesIndex = painterKey.index; seriesRenderer._storeSeriesProperties(chartState, seriesIndex); final Rect axisClipRect = _calculatePlotOffset( @@ -56,12 +54,14 @@ class _HiloPainter extends CustomPainter { } for (int pointIndex = 0; pointIndex < dataPoints.length; pointIndex++) { point = dataPoints[pointIndex]; - seriesRenderer._calculateRegionData(chartState, seriesRenderer, - painterKey.index, point, pointIndex, seriesRenderer.sideBySideInfo); - if (point.isVisible && - !point.isGap && - (!((point.low == point.high) && - !series.showIndicationForSameValues))) { + seriesRenderer._calculateRegionData( + chartState, + seriesRenderer, + painterKey.index, + point, + pointIndex, + seriesRenderer._sideBySideInfo); + if (point.isVisible && !point.isGap) { seriesRenderer._drawSegment( canvas, seriesRenderer._createSegments( diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/hiloopenclose_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/hiloopenclose_painter.dart index 16a349d19..5ea9925a2 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/hiloopenclose_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/hiloopenclose_painter.dart @@ -29,15 +29,13 @@ class _HiloOpenClosePainter extends CustomPainter { final List> dataPoints = seriesRenderer._dataPoints; final HiloOpenCloseSeries series = - seriesRenderer._series as HiloOpenCloseSeries; + seriesRenderer._series as HiloOpenCloseSeries; if (seriesRenderer._visible!) { canvas.save(); assert( // ignore: unnecessary_null_comparison - series.animationDuration != null - ? series.animationDuration >= 0 - : true, - 'The animation duration of the Hilo open-close series must be greater or equal to 0.'); + !(series.animationDuration != null) || series.animationDuration >= 0, + 'The animation duration of the fast line series must be greater or equal to 0.'); final int seriesIndex = painterKey.index; seriesRenderer._storeSeriesProperties(chartState, seriesIndex); final Rect axisClipRect = _calculatePlotOffset( @@ -56,13 +54,15 @@ class _HiloOpenClosePainter extends CustomPainter { } for (int pointIndex = 0; pointIndex < dataPoints.length; pointIndex++) { point = dataPoints[pointIndex]; - seriesRenderer._calculateRegionData(chartState, seriesRenderer, - painterKey.index, point, pointIndex, seriesRenderer.sideBySideInfo); + seriesRenderer._calculateRegionData( + chartState, + seriesRenderer, + painterKey.index, + point, + pointIndex, + seriesRenderer._sideBySideInfo); - if (point.isVisible && - !point.isGap && - (!((point.low == point.high) && - !series.showIndicationForSameValues))) { + if (point.isVisible && !point.isGap) { seriesRenderer._drawSegment( canvas, seriesRenderer._createSegments( diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/histogram_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/histogram_painter.dart index 2762eb680..362c46572 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/histogram_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/histogram_painter.dart @@ -28,20 +28,19 @@ class _HistogramChartPainter extends CustomPainter { CartesianChartPoint point; final ChartAxisRenderer xAxisRenderer = seriesRenderer._xAxisRenderer!; final ChartAxisRenderer yAxisRenderer = seriesRenderer._yAxisRenderer!; + final _RenderingDetails renderingDetails = chartState._renderingDetails; final List> dataPoints = seriesRenderer._dataPoints; /// Clip rect added if (seriesRenderer._visible!) { final HistogramSeries series = - seriesRenderer._series as HistogramSeries; + seriesRenderer._series as HistogramSeries; canvas.save(); assert( // ignore: unnecessary_null_comparison - series.animationDuration != null - ? series.animationDuration >= 0 - : true, - 'The animation duration of the histogram series must be greater or equal to 0.'); + !(series.animationDuration != null) || series.animationDuration >= 0, + 'The animation duration of the fast line series must be greater or equal to 0.'); final int seriesIndex = painterKey.index; seriesRenderer._storeSeriesProperties(chartState, seriesIndex); axisClipRect = _calculatePlotOffset( @@ -62,22 +61,28 @@ class _HistogramChartPainter extends CustomPainter { } for (int pointIndex = 0; pointIndex < dataPoints.length; pointIndex++) { point = dataPoints[pointIndex]; - seriesRenderer._calculateRegionData(chartState, seriesRenderer, - painterKey.index, point, pointIndex, seriesRenderer.sideBySideInfo); + seriesRenderer._calculateRegionData( + chartState, + seriesRenderer, + painterKey.index, + point, + pointIndex, + seriesRenderer._sideBySideInfo); if (point.isVisible && !point.isGap) { seriesRenderer._drawSegment( canvas, seriesRenderer._createSegments( point, segmentIndex += 1, - seriesRenderer.sideBySideInfo!, + seriesRenderer._sideBySideInfo!, painterKey.index, animationFactor)); } } if (series.showNormalDistributionCurve) { if (seriesRenderer._reAnimate || - ((!(chartState._widgetNeedUpdate || chartState._isLegendToggled) || + ((!(renderingDetails.widgetNeedUpdate || + renderingDetails.isLegendToggled) || !chartState._oldSeriesKeys.contains(series.key)) && series.animationDuration > 0)) { _performLinearAnimation( @@ -107,7 +112,7 @@ class _HistogramChartPainter extends CustomPainter { xAxisRenderer._axis.plotOffset, yAxisRenderer._axis.plotOffset)); canvas.restore(); if ((series.animationDuration <= 0 || - !chartState._initialRender! || + !renderingDetails.initialRender! || animationFactor >= chartState._seriesDurationFactor) && (series.markerSettings.isVisible || series.dataLabelSettings.isVisible)) { diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/line_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/line_painter.dart index e3ae503da..cdbe46618 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/line_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/line_painter.dart @@ -24,17 +24,16 @@ class _LineChartPainter extends CustomPainter { Rect clipRect; final ChartAxisRenderer xAxisRenderer = seriesRenderer._xAxisRenderer!; final ChartAxisRenderer yAxisRenderer = seriesRenderer._yAxisRenderer!; + final _RenderingDetails renderingDetails = chartState._renderingDetails; final List> dataPoints = seriesRenderer._dataPoints; final LineSeries series = - seriesRenderer._series as LineSeries; + seriesRenderer._series as LineSeries; if (seriesRenderer._visible!) { assert( // ignore: unnecessary_null_comparison - series.animationDuration != null - ? series.animationDuration >= 0 - : true, - 'The animation duration of the line series must be greater or equal to 0.'); + !(series.animationDuration != null) || series.animationDuration >= 0, + 'The animation duration of the fast line series must be greater or equal to 0.'); canvas.save(); final int seriesIndex = painterKey.index; seriesRenderer._storeSeriesProperties(chartState, seriesIndex); @@ -47,7 +46,8 @@ class _LineChartPainter extends CustomPainter { xAxisRenderer._axis.plotOffset, yAxisRenderer._axis.plotOffset)); canvas.clipRect(axisClipRect); if (seriesRenderer._reAnimate || - ((!(chartState._widgetNeedUpdate || chartState._isLegendToggled) || + ((!(renderingDetails.widgetNeedUpdate || + renderingDetails.isLegendToggled) || !chartState._oldSeriesKeys.contains(series.key)) && series.animationDuration > 0)) { _performLinearAnimation( @@ -103,7 +103,7 @@ class _LineChartPainter extends CustomPainter { canvas.restore(); if ((series.animationDuration <= 0 || - (!chartState._initialRender! && + (!renderingDetails.initialRender! && !seriesRenderer._needAnimateSeriesElements) || animationFactor >= chartState._seriesDurationFactor) && (series.markerSettings.isVisible || diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/range_area_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/range_area_painter.dart index 2a4c82a2d..8b84d71df 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/range_area_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/range_area_painter.dart @@ -21,10 +21,11 @@ class _RangeAreaChartPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { final RangeAreaSeries series = - seriesRenderer._series as RangeAreaSeries; + seriesRenderer._series as RangeAreaSeries; Rect clipRect; final ChartAxisRenderer xAxisRenderer = seriesRenderer._xAxisRenderer!; final ChartAxisRenderer yAxisRenderer = seriesRenderer._yAxisRenderer!; + final _RenderingDetails renderingDetails = chartState._renderingDetails; CartesianSeriesRenderer? oldSeriesRenderer; final List> dataPoints = seriesRenderer._dataPoints; @@ -42,10 +43,8 @@ class _RangeAreaChartPainter extends CustomPainter { if (seriesRenderer._visible!) { assert( // ignore: unnecessary_null_comparison - series.animationDuration != null - ? series.animationDuration >= 0 - : true, - 'The animation duration of the range area series must be greater or equal to 0.'); + !(series.animationDuration != null) || series.animationDuration >= 0, + 'The animation duration of the fast line series must be greater or equal to 0.'); final List oldSeriesRenderers = chartState._oldSeriesRenderers; canvas.save(); @@ -65,7 +64,8 @@ class _RangeAreaChartPainter extends CustomPainter { ? seriesRenderer._seriesAnimation!.value : 1; if (seriesRenderer._reAnimate || - ((!(chartState._widgetNeedUpdate || chartState._isLegendToggled) || + ((!(renderingDetails.widgetNeedUpdate || + renderingDetails.isLegendToggled) || !chartState._oldSeriesKeys.contains(series.key)) && series.animationDuration > 0)) { _performLinearAnimation( @@ -266,7 +266,7 @@ class _RangeAreaChartPainter extends CustomPainter { xAxisRenderer._axis.plotOffset, yAxisRenderer._axis.plotOffset)); canvas.restore(); if ((series.animationDuration <= 0 || - !chartState._initialRender! || + !renderingDetails.initialRender! || animationFactor >= chartState._seriesDurationFactor) && (series.markerSettings.isVisible || series.dataLabelSettings.isVisible)) { diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/range_column_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/range_column_painter.dart index 906df59a7..b0fce7ee6 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/range_column_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/range_column_painter.dart @@ -23,17 +23,16 @@ class _RangeColumnChartPainter extends CustomPainter { void paint(Canvas canvas, Size size) { final ChartAxisRenderer xAxisRenderer = seriesRenderer._xAxisRenderer!; final ChartAxisRenderer yAxisRenderer = seriesRenderer._yAxisRenderer!; + final _RenderingDetails renderingDetails = chartState._renderingDetails; final List> dataPoints = seriesRenderer._dataPoints; final RangeColumnSeries series = - seriesRenderer._series as RangeColumnSeries; + seriesRenderer._series as RangeColumnSeries; if (seriesRenderer._visible!) { assert( // ignore: unnecessary_null_comparison - series.animationDuration != null - ? series.animationDuration >= 0 - : true, - 'The animation duration of the range column series must be greater or equal to 0.'); + !(series.animationDuration != null) || series.animationDuration >= 0, + 'The animation duration of the fast line series must be greater or equal to 0.'); Rect axisClipRect, clipRect; double animationFactor; CartesianChartPoint point; @@ -79,7 +78,7 @@ class _RangeColumnChartPainter extends CustomPainter { xAxisRenderer._axis.plotOffset, yAxisRenderer._axis.plotOffset)); canvas.restore(); if ((series.animationDuration <= 0 || - (!chartState._initialRender! && + (!renderingDetails.initialRender! && !seriesRenderer._needAnimateSeriesElements) || animationFactor >= chartState._seriesDurationFactor) && (series.markerSettings.isVisible || diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/scatter_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/scatter_painter.dart index 7438795fb..de7ec322c 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/scatter_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/scatter_painter.dart @@ -23,14 +23,12 @@ class _ScatterChartPainter extends CustomPainter { canvas.save(); double animationFactor; final ScatterSeries series = - seriesRenderer._series as ScatterSeries; + seriesRenderer._series as ScatterSeries; if (seriesRenderer._visible!) { assert( // ignore: unnecessary_null_comparison - series.animationDuration != null - ? series.animationDuration >= 0 - : true, - 'The animation duration of the scatter series must be greater or equal to 0.'); + !(series.animationDuration != null) || series.animationDuration >= 0, + 'The animation duration of the fast line series must be greater or equal to 0.'); final ChartAxisRenderer xAxisRenderer = seriesRenderer._xAxisRenderer!; final ChartAxisRenderer yAxisRenderer = seriesRenderer._yAxisRenderer!; final List> dataPoints = diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/spline_area_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/spline_area_painter.dart index 7d84276da..f451874fe 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/spline_area_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/spline_area_painter.dart @@ -26,10 +26,13 @@ class _SplineAreaChartPainter extends CustomPainter { _ChartLocation? currentPoint, originPoint, oldPointLocation; final ChartAxisRenderer xAxisRenderer = seriesRenderer._xAxisRenderer!; final ChartAxisRenderer yAxisRenderer = seriesRenderer._yAxisRenderer!; + final _RenderingDetails renderingDetails = chartState._renderingDetails; Rect clipRect; final SplineAreaSeries series = - seriesRenderer._series as SplineAreaSeries; - seriesRenderer._drawControlPoints.clear(); + seriesRenderer._series as SplineAreaSeries; + if (!seriesRenderer._hasDataLabelTemplate) { + seriesRenderer._drawControlPoints.clear(); + } final Path _path = Path(); final Path _strokePath = Path(); final List _points = []; @@ -39,10 +42,8 @@ class _SplineAreaChartPainter extends CustomPainter { if (seriesRenderer._visible!) { assert( // ignore: unnecessary_null_comparison - series.animationDuration != null - ? series.animationDuration >= 0 - : true, - 'The animation duration of the spline area series must be greater or equal to 0.'); + !(series.animationDuration != null) || series.animationDuration >= 0, + 'The animation duration of the fast line series must be greater or equal to 0.'); final List oldSeriesRenderers = chartState._oldSeriesRenderers; final List> dataPoints = @@ -65,7 +66,8 @@ class _SplineAreaChartPainter extends CustomPainter { chartState, seriesRenderer, seriesIndex, oldSeriesRenderers); if (seriesRenderer._reAnimate || - ((!(chartState._widgetNeedUpdate || chartState._isLegendToggled) || + ((!(renderingDetails.widgetNeedUpdate || + renderingDetails.isLegendToggled) || !chartState._oldSeriesKeys.contains(series.key)) && series.animationDuration > 0)) { _performLinearAnimation( @@ -118,19 +120,16 @@ class _SplineAreaChartPainter extends CustomPainter { startControlX = startControlY = endControlX = endControlY = null; _points.add(Offset(currentPoint.x, currentPoint.y)); final bool closed = - series.emptyPointSettings.mode == EmptyPointMode.drop - ? _getSeriesVisibility(dataPoints, pointIndex) - : false; + series.emptyPointSettings.mode == EmptyPointMode.drop && + _getSeriesVisibility(dataPoints, pointIndex); //calculates animation values for control points and data points if (oldPointLocation != null) { - if (isTransposed) { - x = _getAnimateValue(animationFactor, x, oldPointLocation.x, - currentPoint.x, seriesRenderer); - } else { - y = _getAnimateValue(animationFactor, y, oldPointLocation.y, - currentPoint.y, seriesRenderer); - } + isTransposed + ? x = _getAnimateValue(animationFactor, x, oldPointLocation.x, + currentPoint.x, seriesRenderer) + : y = _getAnimateValue(animationFactor, y, oldPointLocation.y, + currentPoint.y, seriesRenderer); if (point.startControl != null) { startControlY = _getAnimateValue( animationFactor, @@ -242,7 +241,7 @@ class _SplineAreaChartPainter extends CustomPainter { xAxisRenderer._axis.plotOffset, yAxisRenderer._axis.plotOffset)); canvas.restore(); if ((series.animationDuration <= 0 || - !chartState._initialRender! || + !renderingDetails.initialRender! || animationFactor >= chartState._seriesDurationFactor) && (series.markerSettings.isVisible || series.dataLabelSettings.isVisible)) { diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/spline_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/spline_painter.dart index 25e776ab6..4440af8be 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/spline_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/spline_painter.dart @@ -23,21 +23,22 @@ class _SplineChartPainter extends CustomPainter { Rect clipRect; double animationFactor; final SplineSeries series = - seriesRenderer._series as SplineSeries; + seriesRenderer._series as SplineSeries; final ChartAxisRenderer xAxisRenderer = seriesRenderer._xAxisRenderer!; final ChartAxisRenderer yAxisRenderer = seriesRenderer._yAxisRenderer!; + final _RenderingDetails renderingDetails = chartState._renderingDetails; final List> dataPoints = seriesRenderer._dataPoints; - seriesRenderer._drawControlPoints.clear(); + if (!seriesRenderer._hasDataLabelTemplate) { + seriesRenderer._drawControlPoints.clear(); + } /// Clip rect will be added for series. if (seriesRenderer._visible!) { assert( // ignore: unnecessary_null_comparison - series.animationDuration != null - ? series.animationDuration >= 0 - : true, - 'The animation duration of the spline series must be greater or equal to 0.'); + !(series.animationDuration != null) || series.animationDuration >= 0, + 'The animation duration of the fast line series must be greater or equal to 0.'); canvas.save(); final int seriesIndex = painterKey.index; seriesRenderer._storeSeriesProperties(chartState, seriesIndex); @@ -50,7 +51,8 @@ class _SplineChartPainter extends CustomPainter { xAxisRenderer._axis.plotOffset, yAxisRenderer._axis.plotOffset)); canvas.clipRect(axisClipRect); if (seriesRenderer._reAnimate || - ((!(chartState._widgetNeedUpdate || chartState._isLegendToggled) || + ((!(renderingDetails.widgetNeedUpdate || + renderingDetails.isLegendToggled) || !chartState._oldSeriesKeys.contains(series.key)) && series.animationDuration > 0)) { _performLinearAnimation( @@ -74,11 +76,9 @@ class _SplineChartPainter extends CustomPainter { point = dataPoints[pointIndex]; if (_withInRange(seriesRenderer._dataPoints[pointIndex].xValue, seriesRenderer._xAxisRenderer!._visibleRange!) || - (pointIndex < dataPoints.length - 1 - ? _withInRange( - seriesRenderer._dataPoints[pointIndex + 1].xValue, - seriesRenderer._xAxisRenderer!._visibleRange!) - : false)) { + (pointIndex < dataPoints.length - 1 && + _withInRange(seriesRenderer._dataPoints[pointIndex + 1].xValue, + seriesRenderer._xAxisRenderer!._visibleRange!))) { seriesRenderer._calculateRegionData( chartState, seriesRenderer, painterKey.index, point, pointIndex); if ((point.isVisible && !point.isGap) && startPoint == null) { @@ -120,7 +120,7 @@ class _SplineChartPainter extends CustomPainter { canvas.restore(); if ((series.animationDuration <= 0 || - (!chartState._initialRender! && + (!renderingDetails.initialRender! && !seriesRenderer._needAnimateSeriesElements) || animationFactor >= chartState._seriesDurationFactor) && (series.markerSettings.isVisible || diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/spline_range_area_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/spline_range_area_painter.dart index 47029203b..bf851e60a 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/spline_range_area_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/spline_range_area_painter.dart @@ -36,6 +36,7 @@ class _SplineRangeAreaChartPainter extends CustomPainter { final List _points = []; final ChartAxisRenderer xAxisRenderer = seriesRenderer._xAxisRenderer!; final ChartAxisRenderer yAxisRenderer = seriesRenderer._yAxisRenderer!; + final _RenderingDetails renderingDetails = chartState._renderingDetails; final List oldSeriesRenderers = chartState._oldSeriesRenderers; double? currentPointLowX, @@ -60,13 +61,11 @@ class _SplineRangeAreaChartPainter extends CustomPainter { seriesRenderer._storeSeriesProperties(chartState, seriesIndex); final bool isTransposed = chartState._requireInvertedAxis; final SplineRangeAreaSeries series = - seriesRenderer._series as SplineRangeAreaSeries; + seriesRenderer._series as SplineRangeAreaSeries; assert( // ignore: unnecessary_null_comparison - series.animationDuration != null - ? series.animationDuration >= 0 - : true, - 'The animation duration of the spline range area series must be greater or equal to 0.'); + !(series.animationDuration != null) || series.animationDuration >= 0, + 'The animation duration of the fast line series must be greater or equal to 0.'); SplineRangeAreaSegment splineRangeAreaSegment; final Rect axisClipRect = _calculatePlotOffset( chartState._chartAxis._axisClipRect, @@ -77,7 +76,8 @@ class _SplineRangeAreaChartPainter extends CustomPainter { ? seriesRenderer._seriesAnimation!.value : 1; if (seriesRenderer._reAnimate || - ((!(chartState._widgetNeedUpdate || chartState._isLegendToggled) || + ((!(renderingDetails.widgetNeedUpdate || + renderingDetails.isLegendToggled) || !chartState._oldSeriesKeys.contains(series.key)) && series.animationDuration > 0)) { _performLinearAnimation( @@ -104,18 +104,17 @@ class _SplineRangeAreaChartPainter extends CustomPainter { pointIndex, oldSeriesRenderer, oldSeriesRenderers); - if (oldChartPoint != null) { - oldPointHigh = _calculatePoint( - oldChartPoint.xValue, - oldChartPoint.high, - oldSeriesRenderer!._xAxisRenderer!, - oldSeriesRenderer._yAxisRenderer!, - isTransposed, - series, - axisClipRect); - } else { - oldPointHigh = null; - } + + oldPointHigh = (oldChartPoint != null) + ? _calculatePoint( + oldChartPoint.xValue, + oldChartPoint.high, + oldSeriesRenderer!._xAxisRenderer!, + oldSeriesRenderer._yAxisRenderer!, + isTransposed, + series, + axisClipRect) + : null; currentPointLow = _calculatePoint(point.xValue, point.low, xAxisRenderer, yAxisRenderer, isTransposed, series, axisClipRect); currentPointHigh = _calculatePoint(point.xValue, point.high, @@ -314,13 +313,17 @@ class _SplineRangeAreaChartPainter extends CustomPainter { _strokePath.moveTo(currentPointLowX, currentPointLowY); _path.moveTo(currentPointLowX, currentPointLowY); } else if (dataPoints[pointIndex].isGap != true) { - if (pointIndex + 1 == dataPoints.length - 1 && - dataPoints[pointIndex + 1].isDrop) { - _strokePath.moveTo(currentPointLowX, currentPointLowY); - } else { - _strokePath.cubicTo(endControlX!, endControlY!, startControlX!, - startControlY!, currentPointLowX, currentPointLowY); - } + (pointIndex + 1 == dataPoints.length - 1 && + dataPoints[pointIndex + 1].isDrop) + ? _strokePath.moveTo(currentPointLowX, currentPointLowY) + : _strokePath.cubicTo( + endControlX!, + endControlY!, + startControlX!, + startControlY!, + currentPointLowX, + currentPointLowY); + _path.cubicTo(endControlX!, endControlY!, startControlX!, startControlY!, currentPointLowX, currentPointLowY); } @@ -356,7 +359,7 @@ class _SplineRangeAreaChartPainter extends CustomPainter { xAxisRenderer._axis.plotOffset, yAxisRenderer._axis.plotOffset)); canvas.restore(); if ((series.animationDuration <= 0 || - (!chartState._initialRender! && + (!renderingDetails.initialRender! && !seriesRenderer._needAnimateSeriesElements) || animationFactor >= chartState._seriesDurationFactor) && (series.markerSettings.isVisible || diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stacked_area_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stacked_area_painter.dart index b6ae4e754..9400b2d7e 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stacked_area_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stacked_area_painter.dart @@ -24,7 +24,7 @@ class _StackedAreaChartPainter extends CustomPainter { canvas, seriesRenderer, chartState, - seriesRenderer._seriesAnimation!, + seriesRenderer._seriesAnimation, seriesRenderer._seriesElementAnimation, painterKey); } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stacked_bar_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stacked_bar_painter.dart index 80c6b1d96..4845f34a5 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stacked_bar_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stacked_bar_painter.dart @@ -8,7 +8,7 @@ class _StackedBarChartPainter extends CustomPainter { required this.animationController, required this.painterKey, required ValueNotifier notifier, - }) : chart = chartState._chart, + }) : chart = chartState._chart, super(repaint: notifier); final SfCartesianChartState chartState; diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stacked_line_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stacked_line_painter.dart index 1be7f8d78..d8b404d58 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stacked_line_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stacked_line_painter.dart @@ -20,13 +20,8 @@ class _StackedLineChartPainter extends CustomPainter { /// Painter method for stacked line series @override void paint(Canvas canvas, Size size) { - _stackedLinePainter( - canvas, - seriesRenderer, - seriesRenderer._seriesAnimation!, - chartState, - seriesRenderer._seriesElementAnimation, - painterKey); + _stackedLinePainter(canvas, seriesRenderer, seriesRenderer._seriesAnimation, + chartState, seriesRenderer._seriesElementAnimation, painterKey); } @override diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stackedarea100_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stackedarea100_painter.dart index a8a21e725..8a261ad85 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stackedarea100_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stackedarea100_painter.dart @@ -24,7 +24,7 @@ class _StackedArea100ChartPainter extends CustomPainter { canvas, seriesRenderer, chartState, - seriesRenderer._seriesAnimation!, + seriesRenderer._seriesAnimation, seriesRenderer._seriesElementAnimation, painterKey); } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stackedbar100_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stackedbar100_painter.dart index 808185d22..d321e94d3 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stackedbar100_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stackedbar100_painter.dart @@ -8,7 +8,7 @@ class _StackedBar100ChartPainter extends CustomPainter { required this.animationController, required this.painterKey, required ValueNotifier notifier, - }) : chart = chartState._chart, + }) : chart = chartState._chart, super(repaint: notifier); final SfCartesianChartState chartState; diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stackedcolumn100_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stackedcolumn100_painter.dart index 33394e810..b1b36d0b7 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stackedcolumn100_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stackedcolumn100_painter.dart @@ -8,7 +8,7 @@ class _StackedColumn100ChartPainter extends CustomPainter { required this.animationController, required this.painterKey, required ValueNotifier notifier, - }) : chart = chartState._chart, + }) : chart = chartState._chart, super(repaint: notifier); final SfCartesianChartState chartState; final SfCartesianChart chart; diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/step_area_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/step_area_painter.dart index bc1e0745e..314d5bf36 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/step_area_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/step_area_painter.dart @@ -20,7 +20,6 @@ class _StepAreaChartPainter extends CustomPainter { /// Painter method for step area series @override void paint(Canvas canvas, Size size) { - Rect clipRect; double animationFactor; CartesianChartPoint? prevPoint, point, oldChartPoint; _ChartLocation? currentPoint, @@ -33,10 +32,10 @@ class _StepAreaChartPainter extends CustomPainter { chartState._oldSeriesRenderers; final ChartAxisRenderer xAxisRenderer = seriesRenderer._xAxisRenderer!; final ChartAxisRenderer yAxisRenderer = seriesRenderer._yAxisRenderer!; + final _RenderingDetails renderingDetails = chartState._renderingDetails; final StepAreaSeries _series = - seriesRenderer._series as StepAreaSeries; - final Path _path = Path(); - final Path _strokePath = Path(); + seriesRenderer._series as StepAreaSeries; + final Path _path = Path(), _strokePath = Path(); final List _points = []; final num? crossesAt = _getCrossesAtValue(seriesRenderer, chartState); final num origin = crossesAt ?? 0; @@ -45,16 +44,15 @@ class _StepAreaChartPainter extends CustomPainter { if (seriesRenderer._visible!) { assert( // ignore: unnecessary_null_comparison - _series.animationDuration != null - ? _series.animationDuration >= 0 - : true, + !(_series.animationDuration != null) || + _series.animationDuration >= 0, 'The animation duration of the step area series must be greater or equal to 0.'); canvas.save(); final List> dataPoints = seriesRenderer._dataPoints; final int seriesIndex = painterKey.index; final StepAreaSeries series = - seriesRenderer._series as StepAreaSeries; + seriesRenderer._series as StepAreaSeries; final Rect axisClipRect = _calculatePlotOffset( chartState._chartAxis._axisClipRect, Offset(seriesRenderer._xAxisRenderer!._axis.plotOffset, @@ -69,7 +67,8 @@ class _StepAreaChartPainter extends CustomPainter { ? seriesRenderer._seriesAnimation!.value : 1; if (seriesRenderer._reAnimate || - ((!(chartState._widgetNeedUpdate || chartState._isLegendToggled) || + ((!(renderingDetails.widgetNeedUpdate || + renderingDetails.isLegendToggled) || !chartState._oldSeriesKeys .contains(seriesRenderer._series.key)) && seriesRenderer._series.animationDuration > 0)) { @@ -154,35 +153,41 @@ class _StepAreaChartPainter extends CustomPainter { seriesRenderer._createSegments(_path, _strokePath, painterKey.index, animationFactor, _points)); } + _drawSeries(canvas, animationFactor); + } + } - clipRect = _calculatePlotOffset( - Rect.fromLTRB( - chartState._chartAxis._axisClipRect.left - - seriesRenderer._series.markerSettings.width, - chartState._chartAxis._axisClipRect.top - - seriesRenderer._series.markerSettings.height, - chartState._chartAxis._axisClipRect.right + - seriesRenderer._series.markerSettings.width, - chartState._chartAxis._axisClipRect.bottom + - seriesRenderer._series.markerSettings.height), - Offset(seriesRenderer._xAxisRenderer!._axis.plotOffset, - seriesRenderer._yAxisRenderer!._axis.plotOffset)); - canvas.restore(); - if ((series.animationDuration <= 0 || - !chartState._initialRender! || - animationFactor >= chartState._seriesDurationFactor) && - (series.markerSettings.isVisible || - series.dataLabelSettings.isVisible)) { - // ignore: unnecessary_null_comparison - assert(seriesRenderer != null, - 'The step area series should be available to render a marker on it.'); - canvas.clipRect(clipRect); - seriesRenderer._renderSeriesElements( - chart, canvas, seriesRenderer._seriesElementAnimation); - } - if (animationFactor >= 1) { - chartState._setPainterKey(painterKey.index, painterKey.name, true); - } + ///Draw series elements and add cliprect + void _drawSeries(Canvas canvas, double animationFactor) { + final StepAreaSeries series = + seriesRenderer._series as StepAreaSeries; + final Rect clipRect = _calculatePlotOffset( + Rect.fromLTRB( + chartState._chartAxis._axisClipRect.left - + series.markerSettings.width, + chartState._chartAxis._axisClipRect.top - + series.markerSettings.height, + chartState._chartAxis._axisClipRect.right + + series.markerSettings.width, + chartState._chartAxis._axisClipRect.bottom + + series.markerSettings.height), + Offset(seriesRenderer._xAxisRenderer!._axis.plotOffset, + seriesRenderer._yAxisRenderer!._axis.plotOffset)); + canvas.restore(); + if ((series.animationDuration <= 0 || + !chartState._renderingDetails.initialRender! || + animationFactor >= chartState._seriesDurationFactor) && + (series.markerSettings.isVisible || + series.dataLabelSettings.isVisible)) { + // ignore: unnecessary_null_comparison + assert(seriesRenderer != null, + 'The step area series should be available to render a marker on it.'); + canvas.clipRect(clipRect); + seriesRenderer._renderSeriesElements( + chart, canvas, seriesRenderer._seriesElementAnimation); + } + if (animationFactor >= 1) { + chartState._setPainterKey(painterKey.index, painterKey.name, true); } } @@ -206,9 +211,8 @@ class _StepAreaChartPainter extends CustomPainter { double y = currentPoint.y; double? previousPointY = previousPoint?.y; final bool closed = - stepAreaSeries.emptyPointSettings.mode == EmptyPointMode.drop - ? _getSeriesVisibility(seriesRenderer._dataPoints, pointIndex) - : false; + stepAreaSeries.emptyPointSettings.mode == EmptyPointMode.drop && + _getSeriesVisibility(seriesRenderer._dataPoints, pointIndex); if (oldPoint != null) { if (chartState._chart.isTransposed) { x = _getAnimateValue( diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stepline_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stepline_painter.dart index d89452b94..5ebba2847 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stepline_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/stepline_painter.dart @@ -21,21 +21,19 @@ class _StepLineChartPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { double animationFactor; - Rect clipRect; final StepLineSeries series = - seriesRenderer._series as StepLineSeries; + seriesRenderer._series as StepLineSeries; final ChartAxisRenderer xAxisRenderer = seriesRenderer._xAxisRenderer!; final ChartAxisRenderer yAxisRenderer = seriesRenderer._yAxisRenderer!; + final _RenderingDetails renderingDetails = chartState._renderingDetails; final List> dataPoints = seriesRenderer._dataPoints; if (seriesRenderer._visible!) { canvas.save(); assert( // ignore: unnecessary_null_comparison - series.animationDuration != null - ? series.animationDuration >= 0 - : true, - 'The animation duration of the step line series must be greater or equal to 0.'); + !(series.animationDuration != null) || series.animationDuration >= 0, + 'The animation duration of the fast line series must be greater or equal to 0.'); final int seriesIndex = painterKey.index; seriesRenderer._storeSeriesProperties(chartState, seriesIndex); animationFactor = seriesRenderer._seriesAnimation != null @@ -47,7 +45,8 @@ class _StepLineChartPainter extends CustomPainter { xAxisRenderer._axis.plotOffset, yAxisRenderer._axis.plotOffset)); canvas.clipRect(axisClipRect); if (seriesRenderer._reAnimate || - ((!(chartState._widgetNeedUpdate || chartState._isLegendToggled) || + ((!(renderingDetails.widgetNeedUpdate || + renderingDetails.isLegendToggled) || !chartState._oldSeriesKeys.contains(series.key)) && series.animationDuration > 0)) { _performLinearAnimation( @@ -81,7 +80,7 @@ class _StepLineChartPainter extends CustomPainter { midX = _nextPoint.xValue; midY = currentPoint.yValue; } else if (_nextPoint.isDrop) { - _nextPoint = getDropValue(dataPoints, pointIndex); + _nextPoint = _getDropValue(dataPoints, pointIndex); midX = _nextPoint?.xValue; midY = currentPoint.yValue; } @@ -107,41 +106,48 @@ class _StepLineChartPainter extends CustomPainter { endPoint = startPoint = midX = midY = null; } } - clipRect = _calculatePlotOffset( - Rect.fromLTRB( - chartState._chartAxis._axisClipRect.left - - series.markerSettings.width, - chartState._chartAxis._axisClipRect.top - - series.markerSettings.height, - chartState._chartAxis._axisClipRect.right + - series.markerSettings.width, - chartState._chartAxis._axisClipRect.bottom + - series.markerSettings.height), - Offset( - xAxisRenderer._axis.plotOffset, yAxisRenderer._axis.plotOffset)); + _drawSeries(canvas, animationFactor); + } + } - canvas.restore(); - if ((series.animationDuration <= 0 || - (!chartState._initialRender! && - !seriesRenderer._needAnimateSeriesElements) || - animationFactor >= chartState._seriesDurationFactor) && - (series.markerSettings.isVisible || - series.dataLabelSettings.isVisible)) { - // ignore: unnecessary_null_comparison - assert(seriesRenderer != null, - 'The step line series should be available to render a marker on it.'); - canvas.clipRect(clipRect); - seriesRenderer._renderSeriesElements( - chart, canvas, seriesRenderer._seriesElementAnimation); - } - if (seriesRenderer._visible! && animationFactor >= 1) { - chartState._setPainterKey(seriesIndex, painterKey.name, true); - } + ///Draw series elements and add cliprect + void _drawSeries(Canvas canvas, double animationFactor) { + final StepLineSeries series = + seriesRenderer._series as StepLineSeries; + final Rect clipRect = _calculatePlotOffset( + Rect.fromLTRB( + chartState._chartAxis._axisClipRect.left - + series.markerSettings.width, + chartState._chartAxis._axisClipRect.top - + series.markerSettings.height, + chartState._chartAxis._axisClipRect.right + + series.markerSettings.width, + chartState._chartAxis._axisClipRect.bottom + + series.markerSettings.height), + Offset(seriesRenderer._xAxisRenderer!._axis.plotOffset, + seriesRenderer._yAxisRenderer!._axis.plotOffset)); + + canvas.restore(); + if ((series.animationDuration <= 0 || + (!chartState._renderingDetails.initialRender! && + !seriesRenderer._needAnimateSeriesElements) || + animationFactor >= chartState._seriesDurationFactor) && + (series.markerSettings.isVisible || + series.dataLabelSettings.isVisible)) { + // ignore: unnecessary_null_comparison + assert(seriesRenderer != null, + 'The step line series should be available to render a marker on it.'); + canvas.clipRect(clipRect); + seriesRenderer._renderSeriesElements( + chart, canvas, seriesRenderer._seriesElementAnimation); + } + if (seriesRenderer._visible! && animationFactor >= 1) { + chartState._setPainterKey(painterKey.index, painterKey.name, true); } } /// To get point value in the drop mode - CartesianChartPoint? getDropValue( + CartesianChartPoint? _getDropValue( List> points, int pointIndex) { CartesianChartPoint? value; for (int i = pointIndex; i < points.length - 1; i++) { diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/waterfall_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/waterfall_painter.dart index 9e6605435..2040dfe7f 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/waterfall_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/series_painter/waterfall_painter.dart @@ -23,23 +23,20 @@ class _WaterfallChartPainter extends CustomPainter { void paint(Canvas canvas, Size size) { final ChartAxisRenderer xAxisRenderer = seriesRenderer._xAxisRenderer!; final ChartAxisRenderer yAxisRenderer = seriesRenderer._yAxisRenderer!; + final _RenderingDetails renderingDetails = chartState._renderingDetails; final List> dataPoints = seriesRenderer._dataPoints; final WaterfallSeries series = - seriesRenderer._series as WaterfallSeries; + seriesRenderer._series as WaterfallSeries; final num origin = math.max(yAxisRenderer._visibleRange!.minimum, 0); - num currentEndValue = 0; - num intermediateOrigin = 0; - num prevEndValue = 0; + num currentEndValue = 0, intermediateOrigin = 0, prevEndValue = 0; num originValue = 0; if (seriesRenderer._visible!) { assert( // ignore: unnecessary_null_comparison - series.animationDuration != null - ? series.animationDuration >= 0 - : true, - 'The animation duration of the waterfall series must be greater or equal to 0.'); - Rect axisClipRect, clipRect; + !(series.animationDuration != null) || series.animationDuration >= 0, + 'The animation duration of the fast line series must be greater or equal to 0.'); + Rect axisClipRect; double animationFactor; CartesianChartPoint? point; canvas.save(); @@ -64,10 +61,10 @@ class _WaterfallChartPainter extends CustomPainter { (point.isIntermediateSum! || point.isTotalSum!) ? 0 : point.yValue; point.yValue = point.y = point.isTotalSum! ? currentEndValue : point.yValue; - originValue = (point.isIntermediateSum == true + originValue = point.isIntermediateSum == true ? intermediateOrigin // ignore: unnecessary_null_comparison - : ((prevEndValue != null) ? prevEndValue : origin)); + : ((prevEndValue != null) ? prevEndValue : origin); originValue = point.isTotalSum! ? 0 : originValue; point.yValue = point.y = point.isIntermediateSum! ? currentEndValue - originValue @@ -76,8 +73,8 @@ class _WaterfallChartPainter extends CustomPainter { point.originValue = originValue; seriesRenderer._calculateRegionData( chartState, seriesRenderer, painterKey.index, point, pointIndex); - if (chartState._templates.isNotEmpty) { - chartState._templates[pointIndex].location = + if (renderingDetails.templates.isNotEmpty) { + renderingDetails.templates[pointIndex].location = Offset(point.markerPoint!.x, point.markerPoint!.y); } if (point.isVisible && !point.isGap) { @@ -91,35 +88,42 @@ class _WaterfallChartPainter extends CustomPainter { } prevEndValue = currentEndValue; } - clipRect = _calculatePlotOffset( - Rect.fromLTRB( - chartState._chartAxis._axisClipRect.left - - series.markerSettings.width, - chartState._chartAxis._axisClipRect.top - - series.markerSettings.height, - chartState._chartAxis._axisClipRect.right + - series.markerSettings.width, - chartState._chartAxis._axisClipRect.bottom + - series.markerSettings.height), - Offset( - xAxisRenderer._axis.plotOffset, yAxisRenderer._axis.plotOffset)); - canvas.restore(); - if ((series.animationDuration <= 0 || - (!chartState._initialRender! && - !seriesRenderer._needAnimateSeriesElements) || - animationFactor >= chartState._seriesDurationFactor) && - (series.markerSettings.isVisible || - series.dataLabelSettings.isVisible)) { - // ignore: unnecessary_null_comparison - assert(seriesRenderer != null, - 'The waterfall series should be available to render a marker on it.'); - canvas.clipRect(clipRect); - seriesRenderer._renderSeriesElements( - chart, canvas, seriesRenderer._seriesElementAnimation); - } - if (animationFactor >= 1) { - chartState._setPainterKey(painterKey.index, painterKey.name, true); - } + _drawSeries(canvas, animationFactor); + } + } + + ///Draw series elements and add cliprect + void _drawSeries(Canvas canvas, double animationFactor) { + final WaterfallSeries series = + seriesRenderer._series as WaterfallSeries; + final Rect clipRect = _calculatePlotOffset( + Rect.fromLTRB( + chartState._chartAxis._axisClipRect.left - + series.markerSettings.width, + chartState._chartAxis._axisClipRect.top - + series.markerSettings.height, + chartState._chartAxis._axisClipRect.right + + series.markerSettings.width, + chartState._chartAxis._axisClipRect.bottom + + series.markerSettings.height), + Offset(seriesRenderer._xAxisRenderer!._axis.plotOffset, + seriesRenderer._yAxisRenderer!._axis.plotOffset)); + canvas.restore(); + if ((series.animationDuration <= 0 || + (!chartState._renderingDetails.initialRender! && + !seriesRenderer._needAnimateSeriesElements) || + animationFactor >= chartState._seriesDurationFactor) && + (series.markerSettings.isVisible || + series.dataLabelSettings.isVisible)) { + // ignore: unnecessary_null_comparison + assert(seriesRenderer != null, + 'The waterfall series should be available to render a marker on it.'); + canvas.clipRect(clipRect); + seriesRenderer._renderSeriesElements( + chart, canvas, seriesRenderer._seriesElementAnimation); + } + if (animationFactor >= 1) { + chartState._setPainterKey(painterKey.index, painterKey.name, true); } } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/accumulation_distribution_indicator.dart b/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/accumulation_distribution_indicator.dart index 034e0337d..b787c2512 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/accumulation_distribution_indicator.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/accumulation_distribution_indicator.dart @@ -8,7 +8,7 @@ part of charts; /// It provides options for series visible, axis name, series name, animation duration, legend visibility, /// signal line width, and color. /// - +@immutable class AccumulationDistributionIndicator extends TechnicalIndicators { /// Creating an argument constructor of AccumulationDistributionIndicator class. @@ -30,7 +30,8 @@ class AccumulationDistributionIndicator LegendIconType? legendIconType, String? legendItemText, Color? signalLineColor, - double? signalLineWidth}) + double? signalLineWidth, + ChartIndicatorRenderCallback? onRenderDetailsUpdate}) : volumeValueMapper = (volumeValueMapper != null) ? ((int index) => volumeValueMapper(dataSource![index], index)) : null, @@ -51,7 +52,8 @@ class AccumulationDistributionIndicator legendIconType: legendIconType, legendItemText: legendItemText, signalLineColor: signalLineColor, - signalLineWidth: signalLineWidth); + signalLineWidth: signalLineWidth, + onRenderDetailsUpdate: onRenderDetailsUpdate); /// Volume of series. /// @@ -79,6 +81,61 @@ class AccumulationDistributionIndicator /// final ChartIndexedValueMapper? volumeValueMapper; + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is AccumulationDistributionIndicator && + other.isVisible == isVisible && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.seriesName == seriesName && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.highValueMapper == highValueMapper && + other.lowValueMapper == lowValueMapper && + other.closeValueMapper == closeValueMapper && + other.volumeValueMapper == volumeValueMapper && + other.name == name && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.signalLineColor == signalLineColor && + other.signalLineWidth == signalLineWidth; + } + + @override + int get hashCode { + final List values = [ + isVisible, + xAxisName, + yAxisName, + seriesName, + dashArray, + animationDuration, + dataSource, + xValueMapper, + highValueMapper, + lowValueMapper, + closeValueMapper, + volumeValueMapper, + name, + isVisibleInLegend, + legendIconType, + legendItemText, + signalLineColor, + signalLineWidth + ]; + return hashList(values); + } + /// To initialise indicators collections // ignore:unused_element void _initSeriesCollection( @@ -87,23 +144,20 @@ class AccumulationDistributionIndicator TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { technicalIndicatorsRenderer._targetSeriesRenderers = []; - technicalIndicatorsRenderer._setSeriesProperties( - indicator, - indicator.name ?? 'AD', - indicator.signalLineColor, - indicator.signalLineWidth, - chart); } /// To initialise data source of technical indicators // ignore:unused_element - void _initDataSource(TechnicalIndicators indicator, - TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { + void _initDataSource( + TechnicalIndicators indicator, + TechnicalIndicatorsRenderer technicalIndicatorsRenderer, + SfCartesianChart chart) { final List> validData = technicalIndicatorsRenderer._dataPoints!; if (validData.isNotEmpty && indicator is AccumulationDistributionIndicator) { - _calculateADPoints(indicator, validData, technicalIndicatorsRenderer); + _calculateADPoints( + indicator, validData, technicalIndicatorsRenderer, chart); } } @@ -111,30 +165,31 @@ class AccumulationDistributionIndicator void _calculateADPoints( AccumulationDistributionIndicator indicator, List> validData, - TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { + TechnicalIndicatorsRenderer technicalIndicatorsRenderer, + SfCartesianChart chart) { final List> points = >[]; final List xValues = []; CartesianChartPoint point; - final CartesianSeriesRenderer signalSeriesRenderer = - technicalIndicatorsRenderer._targetSeriesRenderers[0]; - num sum = 0; - num value = 0; - num high = 0; - num low = 0; - num close = 0; + num sum = 0, value = 0, high = 0, low = 0, close = 0; for (int i = 0; i < validData.length; i++) { high = validData[i].high ?? 0; low = validData[i].low ?? 0; close = validData[i].close ?? 0; value = ((close - low) - (high - close)) / (high - low); sum = sum + value * validData[i].volume!; - point = technicalIndicatorsRenderer._getDataPoint(validData[i].x, sum, - validData[i], signalSeriesRenderer, points.length); + point = technicalIndicatorsRenderer._getDataPoint( + validData[i].x, sum, validData[i], points.length); points.add(point); xValues.add(point.x); } technicalIndicatorsRenderer._renderPoints = points; + technicalIndicatorsRenderer._setSeriesProperties( + indicator, + indicator.name ?? 'AD', + indicator.signalLineColor, + indicator.signalLineWidth, + chart); technicalIndicatorsRenderer._setSeriesRange(points, indicator, xValues); } } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/atr_indicator.dart b/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/atr_indicator.dart index 70e88bb95..75a47258d 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/atr_indicator.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/atr_indicator.dart @@ -3,13 +3,13 @@ part of charts; /// This class holds the properties of the Average True Range Indicator. /// /// -/// ATR indicator is a technical analysis volatility indicator. This indicator does not indicate the price trend. +/// ATR indicator is a technical analysis volatility indicator. This indicator does not indicate the price trend, /// simply the degree of price volatility. The average true range is an N-day smoothed moving average (SMMA) of the true range values. /// /// /// Provides options for series visible, axis name, series name, animation duration, legend visibility, /// signal line width, and color to customize the appearance of indicator. - +@immutable class AtrIndicator extends TechnicalIndicators { /// Creating an argument constructor of AtrIndicator class. AtrIndicator( @@ -30,7 +30,8 @@ class AtrIndicator extends TechnicalIndicators { String? legendItemText, Color? signalLineColor, double? signalLineWidth, - int? period}) + int? period, + ChartIndicatorRenderCallback? onRenderDetailsUpdate}) : super( isVisible: isVisible, xAxisName: xAxisName, @@ -49,7 +50,63 @@ class AtrIndicator extends TechnicalIndicators { legendItemText: legendItemText, signalLineColor: signalLineColor, signalLineWidth: signalLineWidth, - period: period); + period: period, + onRenderDetailsUpdate: onRenderDetailsUpdate); + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is AtrIndicator && + other.isVisible == isVisible && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.seriesName == seriesName && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.highValueMapper == highValueMapper && + other.lowValueMapper == lowValueMapper && + other.closeValueMapper == closeValueMapper && + other.period == period && + other.name == name && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.signalLineColor == signalLineColor && + other.signalLineWidth == signalLineWidth; + } + + @override + int get hashCode { + final List values = [ + isVisible, + xAxisName, + yAxisName, + seriesName, + dashArray, + animationDuration, + dataSource, + xValueMapper, + highValueMapper, + lowValueMapper, + closeValueMapper, + name, + isVisibleInLegend, + legendIconType, + legendItemText, + signalLineColor, + signalLineWidth, + period + ]; + return hashList(values); + } /// To initialise indicators collections // ignore:unused_element @@ -59,38 +116,34 @@ class AtrIndicator extends TechnicalIndicators { TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { technicalIndicatorsRenderer._targetSeriesRenderers = []; - technicalIndicatorsRenderer._setSeriesProperties( - indicator, - indicator.name ?? 'ATR', - indicator.signalLineColor, - indicator.signalLineWidth, - chart); } /// To initialise data source of technical indicators // ignore:unused_element - void _initDataSource(TechnicalIndicators indicator, - TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { + void _initDataSource( + TechnicalIndicators indicator, + TechnicalIndicatorsRenderer technicalIndicatorsRenderer, + SfCartesianChart chart, + ) { final List> validData = technicalIndicatorsRenderer._dataPoints!; if (validData.isNotEmpty && validData.length > indicator.period && indicator is AtrIndicator) { - _calculateATRPoints(indicator, validData, technicalIndicatorsRenderer); + _calculateATRPoints( + indicator, validData, technicalIndicatorsRenderer, chart); } } /// To calculate the rendering points of the ATR indicator void _calculateATRPoints( - AtrIndicator indicator, - List> validData, - TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { + AtrIndicator indicator, + List> validData, + TechnicalIndicatorsRenderer technicalIndicatorsRenderer, + SfCartesianChart chart, + ) { num average = 0; - num highLow = 0; - num highClose = 0; - num lowClose = 0; - num trueRange = 0; - num tempRange = 0; + num highLow = 0, highClose = 0, lowClose = 0, trueRange = 0, tempRange = 0; final List> points = >[]; final List xValues = []; @@ -98,8 +151,6 @@ class AtrIndicator extends TechnicalIndicators { final List<_TempData> temp = <_TempData>[]; final num period = indicator.period; num sum = 0; - final CartesianSeriesRenderer signalSeriesRenderer = - technicalIndicatorsRenderer._targetSeriesRenderers[0]; for (int i = 0; i < validData.length; i++) { if (!validData[i].isDrop && !validData[i].isGap) { highLow = validData[i].high - validData[i].low; @@ -113,15 +164,15 @@ class AtrIndicator extends TechnicalIndicators { if (i >= period && period > 0) { average = (temp[temp.length - 1].y * (period - 1) + trueRange) / period; - point = technicalIndicatorsRenderer._getDataPoint(validData[i].x, - average, validData[i], signalSeriesRenderer, points.length); + point = technicalIndicatorsRenderer._getDataPoint( + validData[i].x, average, validData[i], points.length); points.add(point); xValues.add(point.x); } else { average = sum / period; if (i == period - 1) { - point = technicalIndicatorsRenderer._getDataPoint(validData[i].x, - average, validData[i], signalSeriesRenderer, points.length); + point = technicalIndicatorsRenderer._getDataPoint( + validData[i].x, average, validData[i], points.length); points.add(point); xValues.add(point.x); } @@ -130,6 +181,12 @@ class AtrIndicator extends TechnicalIndicators { } } technicalIndicatorsRenderer._renderPoints = points; + technicalIndicatorsRenderer._setSeriesProperties( + indicator, + indicator.name ?? 'ATR', + indicator.signalLineColor, + indicator.signalLineWidth, + chart); technicalIndicatorsRenderer._setSeriesRange(points, indicator, xValues); } } @@ -137,5 +194,5 @@ class AtrIndicator extends TechnicalIndicators { class _TempData { _TempData(this.x, this.y); final dynamic x; - final dynamic y; + final num y; } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/bollinger_bands_indicator.dart b/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/bollinger_bands_indicator.dart index ab416d1df..0ffcf77b6 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/bollinger_bands_indicator.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/bollinger_bands_indicator.dart @@ -8,6 +8,7 @@ part of charts; /// Provides options for series visible, axis name, series name, animation duration, legend visibility, /// band color to customize the appearance. /// +@immutable class BollingerBandIndicator extends TechnicalIndicators { /// Creating an argument constructor of BollingerBandIndicator class. BollingerBandIndicator( @@ -32,7 +33,8 @@ class BollingerBandIndicator extends TechnicalIndicators { this.upperLineWidth = 2, this.lowerLineColor = Colors.green, this.lowerLineWidth = 2, - this.bandColor = const Color(0x409e9e9e)}) + this.bandColor = const Color(0x409e9e9e), + ChartIndicatorRenderCallback? onRenderDetailsUpdate}) : super( isVisible: isVisible, xAxisName: xAxisName, @@ -49,7 +51,8 @@ class BollingerBandIndicator extends TechnicalIndicators { legendItemText: legendItemText, signalLineColor: signalLineColor, signalLineWidth: signalLineWidth, - period: period); + period: period, + onRenderDetailsUpdate: onRenderDetailsUpdate); /// Standard Deviation value of the bollinger bands /// @@ -153,62 +156,95 @@ class BollingerBandIndicator extends TechnicalIndicators { /// final Color bandColor; + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is BollingerBandIndicator && + other.isVisible == isVisible && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.seriesName == seriesName && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.closeValueMapper == closeValueMapper && + other.period == period && + other.name == name && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.signalLineColor == signalLineColor && + other.signalLineWidth == signalLineWidth && + other.standardDeviation == standardDeviation && + other.upperLineColor == upperLineColor && + other.upperLineWidth == upperLineWidth && + other.lowerLineColor == lowerLineColor && + other.lowerLineWidth == lowerLineWidth && + other.bandColor == bandColor; + } + + @override + int get hashCode { + final List values = [ + isVisible, + xAxisName, + yAxisName, + seriesName, + dashArray, + animationDuration, + dataSource, + xValueMapper, + closeValueMapper, + name, + isVisibleInLegend, + legendIconType, + legendItemText, + signalLineColor, + signalLineWidth, + period, + standardDeviation, + upperLineColor, + upperLineWidth, + lowerLineColor, + lowerLineWidth, + bandColor + ]; + return hashList(values); + } + /// To initialise indicators collections // ignore:unused_element void _initSeriesCollection( BollingerBandIndicator indicator, SfCartesianChart chart, TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { - // Decides the type of renderer class to be used - bool isLine, isRangeArea; technicalIndicatorsRenderer._targetSeriesRenderers = []; - if (indicator.bandColor != Colors.transparent && - // ignore: unnecessary_null_comparison - indicator.bandColor != null) { - isLine = false; - isRangeArea = true; - technicalIndicatorsRenderer._setSeriesProperties(indicator, 'rangearea', - indicator.bandColor, 0, chart, isLine, isRangeArea); - } - isLine = true; - technicalIndicatorsRenderer._setSeriesProperties( - indicator, - indicator.name ?? 'BollingerBand', - indicator.signalLineColor, - indicator.signalLineWidth, - chart); - technicalIndicatorsRenderer._setSeriesProperties(indicator, 'UpperLine', - indicator.upperLineColor, indicator.upperLineWidth, chart, isLine); - technicalIndicatorsRenderer._setSeriesProperties(indicator, 'LowerLine', - indicator.lowerLineColor, indicator.lowerLineWidth, chart, isLine); } /// To initialise data source of technical indicators // ignore:unused_element - void _initDataSource(BollingerBandIndicator indicator, - TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { + void _initDataSource( + BollingerBandIndicator indicator, + TechnicalIndicatorsRenderer technicalIndicatorsRenderer, + SfCartesianChart chart, + ) { final bool enableBand = indicator.bandColor != Colors.transparent && // ignore: unnecessary_null_comparison indicator.bandColor != null; final int start = enableBand ? 1 : 0; final List> signalCollection = - >[]; - final List> upperCollection = - >[]; - final List> lowerCollection = - >[]; - final List> bandCollection = - >[]; - final CartesianSeriesRenderer upperSeriesRenderer = - technicalIndicatorsRenderer._targetSeriesRenderers[start + 1]; - final CartesianSeriesRenderer lowerSeriesRenderer = - technicalIndicatorsRenderer._targetSeriesRenderers[start + 2]; - final CartesianSeriesRenderer signalSeriesRenderer = - technicalIndicatorsRenderer._targetSeriesRenderers[start]; - final CartesianSeriesRenderer? rangeAreaSeriesRenderer = enableBand - ? technicalIndicatorsRenderer._targetSeriesRenderers[0] - : null; + >[], + upperCollection = >[], + lowerCollection = >[], + bandCollection = >[]; final List xValues = []; //prepare data @@ -217,14 +253,12 @@ class BollingerBandIndicator extends TechnicalIndicators { if (validData.isNotEmpty && validData.length >= indicator.period && indicator.period > 0) { - num sum = 0; - num deviationSum = 0; + num sum = 0, deviationSum = 0; final num multiplier = indicator.standardDeviation; - final int limit = validData.length; - final int length = indicator.period.round(); + final int limit = validData.length, length = indicator.period.round(); // This has been null before - final List smaPoints = List.filled(limit, -1); - final List deviations = List.filled(limit, -1); + final List smaPoints = List.filled(limit, -1), + deviations = List.filled(limit, -1); final List<_BollingerData> bollingerPoints = List<_BollingerData>.filled( limit, _BollingerData( @@ -276,8 +310,7 @@ class BollingerBandIndicator extends TechnicalIndicators { } } } - int i = -1; - int j = -1; + int i = -1, j = -1; for (int k = 0; k < limit; k++) { if (k >= (length - 1)) { xValues.add(validData[k].x); @@ -285,19 +318,16 @@ class BollingerBandIndicator extends TechnicalIndicators { validData[k].x, bollingerPoints[k].upBand, validData[k], - upperSeriesRenderer, upperCollection.length)); lowerCollection.add(technicalIndicatorsRenderer._getDataPoint( validData[k].x, bollingerPoints[k].lowBand, validData[k], - lowerSeriesRenderer, lowerCollection.length)); signalCollection.add(technicalIndicatorsRenderer._getDataPoint( validData[k].x, bollingerPoints[k].midBand, validData[k], - signalSeriesRenderer, signalCollection.length)); if (enableBand) { bandCollection.add(technicalIndicatorsRenderer._getRangePoint( @@ -305,13 +335,35 @@ class BollingerBandIndicator extends TechnicalIndicators { upperCollection[++i].y, lowerCollection[++j].y, validData[k], - rangeAreaSeriesRenderer?._series, bandCollection.length)); } } } } technicalIndicatorsRenderer._renderPoints = signalCollection; + technicalIndicatorsRenderer._bollingerUpper = upperCollection; + technicalIndicatorsRenderer._bollingerLower = lowerCollection; + // Decides the type of renderer class to be used + bool isLine, isRangeArea; + if (indicator.bandColor != Colors.transparent && + // ignore: unnecessary_null_comparison + indicator.bandColor != null) { + isLine = false; + isRangeArea = true; + technicalIndicatorsRenderer._setSeriesProperties(indicator, 'rangearea', + indicator.bandColor, 0, chart, isLine, isRangeArea); + } + isLine = true; + technicalIndicatorsRenderer._setSeriesProperties( + indicator, + indicator.name ?? 'BollingerBand', + indicator.signalLineColor, + indicator.signalLineWidth, + chart); + technicalIndicatorsRenderer._setSeriesProperties(indicator, 'UpperLine', + indicator.upperLineColor, indicator.upperLineWidth, chart, isLine); + technicalIndicatorsRenderer._setSeriesProperties(indicator, 'LowerLine', + indicator.lowerLineColor, indicator.lowerLineWidth, chart, isLine); if (enableBand) { technicalIndicatorsRenderer._setSeriesRange(bandCollection, indicator, xValues, technicalIndicatorsRenderer._targetSeriesRenderers[0]); diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/ema_indicator.dart b/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/ema_indicator.dart index af9578fa5..fb786ce93 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/ema_indicator.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/ema_indicator.dart @@ -6,30 +6,32 @@ part of charts; /// /// It also has the [valueField] property. Based on this property Indicator will be rendered /// +@immutable class EmaIndicator extends TechnicalIndicators { /// Creating an argument constructor of EmaIndicator class. - EmaIndicator({ - bool? isVisible, - String? xAxisName, - String? yAxisName, - String? seriesName, - List? dashArray, - double? animationDuration, - List? dataSource, - ChartValueMapper? xValueMapper, - ChartValueMapper? highValueMapper, - ChartValueMapper? lowValueMapper, - ChartValueMapper? openValueMapper, - ChartValueMapper? closeValueMapper, - String? name, - bool? isVisibleInLegend, - LegendIconType? legendIconType, - String? legendItemText, - Color? signalLineColor, - double? signalLineWidth, - int? period, - String? valueField, - }) : valueField = (valueField ?? 'close').toLowerCase(), + EmaIndicator( + {bool? isVisible, + String? xAxisName, + String? yAxisName, + String? seriesName, + List? dashArray, + double? animationDuration, + List? dataSource, + ChartValueMapper? xValueMapper, + ChartValueMapper? highValueMapper, + ChartValueMapper? lowValueMapper, + ChartValueMapper? openValueMapper, + ChartValueMapper? closeValueMapper, + String? name, + bool? isVisibleInLegend, + LegendIconType? legendIconType, + String? legendItemText, + Color? signalLineColor, + double? signalLineWidth, + int? period, + String? valueField, + ChartIndicatorRenderCallback? onRenderDetailsUpdate}) + : valueField = (valueField ?? 'close').toLowerCase(), super( isVisible: isVisible, xAxisName: xAxisName, @@ -49,7 +51,8 @@ class EmaIndicator extends TechnicalIndicators { legendItemText: legendItemText, signalLineColor: signalLineColor, signalLineWidth: signalLineWidth, - period: period); + period: period, + onRenderDetailsUpdate: onRenderDetailsUpdate); ///ValueField property for ema indicator. /// @@ -70,6 +73,65 @@ class EmaIndicator extends TechnicalIndicators { ///``` final String valueField; + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is EmaIndicator && + other.isVisible == isVisible && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.seriesName == seriesName && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.highValueMapper == highValueMapper && + other.lowValueMapper == lowValueMapper && + other.openValueMapper == openValueMapper && + other.closeValueMapper == closeValueMapper && + other.period == period && + other.name == name && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.signalLineColor == signalLineColor && + other.signalLineWidth == signalLineWidth && + other.valueField == valueField; + } + + @override + int get hashCode { + final List values = [ + isVisible, + xAxisName, + yAxisName, + seriesName, + dashArray, + animationDuration, + dataSource, + xValueMapper, + highValueMapper, + lowValueMapper, + openValueMapper, + closeValueMapper, + name, + isVisibleInLegend, + legendIconType, + legendItemText, + signalLineColor, + signalLineWidth, + valueField, + period + ]; + return hashList(values); + } + /// To initialise indicators collections // ignore:unused_element void _initSeriesCollection( @@ -78,25 +140,22 @@ class EmaIndicator extends TechnicalIndicators { TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { technicalIndicatorsRenderer._targetSeriesRenderers = []; - technicalIndicatorsRenderer._setSeriesProperties( - indicator, - indicator.name ?? 'EMA', - indicator.signalLineColor, - indicator.signalLineWidth, - chart); } /// To initialise data source of technical indicators // ignore:unused_element - void _initDataSource(TechnicalIndicators indicator, - TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { + void _initDataSource( + TechnicalIndicators indicator, + TechnicalIndicatorsRenderer technicalIndicatorsRenderer, + SfCartesianChart chart) { final List> validData = technicalIndicatorsRenderer._dataPoints!; if (validData.isNotEmpty && validData.length > indicator.period && indicator is EmaIndicator && indicator.period > 0) { - _calculateEMAPoints(indicator, validData, technicalIndicatorsRenderer); + _calculateEMAPoints( + indicator, validData, technicalIndicatorsRenderer, chart); } } @@ -104,17 +163,15 @@ class EmaIndicator extends TechnicalIndicators { void _calculateEMAPoints( EmaIndicator indicator, List> validData, - TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { - final CartesianSeriesRenderer signalSeriesRenderer = - technicalIndicatorsRenderer._targetSeriesRenderers[0]; + TechnicalIndicatorsRenderer technicalIndicatorsRenderer, + SfCartesianChart chart) { final int period = indicator.period; final List> points = >[]; final List xValues = []; CartesianChartPoint point; if (validData.length >= period && period > 0) { - num sum = 0; - num average = 0; + num sum = 0, average = 0; final num k = 2 / (period + 1); for (int i = 0; i < period; i++) { sum += technicalIndicatorsRenderer._getFieldValue( @@ -122,7 +179,7 @@ class EmaIndicator extends TechnicalIndicators { } average = sum / period; point = technicalIndicatorsRenderer._getDataPoint(validData[period - 1].x, - average, validData[period - 1], signalSeriesRenderer, points.length); + average, validData[period - 1], points.length); points.add(point); xValues.add(point.x); int index = period; @@ -136,8 +193,8 @@ class EmaIndicator extends TechnicalIndicators { prevAverage) * k + prevAverage; - point = technicalIndicatorsRenderer._getDataPoint(validData[index].x, - yValue, validData[index], signalSeriesRenderer, points.length); + point = technicalIndicatorsRenderer._getDataPoint( + validData[index].x, yValue, validData[index], points.length); points.add(point); xValues.add(point.x); } @@ -145,6 +202,12 @@ class EmaIndicator extends TechnicalIndicators { } } technicalIndicatorsRenderer._renderPoints = points; + technicalIndicatorsRenderer._setSeriesProperties( + indicator, + indicator.name ?? 'EMA', + indicator.signalLineColor, + indicator.signalLineWidth, + chart); technicalIndicatorsRenderer._setSeriesRange(points, indicator, xValues); } } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/macd_indicator.dart b/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/macd_indicator.dart index 7add88c91..ca239ecbf 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/macd_indicator.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/macd_indicator.dart @@ -10,6 +10,7 @@ part of charts; /// /// Provides the options of macd type, name, short Period, long period and macd line color is used to customize the appearance. /// +@immutable class MacdIndicator extends TechnicalIndicators { /// Creating an argument constructor of MacdIndicator class. MacdIndicator( @@ -35,7 +36,8 @@ class MacdIndicator extends TechnicalIndicators { this.macdLineWidth = 2, this.macdType = MacdType.both, this.histogramPositiveColor = Colors.green, - this.histogramNegativeColor = Colors.red}) + this.histogramNegativeColor = Colors.red, + ChartIndicatorRenderCallback? onRenderDetailsUpdate}) : super( isVisible: isVisible, xAxisName: xAxisName, @@ -52,7 +54,8 @@ class MacdIndicator extends TechnicalIndicators { legendItemText: legendItemText, signalLineColor: signalLineColor, signalLineWidth: signalLineWidth, - period: period); + period: period, + onRenderDetailsUpdate: onRenderDetailsUpdate); /// Short period value of the macd indicator. /// @@ -173,51 +176,88 @@ class MacdIndicator extends TechnicalIndicators { /// final Color histogramNegativeColor; + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is MacdIndicator && + other.isVisible == isVisible && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.seriesName == seriesName && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.closeValueMapper == closeValueMapper && + other.period == period && + other.name == name && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.signalLineColor == signalLineColor && + other.signalLineWidth == signalLineWidth && + other.shortPeriod == shortPeriod && + other.longPeriod == longPeriod && + other.macdLineColor == macdLineColor && + other.macdLineWidth == macdLineWidth && + other.macdType == macdType && + other.histogramPositiveColor == histogramPositiveColor && + other.histogramNegativeColor == histogramNegativeColor; + } + + @override + int get hashCode { + final List values = [ + isVisible, + xAxisName, + yAxisName, + seriesName, + dashArray, + animationDuration, + dataSource, + xValueMapper, + closeValueMapper, + name, + isVisibleInLegend, + legendIconType, + legendItemText, + signalLineColor, + signalLineWidth, + period, + shortPeriod, + longPeriod, + macdLineColor, + macdLineWidth, + macdType, + histogramPositiveColor, + histogramNegativeColor + ]; + return hashList(values); + } + /// To initialise indicators collections // ignore:unused_element void _initSeriesCollection( MacdIndicator indicator, SfCartesianChart chart, TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { - // To describe the type of series renderer to be assigned - bool isLine, isRangeArea, isHistogram; technicalIndicatorsRenderer._targetSeriesRenderers = []; - technicalIndicatorsRenderer._setSeriesProperties( - indicator, - indicator.name ?? 'MACD', - indicator.signalLineColor, - indicator.signalLineWidth, - chart); - - if (indicator.macdType == MacdType.line || - indicator.macdType == MacdType.both) { - // Decides the type of renderer class to be used - isLine = true; - technicalIndicatorsRenderer._setSeriesProperties(indicator, 'MacdLine', - indicator.macdLineColor, indicator.macdLineWidth, chart, isLine); - } - if (indicator.macdType == MacdType.histogram || - indicator.macdType == MacdType.both) { - isLine = false; - isRangeArea = false; - isHistogram = true; - technicalIndicatorsRenderer._setSeriesProperties( - indicator, - 'Histogram', - indicator.histogramPositiveColor, - indicator.signalLineWidth / 2, - chart, - isLine, - isRangeArea, - isHistogram); - } } /// To initialise data source of technical indicators // ignore:unused_element - void _initDataSource(MacdIndicator indicator, - TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { + void _initDataSource( + MacdIndicator indicator, + TechnicalIndicatorsRenderer technicalIndicatorsRenderer, + SfCartesianChart chart, + ) { List> signalCollection = >[]; final num fastPeriod = indicator.longPeriod; @@ -225,31 +265,15 @@ class MacdIndicator extends TechnicalIndicators { final num trigger = indicator.period; final num length = fastPeriod + trigger; List> macdCollection = - >[]; - List> histogramCollection = - >[]; + >[], + histogramCollection = >[]; final List> validData = technicalIndicatorsRenderer._dataPoints!; - final CartesianSeriesRenderer signalSeriesRenderer = - technicalIndicatorsRenderer._targetSeriesRenderers[0]; List signalX = [], histogramX = [], macdX = [], collection; - CartesianSeriesRenderer? histogramSeriesRenderer; - CartesianSeriesRenderer? macdLineSeriesRenderer; - - if (indicator.macdType == MacdType.histogram) { - histogramSeriesRenderer = - technicalIndicatorsRenderer._targetSeriesRenderers[1]; - } else { - macdLineSeriesRenderer = - technicalIndicatorsRenderer._targetSeriesRenderers[1]; - if (indicator.macdType == MacdType.both) { - histogramSeriesRenderer = - technicalIndicatorsRenderer._targetSeriesRenderers[2]; - } - } + CartesianSeriesRenderer? histogramSeriesRenderer, macdLineSeriesRenderer; if (validData.isNotEmpty && length < validData.length && @@ -263,27 +287,67 @@ class MacdIndicator extends TechnicalIndicators { fastPeriod, validData, 'close', technicalIndicatorsRenderer); final List macdValues = _getMACDVales(indicator, shortEMA, longEMA); collection = _getMACDPoints( - indicator, - macdValues, - validData, - macdLineSeriesRenderer ?? signalSeriesRenderer, - technicalIndicatorsRenderer); + indicator, macdValues, validData, technicalIndicatorsRenderer); macdCollection = collection[0]; macdX = collection[1]; final List signalEMA = _calculateEMAValues( trigger, macdCollection, 'y', technicalIndicatorsRenderer); - collection = _getSignalPoints(indicator, signalEMA, validData, - signalSeriesRenderer, technicalIndicatorsRenderer); + collection = _getSignalPoints( + indicator, signalEMA, validData, technicalIndicatorsRenderer); signalCollection = collection[0]; signalX = collection[1]; - if (histogramSeriesRenderer != null) { + if (indicator.macdType == MacdType.histogram || + indicator.macdType == MacdType.both) { collection = _getHistogramPoints(indicator, macdValues, signalEMA, - validData, histogramSeriesRenderer, technicalIndicatorsRenderer); + validData, technicalIndicatorsRenderer); histogramCollection = collection[0]; histogramX = collection[1]; } } technicalIndicatorsRenderer._renderPoints = signalCollection; + technicalIndicatorsRenderer._macdHistogram = histogramCollection; + technicalIndicatorsRenderer._macdLine = macdCollection; + technicalIndicatorsRenderer._setSeriesProperties( + indicator, + indicator.name ?? 'MACD', + indicator.signalLineColor, + indicator.signalLineWidth, + chart); + // To describe the type of series renderer to be assigned + bool isLine, isRangeArea, isHistogram; + if (indicator.macdType == MacdType.line || + indicator.macdType == MacdType.both) { + // Decides the type of renderer class to be used + isLine = true; + technicalIndicatorsRenderer._setSeriesProperties(indicator, 'MacdLine', + indicator.macdLineColor, indicator.macdLineWidth, chart, isLine); + } + if (indicator.macdType == MacdType.histogram || + indicator.macdType == MacdType.both) { + isLine = false; + isRangeArea = false; + isHistogram = true; + technicalIndicatorsRenderer._setSeriesProperties( + indicator, + 'Histogram', + indicator.histogramPositiveColor, + indicator.signalLineWidth / 2, + chart, + isLine, + isRangeArea, + isHistogram); + } + if (indicator.macdType == MacdType.histogram) { + histogramSeriesRenderer = + technicalIndicatorsRenderer._targetSeriesRenderers[1]; + } else { + macdLineSeriesRenderer = + technicalIndicatorsRenderer._targetSeriesRenderers[1]; + if (indicator.macdType == MacdType.both) { + histogramSeriesRenderer = + technicalIndicatorsRenderer._targetSeriesRenderers[2]; + } + } technicalIndicatorsRenderer._setSeriesRange(signalCollection, indicator, signalX, technicalIndicatorsRenderer._targetSeriesRenderers[0]); if (histogramSeriesRenderer != null) { @@ -302,8 +366,7 @@ class MacdIndicator extends TechnicalIndicators { List> validData, String valueField, TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { - num sum = 0; - num initialEMA = 0; + num sum = 0, initialEMA = 0; final List emaValues = []; final num emaPercent = 2 / (period + 1); for (int i = 0; i < period; i++) { @@ -329,19 +392,16 @@ class MacdIndicator extends TechnicalIndicators { MacdIndicator indicator, List macdPoints, List> validData, - CartesianSeriesRenderer seriesRenderer, TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { final List> macdCollection = >[]; final List xValues = []; - int dataMACDIndex = indicator.longPeriod - 1; - int macdIndex = 0; + int dataMACDIndex = indicator.longPeriod - 1, macdIndex = 0; while (dataMACDIndex < validData.length) { macdCollection.add(technicalIndicatorsRenderer._getDataPoint( validData[dataMACDIndex].x, macdPoints[macdIndex], validData[dataMACDIndex], - seriesRenderer, macdCollection.length)); xValues.add(validData[dataMACDIndex].x); dataMACDIndex++; @@ -355,7 +415,6 @@ class MacdIndicator extends TechnicalIndicators { MacdIndicator indicator, List signalEma, List> validData, - CartesianSeriesRenderer seriesRenderer, TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { int dataSignalIndex = indicator.longPeriod + indicator.period - 2; int signalIndex = 0; @@ -367,7 +426,6 @@ class MacdIndicator extends TechnicalIndicators { validData[dataSignalIndex].x, signalEma[signalIndex], validData[dataSignalIndex], - seriesRenderer, signalCollection.length)); xValues.add(validData[dataSignalIndex].x); dataSignalIndex++; @@ -393,7 +451,6 @@ class MacdIndicator extends TechnicalIndicators { List macdPoints, List signalEma, List> validData, - CartesianSeriesRenderer seriesRenderer, TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { int dataHistogramIndex = indicator.longPeriod + indicator.period - 2; int histogramIndex = 0; @@ -406,7 +463,6 @@ class MacdIndicator extends TechnicalIndicators { macdPoints[histogramIndex + (indicator.period - 1)] - signalEma[histogramIndex], validData[dataHistogramIndex], - seriesRenderer, histogramCollection.length, indicator)); xValues.add(validData[dataHistogramIndex].x); diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/momentum_indicator.dart b/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/momentum_indicator.dart index c8c33b5ba..c22375203 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/momentum_indicator.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/momentum_indicator.dart @@ -7,31 +7,33 @@ part of charts; /// /// Provides the options for visibility, centerline color, centerline width, and period values to customize the appearance. /// +@immutable class MomentumIndicator extends TechnicalIndicators { /// Creating an argument constructor of MomentumIndicator class. - MomentumIndicator({ - bool? isVisible, - String? xAxisName, - String? yAxisName, - String? seriesName, - List? dashArray, - double? animationDuration, - List? dataSource, - ChartValueMapper? xValueMapper, - ChartValueMapper? highValueMapper, - ChartValueMapper? lowValueMapper, - ChartValueMapper? openValueMapper, - ChartValueMapper? closeValueMapper, - String? name, - bool? isVisibleInLegend, - LegendIconType? legendIconType, - String? legendItemText, - Color? signalLineColor, - double? signalLineWidth, - int? period, - this.centerLineColor = Colors.red, - this.centerLineWidth = 2, - }) : super( + MomentumIndicator( + {bool? isVisible, + String? xAxisName, + String? yAxisName, + String? seriesName, + List? dashArray, + double? animationDuration, + List? dataSource, + ChartValueMapper? xValueMapper, + ChartValueMapper? highValueMapper, + ChartValueMapper? lowValueMapper, + ChartValueMapper? openValueMapper, + ChartValueMapper? closeValueMapper, + String? name, + bool? isVisibleInLegend, + LegendIconType? legendIconType, + String? legendItemText, + Color? signalLineColor, + double? signalLineWidth, + int? period, + this.centerLineColor = Colors.red, + this.centerLineWidth = 2, + ChartIndicatorRenderCallback? onRenderDetailsUpdate}) + : super( isVisible: isVisible, xAxisName: xAxisName, yAxisName: yAxisName, @@ -50,7 +52,8 @@ class MomentumIndicator extends TechnicalIndicators { legendItemText: legendItemText, signalLineColor: signalLineColor, signalLineWidth: signalLineWidth, - period: period); + period: period, + onRenderDetailsUpdate: onRenderDetailsUpdate); /// Center line color of the momentum indicator /// @@ -86,70 +89,121 @@ class MomentumIndicator extends TechnicalIndicators { /// ``` final double centerLineWidth; + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is MomentumIndicator && + other.isVisible == isVisible && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.seriesName == seriesName && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.highValueMapper == highValueMapper && + other.lowValueMapper == lowValueMapper && + other.openValueMapper == openValueMapper && + other.closeValueMapper == closeValueMapper && + other.period == period && + other.name == name && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.signalLineColor == signalLineColor && + other.signalLineWidth == signalLineWidth && + other.centerLineColor == centerLineColor && + other.centerLineWidth == centerLineWidth; + } + + @override + int get hashCode { + final List values = [ + isVisible, + xAxisName, + yAxisName, + seriesName, + dashArray, + animationDuration, + dataSource, + xValueMapper, + highValueMapper, + lowValueMapper, + openValueMapper, + closeValueMapper, + name, + isVisibleInLegend, + legendIconType, + legendItemText, + signalLineColor, + signalLineWidth, + period, + centerLineColor, + centerLineWidth + ]; + return hashList(values); + } + /// To initialise indicators collections // ignore:unused_element void _initSeriesCollection( MomentumIndicator indicator, SfCartesianChart chart, TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { - // Decides the type of renderer class to be used - final bool isLine = true; technicalIndicatorsRenderer._targetSeriesRenderers = []; - technicalIndicatorsRenderer._setSeriesProperties( - indicator, - indicator.name ?? 'Momentum', - indicator.signalLineColor, - indicator.signalLineWidth, - chart); - technicalIndicatorsRenderer._setSeriesProperties(indicator, 'CenterLine', - indicator.centerLineColor, indicator.centerLineWidth, chart, isLine); } /// To initialise data source of technical indicators // ignore:unused_element - void _initDataSource(MomentumIndicator indicator, - TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { + void _initDataSource( + MomentumIndicator indicator, + TechnicalIndicatorsRenderer technicalIndicatorsRenderer, + SfCartesianChart chart, + ) { final List> signalCollection = - >[]; - final List> centerLineCollection = - >[]; - final List> validData = - technicalIndicatorsRenderer._dataPoints!; - final List centerXValues = []; - final List xValues = []; + >[], + centerLineCollection = >[], + validData = technicalIndicatorsRenderer._dataPoints!; + final List centerXValues = [], xValues = []; num value; if (validData.isNotEmpty) { - final CartesianSeriesRenderer signalSeriesRenderer = - technicalIndicatorsRenderer._targetSeriesRenderers[0]; - final CartesianSeriesRenderer upperSeriesRenderer = - technicalIndicatorsRenderer._targetSeriesRenderers[1]; final int length = indicator.period; if (validData.length >= indicator.period && indicator.period > 0) { for (int i = 0; i < validData.length; i++) { centerLineCollection.add(technicalIndicatorsRenderer._getDataPoint( - validData[i].x, - 100, - validData[i], - upperSeriesRenderer, - centerLineCollection.length)); + validData[i].x, 100, validData[i], centerLineCollection.length)); centerXValues.add(validData[i].x); if (!(i < length)) { value = (validData[i].close ?? 0) / (validData[i - length].close ?? 1) * 100; signalCollection.add(technicalIndicatorsRenderer._getDataPoint( - validData[i].x, - value, - validData[i], - signalSeriesRenderer, - signalCollection.length)); + validData[i].x, value, validData[i], signalCollection.length)); xValues.add(validData[i].x); } } } technicalIndicatorsRenderer._renderPoints = signalCollection; + technicalIndicatorsRenderer._momentumCenterLineValue = + centerLineCollection.first.y.toDouble(); + // Decides the type of renderer class to be used + const bool isLine = true; + technicalIndicatorsRenderer._setSeriesProperties( + indicator, + indicator.name ?? 'Momentum', + indicator.signalLineColor, + indicator.signalLineWidth, + chart); + technicalIndicatorsRenderer._setSeriesProperties(indicator, 'CenterLine', + indicator.centerLineColor, indicator.centerLineWidth, chart, isLine); technicalIndicatorsRenderer._setSeriesRange(signalCollection, indicator, xValues, technicalIndicatorsRenderer._targetSeriesRenderers[0]); technicalIndicatorsRenderer._setSeriesRange( diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/rsi_indicator.dart b/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/rsi_indicator.dart index 86e54ba60..cdd03ca1e 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/rsi_indicator.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/rsi_indicator.dart @@ -9,6 +9,7 @@ part of charts; /// ///The [upperLineColor] property is used to define the color for the line that indicates [overbought] region, and ///the [lowerLineColor] property is used to define the color for the line that indicates [oversold] region. +@immutable class RsiIndicator extends TechnicalIndicators { /// Creating an argument constructor of RsiIndicator class. RsiIndicator( @@ -36,7 +37,8 @@ class RsiIndicator extends TechnicalIndicators { this.upperLineColor = Colors.red, this.upperLineWidth = 2, this.lowerLineColor = Colors.green, - this.lowerLineWidth = 2}) + this.lowerLineWidth = 2, + ChartIndicatorRenderCallback? onRenderDetailsUpdate}) : super( isVisible: isVisible, xAxisName: xAxisName, @@ -55,7 +57,8 @@ class RsiIndicator extends TechnicalIndicators { legendItemText: legendItemText, signalLineColor: signalLineColor, signalLineWidth: signalLineWidth, - period: period); + period: period, + onRenderDetailsUpdate: onRenderDetailsUpdate); ///ShowZones boolean value for RSI indicator /// @@ -176,46 +179,99 @@ class RsiIndicator extends TechnicalIndicators { ///``` final double lowerLineWidth; + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is RsiIndicator && + other.isVisible == isVisible && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.seriesName == seriesName && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.highValueMapper == highValueMapper && + other.lowValueMapper == lowValueMapper && + other.closeValueMapper == closeValueMapper && + other.period == period && + other.name == name && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.signalLineColor == signalLineColor && + other.signalLineWidth == signalLineWidth && + other.showZones == showZones && + other.overbought == overbought && + other.oversold == oversold && + other.upperLineColor == upperLineColor && + other.upperLineWidth == upperLineWidth && + other.lowerLineColor == lowerLineColor && + other.lowerLineWidth == lowerLineWidth; + } + + @override + int get hashCode { + final List values = [ + isVisible, + xAxisName, + yAxisName, + seriesName, + dashArray, + animationDuration, + dataSource, + xValueMapper, + highValueMapper, + lowValueMapper, + closeValueMapper, + name, + isVisibleInLegend, + legendIconType, + legendItemText, + signalLineColor, + signalLineWidth, + period, + showZones, + overbought, + oversold, + upperLineColor, + upperLineWidth, + lowerLineColor, + lowerLineWidth + ]; + return hashList(values); + } + /// To initialise indicators collections // ignore:unused_element void _initSeriesCollection( RsiIndicator indicator, SfCartesianChart chart, TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { - // Decides the type of renderer class to be used - final bool isLine = true; technicalIndicatorsRenderer._targetSeriesRenderers = []; - technicalIndicatorsRenderer._setSeriesProperties( - indicator, - indicator.name ?? 'RSI', - indicator.signalLineColor, - indicator.signalLineWidth, - chart); - if (indicator.showZones == true) { - technicalIndicatorsRenderer._setSeriesProperties(indicator, 'UpperLine', - indicator.upperLineColor, indicator.upperLineWidth, chart, isLine); - technicalIndicatorsRenderer._setSeriesProperties(indicator, 'LowerLine', - indicator.lowerLineColor, indicator.lowerLineWidth, chart, isLine); - } } /// To initialise data source of technical indicators // ignore:unused_element - void _initDataSource(RsiIndicator indicator, - TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { + void _initDataSource( + RsiIndicator indicator, + TechnicalIndicatorsRenderer technicalIndicatorsRenderer, + SfCartesianChart chart, + ) { final List> signalCollection = - >[]; - final List> lowerCollection = - >[]; - final List> upperCollection = - >[]; - final CartesianSeriesRenderer signalSeriesRenderer = - technicalIndicatorsRenderer._targetSeriesRenderers[0]; - final List> validData = - technicalIndicatorsRenderer._dataPoints!; - final List xValues = []; - final List signalXValues = []; + >[], + lowerCollection = >[], + upperCollection = >[], + validData = technicalIndicatorsRenderer._dataPoints!; + + final List xValues = [], signalXValues = []; if (validData.isNotEmpty && validData.length >= indicator.period && @@ -226,20 +282,16 @@ class RsiIndicator extends TechnicalIndicators { validData[i].x, indicator.overbought, validData[i], - technicalIndicatorsRenderer._targetSeriesRenderers[1], upperCollection.length)); lowerCollection.add(technicalIndicatorsRenderer._getDataPoint( validData[i].x, indicator.oversold, validData[i], - technicalIndicatorsRenderer._targetSeriesRenderers[2], lowerCollection.length)); xValues.add(validData[i].x); } } - num prevClose = validData[0].close ?? 0; - num gain = 0; - num loss = 0; + num prevClose = validData[0].close ?? 0, gain = 0, loss = 0; for (int i = 1; i <= indicator.period; i++) { final num close = validData[i].close ?? 0.0; if (close > prevClose) { @@ -256,7 +308,6 @@ class RsiIndicator extends TechnicalIndicators { validData[indicator.period].x, 100 - (100 / (1 + (gain / loss))), validData[indicator.period], - signalSeriesRenderer, signalCollection.length)); signalXValues.add(validData[indicator.period].x); @@ -277,13 +328,29 @@ class RsiIndicator extends TechnicalIndicators { validData[j].x, 100 - (100 / (1 + (gain / loss))), validData[j], - signalSeriesRenderer, signalCollection.length)); signalXValues.add(validData[j].x); } } } technicalIndicatorsRenderer._renderPoints = signalCollection; + // Decides the type of renderer class to be used + const bool isLine = true; + // final CartesianSeriesRenderer signalSeriesRenderer = + // technicalIndicatorsRenderer._targetSeriesRenderers[0]; + technicalIndicatorsRenderer._setSeriesProperties( + indicator, + indicator.name ?? 'RSI', + indicator.signalLineColor, + indicator.signalLineWidth, + chart); + if (indicator.showZones == true) { + technicalIndicatorsRenderer._setSeriesProperties(indicator, 'UpperLine', + indicator.upperLineColor, indicator.upperLineWidth, chart, isLine); + technicalIndicatorsRenderer._setSeriesProperties(indicator, 'LowerLine', + indicator.lowerLineColor, indicator.lowerLineWidth, chart, isLine); + } + technicalIndicatorsRenderer._setSeriesRange(signalCollection, indicator, signalXValues, technicalIndicatorsRenderer._targetSeriesRenderers[0]); if (indicator.showZones) { diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/sma_indicator.dart b/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/sma_indicator.dart index ce89a5935..d04944a04 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/sma_indicator.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/sma_indicator.dart @@ -6,30 +6,32 @@ part of charts; /// then dividing the total by the number of time periods in the calculation average. /// /// It also has a [valueField] property. Based on this property, the indicator will be rendered. +@immutable class SmaIndicator extends TechnicalIndicators { /// Creating an argument constructor of SmaIndicator class. - SmaIndicator({ - bool? isVisible, - String? xAxisName, - String? yAxisName, - String? seriesName, - List? dashArray, - double? animationDuration, - List? dataSource, - ChartValueMapper? xValueMapper, - ChartValueMapper? highValueMapper, - ChartValueMapper? lowValueMapper, - ChartValueMapper? openValueMapper, - ChartValueMapper? closeValueMapper, - String? name, - bool? isVisibleInLegend, - LegendIconType? legendIconType, - String? legendItemText, - Color? signalLineColor, - double? signalLineWidth, - int? period, - String? valueField, - }) : valueField = (valueField ?? 'close').toLowerCase(), + SmaIndicator( + {bool? isVisible, + String? xAxisName, + String? yAxisName, + String? seriesName, + List? dashArray, + double? animationDuration, + List? dataSource, + ChartValueMapper? xValueMapper, + ChartValueMapper? highValueMapper, + ChartValueMapper? lowValueMapper, + ChartValueMapper? openValueMapper, + ChartValueMapper? closeValueMapper, + String? name, + bool? isVisibleInLegend, + LegendIconType? legendIconType, + String? legendItemText, + Color? signalLineColor, + double? signalLineWidth, + int? period, + String? valueField, + ChartIndicatorRenderCallback? onRenderDetailsUpdate}) + : valueField = (valueField ?? 'close').toLowerCase(), super( isVisible: isVisible, xAxisName: xAxisName, @@ -49,7 +51,8 @@ class SmaIndicator extends TechnicalIndicators { legendItemText: legendItemText, signalLineColor: signalLineColor, signalLineWidth: signalLineWidth, - period: period); + period: period, + onRenderDetailsUpdate: onRenderDetailsUpdate); ///ValueField value for sma indicator. /// @@ -70,6 +73,65 @@ class SmaIndicator extends TechnicalIndicators { ///``` final String valueField; + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is SmaIndicator && + other.isVisible == isVisible && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.seriesName == seriesName && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.highValueMapper == highValueMapper && + other.lowValueMapper == lowValueMapper && + other.openValueMapper == openValueMapper && + other.closeValueMapper == closeValueMapper && + other.period == period && + other.name == name && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.signalLineColor == signalLineColor && + other.signalLineWidth == signalLineWidth && + other.valueField == valueField; + } + + @override + int get hashCode { + final List values = [ + isVisible, + xAxisName, + yAxisName, + seriesName, + dashArray, + animationDuration, + dataSource, + xValueMapper, + highValueMapper, + lowValueMapper, + openValueMapper, + closeValueMapper, + name, + isVisibleInLegend, + legendIconType, + legendItemText, + signalLineColor, + signalLineWidth, + valueField, + period + ]; + return hashList(values); + } + /// To initialise indicators collections // ignore:unused_element void _initSeriesCollection( @@ -78,18 +140,15 @@ class SmaIndicator extends TechnicalIndicators { TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { technicalIndicatorsRenderer._targetSeriesRenderers = []; - technicalIndicatorsRenderer._setSeriesProperties( - indicator, - indicator.name ?? 'SMA', - indicator.signalLineColor, - indicator.signalLineWidth, - chart); } /// To initialise data source of technical indicators // ignore:unused_element - void _initDataSource(SmaIndicator indicator, - TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { + void _initDataSource( + SmaIndicator indicator, + TechnicalIndicatorsRenderer technicalIndicatorsRenderer, + SfCartesianChart chart, + ) { final List> smaPoints = >[]; final List> points = @@ -98,12 +157,9 @@ class SmaIndicator extends TechnicalIndicators { CartesianChartPoint point; if (points.isNotEmpty) { final List> validData = points; - final CartesianSeriesRenderer signalSeriesRenderer = - technicalIndicatorsRenderer._targetSeriesRenderers[0]; if (validData.length >= indicator.period && indicator.period > 0) { - num average = 0; - num sum = 0; + num average = 0, sum = 0; for (int i = 0; i < indicator.period; i++) { sum += technicalIndicatorsRenderer._getFieldValue( @@ -115,7 +171,6 @@ class SmaIndicator extends TechnicalIndicators { validData[indicator.period - 1].x, average, validData[indicator.period - 1], - signalSeriesRenderer, smaPoints.length); smaPoints.add(point); xValues.add(point.x); @@ -128,17 +183,21 @@ class SmaIndicator extends TechnicalIndicators { validData, index, valueField); average = sum / indicator.period; point = technicalIndicatorsRenderer._getDataPoint( - validData[index].x, - average, - validData[index], - signalSeriesRenderer, - smaPoints.length); + validData[index].x, average, validData[index], smaPoints.length); smaPoints.add(point); xValues.add(point.x); index++; } } technicalIndicatorsRenderer._renderPoints = smaPoints; + technicalIndicatorsRenderer._setSeriesProperties( + indicator, + indicator.name ?? 'SMA', + indicator.signalLineColor, + indicator.signalLineWidth, + chart); + // final CartesianSeriesRenderer signalSeriesRenderer = + // technicalIndicatorsRenderer._targetSeriesRenderers[0]; technicalIndicatorsRenderer._setSeriesRange( smaPoints, indicator, xValues); } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/stochastic_indicator.dart b/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/stochastic_indicator.dart index 83dc245d9..8a2e954e2 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/stochastic_indicator.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/stochastic_indicator.dart @@ -7,6 +7,7 @@ part of charts; /// /// In this indicator [upperLineColor], [lowerLineColor] and [periodLineColor] property are used to define the color for /// the Stochastic indicator lines. +@immutable class StochasticIndicator extends TechnicalIndicators { /// Creating an argument constructor of StochasticIndicator class. StochasticIndicator( @@ -39,7 +40,8 @@ class StochasticIndicator extends TechnicalIndicators { this.periodLineColor = Colors.yellow, this.periodLineWidth = 2, this.kPeriod = 3, - this.dPeriod = 5}) + this.dPeriod = 5, + ChartIndicatorRenderCallback? onRenderDetailsUpdate}) : super( isVisible: isVisible, xAxisName: xAxisName, @@ -59,7 +61,8 @@ class StochasticIndicator extends TechnicalIndicators { legendItemText: legendItemText, signalLineColor: signalLineColor, signalLineWidth: signalLineWidth, - period: period); + period: period, + onRenderDetailsUpdate: onRenderDetailsUpdate); ///ShowZones boolean value for Stochastic indicator /// @@ -248,46 +251,109 @@ class StochasticIndicator extends TechnicalIndicators { ///``` final num dPeriod; + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is StochasticIndicator && + other.isVisible == isVisible && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.seriesName == seriesName && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.highValueMapper == highValueMapper && + other.lowValueMapper == lowValueMapper && + other.openValueMapper == openValueMapper && + other.closeValueMapper == closeValueMapper && + other.period == period && + other.name == name && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.signalLineColor == signalLineColor && + other.signalLineWidth == signalLineWidth && + other.showZones == showZones && + other.overbought == overbought && + other.oversold == oversold && + other.upperLineColor == upperLineColor && + other.upperLineWidth == upperLineWidth && + other.lowerLineColor == lowerLineColor && + other.lowerLineWidth == lowerLineWidth && + other.periodLineColor == periodLineColor && + other.periodLineWidth == periodLineWidth && + other.kPeriod == kPeriod && + other.dPeriod == dPeriod; + } + + @override + int get hashCode { + final List values = [ + isVisible, + xAxisName, + yAxisName, + seriesName, + dashArray, + animationDuration, + dataSource, + xValueMapper, + highValueMapper, + lowValueMapper, + openValueMapper, + closeValueMapper, + name, + isVisibleInLegend, + legendIconType, + legendItemText, + signalLineColor, + signalLineWidth, + period, + showZones, + overbought, + oversold, + upperLineColor, + upperLineWidth, + lowerLineColor, + lowerLineWidth, + periodLineColor, + periodLineWidth, + kPeriod, + dPeriod + ]; + return hashList(values); + } + /// To initialise indicators collections // ignore:unused_element void _initSeriesCollection( StochasticIndicator indicator, SfCartesianChart chart, TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { - // Decides the type of renderer class to be used - final bool isLine = true; technicalIndicatorsRenderer._targetSeriesRenderers = []; - technicalIndicatorsRenderer._setSeriesProperties( - indicator, - indicator.name ?? 'Stocastic', - indicator.signalLineColor, - indicator.signalLineWidth, - chart); - technicalIndicatorsRenderer._setSeriesProperties(indicator, 'PeriodLine', - indicator.periodLineColor, indicator.periodLineWidth, chart, isLine); - if (showZones) { - technicalIndicatorsRenderer._setSeriesProperties(indicator, 'UpperLine', - indicator.upperLineColor, indicator.upperLineWidth, chart, isLine); - technicalIndicatorsRenderer._setSeriesProperties(indicator, 'LowerLine', - indicator.lowerLineColor, indicator.lowerLineWidth, chart, isLine); - } } /// To initialise data source of technical indicators // ignore:unused_element - void _initDataSource(StochasticIndicator indicator, - TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { + void _initDataSource( + StochasticIndicator indicator, + TechnicalIndicatorsRenderer technicalIndicatorsRenderer, + SfCartesianChart chart, + ) { List> signalCollection = - >[]; + >[], + source = >[], + periodCollection = >[]; final List> lowerCollection = - >[]; - final List> upperCollection = - >[]; - List> source = - >[]; - List> periodCollection = - >[]; + >[], + upperCollection = >[]; final List> validData = technicalIndicatorsRenderer._dataPoints!; final List xValues = []; @@ -301,41 +367,47 @@ class StochasticIndicator extends TechnicalIndicators { validData[i].x, indicator.overbought, validData[i], - technicalIndicatorsRenderer._targetSeriesRenderers[2], upperCollection.length)); lowerCollection.add(technicalIndicatorsRenderer._getDataPoint( validData[i].x, indicator.oversold, validData[i], - technicalIndicatorsRenderer._targetSeriesRenderers[3], lowerCollection.length)); xValues.add(validData[i].x); } } - source = _calculatePeriod( - indicator.period, - indicator.kPeriod.toInt(), - validData, - technicalIndicatorsRenderer._targetSeriesRenderers[1], - technicalIndicatorsRenderer); - collection = _stochasticCalculation( - indicator.period, - indicator.kPeriod.toInt(), - source, - technicalIndicatorsRenderer._targetSeriesRenderers[1], - technicalIndicatorsRenderer); + source = _calculatePeriod(indicator.period, indicator.kPeriod.toInt(), + validData, technicalIndicatorsRenderer); + collection = _stochasticCalculation(indicator.period, + indicator.kPeriod.toInt(), source, technicalIndicatorsRenderer); periodCollection = collection[0]; periodX = collection[1]; collection = _stochasticCalculation( (indicator.period + indicator.kPeriod - 1).toInt(), indicator.dPeriod.toInt(), source, - technicalIndicatorsRenderer._targetSeriesRenderers[0], technicalIndicatorsRenderer); signalCollection = collection[0]; signalX = collection[1]; } technicalIndicatorsRenderer._renderPoints = signalCollection; + technicalIndicatorsRenderer._stochasticperiod = periodCollection; + // Decides the type of renderer class to be used + const bool isLine = true; + technicalIndicatorsRenderer._setSeriesProperties( + indicator, + indicator.name ?? 'Stocastic', + indicator.signalLineColor, + indicator.signalLineWidth, + chart); + technicalIndicatorsRenderer._setSeriesProperties(indicator, 'PeriodLine', + indicator.periodLineColor, indicator.periodLineWidth, chart, isLine); + if (showZones) { + technicalIndicatorsRenderer._setSeriesProperties(indicator, 'UpperLine', + indicator.upperLineColor, indicator.upperLineWidth, chart, isLine); + technicalIndicatorsRenderer._setSeriesProperties(indicator, 'LowerLine', + indicator.lowerLineColor, indicator.lowerLineWidth, chart, isLine); + } technicalIndicatorsRenderer._setSeriesRange(signalCollection, indicator, signalX, technicalIndicatorsRenderer._targetSeriesRenderers[0]); technicalIndicatorsRenderer._setSeriesRange(periodCollection, indicator, @@ -353,15 +425,13 @@ class StochasticIndicator extends TechnicalIndicators { int period, int kPeriod, List> data, - CartesianSeriesRenderer sourceSeriesRenderer, TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { final List> pointCollection = >[]; final List xValues = []; if (data.length >= period + kPeriod && kPeriod > 0) { final int count = period + (kPeriod - 1); - final List temp = []; - final List values = []; + final List temp = [], values = []; for (int i = 0; i < data.length; i++) { final num value = data[i].y; temp.add(value); @@ -382,11 +452,7 @@ class StochasticIndicator extends TechnicalIndicators { for (int i = 0; i < data.length; i++) { if (!(i < len)) { pointCollection.add(technicalIndicatorsRenderer._getDataPoint( - data[i].x, - values[i - len], - data[i], - sourceSeriesRenderer, - pointCollection.length)); + data[i].x, values[i - len], data[i], pointCollection.length)); xValues.add(data[i].x); data[i].y = values[i - len]; } @@ -401,7 +467,6 @@ class StochasticIndicator extends TechnicalIndicators { int period, int kPeriod, List> data, - CartesianSeriesRenderer seriesRenderer, TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { // This has been null before final List lowValue = List.filled(data.length, -1); @@ -416,13 +481,12 @@ class StochasticIndicator extends TechnicalIndicators { closeValue[j] = data[j].close ?? 0; } if (data.length > period) { - final List mins = []; - final List maxs = []; + final List mins = [], maxs = []; for (int i = 0; i < period - 1; ++i) { maxs.add(0); mins.add(0); - modifiedSource.add(technicalIndicatorsRenderer._getDataPoint(data[i].x, - data[i].close, data[i], seriesRenderer, modifiedSource.length)); + modifiedSource.add(technicalIndicatorsRenderer._getDataPoint( + data[i].x, data[i].close, data[i], modifiedSource.length)); } num? min, max; for (int i = period - 1; i < data.length; ++i) { @@ -439,16 +503,11 @@ class StochasticIndicator extends TechnicalIndicators { } for (int i = period - 1; i < data.length; ++i) { - num top = 0; - num bottom = 0; + num top = 0, bottom = 0; top += closeValue[i] - mins[i]; bottom += maxs[i] - mins[i]; modifiedSource.add(technicalIndicatorsRenderer._getDataPoint( - data[i].x, - (top / bottom) * 100, - data[i], - seriesRenderer, - modifiedSource.length)); + data[i].x, (top / bottom) * 100, data[i], modifiedSource.length)); } } return modifiedSource; diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/technical_indicator.dart b/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/technical_indicator.dart index ad37191b2..911585f37 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/technical_indicator.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/technical_indicator.dart @@ -24,6 +24,7 @@ class TechnicalIndicators { ChartValueMapper? openValueMapper, ChartValueMapper? closeValueMapper, this.name, + this.onRenderDetailsUpdate, bool? isVisibleInLegend, LegendIconType? legendIconType, this.legendItemText, @@ -365,9 +366,25 @@ class TechnicalIndicators { ///} ///``` final int period; + + /// Callback which gets called while rendering the indicators + ///```dart + ///Widget build(BuildContext context) { + /// return Container( + /// child: SfCartesianChart( + /// indicators: >[ + /// SmaIndicator( + /// onRenderDetailsUpdate: (IndicatorRenderParams params) { + /// return ChartIndicator(Colors.cyan, 3.0, [5,5]); + /// }, + /// )], + /// )); + ///} + ///``` + final ChartIndicatorRenderCallback? onRenderDetailsUpdate; } -///Technical indicaor renderer class for mutable fields and methods +///Technical indicator renderer class for mutable fields and methods class TechnicalIndicatorsRenderer { /// Creates an argument constructor for TechnicalIndicator renderer class TechnicalIndicatorsRenderer(this._technicalIndicatorRenderer); @@ -380,6 +397,7 @@ class TechnicalIndicatorsRenderer { bool _isIndicator = true; final String _seriesType = 'indicator'; late String _indicatorType; + //ignore: unused_field late int _index; //ignore: prefer_final_fields @@ -394,13 +412,38 @@ class TechnicalIndicatorsRenderer { List> _renderPoints = >[]; + ///used for events + //ignore: prefer_final_fields + List>? _bollingerUpper = + >[]; + + ///used for events + //ignore: prefer_final_fields + List>? _bollingerLower = + >[]; + + ///used for events + //ignore: prefer_final_fields + List>? _macdLine = + >[]; + + ///used for events + //ignore: prefer_final_fields + List>? _macdHistogram = + >[]; + + ///used for events + //ignore: prefer_final_fields + List>? _stochasticperiod = + >[]; + + ///used for events + //ignore: prefer_final_fields + double? _momentumCenterLineValue; + /// To get and return CartesianChartPoint CartesianChartPoint _getDataPoint( - dynamic x, - dynamic y, - CartesianChartPoint sourcePoint, - CartesianSeriesRenderer seriesRenderer, - int index, + dynamic x, num y, CartesianChartPoint sourcePoint, int index, [TechnicalIndicators? indicator]) { final CartesianChartPoint point = CartesianChartPoint(x, y); @@ -408,9 +451,11 @@ class TechnicalIndicatorsRenderer { point.index = index; point.yValue = y; point.isVisible = true; - if (indicator is MacdIndicator && seriesRenderer._seriesType == 'column') { + if (indicator is MacdIndicator && + (indicator.macdType == MacdType.histogram || + indicator.macdType == MacdType.both)) { final MacdIndicator _indicator = indicator; - point.pointColorMapper = point.yValue >= 0 + point.pointColorMapper = point.yValue >= 0 == true ? _indicator.histogramPositiveColor : _indicator.histogramNegativeColor; } @@ -418,13 +463,8 @@ class TechnicalIndicatorsRenderer { } /// To get chart point of range type series - CartesianChartPoint _getRangePoint( - dynamic x, - num high, - num low, - CartesianChartPoint sourcePoint, - CartesianSeries? series, - int index, + CartesianChartPoint _getRangePoint(dynamic x, num high, num low, + CartesianChartPoint sourcePoint, int index, //ignore: unused_element [TechnicalIndicators? indicator]) { final CartesianChartPoint point = @@ -440,25 +480,47 @@ class TechnicalIndicatorsRenderer { /// To set properties of technical indicators void _setSeriesProperties(TechnicalIndicators indicator, String name, Color color, double width, SfCartesianChart chart, - [isLine = false, isRangeArea = false, isHistogram = false]) { + [bool isLine = false, + bool isRangeArea = false, + bool isHistogram = false]) { List? _dashArray; - if (chart.onIndicatorRender != null && - !isRangeArea && - !isHistogram && - !isLine) { - final IndicatorRenderArgs indicatorArgs = IndicatorRenderArgs( - indicator, _index, chart.indicators[_index].seriesName, _dataPoints); - indicatorArgs.indicatorName = name; - indicatorArgs.signalLineColor = color; - indicatorArgs.signalLineWidth = width; - indicatorArgs.lineDashArray = indicator.dashArray; - chart.onIndicatorRender!(indicatorArgs); - color = indicatorArgs.signalLineColor; - width = indicatorArgs.signalLineWidth; - name = indicatorArgs.indicatorName; - _dashArray = indicatorArgs.lineDashArray; + if (indicator.onRenderDetailsUpdate != null && + isRangeArea == false && + isHistogram == false && + isLine == false) { + TechnicalIndicatorRenderDetails indicators; + if (indicator is BollingerBandIndicator) { + final BollingerBandIndicatorRenderParams indicatorRenderParams = + BollingerBandIndicatorRenderParams(_bollingerUpper, _bollingerLower, + _renderPoints, name, width, color, indicator.dashArray); + indicators = indicator.onRenderDetailsUpdate!(indicatorRenderParams); + } else if (indicator is MomentumIndicator) { + final MomentumIndicatorRenderParams indicatorRenderParams = + MomentumIndicatorRenderParams(_momentumCenterLineValue, + _renderPoints, name, width, color, indicator.dashArray); + indicators = indicator.onRenderDetailsUpdate!(indicatorRenderParams); + } else if (indicator is StochasticIndicator) { + final StochasticIndicatorRenderParams indicatorRenderParams = + StochasticIndicatorRenderParams(_stochasticperiod, _renderPoints, + name, width, color, indicator.dashArray); + indicators = indicator.onRenderDetailsUpdate!(indicatorRenderParams); + } else if (indicator is MacdIndicator) { + final MacdIndicatorRenderParams indicatorRenderParams = + MacdIndicatorRenderParams(_macdLine, _macdHistogram, _renderPoints, + name, width, color, indicator.dashArray); + indicators = indicator.onRenderDetailsUpdate!(indicatorRenderParams); + } else { + final IndicatorRenderParams indicatorRenderParams = + IndicatorRenderParams( + _renderPoints, name, width, color, indicator.dashArray); + indicators = indicator.onRenderDetailsUpdate!(indicatorRenderParams); + } + + color = indicators.signalLineColor ?? color; + width = indicators.signalLineWidth ?? width; + _dashArray = indicators.signalLineDashArray ?? indicator.dashArray; } - final CartesianSeries series = isRangeArea + final CartesianSeries series = isRangeArea == true ? RangeAreaSeries( name: name, color: color, @@ -468,11 +530,14 @@ class TechnicalIndicatorsRenderer { animationDuration: indicator.animationDuration, yAxisName: indicator.yAxisName, enableTooltip: false, + //ignore: always_specify_types xValueMapper: (dynamic, _) => null, + //ignore: always_specify_types highValueMapper: (dynamic, _) => null, + //ignore: always_specify_types lowValueMapper: (dynamic, _) => null, - dataSource: []) - : (isHistogram + dataSource: []) + : (isHistogram == true ? ColumnSeries( name: name, color: color, @@ -480,9 +545,11 @@ class TechnicalIndicatorsRenderer { xAxisName: indicator.xAxisName, animationDuration: indicator.animationDuration, yAxisName: indicator.yAxisName, + //ignore: always_specify_types xValueMapper: (dynamic, _) => null, + //ignore: always_specify_types yValueMapper: (dynamic, _) => null, - dataSource: []) + dataSource: []) : LineSeries( name: name, color: color, @@ -491,17 +558,20 @@ class TechnicalIndicatorsRenderer { xAxisName: indicator.xAxisName, animationDuration: indicator.animationDuration, yAxisName: indicator.yAxisName, + //ignore: always_specify_types xValueMapper: (dynamic, _) => null, + //ignore: always_specify_types yValueMapper: (dynamic, _) => null, - dataSource: [])); - final CartesianSeriesRenderer seriesRenderer = isRangeArea + dataSource: [])); + final CartesianSeriesRenderer seriesRenderer = isRangeArea == true ? RangeAreaSeriesRenderer() - : (isHistogram ? ColumnSeriesRenderer() : LineSeriesRenderer()); + : (isHistogram == true ? ColumnSeriesRenderer() : LineSeriesRenderer()); seriesRenderer._series = series; seriesRenderer._visible = _visible; seriesRenderer._chart = chart; - seriesRenderer._seriesType = - isRangeArea ? 'rangearea' : (isHistogram ? 'column' : 'line'); + seriesRenderer._seriesType = isRangeArea == true + ? 'rangearea' + : (isHistogram == true ? 'column' : 'line'); seriesRenderer._isIndicator = true; seriesRenderer._seriesName = _name; _targetSeriesRenderers.add(seriesRenderer); @@ -521,19 +591,19 @@ class TechnicalIndicatorsRenderer { } /// To get the value field value of technical indicators - num _getFieldValue( - List?> point, int itr, String valueField) { + num _getFieldValue(List?> dataPoints, int index, + String valueField) { num? val; if (valueField == 'low') { - val = point[itr]?.low; + val = dataPoints[index]?.low; } else if (valueField == 'high') { - val = point[itr]?.high; + val = dataPoints[index]?.high; } else if (valueField == 'open') { - val = point[itr]?.open; + val = dataPoints[index]?.open; } else if (valueField == 'y') { - val = point[itr]?.y; + val = dataPoints[index]?.y; } else { - val = point[itr]?.close; + val = dataPoints[index]?.close; } ///ignore: unnecessary_statements diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/tma_indicator.dart b/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/tma_indicator.dart index 44777f42f..62e137aaf 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/tma_indicator.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/technical_indicators/tma_indicator.dart @@ -6,6 +6,7 @@ part of charts; ///The TMA shows the average (or average) price of an asset over a specified number of data points over a period of time. /// ///The technical indicator is rendered on the basis of the [valueField] property. +@immutable class TmaIndicator extends TechnicalIndicators { /// Creating an argument constructor of TmaIndicator class. TmaIndicator( @@ -28,7 +29,8 @@ class TmaIndicator extends TechnicalIndicators { Color? signalLineColor, double? signalLineWidth, int? period, - String? valueField}) + String? valueField, + ChartIndicatorRenderCallback? onRenderDetailsUpdate}) : valueField = (valueField ?? 'close').toLowerCase(), super( isVisible: isVisible, @@ -49,7 +51,8 @@ class TmaIndicator extends TechnicalIndicators { legendItemText: legendItemText, signalLineColor: signalLineColor, signalLineWidth: signalLineWidth, - period: period); + period: period, + onRenderDetailsUpdate: onRenderDetailsUpdate); ///ValueField value for tma indicator. /// @@ -70,6 +73,65 @@ class TmaIndicator extends TechnicalIndicators { ///``` final String valueField; + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is TmaIndicator && + other.isVisible == isVisible && + other.xAxisName == xAxisName && + other.yAxisName == yAxisName && + other.seriesName == seriesName && + other.dashArray == dashArray && + other.animationDuration == animationDuration && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.highValueMapper == highValueMapper && + other.lowValueMapper == lowValueMapper && + other.openValueMapper == openValueMapper && + other.closeValueMapper == closeValueMapper && + other.period == period && + other.name == name && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.legendItemText == legendItemText && + other.signalLineColor == signalLineColor && + other.signalLineWidth == signalLineWidth && + other.valueField == valueField; + } + + @override + int get hashCode { + final List values = [ + isVisible, + xAxisName, + yAxisName, + seriesName, + dashArray, + animationDuration, + dataSource, + xValueMapper, + highValueMapper, + lowValueMapper, + openValueMapper, + closeValueMapper, + name, + isVisibleInLegend, + legendIconType, + legendItemText, + signalLineColor, + signalLineWidth, + valueField, + period + ]; + return hashList(values); + } + /// To initialise indicators collections // ignore:unused_element void _initSeriesCollection( @@ -78,24 +140,22 @@ class TmaIndicator extends TechnicalIndicators { TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { technicalIndicatorsRenderer._targetSeriesRenderers = []; - technicalIndicatorsRenderer._setSeriesProperties( - indicator, - indicator.name ?? 'TMA', - indicator.signalLineColor, - indicator.signalLineWidth, - chart); } /// To initialise data source of technical indicators // ignore:unused_element - void _initDataSource(TechnicalIndicators indicator, - TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { + void _initDataSource( + TechnicalIndicators indicator, + TechnicalIndicatorsRenderer technicalIndicatorsRenderer, + SfCartesianChart chart, + ) { final List> validData = technicalIndicatorsRenderer._dataPoints!; if (validData.isNotEmpty && validData.length > indicator.period && indicator is TmaIndicator) { - _calculateTMAPoints(indicator, validData, technicalIndicatorsRenderer); + _calculateTMAPoints( + indicator, validData, technicalIndicatorsRenderer, chart); } } @@ -103,7 +163,8 @@ class TmaIndicator extends TechnicalIndicators { void _calculateTMAPoints( TmaIndicator indicator, List> validData, - TechnicalIndicatorsRenderer technicalIndicatorsRenderer) { + TechnicalIndicatorsRenderer technicalIndicatorsRenderer, + SfCartesianChart chart) { final num period = indicator.period; final List> points = >[]; @@ -112,8 +173,6 @@ class TmaIndicator extends TechnicalIndicators { if (validData.isNotEmpty && validData.length >= indicator.period && period > 0) { - final CartesianSeriesRenderer signalSeriesRenderer = - technicalIndicatorsRenderer._targetSeriesRenderers[0]; //prepare data if (validData.isNotEmpty && validData.length >= period) { num sum = 0; @@ -140,7 +199,7 @@ class TmaIndicator extends TechnicalIndicators { validData, j, valueField); } sum = sum / (k + 1); - smaValues = _splice(smaValues, k, 0, sum); + smaValues = _splice(smaValues, k, sum); } index = indicator.period; @@ -151,11 +210,7 @@ class TmaIndicator extends TechnicalIndicators { } sum = sum / indicator.period; point = technicalIndicatorsRenderer._getDataPoint( - validData[index - 1].x, - sum, - validData[index - 1], - signalSeriesRenderer, - points.length); + validData[index - 1].x, sum, validData[index - 1], points.length); points.add(point); xValues.add(point.x); index++; @@ -163,14 +218,19 @@ class TmaIndicator extends TechnicalIndicators { } } technicalIndicatorsRenderer._renderPoints = points; + technicalIndicatorsRenderer._setSeriesProperties( + indicator, + indicator.name ?? 'TMA', + indicator.signalLineColor, + indicator.signalLineWidth, + chart); + // final CartesianSeriesRenderer signalSeriesRenderer = + // technicalIndicatorsRenderer._targetSeriesRenderers[0]; technicalIndicatorsRenderer._setSeriesRange(points, indicator, xValues); } /// To return list of spliced values - List _splice(List list, int index, - //ignore: unused_element - [num? howMany, - num? elements]) { + List _splice(List list, int index, num? elements) { if (elements != null) { list.insertAll(index, [elements]); } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/trendlines/trendlines.dart b/packages/syncfusion_flutter_charts/lib/src/chart/trendlines/trendlines.dart index 865c4e251..f771246a1 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/trendlines/trendlines.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/trendlines/trendlines.dart @@ -374,6 +374,63 @@ class Trendline { ///} ///``` final int period; + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is Trendline && + other.enableTooltip == enableTooltip && + other.intercept == intercept && + other.name == name && + other.dashArray == dashArray && + other.color == color && + other.type == type && + other.backwardForecast == backwardForecast && + other.forwardForecast == forwardForecast && + other.opacity == opacity && + other.isVisible == isVisible && + other.width == width && + other.animationDuration == animationDuration && + other.valueField == valueField && + other.isVisibleInLegend == isVisibleInLegend && + other.legendIconType == legendIconType && + other.markerSettings == markerSettings && + other.polynomialOrder == polynomialOrder && + other.period == period; + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode { + final List values = [ + enableTooltip, + intercept, + name, + dashArray, + color, + type, + backwardForecast, + forwardForecast, + opacity, + isVisible, + width, + animationDuration, + valueField, + isVisibleInLegend, + legendIconType, + markerSettings, + polynomialOrder, + period + ]; + return hashList(values); + } } ///Trendline renderer class for mutable fields and methods @@ -454,7 +511,7 @@ class TrendlineRenderer { x1Linear = _increaseDateTimeForecast( seriesRenderer._xAxisRenderer as DateTimeAxisRenderer, xValues[0], - -(_trendline.backwardForecast)); + -_trendline.backwardForecast); x2Linear = _increaseDateTimeForecast( seriesRenderer._xAxisRenderer as DateTimeAxisRenderer, xValues[xValues.length - 1], @@ -483,17 +540,16 @@ class TrendlineRenderer { while (index < points.length) { final CartesianChartPoint point = points[index]; xValues.add(point.xValue ?? point.x); - if (!(seriesRenderer._series is RangeAreaSeries || - seriesRenderer._series is RangeColumnSeries || - seriesRenderer._series is HiloSeries || - seriesRenderer._series is HiloOpenCloseSeries || - seriesRenderer._series is CandleSeries)) { - yValues.add(point.yValue ?? point.y); - } else { - yValues.add(_trendline.valueField.toLowerCase() == 'low' - ? point.low - : point.high); - } + (!(seriesRenderer._series is RangeAreaSeries || + seriesRenderer._series is RangeColumnSeries || + seriesRenderer._series is HiloSeries || + seriesRenderer._series is HiloOpenCloseSeries || + seriesRenderer._series is CandleSeries)) + ? yValues.add(point.yValue ?? point.y) + : yValues.add(_trendline.valueField.toLowerCase() == 'low' + ? point.low + : point.high); + index++; } _slopeIntercept = _findSlopeIntercept(xValues, yValues, points); @@ -516,7 +572,7 @@ class TrendlineRenderer { x1 = _increaseDateTimeForecast( seriesRenderer._xAxisRenderer as DateTimeAxisRenderer, xValues[0], - -(_trendline.backwardForecast)); + -_trendline.backwardForecast); x2 = xValues[midPoint - 1]; x3 = _increaseDateTimeForecast( seriesRenderer._xAxisRenderer as DateTimeAxisRenderer, @@ -553,17 +609,16 @@ class TrendlineRenderer { while (index < points.length) { final CartesianChartPoint point = points[index]; xValues.add(point.xValue ?? point.x); - if (!(seriesRenderer._series is RangeAreaSeries || - seriesRenderer._series is RangeColumnSeries || - seriesRenderer._series is HiloSeries || - seriesRenderer._series is HiloOpenCloseSeries || - seriesRenderer._series is CandleSeries)) { - yValues.add(math.log(point.yValue ?? point.y)); - } else { - yValues.add(_trendline.valueField.toLowerCase() == 'low' - ? math.log(point.low) - : math.log(point.high)); - } + (!(seriesRenderer._series is RangeAreaSeries || + seriesRenderer._series is RangeColumnSeries || + seriesRenderer._series is HiloSeries || + seriesRenderer._series is HiloOpenCloseSeries || + seriesRenderer._series is CandleSeries)) + ? yValues.add(math.log(point.yValue ?? point.y)) + : yValues.add(_trendline.valueField.toLowerCase() == 'low' + ? math.log(point.low) + : math.log(point.high)); + index++; } _slopeIntercept = _findSlopeIntercept(xValues, yValues, points); @@ -586,7 +641,7 @@ class TrendlineRenderer { x1 = _increaseDateTimeForecast( seriesRenderer._xAxisRenderer as DateTimeAxisRenderer, xValues[0], - -(_trendline.backwardForecast)); + -_trendline.backwardForecast); x2 = xValues[midPoint - 1]; x3 = _increaseDateTimeForecast( seriesRenderer._xAxisRenderer as DateTimeAxisRenderer, @@ -631,17 +686,15 @@ class TrendlineRenderer { ? point.xValue : point.x; xValues.add(xVal); - if (!(seriesRenderer._series is RangeAreaSeries || - seriesRenderer._series is RangeColumnSeries || - seriesRenderer._series is HiloSeries || - seriesRenderer._series is HiloOpenCloseSeries || - seriesRenderer._series is CandleSeries)) { - yValues.add(math.log(point.yValue ?? point.y)); - } else { - yValues.add(_trendline.valueField.toLowerCase() == 'low' - ? math.log(point.low) - : math.log(point.high)); - } + (!(seriesRenderer._series is RangeAreaSeries || + seriesRenderer._series is RangeColumnSeries || + seriesRenderer._series is HiloSeries || + seriesRenderer._series is HiloOpenCloseSeries || + seriesRenderer._series is CandleSeries)) + ? yValues.add(math.log(point.yValue ?? point.y)) + : yValues.add(_trendline.valueField.toLowerCase() == 'low' + ? math.log(point.low) + : math.log(point.high)); index++; } _slopeIntercept = _findSlopeIntercept(xValues, yValues, points); @@ -664,7 +717,7 @@ class TrendlineRenderer { x1 = _increaseDateTimeForecast( seriesRenderer._xAxisRenderer as DateTimeAxisRenderer, xValues[0], - -(_trendline.backwardForecast)); + -_trendline.backwardForecast); x2 = xValues[midPoint - 1]; x3 = _increaseDateTimeForecast( seriesRenderer._xAxisRenderer as DateTimeAxisRenderer, @@ -710,17 +763,15 @@ class TrendlineRenderer { ? point.xValue : point.x; xLogValue.add(xVal); - if (!(seriesRenderer._series is RangeAreaSeries || - seriesRenderer._series is RangeColumnSeries || - seriesRenderer._series is HiloSeries || - seriesRenderer._series is HiloOpenCloseSeries || - seriesRenderer._series is CandleSeries)) { - yLogValue.add(point.yValue ?? point.y); - } else { - yLogValue.add(_trendline.valueField.toLowerCase() == 'low' - ? point.low - : point.high); - } + (!(seriesRenderer._series is RangeAreaSeries || + seriesRenderer._series is RangeColumnSeries || + seriesRenderer._series is HiloSeries || + seriesRenderer._series is HiloOpenCloseSeries || + seriesRenderer._series is CandleSeries)) + ? yLogValue.add(point.yValue ?? point.y) + : yLogValue.add(_trendline.valueField.toLowerCase() == 'low' + ? point.low + : point.high); index++; } _slopeIntercept = _findSlopeIntercept(xLogValue, yLogValue, points); @@ -775,7 +826,9 @@ class TrendlineRenderer { } } if (!_gaussJordanElimination(matrix, _polynomialSlopes!)) { - _polynomialSlopes = null; + //The trendline will not be generated if there is just one data point or if the x and y values are the same, + //for example (1,1), (1,1). So, the line was commented. And now marker alone will be rendered in this case. + // _polynomialSlopes = null; } pts = _getPoints(points, xValues, yValues, seriesRenderer); return pts; @@ -790,17 +843,15 @@ class TrendlineRenderer { while (index < points.length) { final CartesianChartPoint point = points[index]; xPolyValues.add(point.xValue ?? point.x); - if (!(seriesRenderer._series is RangeAreaSeries || - seriesRenderer._series is RangeColumnSeries || - seriesRenderer._series is HiloSeries || - seriesRenderer._series is HiloOpenCloseSeries || - seriesRenderer._series is CandleSeries)) { - yPolyValues.add(point.yValue ?? point.y); - } else { - yPolyValues.add(_trendline.valueField.toLowerCase() == 'low' - ? point.low - : point.high); - } + (!(seriesRenderer._series is RangeAreaSeries || + seriesRenderer._series is RangeColumnSeries || + seriesRenderer._series is HiloSeries || + seriesRenderer._series is HiloOpenCloseSeries || + seriesRenderer._series is CandleSeries)) + ? yPolyValues.add(point.yValue ?? point.y) + : yPolyValues.add(_trendline.valueField.toLowerCase() == 'low' + ? point.low + : point.high); index++; } _pointsData = @@ -822,11 +873,11 @@ class TrendlineRenderer { num x1 = 1; dynamic xVal; num yVal; - final dynamic _backwardForecast = + final num _backwardForecast = seriesRenderer._xAxisRenderer is DateTimeAxisRenderer ? _getForecastDate(seriesRenderer._xAxisRenderer!, false) : _trendline.backwardForecast; - final dynamic _forwardForecast = + final num _forwardForecast = seriesRenderer._xAxisRenderer is DateTimeAxisRenderer ? _getForecastDate(seriesRenderer._xAxisRenderer!, true) : _trendline.forwardForecast; @@ -877,8 +928,7 @@ class TrendlineRenderer { periods = max(2, periods); int? y; dynamic x; - int count; - int nullCount; + int count, nullCount; for (int index = 0; index < points.length - 1; index++) { y = count = nullCount = 0; for (int j = index; count < periods; j++) { @@ -901,25 +951,22 @@ class TrendlineRenderer { /// Setting the moving average range for trendline series void _setMovingAverageRange(List> points, CartesianSeriesRenderer seriesRenderer) { - final List xValues = []; + final List xValues = [], xAvgValues = []; final List yValues = []; - final List xAvgValues = []; for (int index = 0; index < points.length; index++) { final dynamic point = points[index]; xAvgValues.add(point.xValue ?? point.x); xValues.add(index + 1); - if (!(seriesRenderer._series is RangeAreaSeries || - seriesRenderer._series is RangeColumnSeries || - seriesRenderer._series is HiloSeries || - seriesRenderer._series is HiloOpenCloseSeries || - seriesRenderer._series is CandleSeries)) { - yValues.add(point.yValue ?? point.y); - } else { - yValues.add(_trendline.valueField.toLowerCase() == 'low' - ? point.low - : point.high); - } + (!(seriesRenderer._series is RangeAreaSeries || + seriesRenderer._series is RangeColumnSeries || + seriesRenderer._series is HiloSeries || + seriesRenderer._series is HiloOpenCloseSeries || + seriesRenderer._series is CandleSeries)) + ? yValues.add(point.yValue ?? point.y) + : yValues.add(_trendline.valueField.toLowerCase() == 'low' + ? point.low + : point.high); } _pointsData = getMovingAveragePoints(points, xAvgValues, yValues, seriesRenderer); @@ -928,16 +975,11 @@ class TrendlineRenderer { /// Setting the slope intercept for trendline series _SlopeIntercept _findSlopeIntercept(dynamic xValues, dynamic yValues, List> points) { - double xAvg = 0.0; - double yAvg = 0.0; - double xyAvg = 0.0; - double xxAvg = 0.0; - // double yyAvg = 0.0; + double xAvg = 0.0, yAvg = 0.0, xyAvg = 0.0, xxAvg = 0.0; int index = 0; - double slope = 0.0; - double intercept = 0.0; + double slope = 0.0, intercept = 0.0; while (index < points.length) { - if ((yValues[index]).isNaN) { + if ((yValues[index]).isNaN == true) { yValues[index] = (yValues[index - 1] + yValues[index + 1]) / 2; } xAvg += xValues[index]; @@ -964,15 +1006,13 @@ class TrendlineRenderer { } else { slope = ((points.length * xyAvg) - (xAvg * yAvg)) / ((points.length * xxAvg) - (xAvg * xAvg)); - if (_trendline.type == TrendlineType.exponential || - _trendline.type == TrendlineType.power) { - intercept = math.exp((yAvg - (slope * xAvg)) / points.length); - } else { - intercept = (yAvg - (slope * xAvg)) / points.length; - } + + intercept = (_trendline.type == TrendlineType.exponential || + _trendline.type == TrendlineType.power) + ? math.exp((yAvg - (slope * xAvg)) / points.length) + : (yAvg - (slope * xAvg)) / points.length; } - _SlopeIntercept _slopeIntercept; - _slopeIntercept = _SlopeIntercept(); + final _SlopeIntercept _slopeIntercept = _SlopeIntercept(); _slopeIntercept.slope = slope; _slopeIntercept.intercept = intercept; return _slopeIntercept; @@ -1075,8 +1115,7 @@ class TrendlineRenderer { List _getControlPoints(List _dataPoints, int index) { List yCoef = []; final List controlPoints = []; - final List xValues = []; - final List yValues = []; + final List xValues = [], yValues = []; for (int i = 0; i < _dataPoints.length; i++) { xValues.add(_dataPoints[i].dx); yValues.add(_dataPoints[i].dy); @@ -1095,27 +1134,43 @@ class TrendlineRenderer { switch (axis.intervalType) { case DateTimeIntervalType.years: dateTime = DateTime(dateTime.year + interval.floor(), dateTime.month, - dateTime.day, 0, 0, 0); + dateTime.day, 0, 0, 0, 0); break; case DateTimeIntervalType.months: dateTime = DateTime(dateTime.year, dateTime.month + interval.floor(), - dateTime.day, 0, 0, 0); + dateTime.day, 0, 0, 0, 0); break; case DateTimeIntervalType.days: dateTime = DateTime(dateTime.year, dateTime.month, - dateTime.day + interval.floor(), 0, 0, 0); + dateTime.day + interval.floor(), 0, 0, 0, 0); break; case DateTimeIntervalType.hours: dateTime = DateTime(dateTime.year, dateTime.month, dateTime.day, - dateTime.hour + interval.floor(), 0, 0); + dateTime.hour + interval.floor(), 0, 0, 0); break; case DateTimeIntervalType.minutes: dateTime = DateTime(dateTime.year, dateTime.month, dateTime.day, - dateTime.hour, dateTime.minute + interval.floor(), 0); + dateTime.hour, dateTime.minute + interval.floor(), 0, 0); break; case DateTimeIntervalType.seconds: - dateTime = DateTime(dateTime.year, dateTime.month, dateTime.day, - dateTime.hour, dateTime.minute, dateTime.second + interval.floor()); + dateTime = DateTime( + dateTime.year, + dateTime.month, + dateTime.day, + dateTime.hour, + dateTime.minute, + dateTime.second + interval.floor(), + 0); + break; + case DateTimeIntervalType.milliseconds: + dateTime = DateTime( + dateTime.year, + dateTime.month, + dateTime.day, + dateTime.hour, + dateTime.minute, + dateTime.second, + dateTime.millisecond + interval.floor()); break; case DateTimeIntervalType.auto: break; @@ -1137,15 +1192,13 @@ class TrendlineRenderer { int index1 = 0; while (index1 < length) { num num1 = 0; - int index2 = 0; - int index3 = 0; - int index4 = 0; + int index2 = 0, index3 = 0, index4 = 0; while (index4 < length) { if (numArray3[index4] != 1) { int index5 = 0; while (index5 < length) { if (numArray3[index5] == 0 && - (matrix[index4][index5]).abs() >= num1) { + (matrix[index4][index5]).abs() >= num1 == true) { num1 = (matrix[index4][index5]).abs(); index2 = index4; index3 = index5; @@ -1225,7 +1278,7 @@ class TrendlineRenderer { for (dynamic x = start; polyPoints.length <= 100; x += (end - start) / 100) { - final dynamic y = _getPolynomialYValue(_polynomialSlopes!, x); + final double y = _getPolynomialYValue(_polynomialSlopes!, x); final _ChartLocation position = _calculatePoint( x, y, @@ -1240,9 +1293,9 @@ class TrendlineRenderer { } /// To return predicted forecast values - dynamic _getForecastDate(dynamic axisRenderer, bool _isForward) { + int _getForecastDate(ChartAxisRenderer axisRenderer, bool _isForward) { Duration duration = const Duration(seconds: 0); - final DateTimeAxis axis = axisRenderer._axis; + final DateTimeAxis axis = axisRenderer._axis as DateTimeAxis; switch (axis.intervalType) { case DateTimeIntervalType.auto: duration = const Duration(seconds: 0); @@ -1290,6 +1343,13 @@ class TrendlineRenderer { ? _trendline.forwardForecast : _trendline.backwardForecast) .round()); + break; + case DateTimeIntervalType.milliseconds: + duration = Duration( + milliseconds: (_isForward + ? _trendline.forwardForecast + : _trendline.backwardForecast) + .round()); } return duration.inMilliseconds; } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/trendlines/trendlines_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/trendlines/trendlines_painter.dart index 75c262a31..697c97896 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/trendlines/trendlines_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/trendlines/trendlines_painter.dart @@ -11,7 +11,6 @@ class _TrendlinePainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { - Rect clipRect; double animationFactor; Animation? trendlineAnimation; for (int i = 0; @@ -19,8 +18,9 @@ class _TrendlinePainter extends CustomPainter { i++) { final CartesianSeriesRenderer seriesRenderer = chartState._chartSeries.visibleSeriesRenderers[i]; + final _RenderingDetails renderingDetails = chartState._renderingDetails; final XyDataSeries series = - seriesRenderer._series as XyDataSeries; + seriesRenderer._series as XyDataSeries; TrendlineRenderer trendlineRenderer; Trendline trendline; List controlPoints; @@ -33,11 +33,15 @@ class _TrendlinePainter extends CustomPainter { assert(trendline.animationDuration >= 0, 'The animation duration time for trendlines should be greater than or equal to 0.'); trendlineAnimation = trendlineAnimations['$i-$j']; - if (trendlineRenderer._isNeedRender && trendline.isVisible) { + if (trendlineRenderer._isNeedRender && + trendline.isVisible && + trendlineRenderer._pointsData != null && + trendlineRenderer._pointsData!.isNotEmpty) { canvas.save(); - animationFactor = (!seriesRenderer._chartState!._isLegendToggled && - (seriesRenderer._oldSeries == null)) - ? trendlineAnimation!.value + animationFactor = (renderingDetails.isLegendToggled && + (seriesRenderer._oldSeries == null)) && + trendlineAnimation != null + ? trendlineAnimation.value : 1; final Rect axisClipRect = _calculatePlotOffset( chartState._chartAxis._axisClipRect, @@ -87,7 +91,9 @@ class _TrendlinePainter extends CustomPainter { trendlineRenderer._points[i + 1].dx, trendlineRenderer._points[i + 1].dy); } - } else if (trendline.type == TrendlineType.polynomial) { + } else if (trendline.type == TrendlineType.polynomial && + trendlineRenderer._pointsData != null && + trendlineRenderer._pointsData!.isNotEmpty) { final List polynomialPoints = trendlineRenderer.getPolynomialCurve( trendlineRenderer._pointsData!, @@ -105,61 +111,69 @@ class _TrendlinePainter extends CustomPainter { trendlineRenderer._points[i].dy); } } - if (trendlineRenderer._dashArray != null) { - _drawDashedLine( - canvas, trendlineRenderer._dashArray!, paint, path); - } else { - canvas.drawPath(path, paint); - } - clipRect = _calculatePlotOffset( - Rect.fromLTRB( - chartState._chartAxis._axisClipRect.left - - trendline.markerSettings.width, - chartState._chartAxis._axisClipRect.top - - trendline.markerSettings.height, - chartState._chartAxis._axisClipRect.right + - trendline.markerSettings.width, - chartState._chartAxis._axisClipRect.bottom + - trendline.markerSettings.height), - Offset(seriesRenderer._xAxisRenderer!._axis.plotOffset, - seriesRenderer._yAxisRenderer!._axis.plotOffset)); - canvas.restore(); - if (trendlineRenderer._visible && - (animationFactor > chartState._trendlineDurationFactor)) { - canvas.clipRect(clipRect); - if (trendline.markerSettings.isVisible) { - for (final CartesianChartPoint point - in trendlineRenderer._pointsData!) { - if (point.isVisible && point.isGap != true) { - if (trendline.markerSettings.shape == - DataMarkerType.image) { - _drawImageMarker(seriesRenderer, canvas, - point.markerPoint!.x, point.markerPoint!.y); - } - final Paint strokePaint = Paint() - ..color = trendline.markerSettings.borderWidth == 0 - ? Colors.transparent - : trendline.markerSettings.borderColor ?? - trendlineRenderer._fillColor - ..strokeWidth = trendline.markerSettings.borderWidth - ..style = PaintingStyle.stroke; + _drawTrendlineMarker(trendlineRenderer, trendline, seriesRenderer, + animationFactor, canvas, path, paint); + } + } + } + } + } - final Paint fillPaint = Paint() - ..color = trendline.markerSettings.color ?? - (chartState._chartTheme.brightness == Brightness.light - ? Colors.white - : Colors.black) - ..style = PaintingStyle.fill; - final int index = - trendlineRenderer._pointsData!.indexOf(point); - canvas.drawPath( - trendlineRenderer._markerShapes[index], strokePaint); - canvas.drawPath( - trendlineRenderer._markerShapes[index], fillPaint); - } - } - } + ///To draw the marker on trendline + void _drawTrendlineMarker( + TrendlineRenderer trendlineRenderer, + Trendline trendline, + CartesianSeriesRenderer seriesRenderer, + double animationFactor, + Canvas canvas, + Path path, + Paint paint, + ) { + Rect clipRect; + final Rect _axisClipRect = chartState._chartAxis._axisClipRect; + final _RenderingDetails renderingDetails = chartState._renderingDetails; + (trendlineRenderer._dashArray != null) + ? _drawDashedLine(canvas, trendlineRenderer._dashArray!, paint, path) + : canvas.drawPath(path, paint); + clipRect = _calculatePlotOffset( + Rect.fromLTRB( + _axisClipRect.left - trendline.markerSettings.width, + _axisClipRect.top - trendline.markerSettings.height, + _axisClipRect.right + trendline.markerSettings.width, + _axisClipRect.bottom + trendline.markerSettings.height), + Offset(seriesRenderer._xAxisRenderer!._axis.plotOffset, + seriesRenderer._yAxisRenderer!._axis.plotOffset)); + canvas.restore(); + if (trendlineRenderer._visible && + (animationFactor > chartState._trendlineDurationFactor)) { + canvas.clipRect(clipRect); + + if (trendline.markerSettings.isVisible) { + for (final CartesianChartPoint point + in trendlineRenderer._pointsData!) { + if (point.isVisible && point.isGap != true) { + if (trendline.markerSettings.shape == DataMarkerType.image) { + _drawImageMarker(seriesRenderer, canvas, point.markerPoint!.x, + point.markerPoint!.y); } + final Paint strokePaint = Paint() + ..color = trendline.markerSettings.borderWidth == 0 + ? Colors.transparent + : trendline.markerSettings.borderColor ?? + trendlineRenderer._fillColor + ..strokeWidth = trendline.markerSettings.borderWidth + ..style = PaintingStyle.stroke; + + final Paint fillPaint = Paint() + ..color = trendline.markerSettings.color ?? + (renderingDetails.chartTheme.brightness == Brightness.light + ? Colors.white + : Colors.black) + ..style = PaintingStyle.fill; + final int index = trendlineRenderer._pointsData!.indexOf(point); + canvas.drawPath( + trendlineRenderer._markerShapes[index], strokePaint); + canvas.drawPath(trendlineRenderer._markerShapes[index], fillPaint); } } } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/crosshair.dart b/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/crosshair.dart index 4ce8cd15c..2ef3ff3fd 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/crosshair.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/crosshair.dart @@ -2,7 +2,7 @@ part of charts; /// This class has the properties of the crosshair behavior. /// -/// crosshair behavior has the activation mode and line type property to set the behavior of the crosshair. +/// Crosshair behavior has the activation mode and line type property to set the behavior of the crosshair. /// It also has the property to customize the appearance. /// /// Provide options for activation mode, line type, line color, line width, hide delay for customizing the @@ -53,7 +53,6 @@ class CrosshairBehavior { /// Color will be applied based on the brightness ///property of the app. /// - ///Defaults to `1` ///```dart ///Widget build(BuildContext context) { /// return Container( @@ -89,7 +88,7 @@ class CrosshairBehavior { /// /// Defaults to `ActivationMode.longPress` /// - /// Also refer ActivationMode + /// Also refer [ActivationMode] ///```dart ///Widget build(BuildContext context) { /// return Container( @@ -136,7 +135,7 @@ class CrosshairBehavior { ///``` final bool shouldAlwaysShow; - ///Giving disapper delay for crosshair + ///Time delay for hiding the crosshair. /// /// Defaults to `0` ///```dart @@ -149,6 +148,43 @@ class CrosshairBehavior { ///``` final double hideDelay; + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is CrosshairBehavior && + other.activationMode == activationMode && + other.lineType == lineType && + other.lineDashArray == lineDashArray && + other.enable == enable && + other.lineColor == lineColor && + other.lineWidth == lineWidth && + other.shouldAlwaysShow == shouldAlwaysShow && + other.hideDelay == hideDelay; + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode { + final List values = [ + activationMode, + lineType, + lineDashArray, + enable, + lineColor, + lineWidth, + shouldAlwaysShow, + hideDelay + ]; + return hashList(values); + } + SfCartesianChartState? _chartState; /// Displays the crosshair at the specified x and y-positions. @@ -167,7 +203,7 @@ class CrosshairBehavior { ._crosshairPainter!.chartState._chartSeries.visibleSeriesRenderers[0]; final ChartAxisRenderer xAxisRenderer = seriesRenderer._xAxisRenderer!; final _ChartLocation location = _calculatePoint( - (x is DateTime && !(xAxisRenderer is DateTimeCategoryAxisRenderer)) + (x is DateTime && xAxisRenderer is! DateTimeCategoryAxisRenderer) ? x.millisecondsSinceEpoch : ((x is DateTime && xAxisRenderer is DateTimeCategoryAxisRenderer) @@ -192,8 +228,8 @@ class CrosshairBehavior { crosshairBehaviorRenderer._crosshairPainter! ._generateAllPoints(Offset(x.toDouble(), y)); crosshairBehaviorRenderer._crosshairPainter!.canResetPath = false; - crosshairBehaviorRenderer - ._crosshairPainter!.chartState._crosshairRepaintNotifier.value++; + crosshairBehaviorRenderer._crosshairPainter!.chartState + ._repaintNotifiers['crosshair']!.value++; } } @@ -217,8 +253,8 @@ class CrosshairBehavior { seriesRenderer._dataPoints[pointIndex].markerPoint!.x, seriesRenderer._dataPoints[pointIndex].markerPoint!.y)); crosshairBehaviorRenderer._crosshairPainter!.canResetPath = false; - crosshairBehaviorRenderer - ._crosshairPainter!.chartState._crosshairRepaintNotifier.value++; + crosshairBehaviorRenderer._crosshairPainter!.chartState + ._repaintNotifiers['crosshair']!.value++; } } } @@ -230,12 +266,12 @@ class CrosshairBehavior { chartState._crosshairBehaviorRenderer; if (crosshairBehaviorRenderer._crosshairPainter != null) { crosshairBehaviorRenderer._crosshairPainter!.canResetPath = false; - ValueNotifier(crosshairBehaviorRenderer - ._crosshairPainter!.chartState._crosshairRepaintNotifier.value++); + ValueNotifier(crosshairBehaviorRenderer._crosshairPainter!.chartState + ._repaintNotifiers['crosshair']!.value++); crosshairBehaviorRenderer._crosshairPainter!.timer?.cancel(); if (!chartState._isTouchUp) { - crosshairBehaviorRenderer - ._crosshairPainter!.chartState._trackballRepaintNotifier.value++; + crosshairBehaviorRenderer._crosshairPainter!.chartState + ._repaintNotifiers['crosshair']!.value++; crosshairBehaviorRenderer._crosshairPainter!.canResetPath = true; } else { if (!shouldAlwaysShow) { @@ -247,7 +283,7 @@ class CrosshairBehavior { crosshairBehaviorRenderer._crosshairPainter!.timer = Timer(Duration(milliseconds: duration.toInt()), () { crosshairBehaviorRenderer._crosshairPainter!.chartState - ._crosshairRepaintNotifier.value++; + ._repaintNotifiers['crosshair']!.value++; crosshairBehaviorRenderer._crosshairPainter!.canResetPath = true; }); } @@ -261,9 +297,9 @@ class CrosshairBehaviorRenderer with ChartBehavior { /// Creates an argument constructor for Crosshair renderer class CrosshairBehaviorRenderer(this._chartState); - dynamic get _chart => _chartState._chart; + SfCartesianChart get _chart => _chartState._chart; - final dynamic _chartState; + final SfCartesianChartState _chartState; CrosshairBehavior get _crosshairBehavior => _chart.crosshairBehavior; diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/crosshair_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/crosshair_painter.dart index 8b2919a65..54167aa4a 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/crosshair_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/crosshair_painter.dart @@ -6,6 +6,7 @@ class _CrosshairPainter extends CustomPainter { super(repaint: valueNotifier); final SfCartesianChartState chartState; final SfCartesianChart chart; + _RenderingDetails get _renderingDetails => chartState._renderingDetails; Timer? timer; ValueNotifier valueNotifier; // double pointerLength; @@ -98,13 +99,13 @@ class _CrosshairPainter extends CustomPainter { /// To draw cross hair void _drawCrosshair(Canvas canvas) { final Paint fillPaint = Paint() - ..color = chartState._chartTheme.crosshairBackgroundColor + ..color = _renderingDetails.chartTheme.crosshairBackgroundColor ..strokeCap = StrokeCap.butt ..isAntiAlias = false ..style = PaintingStyle.fill; final Paint strokePaint = Paint() - ..color = chartState._chartTheme.crosshairBackgroundColor + ..color = _renderingDetails.chartTheme.crosshairBackgroundColor ..strokeCap = StrokeCap.butt ..isAntiAlias = false ..style = PaintingStyle.stroke; @@ -117,7 +118,7 @@ class _CrosshairPainter extends CustomPainter { final Offset position = chartState._crosshairBehaviorRenderer._position!; crosshairLinePaint.color = chart.crosshairBehavior.lineColor ?? - chartState._chartTheme.crosshairLineColor; + _renderingDetails.chartTheme.crosshairLineColor; crosshairLinePaint.strokeWidth = chart.crosshairBehavior.lineWidth; crosshairLinePaint.style = PaintingStyle.stroke; CrosshairRenderArgs crosshairEventArgs; @@ -144,7 +145,7 @@ class _CrosshairPainter extends CustomPainter { void _drawBottomAxesTooltip( Canvas canvas, Offset position, Paint strokePaint, Paint fillPaint) { ChartAxisRenderer axisRenderer; - dynamic value; + String value; Size labelSize; Rect labelRect, validatedRect; RRect tooltipRect; @@ -159,29 +160,29 @@ class _CrosshairPainter extends CustomPainter { final ChartAxis axis = axisRenderer._axis; if (_needToAddTooltip(axisRenderer)) { fillPaint.color = axis.interactiveTooltip.color ?? - chartState._chartTheme.crosshairBackgroundColor; + _renderingDetails.chartTheme.crosshairBackgroundColor; strokePaint.color = axis.interactiveTooltip.borderColor ?? - chartState._chartTheme.crosshairBackgroundColor; + _renderingDetails.chartTheme.crosshairBackgroundColor; strokePaint.strokeWidth = axis.interactiveTooltip.borderWidth; value = _getXValue(axisRenderer, position); if (chart.onCrosshairPositionChanging != null) { crosshairEventArgs = CrosshairRenderArgs( axis, value, axisRenderer._name, axisRenderer._orientation); - crosshairEventArgs.text = value.toString(); + crosshairEventArgs.text = value; crosshairEventArgs.lineColor = chart.crosshairBehavior.lineColor ?? - chartState._chartTheme.crosshairLineColor; + _renderingDetails.chartTheme.crosshairLineColor; chart.onCrosshairPositionChanging!(crosshairEventArgs); value = crosshairEventArgs.text; color = crosshairEventArgs.lineColor; } - labelSize = - measureText(value.toString(), axis.interactiveTooltip.textStyle); + labelSize = measureText(value, axis.interactiveTooltip.textStyle); labelRect = Rect.fromLTWH( position.dx - (labelSize.width / 2 + padding / 2), axisRenderer._bounds.top + axis.interactiveTooltip.arrowLength, labelSize.width + padding, labelSize.height + padding); - labelRect = _validateRectBounds(labelRect, chartState._containerRect); + labelRect = _validateRectBounds( + labelRect, _renderingDetails.chartContainerRect); validatedRect = _validateRectXPosition(labelRect, chartState); backgroundPath.reset(); tooltipRect = _getRoundedCornerRect( @@ -202,44 +203,8 @@ class _CrosshairPainter extends CustomPainter { tooltipRect.top, position.dx, tooltipRect.top - axis.interactiveTooltip.arrowLength); - _drawText( - canvas, - value.toString(), - Offset( - (tooltipRect.left + tooltipRect.width / 2) - - labelSize.width / 2, - (tooltipRect.top + tooltipRect.height / 2) - - labelSize.height / 2), - TextStyle( - color: axis.interactiveTooltip.textStyle.color ?? - chartState._chartTheme.tooltipLabelColor, - fontSize: axis.interactiveTooltip.textStyle.fontSize, - fontWeight: axis.interactiveTooltip.textStyle.fontWeight, - fontFamily: axis.interactiveTooltip.textStyle.fontFamily, - fontStyle: axis.interactiveTooltip.textStyle.fontStyle, - inherit: axis.interactiveTooltip.textStyle.inherit, - backgroundColor: - axis.interactiveTooltip.textStyle.backgroundColor, - letterSpacing: axis.interactiveTooltip.textStyle.letterSpacing, - wordSpacing: axis.interactiveTooltip.textStyle.wordSpacing, - textBaseline: axis.interactiveTooltip.textStyle.textBaseline, - height: axis.interactiveTooltip.textStyle.height, - locale: axis.interactiveTooltip.textStyle.locale, - foreground: axis.interactiveTooltip.textStyle.foreground, - background: axis.interactiveTooltip.textStyle.background, - shadows: axis.interactiveTooltip.textStyle.shadows, - fontFeatures: axis.interactiveTooltip.textStyle.fontFeatures, - decoration: axis.interactiveTooltip.textStyle.decoration, - decorationColor: - axis.interactiveTooltip.textStyle.decorationColor, - decorationStyle: - axis.interactiveTooltip.textStyle.decorationStyle, - decorationThickness: - axis.interactiveTooltip.textStyle.decorationThickness, - debugLabel: axis.interactiveTooltip.textStyle.debugLabel, - fontFamilyFallback: - axis.interactiveTooltip.textStyle.fontFamilyFallback), - 0); + _drawTooltipText(canvas, value, axis.interactiveTooltip.textStyle, + tooltipRect, labelSize); } } } @@ -249,7 +214,7 @@ class _CrosshairPainter extends CustomPainter { Canvas canvas, Offset position, Paint strokePaint, Paint fillPaint) { ChartAxis axis; ChartAxisRenderer axisRenderer; - dynamic value; + String value; Size labelSize; Rect labelRect, validatedRect; RRect tooltipRect; @@ -264,23 +229,22 @@ class _CrosshairPainter extends CustomPainter { axis = axisRenderer._axis; if (_needToAddTooltip(axisRenderer)) { fillPaint.color = axis.interactiveTooltip.color ?? - chartState._chartTheme.crosshairBackgroundColor; + _renderingDetails.chartTheme.crosshairBackgroundColor; strokePaint.color = axis.interactiveTooltip.borderColor ?? - chartState._chartTheme.crosshairBackgroundColor; + _renderingDetails.chartTheme.crosshairBackgroundColor; strokePaint.strokeWidth = axis.interactiveTooltip.borderWidth; value = _getXValue(axisRenderer, position); if (chart.onCrosshairPositionChanging != null) { crosshairEventArgs = CrosshairRenderArgs(axisRenderer._axis, value, axisRenderer._name, axisRenderer._orientation); - crosshairEventArgs.text = value.toString(); + crosshairEventArgs.text = value; crosshairEventArgs.lineColor = chart.crosshairBehavior.lineColor ?? - chartState._chartTheme.crosshairLineColor; + _renderingDetails.chartTheme.crosshairLineColor; chart.onCrosshairPositionChanging!(crosshairEventArgs); value = crosshairEventArgs.text; color = crosshairEventArgs.lineColor; } - labelSize = - measureText(value.toString(), axis.interactiveTooltip.textStyle); + labelSize = measureText(value, axis.interactiveTooltip.textStyle); labelRect = Rect.fromLTWH( position.dx - (labelSize.width / 2 + padding / 2), axisRenderer._bounds.top - @@ -288,13 +252,13 @@ class _CrosshairPainter extends CustomPainter { axis.interactiveTooltip.arrowLength, labelSize.width + padding, labelSize.height + padding); - labelRect = _validateRectBounds(labelRect, chartState._containerRect); + labelRect = _validateRectBounds( + labelRect, _renderingDetails.chartContainerRect); validatedRect = _validateRectXPosition(labelRect, chartState); backgroundPath.reset(); tooltipRect = _getRoundedCornerRect( validatedRect, axis.interactiveTooltip.borderRadius); backgroundPath.addRRect(tooltipRect); - _drawTooltipArrowhead( canvas, backgroundPath, @@ -310,44 +274,8 @@ class _CrosshairPainter extends CustomPainter { tooltipRect.bottom, position.dx, tooltipRect.bottom + axis.interactiveTooltip.arrowLength); - _drawText( - canvas, - value.toString(), - Offset( - (tooltipRect.left + tooltipRect.width / 2) - - labelSize.width / 2, - (tooltipRect.top + tooltipRect.height / 2) - - labelSize.height / 2), - TextStyle( - color: axis.interactiveTooltip.textStyle.color ?? - chartState._chartTheme.tooltipLabelColor, - fontSize: axis.interactiveTooltip.textStyle.fontSize, - fontWeight: axis.interactiveTooltip.textStyle.fontWeight, - fontFamily: axis.interactiveTooltip.textStyle.fontFamily, - fontStyle: axis.interactiveTooltip.textStyle.fontStyle, - inherit: axis.interactiveTooltip.textStyle.inherit, - backgroundColor: - axis.interactiveTooltip.textStyle.backgroundColor, - letterSpacing: axis.interactiveTooltip.textStyle.letterSpacing, - wordSpacing: axis.interactiveTooltip.textStyle.wordSpacing, - textBaseline: axis.interactiveTooltip.textStyle.textBaseline, - height: axis.interactiveTooltip.textStyle.height, - locale: axis.interactiveTooltip.textStyle.locale, - foreground: axis.interactiveTooltip.textStyle.foreground, - background: axis.interactiveTooltip.textStyle.background, - shadows: axis.interactiveTooltip.textStyle.shadows, - fontFeatures: axis.interactiveTooltip.textStyle.fontFeatures, - decoration: axis.interactiveTooltip.textStyle.decoration, - decorationColor: - axis.interactiveTooltip.textStyle.decorationColor, - decorationStyle: - axis.interactiveTooltip.textStyle.decorationStyle, - decorationThickness: - axis.interactiveTooltip.textStyle.decorationThickness, - debugLabel: axis.interactiveTooltip.textStyle.debugLabel, - fontFamilyFallback: - axis.interactiveTooltip.textStyle.fontFamilyFallback), - 0); + _drawTooltipText(canvas, value, axis.interactiveTooltip.textStyle, + tooltipRect, labelSize); } } } @@ -357,7 +285,7 @@ class _CrosshairPainter extends CustomPainter { Canvas canvas, Offset position, Paint strokePaint, Paint fillPaint) { ChartAxis axis; ChartAxisRenderer axisRenderer; - dynamic value; + String value; Size labelSize; Rect labelRect, validatedRect; RRect tooltipRect; @@ -372,23 +300,22 @@ class _CrosshairPainter extends CustomPainter { axis = axisRenderer._axis; if (_needToAddTooltip(axisRenderer)) { fillPaint.color = axis.interactiveTooltip.color ?? - chartState._chartTheme.crosshairBackgroundColor; + _renderingDetails.chartTheme.crosshairBackgroundColor; strokePaint.color = axis.interactiveTooltip.borderColor ?? - chartState._chartTheme.crosshairBackgroundColor; + _renderingDetails.chartTheme.crosshairBackgroundColor; strokePaint.strokeWidth = axis.interactiveTooltip.borderWidth; value = _getYValue(axisRenderer, position); if (chart.onCrosshairPositionChanging != null) { crosshairEventArgs = CrosshairRenderArgs( axis, value, axisRenderer._name, axisRenderer._orientation); - crosshairEventArgs.text = value.toString(); + crosshairEventArgs.text = value; crosshairEventArgs.lineColor = chart.crosshairBehavior.lineColor ?? - chartState._chartTheme.crosshairLineColor; + _renderingDetails.chartTheme.crosshairLineColor; chart.onCrosshairPositionChanging!(crosshairEventArgs); value = crosshairEventArgs.text; color = crosshairEventArgs.lineColor; } - labelSize = - measureText(value.toString(), axis.interactiveTooltip.textStyle); + labelSize = measureText(value, axis.interactiveTooltip.textStyle); labelRect = Rect.fromLTWH( axisRenderer._bounds.left - (labelSize.width + padding) - @@ -396,7 +323,8 @@ class _CrosshairPainter extends CustomPainter { position.dy - (labelSize.height + padding) / 2, labelSize.width + padding, labelSize.height + padding); - labelRect = _validateRectBounds(labelRect, chartState._containerRect); + labelRect = _validateRectBounds( + labelRect, _renderingDetails.chartContainerRect); validatedRect = _validateRectYPosition(labelRect, chartState); backgroundPath.reset(); tooltipRect = _getRoundedCornerRect( @@ -420,44 +348,9 @@ class _CrosshairPainter extends CustomPainter { position.dy, tooltipRect.right + axis.interactiveTooltip.arrowLength, position.dy); - _drawText( - canvas, - value.toString(), - Offset( - (tooltipRect.left + tooltipRect.width / 2) - - labelSize.width / 2, - (tooltipRect.top + tooltipRect.height / 2) - - labelSize.height / 2), - TextStyle( - color: axis.interactiveTooltip.textStyle.color ?? - chartState._chartTheme.tooltipLabelColor, - fontSize: axis.interactiveTooltip.textStyle.fontSize, - fontWeight: axis.interactiveTooltip.textStyle.fontWeight, - fontFamily: axis.interactiveTooltip.textStyle.fontFamily, - fontStyle: axis.interactiveTooltip.textStyle.fontStyle, - inherit: axis.interactiveTooltip.textStyle.inherit, - backgroundColor: - axis.interactiveTooltip.textStyle.backgroundColor, - letterSpacing: axis.interactiveTooltip.textStyle.letterSpacing, - wordSpacing: axis.interactiveTooltip.textStyle.wordSpacing, - textBaseline: axis.interactiveTooltip.textStyle.textBaseline, - height: axis.interactiveTooltip.textStyle.height, - locale: axis.interactiveTooltip.textStyle.locale, - foreground: axis.interactiveTooltip.textStyle.foreground, - background: axis.interactiveTooltip.textStyle.background, - shadows: axis.interactiveTooltip.textStyle.shadows, - fontFeatures: axis.interactiveTooltip.textStyle.fontFeatures, - decoration: axis.interactiveTooltip.textStyle.decoration, - decorationColor: - axis.interactiveTooltip.textStyle.decorationColor, - decorationStyle: - axis.interactiveTooltip.textStyle.decorationStyle, - decorationThickness: - axis.interactiveTooltip.textStyle.decorationThickness, - debugLabel: axis.interactiveTooltip.textStyle.debugLabel, - fontFamilyFallback: - axis.interactiveTooltip.textStyle.fontFamilyFallback), - 0); + + _drawTooltipText(canvas, value, axis.interactiveTooltip.textStyle, + tooltipRect, labelSize); } } } @@ -467,7 +360,7 @@ class _CrosshairPainter extends CustomPainter { Canvas canvas, Offset position, Paint strokePaint, Paint fillPaint) { ChartAxis axis; ChartAxisRenderer axisRenderer; - dynamic value; + String value; Size labelSize; Rect labelRect, validatedRect; CrosshairRenderArgs crosshairEventArgs; @@ -482,29 +375,29 @@ class _CrosshairPainter extends CustomPainter { axis = axisRenderer._axis; if (_needToAddTooltip(axisRenderer)) { fillPaint.color = axis.interactiveTooltip.color ?? - chartState._chartTheme.crosshairBackgroundColor; + _renderingDetails.chartTheme.crosshairBackgroundColor; strokePaint.color = axis.interactiveTooltip.borderColor ?? - chartState._chartTheme.crosshairBackgroundColor; + _renderingDetails.chartTheme.crosshairBackgroundColor; strokePaint.strokeWidth = axis.interactiveTooltip.borderWidth; value = _getYValue(axisRenderer, position); if (chart.onCrosshairPositionChanging != null) { crosshairEventArgs = CrosshairRenderArgs( axis, value, axisRenderer._name, axisRenderer._orientation); - crosshairEventArgs.text = value.toString(); + crosshairEventArgs.text = value; crosshairEventArgs.lineColor = chart.crosshairBehavior.lineColor ?? - chartState._chartTheme.crosshairLineColor; + _renderingDetails.chartTheme.crosshairLineColor; chart.onCrosshairPositionChanging!(crosshairEventArgs); value = crosshairEventArgs.text; color = crosshairEventArgs.lineColor; } - labelSize = - measureText(value.toString(), axis.interactiveTooltip.textStyle); + labelSize = measureText(value, axis.interactiveTooltip.textStyle); labelRect = Rect.fromLTWH( axisRenderer._bounds.left + axis.interactiveTooltip.arrowLength, position.dy - (labelSize.height / 2 + padding / 2), labelSize.width + padding, labelSize.height + padding); - labelRect = _validateRectBounds(labelRect, chartState._containerRect); + labelRect = _validateRectBounds( + labelRect, _renderingDetails.chartContainerRect); validatedRect = _validateRectYPosition(labelRect, chartState); backgroundPath.reset(); tooltipRect = _getRoundedCornerRect( @@ -527,51 +420,49 @@ class _CrosshairPainter extends CustomPainter { position.dy, tooltipRect.left - axis.interactiveTooltip.arrowLength, position.dy); - _drawText( - canvas, - value.toString(), - Offset( - (tooltipRect.left + tooltipRect.width / 2) - - labelSize.width / 2, - (tooltipRect.top + tooltipRect.height / 2) - - labelSize.height / 2), - TextStyle( - color: axis.interactiveTooltip.textStyle.color ?? - chartState._chartTheme.tooltipLabelColor, - fontSize: axis.interactiveTooltip.textStyle.fontSize, - fontWeight: axis.interactiveTooltip.textStyle.fontWeight, - fontFamily: axis.interactiveTooltip.textStyle.fontFamily, - fontStyle: axis.interactiveTooltip.textStyle.fontStyle, - inherit: axis.interactiveTooltip.textStyle.inherit, - backgroundColor: - axis.interactiveTooltip.textStyle.backgroundColor, - letterSpacing: axis.interactiveTooltip.textStyle.letterSpacing, - wordSpacing: axis.interactiveTooltip.textStyle.wordSpacing, - textBaseline: axis.interactiveTooltip.textStyle.textBaseline, - height: axis.interactiveTooltip.textStyle.height, - locale: axis.interactiveTooltip.textStyle.locale, - foreground: axis.interactiveTooltip.textStyle.foreground, - background: axis.interactiveTooltip.textStyle.background, - shadows: axis.interactiveTooltip.textStyle.shadows, - fontFeatures: axis.interactiveTooltip.textStyle.fontFeatures, - decoration: axis.interactiveTooltip.textStyle.decoration, - decorationColor: - axis.interactiveTooltip.textStyle.decorationColor, - decorationStyle: - axis.interactiveTooltip.textStyle.decorationStyle, - decorationThickness: - axis.interactiveTooltip.textStyle.decorationThickness, - debugLabel: axis.interactiveTooltip.textStyle.debugLabel, - fontFamilyFallback: - axis.interactiveTooltip.textStyle.fontFamilyFallback), - 0); + _drawTooltipText(canvas, value, axis.interactiveTooltip.textStyle, + tooltipRect, labelSize); } } } + void _drawTooltipText(Canvas canvas, String text, TextStyle textStyle, + RRect tooltipRect, Size labelSize) { + _drawText( + canvas, + text, + Offset((tooltipRect.left + tooltipRect.width / 2) - labelSize.width / 2, + (tooltipRect.top + tooltipRect.height / 2) - labelSize.height / 2), + TextStyle( + color: textStyle.color ?? + _renderingDetails.chartTheme.tooltipLabelColor, + fontSize: textStyle.fontSize, + fontWeight: textStyle.fontWeight, + fontFamily: textStyle.fontFamily, + fontStyle: textStyle.fontStyle, + inherit: textStyle.inherit, + backgroundColor: textStyle.backgroundColor, + letterSpacing: textStyle.letterSpacing, + wordSpacing: textStyle.wordSpacing, + textBaseline: textStyle.textBaseline, + height: textStyle.height, + locale: textStyle.locale, + foreground: textStyle.foreground, + background: textStyle.background, + shadows: textStyle.shadows, + fontFeatures: textStyle.fontFeatures, + decoration: textStyle.decoration, + decorationColor: textStyle.decorationColor, + decorationStyle: textStyle.decorationStyle, + decorationThickness: textStyle.decorationThickness, + debugLabel: textStyle.debugLabel, + fontFamilyFallback: textStyle.fontFamilyFallback), + 0); + } + /// To find the x value of crosshair - dynamic _getXValue(ChartAxisRenderer axisRenderer, Offset position) { - final dynamic value = _pointToXVal( + String _getXValue(ChartAxisRenderer axisRenderer, Offset position) { + final num value = _pointToXVal( chart, axisRenderer, axisRenderer._bounds, @@ -592,7 +483,7 @@ class _CrosshairPainter extends CustomPainter { } /// To find the y value of crosshair - dynamic _getYValue(ChartAxisRenderer axisRenderer, Offset position) { + String _getYValue(ChartAxisRenderer axisRenderer, Offset position) { final num value = _pointToYVal( chart, axisRenderer, @@ -616,7 +507,7 @@ class _CrosshairPainter extends CustomPainter { /// To add the tooltip for crosshair bool _needToAddTooltip(ChartAxisRenderer axisRenderer) { return axisRenderer._axis.interactiveTooltip.enable && - ((!(axisRenderer is CategoryAxisRenderer) && + ((axisRenderer is! CategoryAxisRenderer && axisRenderer._visibleLabels.isNotEmpty) || (axisRenderer is CategoryAxisRenderer && axisRenderer._labels.isNotEmpty)); diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/selection_renderer.dart b/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/selection_renderer.dart index 51e27ba65..21e7a5279 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/selection_renderer.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/selection_renderer.dart @@ -39,7 +39,7 @@ class _SelectionRenderer { /// Selection type and multi-selection functionality is also applicable for this, but it is based on /// the API values specified in [ChartSelectionBehavior]. /// - ///_Note:_ - Even though, the [enable] property in [ChartSelectionBehavior] is set to false, this method + ///_Note:_ Even though, the [enable] property in [ChartSelectionBehavior] is set to false, this method /// will work. void selectDataPoints(int? pointIndex, [int? seriesIndex]) { @@ -56,11 +56,11 @@ class _SelectionRenderer { final SelectionBehaviorRenderer selectionBehaviorRenderer = seriesRenderer._selectionBehaviorRenderer; selectionBehaviorRenderer._selectionRenderer!._isInteraction = true; - if (_checkSeriesType(seriesType) || + if (_isLineTypeSeries(seriesType) || seriesType.contains('hilo') || seriesType == 'candle' || seriesType.contains('boxandwhisker')) { - if (seriesRenderer._isSelectionEnable) { + if (seriesRenderer._isSelectionEnable = true) { selectionBehaviorRenderer._selectionRenderer!.cartesianPointIndex = pointIndex; selectionBehaviorRenderer._selectionRenderer!.cartesianSeriesIndex = @@ -179,16 +179,14 @@ class _SelectionRenderer { final dynamic selectionBehavior = seriesRenderer._selectionBehavior; final CartesianSeries series = seriesRenderer._series; assert( - selectionBehavior.selectedOpacity != null - ? selectionBehavior.selectedOpacity >= 0 && - selectionBehavior.selectedOpacity <= 1 - : true, + ((selectionBehavior.selectedOpacity != null) == false) || + selectionBehavior.selectedOpacity >= 0 == true && + selectionBehavior.selectedOpacity <= 1 == true, 'The selected opacity of selection settings should between 0 and 1.'); assert( - selectionBehavior.unselectedOpacity != null - ? selectionBehavior.unselectedOpacity >= 0 && - selectionBehavior.unselectedOpacity <= 1 - : true, + ((selectionBehavior.unselectedOpacity != null) == false) || + selectionBehavior.unselectedOpacity >= 0 == true && + selectionBehavior.unselectedOpacity <= 1 == true, 'The unselected opacity of selection settings should between 0 and 1.'); final ChartSelectionCallback? chartEventSelection = chart.onSelectionChanged; @@ -316,7 +314,9 @@ class _SelectionRenderer { segment is HiloOpenCloseSegment || segment is CandleSegment || segment is BoxAndWhiskerSegment - ? strokeColor = chartEventSelection != null && selectionArgs != null + ? strokeColor = chartEventSelection != null && + selectionArgs != null && + selectionArgs!.unselectedColor != null ? selectionArgs!.unselectedColor : _chartState._selectionArgs != null && _chartState._selectionArgs!.unselectedColor != null @@ -364,7 +364,7 @@ class _SelectionRenderer { for (int i = 0; i < selectedSegments.length; i++) { seriesRenderer = _chartState._chartSeries .visibleSeriesRenderers[selectedSegments[i]._seriesIndex]; - if (!seriesRenderer._seriesType.contains('area') && + if (seriesRenderer._seriesType.contains('area') == false && seriesRenderer._seriesType != 'fastline') { final ChartSegment currentSegment = seriesRenderer._segments[selectedSegments[i].currentSegmentIndex]; @@ -401,7 +401,7 @@ class _SelectionRenderer { for (int i = 0; i < unselectedSegments.length; i++) { seriesRenderer = _chartState._chartSeries .visibleSeriesRenderers[unselectedSegments[i]._seriesIndex]; - if (!seriesRenderer._seriesType.contains('area') && + if (seriesRenderer._seriesType.contains('area') == false && seriesRenderer._seriesType != 'fastline') { final ChartSegment currentSegment = seriesRenderer._segments[unselectedSegments[i].currentSegmentIndex]; @@ -446,7 +446,7 @@ class _SelectionRenderer { while (unselectedSegments.isNotEmpty) { seriesRenderer = _chartState._chartSeries .visibleSeriesRenderers[unselectedSegments[k]._seriesIndex]; - if (!seriesRenderer._seriesType.contains('area') && + if (seriesRenderer._seriesType.contains('area') == false && seriesRenderer._seriesType != 'fastline') { if (unselectedSegments[k].currentSegmentIndex! < seriesRenderer._segments.length) { @@ -481,7 +481,7 @@ class _SelectionRenderer { while (selectedSegments.isNotEmpty) { seriesRenderer = _chartState._chartSeries .visibleSeriesRenderers[selectedSegments[j]._seriesIndex]; - if (!seriesRenderer._seriesType.contains('area') && + if (seriesRenderer._seriesType.contains('area') == false && seriesRenderer._seriesType != 'fastline') { if (selectedSegments[j].currentSegmentIndex! < seriesRenderer._segments.length) { @@ -498,9 +498,9 @@ class _SelectionRenderer { seriesRenderer._seriesType == 'stepline' || seriesRenderer._seriesType == 'stackedline' || seriesRenderer._seriesType == 'stackedline100' || - seriesRenderer._seriesType.contains('hilo') || + seriesRenderer._seriesType.contains('hilo') == true || seriesRenderer._seriesType == 'candle' || - seriesRenderer._seriesType.contains('boxandwhisker')) { + seriesRenderer._seriesType.contains('boxandwhisker') == true) { if (selectedSegments[j]._currentPoint!.overallDataPointIndex == cartesianPointIndex && selectedSegments[j]._seriesIndex == cartesianSeriesIndex) { @@ -537,6 +537,55 @@ class _SelectionRenderer { return isSamePointSelect; } + bool _isSamePointSelected(List selectedSegments) { + bool isSamePointSelected = false; + for (int j = 0; + j < selectedSegments.length && selectedSegments.isNotEmpty; + j++) { + seriesRenderer = _chartState._chartSeries + .visibleSeriesRenderers[selectedSegments[j]._seriesIndex]; + if (seriesRenderer._seriesType.contains('area') == false && + seriesRenderer._seriesType != 'fastline') { + if (selectedSegments[j].currentSegmentIndex! < + seriesRenderer._segments.length) { + final ChartSegment currentSegment = + seriesRenderer._segments[selectedSegments[j].currentSegmentIndex]; + if (((seriesRenderer._seriesType.indexOf('line') >= 0) == true || + seriesRenderer._seriesType.contains('hilo') == true || + seriesRenderer._seriesType == 'candle' || + seriesRenderer._seriesType.contains('boxandwhisker') == + true) && + selectedSegments[j]._currentPoint!.overallDataPointIndex == + cartesianPointIndex && + selectedSegments[j]._seriesIndex == cartesianSeriesIndex) { + isSamePointSelected = true; + } else { + if ((currentSegment._currentPoint!.overallDataPointIndex == + pointIndex || + selectedSegments[j]._currentPoint!.overallDataPointIndex == + pointIndex) && + currentSegment._oldSegmentIndex == + selectedSegments[j]._oldSegmentIndex && + selectedSegments[j]._seriesIndex == seriesIndex) { + isSamePointSelected = true; + } + } + } + } else { + final ChartSegment currentSegment = seriesRenderer._segments[0]; + final Paint fillPaint = getDefaultFillColor(null, null, currentSegment); + currentSegment.fillPaint = fillPaint; + final Paint strokePaint = + getDefaultStrokeColor(null, null, currentSegment); + currentSegment.strokePaint = strokePaint; + if (selectedSegments[0]._seriesIndex == seriesIndex) { + isSamePointSelected = true; + } + } + } + return isSamePointSelected; + } + ChartSegment? getTappedSegment() { for (int i = 0; i < _chartState._chartSeries.visibleSeriesRenderers.length; @@ -606,7 +655,10 @@ class _SelectionRenderer { chart = chartAssign; seriesRenderer = seriesAssign; - if (chart.onSelectionChanged != null && selected) { + if (chart.onSelectionChanged != null && + selected && + (!(seriesRenderer._selectionBehavior.toggleSelection == false && + _isSamePointSelected(selectedSegments)))) { chart.onSelectionChanged(getSelectionEventArgs(seriesRenderer._series, seriesIndex!, viewportIndex!, seriesRenderer)); selected = false; @@ -619,495 +671,512 @@ class _SelectionRenderer { /// For point mode if ((selectionType ?? chart.selectionType) == SelectionType.point) { - bool isSamePointSelect = false; + if (!(seriesRenderer._selectionBehavior.toggleSelection == false && + _isSamePointSelected(selectedSegments))) { + bool isSamePointSelect = false; - /// UnSelecting the last selected segment - if (selectedSegments.length == 1) { - changeColorAndPopUnselectedSegments(unselectedSegments!); - } + /// UnSelecting the last selected segment + if (selectedSegments.length == 1) { + changeColorAndPopUnselectedSegments(unselectedSegments!); + } - /// Executes when multiSelection is enabled - bool multiSelect = false; - if (chart.enableMultiSelection) { - if (selectedSegments.isNotEmpty) { - for (int i = - _chartState._chartSeries.visibleSeriesRenderers.length - 1; - i >= 0; - i--) { - final CartesianSeriesRenderer seriesRenderer = - _chartState._chartSeries.visibleSeriesRenderers[i]; - - /// To identify the tapped segment - for (int k = 0; k < seriesRenderer._segments.length; k++) { - currentSegment = seriesRenderer._segments[k]; - if ((currentSegment!._currentPoint!.overallDataPointIndex == - pointIndex) && - currentSegment!._seriesIndex == seriesIndex) { - selectedSegment = seriesRenderer._segments[k]; - break; - } - } - } + /// Executes when multiSelection is enabled + bool multiSelect = false; + if (chart.enableMultiSelection == true) { + if (selectedSegments.isNotEmpty) { + for (int i = + _chartState._chartSeries.visibleSeriesRenderers.length - 1; + i >= 0; + i--) { + final CartesianSeriesRenderer seriesRenderer = + _chartState._chartSeries.visibleSeriesRenderers[i]; - /// To identify that tapped segment in any one of the selected segment - if (selectedSegment != null) { - for (int k = 0; k < selectedSegments.length; k++) { - if ((selectedSegment!.currentSegmentIndex == - selectedSegments[k].currentSegmentIndex || - selectedSegment!._currentPoint!.overallDataPointIndex == - selectedSegments[k] - ._currentPoint! - .overallDataPointIndex) && - selectedSegment!._seriesIndex == - selectedSegments[k]._seriesIndex) { - multiSelect = true; - break; + /// To identify the tapped segment + for (int k = 0; k < seriesRenderer._segments.length; k++) { + currentSegment = seriesRenderer._segments[k]; + if ((currentSegment!._currentPoint!.overallDataPointIndex == + pointIndex) && + currentSegment!._seriesIndex == seriesIndex) { + selectedSegment = seriesRenderer._segments[k]; + break; + } } } - } - /// Executes when tapped again in one of the selected segments - if (multiSelect) { - for (int j = selectedSegments.length - 1; j >= 0; j--) { - seriesRenderer = _chartState._chartSeries - .visibleSeriesRenderers[selectedSegments[j]._seriesIndex]; - final ChartSegment currentSegment = seriesRenderer - ._segments[selectedSegments[j].currentSegmentIndex]; - - /// Applying default settings when last selected segment becomes unselected - if (((selectedSegment!.currentSegmentIndex == - selectedSegments[j].currentSegmentIndex || - selectedSegment! - ._currentPoint!.overallDataPointIndex == - selectedSegments[j] - ._currentPoint! - .overallDataPointIndex) && - selectedSegment!._seriesIndex == - selectedSegments[j]._seriesIndex) && - (selectedSegments.length == 1)) { - final Paint fillPaint = - getDefaultFillColor(null, null, currentSegment); - final Paint strokePaint = - getDefaultStrokeColor(null, null, currentSegment); - currentSegment.fillPaint = fillPaint; - currentSegment.strokePaint = strokePaint; - - if ((currentSegment._currentPoint!.overallDataPointIndex == - pointIndex || - selectedSegments[j] + /// To identify that tapped segment in any one of the selected segment + if (selectedSegment != null) { + for (int k = 0; k < selectedSegments.length; k++) { + if ((selectedSegment!.currentSegmentIndex == + selectedSegments[k].currentSegmentIndex || + selectedSegment!._currentPoint!.overallDataPointIndex == + selectedSegments[k] ._currentPoint! - .overallDataPointIndex == - pointIndex) && - selectedSegments[j]._seriesIndex == seriesIndex) { - isSamePointSelect = true; + .overallDataPointIndex) && + selectedSegment!._seriesIndex == + selectedSegments[k]._seriesIndex) { + multiSelect = true; + break; } - selectedSegments.remove(selectedSegments[j]); } + } - /// Applying unselected color for unselected segments in multiSelect option - else if ((selectedSegment!._currentPoint!.overallDataPointIndex == - selectedSegments[j] - ._currentPoint! - .overallDataPointIndex) && - selectedSegment!._seriesIndex == - selectedSegments[j]._seriesIndex) { - final Paint fillPaint = getFillColor(false, currentSegment); - currentSegment.fillPaint = fillPaint; - final Paint strokePaint = getStrokeColor(false, currentSegment); - currentSegment.strokePaint = strokePaint; + /// Executes when tapped again in one of the selected segments + if (multiSelect) { + for (int j = selectedSegments.length - 1; j >= 0; j--) { + seriesRenderer = _chartState._chartSeries + .visibleSeriesRenderers[selectedSegments[j]._seriesIndex]; + final ChartSegment currentSegment = seriesRenderer + ._segments[selectedSegments[j].currentSegmentIndex]; + + /// Applying default settings when last selected segment becomes unselected + if (((selectedSegment!.currentSegmentIndex == + selectedSegments[j].currentSegmentIndex || + selectedSegment! + ._currentPoint!.overallDataPointIndex == + selectedSegments[j] + ._currentPoint! + .overallDataPointIndex) && + selectedSegment!._seriesIndex == + selectedSegments[j]._seriesIndex) && + (selectedSegments.length == 1)) { + final Paint fillPaint = + getDefaultFillColor(null, null, currentSegment); + final Paint strokePaint = + getDefaultStrokeColor(null, null, currentSegment); + currentSegment.fillPaint = fillPaint; + currentSegment.strokePaint = strokePaint; - if ((currentSegment._currentPoint!.overallDataPointIndex == - pointIndex || + if ((currentSegment._currentPoint!.overallDataPointIndex == + pointIndex || + selectedSegments[j] + ._currentPoint! + .overallDataPointIndex == + pointIndex) && + selectedSegments[j]._seriesIndex == seriesIndex) { + isSamePointSelect = true; + } + selectedSegments.remove(selectedSegments[j]); + } + + /// Applying unselected color for unselected segments in multiSelect option + else if ((selectedSegment! + ._currentPoint!.overallDataPointIndex == selectedSegments[j] - ._currentPoint! - .overallDataPointIndex == - pointIndex) && - selectedSegments[j]._seriesIndex == seriesIndex) { - isSamePointSelect = true; + ._currentPoint! + .overallDataPointIndex) && + selectedSegment!._seriesIndex == + selectedSegments[j]._seriesIndex) { + final Paint fillPaint = getFillColor(false, currentSegment); + currentSegment.fillPaint = fillPaint; + final Paint strokePaint = + getStrokeColor(false, currentSegment); + currentSegment.strokePaint = strokePaint; + + if ((currentSegment._currentPoint!.overallDataPointIndex == + pointIndex || + selectedSegments[j] + ._currentPoint! + .overallDataPointIndex == + pointIndex) && + selectedSegments[j]._seriesIndex == seriesIndex) { + isSamePointSelect = true; + } + unselectedSegments!.add(selectedSegments[j]); + selectedSegments.remove(selectedSegments[j]); } - unselectedSegments!.add(selectedSegments[j]); - selectedSegments.remove(selectedSegments[j]); } } } + } else { + unselectedSegments?.clear(); + isSamePointSelect = changeColorAndPopSelectedSegments( + selectedSegments, isSamePointSelect); } - } else { - unselectedSegments?.clear(); - isSamePointSelect = changeColorAndPopSelectedSegments( - selectedSegments, isSamePointSelect); - } - /// To check that the selection setting is enable or not - if (seriesRenderer._isSelectionEnable) { - if (!isSamePointSelect) { - seriesRenderer._seriesType == 'column' || - seriesRenderer._seriesType == 'bar' || - seriesRenderer._seriesType == 'scatter' || - seriesRenderer._seriesType == 'bubble' || - seriesRenderer._seriesType.contains('stackedcolumn') || - seriesRenderer._seriesType.contains('stackedbar') || - seriesRenderer._seriesType == 'rangecolumn' || - seriesRenderer._seriesType == 'waterfall' - ? isSelected = checkPosition() - : isSelected = true; - unselectedSegments?.clear(); - for (int i = - _chartState._chartSeries.visibleSeriesRenderers.length - 1; - i >= 0; - i--) { - final CartesianSeriesRenderer seriesRenderer = - _chartState._chartSeries.visibleSeriesRenderers[i]; - if (isSelected) { - for (int j = 0; j < seriesRenderer._segments.length; j++) { - currentSegment = seriesRenderer._segments[j]; - if (currentSegment!.currentSegmentIndex == null || - pointIndex == null) { - break; + /// To check that the selection setting is enable or not + if (seriesRenderer._isSelectionEnable == true) { + if (!isSamePointSelect) { + seriesRenderer._seriesType == 'column' || + seriesRenderer._seriesType == 'bar' || + seriesRenderer._seriesType == 'scatter' || + seriesRenderer._seriesType == 'bubble' || + seriesRenderer._seriesType.contains('stackedcolumn') == + true || + seriesRenderer._seriesType.contains('stackedbar') == true || + seriesRenderer._seriesType == 'rangecolumn' || + seriesRenderer._seriesType == 'waterfall' + ? isSelected = checkPosition() + : isSelected = true; + unselectedSegments?.clear(); + for (int i = + _chartState._chartSeries.visibleSeriesRenderers.length - 1; + i >= 0; + i--) { + final CartesianSeriesRenderer seriesRenderer = + _chartState._chartSeries.visibleSeriesRenderers[i]; + if (isSelected) { + for (int j = 0; j < seriesRenderer._segments.length; j++) { + currentSegment = seriesRenderer._segments[j]; + if (currentSegment!.currentSegmentIndex == null || + pointIndex == null) { + break; + } + (seriesRenderer._seriesType.contains('area') + ? currentSegment!.currentSegmentIndex == + pointIndex + : currentSegment! + ._currentPoint!.overallDataPointIndex == + pointIndex) && + currentSegment!._seriesIndex == seriesIndex + ? selectedSegments.add(seriesRenderer._segments[j]) + : unselectedSegments!.add(seriesRenderer._segments[j]); } - (seriesRenderer._seriesType.contains('area') - ? currentSegment!.currentSegmentIndex == pointIndex - : currentSegment! - ._currentPoint!.overallDataPointIndex == - pointIndex) && - currentSegment!._seriesIndex == seriesIndex - ? selectedSegments.add(seriesRenderer._segments[j]) - : unselectedSegments!.add(seriesRenderer._segments[j]); - } - /// Giving color to unselected segments - _unselectedSegmentsColors(unselectedSegments!); + /// Giving color to unselected segments + _unselectedSegmentsColors(unselectedSegments!); - /// Giving Color to selected segments - _selectedSegmentsColors(selectedSegments); + /// Giving Color to selected segments + _selectedSegmentsColors(selectedSegments); + } } + } else { + isSelected = true; } - } else { - isSelected = true; } } } ///For Series Mode else if ((selectionType ?? chart.selectionType) == SelectionType.series) { - bool isSamePointSelect = false; - - for (int i = 0; - i < _chartState._chartSeries.visibleSeriesRenderers.length; - i++) { - final CartesianSeriesRenderer seriesRenderer = - _chartState._chartSeries.visibleSeriesRenderers[i]; - for (int k = 0; k < seriesRenderer._segments.length; k++) { - currentSegment = seriesRenderer._segments[k]; - final ChartSegment compareSegment = seriesRenderer._segments[k]; - if (currentSegment!.currentSegmentIndex != - compareSegment.currentSegmentIndex && - currentSegment!._seriesIndex != compareSegment._seriesIndex) { - isSelected = false; + if (!(seriesRenderer._selectionBehavior.toggleSelection == false && + _isSamePointSelected(selectedSegments))) { + bool isSamePointSelect = false; + + for (int i = 0; + i < _chartState._chartSeries.visibleSeriesRenderers.length; + i++) { + final CartesianSeriesRenderer seriesRenderer = + _chartState._chartSeries.visibleSeriesRenderers[i]; + for (int k = 0; k < seriesRenderer._segments.length; k++) { + currentSegment = seriesRenderer._segments[k]; + final ChartSegment compareSegment = seriesRenderer._segments[k]; + if (currentSegment!.currentSegmentIndex != + compareSegment.currentSegmentIndex && + currentSegment!._seriesIndex != compareSegment._seriesIndex) { + isSelected = false; + } } } - } - /// Executes only when final selected segment became unselected - if (selectedSegments.length == seriesRenderer._segments.length) { - changeColorAndPopUnselectedSegments(unselectedSegments!); - } + /// Executes only when final selected segment became unselected + if (selectedSegments.length == seriesRenderer._segments.length) { + changeColorAndPopUnselectedSegments(unselectedSegments!); + } - /// Executes when multiSelect option is enabled - bool multiSelect = false; - if (chart.enableMultiSelection) { - if (selectedSegments.isNotEmpty) { - selectedSegment = getTappedSegment(); - - /// To identify that tapped again in any one of the selected segments - if (selectedSegment != null) { - for (int k = 0; k < selectedSegments.length; k++) { - if (seriesIndex == selectedSegments[k]._seriesIndex) { - multiSelect = true; - break; + /// Executes when multiSelect option is enabled + bool multiSelect = false; + if (chart.enableMultiSelection == true) { + if (selectedSegments.isNotEmpty) { + selectedSegment = getTappedSegment(); + + /// To identify that tapped again in any one of the selected segments + if (selectedSegment != null) { + for (int k = 0; k < selectedSegments.length; k++) { + if (seriesIndex == selectedSegments[k]._seriesIndex) { + multiSelect = true; + break; + } } } - } - /// Executes when tapped again in one of the selected segments - if (multiSelect) { - ChartSegment currentSegment; - for (int j = selectedSegments.length - 1; j >= 0; j--) { - seriesRenderer = _chartState._chartSeries - .visibleSeriesRenderers[selectedSegments[j]._seriesIndex]; + /// Executes when tapped again in one of the selected segments + if (multiSelect) { + ChartSegment currentSegment; + for (int j = selectedSegments.length - 1; j >= 0; j--) { + seriesRenderer = _chartState._chartSeries + .visibleSeriesRenderers[selectedSegments[j]._seriesIndex]; + + currentSegment = + (seriesRenderer._seriesType.contains('area') == false && + seriesRenderer._seriesType != 'fastline') + ? seriesRenderer + ._segments[selectedSegments[j].currentSegmentIndex] + : seriesRenderer._segments[0]; + + /// Applying series fill when all last selected segment becomes unselected + if (seriesRenderer._seriesType.contains('area') == false && + seriesRenderer._seriesType != 'fastline') { + if ((selectedSegment!._seriesIndex == + selectedSegments[j]._seriesIndex) && + (selectedSegments.length <= + seriesRenderer._segments.length)) { + final Paint fillPaint = + getDefaultFillColor(null, null, currentSegment); + final Paint strokePaint = + getDefaultStrokeColor(null, null, currentSegment); + currentSegment.fillPaint = fillPaint; + currentSegment.strokePaint = strokePaint; + if (selectedSegments[j] + ._currentPoint! + .overallDataPointIndex == + pointIndex && + selectedSegments[j]._seriesIndex == seriesIndex) { + isSamePointSelect = true; + } + selectedSegments.remove(selectedSegments[j]); + } - if (!seriesRenderer._seriesType.contains('area') && - seriesRenderer._seriesType != 'fastline') { - currentSegment = seriesRenderer - ._segments[selectedSegments[j].currentSegmentIndex]; - } else { - currentSegment = seriesRenderer._segments[0]; - } + /// Applying unselected color for unselected segments in multiSelect option + else if (selectedSegment!._seriesIndex == + selectedSegments[j]._seriesIndex) { + final Paint fillPaint = getFillColor(false, currentSegment); + final Paint strokePaint = + getStrokeColor(false, currentSegment); + currentSegment.fillPaint = fillPaint; + currentSegment.strokePaint = strokePaint; + if (selectedSegments[j] + ._currentPoint! + .overallDataPointIndex == + pointIndex && + selectedSegments[j]._seriesIndex == seriesIndex) { + isSamePointSelect = true; + } + unselectedSegments!.add(selectedSegments[j]); + selectedSegments.remove(selectedSegments[j]); + } + } else { + if ((selectedSegment!._seriesIndex == + selectedSegments[j]._seriesIndex) && + (selectedSegments.length <= + seriesRenderer._segments.length)) { + final Paint fillPaint = + getDefaultFillColor(null, null, currentSegment); + final Paint strokePaint = + getDefaultStrokeColor(null, null, currentSegment); + currentSegment.fillPaint = fillPaint; + currentSegment.strokePaint = strokePaint; + if (selectedSegments[j]._seriesIndex == seriesIndex) { + isSamePointSelect = true; + } + selectedSegments.remove(selectedSegments[j]); + } - /// Applying series fill when all last selected segment becomes unselected - if (!seriesRenderer._seriesType.contains('area') && - seriesRenderer._seriesType != 'fastline') { - if ((selectedSegment!._seriesIndex == - selectedSegments[j]._seriesIndex) && - (selectedSegments.length <= - seriesRenderer._segments.length)) { - final Paint fillPaint = - getDefaultFillColor(null, null, currentSegment); - final Paint strokePaint = - getDefaultStrokeColor(null, null, currentSegment); - currentSegment.fillPaint = fillPaint; - currentSegment.strokePaint = strokePaint; - if (selectedSegments[j] - ._currentPoint! - .overallDataPointIndex == - pointIndex && - selectedSegments[j]._seriesIndex == seriesIndex) { - isSamePointSelect = true; + /// Applying unselected color for unselected segments in multiSelect option + else if (selectedSegment!._seriesIndex == + selectedSegments[j]._seriesIndex) { + final Paint fillPaint = getFillColor(false, currentSegment); + final Paint strokePaint = + getStrokeColor(false, currentSegment); + currentSegment.fillPaint = fillPaint; + currentSegment.strokePaint = strokePaint; + if (selectedSegments[j]._seriesIndex == seriesIndex) { + isSamePointSelect = true; + } + unselectedSegments!.add(selectedSegments[j]); + selectedSegments.remove(selectedSegments[j]); } - selectedSegments.remove(selectedSegments[j]); } + } + } + } + } else { + ///Executes when multiSelect is not enable + unselectedSegments?.clear(); + isSamePointSelect = changeColorAndPopSelectedSegments( + selectedSegments, isSamePointSelect); + } - /// Applying unselected color for unselected segments in multiSelect option - else if (selectedSegment!._seriesIndex == - selectedSegments[j]._seriesIndex) { - final Paint fillPaint = getFillColor(false, currentSegment); - final Paint strokePaint = - getStrokeColor(false, currentSegment); - currentSegment.fillPaint = fillPaint; - currentSegment.strokePaint = strokePaint; - if (selectedSegments[j] - ._currentPoint! - .overallDataPointIndex == - pointIndex && - selectedSegments[j]._seriesIndex == seriesIndex) { - isSamePointSelect = true; + /// To identify the Tapped segment + if (seriesRenderer._isSelectionEnable == true) { + if (!isSamePointSelect) { + seriesRenderer._seriesType == 'column' || + seriesRenderer._seriesType == 'bar' || + seriesRenderer._seriesType == 'scatter' || + seriesRenderer._seriesType == 'bubble' || + seriesRenderer._seriesType.contains('stackedcolumn') == + true || + seriesRenderer._seriesType.contains('stackedbar') == true || + seriesRenderer._seriesType == 'rangecolumn' || + seriesRenderer._seriesType == 'waterfall' + ? isSelected = checkPosition() + : isSelected = true; + selectedSegment = getTappedSegment(); + if (isSelected) { + /// To Push the Selected and Unselected segment + for (int i = 0; + i < _chartState._chartSeries.visibleSeriesRenderers.length; + i++) { + final CartesianSeriesRenderer seriesRenderer = + _chartState._chartSeries.visibleSeriesRenderers[i]; + if (!seriesRenderer._seriesType.contains('area') && + seriesRenderer._seriesType != 'fastline') { + if (seriesIndex != null) { + for (int k = 0; k < seriesRenderer._segments.length; k++) { + currentSegment = seriesRenderer._segments[k]; + currentSegment!._seriesIndex == seriesIndex + ? selectedSegments.add(seriesRenderer._segments[k]) + : unselectedSegments! + .add(seriesRenderer._segments[k]); + } } - unselectedSegments!.add(selectedSegments[j]); - selectedSegments.remove(selectedSegments[j]); + } else { + currentSegment = seriesRenderer._segments[0]; + currentSegment!._seriesIndex == seriesIndex + ? selectedSegments.add(seriesRenderer._segments[0]) + : unselectedSegments!.add(seriesRenderer._segments[0]); } - } else { - if ((selectedSegment!._seriesIndex == - selectedSegments[j]._seriesIndex) && + + /// Give Color to the Unselected segment + _unselectedSegmentsColors(unselectedSegments!); + + /// Give Color to the Selected segment + _selectedSegmentsColors(selectedSegments); + } + } + } else { + isSelected = true; + } + } + } + } + + /// For Cluster Mode + else if ((selectionType ?? chart.selectionType) == SelectionType.cluster) { + if (!(seriesRenderer._selectionBehavior.toggleSelection == false && + _isSamePointSelected(selectedSegments))) { + bool isSamePointSelect = false; + + /// Executes only when last selected segment became unselected + if (selectedSegments.length == + _chartState._chartSeries.visibleSeriesRenderers.length) { + changeColorAndPopUnselectedSegments(unselectedSegments!); + } + + /// Executes when multiSelect option is enabled + bool multiSelect = false; + if (chart.enableMultiSelection == true) { + if (selectedSegments.isNotEmpty) { + selectedSegment = getTappedSegment(); + + /// To identify that tapped again in any one of the selected segment + if (selectedSegment != null) { + for (int k = 0; k < selectedSegments.length; k++) { + if (selectedSegment!.currentSegmentIndex == + selectedSegments[k].currentSegmentIndex) { + multiSelect = true; + break; + } + } + } + + /// Executes when tapped again in one of the selected segment + if (multiSelect) { + for (int j = selectedSegments.length - 1; j >= 0; j--) { + seriesRenderer = _chartState._chartSeries + .visibleSeriesRenderers[selectedSegments[j]._seriesIndex]; + final ChartSegment currentSegment = seriesRenderer + ._segments[selectedSegments[j].currentSegmentIndex]; + + /// Applying default settings when last selected segment becomes unselected + if ((selectedSegment!.currentSegmentIndex == + selectedSegments[j].currentSegmentIndex) && (selectedSegments.length <= - seriesRenderer._segments.length)) { + _chartState + ._chartSeries.visibleSeriesRenderers.length)) { final Paint fillPaint = getDefaultFillColor(null, null, currentSegment); final Paint strokePaint = getDefaultStrokeColor(null, null, currentSegment); currentSegment.fillPaint = fillPaint; currentSegment.strokePaint = strokePaint; - if (selectedSegments[j]._seriesIndex == seriesIndex) { + + if (selectedSegments[j].currentSegmentIndex == pointIndex && + selectedSegments[j]._seriesIndex == seriesIndex) { isSamePointSelect = true; } + // if(isSamePointSelect == false && ) selectedSegments.remove(selectedSegments[j]); } - /// Applying unselected color for unselected segments in multiSelect option - else if (selectedSegment!._seriesIndex == - selectedSegments[j]._seriesIndex) { + /// Applying unselected color for unselected segment in multiSelect option + else if (selectedSegment!.currentSegmentIndex == + selectedSegments[j].currentSegmentIndex) { final Paint fillPaint = getFillColor(false, currentSegment); final Paint strokePaint = getStrokeColor(false, currentSegment); currentSegment.fillPaint = fillPaint; currentSegment.strokePaint = strokePaint; - if (selectedSegments[j]._seriesIndex == seriesIndex) { + + if (selectedSegments[j].currentSegmentIndex == pointIndex && + selectedSegments[j]._seriesIndex == seriesIndex) { isSamePointSelect = true; } + unselectedSegments!.add(selectedSegments[j]); selectedSegments.remove(selectedSegments[j]); } } } } + } else { + unselectedSegments?.clear(); + + ///Executes when multiSelect is not enable + isSamePointSelect = changeColorAndPopSelectedSegments( + selectedSegments, isSamePointSelect); } - } else { - ///Executes when multiSelect is not enable - unselectedSegments?.clear(); - isSamePointSelect = changeColorAndPopSelectedSegments( - selectedSegments, isSamePointSelect); - } - /// To identify the Tapped segment - if (seriesRenderer._isSelectionEnable) { - if (!isSamePointSelect) { - seriesRenderer._seriesType == 'column' || - seriesRenderer._seriesType == 'bar' || - seriesRenderer._seriesType == 'scatter' || - seriesRenderer._seriesType == 'bubble' || - seriesRenderer._seriesType.contains('stackedcolumn') || - seriesRenderer._seriesType.contains('stackedbar') || - seriesRenderer._seriesType == 'rangecolumn' || - seriesRenderer._seriesType == 'waterfall' - ? isSelected = checkPosition() - : isSelected = true; - selectedSegment = getTappedSegment(); - if (isSelected) { - /// To Push the Selected and Unselected segment - for (int i = 0; - i < _chartState._chartSeries.visibleSeriesRenderers.length; - i++) { - final CartesianSeriesRenderer seriesRenderer = - _chartState._chartSeries.visibleSeriesRenderers[i]; - if (!seriesRenderer._seriesType.contains('area') && - seriesRenderer._seriesType != 'fastline') { - if (seriesIndex != null) { - for (int k = 0; k < seriesRenderer._segments.length; k++) { - currentSegment = seriesRenderer._segments[k]; - currentSegment!._seriesIndex == seriesIndex + /// To identify the Tapped segment + if (seriesRenderer._isSelectionEnable == true) { + if (!isSamePointSelect) { + final bool isSegmentSeries = seriesRenderer._seriesType == + 'column' || + seriesRenderer._seriesType == 'bar' || + seriesRenderer._seriesType == 'scatter' || + seriesRenderer._seriesType == 'bubble' || + seriesRenderer._seriesType.contains('stackedcolumn') == true || + seriesRenderer._seriesType.contains('stackedbar') == true || + seriesRenderer._seriesType == 'rangecolumn' || + seriesRenderer._seriesType == 'waterfall'; + selectedSegment = getTappedSegment(); + isSegmentSeries ? isSelected = checkPosition() : isSelected = true; + if (isSelected) { + /// To Push the Selected and Unselected segments + for (int i = 0; + i < _chartState._chartSeries.visibleSeriesRenderers.length; + i++) { + final CartesianSeriesRenderer seriesRenderer = + _chartState._chartSeries.visibleSeriesRenderers[i]; + if (currentSegment!.currentSegmentIndex == null || + pointIndex == null) { + break; + } + for (int k = 0; k < seriesRenderer._segments.length; k++) { + currentSegment = seriesRenderer._segments[k]; + + if (isSegmentSeries) { + currentSegment!._currentPoint!.xValue == + selectedSegment!._currentPoint!.xValue + ? selectedSegments.add(seriesRenderer._segments[k]) + : unselectedSegments!.add(seriesRenderer._segments[k]); + } else { + currentSegment!.currentSegmentIndex == + selectedSegment!.currentSegmentIndex ? selectedSegments.add(seriesRenderer._segments[k]) : unselectedSegments!.add(seriesRenderer._segments[k]); } } - } else { - currentSegment = seriesRenderer._segments[0]; - currentSegment!._seriesIndex == seriesIndex - ? selectedSegments.add(seriesRenderer._segments[0]) - : unselectedSegments!.add(seriesRenderer._segments[0]); } - /// Give Color to the Unselected segment + /// Giving color to unselected segments _unselectedSegmentsColors(unselectedSegments!); - /// Give Color to the Selected segment + /// Giving Color to selected segments _selectedSegmentsColors(selectedSegments); } + } else { + isSelected = true; } - } else { - isSelected = true; - } - } - } - - /// For Cluster Mode - else if ((selectionType ?? chart.selectionType) == SelectionType.cluster) { - bool isSamePointSelect = false; - - /// Executes only when last selected segment became unselected - if (selectedSegments.length == - _chartState._chartSeries.visibleSeriesRenderers.length) { - changeColorAndPopUnselectedSegments(unselectedSegments!); - } - - /// Executes when multiSelect option is enabled - bool multiSelect = false; - if (chart.enableMultiSelection) { - if (selectedSegments.isNotEmpty) { - selectedSegment = getTappedSegment(); - - /// To identify that tapped again in any one of the selected segment - if (selectedSegment != null) { - for (int k = 0; k < selectedSegments.length; k++) { - if (selectedSegment!.currentSegmentIndex == - selectedSegments[k].currentSegmentIndex) { - multiSelect = true; - break; - } - } - } - - /// Executes when tapped again in one of the selected segment - if (multiSelect) { - for (int j = selectedSegments.length - 1; j >= 0; j--) { - seriesRenderer = _chartState._chartSeries - .visibleSeriesRenderers[selectedSegments[j]._seriesIndex]; - final ChartSegment currentSegment = seriesRenderer - ._segments[selectedSegments[j].currentSegmentIndex]; - - /// Applying default settings when last selected segment becomes unselected - if ((selectedSegment!.currentSegmentIndex == - selectedSegments[j].currentSegmentIndex) && - (selectedSegments.length <= - _chartState._chartSeries.visibleSeriesRenderers.length)) { - final Paint fillPaint = - getDefaultFillColor(null, null, currentSegment); - final Paint strokePaint = - getDefaultStrokeColor(null, null, currentSegment); - currentSegment.fillPaint = fillPaint; - currentSegment.strokePaint = strokePaint; - - if (selectedSegments[j].currentSegmentIndex == pointIndex && - selectedSegments[j]._seriesIndex == seriesIndex) { - isSamePointSelect = true; - } - - selectedSegments.remove(selectedSegments[j]); - } - - /// Applying unselected color for unselected segment in multiSelect option - else if (selectedSegment!.currentSegmentIndex == - selectedSegments[j].currentSegmentIndex) { - final Paint fillPaint = getFillColor(false, currentSegment); - final Paint strokePaint = getStrokeColor(false, currentSegment); - currentSegment.fillPaint = fillPaint; - currentSegment.strokePaint = strokePaint; - - if (selectedSegments[j].currentSegmentIndex == pointIndex && - selectedSegments[j]._seriesIndex == seriesIndex) { - isSamePointSelect = true; - } - - unselectedSegments!.add(selectedSegments[j]); - selectedSegments.remove(selectedSegments[j]); - } - } - } - } - } else { - unselectedSegments?.clear(); - - ///Executes when multiSelect is not enable - isSamePointSelect = changeColorAndPopSelectedSegments( - selectedSegments, isSamePointSelect); - } - - /// To identify the Tapped segment - if (seriesRenderer._isSelectionEnable) { - if (!isSamePointSelect) { - final bool isSegmentSeries = seriesRenderer._seriesType == 'column' || - seriesRenderer._seriesType == 'bar' || - seriesRenderer._seriesType == 'scatter' || - seriesRenderer._seriesType == 'bubble' || - seriesRenderer._seriesType.contains('stackedcolumn') || - seriesRenderer._seriesType.contains('stackedbar') || - seriesRenderer._seriesType == 'rangecolumn' || - seriesRenderer._seriesType == 'waterfall'; - selectedSegment = getTappedSegment(); - isSegmentSeries ? isSelected = checkPosition() : isSelected = true; - if (isSelected) { - /// To Push the Selected and Unselected segments - for (int i = 0; - i < _chartState._chartSeries.visibleSeriesRenderers.length; - i++) { - final CartesianSeriesRenderer seriesRenderer = - _chartState._chartSeries.visibleSeriesRenderers[i]; - if (currentSegment!.currentSegmentIndex == null || - pointIndex == null) { - break; - } - for (int k = 0; k < seriesRenderer._segments.length; k++) { - currentSegment = seriesRenderer._segments[k]; - - if (isSegmentSeries) { - currentSegment!._currentPoint!.xValue == - selectedSegment!._currentPoint!.xValue - ? selectedSegments.add(seriesRenderer._segments[k]) - : unselectedSegments!.add(seriesRenderer._segments[k]); - } else { - currentSegment!.currentSegmentIndex == - selectedSegment!.currentSegmentIndex - ? selectedSegments.add(seriesRenderer._segments[k]) - : unselectedSegments!.add(seriesRenderer._segments[k]); - } - } - } - - /// Giving color to unselected segments - _unselectedSegmentsColors(unselectedSegments!); - - /// Giving Color to selected segments - _selectedSegmentsColors(selectedSegments); - } - } else { - isSelected = true; } } } @@ -1205,31 +1274,32 @@ class _SelectionRenderer { seriesRenderer._seriesType.contains('boxandwhisker')) { startSegment = seriesRenderer._segments[dataPointIndex]; } else { - if (dataPointIndex == 0) { + if (dataPointIndex == 0 && + dataPointIndex < seriesRenderer._segments.length) { startSegment = seriesRenderer._segments[dataPointIndex]; - } else if (dataPointIndex == seriesRenderer._dataPoints.length - 1) { + } else if (dataPointIndex == seriesRenderer._dataPoints.length - 1 && + dataPointIndex - 1 < seriesRenderer._segments.length) { startSegment = seriesRenderer._segments[dataPointIndex - 1]; } else { - startSegment = seriesRenderer._segments[dataPointIndex - 1]; - endSegment = seriesRenderer._segments[dataPointIndex]; + if (dataPointIndex - 1 < seriesRenderer._segments.length) { + startSegment = seriesRenderer._segments[dataPointIndex - 1]; + } + + if (dataPointIndex < seriesRenderer._segments.length) { + endSegment = seriesRenderer._segments[dataPointIndex]; + } } } // ignore: unnecessary_null_comparison - startSegment != null - ? cartesianSeriesIndex = startSegment._seriesIndex - : cartesianSeriesIndex = endSegment!._seriesIndex; -// ignore: unnecessary_null_comparison - startSegment != null - ? cartesianPointIndex = startSegment.currentSegmentIndex - : cartesianPointIndex = endSegment!.currentSegmentIndex; -// ignore: unnecessary_null_comparison if (startSegment != null) { + cartesianSeriesIndex = startSegment._seriesIndex; + cartesianPointIndex = startSegment.currentSegmentIndex; if (_isSegmentIntersect(startSegment, position.dx, position.dy)) { return true; } - } - - if (endSegment != null) { + } else if (endSegment != null) { + cartesianSeriesIndex = endSegment._seriesIndex; + cartesianPointIndex = endSegment.currentSegmentIndex; return _isSegmentIntersect(endSegment, position.dx, position.dy); } } @@ -1279,9 +1349,9 @@ class _SelectionRenderer { firstNearestDataPoints); for (final CartesianChartPoint dataPoint in firstNearestPoints!) { - if (seriesRenderer._seriesType.contains('hilo') || + if (seriesRenderer._seriesType.contains('hilo') == true || seriesRenderer._seriesType == 'candle' || - seriesRenderer._seriesType.contains('boxandwhisker')) { + seriesRenderer._seriesType.contains('boxandwhisker') == true) { nearestDataPointIndex = dataPointIndex; } else { if (dataPointIndex! < dataPoint.overallDataPointIndex!) { @@ -1405,7 +1475,9 @@ class _SelectionRenderer { } } } - if (selectionBehaviorRenderer == null) return; + if (selectionBehaviorRenderer == null) { + return; + } selectionBehaviorRenderer._selectionRenderer!.seriesIndex = seriesIndex; } @@ -1419,13 +1491,12 @@ class _SelectionRenderer { seriesRenderer._seriesType == 'spline' || seriesRenderer._seriesType == 'stepline' || seriesRenderer._seriesType == 'stackedline' || - seriesRenderer._seriesType.contains('hilo') || + seriesRenderer._seriesType.contains('hilo') == true || seriesRenderer._seriesType == 'candle' || - seriesRenderer._seriesType.contains('boxandwhisker') || + seriesRenderer._seriesType.contains('boxandwhisker') == true || seriesRenderer._seriesType == 'stackedline100') { - isSelect = seriesRenderer._isSelectionEnable - ? _isSeriesContainsPoint(seriesRenderer, position) - : false; + isSelect = seriesRenderer._isSelectionEnable == true && + _isSeriesContainsPoint(seriesRenderer, position); if (isSelect) { cartesianPointIndex = getCartesianPointIndex(position); selected = cartesianPointIndex != null; @@ -1435,12 +1506,11 @@ class _SelectionRenderer { } } else { _chartState._renderDatalabelRegions = []; - if (seriesRenderer._seriesType.contains('area') || - seriesRenderer._seriesType == 'fastline') { - getSelectedSeriesIndex(chart, position, seriesRenderer); - } else { - getPointAndSeriesIndex(chart, position, seriesRenderer); - } + (seriesRenderer._seriesType.contains('area') == true || + seriesRenderer._seriesType == 'fastline') + ? getSelectedSeriesIndex(chart, position, seriesRenderer) + : getPointAndSeriesIndex(chart, position, seriesRenderer); + select = seriesRenderer._selectionBehaviorRenderer._selectionRenderer .isCartesianSelection(chart, seriesRenderer, pointIndex, seriesIndex); } @@ -1461,7 +1531,7 @@ class _SelectionRenderer { for (int i = 0; i < selectedSegments.length; i++) { if (selectedSegments[i]._seriesIndex == currentSegment._seriesIndex && (_isInteraction || currentSegment._oldSegmentIndex != -1) && - (seriesRenderer._seriesType.contains('area') + (seriesRenderer._seriesType.contains('area') == true ? selectedSegments[i].currentSegmentIndex == currentSegment.currentSegmentIndex : selectedSegments[i]._currentPoint!.overallDataPointIndex == @@ -1484,7 +1554,7 @@ class _SelectionRenderer { (currentSegment._oldSegmentIndex == -1 || currentSegment._oldSegmentIndex != currentSegment.currentSegmentIndex || - seriesRenderer._seriesType.contains('area') + seriesRenderer._seriesType.contains('area') == true ? unselectedSegments![i].currentSegmentIndex == currentSegment.currentSegmentIndex : unselectedSegments![i]._currentPoint?.overallDataPointIndex == @@ -1497,8 +1567,9 @@ class _SelectionRenderer { } } - SelectionArgs getSelectionEventArgs(dynamic series, int seriesIndex, - int pointIndex, CartesianSeriesRenderer seriesRender) { + SelectionArgs getSelectionEventArgs(CartesianSeries series, + int seriesIndex, int pointIndex, CartesianSeriesRenderer seriesRender) { + // ignore: unnecessary_null_comparison if (series != null) { selectionArgs = SelectionArgs( seriesRenderer: seriesRenderer, @@ -1506,17 +1577,6 @@ class _SelectionRenderer { viewportPointIndex: pointIndex, pointIndex: seriesRender ._visibleDataPoints![pointIndex].overallDataPointIndex!); - final dynamic selectionBehavior = seriesRenderer._selectionBehavior; - selectionArgs!.selectedBorderColor = - selectionBehavior.selectedBorderColor; - selectionArgs!.unselectedBorderColor = - selectionBehavior.unselectedBorderColor; - selectionArgs!.selectedBorderWidth = - selectionBehavior.selectedBorderWidth; - selectionArgs!.unselectedBorderWidth = - selectionBehavior.unselectedBorderWidth; - selectionArgs!.selectedColor = selectionBehavior.selectedColor; - selectionArgs!.unselectedColor = selectionBehavior.unselectedColor; } return selectionArgs!; } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/trackball.dart b/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/trackball.dart index 52ce34264..396b4edfa 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/trackball.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/trackball.dart @@ -6,6 +6,7 @@ part of charts; /// This feature can be enabled using enable property of [TrackballBehavior]. /// /// Provides options to customize the [activationMode], [tooltipDisplayMode], [lineType] and [tooltipSettings]. +@immutable class TrackballBehavior { /// Creating an argument constructor of TrackballBehavior class. TrackballBehavior({ @@ -291,6 +292,51 @@ class TrackballBehavior { ///``` final ChartTrackballBuilder? builder; + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is TrackballBehavior && + other.activationMode == activationMode && + other.lineType == lineType && + other.tooltipDisplayMode == tooltipDisplayMode && + other.tooltipAlignment == tooltipAlignment && + other.tooltipSettings == tooltipSettings && + other.lineDashArray == lineDashArray && + other.markerSettings == markerSettings && + other.enable == enable && + other.lineColor == lineColor && + other.lineWidth == lineWidth && + other.shouldAlwaysShow == shouldAlwaysShow && + other.builder == builder && + other.hideDelay == hideDelay; + } + + @override + int get hashCode { + final List values = [ + activationMode, + lineType, + tooltipDisplayMode, + tooltipAlignment, + tooltipSettings, + markerSettings, + lineDashArray, + enable, + lineColor, + lineWidth, + shouldAlwaysShow, + builder, + hideDelay + ]; + return hashList(values); + } + SfCartesianChartState? _chartState; ///Options to customize the markers that are displayed when trackball is enabled. @@ -326,7 +372,7 @@ class TrackballBehavior { final ChartAxisRenderer xAxisRenderer = seriesRenderer._xAxisRenderer!; if (coordinateUnit != 'pixel') { final _ChartLocation location = _calculatePoint( - (x is DateTime && !(xAxisRenderer is DateTimeCategoryAxisRenderer)) + (x is DateTime && (xAxisRenderer is! DateTimeCategoryAxisRenderer)) ? x.millisecondsSinceEpoch : ((x is DateTime && xAxisRenderer is DateTimeCategoryAxisRenderer) @@ -355,7 +401,7 @@ class TrackballBehavior { if (trackballBehaviorRenderer._trackballPainter != null) { trackballBehaviorRenderer._trackballPainter!.canResetPath = false; - chartState._trackballRepaintNotifier.value++; + chartState._repaintNotifiers['trackball']!.value++; } } @@ -377,8 +423,8 @@ class TrackballBehavior { } if (trackballBehaviorRenderer._trackballPainter != null) { trackballBehaviorRenderer._trackballPainter!.canResetPath = false; - trackballBehaviorRenderer - ._trackballPainter!.chartState._trackballRepaintNotifier.value++; + trackballBehaviorRenderer._trackballPainter!.chartState + ._repaintNotifiers['trackball']!.value++; } } } @@ -394,15 +440,15 @@ class TrackballBehavior { if (chartState._chart.trackballBehavior.activationMode == ActivationMode.doubleTap) { trackballBehaviorRenderer._trackballPainter!.canResetPath = false; - ValueNotifier(trackballBehaviorRenderer - ._trackballPainter!.chartState._trackballRepaintNotifier.value++); + ValueNotifier(trackballBehaviorRenderer._trackballPainter! + .chartState._repaintNotifiers['trackball']!.value++); if (trackballBehaviorRenderer._trackballPainter!.timer != null) { trackballBehaviorRenderer._trackballPainter!.timer?.cancel(); } } if (!_chartState!._isTouchUp) { - trackballBehaviorRenderer - ._trackballPainter!.chartState._trackballRepaintNotifier.value++; + trackballBehaviorRenderer._trackballPainter!.chartState + ._repaintNotifiers['trackball']!.value++; trackballBehaviorRenderer._trackballPainter!.canResetPath = true; } else { final double duration = @@ -411,7 +457,7 @@ class TrackballBehavior { trackballBehaviorRenderer._trackballPainter!.timer = Timer(Duration(milliseconds: duration.toInt()), () { trackballBehaviorRenderer._trackballPainter!.chartState - ._trackballRepaintNotifier.value++; + ._repaintNotifiers['trackball']!.value++; trackballBehaviorRenderer._trackballPainter!.canResetPath = true; }); } @@ -442,10 +488,11 @@ class TrackballBehaviorRenderer with ChartBehavior { TrackballBehavior get _trackballBehavior => _chart.trackballBehavior; /// Check whether long press activated or not . + // ignore: prefer_final_fields bool _isLongPressActivated = false; /// check whether onPointerMove or not. - /// ignore: prefer_final_fields + // ignore: prefer_final_fields bool _isMoving = false; /// Touch position @@ -462,10 +509,11 @@ class TrackballBehaviorRenderer with ChartBehavior { late double _xPos; //ignore: unused_field late double _yPos; - List _points = []; + List> _points = >[]; List _currentPointIndices = []; List _visibleSeriesIndices = []; - List _visibleSeriesList = []; + List> _visibleSeriesList = + >[]; late TrackballGroupingModeInfo _groupingModeInfo; List<_ChartPointInfo> _chartPointInfo = <_ChartPointInfo>[]; List _tooltipTop = []; @@ -600,10 +648,10 @@ class TrackballBehaviorRenderer with ChartBehavior { trackballTemplateState._markerShapes = _markerShapes; trackballTemplateState.refresh(); } - _points = []; + _points = >[]; _currentPointIndices = []; _visibleSeriesIndices = []; - _visibleSeriesList = []; + _visibleSeriesList = >[]; _tooltipTop.clear(); _tooltipBottom.clear(); } @@ -615,11 +663,9 @@ class TrackballBehaviorRenderer with ChartBehavior { _chartPointInfo = <_ChartPointInfo>[]; _visiblePoints = <_ClosestPoints>[]; _markerShapes.clear(); - _tooltipTop = []; - _tooltipBottom = []; + _tooltipTop = _tooltipBottom = _visibleLocation = []; _trackballPainter?._tooltipTop = []; _trackballPainter?._tooltipBottom = []; - _visibleLocation = []; final Rect seriesBounds = _axisClipRect; _tapPosition = position; double? xPos = 0, @@ -641,10 +687,8 @@ class TrackballBehaviorRenderer with ChartBehavior { maxYPos, maxXPos; int seriesIndex = 0, index; - final List seriesAxisRenderers = []; - CartesianSeriesRenderer visibleSeriesRenderer, cartesianSeriesRenderer; - ChartAxisRenderer? chartAxisRenderer; - ChartAxisRenderer xAxisRenderer, yAxisRenderer; + late CartesianSeriesRenderer cartesianSeriesRenderer; + ChartAxisRenderer chartAxisRenderer, xAxisRenderer, yAxisRenderer; CartesianChartPoint chartDataPoint; _ChartAxis chartAxis; String seriesType, labelValue, seriesName; @@ -667,275 +711,214 @@ class TrackballBehaviorRenderer with ChartBehavior { final TrackballDisplayMode tooltipDisplayMode = _chartState._chart.trackballBehavior.tooltipDisplayMode; _ChartLocation highLocation, maxLocation; - for (final CartesianSeriesRenderer seriesRenderer - in _chartState._seriesRenderers) { - visibleSeriesRenderer = seriesRenderer; - chartAxisRenderer = visibleSeriesRenderer._xAxisRenderer!; - // ignore: unnecessary_null_comparison - if (chartAxisRenderer == null) { + chartAxisRenderer = _chartState._seriesRenderers[0]._xAxisRenderer!; + for (final CartesianSeriesRenderer axisSeriesRenderer + in chartAxisRenderer._seriesRenderers) { + cartesianSeriesRenderer = axisSeriesRenderer; + seriesType = cartesianSeriesRenderer._seriesType; + _isRangeSeries = seriesType.contains('range') || + seriesType.contains('hilo') || + seriesType == 'candle'; + _isBoxSeries = seriesType == 'boxandwhisker'; + if (axisSeriesRenderer._visible == false || + (axisSeriesRenderer._dataPoints.isEmpty && + !axisSeriesRenderer._isRectSeries)) { continue; } - if (!seriesAxisRenderers.contains(chartAxisRenderer)) { - seriesAxisRenderers.add(chartAxisRenderer); - for (final CartesianSeriesRenderer axisSeriesRenderer - in chartAxisRenderer._seriesRenderers) { - cartesianSeriesRenderer = axisSeriesRenderer; - seriesType = cartesianSeriesRenderer._seriesType; - _isRangeSeries = seriesType.contains('range') || - seriesType.contains('hilo') || - seriesType == 'candle'; - _isBoxSeries = seriesType == 'boxandwhisker'; - if (axisSeriesRenderer._visible == false || - (axisSeriesRenderer._dataPoints.isEmpty && - !axisSeriesRenderer._isRectSeries)) { - continue; + if (cartesianSeriesRenderer._dataPoints.isNotEmpty) { + final List>? nearestDataPoints = + _getNearestChartPoints( + position.dx, + position.dy, + cartesianSeriesRenderer._xAxisRenderer!, + cartesianSeriesRenderer._yAxisRenderer!, + cartesianSeriesRenderer); + for (final CartesianChartPoint dataPoint + in nearestDataPoints!) { + index = axisSeriesRenderer._dataPoints.indexOf(dataPoint); + chartDataPoint = cartesianSeriesRenderer._dataPoints[index]; + xAxisRenderer = cartesianSeriesRenderer._xAxisRenderer!; + yAxisRenderer = cartesianSeriesRenderer._yAxisRenderer!; + chartAxis = cartesianSeriesRenderer._chartState!._chartAxis; + invertedAxis = _chartState._requireInvertedAxis; + series = cartesianSeriesRenderer._series; + xValue = chartDataPoint.xValue; + if (seriesType != 'boxandwhisker') { + yValue = chartDataPoint.yValue; } - if (cartesianSeriesRenderer._dataPoints.isNotEmpty) { - final List>? nearestDataPoints = - _getNearestChartPoints( - position.dx, - position.dy, - chartAxisRenderer, - visibleSeriesRenderer._yAxisRenderer!, - cartesianSeriesRenderer); - for (final CartesianChartPoint dataPoint - in nearestDataPoints!) { - index = axisSeriesRenderer._dataPoints.indexOf(dataPoint); - chartDataPoint = cartesianSeriesRenderer._dataPoints[index]; - xAxisRenderer = cartesianSeriesRenderer._xAxisRenderer!; - yAxisRenderer = cartesianSeriesRenderer._yAxisRenderer!; - chartAxis = cartesianSeriesRenderer._chartState!._chartAxis; - invertedAxis = _chartState._requireInvertedAxis; - series = cartesianSeriesRenderer._series; - xValue = chartDataPoint.xValue; - if (seriesType != 'boxandwhisker') { - yValue = chartDataPoint.yValue; + minimumValue = chartDataPoint.minimum; + maximumValue = chartDataPoint.maximum; + lowerQuartileValue = chartDataPoint.lowerQuartile; + upperQuartileValue = chartDataPoint.upperQuartile; + meanValue = chartDataPoint.mean; + highValue = chartDataPoint.high; + lowValue = chartDataPoint.low; + openValue = chartDataPoint.open; + closeValue = chartDataPoint.close; + seriesName = + cartesianSeriesRenderer._series.name ?? 'Series $seriesIndex'; + bubbleSize = chartDataPoint.bubbleSize; + cumulativeValue = chartDataPoint.cumulativeValue; + axisClipRect = _calculatePlotOffset( + chartAxis._axisClipRect, + Offset(xAxisRenderer._axis.plotOffset, + yAxisRenderer._axis.plotOffset)); + cummulativePos = _calculatePoint( + xValue!, + cumulativeValue, + xAxisRenderer, + yAxisRenderer, + invertedAxis, + series, + axisClipRect) + .y; + xPos = _calculatePoint( + xValue, + seriesType.contains('stacked') ? cumulativeValue : yValue, + xAxisRenderer, + yAxisRenderer, + invertedAxis, + series, + axisClipRect) + .x; + if (!xPos.toDouble().isNaN) { + if (seriesIndex == 0 || + ((leastX! - position.dx) > (leastX - xPos))) { + leastX = xPos; + } + labelValue = _getTrackballLabelText( + cartesianSeriesRenderer, + xValue, + yValue, + lowValue, + highValue, + openValue, + closeValue, + minimumValue, + maximumValue, + lowerQuartileValue, + upperQuartileValue, + meanValue, + seriesName, + bubbleSize, + cumulativeValue, + dataPoint); + yPos = seriesType.contains('stacked') + ? cummulativePos + : _calculatePoint(xValue, yValue, xAxisRenderer, yAxisRenderer, + invertedAxis, series, axisClipRect) + .y; + if (_isRangeSeries) { + lowYPos = _calculatePoint(xValue, lowValue, xAxisRenderer, + yAxisRenderer, invertedAxis, series, axisClipRect) + .y; + highLocation = _calculatePoint(xValue, highValue, xAxisRenderer, + yAxisRenderer, invertedAxis, series, axisClipRect); + highYPos = highLocation.y; + highXPos = highLocation.x; + if (seriesType == 'hiloopenclose' || seriesType == 'candle') { + openXPos = dataPoint.openPoint!.x; + openYPos = dataPoint.openPoint!.y; + closeXPos = dataPoint.closePoint!.x; + closeYPos = dataPoint.closePoint!.y; } - minimumValue = chartDataPoint.minimum; - maximumValue = chartDataPoint.maximum; - lowerQuartileValue = chartDataPoint.lowerQuartile; - upperQuartileValue = chartDataPoint.upperQuartile; - meanValue = chartDataPoint.mean; - highValue = chartDataPoint.high; - lowValue = chartDataPoint.low; - openValue = chartDataPoint.open; - closeValue = chartDataPoint.close; - seriesName = - cartesianSeriesRenderer._series.name ?? 'Series $seriesIndex'; - bubbleSize = chartDataPoint.bubbleSize; - cumulativeValue = chartDataPoint.cumulativeValue; - axisClipRect = _calculatePlotOffset( - chartAxis._axisClipRect, - Offset(xAxisRenderer._axis.plotOffset, - yAxisRenderer._axis.plotOffset)); - cummulativePos = _calculatePoint( - xValue!, - cumulativeValue, - xAxisRenderer, - yAxisRenderer, - invertedAxis, - series, - axisClipRect) + } else if (seriesType == 'boxandwhisker') { + minYPos = _calculatePoint(xValue, minimumValue, xAxisRenderer, + yAxisRenderer, invertedAxis, series, axisClipRect) .y; - xPos = _calculatePoint( - xValue, - seriesType.contains('stacked') ? cumulativeValue : yValue, - xAxisRenderer, - yAxisRenderer, - invertedAxis, - series, - axisClipRect) - .x; - if (!xPos.toDouble().isNaN) { - if (seriesIndex == 0 || - ((leastX! - position.dx) > (leastX - xPos))) { - leastX = xPos; - } - labelValue = _getTrackballLabelText( - cartesianSeriesRenderer, - xValue, - yValue, - lowValue, - highValue, - openValue, - closeValue, - minimumValue, - maximumValue, - lowerQuartileValue, - upperQuartileValue, - meanValue, - seriesName, - bubbleSize, - cumulativeValue, - dataPoint); - yPos = seriesType.contains('stacked') - ? cummulativePos - : _calculatePoint(xValue, yValue, xAxisRenderer, - yAxisRenderer, invertedAxis, series, axisClipRect) - .y; - if (_isRangeSeries) { - lowYPos = _calculatePoint(xValue, lowValue, xAxisRenderer, - yAxisRenderer, invertedAxis, series, axisClipRect) - .y; - highLocation = _calculatePoint( - xValue, - highValue, - xAxisRenderer, - yAxisRenderer, - invertedAxis, - series, - axisClipRect); - highYPos = highLocation.y; - highXPos = highLocation.x; - if (seriesType == 'hiloopenclose' || seriesType == 'candle') { - openXPos = dataPoint.openPoint!.x; - openYPos = dataPoint.openPoint!.y; - closeXPos = dataPoint.closePoint!.x; - closeYPos = dataPoint.closePoint!.y; - } - } else if (seriesType == 'boxandwhisker') { - minYPos = _calculatePoint(xValue, minimumValue, xAxisRenderer, - yAxisRenderer, invertedAxis, series, axisClipRect) - .y; - maxLocation = _calculatePoint( - xValue, - maximumValue, - xAxisRenderer, - yAxisRenderer, - invertedAxis, - series, - axisClipRect); - maxXPos = maxLocation.x; - maxYPos = maxLocation.y; - lowerXPos = dataPoint.lowerQuartilePoint!.x; - lowerYPos = dataPoint.lowerQuartilePoint!.y; - upperXPos = dataPoint.upperQuartilePoint!.x; - upperYPos = dataPoint.upperQuartilePoint!.y; - } - final Rect rect = seriesBounds.intersect(Rect.fromLTWH( - xPos - 1, + maxLocation = _calculatePoint(xValue, maximumValue, xAxisRenderer, + yAxisRenderer, invertedAxis, series, axisClipRect); + maxXPos = maxLocation.x; + maxYPos = maxLocation.y; + lowerXPos = dataPoint.lowerQuartilePoint!.x; + lowerYPos = dataPoint.lowerQuartilePoint!.y; + upperXPos = dataPoint.upperQuartilePoint!.x; + upperYPos = dataPoint.upperQuartilePoint!.y; + } + final Rect rect = seriesBounds.intersect(Rect.fromLTWH( + xPos - 1, + _isRangeSeries + ? highYPos! - 1 + : _isBoxSeries + ? maxYPos! - 1 + : yPos - 1, + 2, + 2)); + if (seriesBounds.contains(Offset( + xPos, _isRangeSeries - ? highYPos! - 1 + ? highYPos! : _isBoxSeries - ? maxYPos! - 1 - : yPos - 1, - 2, - 2)); - if (seriesBounds.contains(Offset( - xPos, - _isRangeSeries - ? highYPos! - : _isBoxSeries - ? maxYPos! - : yPos)) || - seriesBounds.overlaps(rect)) { - _visiblePoints.add(_ClosestPoints( - closestPointX: !_isRangeSeries - ? xPos - : _isBoxSeries - ? maxXPos! - : highXPos!, - closestPointY: _isRangeSeries - ? highYPos! - : _isBoxSeries - ? maxYPos! - : yPos)); - _addChartPointInfo( - cartesianSeriesRenderer, - xPos, - yPos, - index, - !_isTrackballTemplate ? labelValue : null, - seriesIndex, - lowYPos, - highXPos, - highYPos, - openXPos, - openYPos, - closeXPos, - closeYPos, - minYPos, - maxXPos, - maxYPos, - lowerXPos, - lowerYPos, - upperXPos, - upperYPos); - if (tooltipDisplayMode == - TrackballDisplayMode.groupAllPoints && - leastX >= seriesBounds.left) { - invertedAxis ? yPos = leastX : xPos = leastX; - } - } + ? maxYPos! + : yPos)) || + seriesBounds.overlaps(rect)) { + _visiblePoints.add(_ClosestPoints( + closestPointX: !_isRangeSeries + ? xPos + : _isBoxSeries + ? maxXPos! + : highXPos!, + closestPointY: _isRangeSeries + ? highYPos! + : _isBoxSeries + ? maxYPos! + : yPos)); + _addChartPointInfo( + cartesianSeriesRenderer, + xPos, + yPos, + index, + !_isTrackballTemplate ? labelValue : null, + seriesIndex, + lowYPos, + highXPos, + highYPos, + openXPos, + openYPos, + closeXPos, + closeYPos, + minYPos, + maxXPos, + maxYPos, + lowerXPos, + lowerYPos, + upperXPos, + upperYPos); + if (tooltipDisplayMode == TrackballDisplayMode.groupAllPoints && + leastX >= seriesBounds.left) { + invertedAxis ? yPos = leastX : xPos = leastX; } } - seriesIndex++; - } - _validateNearestXValue( - leastX!, cartesianSeriesRenderer, position.dx, position.dy); - } - if (_visiblePoints.isNotEmpty) { - invertedAxis - ? _visiblePoints.sort((_ClosestPoints a, _ClosestPoints b) => - a.closestPointX.compareTo(b.closestPointX)) - : _visiblePoints.sort((_ClosestPoints a, _ClosestPoints b) => - a.closestPointY.compareTo(b.closestPointY)); - } - if (_chartPointInfo.isNotEmpty) { - if (tooltipDisplayMode != TrackballDisplayMode.groupAllPoints) { - invertedAxis - ? _chartPointInfo.sort((_ChartPointInfo a, _ChartPointInfo b) => - a.xPosition!.compareTo(b.xPosition!)) - : _chartPointInfo.sort((_ChartPointInfo a, _ChartPointInfo b) => - a.yPosition!.compareTo(b.yPosition!)); - } - if (tooltipDisplayMode == TrackballDisplayMode.nearestPoint || - (seriesRenderer._isRectSeries && - tooltipDisplayMode != TrackballDisplayMode.groupAllPoints)) { - _validateNearestPointForAllSeries( - leastX!, _chartPointInfo, position.dx, position.dy); } } + seriesIndex++; } + _validateNearestXValue( + leastX!, cartesianSeriesRenderer, position.dx, position.dy); } - _triggerTrackballRenderCallback(); - _chartPointInfo = _getValidPoints(_chartPointInfo, position); - } - - /// To get valid points of trackball - List<_ChartPointInfo> _getValidPoints( - List<_ChartPointInfo> points, Offset position) { - final List<_ChartPointInfo> validPoints = <_ChartPointInfo>[]; - for (final _ChartPointInfo point in points) { - if (validPoints.isEmpty) { - validPoints.add(point); - } else if (validPoints[0].seriesRenderer!._xAxisRenderer == - point.seriesRenderer!._xAxisRenderer) { - if (!point.seriesRenderer!._chartState!._requireInvertedAxis) { - if ((validPoints[0].xPosition! - position.dx).abs() == - (point.xPosition! - position.dx).abs() || - validPoints[0].series != point.series) { - validPoints.add(point); - } - } else if ((validPoints[0].yPosition! - position.dy).abs() == - (point.yPosition! - position.dy).abs() || - validPoints[0].series != point.series) { - validPoints.add(point); - } - } else if ((validPoints[0].xPosition! - position.dx).abs() > - (point.xPosition! - position.dx).abs()) { - validPoints.clear(); - validPoints.add(point); - } else if ((validPoints[0].xPosition! - position.dx).abs() == - (point.xPosition! - position.dx).abs()) { - if ((validPoints[0].yPosition! - position.dy).abs() > - (point.yPosition! - position.dy).abs()) { - validPoints.clear(); - validPoints.add(point); - } + if (_visiblePoints.isNotEmpty) { + invertedAxis + ? _visiblePoints.sort((_ClosestPoints a, _ClosestPoints b) => + a.closestPointX.compareTo(b.closestPointX)) + : _visiblePoints.sort((_ClosestPoints a, _ClosestPoints b) => + a.closestPointY.compareTo(b.closestPointY)); + } + if (_chartPointInfo.isNotEmpty) { + if (tooltipDisplayMode != TrackballDisplayMode.groupAllPoints) { + invertedAxis + ? _chartPointInfo.sort((_ChartPointInfo a, _ChartPointInfo b) => + a.xPosition!.compareTo(b.xPosition!)) + : _chartPointInfo.sort((_ChartPointInfo a, _ChartPointInfo b) => + a.yPosition!.compareTo(b.yPosition!)); + } + if (tooltipDisplayMode == TrackballDisplayMode.nearestPoint || + (cartesianSeriesRenderer._isRectSeries && + tooltipDisplayMode != TrackballDisplayMode.groupAllPoints)) { + _validateNearestPointForAllSeries( + leastX!, _chartPointInfo, position.dx, position.dy); } } - return validPoints; + _triggerTrackballRenderCallback(); } /// Event for trackball render @@ -1011,7 +994,7 @@ class TrackballBehaviorRenderer with ChartBehavior { yPos = touchYPos; if (pointInfoIndex < tempTrackballInfo.length - 1) { nextPointInfo = tempTrackballInfo[pointInfoIndex + 1]; - if ((pointInfo.yPosition! > yPos && pointInfoIndex == 0)) { + if (pointInfo.yPosition! > yPos && pointInfoIndex == 0) { continue; } if (!(yPos < @@ -1064,7 +1047,7 @@ class TrackballBehaviorRenderer with ChartBehavior { num xValue; num? yValue; CartesianChartPoint dataPoint; - CartesianSeries series; + CartesianSeries series; ChartAxisRenderer xAxisRenderer, yAxisRenderer; _ChartLocation currXLocation; for (final _ChartPointInfo pointInfo in _chartPointInfo) { @@ -1129,6 +1112,8 @@ class TrackballBehaviorRenderer with ChartBehavior { seriesRenderer._dataPoints[index]; final TrackballMarkerSettings markerSettings = trackballBehavior.markerSettings!; + final _RenderingDetails renderingDetails = + seriesRenderer._renderingDetails!; if (markerSettings.shape == DataMarkerType.image) { _drawImageMarker(null, canvas, _chartPointInfo[index].markerXPos!, _chartPointInfo[index].markerYPos!, markerSettings, _chartState); @@ -1144,7 +1129,7 @@ class TrackballBehaviorRenderer with ChartBehavior { final Paint fillPaint = Paint() ..color = markerSettings.color ?? - (_chartState._chartTheme.brightness == Brightness.light + (renderingDetails.chartTheme.brightness == Brightness.light ? Colors.white : Colors.black) ..style = PaintingStyle.fill; @@ -1231,23 +1216,21 @@ class TrackballBehaviorRenderer with ChartBehavior { num tooltipWidth = 0; _TooltipPositions tooltipPosition; for (int i = 0; i < chartPointInfo.length; i++) { - if (requireInvertedAxis) { - _visibleLocation.add(chartPointInfo[i].xPosition!); - } else { - _visibleLocation.add((chartPointInfo[i] - .seriesRenderer! - ._seriesType - .contains('range') || - chartPointInfo[i] - .seriesRenderer! - ._seriesType - .contains('hilo') || - chartPointInfo[i].seriesRenderer!._seriesType == 'candle') - ? chartPointInfo[i].highYPosition! - : chartPointInfo[i].seriesRenderer!._seriesType == 'boxandwhisker' - ? chartPointInfo[i].maxYPosition! - : chartPointInfo[i].yPosition!); - } + requireInvertedAxis + ? _visibleLocation.add(chartPointInfo[i].xPosition!) + : _visibleLocation.add((chartPointInfo[i] + .seriesRenderer! + ._seriesType + .contains('range') || + chartPointInfo[i] + .seriesRenderer! + ._seriesType + .contains('hilo') || + chartPointInfo[i].seriesRenderer!._seriesType == 'candle') + ? chartPointInfo[i].highYPosition! + : chartPointInfo[i].seriesRenderer!._seriesType == 'boxandwhisker' + ? chartPointInfo[i].maxYPosition! + : chartPointInfo[i].yPosition!); tooltipWidth += tooltipBottom[i] - tooltipTop[i] + _tooltipPadding; } @@ -1268,7 +1251,7 @@ class TrackballBehaviorRenderer with ChartBehavior { final _TooltipPositions tooltipPosition = tooltipPPosition; num? startPos, chartHeight; final bool isTransposed = _chartState._requireInvertedAxis; - dynamic secWidth, width; + num secWidth, width; final int length = tooltipPosition.tooltipTop.length; ChartAxisRenderer yAxisRenderer; final int axesLength = @@ -1308,7 +1291,6 @@ class TrackballBehaviorRenderer with ChartBehavior { } } } - for (int i = 0; i < length; i++) { yAxisRenderer = _yAxesInfo[i]; for (int k = 0; k < axesLength; k++) { @@ -1368,7 +1350,6 @@ class TrackballBehaviorRenderer with ChartBehavior { tooltipBottom[i + 1] - tooltipTop[i + 1] + _tooltipPadding; temp += tooltipHeight; count++; - // This condition executes when the tooltip count is half of the total number of tooltips if (count - 1 == endPoint - 1 || i == endPoint - 1) { halfHeight = (temp - start) / 2; @@ -1388,7 +1369,6 @@ class TrackballBehaviorRenderer with ChartBehavior { break; } } - // To set tool tip positions based on the half height and other tooltip height for (j = startPoint + 1; j <= startPoint + count; j++) { tempTooltipHeight = tooltipBottom[j] - tooltipTop[j]; @@ -1398,7 +1378,6 @@ class TrackballBehaviorRenderer with ChartBehavior { } } else { count = i > 0 ? count : 0; - // This exectutes when any of the middle tooltip collides if (count > 0) { halfHeight = (temp - start) / 2; @@ -1439,7 +1418,7 @@ class TrackballBehaviorRenderer with ChartBehavior { /// To get and return label text of the trackball String _getTrackballLabelText( - CartesianSeriesRenderer cartesianSeriesRenderer, + CartesianSeriesRenderer seriesRenderer, num? xValue, num? yValue, num? lowValue, @@ -1457,14 +1436,14 @@ class TrackballBehaviorRenderer with ChartBehavior { CartesianChartPoint dataPoint) { String labelValue; final int digits = _trackballBehavior.tooltipSettings.decimalPlaces; + final ChartAxis yAxis = seriesRenderer._yAxisRenderer!._axis; if (_trackballBehavior.tooltipSettings.format != null) { dynamic x; - final ChartAxisRenderer axisRenderer = - cartesianSeriesRenderer._xAxisRenderer!; + final ChartAxisRenderer axisRenderer = seriesRenderer._xAxisRenderer!; if (axisRenderer is DateTimeAxisRenderer) { - final DateTimeAxis _axis = axisRenderer._axis as DateTimeAxis; final DateFormat dateFormat = - _axis.dateFormat ?? _getDateTimeLabelFormat(axisRenderer); + (axisRenderer._axis as DateTimeAxis).dateFormat ?? + _getDateTimeLabelFormat(axisRenderer); x = dateFormat .format(DateTime.fromMillisecondsSinceEpoch(xValue! as int)); } else if (axisRenderer is CategoryAxisRenderer) { @@ -1473,76 +1452,97 @@ class TrackballBehaviorRenderer with ChartBehavior { x = axisRenderer._labels .indexOf(axisRenderer._dateFormat.format(dataPoint.x)); } - labelValue = _trackballBehavior.tooltipSettings.format! - .replaceAll('point.x', (x ?? xValue).toString()) - .replaceAll( - 'point.y', - _getLabelValue(yValue, - cartesianSeriesRenderer._yAxisRenderer!._axis, digits)) - .replaceAll('point.high', highValue.toString()) - .replaceAll('point.low', lowValue.toString()) - .replaceAll('point.open', openValue.toString()) - .replaceAll('point.close', closeValue.toString()) - .replaceAll('point.minimum', minValue.toString()) - .replaceAll('point.maximum', maxValue.toString()) - .replaceAll('point.lowerQuartile', lowerQuartileValue.toString()) - .replaceAll('point.upperQuartile', upperQuartileValue.toString()) - .replaceAll('{', '') - .replaceAll('}', '') - .replaceAll('series.name', seriesName) - .replaceAll('point.size', bubbleSize.toString()) - .replaceAll('point.cumulativeValue', cumulativeValue.toString()); + labelValue = seriesRenderer._seriesType.contains('hilo') || + seriesRenderer._seriesType.contains('range') || + seriesRenderer._seriesType.contains('candle') || + seriesRenderer._seriesType.contains('boxandwhisker') + ? seriesRenderer._seriesType.contains('boxandwhisker') + ? (_trackballBehavior.tooltipSettings.format! + .replaceAll('point.x', (x ?? xValue).toString()) + .replaceAll('point.minimum', minValue.toString()) + .replaceAll('point.maximum', maxValue.toString()) + .replaceAll( + 'point.lowerQuartile', lowerQuartileValue.toString()) + .replaceAll( + 'point.upperQuartile', upperQuartileValue.toString()) + .replaceAll('{', '') + .replaceAll('}', '') + .replaceAll('series.name', seriesName)) + : seriesRenderer._seriesType == 'hilo' || + seriesRenderer._seriesType.contains('range') + ? (_trackballBehavior.tooltipSettings.format! + .replaceAll('point.x', (x ?? xValue).toString()) + .replaceAll('point.high', highValue.toString()) + .replaceAll('point.low', lowValue.toString()) + .replaceAll('{', '') + .replaceAll('}', '') + .replaceAll('series.name', seriesName)) + : (_trackballBehavior.tooltipSettings.format! + .replaceAll('point.x', (x ?? xValue).toString()) + .replaceAll('point.high', highValue.toString()) + .replaceAll('point.low', lowValue.toString()) + .replaceAll('point.open', openValue.toString()) + .replaceAll('point.close', closeValue.toString()) + .replaceAll('{', '') + .replaceAll('}', '') + .replaceAll('series.name', seriesName)) + : seriesRenderer._seriesType == 'bubble' + ? (_trackballBehavior.tooltipSettings.format! + .replaceAll('point.x', (x ?? xValue).toString()) + .replaceAll( + 'point.y', + _getLabelValue( + yValue, seriesRenderer._yAxisRenderer!._axis, digits)) + .replaceAll('{', '') + .replaceAll('}', '') + .replaceAll('series.name', seriesName) + .replaceAll('point.size', bubbleSize.toString())) + : seriesRenderer._seriesType.contains('stacked') + ? (_trackballBehavior.tooltipSettings.format! + .replaceAll('point.x', (x ?? xValue).toString()) + .replaceAll('point.y', _getLabelValue(yValue, seriesRenderer._yAxisRenderer!._axis, digits)) + .replaceAll('{', '') + .replaceAll('}', '') + .replaceAll('series.name', seriesName) + .replaceAll('point.cumulativeValue', cumulativeValue.toString())) + : (_trackballBehavior.tooltipSettings.format!.replaceAll('point.x', (x ?? xValue).toString()).replaceAll('point.y', _getLabelValue(yValue, seriesRenderer._yAxisRenderer!._axis, digits)).replaceAll('{', '').replaceAll('}', '').replaceAll('series.name', seriesName)); } else { - labelValue = !cartesianSeriesRenderer._seriesType.contains('range') && - !cartesianSeriesRenderer._seriesType.contains('candle') && - !cartesianSeriesRenderer._seriesType.contains('hilo') && - !cartesianSeriesRenderer._seriesType.contains('boxandwhisker') - ? _getLabelValue( - yValue, cartesianSeriesRenderer._yAxisRenderer!._axis, digits) - : cartesianSeriesRenderer._seriesType == 'hiloopenclose' || - cartesianSeriesRenderer._seriesType.contains('candle') || - cartesianSeriesRenderer._seriesType.contains('boxandwhisker') - ? cartesianSeriesRenderer._seriesType.contains('boxandwhisker') + labelValue = !seriesRenderer._seriesType.contains('range') && + !seriesRenderer._seriesType.contains('candle') && + !seriesRenderer._seriesType.contains('hilo') && + !seriesRenderer._seriesType.contains('boxandwhisker') + ? _getLabelValue(yValue, yAxis, digits) + : seriesRenderer._seriesType == 'hiloopenclose' || + seriesRenderer._seriesType.contains('candle') || + seriesRenderer._seriesType.contains('boxandwhisker') + ? seriesRenderer._seriesType.contains('boxandwhisker') ? 'Maximum : ' + - _getLabelValue(maxValue, cartesianSeriesRenderer._yAxisRenderer!._axis) - .toString() + + _getLabelValue(maxValue, yAxis) + '\n' + 'Minimum : ' + - _getLabelValue(minValue, cartesianSeriesRenderer._yAxisRenderer!._axis) - .toString() + + _getLabelValue(minValue, yAxis) + '\n' + 'LowerQuartile : ' + - _getLabelValue(lowerQuartileValue, - cartesianSeriesRenderer._yAxisRenderer!._axis) - .toString() + + _getLabelValue(lowerQuartileValue, yAxis) + '\n' + 'UpperQuartile : ' + - _getLabelValue(upperQuartileValue, - cartesianSeriesRenderer._yAxisRenderer!._axis) - .toString() + _getLabelValue(upperQuartileValue, yAxis) : 'High : ' + - _getLabelValue(highValue, cartesianSeriesRenderer._yAxisRenderer!._axis) - .toString() + + _getLabelValue(highValue, yAxis) + '\n' + 'Low : ' + - _getLabelValue(lowValue, cartesianSeriesRenderer._yAxisRenderer!._axis) - .toString() + + _getLabelValue(lowValue, yAxis) + '\n' + 'Open : ' + - _getLabelValue(openValue, cartesianSeriesRenderer._yAxisRenderer!._axis) - .toString() + + _getLabelValue(openValue, yAxis) + '\n' + 'Close : ' + - _getLabelValue(closeValue, - cartesianSeriesRenderer._yAxisRenderer!._axis) - .toString() + _getLabelValue(closeValue, yAxis) : 'High : ' + - _getLabelValue(highValue, cartesianSeriesRenderer._yAxisRenderer!._axis) - .toString() + + _getLabelValue(highValue, yAxis) + '\n' + 'Low : ' + - _getLabelValue(lowValue, cartesianSeriesRenderer._yAxisRenderer!._axis) - .toString(); + _getLabelValue(lowValue, yAxis); } return labelValue; } @@ -1569,8 +1569,7 @@ class TrackballBehaviorRenderer with ChartBehavior { '${point.x.microsecondsSinceEpoch}', xAxisRenderer._dateFormat) : _getLabelValue(point.xValue, xAxisRenderer._axis, - _chart.tooltipBehavior.decimalPlaces) - .toString()); + _chart.tooltipBehavior.decimalPlaces)); return headerText; } @@ -1681,7 +1680,7 @@ class TrackballGroupingModeInfo { this.visibleSeriesIndices, this.visibleSeriesList); /// It specifies the cartesian chart points. - final List points; + final List> points; /// It specifies the current point indices. final List currentPointIndices; @@ -1690,5 +1689,5 @@ class TrackballGroupingModeInfo { final List visibleSeriesIndices; /// It specifies the cartesian visible series list. - final List visibleSeriesList; + final List> visibleSeriesList; } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/trackball_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/trackball_painter.dart index e22e522eb..b7d52cd20 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/trackball_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/trackball_painter.dart @@ -50,6 +50,7 @@ class _TrackballPainter extends CustomPainter { late num markerSize, markerPadding; bool isGroupMode = false; late double lastMarkerResultHeight; + _ChartLocation? _minLocation, _maxLocation; @override void paint(Canvas canvas, Size size) => @@ -59,6 +60,7 @@ class _TrackballPainter extends CustomPainter { /// To draw the trackball for all series void _drawTrackball(Canvas canvas) { + final _RenderingDetails renderingDetails = chartState._renderingDetails; if (!_isSeriesAnimating()) { chartPointInfo = chartState._trackballBehaviorRenderer._chartPointInfo; _markerShapes = chartState._trackballBehaviorRenderer._markerShapes; @@ -69,10 +71,8 @@ class _TrackballPainter extends CustomPainter { borderRadius = chart.trackballBehavior.tooltipSettings.borderRadius; pointerLength = chart.trackballBehavior.tooltipSettings.arrowLength; pointerWidth = chart.trackballBehavior.tooltipSettings.arrowWidth; - isGroupMode = (chart.trackballBehavior.tooltipDisplayMode == - TrackballDisplayMode.groupAllPoints) - ? true - : false; + isGroupMode = chart.trackballBehavior.tooltipDisplayMode == + TrackballDisplayMode.groupAllPoints; isLeft = false; isRight = false; @@ -81,7 +81,7 @@ class _TrackballPainter extends CustomPainter { totalWidth = boundaryRect.left + boundaryRect.width; labelStyle = TextStyle( color: chart.trackballBehavior.tooltipSettings.textStyle.color ?? - chartState._chartTheme.crosshairLabelColor, + renderingDetails.chartTheme.crosshairLabelColor, fontSize: chart.trackballBehavior.tooltipSettings.textStyle.fontSize, fontFamily: chart.trackballBehavior.tooltipSettings.textStyle.fontFamily, @@ -216,15 +216,18 @@ class _TrackballPainter extends CustomPainter { chartPointInfo[index].label != '') { _tooltipTop.add(chartState._requireInvertedAxis ? _visiblePoints[index].closestPointX - - (_tooltipPadding) - + _tooltipPadding - (width / 2) : _visiblePoints[index].closestPointY - _tooltipPadding - height / 2); _tooltipBottom.add(chartState._requireInvertedAxis - ? _visiblePoints[index].closestPointX + - (_tooltipPadding) + - (width / 2) + ? (_visiblePoints[index].closestPointX + + _tooltipPadding + + (width / 2)) + + (chart.trackballBehavior.tooltipSettings.canShowMarker + ? 20 + : 0) : _visiblePoints[index].closestPointY + _tooltipPadding + height / 2); @@ -246,7 +249,7 @@ class _TrackballPainter extends CustomPainter { chart.trackballBehavior.lineType != TrackballLineType.none) { final Paint trackballLinePaint = Paint(); trackballLinePaint.color = chart.trackballBehavior.lineColor ?? - chartState._chartTheme.crosshairLineColor; + renderingDetails.chartTheme.crosshairLineColor; trackballLinePaint.strokeWidth = chart.trackballBehavior.lineWidth; trackballLinePaint.style = PaintingStyle.stroke; chart.trackballBehavior.lineWidth == 0 @@ -333,7 +336,7 @@ class _TrackballPainter extends CustomPainter { chartState._chartSeries.visibleSeriesRenderers[i]; if (!(seriesRenderer._animationCompleted || seriesRenderer._series.animationDuration == 0 || - !chartState._initialRender!) && + !chartState._renderingDetails.initialRender!) && seriesRenderer._series.isVisible) { return true; } @@ -349,6 +352,11 @@ class _TrackballPainter extends CustomPainter { Size _getTooltipSize(double height, double width, int index) { final Offset position = Offset( chartPointInfo[index].xPosition!, chartPointInfo[index].yPosition!); + Offset pos; + SfCartesianChartState? _chartState; + ChartAxisRenderer? xAxisRenderer, yAxisRenderer; + CartesianSeriesRenderer? seriesRender; + num? _minX, _maxX; stringValue = <_TrackballElement>[]; final String? format = chartPointInfo[index] .seriesRenderer! @@ -374,6 +382,29 @@ class _TrackballPainter extends CustomPainter { if (isGroupMode) { String str1 = ''; for (int i = 0; i < chartPointInfo.length; i++) { + pos = chartState._trackballBehaviorRenderer._tapPosition; + _chartState = chartPointInfo[i].seriesRenderer?._chartState; + xAxisRenderer = chartPointInfo[i].seriesRenderer?._xAxisRenderer; + seriesRender = chartPointInfo[i].seriesRenderer; + yAxisRenderer = chartPointInfo[i].seriesRenderer?._yAxisRenderer; + _minX = seriesRender?._minimumX; + _maxX = seriesRender?._maximumX; + _minLocation = _calculatePoint( + _minX!, + seriesRender?._minimumY!, + xAxisRenderer!, + yAxisRenderer!, + _chartState!._requireInvertedAxis, + chartPointInfo[index].series, + _chartState._chartAxis._axisClipRect); + _maxLocation = _calculatePoint( + _maxX!, + seriesRender?._maximumY!, + xAxisRenderer, + yAxisRenderer, + _chartState._requireInvertedAxis, + chartPointInfo[index].series, + _chartState._chartAxis._axisClipRect); if (chartPointInfo[i].header != null && chartPointInfo[i].header!.contains(':')) { headerText = true; @@ -450,12 +481,17 @@ class _TrackballPainter extends CustomPainter { stringValue.add(_TrackballElement('', null)); } if (isLabel) { - stringValue.add(_TrackballElement( - str1 + - chartPointInfo[i].seriesRenderer!._series.name! + - ': ' + - chartPointInfo[i].label!, - chartPointInfo[i].seriesRenderer!)); + //ignore: avoid_bool_literals_in_conditional_expressions + if (chartPointInfo[i].seriesRenderer!._isIndicator + ? pos.dx >= _minLocation!.x && pos.dx <= _maxLocation!.x + : true) { + stringValue.add(_TrackballElement( + str1 + + chartPointInfo[i].seriesRenderer!._series.name! + + ': ' + + chartPointInfo[i].label!, + chartPointInfo[i].seriesRenderer!)); + } } divider = (chartPointInfo[0].header != null && chartPointInfo[0].header != '') && @@ -546,7 +582,9 @@ class _TrackballPainter extends CustomPainter { Canvas canvas, double width, double height, int index, [List<_ChartPointInfo>? chartPointInfo, _TooltipPositions? tooltipPosition]) { - final double widthPadding = 17; + final String seriesType = + chartPointInfo![index].seriesRenderer!._seriesType; + const double widthPadding = 17; markerSize = 10; Rect leftRect, rightRect; if (!chart.trackballBehavior.tooltipSettings.canShowMarker) { @@ -586,7 +624,7 @@ class _TrackballPainter extends CustomPainter { _calculateTooltipSize(labelRect, chartPointInfo, tooltipPosition, index); } else { isTop = false; - if (chartPointInfo![index].seriesRenderer!._seriesType.contains('bar') + if (seriesType.contains('bar') ? chartState._requireInvertedAxis : chartState._requireInvertedAxis) { xPos = x! - (labelRect.width / 2); @@ -604,31 +642,28 @@ class _TrackballPainter extends CustomPainter { xPos = x! - labelRect.width / 2; yPos = y; } else { - xPos = x! + _padding + pointerLength; + xPos = x!; yPos = (y! + pointerLength / 2) + _padding; } nosePointX = labelRect.left; nosePointY = labelRect.top; - final double tooltipRightEnd = x! + labelRect.width; - if (xPos! < boundaryRect.left) { - xPos = (isGroupMode) - ? (x! < boundaryRect.left) - ? boundaryRect.left + groupAllPadding - : x! + groupAllPadding - : boundaryRect.left; - } else if (tooltipRightEnd > totalWidth) { + + //ignore: prefer_final_locals + num? leftSideAvailableSize = xPos! - boundaryRect.left; + //ignore: prefer_final_locals + num? rightSideAvailableSize = boundaryRect.width - xPos!; + + if (leftSideAvailableSize > rightSideAvailableSize) { xPos = isGroupMode ? (xPos! - (labelRect.width / 2) - groupAllPadding) - : ((xPos! - labelRect.width - (2 * _padding)) - - 2 * pointerLength); + : xPos! - labelRect.width - _padding - pointerLength; isRight = true; } else { xPos = isGroupMode - ? xPos! + (labelRect.width / 2) + groupAllPadding - : xPos; + ? x! + groupAllPadding + : x! + _padding + pointerLength; } - if (isGroupMode && (yPos! + labelRect.height) >= boundaryRect.bottom) { yPos = boundaryRect.bottom - labelRect.height; } @@ -638,17 +673,10 @@ class _TrackballPainter extends CustomPainter { } } } - if (!isGroupMode) { - if (xPos! <= boundaryRect.left + 5) { - xPos = xPos! + 10; - } else if (xPos! + labelRect.width >= totalWidth - 5) { - xPos = xPos! - 10; - } - } + labelRect = isGroupMode || chart.trackballBehavior.tooltipDisplayMode == - TrackballDisplayMode.nearestPoint || - chart.axes.isNotEmpty + TrackballDisplayMode.nearestPoint ? Rect.fromLTWH(xPos!, yPos!, labelRect.width, labelRect.height) : Rect.fromLTWH( chartState._requireInvertedAxis @@ -690,14 +718,18 @@ class _TrackballPainter extends CustomPainter { isLeft, isRight, index, - isRangeSeries - ? chartPointInfo![index].highXPosition - : isBoxSeries - ? chartPointInfo![index].maxXPosition - : chartPointInfo![index].xPosition, - isRangeSeries + seriesType.contains('range') || + seriesType.contains('hilo') || + seriesType == 'candle' + ? chartPointInfo[index].highXPosition + : seriesType.contains('box') + ? chartPointInfo[index].maxXPosition + : chartPointInfo[index].xPosition, + seriesType.contains('range') || + seriesType.contains('hilo') || + seriesType == 'candle' ? chartPointInfo[index].highYPosition - : isBoxSeries + : seriesType.contains('box') ? chartPointInfo[index].maxYPosition : chartPointInfo[index].yPosition); } @@ -730,28 +762,24 @@ class _TrackballPainter extends CustomPainter { yPos = boundaryRect.bottom - labelRect.height; } } else { - xPos = x! + _padding + pointerLength; - yPos = (y! - labelRect.height / 2); + xPos = x!; + yPos = y! - labelRect.height / 2; nosePointY = yPos!; nosePointX = labelRect.left; - final double tooltipRightEnd = xPos! + labelRect.width; - final double tooltipRightTotalWidth = tooltipRightEnd > totalWidth - ? tooltipRightEnd - : tooltipRightEnd + labelRect.left + pointerLength; - if (xPos! < boundaryRect.left) { - xPos = (isGroupMode && - chart.trackballBehavior.tooltipAlignment == ChartAlignment.far) - ? boundaryRect.left + groupAllPadding - : (x! < boundaryRect.left) - ? boundaryRect.left - : x!; - } else if (tooltipRightTotalWidth > totalWidth) { - xPos = (isGroupMode && - chart.trackballBehavior.tooltipAlignment == ChartAlignment.far) - ? xPos! - labelRect.width - (2 * _padding) - groupAllPadding - : ((xPos! - labelRect.width - (2 * _padding)) - 2 * pointerLength); + //ignore: prefer_final_locals + num? leftSideAvailableSize = xPos! - boundaryRect.left; + //ignore: prefer_final_locals + num? rightSideAvailableSize = boundaryRect.width - xPos!; + + if (leftSideAvailableSize > rightSideAvailableSize) { + xPos = isGroupMode + ? xPos! - labelRect.width - groupAllPadding + : xPos! - labelRect.width - _padding - pointerLength; isRight = true; + } else { + xPos = + isGroupMode ? x! + groupAllPadding : x! + _padding + pointerLength; } if (yPos! + labelRect.height >= boundaryRect.bottom) { yPos = boundaryRect.bottom - labelRect.height; @@ -897,6 +925,7 @@ class _TrackballPainter extends CustomPainter { /// draw trackball tooltip rect and text void _drawRectandText( Canvas canvas, Path backgroundPath, Rect rect, int index) { + final _RenderingDetails renderingDetails = chartState._renderingDetails; final RRect tooltipRect = RRect.fromRectAndCorners( rect, bottomLeft: Radius.circular(borderRadius), @@ -909,13 +938,13 @@ class _TrackballPainter extends CustomPainter { final Paint fillPaint = Paint() ..color = chart.trackballBehavior.tooltipSettings.color ?? - chartState._chartTheme.crosshairBackgroundColor + renderingDetails.chartTheme.crosshairBackgroundColor ..isAntiAlias = false ..style = PaintingStyle.fill; final Paint stokePaint = Paint() ..color = chart.trackballBehavior.tooltipSettings.borderColor ?? - chartState._chartTheme.crosshairBackgroundColor + renderingDetails.chartTheme.crosshairBackgroundColor ..strokeWidth = chart.trackballBehavior.tooltipSettings.borderWidth ..strokeCap = StrokeCap.butt ..isAntiAlias = false @@ -924,7 +953,7 @@ class _TrackballPainter extends CustomPainter { canvas.drawPath(backgroundPath, stokePaint); canvas.drawPath(backgroundPath, fillPaint); final Paint dividerPaint = Paint(); - dividerPaint.color = chartState._chartTheme.tooltipSeparatorColor; + dividerPaint.color = renderingDetails.chartTheme.tooltipSeparatorColor; dividerPaint.strokeWidth = 1; dividerPaint.style = PaintingStyle.stroke; if (isGroupMode && divider) { @@ -958,7 +987,7 @@ class _TrackballPainter extends CustomPainter { } } - final double animationFactor = 1; + const double animationFactor = 1; labelStyle = TextStyle( fontWeight: FontWeight.normal, color: labelStyle.color, @@ -1087,7 +1116,7 @@ class _TrackballPainter extends CustomPainter { canvas, str1[k], Offset( - ((((!isGroupMode && + (((!isGroupMode && chart.trackballBehavior .tooltipSettings.canShowMarker) ? (tooltipRect.left + @@ -1095,7 +1124,7 @@ class _TrackballPainter extends CustomPainter { labelSize.width / 2) : (tooltipRect.left + 4)) + markerPadding) + - width), + width, (eachTextHeight - labelSize.height) + padding), labelStyle, 0); @@ -1293,10 +1322,10 @@ class _TrackballPainter extends CustomPainter { seriesRenderer._seriesType.contains('candle') || seriesRenderer._seriesType.contains('boxandwhisker')) { markerPoint = Offset( - ((tooltipRect.left + - tooltipRect.width / 2 - - tooltipStringResult.width / 2) - - markerSize), + tooltipRect.left + + tooltipRect.width / 2 - + tooltipStringResult.width / 2 - + markerSize, (eachTextHeight - tooltipStringResult.height / 2) + 0.0); _renderMarker( markerPoint, seriesRenderer, animationFactor, canvas, index); @@ -1316,10 +1345,10 @@ class _TrackballPainter extends CustomPainter { if (i > 0 && labelValue != '') { seriesRenderer = stringValue[i].seriesRenderer!; // ignore: unnecessary_null_comparison - if ((seriesRenderer != null && + if (seriesRenderer != null && seriesRenderer._series.name != null && seriesRenderer._chart.trackballBehavior.tooltipSettings.format == - null)) { + null) { if (previousWidth != null && width != null) { markerPoint = Offset( (tooltipRect.left + 10) + @@ -1329,7 +1358,7 @@ class _TrackballPainter extends CustomPainter { markerPoint, seriesRenderer, animationFactor, canvas, index); } else if (stringValue[i].needRender) { markerPoint = Offset( - (tooltipRect.left + 10), + tooltipRect.left + 10, (headerSize!.height * 2 + tooltipRect.top + markerSize + @@ -1379,15 +1408,21 @@ class _TrackballPainter extends CustomPainter { if (_seriesRenderer._seriesType.contains('candle')) { final CandleSeriesRenderer seriesRenderer = _seriesRenderer as CandleSeriesRenderer; - _seriesColor = seriesRenderer._candleSegment._isBull - ? seriesRenderer._candleSeries.bullColor - : seriesRenderer._candleSeries.bearColor; + _seriesColor = + (seriesRenderer._segments[chartPointInfo[index].dataPointIndex!] + as CandleSegment) + ._isBull + ? seriesRenderer._candleSeries.bullColor + : seriesRenderer._candleSeries.bearColor; } else if (_seriesRenderer._seriesType.contains('hiloopenclose')) { final HiloOpenCloseSeriesRenderer seriesRenderer = _seriesRenderer as HiloOpenCloseSeriesRenderer; - _seriesColor = seriesRenderer._segment._isBull - ? seriesRenderer._hiloOpenCloseSeries.bullColor - : seriesRenderer._hiloOpenCloseSeries.bearColor; + _seriesColor = + (seriesRenderer._segments[chartPointInfo[index].dataPointIndex!] + as HiloOpenCloseSegment) + ._isBull + ? seriesRenderer._hiloOpenCloseSeries.bullColor + : seriesRenderer._hiloOpenCloseSeries.bearColor; } else { _seriesColor = (chartPointInfo[index].dataPointIndex! < _seriesRenderer._dataPoints.length @@ -1416,7 +1451,7 @@ class _TrackballPainter extends CustomPainter { Paint markerBorderPaint = Paint(); markerBorderPaint.color = markerSettings.borderColor ?? _seriesColor ?? - _seriesRenderer._chartState!._chartTheme.tooltipLabelColor; + _seriesRenderer._renderingDetails!.chartTheme.tooltipLabelColor; markerBorderPaint.strokeWidth = 1; markerBorderPaint.style = PaintingStyle.stroke; diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/trackball_template.dart b/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/trackball_template.dart index 80e2a6e06..7346df606 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/trackball_template.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/trackball_template.dart @@ -70,11 +70,11 @@ class _TrackballTemplateState extends State<_TrackballTemplate> { template: _template, chartState: widget.chartState, xPos: _chartPointInfo![index].xPosition!, - yPos: _isRangeSeries - ? _chartPointInfo![index].highYPosition! + yPos: (_isRangeSeries + ? _chartPointInfo![index].highYPosition : _isBoxSeries - ? _chartPointInfo![index].maxYPosition! - : _chartPointInfo![index].yPosition!, + ? _chartPointInfo![index].maxYPosition + : _chartPointInfo![index].yPosition)!, trackballBehavior: widget.trackballBehavior); trackballWidgets.add(trackballWidget); @@ -98,11 +98,11 @@ class _TrackballTemplateState extends State<_TrackballTemplate> { template: _template, chartState: widget.chartState, xPos: _chartPointInfo![index].xPosition!, - yPos: _isRangeSeries - ? _chartPointInfo![index].highYPosition! + yPos: (_isRangeSeries + ? _chartPointInfo![index].highYPosition : _isBoxSeries - ? _chartPointInfo![index].maxYPosition! - : _chartPointInfo![index].yPosition!, + ? _chartPointInfo![index].maxYPosition + : _chartPointInfo![index].yPosition)!, trackballBehavior: widget.trackballBehavior, chartPointInfo: _chartPointInfo!, index: index); @@ -122,11 +122,7 @@ class _TrackballTemplateState extends State<_TrackballTemplate> { } } - @override - void dispose() { - super.dispose(); - } - + /// Notify the object changes to framework void refresh() { setState(() { _isRender = true; @@ -143,8 +139,9 @@ class _TrackballTemplateState extends State<_TrackballTemplate> { } } +@immutable class _TrackballRenderObject extends SingleChildRenderObjectWidget { - _TrackballRenderObject( + const _TrackballRenderObject( {Key? key, required Widget child, required this.template, @@ -173,11 +170,11 @@ class _TrackballRenderObject extends SingleChildRenderObjectWidget { @override void updateRenderObject( BuildContext context, covariant _TrackballTemplateRenderBox renderBox) { - renderBox..template = template; - renderBox..index = index; - renderBox..xPos = xPos; + renderBox.template = template; + renderBox.index = index; + renderBox.xPos = xPos; renderBox.yPos = yPos; - renderBox..chartPointInfo = chartPointInfo; + renderBox.chartPointInfo = chartPointInfo; } } @@ -221,8 +218,8 @@ class _TrackballTemplateRenderBox extends RenderShiftedBox { TrackballDisplayMode.nearestPoint; final TrackballBehaviorRenderer trackballBehaviorRenderer = _chartState._trackballBehaviorRenderer; - final List? tooltipTop = trackballBehaviorRenderer._tooltipTop; - final List tooltipBottom = trackballBehaviorRenderer._tooltipBottom; + final List? tooltipTop = []; + final List tooltipBottom = []; final List<_ClosestPoints> visiblePoints = trackballBehaviorRenderer._visiblePoints; final List xAxesInfo = @@ -232,7 +229,8 @@ class _TrackballTemplateRenderBox extends RenderShiftedBox { boundaryRect = _chartState._chartAxis._axisClipRect; final num totalWidth = boundaryRect.left + boundaryRect.width; tooltipPosition = trackballBehaviorRenderer._tooltipPosition; - final isTrackballMarkerEnabled = trackballBehavior.markerSettings != null; + final bool isTrackballMarkerEnabled = + trackballBehavior.markerSettings != null; final BoxConstraints constraints = this.constraints; pointerLength = trackballBehavior.tooltipSettings.arrowLength; pointerWidth = trackballBehavior.tooltipSettings.arrowWidth; @@ -259,8 +257,7 @@ class _TrackballTemplateRenderBox extends RenderShiftedBox { yPos = boundaryRect.bottom - size.height; } } - - if (index == 0) { + if (chartPointInfo != null) { for (int index = 0; index < chartPointInfo!.length; index++) { tooltipTop!.add(_chartState._requireInvertedAxis ? visiblePoints[index].closestPointX - (size.width / 2) @@ -274,7 +271,6 @@ class _TrackballTemplateRenderBox extends RenderShiftedBox { .add(chartPointInfo![index].seriesRenderer!._yAxisRenderer!); } } - if (tooltipTop != null && tooltipTop.isNotEmpty) { tooltipPosition = trackballBehaviorRenderer._smartTooltipPositions( tooltipTop, @@ -415,7 +411,7 @@ class _TrackballTemplateRenderBox extends RenderShiftedBox { @override void paint(PaintingContext context, Offset offset) { super.paint(context, offset); - + final _RenderingDetails renderingDetails = _chartState._renderingDetails; if (!isGroupAllPoints) { final Rect templateRect = Rect.fromLTWH( offset.dx + trackballTemplateRect!.left, @@ -426,13 +422,13 @@ class _TrackballTemplateRenderBox extends RenderShiftedBox { final Paint fillPaint = Paint() ..color = trackballBehavior.tooltipSettings.color ?? (chartPointInfo![index!].seriesRenderer!._series.color ?? - _chartState._chartTheme.crosshairBackgroundColor) + renderingDetails.chartTheme.crosshairBackgroundColor) ..isAntiAlias = false ..style = PaintingStyle.fill; final Paint strokePaint = Paint() ..color = trackballBehavior.tooltipSettings.borderColor ?? (chartPointInfo![index!].seriesRenderer!._series.color ?? - _chartState._chartTheme.crosshairBackgroundColor) + renderingDetails.chartTheme.crosshairBackgroundColor) ..strokeWidth = trackballBehavior.tooltipSettings.borderWidth ..strokeCap = StrokeCap.butt ..isAntiAlias = false @@ -496,7 +492,7 @@ class _TracklinePainter extends CustomPainter { final Path dashArrayPath = Path(); final Paint trackballLinePaint = Paint(); trackballLinePaint.color = trackballBehavior.lineColor ?? - chartState._chartTheme.crosshairLineColor; + chartState._renderingDetails.chartTheme.crosshairLineColor; trackballLinePaint.strokeWidth = trackballBehavior.lineWidth; trackballLinePaint.style = PaintingStyle.stroke; trackballBehavior.lineWidth == 0 diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/zooming_painter.dart b/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/zooming_painter.dart index f6eff8328..2226090f9 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/zooming_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/zooming_painter.dart @@ -12,6 +12,7 @@ class _ZoomRectPainter extends CustomPainter { final SfCartesianChart chart; SfCartesianChartState chartState; late Paint strokePaint, fillPaint; + _RenderingDetails get renderingDetails => chartState._renderingDetails; @override void paint(Canvas canvas, Size size) => @@ -22,7 +23,7 @@ class _ZoomRectPainter extends CustomPainter { final Color? fillColor = chart.zoomPanBehavior.selectionRectColor; strokePaint = Paint() ..color = chart.zoomPanBehavior.selectionRectBorderColor ?? - chartState._chartTheme.selectionRectBorderColor + renderingDetails.chartTheme.selectionRectBorderColor ..strokeWidth = chart.zoomPanBehavior.selectionRectBorderWidth ..style = PaintingStyle.stroke; chart.zoomPanBehavior.selectionRectBorderWidth == 0 @@ -31,7 +32,7 @@ class _ZoomRectPainter extends CustomPainter { fillPaint = Paint() ..color = fillColor != null ? Color.fromRGBO(fillColor.red, fillColor.green, fillColor.blue, 0.3) - : chartState._chartTheme.selectionRectColor + : renderingDetails.chartTheme.selectionRectColor ..style = PaintingStyle.fill; strokePaint.isAntiAlias = false; if (chartState._zoomPanBehaviorRenderer._rectPath != null) { @@ -59,16 +60,14 @@ class _ZoomRectPainter extends CustomPainter { /// To draw connector line void _drawConnectorLine(Canvas canvas, Offset start, Offset end) { - final Offset startPosition = start; - final Offset endPosition = end; - _drawAxisTooltip(chartState._chartAxis._bottomAxisRenderers, canvas, - startPosition, endPosition, 'bottom'); - _drawAxisTooltip(chartState._chartAxis._topAxisRenderers, canvas, - startPosition, endPosition, 'top'); - _drawAxisTooltip(chartState._chartAxis._leftAxisRenderers, canvas, - startPosition, endPosition, 'left'); - _drawAxisTooltip(chartState._chartAxis._rightAxisRenderers, canvas, - startPosition, endPosition, 'right'); + _drawAxisTooltip(chartState._chartAxis._bottomAxisRenderers, canvas, start, + end, 'bottom'); + _drawAxisTooltip( + chartState._chartAxis._topAxisRenderers, canvas, start, end, 'top'); + _drawAxisTooltip( + chartState._chartAxis._leftAxisRenderers, canvas, start, end, 'left'); + _drawAxisTooltip( + chartState._chartAxis._rightAxisRenderers, canvas, start, end, 'right'); } /// Draw axis tootip connector line @@ -85,36 +84,32 @@ class _ZoomRectPainter extends CustomPainter { } /// It returns the tooltip label on zooming - dynamic _getValue( + String _getValue( Offset position, ChartAxisRenderer axisRenderer, String axisPosition) { - dynamic value; final ChartAxis axis = axisRenderer._axis; final bool isHorizontal = axisPosition == 'bottom' || axisPosition == 'top'; - value = isHorizontal + final Rect axisClipRect = chartState._chartAxis._axisClipRect; + final num value = isHorizontal ? _pointToXVal( chart, axisRenderer, axisRenderer._bounds, - position.dx - - (chartState._chartAxis._axisClipRect.left + axis.plotOffset), - position.dy - - (chartState._chartAxis._axisClipRect.top + axis.plotOffset)) + position.dx - (axisClipRect.left + axis.plotOffset), + position.dy - (axisClipRect.top + axis.plotOffset)) : _pointToYVal( chart, axisRenderer, axisRenderer._bounds, - position.dx - - (chartState._chartAxis._axisClipRect.left + axis.plotOffset), - position.dy - - (chartState._chartAxis._axisClipRect.top + axis.plotOffset)); + position.dx - (axisClipRect.left + axis.plotOffset), + position.dy - (axisClipRect.top + axis.plotOffset)); - dynamic resultantString = _getInteractiveTooltipLabel(value, axisRenderer); + dynamic result = _getInteractiveTooltipLabel(value, axisRenderer); if (axis.interactiveTooltip.format != null) { - final String stringValue = axis.interactiveTooltip.format! - .replaceAll('{value}', resultantString); - resultantString = stringValue; + final String stringValue = + axis.interactiveTooltip.format!.replaceAll('{value}', result); + result = stringValue; } - return resultantString; + return result.toString(); } /// Validate the rect by comparing small and large rect. @@ -134,11 +129,12 @@ class _ZoomRectPainter extends CustomPainter { Size labelSize, String axisPosition) { Rect rect; const double paddingForRect = 10; - final ChartAxis axis = axisRenderer._axis; + final double arrowLength = + axisRenderer._axis.interactiveTooltip.arrowLength; if (axisPosition == 'bottom') { rect = Rect.fromLTWH( position.dx - (labelSize.width / 2 + paddingForRect / 2), - axisRenderer._bounds.top + axis.interactiveTooltip.arrowLength, + axisRenderer._bounds.top + arrowLength, labelSize.width + paddingForRect, labelSize.height + paddingForRect); } else if (axisPosition == 'top') { @@ -146,20 +142,20 @@ class _ZoomRectPainter extends CustomPainter { position.dx - (labelSize.width / 2 + paddingForRect / 2), axisRenderer._bounds.top - (labelSize.height + paddingForRect) - - axis.interactiveTooltip.arrowLength, + arrowLength, labelSize.width + paddingForRect, labelSize.height + paddingForRect); } else if (axisPosition == 'left') { rect = Rect.fromLTWH( axisRenderer._bounds.left - (labelSize.width + paddingForRect) - - axis.interactiveTooltip.arrowLength, + arrowLength, position.dy - (labelSize.height + paddingForRect) / 2, labelSize.width + paddingForRect, labelSize.height + paddingForRect); } else { rect = Rect.fromLTWH( - axisRenderer._bounds.left + axis.interactiveTooltip.arrowLength, + axisRenderer._bounds.left + arrowLength, position.dy - (labelSize.height / 2 + paddingForRect / 2), labelSize.width + paddingForRect, labelSize.height + paddingForRect); @@ -174,30 +170,26 @@ class _ZoomRectPainter extends CustomPainter { Offset endPosition, Canvas canvas, String axisPosition) { - RRect? startTooltipRect; - RRect? endTooltipRect; - String startValue; - String endValue; - Size startLabelSize; - Size endLabelSize; - Rect startLabelRect; - Rect endLabelRect; + RRect? startTooltipRect, endTooltipRect; + String startValue, endValue; + Size startLabelSize, endLabelSize; + Rect startLabelRect, endLabelRect; final ChartAxis axis = axisRenderer._axis; final Paint labelFillPaint = Paint() - ..color = chartState._chartTheme.crosshairBackgroundColor + ..color = renderingDetails.chartTheme.crosshairBackgroundColor ..strokeCap = StrokeCap.butt ..isAntiAlias = false ..style = PaintingStyle.fill; final Paint labelStrokePaint = Paint() - ..color = chartState._chartTheme.crosshairBackgroundColor + ..color = renderingDetails.chartTheme.crosshairBackgroundColor ..strokeCap = StrokeCap.butt ..isAntiAlias = false ..style = PaintingStyle.stroke; final Paint connectorLinePaint = Paint() ..color = axis.interactiveTooltip.connectorLineColor ?? - chartState._chartTheme.selectionTooltipConnectorLineColor + renderingDetails.chartTheme.selectionTooltipConnectorLineColor ..strokeWidth = axis.interactiveTooltip.connectorLineWidth ..style = PaintingStyle.stroke; @@ -210,10 +202,8 @@ class _ZoomRectPainter extends CustomPainter { final bool isHorizontal = axisPosition == 'bottom' || axisPosition == 'top'; startValue = _getValue(startPosition, axisRenderer, axisPosition); endValue = _getValue(endPosition, axisRenderer, axisPosition); - startLabelSize = - measureText(startValue.toString(), axis.interactiveTooltip.textStyle); - endLabelSize = - measureText(endValue.toString(), axis.interactiveTooltip.textStyle); + startLabelSize = measureText(startValue, axis.interactiveTooltip.textStyle); + endLabelSize = measureText(endValue, axis.interactiveTooltip.textStyle); startLabelRect = _calculateRect( axisRenderer, startPosition, startLabelSize, axisPosition); endLabelRect = @@ -305,18 +295,19 @@ class _ZoomRectPainter extends CustomPainter { Offset position, Rect labelRect, RRect? rect, - dynamic value, + String value, Size labelSize, InteractiveTooltip tooltip, String axisPosition) { fillPaint.color = - tooltip.color ?? chartState._chartTheme.crosshairBackgroundColor; - strokePaint.color = - tooltip.borderColor ?? chartState._chartTheme.crosshairBackgroundColor; + tooltip.color ?? renderingDetails.chartTheme.crosshairBackgroundColor; + strokePaint.color = tooltip.borderColor ?? + renderingDetails.chartTheme.crosshairBackgroundColor; strokePaint.strokeWidth = tooltip.borderWidth; final bool isHorizontal = axisPosition == 'bottom' || axisPosition == 'top'; - labelRect = _validateRectBounds(labelRect, chartState._containerRect); + labelRect = + _validateRectBounds(labelRect, renderingDetails.chartContainerRect); labelRect = isHorizontal ? _validateRectXPosition(labelRect, chartState) : _validateRectYPosition(labelRect, chartState); @@ -327,12 +318,12 @@ class _ZoomRectPainter extends CustomPainter { position, rect, tooltip); _drawText( canvas, - value.toString(), + value, Offset((rect.left + rect.width / 2) - labelSize.width / 2, (rect.top + rect.height / 2) - labelSize.height / 2), TextStyle( color: tooltip.textStyle.color ?? - chartState._chartTheme.tooltipLabelColor, + renderingDetails.chartTheme.tooltipLabelColor, fontSize: tooltip.textStyle.fontSize, fontWeight: tooltip.textStyle.fontWeight, fontFamily: tooltip.textStyle.fontFamily, diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/zooming_panning.dart b/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/zooming_panning.dart index 38993d76b..24efbe7b3 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/zooming_panning.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/user_interaction/zooming_panning.dart @@ -194,10 +194,52 @@ class ZoomPanBehavior { ///``` final Color? selectionRectColor; + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is ZoomPanBehavior && + other.enablePinching == enablePinching && + other.enableDoubleTapZooming == enableDoubleTapZooming && + other.enablePanning == enablePanning && + other.enableSelectionZooming == enableSelectionZooming && + other.enableMouseWheelZooming == enableMouseWheelZooming && + other.zoomMode == zoomMode && + other.maximumZoomLevel == maximumZoomLevel && + other.selectionRectBorderWidth == selectionRectBorderWidth && + other.selectionRectBorderColor == selectionRectBorderColor && + other.selectionRectColor == selectionRectColor; + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode { + final List values = [ + enablePinching, + enableDoubleTapZooming, + enablePanning, + enableSelectionZooming, + enableMouseWheelZooming, + zoomMode, + maximumZoomLevel, + selectionRectBorderWidth, + selectionRectBorderColor, + selectionRectColor + ]; + return hashList(values); + } + SfCartesianChartState? _chartState; /// Increases the magnification of the plot area. void zoomIn() { + _chartState!._canSetRangeController = true; final SfCartesianChartState chartState = _chartState!; final SfCartesianChart chart = chartState._chart; final ZoomPanBehaviorRenderer zoomPanBehaviorRenderer = @@ -223,8 +265,7 @@ class ZoomPanBehavior { } } if (chart.onZooming != null) { - ZoomPanArgs? zoomingArgs; - _bindZoomEvent(chart, axisRenderer, zoomingArgs, chart.onZooming!); + _bindZoomEvent(chart, axisRenderer, chart.onZooming!); } } if (needZoom == true) { @@ -234,6 +275,7 @@ class ZoomPanBehavior { /// Decreases the magnification of the plot area. void zoomOut() { + _chartState!._canSetRangeController = true; final SfCartesianChartState chartState = _chartState!; final SfCartesianChart chart = chartState._chart; final ZoomPanBehaviorRenderer zoomPanBehaviorRenderer = @@ -254,8 +296,7 @@ class ZoomPanBehavior { : (axisRenderer._zoomFactor < 0.0 ? 0.0 : axisRenderer._zoomFactor); } if (chart.onZooming != null) { - ZoomPanArgs? zoomingArgs; - _bindZoomEvent(chart, axisRenderer, zoomingArgs, chart.onZooming!); + _bindZoomEvent(chart, axisRenderer, chart.onZooming!); } } zoomPanBehaviorRenderer._createZoomState(); @@ -265,10 +306,9 @@ class ZoomPanBehavior { /// /// Here, you can pass the zoom factor of an axis to magnify the plot area. The value ranges from 0 to 1. void zoomByFactor(double zoomFactor) { + _chartState!._canSetRangeController = true; final SfCartesianChartState chartState = _chartState!; final SfCartesianChart chart = chartState._chart; - final ZoomPanBehaviorRenderer zoomPanBehaviorRenderer = - chartState._zoomPanBehaviorRenderer; ChartAxisRenderer axisRenderer; for (int index = 0; index < chartState._chartAxis._axisRenderersCollection.length; @@ -276,10 +316,9 @@ class ZoomPanBehavior { axisRenderer = chartState._chartAxis._axisRenderersCollection[index]; axisRenderer._zoomFactor = zoomFactor; if (chart.onZooming != null) { - ZoomPanArgs? zoomingArgs; - _bindZoomEvent(chart, axisRenderer, zoomingArgs, chart.onZooming!); + _bindZoomEvent(chart, axisRenderer, chart.onZooming!); } - zoomPanBehaviorRenderer._createZoomState(); + chartState._zoomPanBehaviorRenderer._createZoomState(); } } @@ -288,6 +327,7 @@ class ZoomPanBehavior { /// Here, you can pass the rectangle with the left, right, top, and bottom values, /// using which the selection zooming will be performed. void zoomByRect(Rect rect) { + _chartState!._canSetRangeController = true; final SfCartesianChartState chartState = _chartState!; chartState._zoomPanBehaviorRenderer._doSelectionZooming(rect); } @@ -297,15 +337,14 @@ class ZoomPanBehavior { /// Here, you need to pass axis, zoom factor, zoom position of the zoom level that needs to be modified. void zoomToSingleAxis( ChartAxis axis, double zoomPosition, double zoomFactor) { + _chartState!._canSetRangeController = true; final SfCartesianChartState chartState = _chartState!; - final ZoomPanBehaviorRenderer zoomPanBehaviorRenderer = - chartState._zoomPanBehaviorRenderer; final ChartAxisRenderer? axisRenderer = _findExistingAxisRenderer( axis, chartState._chartAxis._axisRenderersCollection); if (axisRenderer != null) { axisRenderer._zoomFactor = zoomFactor; axisRenderer._zoomPosition = zoomPosition; - zoomPanBehaviorRenderer._createZoomState(); + chartState._zoomPanBehaviorRenderer._createZoomState(); } } @@ -314,10 +353,9 @@ class ZoomPanBehavior { /// To perform /// this action, the plot area needs to be in zoomed state. void panToDirection(String direction) { + _chartState!._canSetRangeController = true; final SfCartesianChartState chartState = _chartState!; final SfCartesianChart chart = chartState._chart; - final ZoomPanBehaviorRenderer zoomPanBehaviorRenderer = - chartState._zoomPanBehaviorRenderer; ChartAxisRenderer axisRenderer; direction = direction.toLowerCase(); for (int axisIndex = 0; @@ -364,19 +402,17 @@ class ZoomPanBehavior { } } if (chart.onZooming != null) { - ZoomPanArgs? zoomingArgs; - _bindZoomEvent(chart, axisRenderer, zoomingArgs, chart.onZooming!); + _bindZoomEvent(chart, axisRenderer, chart.onZooming!); } } - zoomPanBehaviorRenderer._createZoomState(); + chartState._zoomPanBehaviorRenderer._createZoomState(); } /// Returns the plot area back to its original position after zooming. void reset() { + _chartState!._canSetRangeController = true; final SfCartesianChartState chartState = _chartState!; final SfCartesianChart chart = chartState._chart; - final ZoomPanBehaviorRenderer zoomPanBehaviorRenderer = - chartState._zoomPanBehaviorRenderer; ChartAxisRenderer axisRenderer; for (int index = 0; index < chartState._chartAxis._axisRenderersCollection.length; @@ -385,11 +421,10 @@ class ZoomPanBehavior { axisRenderer._zoomFactor = 1.0; axisRenderer._zoomPosition = 0.0; if (chart.onZoomReset != null) { - ZoomPanArgs? zoomResetArgs; - _bindZoomEvent(chart, axisRenderer, zoomResetArgs, chart.onZoomReset!); + _bindZoomEvent(chart, axisRenderer, chart.onZoomReset!); } } - zoomPanBehaviorRenderer._createZoomState(); + chartState._zoomPanBehaviorRenderer._createZoomState(); } } @@ -404,6 +439,7 @@ class ZoomPanBehaviorRenderer with ZoomBehavior { late _ZoomRectPainter _painter; Offset? _previousMovedPosition; bool? _isPanning, _isPinching; + //ignore: prefer_final_fields bool _canPerformSelection = false; Rect _zoomingRect = const Rect.fromLTWH(0, 0, 0, 0); bool _delayRedraw = false; @@ -413,8 +449,8 @@ class ZoomPanBehaviorRenderer with ZoomBehavior { /// Below method for Double tap Zooming void _doubleTapZooming(double xPos, double yPos, double? zoomFactor) { + _chartState._canSetRangeController = true; _chartState._zoomProgress = true; - ZoomPanArgs? zoomStartArgs; ChartAxisRenderer axisRenderer; double cumulative, origin, maxZoomFactor; for (int axisIndex = 0; @@ -422,8 +458,7 @@ class ZoomPanBehaviorRenderer with ZoomBehavior { axisIndex++) { axisRenderer = _chartState._chartAxis._axisRenderersCollection[axisIndex]; if (_chart.onZoomStart != null) { - _bindZoomEvent( - _chart, axisRenderer, zoomStartArgs, _chart.onZoomStart!); + _bindZoomEvent(_chart, axisRenderer, _chart.onZoomStart!); } axisRenderer._previousZoomFactor = axisRenderer._zoomFactor; axisRenderer._previousZoomPosition = axisRenderer._zoomPosition; @@ -443,9 +478,8 @@ class ZoomPanBehaviorRenderer with ZoomBehavior { : origin < 0 ? 0 : origin; - zoomFactor = ((cumulative == 1) - ? 1 - : _minMax(1 / cumulative, 0, 1).toDouble()); + zoomFactor = + cumulative == 1 ? 1 : _minMax(1 / cumulative, 0, 1).toDouble(); _zoomPosition = (cumulative == 1) ? 0 : axisRenderer._zoomPosition + @@ -471,8 +505,7 @@ class ZoomPanBehaviorRenderer with ZoomBehavior { } if (_chart.onZoomEnd != null) { - ZoomPanArgs? zoomEndArgs; - _bindZoomEvent(_chart, axisRenderer, zoomEndArgs, _chart.onZoomEnd!); + _bindZoomEvent(_chart, axisRenderer, _chart.onZoomEnd!); } } _createZoomState(); @@ -480,6 +513,7 @@ class ZoomPanBehaviorRenderer with ZoomBehavior { /// Below method is for panning the zoomed chart void _doPan(double xPos, double yPos) { + _chartState._canSetRangeController = true; num currentScale, value; ChartAxisRenderer axisRenderer; double currentZoomPosition; @@ -522,8 +556,7 @@ class ZoomPanBehaviorRenderer with ZoomBehavior { } } if (_chart.onZooming != null) { - ZoomPanArgs? zoomingArgs; - _bindZoomEvent(_chart, axisRenderer, zoomingArgs, _chart.onZooming!); + _bindZoomEvent(_chart, axisRenderer, _chart.onZooming!); } } _createZoomState(); @@ -532,6 +565,7 @@ class ZoomPanBehaviorRenderer with ZoomBehavior { ///Below method for drawing selection rectangle void _drawSelectionZoomRect( double currentX, double currentY, double startX, double startY) { + _chartState._canSetRangeController = true; final Rect clipRect = _chartState._chartAxis._axisClipRect; final Offset startPosition = Offset( (startX < clipRect.left) ? clipRect.left : startX, @@ -564,22 +598,20 @@ class ZoomPanBehaviorRenderer with ZoomBehavior { _rectPath!.close(); } _zoomingRect = _rectPath!.getBounds(); - _chartState._zoomRepaintNotifier.value++; + _chartState._repaintNotifiers['zoom']!.value++; } /// Below method for zooming selected portion void _doSelectionZooming(Rect zoomRect) { - ZoomPanArgs? zoomEndArgs; + _chartState._canSetRangeController = true; final Rect rect = _chartState._chartAxis._axisClipRect; ChartAxisRenderer axisRenderer; for (int axisIndex = 0; axisIndex < _chartState._chartAxis._axisRenderersCollection.length; axisIndex++) { axisRenderer = _chartState._chartAxis._axisRenderersCollection[axisIndex]; - ZoomPanArgs? zoomStartArgs; if (_chart.onZoomStart != null) { - _bindZoomEvent( - _chart, axisRenderer, zoomStartArgs, _chart.onZoomStart!); + _bindZoomEvent(_chart, axisRenderer, _chart.onZoomStart!); } axisRenderer._previousZoomFactor = axisRenderer._zoomFactor; axisRenderer._previousZoomPosition = axisRenderer._zoomPosition; @@ -613,7 +645,7 @@ class ZoomPanBehaviorRenderer with ZoomBehavior { } } if (_chart.onZoomEnd != null) { - _bindZoomEvent(_chart, axisRenderer, zoomEndArgs, _chart.onZoomEnd!); + _bindZoomEvent(_chart, axisRenderer, _chart.onZoomEnd!); } } @@ -625,6 +657,7 @@ class ZoomPanBehaviorRenderer with ZoomBehavior { /// Below method is for pinch zooming void _performPinchZooming( List touchStartList, List touchMoveList) { + _chartState._canSetRangeController = true; num touch0StartX, touch0StartY, touch1StartX, @@ -636,28 +669,29 @@ class ZoomPanBehaviorRenderer with ZoomBehavior { if (!(_zoomingRect.width > 0 && _zoomingRect.height > 0)) { _calculateZoomAxesRange(_chart); _delayRedraw = true; - final Rect offsetRect = _chartState._chartAxis._axisClipRect; - final Rect elementOffsetRect = _chartState._containerRect; - touch0StartX = touchStartList[0].position.dx - elementOffsetRect.left; - touch0StartY = touchStartList[0].position.dy - elementOffsetRect.top; - touch0EndX = touchMoveList[0].position.dx - elementOffsetRect.left; - touch0EndY = touchMoveList[0].position.dy - elementOffsetRect.top; - touch1StartX = touchStartList[1].position.dx - elementOffsetRect.left; - touch1StartY = touchStartList[1].position.dy - elementOffsetRect.top; - touch1EndX = touchMoveList[1].position.dx - elementOffsetRect.left; - touch1EndY = touchMoveList[1].position.dy - elementOffsetRect.top; + final Rect clipRect = _chartState._chartAxis._axisClipRect; + final Rect containerRect = + _chartState._renderingDetails.chartContainerRect; + touch0StartX = touchStartList[0].position.dx - containerRect.left; + touch0StartY = touchStartList[0].position.dy - containerRect.top; + touch0EndX = touchMoveList[0].position.dx - containerRect.left; + touch0EndY = touchMoveList[0].position.dy - containerRect.top; + touch1StartX = touchStartList[1].position.dx - containerRect.left; + touch1StartY = touchStartList[1].position.dy - containerRect.top; + touch1EndX = touchMoveList[1].position.dx - containerRect.left; + touch1EndY = touchMoveList[1].position.dy - containerRect.top; double scaleX, scaleY, clipX, clipY; Rect pinchRect; scaleX = (touch0EndX - touch1EndX).abs() / (touch0StartX - touch1StartX).abs(); scaleY = (touch0EndY - touch1EndY).abs() / (touch0StartY - touch1StartY).abs(); - clipX = ((offsetRect.left - touch0EndX) / scaleX) + + clipX = ((clipRect.left - touch0EndX) / scaleX) + math.min(touch0StartX, touch1StartX); - clipY = ((offsetRect.top - touch0EndY) / scaleY) + + clipY = ((clipRect.top - touch0EndY) / scaleY) + math.min(touch0StartY, touch1StartY); pinchRect = Rect.fromLTWH( - clipX, clipY, offsetRect.width / scaleX, offsetRect.height / scaleY); + clipX, clipY, clipRect.width / scaleX, clipRect.height / scaleY); _calculatePinchZoomFactor(_chart, pinchRect); _chartState._zoomProgress = true; _createZoomState(); @@ -666,30 +700,25 @@ class ZoomPanBehaviorRenderer with ZoomBehavior { /// To create zoomed states void _createZoomState() { + _chartState._rangeChangeBySlider = false; + _chartState._isRedrawByZoomPan = true; _chartState._zoomedAxisRendererStates = []; _chartState._zoomedAxisRendererStates .addAll(_chartState._chartAxis._axisRenderersCollection); - _chartState._isLegendToggled = false; + _chartState._renderingDetails.isLegendToggled = false; _chartState._redraw(); } /// Below method is for pinch zooming void _calculatePinchZoomFactor(SfCartesianChart chart, Rect pinchRect) { + _chartState._canSetRangeController = true; final ZoomMode mode = _zoomPanBehavior.zoomMode; - num selectionMin; - num selectionMax; - num rangeMin; - num rangeMax; - num value; - num axisTrans; - double currentZoomPosition; - double currentZoomFactor; - double currentFactor; - double currentPosition; - final Rect offsetRect = _chartState._chartAxis._axisClipRect; + num selectionMin, selectionMax, rangeMin, rangeMax, value, axisTrans; + double currentZoomPosition, currentZoomFactor; + double currentFactor, currentPosition, maxZoomFactor, minZoomFactor = 1.0; + final Rect clipRect = _chartState._chartAxis._axisClipRect; final List<_ZoomAxisRange> _zoomAxes = _chartState._zoomAxes; ChartAxisRenderer axisRenderer; - double maxZoomFactor; for (int axisIndex = 0; axisIndex < _chartState._chartAxis._axisRenderersCollection.length; axisIndex++) { @@ -701,18 +730,18 @@ class ZoomPanBehaviorRenderer with ZoomBehavior { (axisRenderer._orientation == AxisOrientation.vertical && mode != ZoomMode.x)) { if (axisRenderer._orientation == AxisOrientation.horizontal) { - value = pinchRect.left - offsetRect.left; - axisTrans = offsetRect.width / _zoomAxes[axisIndex].delta!; + value = pinchRect.left - clipRect.left; + axisTrans = clipRect.width / _zoomAxes[axisIndex].delta!; rangeMin = value / axisTrans + _zoomAxes[axisIndex].min!; - value = pinchRect.left + pinchRect.width - offsetRect.left; + value = pinchRect.left + pinchRect.width - clipRect.left; rangeMax = value / axisTrans + _zoomAxes[axisIndex].min!; } else { - value = pinchRect.top - offsetRect.top; - axisTrans = offsetRect.height / _zoomAxes[axisIndex].delta!; - rangeMin = (value * -1 + offsetRect.height) / axisTrans + + value = pinchRect.top - clipRect.top; + axisTrans = clipRect.height / _zoomAxes[axisIndex].delta!; + rangeMin = (value * -1 + clipRect.height) / axisTrans + _zoomAxes[axisIndex].min!; - value = pinchRect.top + pinchRect.height - offsetRect.top; - rangeMax = (value * -1 + offsetRect.height) / axisTrans + + value = pinchRect.top + pinchRect.height - clipRect.top; + rangeMax = (value * -1 + clipRect.height) / axisTrans + _zoomAxes[axisIndex].min!; } selectionMin = math.min(rangeMin, rangeMax); @@ -724,16 +753,26 @@ class ZoomPanBehaviorRenderer with ZoomBehavior { currentZoomPosition = currentPosition < 0 ? 0 : currentPosition; currentZoomFactor = currentFactor > 1 ? 1 : currentFactor; maxZoomFactor = _zoomPanBehavior.maximumZoomLevel ?? 0.1; + if (axisRenderer._axis.autoScrollingDelta != null && + axisRenderer._scrollingDelta != null) { + //to find zoom factor for corresponding auto scroll delta + minZoomFactor = + axisRenderer._scrollingDelta! / _zoomAxes[axisIndex].actualDelta!; + } if (currentZoomFactor < maxZoomFactor) { axisRenderer._zoomFactor = maxZoomFactor; axisRenderer._zoomPosition = 0.0; currentZoomFactor = maxZoomFactor; } + if (currentZoomFactor > minZoomFactor) { + //to restrict zoom factor to corresponding auto scroll delta + axisRenderer._zoomFactor = minZoomFactor; + currentZoomFactor = minZoomFactor; + } onPinch(axisRenderer, currentZoomPosition, currentZoomFactor); } if (chart.onZooming != null) { - ZoomPanArgs? zoomingArgs; - _bindZoomEvent(chart, axisRenderer, zoomingArgs, chart.onZooming!); + _bindZoomEvent(chart, axisRenderer, chart.onZooming!); } } } @@ -778,15 +817,13 @@ class ZoomPanBehaviorRenderer with ZoomBehavior { _chartState._zoomProgress = true; _calculateZoomAxesRange(_chart); _isPanning = _chart.zoomPanBehavior.enablePanning; - ZoomPanArgs? zoomStartArgs, zoomResetArgs; ChartAxisRenderer axisRenderer; for (int axisIndex = 0; axisIndex < _chartState._chartAxis._axisRenderersCollection.length; axisIndex++) { axisRenderer = _chartState._chartAxis._axisRenderersCollection[axisIndex]; if (_chart.onZoomStart != null) { - _bindZoomEvent( - _chart, axisRenderer, zoomStartArgs, _chart.onZoomStart!); + _bindZoomEvent(_chart, axisRenderer, _chart.onZoomStart!); } axisRenderer._previousZoomFactor = axisRenderer._zoomFactor; axisRenderer._previousZoomPosition = axisRenderer._zoomPosition; @@ -830,15 +867,12 @@ class ZoomPanBehaviorRenderer with ZoomBehavior { zoomFactor = maxZoomFactor; } if (_chart.onZoomEnd != null) { - ZoomPanArgs? zoomEndArgs; - _bindZoomEvent( - _chart, axisRenderer, zoomEndArgs, _chart.onZoomEnd!); + _bindZoomEvent(_chart, axisRenderer, _chart.onZoomEnd!); } if (axisRenderer._zoomFactor.toInt() == 1 && axisRenderer._zoomPosition.toInt() == 0 && _chart.onZoomReset != null) { - _bindZoomEvent( - _chart, axisRenderer, zoomResetArgs, _chart.onZoomReset!); + _bindZoomEvent(_chart, axisRenderer, _chart.onZoomReset!); } } } diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/utils/enum.dart b/packages/syncfusion_flutter_charts/lib/src/chart/utils/enum.dart index 494bb0add..b6ca67f56 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/utils/enum.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/utils/enum.dart @@ -61,7 +61,7 @@ enum AxisLabelIntersectAction { rotate90 } -/// Interval type of the DateTime axis. +/// Interval type of the DateTime and DateTimeCategory axis. enum DateTimeIntervalType { /// - DateTimeIntervalType.auto, will calculate interval based on the data points. auto, @@ -82,7 +82,10 @@ enum DateTimeIntervalType { minutes, /// - DateTimeIntervalType.seconds, will consider interval as seconds. - seconds + seconds, + + /// - DateTimeIntervalType.milliseconds, will consider interval as milliseconds(ms). + milliseconds, } /// Position of the axis labels. @@ -410,7 +413,7 @@ enum ChartSwipeDirection { ///Determines whether the axis should be scrolled from the start position or end position. /// -///For example, if there are 10 data points and [autoScrollingDelta] value is 5 and `AutoScrollingMode.end` +///For example, if there are 10 data points and `autoScrollingDelta` value is 5 and `AutoScrollingMode.end` /// is specified to this property, last 5 points will be displayed in the chart. If `AutoScrollingMode.start` /// is set to this property, first 5 points will be displayed. /// diff --git a/packages/syncfusion_flutter_charts/lib/src/chart/utils/helper.dart b/packages/syncfusion_flutter_charts/lib/src/chart/utils/helper.dart index e0862ca28..5cdc981fe 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/utils/helper.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/utils/helper.dart @@ -89,13 +89,13 @@ bool _withInRange(num value, _VisibleRange range) => /// To find the proper series color of each point in waterfall chart, /// which includes intermediate sum, total sum and negative point. -Color? _getWaterfallSeriesColor(WaterfallSeries series, +Color? _getWaterfallSeriesColor(WaterfallSeries series, CartesianChartPoint point, Color? seriesColor) => point.isIntermediateSum! ? series.intermediateSumColor ?? seriesColor : point.isTotalSum! ? series.totalSumColor ?? seriesColor - : point.yValue < 0 + : point.yValue < 0 == true ? series.negativePointsColor ?? seriesColor : seriesColor; @@ -148,11 +148,15 @@ num _calculateMinPointsDelta( (axisRenderer._name == chartState._chartAxis._primaryXAxisRenderer._name && series.xAxisName != null))) { - xValues = seriesRenderer._dataPoints - .map((CartesianChartPoint point) { - return point.xValue; - }).toList(); - + xValues = axisRenderer is DateTimeAxisRenderer + ? seriesRenderer._overAllDataPoints + .map((CartesianChartPoint? point) { + return point!.xValue; + }).toList() + : seriesRenderer._dataPoints + .map((CartesianChartPoint point) { + return point.xValue; + }).toList(); xValues.sort(); if (xValues.length == 1) { DateTime? minDate; @@ -160,7 +164,7 @@ num _calculateMinPointsDelta( if (axisRenderer is DateTimeAxisRenderer) { minDate = DateTime.fromMillisecondsSinceEpoch( seriesRenderer._minimumX! as int); - minDate = minDate.subtract(Duration(days: 1)); + minDate = minDate.subtract(const Duration(days: 1)); minimumInSeconds = minDate.millisecondsSinceEpoch; } seriesMin = (axisRenderer is DateTimeAxisRenderer && @@ -194,8 +198,7 @@ num _calculateMinPointsDelta( } ///Draw Legend series type icon -PaintingStyle _calculateLegendShapes(Path path, double x, double y, - double width, double height, String seriesType) { +PaintingStyle _calculateLegendShapes(Path path, Rect rect, String seriesType) { PaintingStyle style = PaintingStyle.fill; switch (seriesType) { case 'line': @@ -205,82 +208,84 @@ PaintingStyle _calculateLegendShapes(Path path, double x, double y, style = PaintingStyle.stroke; break; case 'spline': - path.moveTo(x - width / 2, y + height / 5); - path.quadraticBezierTo(x, y - height, x, y + height / 5); - path.moveTo(x, y + height / 5); - path.quadraticBezierTo( - x + width / 2, y + height / 2, x + width / 2, y - height / 2); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.splineSeries); style = PaintingStyle.stroke; break; case 'splinearea': case 'splinerangearea': - path.moveTo(x - width / 2, y + height / 2); - path.quadraticBezierTo(x, y - height, x, y + height / 5); - path.quadraticBezierTo( - x + width / 2, y - height / 2, x + width / 2, y + height / 2); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.splineAreaSeries); break; case 'bar': case 'stackedbar': case 'stackedbar100': - _calculateBarTypeIconPath(path, x, y, width, height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.barSeries); break; case 'column': case 'stackedcolumn': case 'stackedcolumn100': case 'rangecolumn': case 'histogram': - _calculateColumnTypeIconPath(path, x, y, width, height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.columnSeries); break; case 'area': case 'stackedarea': case 'rangearea': case 'stackedarea100': - _calculateAreaTypeIconPath(path, x, y, width, height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.areaSeries); break; case 'stepline': - _calculateSteplineIconPath(path, x, y, width, height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.stepLineSeries); style = PaintingStyle.stroke; break; case 'bubble': - path.addArc(Rect.fromLTWH(x - width / 2, y - height / 2, width, height), - 0.0, 2 * math.pi); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.bubbleSeries); break; - case 'hilo': - path.moveTo(x, y + height / 2); - path.lineTo(x, y - height / 2); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.hiloSeries); style = PaintingStyle.stroke; break; case 'hiloopenclose': case 'candle': - path.moveTo(x - width / 2, y); - path.lineTo(x + width / 2, y); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.candleSeries); style = PaintingStyle.stroke; break; case 'waterfall': case 'boxandwhisker': - path.addRect(Rect.fromLTRB( - x - width / 2, y - height / 2, x + width / 2, y + height / 2)); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.waterfallSeries); break; case 'pie': - _calculatePieIconPath(path, x, y, width, height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.pieSeries); break; case 'doughnut': case 'radialbar': break; case 'steparea': - _calculateStepAreaIconPath(path, x, y, width, height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.stepAreaSeries); break; case 'pyramid': - _calculatePyramidIconPath(path, x, y, width, height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.pyramidSeries); break; case 'funnel': - _calculateFunnelIconPath(path, x, y, width, height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.funnelSeries); break; default: path.addArc( - Rect.fromLTRB( - x - width / 2, y - height / 2, x + width / 2, y + height / 2), + Rect.fromLTRB(rect.left - rect.width / 2, rect.top - rect.height / 2, + rect.left + rect.width / 2, rect.top + rect.height / 2), 0.0, 2 * math.pi); break; @@ -288,143 +293,6 @@ PaintingStyle _calculateLegendShapes(Path path, double x, double y, return style; } -///calculate bar legend icon path -void _calculateBarTypeIconPath( - Path path, double x, double y, double width, double height) { - const num padding = 10; - path.moveTo(x - (width / 2) - padding / 4, y - 3 * (height / 5)); - path.lineTo(x + 3 * (width / 10), y - 3 * (height / 5)); - path.lineTo(x + 3 * (width / 10), y - 3 * (height / 10)); - path.lineTo(x - (width / 2) - padding / 4, y - 3 * (height / 10)); - path.close(); - path.moveTo( - x - (width / 2) - (padding / 4), y - (height / 5) + (padding / 20)); - path.lineTo( - x + (width / 2) + (padding / 4), y - (height / 5) + (padding / 20)); - path.lineTo( - x + (width / 2) + (padding / 4), y + (height / 10) + (padding / 20)); - path.lineTo( - x - (width / 2) - (padding / 4), y + (height / 10) + (padding / 20)); - path.close(); - path.moveTo( - x - (width / 2) - (padding / 4), y + (height / 5) + (padding / 10)); - path.lineTo(x - width / 4, y + (height / 5) + (padding / 10)); - path.lineTo(x - width / 4, y + (height / 2) + (padding / 10)); - path.lineTo( - x - (width / 2) - (padding / 4), y + (height / 2) + (padding / 10)); - path.close(); -} - -///calculate column legend icon path -void _calculateColumnTypeIconPath( - Path path, double x, double y, double width, double height) { - const num padding = 10; - path.moveTo(x - 3 * (width / 5), y - (height / 5)); - path.lineTo(x + 3 * (-width / 10), y - (height / 5)); - path.lineTo(x + 3 * (-width / 10), y + (height / 2)); - path.lineTo(x - 3 * (width / 5), y + (height / 2)); - path.close(); - path.moveTo( - x - (width / 10) - (width / 20), y - (height / 4) - (padding / 2)); - path.lineTo( - x + (width / 10) + (width / 20), y - (height / 4) - (padding / 2)); - path.lineTo(x + (width / 10) + (width / 20), y + (height / 2)); - path.lineTo(x - (width / 10) - (width / 20), y + (height / 2)); - path.close(); - path.moveTo(x + 3 * (width / 10), y); - path.lineTo(x + 3 * (width / 5), y); - path.lineTo(x + 3 * (width / 5), y + (height / 2)); - path.lineTo(x + 3 * (width / 10), y + (height / 2)); - path.close(); -} - -///calculate area type legend icon path -void _calculateAreaTypeIconPath( - Path path, double x, double y, double width, double height) { - const num padding = 10; - path.moveTo(x - (width / 2) - (padding / 4), y + (height / 2)); - path.lineTo(x - (width / 4) - (padding / 8), y - (height / 2)); - path.lineTo(x, y + (height / 4)); - path.lineTo(x + (width / 4) + (padding / 8), y - (height / 2) + (height / 4)); - path.lineTo(x + (height / 2) + (padding / 4), y + (height / 2)); - path.close(); -} - -///calculate stepline legend icon path -void _calculateSteplineIconPath( - Path path, double x, double y, double width, double height) { - const num padding = 10; - path.moveTo(x - (width / 2) - (padding / 4), y + (height / 2)); - path.lineTo(x - (width / 2) + (width / 10), y + (height / 2)); - path.lineTo(x - (width / 2) + (width / 10), y); - path.lineTo(x - (width / 10), y); - path.lineTo(x - (width / 10), y + (height / 2)); - path.lineTo(x + (width / 5), y + (height / 2)); - path.lineTo(x + (width / 5), y - (height / 2)); - path.lineTo(x + (width / 2), y - (height / 2)); - path.lineTo(x + (width / 2), y + (height / 2)); - path.lineTo(x + (width / 2) + (padding / 4), y + (height / 2)); -} - -///calculate steparea legend icon path -void _calculateStepAreaIconPath( - Path path, double x, double y, double width, double height) { - const num padding = 10; - path.moveTo(x - (width / 2) - (padding / 4), y + (height / 2)); - path.lineTo(x - (width / 2) - (padding / 4), y - (height / 4)); - path.lineTo(x - (width / 2) + (width / 10), y - (height / 4)); - path.lineTo(x - (width / 2) + (width / 10), y - (height / 2)); - path.lineTo(x - (width / 10), y - (height / 2)); - path.lineTo(x - (width / 10), y); - path.lineTo(x + (width / 5), y); - path.lineTo(x + (width / 5), y - (height / 3)); - path.lineTo(x + (width / 2), y - (height / 3)); - path.lineTo(x + (width / 2), y + (height / 2)); - path.close(); -} - -///Calculate pie legend icon path -void _calculatePieIconPath( - Path path, double x, double y, double width, double height) { - final double r = math.min(height, width) / 2; - path.moveTo(x, y); - path.lineTo(x + r, y); - path.arcTo( - Rect.fromCircle(center: Offset(x, y), radius: r), - _degreesToRadians(0).toDouble(), - _degreesToRadians(270).toDouble(), - false); - path.close(); - path.moveTo(x + width / 10, y - height / 10); - path.lineTo(x + r, y - height / 10); - path.arcTo( - Rect.fromCircle(center: Offset(x + 2, y - 2), radius: r), - _degreesToRadians(-5).toDouble(), - _degreesToRadians(-80).toDouble(), - false); - path.close(); -} - -///calculate pyramid legend icon path -void _calculatePyramidIconPath( - Path path, double x, double y, double width, double height) { - path.moveTo(x - width / 2, y + height / 2); - path.lineTo(x + width / 2, y + height / 2); - path.lineTo(x, y - height / 2); - path.lineTo(x - width / 2, y + height / 2); - path.close(); -} - -///calculate funnel legend icon path -void _calculateFunnelIconPath( - Path path, double x, double y, double width, double height) { - path.moveTo(x + width / 2, y - height / 2); - path.lineTo(x, y + height / 2); - path.lineTo(x - width / 2, y - height / 2); - path.lineTo(x + width / 2, y - height / 2); - path.close(); -} - /// Calculate the rect bounds for column series and Bar series. Rect _calculateRectangle(num x1, num? y1, num x2, num? y2, CartesianSeriesRenderer seriesRenderer, SfCartesianChartState _chartState) { @@ -498,17 +366,21 @@ Rect _calculateShadowRectangle( RangeColumnSeries? rangeColumnSeries; HistogramSeries? histogramSeries; if (seriesRenderer._seriesType == 'column') { - columnSeries = seriesRenderer._series as ColumnSeries; + columnSeries = seriesRenderer._series as ColumnSeries; } else if (seriesRenderer._seriesType == 'bar') { - barSeries = seriesRenderer._series as BarSeries; + barSeries = seriesRenderer._series as BarSeries; } else if (seriesRenderer._seriesType == 'stackedcolumn') { - stackedColumnSeries = seriesRenderer._series as StackedColumnSeries; + stackedColumnSeries = + seriesRenderer._series as StackedColumnSeries; } else if (seriesRenderer._seriesType == 'stackedbar') { - stackedBarSeries = seriesRenderer._series as StackedBarSeries; + stackedBarSeries = + seriesRenderer._series as StackedBarSeries; } else if (seriesRenderer._seriesType == 'rangecolumn') { - rangeColumnSeries = seriesRenderer._series as RangeColumnSeries; + rangeColumnSeries = + seriesRenderer._series as RangeColumnSeries; } else if (seriesRenderer._seriesType == 'histogram') { - histogramSeries = seriesRenderer._series as HistogramSeries; + histogramSeries = + seriesRenderer._series as HistogramSeries; } return !_chartState._chart.isTransposed ? _getNormalShadowRect( @@ -793,7 +665,8 @@ _VisibleRange _calculateSideBySideInfo( } if (seriesRenderer._seriesType == 'column') { - final ColumnSeries columnSeries = series as ColumnSeries; + final ColumnSeries columnSeries = + series as ColumnSeries; seriesSpacing = chart.enableSideBySideSeriesPlacement ? columnSeries.spacing : 0; assert(columnSeries.width == null || columnSeries.width! <= 1, @@ -801,7 +674,7 @@ _VisibleRange _calculateSideBySideInfo( pointSpacing = columnSeries.width!; } else if (seriesRenderer._seriesType == 'histogram') { final HistogramSeries histogramSeries = - series as HistogramSeries; + series as HistogramSeries; seriesSpacing = chart.enableSideBySideSeriesPlacement ? histogramSeries.spacing : 0; assert(histogramSeries.width! <= 1, @@ -812,37 +685,39 @@ _VisibleRange _calculateSideBySideInfo( seriesRenderer._seriesType == 'stackedbar' || seriesRenderer._seriesType == 'stackedbar100') { final _StackedSeriesBase stackedRectSeries = - series as _StackedSeriesBase; + series as _StackedSeriesBase; seriesSpacing = chart.enableSideBySideSeriesPlacement ? stackedRectSeries.spacing : 0; pointSpacing = stackedRectSeries.width!; } else if (seriesRenderer._seriesType == 'rangecolumn') { final RangeColumnSeries rangeColumnSeries = - series as RangeColumnSeries; + series as RangeColumnSeries; seriesSpacing = chart.enableSideBySideSeriesPlacement ? rangeColumnSeries.spacing : 0; assert(rangeColumnSeries.width == null || rangeColumnSeries.width! <= 1, 'The width of the range column series must be less than or equal 1.'); pointSpacing = rangeColumnSeries.width; } else if (seriesRenderer._seriesType == 'hilo') { - final HiloSeries hiloSeries = series as HiloSeries; + final HiloSeries hiloSeries = + series as HiloSeries; seriesSpacing = chart.enableSideBySideSeriesPlacement ? hiloSeries.spacing : 0; pointSpacing = hiloSeries.width!; } else if (seriesRenderer._seriesType == 'hiloopenclose') { final HiloOpenCloseSeries hiloOpenCloseSeries = - series as HiloOpenCloseSeries; + series as HiloOpenCloseSeries; seriesSpacing = chart.enableSideBySideSeriesPlacement ? hiloOpenCloseSeries.spacing : 0; pointSpacing = hiloOpenCloseSeries.width!; } else if (seriesRenderer._seriesType == 'candle') { - final CandleSeries candleSeries = series as CandleSeries; + final CandleSeries candleSeries = + series as CandleSeries; seriesSpacing = chart.enableSideBySideSeriesPlacement ? candleSeries.spacing : 0; pointSpacing = candleSeries.width!; } else if (seriesRenderer._seriesType == 'boxandwhisker') { final BoxAndWhiskerSeries boxAndWhiskerSeries = - series as BoxAndWhiskerSeries; + series as BoxAndWhiskerSeries; seriesSpacing = chart.enableSideBySideSeriesPlacement ? boxAndWhiskerSeries.spacing : 0; assert(boxAndWhiskerSeries.width! <= 1, @@ -850,14 +725,15 @@ _VisibleRange _calculateSideBySideInfo( pointSpacing = boxAndWhiskerSeries.width!; } else if (seriesRenderer._seriesType == 'waterfall') { final WaterfallSeries waterfallSeries = - series as WaterfallSeries; + series as WaterfallSeries; seriesSpacing = chart.enableSideBySideSeriesPlacement ? waterfallSeries.spacing : 0; assert(waterfallSeries.width! <= 1, 'The width of the waterfall series must be less than or equal to 1.'); pointSpacing = waterfallSeries.width!; } else { - final BarSeries barSeries = series as BarSeries; + final BarSeries barSeries = + series as BarSeries; seriesSpacing = chart.enableSideBySideSeriesPlacement ? barSeries.spacing : 0; // assert(barSeries.width == null || barSeries.width! <= 1, @@ -959,32 +835,16 @@ void _calculateSideBySidePositions( num? position; final num seriesLength = seriesCollection.length; List<_StackingGroup>? stackingGroupPos; - for (final CartesianSeriesRenderer seriesRenderer in seriesCollection) { - if (seriesRenderer is ColumnSeriesRenderer) { - seriesRenderer._rectPosition = rectCount++; - seriesRenderer._rectCount = seriesLength; - } else if (seriesRenderer is BarSeriesRenderer) { - seriesRenderer._rectPosition = rectCount++; - seriesRenderer._rectCount = seriesLength; - } else if (seriesRenderer is RangeColumnSeriesRenderer) { - seriesRenderer._rectPosition = rectCount++; - seriesRenderer._rectCount = seriesLength; - } else if (seriesRenderer is HiloSeriesRenderer) { - seriesRenderer._rectPosition = rectCount++; - seriesRenderer._rectCount = seriesLength; - } else if (seriesRenderer is HiloOpenCloseSeriesRenderer) { - seriesRenderer._rectPosition = rectCount++; - seriesRenderer._rectCount = seriesLength; - } else if (seriesRenderer is HistogramSeriesRenderer) { - seriesRenderer._rectPosition = rectCount++; - seriesRenderer._rectCount = seriesLength; - } else if (seriesRenderer is CandleSeriesRenderer) { - seriesRenderer._rectPosition = rectCount++; - seriesRenderer._rectCount = seriesLength; - } else if (seriesRenderer is BoxAndWhiskerSeriesRenderer) { - seriesRenderer._rectPosition = rectCount++; - seriesRenderer._rectCount = seriesLength; - } else if (seriesRenderer is WaterfallSeriesRenderer) { + for (final dynamic seriesRenderer in seriesCollection) { + if (seriesRenderer is ColumnSeriesRenderer || + seriesRenderer is BarSeriesRenderer || + seriesRenderer is RangeColumnSeriesRenderer || + seriesRenderer is HiloSeriesRenderer || + seriesRenderer is HiloOpenCloseSeriesRenderer || + seriesRenderer is HistogramSeriesRenderer || + seriesRenderer is CandleSeriesRenderer || + seriesRenderer is BoxAndWhiskerSeriesRenderer || + seriesRenderer is WaterfallSeriesRenderer) { seriesRenderer._rectPosition = rectCount++; seriesRenderer._rectCount = seriesLength; } @@ -994,7 +854,7 @@ void _calculateSideBySidePositions( _StackedSeriesBase? series; if (seriesCollection[i] is _StackedSeriesRenderer) { seriesRenderer = seriesCollection[i]; - series = seriesRenderer._series as _StackedSeriesBase; + series = seriesRenderer._series as _StackedSeriesBase; } // ignore: unnecessary_null_comparison if (seriesRenderer != null && seriesRenderer is _StackedSeriesRenderer) { @@ -1174,7 +1034,7 @@ Paint _getShaderPaint(Shader shader) { } /// It returns the actual label value for tooltip and data label etc -dynamic _getLabelValue(dynamic value, dynamic axis, [int? showDigits]) { +String _getLabelValue(dynamic value, dynamic axis, [int? showDigits]) { if (value.toString().split('.').length > 1) { final String str = value.toString(); final List list = str.split('.'); @@ -1193,9 +1053,9 @@ dynamic _getLabelValue(dynamic value, dynamic axis, [int? showDigits]) { final dynamic text = axis is NumericAxis && axis.numberFormat != null ? axis.numberFormat!.format(value) : value; - return (axis.labelFormat != null && axis.labelFormat != '') + return ((axis.labelFormat != null && axis.labelFormat != '') ? axis.labelFormat.replaceAll(RegExp('{value}'), text.toString()) - : text.toString(); + : text.toString()) as String; } /// Calculate the X value from the current screen point @@ -1465,21 +1325,23 @@ dynamic _getInteractiveTooltipLabel( dynamic value, ChartAxisRenderer axisRenderer) { final ChartAxis axis = axisRenderer._axis; if (axisRenderer is CategoryAxisRenderer) { - value = value < 0 ? 0 : value; - value = axisRenderer._labels[(value.round() >= axisRenderer._labels.length - ? (value.round() > axisRenderer._labels.length - ? axisRenderer._labels.length - 1 - : value - 1) - : value) - .round()]; + value = value < 0 == true ? 0 : value; + value = axisRenderer._labels[ + (value.round() >= axisRenderer._labels.length == true + ? (value.round() > axisRenderer._labels.length == true + ? axisRenderer._labels.length - 1 + : value - 1) + : value) + .round()]; } else if (axisRenderer is DateTimeCategoryAxisRenderer) { - value = value < 0 ? 0 : value; - value = axisRenderer._labels[(value.round() >= axisRenderer._labels.length - ? (value.round() > axisRenderer._labels.length - ? axisRenderer._labels.length - 1 - : value - 1) - : value) - .round()]; + value = value < 0 == true ? 0 : value; + value = axisRenderer._labels[ + (value.round() >= axisRenderer._labels.length == true + ? (value.round() > axisRenderer._labels.length == true + ? axisRenderer._labels.length - 1 + : value - 1) + : value) + .round()]; } else if (axisRenderer is DateTimeAxisRenderer) { final DateTimeAxis _dateTimeAxis = axisRenderer._axis as DateTimeAxis; final DateFormat dateFormat = @@ -1497,7 +1359,8 @@ Path _getMarkerShapesPath(DataMarkerType markerType, Offset position, Size size, [CartesianSeriesRenderer? seriesRenderer, int? index, TrackballBehavior? trackballBehavior, - Animation? animationController]) { + Animation? animationController, + ChartSegment? segment]) { if (seriesRenderer?._chart.onMarkerRender != null && !seriesRenderer!._isMarkerRenderEvent) { final MarkerRenderArgs event = _triggerMarkerRenderEvent( @@ -1505,22 +1368,25 @@ Path _getMarkerShapesPath(DataMarkerType markerType, Offset position, Size size, size, markerType, seriesRenderer._dataPoints[index!].visiblePointIndex!, - animationController)!; + animationController, + segment)!; markerType = event.shape; size = Size(event.markerHeight, event.markerWidth); } final Path path = Path(); + final Rect rect = Rect.fromLTWH(position.dx - size.width / 2, + position.dy - size.height / 2, size.width, size.height); switch (markerType) { case DataMarkerType.circle: { - ShapeMaker.drawCircle( - path, position.dx, position.dy, size.width, size.height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.circle); } break; case DataMarkerType.rectangle: { - ShapeMaker.drawRectangle( - path, position.dx, position.dy, size.width, size.height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.rectangle); } break; case DataMarkerType.image: @@ -1532,38 +1398,40 @@ Path _getMarkerShapesPath(DataMarkerType markerType, Offset position, Size size, break; case DataMarkerType.pentagon: { - ShapeMaker.drawPentagon( - path, position.dx, position.dy, size.width, size.height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.pentagon); } break; case DataMarkerType.verticalLine: { - ShapeMaker.drawVerticalLine( - path, position.dx, position.dy, size.width, size.height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.verticalLine); } break; case DataMarkerType.invertedTriangle: { - ShapeMaker.drawInvertedTriangle( - path, position.dx, position.dy, size.width, size.height); + ShapePainter.getShapesPath( + path: path, + rect: rect, + shapeType: ShapeMarkerType.invertedTriangle); } break; case DataMarkerType.horizontalLine: { - ShapeMaker.drawHorizontalLine( - path, position.dx, position.dy, size.width, size.height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.horizontalLine); } break; case DataMarkerType.diamond: { - ShapeMaker.drawDiamond( - path, position.dx, position.dy, size.width, size.height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.diamond); } break; case DataMarkerType.triangle: { - ShapeMaker.drawTriangle( - path, position.dx, position.dy, size.width, size.height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.triangle); } break; case DataMarkerType.none: @@ -1591,7 +1459,7 @@ class _StackingGroup { void _loadMarkerImage(CartesianSeriesRenderer seriesRenderer, TrackballBehavior? trackballBehavior) async { final XyDataSeries series = - seriesRenderer._series as XyDataSeries; + seriesRenderer._series as XyDataSeries; if ((trackballBehavior != null && trackballBehavior.markerSettings != null && trackballBehavior.markerSettings!.shape == DataMarkerType.image && @@ -1603,7 +1471,7 @@ void _loadMarkerImage(CartesianSeriesRenderer seriesRenderer, series.markerSettings.shape == DataMarkerType.image && series.markerSettings.image != null)) { _calculateImage( - seriesRenderer._chartState, seriesRenderer, trackballBehavior); + seriesRenderer._chartState!, seriesRenderer, trackballBehavior); } } @@ -1635,7 +1503,7 @@ _ChartLocation _getAnnotationLocation( if (annotation.x != null && num.tryParse(annotation.x.toString()) != null) { xValue = num.tryParse(annotation.x.toString())!; - } else if (xAxisRenderer._labels.length > 0) { + } else if (xAxisRenderer._labels.isNotEmpty) { xValue = xAxisRenderer._labels.indexOf(annotation.x); } } else if (xAxisRenderer is DateTimeAxisRenderer) { @@ -1828,7 +1696,7 @@ void _renderStackingRectSeries( CartesianChartPoint _currentPoint, int currentSegmentIndex) { final _StackedSeriesBase series = - seriesRenderer._series as _StackedSeriesBase; + seriesRenderer._series as _StackedSeriesBase; if (seriesRenderer._isSelectionEnable) { final SelectionBehaviorRenderer? selectionBehaviorRenderer = seriesRenderer._selectionBehaviorRenderer; @@ -1850,7 +1718,7 @@ void _renderStackingRectSeries( if (strokePaint != null) { if (series.dashArray[0] != 0 && series.dashArray[1] != 0) { final XyDataSeries _series = - seriesRenderer._series as XyDataSeries; + seriesRenderer._series as XyDataSeries; _drawDashedLine(canvas, _series.dashArray, strokePaint, path); } else { series.animationDuration > 0 @@ -1867,20 +1735,6 @@ void _renderStackingRectSeries( } } -/// Render stacked area series -void _renderStackedAreaSeries( - CartesianSeriesRenderer seriesRenderer, - Paint fillPaint, - Paint strokePaint, - Canvas canvas, - int _seriesIndex, - Paint getFillPaint, - Path _path, - Path _strokePath) { - _drawStackedAreaPath( - _path, _strokePath, seriesRenderer, canvas, fillPaint, strokePaint); -} - /// Draw stacked area path void _drawStackedAreaPath( Path _path, @@ -1893,7 +1747,7 @@ void _drawStackedAreaPath( dynamic stackedAreaSegment; _pathRect = _path.getBounds(); final XyDataSeries _series = - seriesRenderer._series as XyDataSeries; + seriesRenderer._series as XyDataSeries; stackedAreaSegment = seriesRenderer._segments[0]; stackedAreaSegment._pathRect = _pathRect; if (seriesRenderer._isSelectionEnable) { @@ -1935,75 +1789,77 @@ void _stackedAreaPainter( dynamic seriesRenderer, SfCartesianChartState _chartState, Animation? seriesAnimation, - Animation chartElementAnimation, + Animation? chartElementAnimation, _PainterKey painterKey) { Rect clipRect, axisClipRect; final int seriesIndex = painterKey.index; final SfCartesianChart chart = _chartState._chart; + final _RenderingDetails _renderingDetails = _chartState._renderingDetails; seriesRenderer._storeSeriesProperties(_chartState, seriesIndex); double animationFactor; final num? crossesAt = _getCrossesAtValue(seriesRenderer, _chartState); + final List> dataPoints = + seriesRenderer._dataPoints; /// Clip rect will be added for series. - if (seriesRenderer._visible!) { + if (seriesRenderer._visible! == true) { final dynamic series = seriesRenderer._series; canvas.save(); axisClipRect = _calculatePlotOffset( - seriesRenderer._chartState!._chartAxis._axisClipRect, + _chartState._chartAxis._axisClipRect, Offset(seriesRenderer._xAxisRenderer?._axis?.plotOffset, seriesRenderer._yAxisRenderer?._axis?.plotOffset)); canvas.clipRect(axisClipRect); animationFactor = seriesAnimation != null ? seriesAnimation.value : 1; - if (seriesRenderer._reAnimate || - ((!(_chartState._widgetNeedUpdate || _chartState._isLegendToggled) || + if (seriesRenderer._reAnimate == true || + ((!(_renderingDetails.widgetNeedUpdate || + _renderingDetails.isLegendToggled) || !_chartState._oldSeriesKeys.contains(series.key)) && - series.animationDuration > 0)) { + series.animationDuration > 0 == true)) { _performLinearAnimation(_chartState, seriesRenderer._xAxisRenderer!._axis, canvas, animationFactor); } final Path _path = Path(), _strokePath = Path(); - final Rect rect = seriesRenderer._chartState!._chartAxis._axisClipRect; + final Rect rect = _chartState._chartAxis._axisClipRect; _ChartLocation point1, point2; final ChartAxisRenderer xAxisRenderer = seriesRenderer._xAxisRenderer!, yAxisRenderer = seriesRenderer._yAxisRenderer!; CartesianChartPoint? point; final dynamic _series = seriesRenderer._series; final List _points = []; - if (seriesRenderer._dataPoints.isNotEmpty) { + if (dataPoints.isNotEmpty) { int startPoint = 0; final _StackedValues stackedValues = seriesRenderer._stackingValues[0]; List seriesRendererCollection; CartesianSeriesRenderer previousSeriesRenderer; seriesRendererCollection = _findSeriesCollection(_chartState); point1 = _calculatePoint( - seriesRenderer._dataPoints[0].xValue, + dataPoints[0].xValue, math_lib.max(yAxisRenderer._visibleRange!.minimum, crossesAt ?? stackedValues.startValues[0]), xAxisRenderer, yAxisRenderer, - seriesRenderer._chartState!._requireInvertedAxis, + _chartState._requireInvertedAxis, _series, rect); _path.moveTo(point1.x, point1.y); _strokePath.moveTo(point1.x, point1.y); if (seriesRenderer._visibleDataPoints == null || - seriesRenderer._visibleDataPoints!.isNotEmpty) { + seriesRenderer._visibleDataPoints!.isNotEmpty == true) { seriesRenderer._visibleDataPoints = >[]; } - for (int pointIndex = 0; - pointIndex < seriesRenderer._dataPoints.length; - pointIndex++) { - point = seriesRenderer._dataPoints[pointIndex]; + for (int pointIndex = 0; pointIndex < dataPoints.length; pointIndex++) { + point = dataPoints[pointIndex]; seriesRenderer._calculateRegionData( - _chartState, seriesRenderer, seriesIndex, point!, pointIndex); + _chartState, seriesRenderer, seriesIndex, point, pointIndex); if (point.isVisible) { point1 = _calculatePoint( - seriesRenderer._dataPoints[pointIndex].xValue, + dataPoints[pointIndex].xValue, stackedValues.endValues[pointIndex], xAxisRenderer, yAxisRenderer, - seriesRenderer._chartState!._requireInvertedAxis, + _chartState._requireInvertedAxis, _series, rect); _points.add(Offset(point1.x, point1.y)); @@ -2013,11 +1869,11 @@ void _stackedAreaPainter( if (_series.emptyPointSettings.mode != EmptyPointMode.drop) { for (int j = pointIndex - 1; j >= startPoint; j--) { point2 = _calculatePoint( - seriesRenderer._dataPoints[j].xValue, + dataPoints[j].xValue, crossesAt ?? stackedValues.startValues[j], xAxisRenderer, yAxisRenderer, - seriesRenderer._chartState!._requireInvertedAxis, + _chartState._requireInvertedAxis, _series, rect); _path.lineTo(point2.x, point2.y); @@ -2027,15 +1883,16 @@ void _stackedAreaPainter( _strokePath.lineTo(point2.x, point2.y); } } - if (seriesRenderer._dataPoints.length > pointIndex + 1 && - seriesRenderer._dataPoints[pointIndex + 1] != null && - seriesRenderer._dataPoints[pointIndex + 1].isVisible) { + if (dataPoints.length > pointIndex + 1 && + // ignore: unnecessary_null_comparison + dataPoints[pointIndex + 1] != null && + dataPoints[pointIndex + 1].isVisible) { point1 = _calculatePoint( - seriesRenderer._dataPoints[pointIndex + 1].xValue, + dataPoints[pointIndex + 1].xValue, crossesAt ?? stackedValues.startValues[pointIndex + 1], xAxisRenderer, yAxisRenderer, - seriesRenderer._chartState!._requireInvertedAxis, + _chartState._requireInvertedAxis, _series, rect); _path.moveTo(point1.x, point1.y); @@ -2044,25 +1901,23 @@ void _stackedAreaPainter( startPoint = pointIndex + 1; } } - if (pointIndex >= seriesRenderer._dataPoints.length - 1) { + if (pointIndex >= dataPoints.length - 1) { seriesRenderer._createSegments( painterKey.index, chart, animationFactor, _points); } } - for (int j = seriesRenderer._dataPoints.length - 1; - j >= startPoint; - j--) { + for (int j = dataPoints.length - 1; j >= startPoint; j--) { previousSeriesRenderer = _getPreviousSeriesRenderer(seriesRendererCollection, seriesIndex); if (previousSeriesRenderer._series.emptyPointSettings.mode != EmptyPointMode.drop || previousSeriesRenderer._dataPoints[j].isVisible) { point2 = _calculatePoint( - seriesRenderer._dataPoints[j].xValue, + dataPoints[j].xValue, crossesAt ?? stackedValues.startValues[j], xAxisRenderer, yAxisRenderer, - seriesRenderer._chartState!._requireInvertedAxis, + _chartState._requireInvertedAxis, _series, rect); _path.lineTo(point2.x, point2.y); @@ -2077,7 +1932,7 @@ void _stackedAreaPainter( // ignore: unnecessary_null_comparison if (_path != null && seriesRenderer._segments != null && - seriesRenderer._segments.isNotEmpty) { + seriesRenderer._segments.isNotEmpty == true) { final dynamic areaSegment = seriesRenderer._segments[0]; seriesRenderer._drawSegment( canvas, @@ -2088,22 +1943,18 @@ void _stackedAreaPainter( clipRect = _calculatePlotOffset( Rect.fromLTRB( - seriesRenderer._chartState!._chartAxis._axisClipRect.left - - _series.markerSettings.width, - seriesRenderer._chartState!._chartAxis._axisClipRect.top - - _series.markerSettings.height, - seriesRenderer._chartState!._chartAxis._axisClipRect.right + - _series.markerSettings.width, - seriesRenderer._chartState!._chartAxis._axisClipRect.bottom + - _series.markerSettings.height), + rect.left - _series.markerSettings.width, + rect.top - _series.markerSettings.height, + rect.right + _series.markerSettings.width, + rect.bottom + _series.markerSettings.height), Offset(seriesRenderer._xAxisRenderer?._axis?.plotOffset, seriesRenderer._yAxisRenderer?._axis?.plotOffset)); canvas.restore(); - if ((_series.animationDuration <= 0 || - !_chartState._initialRender! || + if ((_series.animationDuration <= 0 == true || + !_renderingDetails.initialRender! || animationFactor >= _chartState._seriesDurationFactor) && - (_series.markerSettings.isVisible || - _series.dataLabelSettings.isVisible)) { + (_series.markerSettings.isVisible == true || + _series.dataLabelSettings.isVisible == true)) { canvas.clipRect(clipRect); seriesRenderer._renderSeriesElements( chart, canvas, chartElementAnimation); @@ -2130,11 +1981,12 @@ CartesianSeriesRenderer _getPreviousSeriesRenderer( /// Rect painter for stacked series void _stackedRectPainter(Canvas canvas, dynamic seriesRenderer, SfCartesianChartState _chartState, _PainterKey painterKey) { - if (seriesRenderer._visible!) { + if (seriesRenderer._visible! == true) { canvas.save(); Rect clipRect, axisClipRect; CartesianChartPoint point; final XyDataSeries series = seriesRenderer._series; + final _RenderingDetails _renderingDetails = _chartState._renderingDetails; final int seriesIndex = painterKey.index; final Animation seriesAnimation = seriesRenderer._seriesAnimation; final Animation chartElementAnimation = @@ -2143,9 +1995,9 @@ void _stackedRectPainter(Canvas canvas, dynamic seriesRenderer, double animationFactor; // ignore: unnecessary_null_comparison animationFactor = seriesAnimation != null && - (seriesRenderer._reAnimate || - (!(_chartState._widgetNeedUpdate || - _chartState._isLegendToggled))) + (seriesRenderer._reAnimate == true || + (!(_renderingDetails.widgetNeedUpdate || + _renderingDetails.isLegendToggled))) ? seriesAnimation.value : 1; @@ -2157,7 +2009,7 @@ void _stackedRectPainter(Canvas canvas, dynamic seriesRenderer, canvas.clipRect(axisClipRect); int segmentIndex = -1; if (seriesRenderer._visibleDataPoints == null || - seriesRenderer._visibleDataPoints!.isNotEmpty) { + seriesRenderer._visibleDataPoints!.isNotEmpty == true) { seriesRenderer._visibleDataPoints = >[]; } for (int pointIndex = 0; @@ -2187,7 +2039,7 @@ void _stackedRectPainter(Canvas canvas, dynamic seriesRenderer, seriesRenderer._yAxisRenderer?._axis?.plotOffset)); canvas.restore(); if ((series.animationDuration <= 0 || - !_chartState._initialRender! || + !_renderingDetails.initialRender! || animationFactor >= _chartState._seriesDurationFactor) && (series.markerSettings.isVisible || series.dataLabelSettings.isVisible)) { @@ -2207,11 +2059,12 @@ void _stackedLinePainter( dynamic seriesRenderer, Animation? seriesAnimation, SfCartesianChartState _chartState, - Animation chartElementAnimation, + Animation? chartElementAnimation, _PainterKey painterKey) { Rect clipRect; double animationFactor; - if (seriesRenderer._visible!) { + final _RenderingDetails _renderingDetails = _chartState._renderingDetails; + if (seriesRenderer._visible! == true) { final XyDataSeries series = seriesRenderer._series; canvas.save(); animationFactor = seriesAnimation != null ? seriesAnimation.value : 1; @@ -2222,15 +2075,17 @@ void _stackedLinePainter( seriesRenderer._stackingValues.isNotEmpty) { stackedValues = seriesRenderer._stackingValues[0]; } + final Rect rect = seriesRenderer._chartState!._chartAxis._axisClipRect; /// Clip rect will be added for series. final Rect axisClipRect = _calculatePlotOffset( - seriesRenderer._chartState!._chartAxis._axisClipRect, + rect, Offset(seriesRenderer._xAxisRenderer?._axis?.plotOffset, seriesRenderer._yAxisRenderer?._axis?.plotOffset)); canvas.clipRect(axisClipRect); - if (seriesRenderer._reAnimate || - ((!(_chartState._widgetNeedUpdate || _chartState._isLegendToggled) || + if (seriesRenderer._reAnimate == true || + ((!(_renderingDetails.widgetNeedUpdate || + _renderingDetails.isLegendToggled) || !_chartState._oldSeriesKeys.contains(series.key)) && series.animationDuration > 0)) { _performLinearAnimation(_chartState, seriesRenderer._xAxisRenderer!._axis, @@ -2244,7 +2099,7 @@ void _stackedLinePainter( currentPoint, _nextPoint; if (seriesRenderer._visibleDataPoints == null || - seriesRenderer._visibleDataPoints!.isNotEmpty) { + seriesRenderer._visibleDataPoints!.isNotEmpty == true) { seriesRenderer._visibleDataPoints = >[]; } for (int pointIndex = 0; @@ -2287,19 +2142,15 @@ void _stackedLinePainter( } clipRect = _calculatePlotOffset( Rect.fromLTRB( - seriesRenderer._chartState!._chartAxis._axisClipRect.left - - series.markerSettings.width, - seriesRenderer._chartState!._chartAxis._axisClipRect.top - - series.markerSettings.height, - seriesRenderer._chartState!._chartAxis._axisClipRect.right + - series.markerSettings.width, - seriesRenderer._chartState!._chartAxis._axisClipRect.bottom + - series.markerSettings.height), + rect.left - series.markerSettings.width, + rect.top - series.markerSettings.height, + rect.right + series.markerSettings.width, + rect.bottom + series.markerSettings.height), Offset(seriesRenderer._xAxisRenderer?._axis?.plotOffset, seriesRenderer._yAxisRenderer?._axis?.plotOffset)); canvas.restore(); if ((series.animationDuration <= 0 || - !_chartState._initialRender! || + !_renderingDetails.initialRender! || animationFactor >= _chartState._seriesDurationFactor) && (series.markerSettings.isVisible || series.dataLabelSettings.isVisible)) { @@ -2555,26 +2406,34 @@ MarkerRenderArgs? _triggerMarkerRenderEvent( Size size, DataMarkerType markerType, int pointIndex, - Animation? animationController) { + Animation? animationController, + [ChartSegment? segment]) { MarkerRenderArgs? markerargs; final int seriesIndex = seriesRenderer ._chartState!._chartSeries.visibleSeriesRenderers .indexOf(seriesRenderer); final XyDataSeries series = - seriesRenderer._series as XyDataSeries; + seriesRenderer._series as XyDataSeries; final MarkerSettingsRenderer markerSettingsRenderer = seriesRenderer._markerSettingsRenderer!; markerSettingsRenderer._color = series.markerSettings.color; + final bool isScatter = segment != null && segment is ScatterSegment; if (seriesRenderer._chart.onMarkerRender != null) { markerargs = MarkerRenderArgs(pointIndex, seriesIndex, seriesRenderer._visibleDataPoints![pointIndex].overallDataPointIndex!); markerargs.markerHeight = size.height; markerargs.markerWidth = size.width; markerargs.shape = markerType; - markerargs.color = markerSettingsRenderer._color; - markerargs.borderColor = markerSettingsRenderer._borderColor; - markerargs.borderWidth = markerSettingsRenderer._borderWidth; + markerargs.color = + isScatter ? segment.fillPaint!.color : markerSettingsRenderer._color; + markerargs.borderColor = isScatter + ? segment.strokePaint!.color + : markerSettingsRenderer._borderColor; + markerargs.borderWidth = isScatter + ? segment.strokePaint!.strokeWidth + : markerSettingsRenderer._borderWidth; if (animationController == null || + isScatter || ((animationController.value == 1.0 && animationController.status == AnimationStatus.completed) || seriesRenderer._animationController.duration!.inMilliseconds == @@ -2586,6 +2445,22 @@ MarkerRenderArgs? _triggerMarkerRenderEvent( markerSettingsRenderer._borderColor = markerargs.borderColor; markerSettingsRenderer._borderWidth = markerargs.borderWidth; } + + if (isScatter) { + segment.fillPaint!.color = (markerargs.color != null && + segment.fillPaint!.color != markerargs.color + ? markerargs.color + : segment.fillPaint!.color)!; + segment.strokePaint!.color = (markerargs.borderColor != null && + segment.strokePaint!.color != markerargs.borderColor + ? markerargs.borderColor + : segment.strokePaint!.color)!; + // ignore: unnecessary_null_comparison + segment.strokePaint!.strokeWidth = markerargs.borderWidth != null && + segment.strokePaint!.strokeWidth != markerargs.borderWidth + ? markerargs.borderWidth + : segment.strokePaint!.strokeWidth; + } } return markerargs; } @@ -2678,21 +2553,19 @@ void _calculateSplineAreaControlPoints(CartesianSeriesRenderer seriesRenderer) { xValues, highValues, highCoef, xValues.length, splineType); } } - if (seriesRenderer is SplineAreaSeriesRenderer || - seriesRenderer is SplineSeriesRenderer) { - _updateSplineAreaControlPoints( - seriesRenderer, splineType, xValues, yValues, yCoef, dx); - } else { - _findSplineRangeAreaControlPoint( - seriesRenderer as SplineRangeAreaSeriesRenderer, - splineType, - xValues, - lowValues, - highValues, - dx, - lowCoef, - highCoef); - } + (seriesRenderer is SplineAreaSeriesRenderer || + seriesRenderer is SplineSeriesRenderer) + ? _updateSplineAreaControlPoints( + seriesRenderer, splineType, xValues, yValues, yCoef, dx) + : _findSplineRangeAreaControlPoint( + seriesRenderer as SplineRangeAreaSeriesRenderer, + splineType, + xValues, + lowValues, + highValues, + dx, + lowCoef, + highCoef); } } @@ -2705,9 +2578,8 @@ void _updateSplineAreaControlPoints( List? yCoef, List? dx) { double x, y, nextX, nextY; - int pointIndex; List controlPoints; - for (pointIndex = 0; pointIndex < xValues.length - 1; pointIndex++) { + for (int pointIndex = 0; pointIndex < xValues.length - 1; pointIndex++) { controlPoints = []; // ignore: unnecessary_null_comparison if (xValues[pointIndex] != null && @@ -2901,8 +2773,8 @@ CartesianChartPoint? _getChartPoint( minVal = seriesRenderer._histogramValues.minValue; yVal = seriesRenderer._histogramValues.yValues! .where((dynamic x) => - x >= minVal && - x < (minVal + seriesRenderer._histogramValues.binWidth)) + x >= minVal == true && + x < (minVal + seriesRenderer._histogramValues.binWidth) == true) .length; xVal = minVal + seriesRenderer._histogramValues.binWidth! / 2; minVal += seriesRenderer._histogramValues.binWidth; @@ -2940,6 +2812,7 @@ CartesianChartPoint? _getChartPoint( } if (yVal != null && series is BoxAndWhiskerSeries) { final List yValues = yVal; + //ignore: always_specify_types yValues.removeWhere((value) => value == null); maximumVal = yValues.cast().reduce(max); minimumVal = yValues.cast().reduce(min); @@ -2954,7 +2827,7 @@ CartesianChartPoint? _getChartPoint( if (series is _FinancialSeriesBase) { final _FinancialSeriesBase financialSeries = - seriesRenderer._series as _FinancialSeriesBase; + seriesRenderer._series as _FinancialSeriesBase; final ChartIndexedValueMapper? _openMap = financialSeries.openValueMapper; final ChartIndexedValueMapper? _closeMap = @@ -3051,7 +2924,7 @@ bool _findChangesInPoint( point.sortValue != oldPoint.sortValue; } else if (seriesRenderer._seriesType == 'waterfall') { return point.x != oldPoint.x || - (point.y != null ? (point.y != oldPoint.y) : false) || + (point.y != null && (point.y != oldPoint.y)) || point.sortValue != oldPoint.sortValue || point.isIntermediateSum != oldPoint.isIntermediateSum || point.isTotalSum != oldPoint.isTotalSum; @@ -3102,7 +2975,8 @@ _VisibleRange _calculateYRangeOnZoomX( for (int j = 0; j < _seriesRenderers[i]._dataPoints.length; j++) { final CartesianChartPoint point = _seriesRenderers[i]._dataPoints[j]; - if (point.xValue >= xRange.minimum && point.xValue <= xRange.maximum) { + if (point.xValue >= xRange.minimum == true && + point.xValue <= xRange.maximum == true) { if (point.yValue != null) { _mini = min(_mini ?? point.yValue, point.yValue); _maxi = max(_maxi ?? point.yValue, point.yValue); @@ -3163,13 +3037,13 @@ int _getOldSegmentIndex(ChartSegment segment) { //this method determines the whether all the series animations has been completed and renders the datalabel void _setAnimationStatus(dynamic chartState) { if (chartState._totalAnimatingSeries == chartState._animationCompleteCount) { - chartState._animateCompleted = true; + chartState._renderingDetails.animateCompleted = true; chartState._animationCompleteCount = 0; } else { - chartState._animateCompleted = false; + chartState._renderingDetails.animateCompleted = false; } if (chartState._renderDataLabel != null) { - if (chartState._renderDataLabel.state.mounted) { + if (chartState._renderDataLabel.state.mounted == true) { chartState._renderDataLabel.state?.render(); } } @@ -3197,6 +3071,7 @@ int _calculateDateTimeNiceInterval( startDate ??= DateTime.fromMillisecondsSinceEpoch(range.minimum.toInt()); endDate ??= DateTime.fromMillisecondsSinceEpoch(range.maximum.toInt()); num? interval; + const num hours = 24, minutes = 60, seconds = 60, milliseconds = 1000; final num totalDays = ((startDate.millisecondsSinceEpoch - endDate.millisecondsSinceEpoch) / perDay) @@ -3221,15 +3096,19 @@ int _calculateDateTimeNiceInterval( break; case DateTimeIntervalType.hours: interval = axisRenderer._calculateNumericNiceInterval( - axisRenderer, totalDays * 24, size); + axisRenderer, totalDays * hours, size); break; case DateTimeIntervalType.minutes: interval = axisRenderer._calculateNumericNiceInterval( - axisRenderer, totalDays * 24 * 60, size); + axisRenderer, totalDays * hours * minutes, size); break; case DateTimeIntervalType.seconds: interval = axisRenderer._calculateNumericNiceInterval( - axisRenderer, totalDays * 24 * 60 * 60, size); + axisRenderer, totalDays * hours * minutes * seconds, size); + break; + case DateTimeIntervalType.milliseconds: + interval = axisRenderer._calculateNumericNiceInterval(axisRenderer, + totalDays * hours * minutes * seconds * milliseconds, size); break; case DateTimeIntervalType.auto: @@ -3292,12 +3171,24 @@ int _calculateDateTimeNiceInterval( /// for seconds interval = axisRenderer._calculateNumericNiceInterval( axisRenderer, totalDays * 24 * 60 * 60, size); + if (interval >= 1) { + _setActualIntervalType( + axisRenderer, + notDoubleInterval + ? DateTimeIntervalType.seconds + : DateTimeIntervalType.minutes); + return interval.floor(); + } + + /// for milliseconds + interval = axisRenderer._calculateNumericNiceInterval( + axisRenderer, totalDays * 24 * 60 * 60 * 1000, size); _setActualIntervalType( axisRenderer, notDoubleInterval - ? DateTimeIntervalType.seconds - : DateTimeIntervalType.minutes); - return interval.floor(); + ? DateTimeIntervalType.milliseconds + : DateTimeIntervalType.seconds); + return interval < 1 ? interval.ceil() : interval.floor(); default: break; } @@ -3345,6 +3236,10 @@ DateFormat _getDateTimeLabelFormat(ChartAxisRenderer axisRenderer) { case DateTimeIntervalType.seconds: format = notDoubleInterval ? DateFormat.ms() : DateFormat.ms(); break; + case DateTimeIntervalType.milliseconds: + final DateFormat? _format = DateFormat('ss.SSS'); + format = notDoubleInterval ? _format : _format; + break; case DateTimeIntervalType.auto: break; default: @@ -3357,7 +3252,7 @@ void _setCategoryMinMaxValues( ChartAxisRenderer axisRenderer, bool isXVisibleRange, bool isYVisibleRange, - CartesianChartPoint point, + CartesianChartPoint point, int pointIndex, int dataLength, CartesianSeriesRenderer seriesRenderer) { @@ -3452,8 +3347,12 @@ void _calculateDateTimeVisibleRange( Size availableSize, ChartAxisRenderer axisRenderer) { final _VisibleRange actualRange = axisRenderer._actualRange!; final SfCartesianChartState chartState = axisRenderer._chartState; - axisRenderer._visibleRange = - _VisibleRange(actualRange.minimum, actualRange.maximum); + axisRenderer._setOldRangeFromRangeController(); + axisRenderer._visibleRange = chartState._rangeChangeBySlider && + axisRenderer._rangeMinimum != null && + axisRenderer._rangeMaximum != null + ? _VisibleRange(axisRenderer._rangeMinimum, axisRenderer._rangeMaximum) + : _VisibleRange(actualRange.minimum, actualRange.maximum); final _VisibleRange visibleRange = axisRenderer._visibleRange!; visibleRange.delta = actualRange.delta; visibleRange.interval = actualRange.interval; @@ -3469,15 +3368,21 @@ void _calculateDateTimeVisibleRange( axisRenderer._axis.autoScrollingDelta!, axisRenderer); } } - if (!canAutoScroll) { + if ((!canAutoScroll || chartState._zoomedState == true) && + !(chartState._rangeChangeBySlider && + !chartState._canSetRangeController)) { axisRenderer._setZoomFactorAndPosition( axisRenderer, chartState._zoomedAxisRendererStates); } - if (axisRenderer._zoomFactor < 1 || axisRenderer._zoomPosition > 0) { + if (axisRenderer._zoomFactor < 1 || + axisRenderer._zoomPosition > 0 || + (axisRenderer._axis.rangeController != null && + !chartState._renderingDetails.initialRender!)) { chartState._zoomProgress = true; axisRenderer._calculateZoomRange(axisRenderer, availableSize); visibleRange.interval = axisRenderer._axis.enableAutoIntervalOnZooming && chartState._zoomProgress && + !canAutoScroll && axisRenderer is DateTimeAxisRenderer ? axisRenderer.calculateInterval(visibleRange, availableSize) : visibleRange.interval; @@ -3485,11 +3390,15 @@ void _calculateDateTimeVisibleRange( visibleRange.minimum = (visibleRange.minimum).floor(); visibleRange.maximum = (visibleRange.maximum).floor(); } - if (axisRenderer._axis.rangeController != null) { + if (axisRenderer._axis.rangeController != null && + chartState._isRedrawByZoomPan && + chartState._canSetRangeController && + chartState._zoomProgress) { chartState._rangeChangedByChart = true; axisRenderer._setRangeControllerValues(axisRenderer); } } + axisRenderer._setZoomValuesFromRangeController(); } // This method used to return the cross value of the axis. @@ -3519,6 +3428,9 @@ List _getTooltipPaddingData(CartesianSeriesRenderer seriesRenderer, padding = Offset(region.center.dx - region.centerLeft.dx, 2 * (region.center.dy - region.topCenter.dy)); position = Offset(tooltipPosition!.dx, paddedRegion.top); + if (region.top < 0) { + padding = Offset(padding.dx, padding.dy + region.top); + } } else if (seriesRenderer._seriesType == 'scatter') { padding = Offset(seriesRenderer._series.markerSettings.width, seriesRenderer._series.markerSettings.height / 2); @@ -3544,7 +3456,7 @@ CartesianSeriesRenderer? _getOldSeriesRenderer( CartesianSeriesRenderer seriesRenderer, int seriesIndex, List oldSeriesRenderers) { - if (chartState._widgetNeedUpdate && + if (chartState._renderingDetails.widgetNeedUpdate && seriesRenderer._xAxisRenderer!._zoomFactor == 1 && seriesRenderer._yAxisRenderer!._zoomFactor == 1 && // ignore: unnecessary_null_comparison @@ -3560,7 +3472,7 @@ CartesianSeriesRenderer? _getOldSeriesRenderer( } //Returns the old chart point for the given point and series index if present. -CartesianChartPoint? _getOldChartPoint( +CartesianChartPoint? _getOldChartPoint( SfCartesianChartState chartState, CartesianSeriesRenderer seriesRenderer, Type segmentType, @@ -3568,10 +3480,11 @@ CartesianChartPoint? _getOldChartPoint( int pointIndex, CartesianSeriesRenderer? oldSeriesRenderer, List oldSeriesRenderers) { + final _RenderingDetails _renderingDetails = chartState._renderingDetails; return !seriesRenderer._reAnimate && (seriesRenderer._series.animationDuration > 0 && - chartState._widgetNeedUpdate && - !chartState._isLegendToggled && + _renderingDetails.widgetNeedUpdate && + !_renderingDetails.isLegendToggled && // ignore: unnecessary_null_comparison oldSeriesRenderers != null && oldSeriesRenderers.isNotEmpty && @@ -3644,14 +3557,10 @@ int? _getVisibleDataPointIndex( return index; } -bool _checkSeriesType(String seriesType) { - bool isLineSeriesType = false; - if (seriesType == 'line' || +bool _isLineTypeSeries(String seriesType) { + return seriesType == 'line' || seriesType == 'spline' || seriesType == 'stepline' || seriesType == 'stackedline' || - seriesType == 'stackedline100') { - isLineSeriesType = true; - } - return isLineSeriesType; + seriesType == 'stackedline100'; } diff --git a/packages/syncfusion_flutter_charts/lib/src/circular_chart/base/circular_base.dart b/packages/syncfusion_flutter_charts/lib/src/circular_chart/base/circular_base.dart index c0ed902af..c74c8f95e 100644 --- a/packages/syncfusion_flutter_charts/lib/src/circular_chart/base/circular_base.dart +++ b/packages/syncfusion_flutter_charts/lib/src/circular_chart/base/circular_base.dart @@ -1,30 +1,5 @@ part of charts; -/// Returns the LegendRenderArgs. -typedef CircularLegendRenderCallback = void Function( - LegendRenderArgs legendRenderArgs); - -/// Returns the TooltipArgs. -typedef CircularTooltipCallback = void Function(TooltipArgs tooltipArgs); - -/// Returns the DataLabelRenderArgs. -typedef CircularDatalabelRenderCallback = void Function( - DataLabelRenderArgs dataLabelArgs); - -/// Returns the PointTapArgs. -typedef CircularPointTapCallback = void Function(PointTapArgs pointTapArgs); - -/// Returns the SelectionArgs. -typedef CircularSelectionCallback = void Function(SelectionArgs selectionArgs); - -/// Returns the offset. -typedef CircularTouchInteractionCallback = void Function( - ChartTouchInteractionArgs tapArgs); - -///Signature for the callback that returns the shader value to override the fill color of the data points. -typedef CircularShaderCallback = Shader Function( - ChartShaderDetails chartShaderDetails); - ///Renders the circular chart /// ///The SfCircularChart supports pie, doughnut and radial bar series that can be customized within the Circular Charts class. @@ -69,7 +44,7 @@ class SfCircularChart extends StatefulWidget { this.onLegendItemRender, this.onTooltipRender, this.onDataLabelRender, - this.onPointTapped, + @deprecated this.onPointTapped, this.onDataLabelTapped, this.onLegendTapped, this.onSelectionChanged, @@ -232,28 +207,6 @@ class SfCircularChart extends StatefulWidget { ///``` final double borderWidth; - ///Data points or series can be selected while performing interaction on the chart. - ///It can also be selected at the initial rendering using this property. - /// - ///Defaults to `[]`. - /// - ///```dart - ///Widget build(BuildContext context) { - /// return Container( - /// child: SfCircularChart( - /// series: >[ - /// PieSeries( - /// initialSelectedDataIndexes: [2,0], - /// selectionSettings: SelectionSettings( - /// selectedColor: Colors.red.withOpacity(0.8), - /// unselectedColor: Colors.grey.withOpacity(0.5) - /// ), - /// ), - /// ], - /// )); - ///} - ///``` - ///Background image for chart. /// ///Defaults to `null`. @@ -362,6 +315,7 @@ class SfCircularChart extends StatefulWidget { /// print(args.seriesIndex); ///} ///``` + @Deprecated('Use onPointTap in CircularSeries instead.') final CircularPointTapCallback? onPointTapped; ///Fills the data points with the gradient and image shaders. @@ -388,7 +342,7 @@ class SfCircularChart extends StatefulWidget { ///Whenever the data label is tapped, `onDataLabelTapped` callback will be called. Provides options to /// get the position of the data label, series index, point index and its text. /// - ///_Note:_ - This callback will not be called, when the builder is specified for data label + ///_Note:_ This callback will not be called, when the builder is specified for data label /// (data label template). For this case, custom widget specified in the `DataLabelSettings.builder` property /// can be wrapped using the `GestureDetector` and this functionality can be achieved in the application level. ///```dart @@ -507,7 +461,7 @@ class SfCircularChart extends StatefulWidget { /// selectionGesture: ActivationMode.singleTap, /// series: >[ /// PieSeries( - /// selectionSettings: SelectionSettings( + /// selectionBehavior: SelectionBehavior( /// selectedColor: Colors.red.withOpacity(0.8), /// unselectedColor: Colors.grey.withOpacity(0.5) /// ), @@ -529,7 +483,7 @@ class SfCircularChart extends StatefulWidget { /// enableMultiSelection: true, /// series: >[ /// PieSeries( - /// selectionSettings: SelectionSettings( + /// selectionBehavior: SelectionBehavior( /// selectedColor: Colors.red.withOpacity(0.8), /// unselectedColor: Colors.grey.withOpacity(0.5) /// ), @@ -548,91 +502,47 @@ class SfCircularChart extends StatefulWidget { /// class SfCircularChartState extends State with TickerProviderStateMixin { - /// Holds the animation controller list for all series - //ignore: unused_field - late List _controllerList; - - /// Animation controller for series - late AnimationController _animationController; - - /// Animation controller for Annotations - //ignore: unused_field - late AnimationController _annotationController; - - /// Repaint notifier for series container - late ValueNotifier _seriesRepaintNotifier; - - /// To measure legend size and position - late List<_MeasureWidgetContext> _legendWidgetContext; - - /// Chart Template info - late List<_ChartTemplateInfo> _templates; - - /// List of container widgets for chart series - List? _chartWidgets; - - /// Holds the information of chart theme arguments - late SfChartThemeData _chartTheme; + /// Specifies the center location late Offset _centerLocation; - late Rect _chartContainerRect; - late Rect _chartAreaRect; - late bool _animateCompleted; - late List _explodedPoints; - late List<_LegendRenderContext> _legendToggleStates; - late List<_MeasureWidgetContext> _legendToggleTemplateStates; - bool? _initialRender; - //ignore: unused_field - late List> _selectedDataPoints; - //ignore: unused_field - late List> _unselectedDataPoints; - //ignore: unused_field - late List<_Region> _selectedRegions; - //ignore: unused_field - late List<_Region> _unselectedRegions; - late List _dataLabelTemplateRegions; + + /// Specifies the annoatation region late List _annotationRegions; - _ChartTemplate? _chartTemplate; - _ChartInteraction? _currentActive; - Offset? _tapPosition; + + /// Specifies the data label renderer _CircularDataLabelRenderer? _renderDataLabel; - late bool _widgetNeedUpdate; - late bool _isLegendToggled; + + /// Specifies the previous series renderer CircularSeriesRenderer? _prevSeriesRenderer; + + /// Specifies the previous chart points List?>? _oldPoints; - late List _selectionData; - //ignore: unused_field - late Animation _chartElementAnimation; - Orientation? _oldDeviceOrientation; - late Orientation _deviceOrientation; - //ignore: unused_field - CircularSeriesRenderer? _seriesRenderer; - Size? _prevSize; - bool _didSizeChange = false; + //Here, we are using get keyword inorder to get the proper & updated instance of chart widget //When we initialize chart widget as a property to other classes like _ChartSeries, the chart widget is not updated properly and by using get we can rectify this. SfCircularChart get _chart => widget; - late _ChartLegend _chartLegend; - /// Holds the information of SeriesBase class late _CircularSeries _chartSeries; - //ignore: unused_field + /// Specifies the circular chart area late _CircularArea _circularArea; + /// Specifies whether move the label from center late bool _needToMoveFromCenter; + /// Specifies whether to explode the segments late bool _needExplodeAll; - late TooltipBehaviorRenderer _tooltipBehaviorRenderer; - late LegendRenderer _legendRenderer; + /// Gets or sets the value for is toggled + late bool _isToggled; - //ignore: prefer_final_fields - bool _isToggled = false; + /// Specifies the chart rendering details + late _RenderingDetails _renderingDetails; // ignore: unused_element bool get _animationCompleted { - return _animationController.status != AnimationStatus.forward; + return _renderingDetails.animationController.status != + AnimationStatus.forward; } /// Called when this object is inserted into the tree. @@ -648,6 +558,9 @@ class SfCircularChartState extends State @override void initState() { + _renderingDetails = _RenderingDetails(); + _renderingDetails.didSizeChange = false; + _isToggled = false; _initializeDefaultValues(); // Create the series renderer while initial rendering // _createAndUpdateSeriesRenderer(); @@ -665,7 +578,7 @@ class SfCircularChartState extends State @override void didChangeDependencies() { - _chartTheme = SfChartTheme.of(context); + _renderingDetails.chartTheme = SfChartTheme.of(context); super.didChangeDependencies(); } @@ -688,19 +601,19 @@ class SfCircularChartState extends State _createAndUpdateSeriesRenderer(oldWidget); _needsRepaintCircularChart(_chartSeries.visibleSeriesRenderers, - []..add(_prevSeriesRenderer)); + [_prevSeriesRenderer]); _needExplodeAll = widget.series.isNotEmpty && - widget.series[0].explodeAll && - widget.series[0].explode && - oldWidget.series[0].explodeAll != widget.series[0].explodeAll; - _isLegendToggled = false; - _widgetNeedUpdate = true; - if (_legendWidgetContext.isNotEmpty) { - _legendWidgetContext.clear(); + (widget.series[0].explodeAll && + widget.series[0].explode && + oldWidget.series[0].explodeAll != widget.series[0].explodeAll); + _renderingDetails.isLegendToggled = false; + _renderingDetails.widgetNeedUpdate = true; + if (_renderingDetails.legendWidgetContext.isNotEmpty) { + _renderingDetails.legendWidgetContext.clear(); } - if (_tooltipBehaviorRenderer._chartTooltipState != null) { - _tooltipBehaviorRenderer._show = false; + if (_renderingDetails.tooltipBehaviorRenderer._chartTooltipState != null) { + _renderingDetails.tooltipBehaviorRenderer._show = false; } super.didUpdateWidget(oldWidget); } @@ -718,23 +631,22 @@ class SfCircularChartState extends State @override Widget build(BuildContext context) { - _initialRender = (_widgetNeedUpdate && !_isLegendToggled) + _renderingDetails.initialRender = (_renderingDetails.widgetNeedUpdate && + !_renderingDetails.isLegendToggled) ? _needExplodeAll - : (_initialRender == null); - _prevSize = _prevSize ?? MediaQuery.of(context).size; - _didSizeChange = _prevSize != MediaQuery.of(context).size; - _prevSize = MediaQuery.of(context).size; - _oldDeviceOrientation = _oldDeviceOrientation == null - ? MediaQuery.of(context).orientation - : _deviceOrientation; - _deviceOrientation = MediaQuery.of(context).orientation; + : (_renderingDetails.initialRender == null); + _renderingDetails.oldDeviceOrientation = + _renderingDetails.oldDeviceOrientation == null + ? MediaQuery.of(context).orientation + : _renderingDetails.deviceOrientation; + _renderingDetails.deviceOrientation = MediaQuery.of(context).orientation; return RepaintBoundary( child: _ChartContainer( child: GestureDetector( child: Container( decoration: BoxDecoration( - color: - widget.backgroundColor ?? _chartTheme.plotAreaBackgroundColor, + color: widget.backgroundColor ?? + _renderingDetails.chartTheme.plotAreaBackgroundColor, image: widget.backgroundImage != null ? DecorationImage( image: widget.backgroundImage!, fit: BoxFit.fill) @@ -762,7 +674,8 @@ class SfCircularChartState extends State @override void dispose() { - _disposeAnimationController(_animationController, _repaintChartElements); + _disposeAnimationController( + _renderingDetails.animationController, _repaintChartElements); super.dispose(); } @@ -836,30 +749,25 @@ class SfCircularChartState extends State void _initializeDefaultValues() { _chartSeries = _CircularSeries(this); _circularArea = _CircularArea(chartState: this); - _chartLegend = _ChartLegend(this); + _renderingDetails.chartLegend = _ChartLegend(this); _needToMoveFromCenter = true; - _animateCompleted = false; - _controllerList = []; - _annotationController = AnimationController(vsync: this); - _seriesRepaintNotifier = ValueNotifier(0); - _legendWidgetContext = <_MeasureWidgetContext>[]; - _explodedPoints = []; - _templates = <_ChartTemplateInfo>[]; - _legendToggleStates = <_LegendRenderContext>[]; - _selectedDataPoints = >[]; - _unselectedDataPoints = >[]; - _selectedRegions = <_Region>[]; - _unselectedRegions = <_Region>[]; - _legendToggleTemplateStates = <_MeasureWidgetContext>[]; - _dataLabelTemplateRegions = []; + _renderingDetails.animateCompleted = false; + _renderingDetails.annotationController = AnimationController(vsync: this); + _renderingDetails.seriesRepaintNotifier = ValueNotifier(0); + _renderingDetails.legendWidgetContext = <_MeasureWidgetContext>[]; + _renderingDetails.explodedPoints = []; + _renderingDetails.templates = <_ChartTemplateInfo>[]; + _renderingDetails.legendToggleStates = <_LegendRenderContext>[]; + _renderingDetails.legendToggleTemplateStates = <_MeasureWidgetContext>[]; + _renderingDetails.dataLabelTemplateRegions = []; _annotationRegions = []; - _widgetNeedUpdate = false; - _isLegendToggled = false; - _selectionData = []; - _animationController = AnimationController(vsync: this) + _renderingDetails.widgetNeedUpdate = false; + _renderingDetails.isLegendToggled = false; + _renderingDetails.selectionData = []; + _renderingDetails.animationController = AnimationController(vsync: this) ..addListener(_repaintChartElements); - _tooltipBehaviorRenderer = TooltipBehaviorRenderer(this); - _legendRenderer = LegendRenderer(widget.legend); + _renderingDetails.tooltipBehaviorRenderer = TooltipBehaviorRenderer(this); + _renderingDetails.legendRenderer = LegendRenderer(widget.legend); } // In this method, create and update the series renderer for each series // @@ -891,12 +799,13 @@ class SfCircularChartState extends State _prevSeriesRenderer!._series = oldWidget.series[0]; _prevSeriesRenderer!._oldRenderPoints = >[] //ignore: prefer_spread_collections - ..addAll(_prevSeriesRenderer!._renderPoints ?? []); + ..addAll( + _prevSeriesRenderer!._renderPoints ?? >[]); _prevSeriesRenderer!._renderPoints = >[]; } seriesRenderer._series = series; seriesRenderer._isSelectionEnable = - series.selectionBehavior.enable || series.selectionSettings.enable; + series.selectionBehavior.enable == true; seriesRenderer._chartState = this; _chartSeries.visibleSeriesRenderers ..clear() @@ -905,14 +814,14 @@ class SfCircularChartState extends State } void _repaintChartElements() { - _seriesRepaintNotifier.value++; + _renderingDetails.seriesRepaintNotifier.value++; } /// To redraw chart elements // ignore:unused_element void _redraw() { - _initialRender = false; - if (_isLegendToggled) { + _renderingDetails.initialRender = false; + if (_renderingDetails.isLegendToggled) { _isToggled = true; _prevSeriesRenderer = _chartSeries.visibleSeriesRenderers[0]; _oldPoints = List?>.filled( @@ -921,8 +830,8 @@ class SfCircularChartState extends State _oldPoints![i] = _prevSeriesRenderer!._renderPoints![i]; } } - if (_tooltipBehaviorRenderer._chartTooltipState != null) { - _tooltipBehaviorRenderer._show = false; + if (_renderingDetails.tooltipBehaviorRenderer._chartTooltipState != null) { + _renderingDetails.tooltipBehaviorRenderer._show = false; } setState(() { /// The chart will be rebuilding again, When we do the legend toggle, zoom/pan the chart. @@ -930,12 +839,14 @@ class SfCircularChartState extends State } void _refresh() { - final List<_MeasureWidgetContext> legendContexts = _legendWidgetContext; + final List<_MeasureWidgetContext> legendContexts = + _renderingDetails.legendWidgetContext; if (legendContexts.isNotEmpty) { + _MeasureWidgetContext templateContext; + RenderBox renderBox; for (int i = 0; i < legendContexts.length; i++) { - final _MeasureWidgetContext templateContext = legendContexts[i]; - final RenderBox renderBox = - templateContext.context!.findRenderObject() as RenderBox; + templateContext = legendContexts[i]; + renderBox = templateContext.context!.findRenderObject() as RenderBox; templateContext.size = renderBox.size; } setState(() { @@ -950,6 +861,11 @@ class SfCircularChartState extends State child: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { Widget element; + _renderingDetails.prevSize = + _renderingDetails.prevSize ?? constraints.biggest; + _renderingDetails.didSizeChange = + _renderingDetails.prevSize != constraints.biggest; + _renderingDetails.prevSize = constraints.biggest; _initialize(constraints); _chartSeries._findVisibleSeries(); if (_chartSeries.visibleSeriesRenderers.isNotEmpty) { @@ -957,11 +873,13 @@ class SfCircularChartState extends State ._processDataPoints(_chartSeries.visibleSeriesRenderers[0]); } final List legendTemplates = _bindLegendTemplateWidgets(this); - if (legendTemplates.isNotEmpty && _legendWidgetContext.isEmpty) { + if (legendTemplates.isNotEmpty && + _renderingDetails.legendWidgetContext.isEmpty) { element = Container(child: Stack(children: legendTemplates)); SchedulerBinding.instance!.addPostFrameCallback((_) => _refresh()); } else { - _chartLegend._calculateLegendBounds(_chartLegend.chartSize); + _renderingDetails.chartLegend + ._calculateLegendBounds(_renderingDetails.chartLegend.chartSize); element = _getElements(this, _CircularArea(chartState: this), constraints)!; } @@ -975,11 +893,12 @@ class SfCircularChartState extends State final num width = constraints.maxWidth; final num height = constraints.maxHeight; final EdgeInsets margin = widget.margin; - _legendRenderer._legendPosition = + _renderingDetails.legendRenderer._legendPosition = (widget.legend.position == LegendPosition.auto) ? (height > width ? LegendPosition.bottom : LegendPosition.right) : widget.legend.position; - _chartLegend.chartSize = Size(width - margin.left - margin.right, + _renderingDetails.chartLegend.chartSize = Size( + width - margin.left - margin.right, height - margin.top - margin.bottom); } } @@ -991,12 +910,26 @@ class _CircularArea extends StatelessWidget { //Here, we are using get keyword inorder to get the proper & updated instance of chart widget //When we initialize chart widget as a property to other classes like _ChartSeries, the chart widget is not updated properly and by using get we can rectify this. SfCircularChart get chart => chartState._chart; + + /// Specifies the chart state final SfCircularChartState chartState; + + /// Gets or sets the circular series CircularSeries? series; + + /// Holds the render box of the circular chart late RenderBox renderBox; + + /// Specifies the point region _Region? pointRegion; + + /// Holds the tap down details late TapDownDetails tapDownDetails; - late Offset? doubleTapPosition; + + /// Holds the double tap position + Offset? doubleTapPosition; + + /// Specifies whether the mouse is hovered final bool _enableMouseHover = kIsWeb; @override @@ -1010,7 +943,8 @@ class _CircularArea extends StatelessWidget { onHover: (PointerEvent event) => _enableMouseHover ? _onHover(event) : null, onExit: (PointerEvent event) { - chartState._tooltipBehaviorRenderer._isHovering = false; + chartState._renderingDetails.tooltipBehaviorRenderer._isHovering = + false; }, child: Listener( onPointerUp: (PointerUpEvent event) => _onTapUp(event), @@ -1021,7 +955,13 @@ class _CircularArea extends StatelessWidget { onLongPress: _onLongPress, onTapUp: (TapUpDetails details) { if (chart.onPointTapped != null && pointRegion != null) { - _calculatePointSeriesIndex(chart, pointRegion); + _calculatePointSeriesIndex( + chart, chartState, null, pointRegion); + } + if (chart.series[0].onPointTap != null && + pointRegion != null) { + _calculatePointSeriesIndex(chart, chartState, null, + pointRegion, ActivationMode.singleTap); } }, onDoubleTap: _onDoubleTap, @@ -1036,28 +976,21 @@ class _CircularArea extends StatelessWidget { }); } - /// Find point index for selection - void _calculatePointSeriesIndex(SfCircularChart chart, _Region? pointRegion) { - PointTapArgs pointTapArgs; - pointTapArgs = PointTapArgs( - pointRegion?.seriesIndex, - pointRegion?.pointIndex, - chartState._chartSeries.visibleSeriesRenderers[0]._dataPoints, - pointRegion?.pointIndex); - chart.onPointTapped!(pointTapArgs); - } - /// To perform the pointer down event void _onTapDown(PointerDownEvent event) { ChartTouchInteractionArgs touchArgs; - chartState._tooltipBehaviorRenderer._isHovering = false; - chartState._currentActive = null; - chartState._tapPosition = renderBox.globalToLocal(event.position); - pointRegion = _getCircularPointRegion(chart, chartState._tapPosition, + chartState._renderingDetails.tooltipBehaviorRenderer._isHovering = false; + chartState._renderingDetails.currentActive = null; + chartState._renderingDetails.tapPosition = + renderBox.globalToLocal(event.position); + pointRegion = _getCircularPointRegion( + chart, + chartState._renderingDetails.tapPosition, chartState._chartSeries.visibleSeriesRenderers[0]); - doubleTapPosition = chartState._tapPosition; - if (chartState._tapPosition != null && pointRegion != null) { - chartState._currentActive = _ChartInteraction( + doubleTapPosition = chartState._renderingDetails.tapPosition; + if (chartState._renderingDetails.tapPosition != null && + pointRegion != null) { + chartState._renderingDetails.currentActive = _ChartInteraction( pointRegion?.seriesIndex, pointRegion?.pointIndex, chartState._chartSeries @@ -1069,8 +1002,9 @@ class _CircularArea extends StatelessWidget { pointRegion); } else { //hides the tooltip if the point of interaction is outside circular region of the chart - chartState._tooltipBehaviorRenderer._show = false; - chartState._tooltipBehaviorRenderer._hideOnTimer(); + chartState._renderingDetails.tooltipBehaviorRenderer._show = false; + chartState._renderingDetails.tooltipBehaviorRenderer + ._hideTooltipTemplate(); } if (chart.onChartTouchInteractionDown != null) { touchArgs = ChartTouchInteractionArgs(); @@ -1093,7 +1027,11 @@ class _CircularArea extends StatelessWidget { /// To perform double tap touch interactions void _onDoubleTap() { if (doubleTapPosition != null && pointRegion != null) { - chartState._currentActive = _ChartInteraction( + if (chart.series[0].onPointDoubleTap != null && pointRegion != null) { + _calculatePointSeriesIndex( + chart, chartState, null, pointRegion, ActivationMode.doubleTap); + } + chartState._renderingDetails.currentActive = _ChartInteraction( pointRegion?.seriesIndex, pointRegion?.pointIndex, chartState._chartSeries @@ -1103,23 +1041,23 @@ class _CircularArea extends StatelessWidget { .visibleSeriesRenderers[pointRegion!.seriesIndex] ._renderPoints![pointRegion!.pointIndex], pointRegion); - if (chartState._currentActive != null) { - if (chartState._currentActive?.series.explodeGesture == + if (chartState._renderingDetails.currentActive != null) { + if (chartState._renderingDetails.currentActive?.series.explodeGesture == ActivationMode.doubleTap) { - chartState._chartSeries - ._seriesPointExplosion(chartState._currentActive?.region); + chartState._chartSeries._seriesPointExplosion( + chartState._renderingDetails.currentActive?.region); } } chartState._chartSeries ._seriesPointSelection(pointRegion, ActivationMode.doubleTap); if (chart.tooltipBehavior.enable && - chartState._animateCompleted && + chartState._renderingDetails.animateCompleted && chart.tooltipBehavior.activationMode == ActivationMode.doubleTap && doubleTapPosition != null) { if (chart.tooltipBehavior.builder != null) { _showCircularTooltipTemplate(); } else { - chartState._tooltipBehaviorRenderer.onDoubleTap( + chartState._renderingDetails.tooltipBehaviorRenderer.onDoubleTap( doubleTapPosition!.dx.toDouble(), doubleTapPosition!.dy.toDouble()); } @@ -1129,8 +1067,13 @@ class _CircularArea extends StatelessWidget { /// To perform long press touch interactions void _onLongPress() { - if (chartState._tapPosition != null && pointRegion != null) { - chartState._currentActive = _ChartInteraction( + if (chartState._renderingDetails.tapPosition != null && + pointRegion != null) { + if (chart.series[0].onPointLongPress != null && pointRegion != null) { + _calculatePointSeriesIndex( + chart, chartState, null, pointRegion, ActivationMode.longPress); + } + chartState._renderingDetails.currentActive = _ChartInteraction( pointRegion?.seriesIndex, pointRegion?.pointIndex, chartState._chartSeries @@ -1142,23 +1085,23 @@ class _CircularArea extends StatelessWidget { pointRegion); chartState._chartSeries ._seriesPointSelection(pointRegion, ActivationMode.longPress); - if (chartState._currentActive != null) { - if (chartState._currentActive?.series.explodeGesture == + if (chartState._renderingDetails.currentActive != null) { + if (chartState._renderingDetails.currentActive?.series.explodeGesture == ActivationMode.longPress) { - chartState._chartSeries - ._seriesPointExplosion(chartState._currentActive?.region); + chartState._chartSeries._seriesPointExplosion( + chartState._renderingDetails.currentActive?.region); } } if (chart.tooltipBehavior.enable && - chartState._animateCompleted && + chartState._renderingDetails.animateCompleted && chart.tooltipBehavior.activationMode == ActivationMode.longPress && - chartState._tapPosition != null) { + chartState._renderingDetails.tapPosition != null) { if (chart.tooltipBehavior.builder != null) { _showCircularTooltipTemplate(); } else { - chartState._tooltipBehaviorRenderer.onLongPress( - chartState._tapPosition!.dx.toDouble(), - chartState._tapPosition!.dy.toDouble()); + chartState._renderingDetails.tooltipBehaviorRenderer.onLongPress( + chartState._renderingDetails.tapPosition!.dx.toDouble(), + chartState._renderingDetails.tapPosition!.dy.toDouble()); } } } @@ -1166,38 +1109,39 @@ class _CircularArea extends StatelessWidget { /// To perform the pointer up event void _onTapUp(PointerUpEvent event) { - chartState._tooltipBehaviorRenderer._isHovering = false; + chartState._renderingDetails.tooltipBehaviorRenderer._isHovering = false; ChartTouchInteractionArgs touchArgs; final CircularSeriesRenderer seriesRenderer = chartState._chartSeries.visibleSeriesRenderers[0]; if (chart.onDataLabelTapped != null) { - _triggerCircularDataLabelEvent( - chart, seriesRenderer, chartState, chartState._tapPosition); + _triggerCircularDataLabelEvent(chart, seriesRenderer, chartState, + chartState._renderingDetails.tapPosition); } - if (chartState._tapPosition != null) { - if (chartState._currentActive != null && - chartState._currentActive!.series != null && - chartState._currentActive!.series.explodeGesture == + if (chartState._renderingDetails.tapPosition != null) { + if (chartState._renderingDetails.currentActive != null && + chartState._renderingDetails.currentActive!.series != null && + chartState._renderingDetails.currentActive!.series.explodeGesture == ActivationMode.singleTap) { - chartState._chartSeries - ._seriesPointExplosion(chartState._currentActive!.region); + chartState._chartSeries._seriesPointExplosion( + chartState._renderingDetails.currentActive!.region); } - if (chartState._tapPosition != null && - chartState._currentActive != null) { + if (chartState._renderingDetails.tapPosition != null && + chartState._renderingDetails.currentActive != null) { chartState._chartSeries._seriesPointSelection( - chartState._currentActive!.region, ActivationMode.singleTap); + chartState._renderingDetails.currentActive!.region, + ActivationMode.singleTap); } if (chart.tooltipBehavior.enable && - chartState._animateCompleted && + chartState._renderingDetails.animateCompleted && chart.tooltipBehavior.activationMode == ActivationMode.singleTap && - chartState._currentActive != null && - chartState._currentActive!.series != null) { + chartState._renderingDetails.currentActive != null && + chartState._renderingDetails.currentActive!.series != null) { if (chart.tooltipBehavior.builder != null) { _showCircularTooltipTemplate(); } else { final Offset position = renderBox.globalToLocal(event.position); - chartState._tooltipBehaviorRenderer + chartState._renderingDetails.tooltipBehaviorRenderer .onTouchUp(position.dx.toDouble(), position.dy.toDouble()); } } @@ -1207,23 +1151,27 @@ class _CircularArea extends StatelessWidget { chart.onChartTouchInteractionUp!(touchArgs); } } - chartState._tapPosition = null; + chartState._renderingDetails.tapPosition = null; } /// To perform hover event void _onHover(PointerEvent event) { - chartState._currentActive = null; - chartState._tapPosition = renderBox.globalToLocal(event.position); - pointRegion = _getCircularPointRegion(chart, chartState._tapPosition, + chartState._renderingDetails.currentActive = null; + chartState._renderingDetails.tapPosition = + renderBox.globalToLocal(event.position); + pointRegion = _getCircularPointRegion( + chart, + chartState._renderingDetails.tapPosition, chartState._chartSeries.visibleSeriesRenderers[0]); final CircularSeriesRenderer seriesRenderer = chartState._chartSeries.visibleSeriesRenderers[0]; if (chart.onDataLabelTapped != null) { - _triggerCircularDataLabelEvent( - chart, seriesRenderer, chartState, chartState._tapPosition); + _triggerCircularDataLabelEvent(chart, seriesRenderer, chartState, + chartState._renderingDetails.tapPosition); } - if (chartState._tapPosition != null && pointRegion != null) { - chartState._currentActive = _ChartInteraction( + if (chartState._renderingDetails.tapPosition != null && + pointRegion != null) { + chartState._renderingDetails.currentActive = _ChartInteraction( pointRegion!.seriesIndex, pointRegion!.pointIndex, chartState._chartSeries @@ -1235,42 +1183,45 @@ class _CircularArea extends StatelessWidget { pointRegion); } else { //hides the tooltip when the mouse is hovering out of the circular region - chartState._tooltipBehaviorRenderer._hide(); + chartState._renderingDetails.tooltipBehaviorRenderer._hide(); } - if (chartState._tapPosition != null) { + if (chartState._renderingDetails.tapPosition != null) { if (chart.tooltipBehavior.enable && - chartState._currentActive != null && - chartState._currentActive!.series != null) { - chartState._tooltipBehaviorRenderer._isHovering = true; - chartState._tooltipBehaviorRenderer._timer?.cancel(); + chartState._renderingDetails.currentActive != null && + chartState._renderingDetails.currentActive!.series != null) { + chartState._renderingDetails.tooltipBehaviorRenderer._isHovering = true; if (chart.tooltipBehavior.builder != null) { _showCircularTooltipTemplate(); } else { final Offset position = renderBox.globalToLocal(event.position); - chartState._tooltipBehaviorRenderer + chartState._renderingDetails.tooltipBehaviorRenderer .onEnter(position.dx.toDouble(), position.dy.toDouble()); } } else { - chartState._tooltipBehaviorRenderer._prevTooltipValue = null; - chartState._tooltipBehaviorRenderer._currentTooltipValue = null; + chartState._renderingDetails.tooltipBehaviorRenderer._prevTooltipValue = + null; + chartState._renderingDetails.tooltipBehaviorRenderer + ._currentTooltipValue = null; } } - chartState._tapPosition = null; + chartState._renderingDetails.tapPosition = null; } /// This method gets executed for showing tooltip when builder is provided in behavior ///the optional parameters will take values once thee public method gets called void _showCircularTooltipTemplate([int? seriesIndex, int? pointIndex]) { - final tooltipBehaviorRenderer = chartState._tooltipBehaviorRenderer; + final TooltipBehaviorRenderer tooltipBehaviorRenderer = + chartState._renderingDetails.tooltipBehaviorRenderer; if (!tooltipBehaviorRenderer._isHovering) { //assingning null for the previous and current tooltip values in case of touch interaction tooltipBehaviorRenderer._prevTooltipValue = null; tooltipBehaviorRenderer._currentTooltipValue = null; } final CircularSeries chartSeries = - chartState._currentActive?.series ?? chart.series[seriesIndex!]; + chartState._renderingDetails.currentActive?.series ?? + chart.series[seriesIndex!]; final ChartPoint point = pointIndex == null - ? chartState._currentActive?.point + ? chartState._renderingDetails.currentActive?.point : chartState ._chartSeries.visibleSeriesRenderers[0]._dataPoints[pointIndex]; if (point.isVisible) { @@ -1278,28 +1229,31 @@ class _CircularArea extends StatelessWidget { (point.innerRadius! + point.outerRadius!) / 2, point.center!); if (location != null && (chartSeries.enableTooltip)) { tooltipBehaviorRenderer._showLocation = location; - tooltipBehaviorRenderer._renderBox!.boundaryRect = - chartState._chartContainerRect; + tooltipBehaviorRenderer._chartTooltipState!.boundaryRect = + tooltipBehaviorRenderer._tooltipBounds = + chartState._renderingDetails.chartContainerRect; tooltipBehaviorRenderer._tooltipTemplate = chart.tooltipBehavior.builder!( - chartSeries.dataSource![ - pointIndex ?? chartState._currentActive!.pointIndex!], + chartSeries.dataSource![pointIndex ?? + chartState._renderingDetails.currentActive!.pointIndex!], point, chartSeries, - seriesIndex ?? chartState._currentActive!.seriesIndex!, - pointIndex ?? chartState._currentActive!.pointIndex!); + seriesIndex ?? + chartState._renderingDetails.currentActive!.seriesIndex!, + pointIndex ?? + chartState._renderingDetails.currentActive!.pointIndex!); if (tooltipBehaviorRenderer._isHovering) { // assigning values for previous and current tooltip values when the mouse is hovering tooltipBehaviorRenderer._prevTooltipValue = tooltipBehaviorRenderer._currentTooltipValue; tooltipBehaviorRenderer._currentTooltipValue = TooltipValue( - seriesIndex ?? chartState._currentActive!.seriesIndex!, - pointIndex ?? chartState._currentActive!.pointIndex!); + seriesIndex ?? + chartState._renderingDetails.currentActive!.seriesIndex!, + pointIndex ?? + chartState._renderingDetails.currentActive!.pointIndex!); } if (!tooltipBehaviorRenderer._isHovering) { - tooltipBehaviorRenderer._timer = Timer( - Duration(milliseconds: chart.tooltipBehavior.duration.toInt()), - tooltipBehaviorRenderer._hideTooltipTemplate); + tooltipBehaviorRenderer._hideTooltipTemplate(); } tooltipBehaviorRenderer._show = true; tooltipBehaviorRenderer._performTooltip(); @@ -1323,10 +1277,10 @@ class _CircularArea extends StatelessWidget { void _calculateContainerSize(BoxConstraints constraints) { final num width = constraints.maxWidth; final num height = constraints.maxHeight; - chartState._chartContainerRect = + chartState._renderingDetails.chartContainerRect = Rect.fromLTWH(0, 0, width.toDouble(), height.toDouble()); final EdgeInsets margin = chart.margin; - chartState._chartAreaRect = Rect.fromLTWH( + chartState._renderingDetails.chartAreaRect = Rect.fromLTWH( margin.left, margin.top, width - margin.right - margin.left, @@ -1341,7 +1295,10 @@ class _CircularArea extends StatelessWidget { _bindTooltipWidgets(constraints); chartState._circularArea = this; renderBox = context.findRenderObject() as RenderBox; - return Container(child: Stack(children: chartState._chartWidgets!)); + return Container( + child: Stack( + textDirection: TextDirection.ltr, + children: chartState._renderingDetails.chartWidgets!)); } /// To add chart templates @@ -1350,22 +1307,24 @@ class _CircularArea extends StatelessWidget { const num lineLength = 10; ChartPoint point; Widget labelWidget; - chartState._templates = <_ChartTemplateInfo>[]; - chartState._dataLabelTemplateRegions = []; + chartState._renderingDetails.templates = <_ChartTemplateInfo>[]; + chartState._renderingDetails.dataLabelTemplateRegions = []; chartState._annotationRegions = []; + CircularSeriesRenderer seriesRenderer; + CircularSeries series; + ConnectorLineSettings connector; + ChartAlignment labelAlign; + num connectorLength; for (int k = 0; k < chartState._chartSeries.visibleSeriesRenderers.length; k++) { - final CircularSeriesRenderer seriesRenderer = - chartState._chartSeries.visibleSeriesRenderers[k]; - final CircularSeries series = seriesRenderer._series; - final ConnectorLineSettings connector = - series.dataLabelSettings.connectorLineSettings; + seriesRenderer = chartState._chartSeries.visibleSeriesRenderers[k]; + series = seriesRenderer._series; + connector = series.dataLabelSettings.connectorLineSettings; if (series.dataLabelSettings.isVisible && series.dataLabelSettings.builder != null) { for (int i = 0; i < seriesRenderer._renderPoints!.length; i++) { point = seriesRenderer._renderPoints![i]; - ChartAlignment labelAlign; if (point.isVisible) { labelWidget = series.dataLabelSettings.builder!( series.dataSource![i], point, series, i, k); @@ -1376,7 +1335,7 @@ class _CircularArea extends StatelessWidget { labelLocation = Offset(labelLocation.dx, labelLocation.dy); labelAlign = ChartAlignment.center; } else { - final num connectorLength = _percentToValue( + connectorLength = _percentToValue( connector.length ?? '10%', point.outerRadius!)!; labelLocation = _degreeToPoint(point.midAngle!, point.outerRadius! + connectorLength, point.center!); @@ -1389,13 +1348,13 @@ class _CircularArea extends StatelessWidget { ? ChartAlignment.far : ChartAlignment.near; } - chartState._templates.add(_ChartTemplateInfo( + chartState._renderingDetails.templates.add(_ChartTemplateInfo( key: GlobalKey(), templateType: 'DataLabel', pointIndex: i, seriesIndex: k, needMeasure: true, - clipRect: chartState._chartAreaRect, + clipRect: chartState._renderingDetails.chartAreaRect, animationDuration: 500, widget: labelWidget, horizontalAlignment: labelAlign, @@ -1405,28 +1364,38 @@ class _CircularArea extends StatelessWidget { } } } + + _setTemplateInfo(); + } + + /// Method to set the tempalte info + void _setTemplateInfo() { + CircularChartAnnotation annotation; + double radius, annotationHeight, annotationWidth; + _ChartTemplateInfo templateInfo; + Offset point; if (chart.annotations != null && chart.annotations!.isNotEmpty) { for (int i = 0; i < chart.annotations!.length; i++) { - final CircularChartAnnotation annotation = chart.annotations![i]; + annotation = chart.annotations![i]; if (annotation.widget != null) { - final double radius = _percentToValue( + radius = _percentToValue( annotation.radius, chartState._chartSeries.size / 2)! .toDouble(); - final Offset point = _degreeToPoint( + point = _degreeToPoint( annotation.angle, radius, chartState._centerLocation); - final double annotationHeight = _percentToValue( + annotationHeight = _percentToValue( annotation.height, chartState._chartSeries.size / 2)! .toDouble(); - final double annotationWidth = _percentToValue( + annotationWidth = _percentToValue( annotation.width, chartState._chartSeries.size / 2)! .toDouble(); - final _ChartTemplateInfo templateInfo = _ChartTemplateInfo( + templateInfo = _ChartTemplateInfo( key: GlobalKey(), templateType: 'Annotation', needMeasure: true, horizontalAlignment: annotation.horizontalAlignment, verticalAlignment: annotation.verticalAlignment, - clipRect: chartState._chartContainerRect, + clipRect: chartState._renderingDetails.chartContainerRect, widget: annotationHeight > 0 && annotationWidth > 0 ? Container( height: annotationHeight, @@ -1436,7 +1405,7 @@ class _CircularArea extends StatelessWidget { pointIndex: i, animationDuration: 500, location: point); - chartState._templates.add(templateInfo); + chartState._renderingDetails.templates.add(templateInfo); } } } @@ -1444,57 +1413,65 @@ class _CircularArea extends StatelessWidget { /// To render chart templates void _renderTemplates() { - if (chartState._templates.isNotEmpty) { - for (int i = 0; i < chartState._templates.length; i++) { - chartState._templates[i].animationDuration = !chartState._initialRender! - ? 0 - : chartState._templates[i].animationDuration; + if (chartState._renderingDetails.templates.isNotEmpty) { + for (int i = 0; i < chartState._renderingDetails.templates.length; i++) { + chartState._renderingDetails.templates[i].animationDuration = + !chartState._renderingDetails.initialRender! + ? 0 + : chartState._renderingDetails.templates[i].animationDuration; } - chartState._chartTemplate = _ChartTemplate( - templates: chartState._templates, - render: chartState._animateCompleted, + chartState._renderingDetails.chartTemplate = _ChartTemplate( + templates: chartState._renderingDetails.templates, + render: chartState._renderingDetails.animateCompleted, chartState: chartState); - chartState._chartWidgets!.add(chartState._chartTemplate!); + chartState._renderingDetails.chartWidgets! + .add(chartState._renderingDetails.chartTemplate!); } } /// To add tooltip widgets to chart void _bindTooltipWidgets(BoxConstraints constraints) { chart.tooltipBehavior._chartState = chartState; - final SfChartThemeData _chartTheme = chartState._chartTheme; + final SfChartThemeData _chartTheme = + chartState._renderingDetails.chartTheme; if (chart.tooltipBehavior.enable) { - final tooltip = chart.tooltipBehavior; - chartState._tooltipBehaviorRenderer._prevTooltipValue = - chartState._tooltipBehaviorRenderer._currentTooltipValue = null; - chartState._tooltipBehaviorRenderer._chartTooltip = SfTooltip( - color: tooltip.color ?? _chartTheme.tooltipColor, - key: GlobalKey(), - textStyle: tooltip.textStyle, - animationDuration: tooltip.animationDuration, - enable: tooltip.enable, - opacity: tooltip.opacity, - borderColor: tooltip.borderColor, - borderWidth: tooltip.borderWidth, - duration: tooltip.duration, - shouldAlwaysShow: tooltip.shouldAlwaysShow, - elevation: tooltip.elevation, - canShowMarker: tooltip.canShowMarker, - textAlignment: tooltip.textAlignment, - decimalPlaces: tooltip.decimalPlaces, - labelColor: tooltip.textStyle.color ?? _chartTheme.tooltipLabelColor, - header: tooltip.header, - format: tooltip.format, - builder: tooltip.builder, - shadowColor: tooltip.shadowColor, - onTooltipRender: chart.onTooltipRender != null - ? chartState._tooltipBehaviorRenderer._tooltipRenderingEvent - : null); + final TooltipBehavior tooltip = chart.tooltipBehavior; + chartState._renderingDetails.tooltipBehaviorRenderer._prevTooltipValue = + chartState._renderingDetails.tooltipBehaviorRenderer + ._currentTooltipValue = null; + chartState._renderingDetails.tooltipBehaviorRenderer._chartTooltip = + SfTooltip( + color: tooltip.color ?? _chartTheme.tooltipColor, + key: GlobalKey(), + textStyle: tooltip.textStyle, + animationDuration: tooltip.animationDuration, + animationCurve: + const Interval(0.1, 0.8, curve: Curves.easeOutBack), + enable: tooltip.enable, + opacity: tooltip.opacity, + borderColor: tooltip.borderColor, + borderWidth: tooltip.borderWidth, + duration: tooltip.duration.toInt(), + shouldAlwaysShow: tooltip.shouldAlwaysShow, + elevation: tooltip.elevation, + canShowMarker: tooltip.canShowMarker, + textAlignment: tooltip.textAlignment, + decimalPlaces: tooltip.decimalPlaces, + labelColor: + tooltip.textStyle.color ?? _chartTheme.tooltipLabelColor, + header: tooltip.header, + format: tooltip.format, + shadowColor: tooltip.shadowColor, + onTooltipRender: chart.onTooltipRender != null + ? chartState._renderingDetails.tooltipBehaviorRenderer + ._tooltipRenderingEvent + : null); final Widget uiWidget = IgnorePointer( ignoring: true, child: Stack(children: [ - chartState._tooltipBehaviorRenderer._chartTooltip! + chartState._renderingDetails.tooltipBehaviorRenderer._chartTooltip! ])); - chartState._chartWidgets!.add(uiWidget); + chartState._renderingDetails.chartWidgets!.add(uiWidget); } } @@ -1502,22 +1479,20 @@ class _CircularArea extends StatelessWidget { void _bindSeriesWidgets(BuildContext context) { late CustomPainter seriesPainter; Animation? seriesAnimation; - chartState._animateCompleted = false; - chartState._chartWidgets ??= []; + chartState._renderingDetails.animateCompleted = false; + chartState._renderingDetails.chartWidgets ??= []; CircularSeries series; CircularSeriesRenderer seriesRenderer; + dynamic selectionBehavior; + SelectionBehaviorRenderer selectionBehaviorRenderer; for (int i = 0; i < chartState._chartSeries.visibleSeriesRenderers.length; i++) { seriesRenderer = chartState._chartSeries.visibleSeriesRenderers[i]; series = seriesRenderer._series; series.selectionBehavior._chartState = chartState; - series.selectionSettings._chartState = chartState; - final dynamic selectionBehavior = seriesRenderer._selectionBehavior = - series.selectionBehavior.enable - ? series.selectionBehavior - : series.selectionSettings; - SelectionBehaviorRenderer selectionBehaviorRenderer; + selectionBehavior = + seriesRenderer._selectionBehavior = series.selectionBehavior; selectionBehaviorRenderer = seriesRenderer._selectionBehaviorRenderer = SelectionBehaviorRenderer(selectionBehavior, chart, chartState); selectionBehaviorRenderer._selectionRenderer ??= _SelectionRenderer(); @@ -1529,75 +1504,83 @@ class _CircularArea extends StatelessWidget { for (int index = 0; index < series.initialSelectedDataIndexes.length; index++) { - chartState._selectionData + chartState._renderingDetails.selectionData .add(series.initialSelectedDataIndexes[index]); } } - chartState._animateCompleted = false; + chartState._renderingDetails.animateCompleted = false; if (series.animationDuration > 0 && - !chartState._didSizeChange && - (chartState._oldDeviceOrientation == chartState._deviceOrientation) && - (chartState._initialRender! || - (chartState._widgetNeedUpdate && + !chartState._renderingDetails.didSizeChange && + (chartState._renderingDetails.oldDeviceOrientation == + chartState._renderingDetails.deviceOrientation) && + (chartState._renderingDetails.initialRender! || + (chartState._renderingDetails.widgetNeedUpdate && seriesRenderer._needsAnimation) || - chartState._isLegendToggled)) { - chartState._animationController.duration = + chartState._renderingDetails.isLegendToggled)) { + chartState._renderingDetails.animationController.duration = Duration(milliseconds: series.animationDuration.toInt()); seriesAnimation = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation( - parent: chartState._animationController, + parent: chartState._renderingDetails.animationController, curve: const Interval(0.1, 0.8, curve: Curves.linear), )..addStatusListener((AnimationStatus status) { if (status == AnimationStatus.completed) { - chartState._animateCompleted = true; + chartState._renderingDetails.animateCompleted = true; if (chartState._renderDataLabel != null) { chartState._renderDataLabel!.state.render(); } - if (chartState._chartTemplate != null) { - chartState._chartTemplate!.state.templateRender(); + if (chartState._renderingDetails.chartTemplate != null) { + chartState._renderingDetails.chartTemplate!.state + .templateRender(); } } })); - chartState._chartElementAnimation = + chartState._renderingDetails.chartElementAnimation = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation( - parent: chartState._animationController, + parent: chartState._renderingDetails.animationController, curve: const Interval(0.85, 1.0, curve: Curves.decelerate), )); - chartState._animationController.forward(from: 0.0); + chartState._renderingDetails.animationController.forward(from: 0.0); } else { - chartState._animateCompleted = true; + chartState._renderingDetails.animateCompleted = true; } - seriesRenderer._repaintNotifier = chartState._seriesRepaintNotifier; + seriesRenderer._repaintNotifier = + chartState._renderingDetails.seriesRepaintNotifier; if (seriesRenderer._seriesType == 'pie') { seriesPainter = _PieChartPainter( chartState: chartState, index: i, isRepaint: seriesRenderer._needsRepaint, - animationController: chartState._animationController, + animationController: + chartState._renderingDetails.animationController, seriesAnimation: seriesAnimation, - notifier: chartState._seriesRepaintNotifier); + notifier: chartState._renderingDetails.seriesRepaintNotifier); } else if (seriesRenderer._seriesType == 'doughnut') { seriesPainter = _DoughnutChartPainter( chartState: chartState, index: i, isRepaint: seriesRenderer._needsRepaint, - animationController: chartState._animationController, + animationController: + chartState._renderingDetails.animationController, seriesAnimation: seriesAnimation, - notifier: chartState._seriesRepaintNotifier); + notifier: chartState._renderingDetails.seriesRepaintNotifier); } else if (seriesRenderer._seriesType == 'radialbar') { seriesPainter = _RadialBarPainter( chartState: chartState, index: i, isRepaint: seriesRenderer._needsRepaint, - animationController: chartState._animationController, + animationController: + chartState._renderingDetails.animationController, seriesAnimation: seriesAnimation, - notifier: chartState._seriesRepaintNotifier); + notifier: chartState._renderingDetails.seriesRepaintNotifier); } - chartState._chartWidgets! + chartState._renderingDetails.chartWidgets! .add(RepaintBoundary(child: CustomPaint(painter: seriesPainter))); chartState._renderDataLabel = _CircularDataLabelRenderer( - circularChartState: chartState, show: chartState._animateCompleted); - chartState._chartWidgets!.add(chartState._renderDataLabel!); + circularChartState: chartState, + show: chartState._renderingDetails.animateCompleted); + chartState._renderingDetails.chartWidgets! + .add(chartState._renderDataLabel!); } } } diff --git a/packages/syncfusion_flutter_charts/lib/src/circular_chart/base/series_base.dart b/packages/syncfusion_flutter_charts/lib/src/circular_chart/base/series_base.dart index 362d4a838..b158e1eae 100644 --- a/packages/syncfusion_flutter_charts/lib/src/circular_chart/base/series_base.dart +++ b/packages/syncfusion_flutter_charts/lib/src/circular_chart/base/series_base.dart @@ -25,23 +25,24 @@ class _CircularSeries { /// To find the visible series void _findVisibleSeries() { CircularSeries series; + List>? _oldPoints; + CircularSeries? oldSeries; + int oldPointIndex = 0; + ChartPoint currentPoint; for (final CircularSeriesRenderer seriesRenderer in _chartState._chartSeries.visibleSeriesRenderers) { _setSeriesType(seriesRenderer); series = seriesRenderer._series = chart.series[0]; seriesRenderer._dataPoints = >[]; seriesRenderer._needsAnimation = false; - final List>? _oldPoints = - _chartState._prevSeriesRenderer?._oldRenderPoints; - final CircularSeries? oldSeries = - _chartState._prevSeriesRenderer?._series; - int oldPointIndex = 0; + _oldPoints = _chartState._prevSeriesRenderer?._oldRenderPoints; + oldSeries = _chartState._prevSeriesRenderer?._series; + oldPointIndex = 0; if (series.dataSource != null) { for (int pointIndex = 0; pointIndex < series.dataSource!.length; pointIndex++) { - final ChartPoint currentPoint = - _getCircularPoint(seriesRenderer, pointIndex); + currentPoint = _getCircularPoint(seriesRenderer, pointIndex); if (currentPoint.x != null) { seriesRenderer._dataPoints.add(currentPoint); if (!seriesRenderer._needsAnimation) { @@ -50,15 +51,12 @@ class _CircularSeries { (oldSeries.startAngle != series.startAngle) || (oldSeries.endAngle != series.endAngle); } - if (_oldPoints != null && - _oldPoints.isNotEmpty && - !seriesRenderer._needsAnimation && - oldPointIndex < _oldPoints.length) { - seriesRenderer._needsAnimation = - isDataUpdated(currentPoint, _oldPoints[oldPointIndex++]); - } else { - seriesRenderer._needsAnimation = true; - } + + seriesRenderer._needsAnimation = !(_oldPoints != null && + _oldPoints.isNotEmpty && + !seriesRenderer._needsAnimation && + oldPointIndex < _oldPoints.length) || + isDataUpdated(currentPoint, _oldPoints[oldPointIndex++]); } } } @@ -112,7 +110,8 @@ class _CircularSeries { ? firstPoint.sortValue .toLowerCase() .compareTo(secondPoint.sortValue.toLowerCase()) - : firstPoint.sortValue.compareTo(secondPoint.sortValue))); + : firstPoint.sortValue + .compareTo(secondPoint.sortValue))) as int; } else if (seriesRenderer._series.sortingOrder == SortingOrder.descending) { return (firstPoint.sortValue == null) @@ -123,7 +122,8 @@ class _CircularSeries { ? secondPoint.sortValue! .toLowerCase() .compareTo(firstPoint.sortValue!.toLowerCase()) - : secondPoint.sortValue!.compareTo(firstPoint.sortValue))); + : secondPoint.sortValue! + .compareTo(firstPoint.sortValue))) as int; } else { return 0; } @@ -151,16 +151,13 @@ class _CircularSeries { ? textMapping(i) ?? point.y.toString() : point.y.toString() : point.text; - isYText = point.text == point.y.toString() ? true : false; + isYText = point.text == point.y.toString(); if (point.isVisible) { - if (mode == CircularChartGroupMode.point && - groupValue != null && - i >= groupValue) { - sumOfGroup += point.y!.abs(); - } else if (mode == CircularChartGroupMode.value && - groupValue != null && - point.y! <= groupValue) { + if (groupValue != null && + ((mode == CircularChartGroupMode.point && i >= groupValue) || + (mode == CircularChartGroupMode.value && + point.y! <= groupValue))) { sumOfGroup += point.y!.abs(); } else { seriesRenderer._renderPoints!.add(point); @@ -183,6 +180,9 @@ class _CircularSeries { final EmptyPointSettings empty = currentSeries.emptyPointSettings; final List palette = chart.palette; int i = 0; + List<_MeasureWidgetContext> legendToggles; + _MeasureWidgetContext item; + _LegendRenderContext legendRenderContext; for (final ChartPoint point in seriesRenderer._renderPoints!) { // ignore: unnecessary_null_comparison point.fill = point.isEmpty && empty.color != null @@ -201,11 +201,11 @@ class _CircularSeries { point.strokeWidth == 0 ? Colors.transparent : point.strokeColor; if (chart.legend.legendItemBuilder != null) { - final List<_MeasureWidgetContext> legendToggles = - _chartState._legendToggleTemplateStates; + legendToggles = + _chartState._renderingDetails.legendToggleTemplateStates; if (legendToggles.isNotEmpty) { for (int j = 0; j < legendToggles.length; j++) { - final _MeasureWidgetContext item = legendToggles[j]; + item = legendToggles[j]; if (i == item.pointIndex) { point.isVisible = false; break; @@ -213,10 +213,12 @@ class _CircularSeries { } } } else { - if (_chartState._legendToggleStates.isNotEmpty) { - for (int j = 0; j < _chartState._legendToggleStates.length; j++) { - final _LegendRenderContext legendRenderContext = - _chartState._legendToggleStates[j]; + if (_chartState._renderingDetails.legendToggleStates.isNotEmpty) { + for (int j = 0; + j < _chartState._renderingDetails.legendToggleStates.length; + j++) { + legendRenderContext = + _chartState._renderingDetails.legendToggleStates[j]; if (i == legendRenderContext.seriesIndex) { point.isVisible = false; break; @@ -246,27 +248,24 @@ class _CircularSeries { currentSeries.pointRadiusMapper == null && (seriesRenderer._seriesType == 'pie' || seriesRenderer._seriesType == 'doughnut')) { - final Rect areaRect = _chartState._chartAreaRect; + final Rect areaRect = _chartState._renderingDetails.chartAreaRect; bool needExecute = true; - double radius = seriesRenderer._currentRadius.toDouble(); + double radius = + seriesRenderer._segmentRenderingValues['currentRadius']!.toDouble(); while (needExecute) { radius += 1; final Rect circularRect = _getArcPath( 0.0, radius, seriesRenderer._center!, - seriesRenderer._start, - seriesRenderer._end, - seriesRenderer._totalAngle, + seriesRenderer._segmentRenderingValues['start'], + seriesRenderer._segmentRenderingValues['end'], + seriesRenderer._segmentRenderingValues['totalAngle'], chart, true) .getBounds(); - if (circularRect.width > areaRect.width) { - needExecute = false; - seriesRenderer._rect = circularRect; - break; - } - if (circularRect.height > areaRect.height) { + if (circularRect.width > areaRect.width || + circularRect.height > areaRect.height) { needExecute = false; seriesRenderer._rect = circularRect; break; @@ -274,16 +273,17 @@ class _CircularSeries { } seriesRenderer._rect = _getArcPath( 0.0, - seriesRenderer._currentRadius, + seriesRenderer._segmentRenderingValues['currentRadius']!, seriesRenderer._center!, - seriesRenderer._start, - seriesRenderer._end, - seriesRenderer._totalAngle, + seriesRenderer._segmentRenderingValues['start'], + seriesRenderer._segmentRenderingValues['end'], + seriesRenderer._segmentRenderingValues['totalAngle'], chart, true) .getBounds(); for (final ChartPoint point in seriesRenderer._renderPoints!) { - point.outerRadius = seriesRenderer._currentRadius; + point.outerRadius = + seriesRenderer._segmentRenderingValues['currentRadius']; } } } @@ -292,23 +292,24 @@ class _CircularSeries { void _calculateStartAndEndAngle(CircularSeriesRenderer seriesRenderer) { int pointIndex = 0; num pointEndAngle; - num pointStartAngle = seriesRenderer._start; - final num innerRadius = seriesRenderer._currentInnerRadius; + num pointStartAngle = seriesRenderer._segmentRenderingValues['start']!; + final num innerRadius = + seriesRenderer._segmentRenderingValues['currentInnerRadius']!; for (final ChartPoint point in seriesRenderer._renderPoints!) { if (point.isVisible) { point.innerRadius = (seriesRenderer._seriesType == 'doughnut') ? innerRadius : 0.0; point.degree = (point.y!.abs() / - (seriesRenderer._sumOfPoints != 0 - ? seriesRenderer._sumOfPoints + (seriesRenderer._segmentRenderingValues['sumOfPoints']! != 0 + ? seriesRenderer._segmentRenderingValues['sumOfPoints']! : 1)) * - seriesRenderer._totalAngle; + seriesRenderer._segmentRenderingValues['totalAngle']!; pointEndAngle = pointStartAngle + point.degree!; point.startAngle = pointStartAngle; point.endAngle = pointEndAngle; point.midAngle = (pointStartAngle + pointEndAngle) / 2; - point.outerRadius = _calculatePointRadius( - point.radius, point, seriesRenderer._currentRadius); + point.outerRadius = _calculatePointRadius(point.radius, point, + seriesRenderer._segmentRenderingValues['currentRadius']!); point.center = _needExplode(pointIndex, currentSeries) ? _findExplodeCenter( point.midAngle!, seriesRenderer, point.outerRadius!) @@ -328,13 +329,26 @@ class _CircularSeries { bool isNeedExplode = false; final SfCircularChartState chartState = _chartState; if (series.explode) { - if (chartState._initialRender!) { + if (chartState._renderingDetails.initialRender!) { if (pointIndex == series.explodeIndex || series.explodeAll) { - chartState._explodedPoints.add(pointIndex); + chartState._renderingDetails.explodedPoints.add(pointIndex); isNeedExplode = true; } - } else if (!chartState._initialRender! || chartState._isLegendToggled) { - isNeedExplode = chartState._explodedPoints.contains(pointIndex); + } else if (!chartState._renderingDetails.initialRender!) { + if (!chartState._renderingDetails.explodedPoints.contains(pointIndex) && + !chartState._renderingDetails.isLegendToggled) { + if (pointIndex == series.explodeIndex || series.explodeAll) { + chartState._renderingDetails.explodedPoints.add(pointIndex); + isNeedExplode = true; + } else if (!series.explodeAll && + chartState._renderingDetails.explodedPoints.isNotEmpty && + pointIndex <= + chartState._renderingDetails.explodedPoints.length - 1) { + chartState._renderingDetails.explodedPoints.removeAt(pointIndex); + } + } + isNeedExplode = + chartState._renderingDetails.explodedPoints.contains(pointIndex); } } return isNeedExplode; @@ -342,58 +356,75 @@ class _CircularSeries { /// To find sum of points in the series void _findSumOfPoints(CircularSeriesRenderer seriesRenderer) { - seriesRenderer._sumOfPoints = 0; + seriesRenderer._segmentRenderingValues['sumOfPoints'] = 0; for (final ChartPoint point in seriesRenderer._renderPoints!) { if (point.isVisible) { - seriesRenderer._sumOfPoints += point.y!.abs(); + seriesRenderer._segmentRenderingValues['sumOfPoints'] = + seriesRenderer._segmentRenderingValues['sumOfPoints']! + + point.y!.abs(); } } } /// To calculate angle of series void _calculateAngle(CircularSeriesRenderer seriesRenderer) { - seriesRenderer._start = currentSeries.startAngle < 0 - ? currentSeries.startAngle < -360 - ? (currentSeries.startAngle % 360) + 360 - : currentSeries.startAngle + 360 - : currentSeries.startAngle; - seriesRenderer._end = currentSeries.endAngle < 0 + seriesRenderer._segmentRenderingValues['start'] = + currentSeries.startAngle < 0 + ? currentSeries.startAngle < -360 + ? (currentSeries.startAngle % 360) + 360 + : currentSeries.startAngle + 360 + : currentSeries.startAngle; + seriesRenderer._segmentRenderingValues['end'] = currentSeries.endAngle < 0 ? currentSeries.endAngle < -360 ? (currentSeries.endAngle % 360) + 360 : currentSeries.endAngle + 360 : currentSeries.endAngle; - seriesRenderer._start = seriesRenderer._start > 360 - ? seriesRenderer._start % 360 - : seriesRenderer._start; - seriesRenderer._end = seriesRenderer._end > 360 - ? seriesRenderer._end % 360 - : seriesRenderer._end; - seriesRenderer._start -= 90; - seriesRenderer._end -= 90; - seriesRenderer._end = seriesRenderer._start == seriesRenderer._end - ? seriesRenderer._start + 360 - : seriesRenderer._end; - seriesRenderer._totalAngle = seriesRenderer._start > seriesRenderer._end - ? (seriesRenderer._start - 360).abs() + seriesRenderer._end - : (seriesRenderer._start - seriesRenderer._end).abs(); + seriesRenderer._segmentRenderingValues['start'] = + seriesRenderer._segmentRenderingValues['start']! > 360 == true + ? seriesRenderer._segmentRenderingValues['start']! % 360 + : seriesRenderer._segmentRenderingValues['start']!; + seriesRenderer._segmentRenderingValues['end'] = + seriesRenderer._segmentRenderingValues['end']! > 360 == true + ? seriesRenderer._segmentRenderingValues['end']! % 360 + : seriesRenderer._segmentRenderingValues['end']!; + seriesRenderer._segmentRenderingValues['start'] = + seriesRenderer._segmentRenderingValues['start']! - 90; + seriesRenderer._segmentRenderingValues['end'] = + seriesRenderer._segmentRenderingValues['end']! - 90; + seriesRenderer._segmentRenderingValues['end'] = + seriesRenderer._segmentRenderingValues['start']! == + seriesRenderer._segmentRenderingValues['end']! + ? seriesRenderer._segmentRenderingValues['start']! + 360 + : seriesRenderer._segmentRenderingValues['end']!; + seriesRenderer._segmentRenderingValues['totalAngle'] = + seriesRenderer._segmentRenderingValues['start']! > + seriesRenderer._segmentRenderingValues['end']! == + true + ? (seriesRenderer._segmentRenderingValues['start']! - 360).abs() + + seriesRenderer._segmentRenderingValues['end']! + : (seriesRenderer._segmentRenderingValues['start']! - + seriesRenderer._segmentRenderingValues['end']!) + .abs(); } /// To calculate radius of circular chart void _calculateRadius(CircularSeriesRenderer seriesRenderer) { final SfCircularChartState chartState = _chartState; - final Rect chartAreaRect = chartState._chartAreaRect; + final Rect chartAreaRect = chartState._renderingDetails.chartAreaRect; size = min(chartAreaRect.width, chartAreaRect.height); - seriesRenderer._currentRadius = + seriesRenderer._segmentRenderingValues['currentRadius'] = _percentToValue(currentSeries.radius, size / 2)!.toDouble(); - seriesRenderer._currentInnerRadius = _percentToValue( - currentSeries.innerRadius, seriesRenderer._currentRadius)!; + seriesRenderer._segmentRenderingValues['currentInnerRadius'] = + _percentToValue(currentSeries.innerRadius, + seriesRenderer._segmentRenderingValues['currentRadius']!)!; } /// To calculate center location of chart void _calculateOrigin(CircularSeriesRenderer seriesRenderer) { final SfCircularChartState chartState = _chartState; - final Rect chartAreaRect = chartState._chartAreaRect; - final Rect chartContainerRect = chartState._chartContainerRect; + final Rect chartAreaRect = chartState._renderingDetails.chartAreaRect; + final Rect chartContainerRect = + chartState._renderingDetails.chartContainerRect; seriesRenderer._center = Offset( _percentToValue(chart.centerX, chartAreaRect.width)!.toDouble(), _percentToValue(chart.centerY, chartAreaRect.height)!.toDouble()); @@ -416,9 +447,7 @@ class _CircularSeries { /// To calculate and return point radius num _calculatePointRadius( dynamic value, ChartPoint point, num radius) { - if (value != null) { - radius = value != null ? _percentToValue(value, size / 2)! : radius; - } + radius = value != null ? _percentToValue(value, size / 2)! : radius; return radius; } @@ -433,40 +462,84 @@ class _CircularSeries { _chartState._chartSeries.visibleSeriesRenderers[seriesIndex!]; int? currentSelectedIndex; if (seriesRenderer._isSelectionEnable && mode == chart.selectionGesture) { - if (chartState._selectionData.isNotEmpty) { + if (chartState._renderingDetails.selectionData.isNotEmpty) { if (!chart.enableMultiSelection && - chartState._selectionData.isNotEmpty && - chartState._selectionData.length > 1) { - if (chartState._selectionData.contains(pointIndex)) { + chartState._renderingDetails.selectionData.isNotEmpty && + chartState._renderingDetails.selectionData.length > 1) { + if (chartState._renderingDetails.selectionData.contains(pointIndex)) { currentSelectedIndex = pointIndex!; } - chartState._selectionData.clear(); + chartState._renderingDetails.selectionData.clear(); if (currentSelectedIndex != null) { - chartState._selectionData.add(pointIndex!); + chartState._renderingDetails.selectionData.add(pointIndex!); } } - for (int i = 0; i < chartState._selectionData.length; i++) { - final int selectionIndex = chartState._selectionData[i]; + + int selectionIndex; + for (int i = 0; + i < chartState._renderingDetails.selectionData.length; + i++) { + selectionIndex = chartState._renderingDetails.selectionData[i]; if (!chart.enableMultiSelection) { - isPointAlreadySelected = chartState._selectionData.length == 1 && - pointIndex == selectionIndex; - chartState._selectionData.removeAt(i); - chartState._seriesRepaintNotifier.value++; + isPointAlreadySelected = + chartState._renderingDetails.selectionData.length == 1 && + pointIndex == selectionIndex; + if (seriesRenderer._selectionBehavior.toggleSelection == true || + !isPointAlreadySelected) { + chartState._renderingDetails.selectionData.removeAt(i); + } + chartState._renderingDetails.seriesRepaintNotifier.value++; + if (chart.onSelectionChanged != null) { + chart.onSelectionChanged!(_getSelectionEventArgs( + seriesRenderer, seriesIndex, selectionIndex)); + } } else if (pointIndex == selectionIndex) { - chartState._selectionData.removeAt(i); + if (seriesRenderer._selectionBehavior.toggleSelection == true) { + chartState._renderingDetails.selectionData.removeAt(i); + } isPointAlreadySelected = true; - chartState._seriesRepaintNotifier.value++; + chartState._renderingDetails.seriesRepaintNotifier.value++; + if (chart.onSelectionChanged != null) { + chart.onSelectionChanged!(_getSelectionEventArgs( + seriesRenderer, seriesIndex, selectionIndex)); + } break; } } } if (!isPointAlreadySelected) { - chartState._selectionData.add(pointIndex!); - chartState._seriesRepaintNotifier.value++; + chartState._renderingDetails.selectionData.add(pointIndex!); + chartState._renderingDetails.seriesRepaintNotifier.value++; + if (chart.onSelectionChanged != null) { + chart.onSelectionChanged!( + _getSelectionEventArgs(seriesRenderer, seriesIndex, pointIndex)); + } } } } + /// To perform slection event and return Selection Args + SelectionArgs _getSelectionEventArgs( + dynamic seriesRenderer, int seriesIndex, int pointIndex) { + if (seriesRenderer != null) { + final SelectionBehavior selectionBehavior = + seriesRenderer._selectionBehavior; + final SelectionArgs args = SelectionArgs( + seriesRenderer: seriesRenderer, + seriesIndex: seriesIndex, + viewportPointIndex: pointIndex, + pointIndex: pointIndex); + args.selectedBorderColor = selectionBehavior.selectedBorderColor; + args.selectedBorderWidth = selectionBehavior.selectedBorderWidth; + args.selectedColor = selectionBehavior.selectedColor; + args.unselectedBorderColor = selectionBehavior.unselectedBorderColor; + args.unselectedBorderWidth = selectionBehavior.unselectedBorderWidth; + args.unselectedColor = selectionBehavior.unselectedColor; + seriesRenderer._selectionArgs = args; + } + return seriesRenderer._selectionArgs as SelectionArgs; + } + void _seriesPointExplosion(_Region? pointRegion) { bool existExplodedRegion = false; final SfCircularChartState chartState = _chartState; @@ -474,37 +547,44 @@ class _CircularSeries { ._chartSeries.visibleSeriesRenderers[pointRegion!.seriesIndex]; final ChartPoint point = seriesRenderer._renderPoints![pointRegion.pointIndex]; + int explodeIndex; if (seriesRenderer._series.explode) { - if (chartState._explodedPoints.isNotEmpty) { - if (chartState._explodedPoints.length == 1 && - chartState._explodedPoints.contains(pointRegion.pointIndex)) { + if (chartState._renderingDetails.explodedPoints.isNotEmpty) { + if (chartState._renderingDetails.explodedPoints.length == 1 && + chartState._renderingDetails.explodedPoints + .contains(pointRegion.pointIndex)) { existExplodedRegion = true; point.center = seriesRenderer._center; - final int index = - chartState._explodedPoints.indexOf(pointRegion.pointIndex); - chartState._explodedPoints.removeAt(index); - chartState._seriesRepaintNotifier.value++; + final int index = chartState._renderingDetails.explodedPoints + .indexOf(pointRegion.pointIndex); + chartState._renderingDetails.explodedPoints.removeAt(index); + chartState._renderingDetails.seriesRepaintNotifier.value++; chartState._renderDataLabel!.state.dataLabelRepaintNotifier.value++; } else if (seriesRenderer._series.explodeAll && - chartState._explodedPoints.length > 1 && - chartState._explodedPoints.contains(pointRegion.pointIndex)) { - for (int i = 0; i < chartState._explodedPoints.length; i++) { - final int explodeIndex = chartState._explodedPoints[i]; + chartState._renderingDetails.explodedPoints.length > 1 && + chartState._renderingDetails.explodedPoints + .contains(pointRegion.pointIndex)) { + for (int i = 0; + i < chartState._renderingDetails.explodedPoints.length; + i++) { + explodeIndex = chartState._renderingDetails.explodedPoints[i]; seriesRenderer._renderPoints![explodeIndex].center = seriesRenderer._center; - chartState._explodedPoints.removeAt(i); + chartState._renderingDetails.explodedPoints.removeAt(i); i--; } existExplodedRegion = true; - chartState._seriesRepaintNotifier.value++; + chartState._renderingDetails.seriesRepaintNotifier.value++; chartState._renderDataLabel!.state.dataLabelRepaintNotifier.value++; - } else if (chartState._explodedPoints.length == 1) { - for (int i = 0; i < chartState._explodedPoints.length; i++) { - final int explodeIndex = chartState._explodedPoints[i]; + } else if (chartState._renderingDetails.explodedPoints.length == 1) { + for (int i = 0; + i < chartState._renderingDetails.explodedPoints.length; + i++) { + explodeIndex = chartState._renderingDetails.explodedPoints[i]; seriesRenderer._renderPoints![explodeIndex].center = seriesRenderer._center; - chartState._explodedPoints.removeAt(i); - chartState._seriesRepaintNotifier.value++; + chartState._renderingDetails.explodedPoints.removeAt(i); + chartState._renderingDetails.seriesRepaintNotifier.value++; chartState._renderDataLabel!.state.dataLabelRepaintNotifier.value++; } } @@ -512,8 +592,8 @@ class _CircularSeries { if (!existExplodedRegion) { point.center = _findExplodeCenter( point.midAngle!, seriesRenderer, point.outerRadius!); - chartState._explodedPoints.add(pointRegion.pointIndex); - chartState._seriesRepaintNotifier.value++; + chartState._renderingDetails.explodedPoints.add(pointRegion.pointIndex); + chartState._renderingDetails.seriesRepaintNotifier.value++; chartState._renderDataLabel!.state.dataLabelRepaintNotifier.value++; } } diff --git a/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/circular_series.dart b/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/circular_series.dart index f7360ef88..0274c7c6b 100644 --- a/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/circular_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/circular_series.dart @@ -8,6 +8,7 @@ part of charts; /// /// Provide the options of stroke width, stroke color, opacity, and point color mapper to customize the appearance. /// +/// {@youtube 560 315 https://www.youtube.com/watch?v=VJxPp7-2nGk} class CircularSeries extends ChartSeries implements CircularChartEmptyPointBehavior { /// Creating an argument constructor of CircularSeries class. @@ -15,6 +16,9 @@ class CircularSeries extends ChartSeries {this.key, this.onCreateRenderer, this.onRendererCreated, + this.onPointTap, + this.onPointDoubleTap, + this.onPointLongPress, this.dataSource, this.xValueMapper, this.yValueMapper, @@ -45,8 +49,6 @@ class CircularSeries extends ChartSeries bool? enableSmartLabels, this.name, double? animationDuration, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, SortingOrder? sortingOrder, LegendIconType? legendIconType, @@ -63,10 +65,8 @@ class CircularSeries extends ChartSeries explodeGesture = explodeGesture ?? ActivationMode.singleTap, gap = gap ?? '1%', cornerStyle = cornerStyle ?? CornerStyle.bothFlat, - dataLabelSettings = dataLabelSettings ?? DataLabelSettings(), + dataLabelSettings = dataLabelSettings ?? const DataLabelSettings(), emptyPointSettings = emptyPointSettings ?? EmptyPointSettings(), - // ignore: deprecated_member_use_from_same_package - selectionSettings = selectionSettings ?? SelectionSettings(), selectionBehavior = selectionBehavior ?? SelectionBehavior(), borderColor = borderColor ?? Colors.transparent, borderWidth = borderWidth ?? 0.0, @@ -419,7 +419,7 @@ class CircularSeries extends ChartSeries ///Any shape in the LegendIconType can be applied to this property. ///By default, icon will be rendered based on the type of the series. /// - ///Defaults to `LegendIconType.seriesType`` + ///Defaults to `LegendIconType.seriesType` /// ///Also refer [LegendIconType] /// @@ -531,27 +531,6 @@ class CircularSeries extends ChartSeries @override final EmptyPointSettings emptyPointSettings; - ///Customizes the selection of series. - /// - ///```dart - ///Widget build(BuildContext context) { - /// return Container( - /// child: SfCircularChart( - /// series: >[ - /// PieSeries( - /// selectionSettings: SelectionSettings( - /// selectedColor: Colors.red, - /// unselectedColor: Colors.grey - /// ), - /// ), - /// ], - /// )); - ///} - ///``` - @override - // ignore: deprecated_member_use_from_same_package - final SelectionSettings selectionSettings; - ///Customizes the selection of series. /// ///```dart @@ -792,11 +771,11 @@ class CircularSeries extends ChartSeries /// by using this property. /// ///* If `PointRenderMode.segment` is specified, the data points are filled with solid colors from palette - /// or with the colors mentioned in [pointColorMapping] property. + /// or with the colors mentioned in `pointColorMapping` property. ///* If `PointRenderMode.gradient` is specified, a sweep gradient is formed with the solid colors and fills /// the data points. /// - ///`Note:` This property is applicable only if the [onCreateShader] or [pointShaderMapper] is null. + ///_Note:_ This property is applicable only if the `onCreateShader` or `pointShaderMapper` is null. /// ///Also refer [PointRenderMode]. /// @@ -962,6 +941,69 @@ class CircularSeries extends ChartSeries ///``` final ChartSeriesRendererFactory? onCreateRenderer; + ///Called when tapped on the chart data point. + /// + ///The user can fetch the series index, point index, viewport point index and + /// data of the tapped data point. + ///```dart + ///Widget build(BuildContext context) { + /// ChartSeriesController _chartSeriesController; + /// return Container( + /// child: SfCartesianChart( + /// series: >[ + /// LineSeries( + /// onPointTap: (ChartPointDetails details) { + /// print(details.pointIndex); + /// }, + /// ), + /// ], + /// )); + ///} + ///``` + final ChartPointInteractionCallback? onPointTap; + + ///Called when double tapped on the chart data point. + /// + ///The user can fetch the series index, point index, viewport point index and + /// data of the double-tapped data point. + ///```dart + ///Widget build(BuildContext context) { + /// ChartSeriesController _chartSeriesController; + /// return Container( + /// child: SfCartesianChart( + /// series: >[ + /// LineSeries( + /// onPointDoubleTap: (ChartPointDetails details) { + /// print(details.pointIndex); + /// }, + /// ), + /// ], + /// )); + ///} + ///``` + final ChartPointInteractionCallback? onPointDoubleTap; + + ///Called when long pressed on the chart data point. + /// + ///The user can fetch the series index, point index, viewport point index and + /// data of the long-pressed data point. + ///```dart + ///Widget build(BuildContext context) { + /// ChartSeriesController _chartSeriesController; + /// return Container( + /// child: SfCartesianChart( + /// series: >[ + /// LineSeries( + /// onPointLongPress: (ChartPointDetails details) { + /// print(details.pointIndex); + /// }, + /// ), + /// ], + /// )); + ///} + ///``` + final ChartPointInteractionCallback? onPointLongPress; + ///Triggers when the series renderer is created. /// @@ -1227,42 +1269,44 @@ class _ChartSeriesRender with _CircularChartSegment, _LabelSegment { /// Creates a series renderer for Circular series class CircularSeriesRenderer extends ChartSeriesRenderer { + /// Specifies the circular series late CircularSeries _series; + /// Specifies the series type late String _seriesType; + /// Specifies the list of data points late List> _dataPoints; + /// Specifies the list of rendering points List>? _renderPoints; + /// Specifies the list of old render points List>? _oldRenderPoints; - late num _sumOfPoints; - - late num _start; - - late num _end; - - late num _totalAngle; - - late num _currentRadius; - - late num _currentInnerRadius; + /// Specifies the map collection that holds all the values for rendering + /// the segment + final Map _segmentRenderingValues = {}; + /// Specifies the value of center Offset? _center; + /// Specifies the value of point region late List<_Region> _pointRegions; // ignore:unused_field late Rect _rect; // Path saved for radial bar series - List _renderPaths = []; + final List _renderPaths = []; - List _renderList = []; + /// Specifies the value of render list + final List _renderList = []; + /// Specifies the value of inner radial radius num? _innerRadialradius; + /// Specifies the value of selection args SelectionArgs? _selectionArgs; //Determines whether there is a need for animation @@ -1272,15 +1316,19 @@ class CircularSeriesRenderer extends ChartSeriesRenderer { ///in this before we must get the ChartSeriesController onRendererCreated event. CircularSeriesController? _controller; + /// Specifies the circular chart state late SfCircularChartState _chartState; /// Repaint notifier for series late ValueNotifier _repaintNotifier; + /// Specifies the data label setting renderer late DataLabelSettingsRenderer _dataLabelSettingsRenderer; + /// specifeis the selection behavior renderer late SelectionBehaviorRenderer _selectionBehaviorRenderer; + /// Specifies the selection behavior dynamic _selectionBehavior; // ignore: prefer_final_fields @@ -1293,18 +1341,14 @@ class CircularSeriesRenderer extends ChartSeriesRenderer { SfCircularChart chart, ChartPoint? point) { _StyleOptions? pointStyle; - final dynamic selection = _series.selectionBehavior.enable - ? _series.selectionBehavior - : _series.selectionSettings; - const int seriesIndex = 0; - if (selection.enable) { - if (_chartState._selectionData.isNotEmpty) { - for (int i = 0; i < _chartState._selectionData.length; i++) { - final int selectionIndex = _chartState._selectionData[i]; - if (chart.onSelectionChanged != null) { - chart.onSelectionChanged!(_getSelectionEventArgs( - seriesRenderer, seriesIndex, selectionIndex)); - } + final dynamic selection = _series.selectionBehavior; + if (selection.enable == true) { + if (_chartState._renderingDetails.selectionData.isNotEmpty) { + int selectionIndex; + for (int i = 0; + i < _chartState._renderingDetails.selectionData.length; + i++) { + selectionIndex = _chartState._renderingDetails.selectionData[i]; if (currentPointIndex == selectionIndex) { pointStyle = _StyleOptions( fill: seriesRenderer._selectionArgs != null @@ -1318,7 +1362,8 @@ class CircularSeriesRenderer extends ChartSeriesRenderer { : selection.selectedBorderColor, opacity: selection.selectedOpacity); break; - } else if (i == _chartState._selectionData.length - 1) { + } else if (i == + _chartState._renderingDetails.selectionData.length - 1) { pointStyle = _StyleOptions( fill: seriesRenderer._selectionArgs != null ? seriesRenderer._selectionArgs!.unselectedColor @@ -1337,27 +1382,6 @@ class CircularSeriesRenderer extends ChartSeriesRenderer { return pointStyle; } - /// To perform slection event and return Selection Args - SelectionArgs _getSelectionEventArgs( - dynamic seriesRenderer, int seriesIndex, int pointIndex) { - if (seriesRenderer != null) { - final dynamic selectionBehavior = seriesRenderer._selectionBehavior; - final SelectionArgs args = SelectionArgs( - seriesRenderer: seriesRenderer, - seriesIndex: seriesIndex, - viewportPointIndex: pointIndex, - pointIndex: pointIndex); - args.selectedBorderColor = selectionBehavior.selectedBorderColor; - args.selectedBorderWidth = selectionBehavior.selectedBorderWidth; - args.selectedColor = selectionBehavior.selectedColor; - args.unselectedBorderColor = selectionBehavior.unselectedBorderColor; - args.unselectedBorderWidth = selectionBehavior.unselectedBorderWidth; - args.unselectedColor = selectionBehavior.unselectedColor; - seriesRenderer._selectionArgs = args; - } - return seriesRenderer._selectionArgs; - } - /// To calculate point start and end angle num? _circularRenderPoint( SfCircularChart chart, @@ -1382,8 +1406,9 @@ class CircularSeriesRenderer extends ChartSeriesRenderer { /// below lines for dynamic dataSource changes if (isDynamicUpdate) { if (!oldPoint.isVisible && point.isVisible) { - final num val = point.startAngle == seriesRenderer._start - ? seriesRenderer._start + final num val = point.startAngle == + seriesRenderer._segmentRenderingValues['start']! + ? seriesRenderer._segmentRenderingValues['start']! : oldPointList![ _getVisiblePointIndex(oldPointList, 'before', pointIndex)!]! .endAngle!; @@ -1392,9 +1417,12 @@ class CircularSeriesRenderer extends ChartSeriesRenderer { pointEndAngle = val + (point.endAngle! - val) * animationDegreeValue; degree = pointEndAngle - pointStartAngle; } else if (oldPoint.isVisible && !point.isVisible) { - if (oldPoint.startAngle!.round() == seriesRenderer._start && - (oldPoint.endAngle!.round() == seriesRenderer._end || - oldPoint.endAngle!.round() == 360 + seriesRenderer._end)) { + if (oldPoint.startAngle!.round() == + seriesRenderer._segmentRenderingValues['start'] && + (oldPoint.endAngle!.round() == + seriesRenderer._segmentRenderingValues['end'] || + oldPoint.endAngle!.round() == + 360 + seriesRenderer._segmentRenderingValues['end']!)) { pointStartAngle = oldPoint.startAngle!; pointEndAngle = oldPoint.endAngle! - (oldPoint.endAngle! - oldPoint.startAngle!) * @@ -1404,8 +1432,9 @@ class CircularSeriesRenderer extends ChartSeriesRenderer { } else { pointStartAngle = oldPoint.startAngle! - (oldPoint.startAngle! - - (oldPoint.startAngle == seriesRenderer._start - ? seriesRenderer._start + (oldPoint.startAngle == + seriesRenderer._segmentRenderingValues['start']! + ? seriesRenderer._segmentRenderingValues['start']! : seriesRenderer ._renderPoints![_getVisiblePointIndex( seriesRenderer._renderPoints!, @@ -1415,9 +1444,13 @@ class CircularSeriesRenderer extends ChartSeriesRenderer { animationDegreeValue; pointEndAngle = oldPoint.endAngle! - (oldPoint.endAngle! - - ((oldPoint.endAngle!.round() == seriesRenderer._end || + ((oldPoint.endAngle!.round() == + seriesRenderer + ._segmentRenderingValues['end'] || oldPoint.endAngle!.round() == - 360 + seriesRenderer._end) + 360 + + seriesRenderer + ._segmentRenderingValues['end']!) ? oldPoint.endAngle! : seriesRenderer ._renderPoints![_getVisiblePointIndex( @@ -1445,7 +1478,7 @@ class CircularSeriesRenderer extends ChartSeriesRenderer { degree = animationDegreeValue * point.degree!; pointEndAngle = pointStartAngle! + degree; } - outerRadius = _chartState._initialRender! + outerRadius = _chartState._renderingDetails.initialRender! ? animationRadiusValue * outerRadius! : outerRadius; _calculatePath( @@ -1523,7 +1556,7 @@ class CircularSeriesRenderer extends ChartSeriesRenderer { pointEndAngle, degree, chart, - _chartState._animateCompleted); + _chartState._renderingDetails.animateCompleted); } _drawDataPoints(pointIndex, seriesIndex, chart, seriesRenderer, point, canvas, renderPath, degree, innerRadius); @@ -1591,22 +1624,27 @@ class CircularSeriesRenderer extends ChartSeriesRenderer { if (chart.series[0].pointRenderMode == PointRenderMode.gradient && point?.shader == null) { - final List colorsList = []; - final List stopsList = []; + final List colorsList = []; + final List stopsList = []; num initStops = 0; for (int i = 0; i < seriesRenderer._renderPoints!.length; i++) { point = seriesRenderer._renderPoints![i]; if (point.isVisible) { colorsList.add(point.fill); if (stopsList.isEmpty) { - initStops = (point.y! / _sumOfPoints) / 4; - stopsList.add(point.y! / _sumOfPoints - initStops); + initStops = + (point.y! / _segmentRenderingValues['sumOfPoints']!) / 4; + stopsList.add( + point.y! / _segmentRenderingValues['sumOfPoints']! - initStops); } else { if (stopsList.length == 1) { stopsList.add( - (point.y! / _sumOfPoints + stopsList.last) + initStops / 1.5); + (point.y! / _segmentRenderingValues['sumOfPoints']! + + stopsList.last) + + initStops / 1.5); } else { - stopsList.add(point.y! / _sumOfPoints + stopsList.last); + stopsList.add(point.y! / _segmentRenderingValues['sumOfPoints']! + + stopsList.last); } } } @@ -1630,7 +1668,7 @@ class CircularSeriesRenderer extends ChartSeriesRenderer { if (renderPath != null && degree! > 0) { if (seriesRenderer is DoughnutSeriesRenderer) { seriesRenderer._innerRadialradius = - !(point!.isVisible) || (seriesRenderer._innerRadialradius == null) + !point!.isVisible || (seriesRenderer._innerRadialradius == null) ? innerRadius : seriesRenderer._innerRadialradius; } @@ -1659,7 +1697,9 @@ class CircularSeriesRenderer extends ChartSeriesRenderer { _renderList.clear(); seriesRenderer._renderList.add(_StyleOptions( fill: fillColor!, - strokeWidth: _chartState._animateCompleted ? strokeWidth! : 0, + strokeWidth: _chartState._renderingDetails.animateCompleted + ? strokeWidth! + : 0, strokeColor: strokeColor!, opacity: opacity)); seriesRenderer._renderList.add(point._pathRect); @@ -1670,7 +1710,9 @@ class CircularSeriesRenderer extends ChartSeriesRenderer { canvas, _StyleOptions( fill: fillColor!, - strokeWidth: _chartState._animateCompleted ? strokeWidth! : 0, + strokeWidth: _chartState._renderingDetails.animateCompleted + ? strokeWidth! + : 0, strokeColor: strokeColor!, opacity: opacity), renderPath, @@ -1683,7 +1725,7 @@ class CircularSeriesRenderer extends ChartSeriesRenderer { if (strokeColor != null && strokeWidth != null && strokeWidth > 0 && - _chartState._animateCompleted) { + _chartState._renderingDetails.animateCompleted) { final Paint paint = Paint(); paint.color = strokeColor; paint.strokeWidth = strokeWidth.toDouble(); @@ -1696,10 +1738,6 @@ class CircularSeriesRenderer extends ChartSeriesRenderer { } } -/// Return the controller for circular series -typedef CircularSeriesRendererCreatedCallback = void Function( - CircularSeriesController controller); - ///We can redraw the series with updating or creating new points by using this controller.If we need to access the redrawing methods ///in this before we must get the ChartSeriesController onRendererCreated event. class CircularSeriesController { @@ -1804,8 +1842,9 @@ class CircularSeriesController { /// Add or update the data points on dynamic series update void _addOrUpdateDataPoints(List indexes, bool needUpdate) { + int dataIndex; for (int i = 0; i < indexes.length; i++) { - final int dataIndex = indexes[i]; + dataIndex = indexes[i]; _addOrUpdateDataPoint(dataIndex, needUpdate); } } @@ -1839,8 +1878,9 @@ class CircularSeriesController { ///Remove the redudant index from the list final List indexList = removedDataIndexes.toSet().toList(); indexList.sort((int b, int a) => a.compareTo(b)); + int dataIndex; for (int i = 0; i < indexList.length; i++) { - final int dataIndex = indexList[i]; + dataIndex = indexList[i]; _removeDataPoint(dataIndex); } } @@ -1865,10 +1905,10 @@ class CircularSeriesController { _chartState._renderDataLabel!.state.render(); } if (seriesRenderer._series.dataLabelSettings.isVisible && - _chartState._chartTemplate != null && + _chartState._renderingDetails.chartTemplate != null && // ignore: unnecessary_null_comparison - _chartState._chartTemplate!.state != null) { - _chartState._chartTemplate!.state.templateRender(); + _chartState._renderingDetails.chartTemplate!.state != null) { + _chartState._renderingDetails.chartTemplate!.state.templateRender(); } } } diff --git a/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/common.dart b/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/common.dart index 37ff7bb78..77be463df 100644 --- a/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/common.dart +++ b/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/common.dart @@ -10,7 +10,7 @@ class ChartPoint { ChartPoint([this.x, this.y, this.radius, this.pointColor, this.sortValue]); /// X value of chart point - dynamic? x; + dynamic x; /// Y value of chart point num? y; @@ -108,7 +108,7 @@ class ChartPoint { late int index; // Data type - dynamic? _data; + dynamic _data; /// PointShader Mapper ChartShaderMapper? _pointShaderMapper; @@ -252,6 +252,30 @@ class ConnectorLineSettings { ///} ///``` final ConnectorType type; + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is ConnectorLineSettings && + other.length == length && + other.width == width && + other.color == color && + other.type == type; + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode { + final List values = [length, width, color, type]; + return hashList(values); + } } class _ChartInteraction { @@ -269,13 +293,14 @@ class _ChartInteraction { ///Circular chart allows you to mark the specific area of interest in the chart area. /// You can add the custom widgets using this annotation feature, It has the properties for customizing the appearance. /// -/// You can able set the angle, alignment, height, and width of the inserted annotation. +/// The angle, orientation, height, and width of the inserted annotation can all be customized. /// -/// It provides options for an angle, height, width, vertical and horizontal alignment to customize the appearance. +/// It provides options for angle, height, width, vertical and horizontal alignment to customize the appearance. /// +@immutable class CircularChartAnnotation { /// Creating an argument constructor of CircularChartAnnotation class. - CircularChartAnnotation( + const CircularChartAnnotation( {int? angle, String? radius, this.widget, @@ -443,6 +468,39 @@ class CircularChartAnnotation { ///} ///``` final ChartAlignment verticalAlignment; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is CircularChartAnnotation && + other.angle == angle && + other.radius == radius && + other.height == height && + other.horizontalAlignment == horizontalAlignment && + other.verticalAlignment == verticalAlignment && + other.widget == widget && + other.width == width; + } + + @override + int get hashCode { + final List values = [ + angle, + radius, + height, + horizontalAlignment, + verticalAlignment, + widget, + width + ]; + return hashList(values); + } } ///To get circular series data label saturation color @@ -462,23 +520,22 @@ Color _getCircularDataLabelColor(ChartPoint currentPoint, switch (seriesType) { case 'Pie': case 'Doughnut': - if (currentPoint.renderPosition == ChartDataLabelPosition.inside && - !currentPoint.saturationRegionOutside) { - color = _getInnerColor(dataLabelSettingsRenderer._color, - currentPoint.fill, _chartState._chartTheme); - } else { - color = _getOuterColor( - dataLabelSettingsRenderer._color, - dataLabel.useSeriesColor - ? currentPoint.fill - : (_chartState._chart.backgroundColor ?? - _chartState._chartTheme.plotAreaBackgroundColor), - _chartState._chartTheme); - } + color = (currentPoint.renderPosition == ChartDataLabelPosition.inside && + !currentPoint.saturationRegionOutside) + ? _getInnerColor(dataLabelSettingsRenderer._color, currentPoint.fill, + _chartState._renderingDetails.chartTheme) + : _getOuterColor( + dataLabelSettingsRenderer._color, + dataLabel.useSeriesColor + ? currentPoint.fill + : (_chartState._chart.backgroundColor ?? + _chartState._renderingDetails.chartTheme + .plotAreaBackgroundColor), + _chartState._renderingDetails.chartTheme); break; case 'RadialBar': final RadialBarSeries radialBar = - seriesRenderer._series as RadialBarSeries; + seriesRenderer._series as RadialBarSeries; color = radialBar.trackColor; break; default: @@ -518,8 +575,9 @@ bool _checkIsAnyPointSelect(CircularSeriesRenderer seriesRenderer, bool isAnyPointSelected = false; final CircularSeries series = seriesRenderer._series; if (series.initialSelectedDataIndexes.isNotEmpty) { + int data; for (int i = 0; i < series.initialSelectedDataIndexes.length; i++) { - final int data = series.initialSelectedDataIndexes[i]; + data = series.initialSelectedDataIndexes[i]; for (int j = 0; j < seriesRenderer._renderPoints!.length; j++) { if (j == data) { isAnyPointSelected = true; diff --git a/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/data_label_renderer.dart b/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/data_label_renderer.dart index 50d9ecc27..5d2fba9e5 100644 --- a/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/data_label_renderer.dart +++ b/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/data_label_renderer.dart @@ -40,7 +40,9 @@ class _CircularDataLabelRendererState extends State<_CircularDataLabelRenderer> Widget build(BuildContext context) { widget.state = this; animationController.duration = Duration( - milliseconds: widget.circularChartState._initialRender! ? 500 : 0); + milliseconds: widget.circularChartState._renderingDetails.initialRender! + ? 500 + : 0); final Animation dataLabelAnimation = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation( parent: animationController, @@ -99,11 +101,11 @@ class _CircularDataLabelPainter extends CustomPainter { void paint(Canvas canvas, Size size) { final List visibleSeriesRenderers = circularChartState._chartSeries.visibleSeriesRenderers; + CircularSeriesRenderer seriesRenderer; for (int seriesIndex = 0; seriesIndex < visibleSeriesRenderers.length; seriesIndex++) { - final CircularSeriesRenderer seriesRenderer = - visibleSeriesRenderers[seriesIndex]; + seriesRenderer = visibleSeriesRenderers[seriesIndex]; // ignore: unnecessary_null_comparison if (seriesRenderer._series.dataLabelSettings != null && seriesRenderer._series.dataLabelSettings.isVisible) { @@ -139,6 +141,7 @@ void _renderCircularDataLabel( DataLabelRenderArgs dataLabelArgs; TextStyle dataLabelStyle; final List renderDataLabelRegions = []; + Size textSize; for (int pointIndex = 0; pointIndex < seriesRenderer._renderPoints!.length; pointIndex++) { @@ -166,7 +169,7 @@ void _renderCircularDataLabel( seriesRenderer._dataPoints[pointIndex].labelRenderEvent = true; } } - final Size textSize = measureText(label, dataLabelStyle); + textSize = measureText(label, dataLabelStyle); /// condition check for labels after event. if (label != '') { @@ -370,11 +373,8 @@ void _setLabelPosition( dataLabelStyle.debugLabel ?? dataLabel.textStyle.debugLabel, fontFamilyFallback: dataLabelStyle.fontFamilyFallback ?? dataLabel.textStyle.fontFamilyFallback); - if (isDataLabelCollide - ? (dataLabel.labelIntersectAction == LabelIntersectAction.hide) - ? false - : true - : true) { + if (!isDataLabelCollide || + (dataLabel.labelIntersectAction == LabelIntersectAction.hide)) { _drawLabel( point.labelRect, labelLocation, @@ -478,7 +478,7 @@ void _renderOutsideDataLabel( point.labelRect = rect!; labelLocation = Offset(rect.left + margin.left, rect.top + rect.height / 2 - textSize.height / 2); - final Rect containerRect = _chartState._chartAreaRect; + final Rect containerRect = _chartState._renderingDetails.chartAreaRect; if (seriesRenderer._series.dataLabelSettings.builder == null) { if (seriesRenderer._series.dataLabelSettings.labelIntersectAction == LabelIntersectAction.hide) { @@ -618,24 +618,27 @@ void _triggerCircularDataLabelEvent( CircularSeriesRenderer seriesRenderer, SfCircularChartState chartState, Offset? position) { - final int seriesIndex = 0; + const int seriesIndex = 0; final DataLabelSettings dataLabel = seriesRenderer._series.dataLabelSettings; + Offset labelLocation; + num connectorLength; + ChartPoint point; for (int index = 0; index < seriesRenderer._dataPoints.length; index++) { - final ChartPoint point = seriesRenderer._dataPoints[index]; + point = seriesRenderer._dataPoints[index]; if (dataLabel.isVisible && // ignore: unnecessary_null_comparison seriesRenderer._dataPoints[index].labelRect != null && position != null && seriesRenderer._dataPoints[index].labelRect.contains(position)) { if (dataLabel.labelPosition == ChartDataLabelPosition.inside) { - final Offset labelLocation = _degreeToPoint(point.midAngle!, + labelLocation = _degreeToPoint(point.midAngle!, (point.innerRadius! + point.outerRadius!) / 2, point.center!); position = Offset(labelLocation.dx, labelLocation.dy); } else { - final num connectorLength = _percentToValue( + connectorLength = _percentToValue( dataLabel.connectorLineSettings.length ?? '10%', point.outerRadius!)!; - final Offset labelLocation = _degreeToPoint(point.midAngle!, + labelLocation = _degreeToPoint(point.midAngle!, point.outerRadius! + connectorLength, point.center!); position = Offset(labelLocation.dx, labelLocation.dy); } @@ -670,7 +673,8 @@ Color _findthemecolor(SfCircularChartState _chartState, (dataLabel.useSeriesColor ? point.fill : (_chartState._chart.backgroundColor ?? - (_chartState._chartTheme.brightness == Brightness.light + (_chartState._renderingDetails.chartTheme.brightness == + Brightness.light ? Colors.white : Colors.black))); } diff --git a/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/doughnut_series.dart b/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/doughnut_series.dart index 328a1d258..3a0801805 100644 --- a/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/doughnut_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/doughnut_series.dart @@ -7,12 +7,17 @@ part of charts; /// /// Provide options for opacity, stroke width, stroke color, and point color mapper to customize the appearance. /// +/// {@youtube 560 315 https://www.youtube.com/watch?v=VJxPp7-2nGk} +@immutable class DoughnutSeries extends CircularSeries { /// Creating an argument constructor of DoughnutSeries class. DoughnutSeries( {ValueKey? key, ChartSeriesRendererFactory? onCreateRenderer, CircularSeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, List? dataSource, required ChartValueMapper xValueMapper, required ChartValueMapper yValueMapper, @@ -42,8 +47,6 @@ class DoughnutSeries extends CircularSeries { String? name, double? opacity, double? animationDuration, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, SortingOrder? sortingOrder, LegendIconType? legendIconType, @@ -53,6 +56,9 @@ class DoughnutSeries extends CircularSeries { key: key, onCreateRenderer: onCreateRenderer, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, dataSource: dataSource, xValueMapper: (int index) => xValueMapper(dataSource![index], index), yValueMapper: (int index) => yValueMapper(dataSource![index], index), @@ -92,7 +98,6 @@ class DoughnutSeries extends CircularSeries { dataLabelSettings: dataLabelSettings, enableTooltip: enableTooltip, name: name, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendIconType: legendIconType, sortingOrder: sortingOrder, @@ -113,6 +118,104 @@ class DoughnutSeries extends CircularSeries { } return DoughnutSeriesRenderer(); } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is DoughnutSeries && + other.animationDuration == animationDuration && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.cornerStyle == cornerStyle && + other.dataLabelMapper == dataLabelMapper && + other.dataLabelSettings == dataLabelSettings && + other.dataSource == dataSource && + other.emptyPointSettings == emptyPointSettings && + other.enableSmartLabels == enableSmartLabels && + other.enableTooltip == enableTooltip && + other.endAngle == endAngle && + other.explode == explode && + other.explodeAll == explodeAll && + other.explodeGesture == explodeGesture && + other.explodeIndex == explodeIndex && + other.explodeOffset == explodeOffset && + other.groupMode == groupMode && + other.groupTo == groupTo && + listEquals( + other.initialSelectedDataIndexes, initialSelectedDataIndexes) && + other.innerRadius == innerRadius && + other.legendIconType == legendIconType && + other.name == name && + other.onCreateRenderer == onCreateRenderer && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress && + other.opacity == opacity && + other.pointColorMapper == pointColorMapper && + other.pointRadiusMapper == pointRadiusMapper && + other.pointRenderMode == pointRenderMode && + other.pointShaderMapper == pointShaderMapper && + other.radius == radius && + other.selectionBehavior == selectionBehavior && + other.sortFieldValueMapper == sortFieldValueMapper && + other.sortingOrder == sortingOrder && + other.startAngle == startAngle && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper; + } + + @override + int get hashCode { + final List values = [ + animationDuration, + borderColor, + borderWidth, + cornerStyle, + dataLabelMapper, + dataLabelSettings, + dataSource, + emptyPointSettings, + enableSmartLabels, + enableTooltip, + endAngle, + explode, + explodeAll, + explodeGesture, + explodeIndex, + explodeOffset, + groupMode, + groupTo, + initialSelectedDataIndexes, + innerRadius, + legendIconType, + name, + onCreateRenderer, + onRendererCreated, + onPointTap, + onPointDoubleTap, + onPointLongPress, + opacity, + pointColorMapper, + pointRadiusMapper, + pointRenderMode, + pointShaderMapper, + radius, + selectionBehavior, + sortFieldValueMapper, + sortingOrder, + startAngle, + xValueMapper, + yValueMapper + ]; + return hashList(values); + } } class _DoughnutChartPainter extends CustomPainter { @@ -139,15 +242,17 @@ class _DoughnutChartPainter extends CustomPainter { num? pointStartAngle; seriesRenderer = chartState._chartSeries.visibleSeriesRenderers[index] as DoughnutSeriesRenderer; - pointStartAngle = seriesRenderer._start; - seriesRenderer._innerRadius = seriesRenderer._currentInnerRadius; - seriesRenderer._radius = seriesRenderer._currentRadius; + pointStartAngle = seriesRenderer._segmentRenderingValues['start']; + seriesRenderer._innerRadius = + seriesRenderer._segmentRenderingValues['currentInnerRadius']!; + seriesRenderer._radius = + seriesRenderer._segmentRenderingValues['currentRadius']!; ChartPoint point; seriesRenderer._pointRegions = <_Region>[]; ChartPoint? _oldPoint; final DoughnutSeriesRenderer? oldSeriesRenderer = - (chartState._widgetNeedUpdate && - !chartState._isLegendToggled && + (chartState._renderingDetails.widgetNeedUpdate && + !chartState._renderingDetails.isLegendToggled && chartState._prevSeriesRenderer?._seriesType == 'doughnut') ? chartState._prevSeriesRenderer as DoughnutSeriesRenderer : null; @@ -159,7 +264,7 @@ class _DoughnutChartPainter extends CustomPainter { oldSeriesRenderer._oldRenderPoints != null && (oldSeriesRenderer._oldRenderPoints!.length - 1 >= i)) ? oldSeriesRenderer._oldRenderPoints![i] - : ((chartState._isLegendToggled && + : ((chartState._renderingDetails.isLegendToggled && chartState._prevSeriesRenderer?._seriesType == 'doughnut') ? chartState._oldPoints![i] : null); @@ -198,7 +303,7 @@ class _DoughnutChartPainter extends CustomPainter { } if (seriesRenderer._renderList[0].strokeColor != null && seriesRenderer._renderList[0].strokeWidth != null && - seriesRenderer._renderList[0].strokeWidth > 0) { + seriesRenderer._renderList[0].strokeWidth > 0 == true) { final Paint paint = Paint(); paint.color = seriesRenderer._renderList[0].strokeColor; paint.strokeWidth = seriesRenderer._renderList[0].strokeWidth; diff --git a/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/pie_series.dart b/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/pie_series.dart index 39fc1abf2..7a6136a3e 100644 --- a/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/pie_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/pie_series.dart @@ -6,12 +6,16 @@ part of charts; /// /// It provides the options for color, opacity, border color, and border width to customize the appearance. /// +/// {@youtube 560 315 https://www.youtube.com/watch?v=VJxPp7-2nGk} class PieSeries extends CircularSeries { /// Creating an argument constructor of PieSeries class. PieSeries( {ValueKey? key, ChartSeriesRendererFactory? onCreateRenderer, CircularSeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, List? dataSource, ChartValueMapper? xValueMapper, ChartValueMapper? yValueMapper, @@ -40,8 +44,6 @@ class PieSeries extends CircularSeries { bool? enableSmartLabels, String? name, double? animationDuration, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, SortingOrder? sortingOrder, LegendIconType? legendIconType, @@ -50,6 +52,9 @@ class PieSeries extends CircularSeries { key: key, onCreateRenderer: onCreateRenderer, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, animationDuration: animationDuration, dataSource: dataSource, xValueMapper: (int index) => @@ -91,7 +96,6 @@ class PieSeries extends CircularSeries { dataLabelSettings: dataLabelSettings, enableTooltip: enableTooltip, name: name, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, legendIconType: legendIconType, sortingOrder: sortingOrder, @@ -109,6 +113,102 @@ class PieSeries extends CircularSeries { } return PieSeriesRenderer(); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is PieSeries && + other.animationDuration == animationDuration && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.dataLabelMapper == dataLabelMapper && + other.dataLabelSettings == dataLabelSettings && + other.dataSource == dataSource && + other.emptyPointSettings == emptyPointSettings && + other.enableSmartLabels == enableSmartLabels && + other.enableTooltip == enableTooltip && + other.endAngle == endAngle && + other.explode == explode && + other.explodeAll == explodeAll && + other.explodeGesture == explodeGesture && + other.explodeIndex == explodeIndex && + other.explodeOffset == explodeOffset && + other.groupMode == groupMode && + other.groupTo == groupTo && + listEquals( + other.initialSelectedDataIndexes, initialSelectedDataIndexes) && + other.legendIconType == legendIconType && + other.name == name && + other.onCreateRenderer == onCreateRenderer && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress && + other.opacity == opacity && + other.pointColorMapper == pointColorMapper && + other.pointRadiusMapper == pointRadiusMapper && + other.pointRenderMode == pointRenderMode && + other.pointShaderMapper == pointShaderMapper && + other.radius == radius && + other.selectionBehavior == selectionBehavior && + other.sortFieldValueMapper == sortFieldValueMapper && + other.sortingOrder == sortingOrder && + other.startAngle == startAngle && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper; + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode { + final List values = [ + animationDuration, + borderColor, + borderWidth, + dataLabelMapper, + dataLabelSettings, + dataSource, + emptyPointSettings, + enableSmartLabels, + enableTooltip, + endAngle, + explode, + explodeAll, + explodeGesture, + explodeIndex, + explodeOffset, + groupMode, + groupTo, + initialSelectedDataIndexes, + legendIconType, + name, + onCreateRenderer, + onRendererCreated, + onPointTap, + onPointDoubleTap, + onPointLongPress, + opacity, + pointColorMapper, + pointRadiusMapper, + pointRenderMode, + pointShaderMapper, + radius, + selectionBehavior, + sortFieldValueMapper, + sortingOrder, + startAngle, + xValueMapper, + yValueMapper + ]; + return hashList(values); + } } class _PieChartPainter extends CustomPainter { @@ -119,7 +219,7 @@ class _PieChartPainter extends CustomPainter { this.animationController, this.seriesAnimation, required ValueNotifier notifier, - }) : chart = chartState._chart, + }) : chart = chartState._chart, super(repaint: notifier); final SfCircularChartState chartState; final SfCircularChart chart; @@ -136,18 +236,18 @@ class _PieChartPainter extends CustomPainter { num? pointStartAngle; seriesRenderer = chartState._chartSeries.visibleSeriesRenderers[index] as PieSeriesRenderer; - pointStartAngle = seriesRenderer._start; + pointStartAngle = seriesRenderer._segmentRenderingValues['start']; seriesRenderer._pointRegions = <_Region>[]; bool isAnyPointNeedSelect = false; - if (chartState._initialRender!) { + if (chartState._renderingDetails.initialRender!) { isAnyPointNeedSelect = _checkIsAnyPointSelect(seriesRenderer, seriesRenderer._point, chart); } ChartPoint? _oldPoint; ChartPoint? point = seriesRenderer._point; final PieSeriesRenderer? oldSeriesRenderer = - (chartState._widgetNeedUpdate && - !chartState._isLegendToggled && + (chartState._renderingDetails.widgetNeedUpdate && + !chartState._renderingDetails.isLegendToggled && chartState._prevSeriesRenderer != null && chartState._prevSeriesRenderer!._seriesType == 'pie') ? chartState._prevSeriesRenderer! as PieSeriesRenderer @@ -160,7 +260,7 @@ class _PieChartPainter extends CustomPainter { oldSeriesRenderer._oldRenderPoints != null && (oldSeriesRenderer._oldRenderPoints!.length - 1 >= i)) ? oldSeriesRenderer._oldRenderPoints![i] - : ((chartState._isLegendToggled && + : ((chartState._renderingDetails.isLegendToggled && chartState._prevSeriesRenderer?._seriesType == 'pie') ? chartState._oldPoints![i] : null); @@ -199,7 +299,7 @@ class _PieChartPainter extends CustomPainter { } if (seriesRenderer._renderList[0].strokeColor != null && seriesRenderer._renderList[0].strokeWidth != null && - seriesRenderer._renderList[0].strokeWidth > 0) { + seriesRenderer._renderList[0].strokeWidth > 0 == true) { final Paint paint = Paint(); paint.color = seriesRenderer._renderList[0].strokeColor; paint.strokeWidth = seriesRenderer._renderList[0].strokeWidth; diff --git a/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/radial_bar_series.dart b/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/radial_bar_series.dart index 34ea935eb..48653c9cb 100644 --- a/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/radial_bar_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/circular_chart/renderer/radial_bar_series.dart @@ -7,12 +7,18 @@ part of charts; /// ///Provides options to customize the [maximumValue], [trackColor], [trackBorderColor], [trackBorderWidth], [trackOpacity] ///and [useSeriesColor] of the pie segments. +/// +/// {@youtube 560 315 https://www.youtube.com/watch?v=VJxPp7-2nGk} class RadialBarSeries extends CircularSeries { /// Creating an argument constructor of RadialBarSeries class. + /// RadialBarSeries( {ValueKey? key, ChartSeriesRendererFactory? onCreateRenderer, CircularSeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, List? dataSource, required ChartValueMapper xValueMapper, required ChartValueMapper yValueMapper, @@ -38,8 +44,6 @@ class RadialBarSeries extends CircularSeries { bool? enableSmartLabels, String? name, double? animationDuration, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, SortingOrder? sortingOrder, LegendIconType? legendIconType, @@ -49,6 +53,9 @@ class RadialBarSeries extends CircularSeries { key: key, onCreateRenderer: onCreateRenderer, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, dataSource: dataSource, animationDuration: animationDuration, xValueMapper: (int index) => xValueMapper(dataSource![index], index), @@ -78,7 +85,6 @@ class RadialBarSeries extends CircularSeries { enableTooltip: enableTooltip, dataLabelSettings: dataLabelSettings, name: name, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, sortingOrder: sortingOrder, legendIconType: legendIconType, @@ -218,9 +224,103 @@ class RadialBarSeries extends CircularSeries { } return RadialBarSeriesRenderer(); } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is RadialBarSeries && + other.animationDuration == animationDuration && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.cornerStyle == cornerStyle && + other.dataLabelMapper == dataLabelMapper && + other.dataLabelSettings == dataLabelSettings && + other.dataSource == dataSource && + other.enableSmartLabels == enableSmartLabels && + other.enableTooltip == enableTooltip && + other.gap == gap && + listEquals( + other.initialSelectedDataIndexes, initialSelectedDataIndexes) && + other.innerRadius == innerRadius && + other.legendIconType == legendIconType && + other.maximumValue == maximumValue && + other.name == name && + other.onCreateRenderer == onCreateRenderer && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress && + other.opacity == opacity && + other.pointColorMapper == pointColorMapper && + other.pointRadiusMapper == pointRadiusMapper && + other.pointShaderMapper == pointShaderMapper && + other.radius == radius && + other.selectionBehavior == selectionBehavior && + other.sortFieldValueMapper == sortFieldValueMapper && + other.sortingOrder == sortingOrder && + other.trackBorderColor == trackBorderColor && + other.trackBorderWidth == trackBorderWidth && + other.trackColor == trackColor && + other.trackOpacity == trackOpacity && + other.useSeriesColor == useSeriesColor && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper; + } + + @override + int get hashCode { + final List values = [ + animationDuration, + borderColor, + borderWidth, + cornerStyle, + dataLabelMapper, + dataLabelSettings, + dataSource, + enableSmartLabels, + enableTooltip, + gap, + initialSelectedDataIndexes, + innerRadius, + legendIconType, + maximumValue, + name, + onCreateRenderer, + onRendererCreated, + onPointTap, + onPointDoubleTap, + onPointLongPress, + opacity, + pointColorMapper, + pointRadiusMapper, + pointShaderMapper, + radius, + selectionBehavior, + sortFieldValueMapper, + sortingOrder, + trackBorderColor, + trackBorderWidth, + trackColor, + trackOpacity, + useSeriesColor, + xValueMapper, + yValueMapper + ]; + return hashList(values); + } } +/// Represents the pointer to draw radial bar series +/// class _RadialBarPainter extends CustomPainter { + /// Creates the instance for radial bar series + /// _RadialBarPainter({ required this.chartState, required this.index, @@ -235,78 +335,104 @@ class _RadialBarPainter extends CustomPainter { final AnimationController? animationController; final Animation? seriesAnimation; late RadialBarSeriesRenderer seriesRenderer; + late num _length, _sum, _ringSize, _animationValue, _actualStartAngle; + late int? _firstVisible; + late num? _gap; + late bool _isLegendToggle; + late RadialBarSeriesRenderer? _oldSeriesRenderer; + late double actualDegree; - /// To paint radial bar series - @override - void paint(Canvas canvas, Size size) { - num? pointStartAngle, pointEndAngle, degree; + /// Method to get length of the visible point + num _getLength(Canvas canvas) { num length = 0; seriesRenderer = chartState._chartSeries.visibleSeriesRenderers[index] as RadialBarSeriesRenderer; seriesRenderer._pointRegions = <_Region>[]; - final num sum = seriesRenderer._sumOfPoints, - actualStartAngle = seriesRenderer._start; - seriesRenderer._innerRadius = seriesRenderer._currentInnerRadius; - seriesRenderer._radius = seriesRenderer._currentRadius; - ChartPoint? _oldPoint; - late ChartPoint point; + seriesRenderer._innerRadius = + seriesRenderer._segmentRenderingValues['currentInnerRadius']!; + seriesRenderer._radius = + seriesRenderer._segmentRenderingValues['currentRadius']!; seriesRenderer._center = seriesRenderer._center!; - canvas.clipRect(chartState._chartAreaRect); + canvas.clipRect(chartState._renderingDetails.chartAreaRect); /// finding visible points count for (int i = 0; i < seriesRenderer._renderPoints!.length; i++) { length += seriesRenderer._renderPoints![i].isVisible ? 1 : 0; } + return length; + } + + /// Method to initialize the values to draw the radial bar series + /// + void _initializeValues(Canvas canvas) { + _length = _getLength(canvas); + _sum = seriesRenderer._segmentRenderingValues['sumOfPoints']!; + _actualStartAngle = seriesRenderer._segmentRenderingValues['start']!; /// finding first visible point - final int? firstVisible = - seriesRenderer._getFirstVisiblePointIndex(seriesRenderer); - final num ringSize = - (seriesRenderer._currentRadius - seriesRenderer._currentInnerRadius) - .abs() / - length; - final num? gap = _percentToValue( + _firstVisible = seriesRenderer._getFirstVisiblePointIndex(seriesRenderer); + _ringSize = (seriesRenderer._segmentRenderingValues['currentRadius']! - + seriesRenderer._segmentRenderingValues['currentInnerRadius']!) + .abs() / + _length; + _gap = _percentToValue( seriesRenderer._series.gap, - (seriesRenderer._currentRadius - seriesRenderer._currentInnerRadius) + (seriesRenderer._segmentRenderingValues['currentRadius']! - + seriesRenderer._segmentRenderingValues['currentInnerRadius']!) .abs()); - final num animationValue = seriesAnimation?.value ?? 1; - final bool isLegendToggle = chartState._isLegendToggled; - final RadialBarSeriesRenderer? oldSeriesRenderer = - (chartState._widgetNeedUpdate && - !chartState._isLegendToggled && - chartState._prevSeriesRenderer!._seriesType == 'radialbar') - ? chartState._prevSeriesRenderer! as RadialBarSeriesRenderer - : null; + _animationValue = seriesAnimation?.value ?? 1; + _isLegendToggle = chartState._renderingDetails.isLegendToggled; + _oldSeriesRenderer = (chartState._renderingDetails.widgetNeedUpdate && + !chartState._renderingDetails.isLegendToggled && + chartState._prevSeriesRenderer!._seriesType == 'radialbar') + ? chartState._prevSeriesRenderer! as RadialBarSeriesRenderer + : null; seriesRenderer._renderPaths.clear(); + } + + /// Method to paint radial bar series + /// + @override + void paint(Canvas canvas, Size size) { + num? pointStartAngle, pointEndAngle, degree; + ChartPoint? _oldPoint; + late ChartPoint point; + _initializeValues(canvas); + late RadialBarSeries series; + num? oldStart, oldEnd, oldRadius, oldInnerRadius; + late bool isDynamicUpdate, hide; + seriesRenderer._shadowPaths.clear(); + seriesRenderer._overFilledPaths.clear(); for (int i = 0; i < seriesRenderer._renderPoints!.length; i++) { + num? value; point = seriesRenderer._renderPoints![i]; - late RadialBarSeries series; if (seriesRenderer._series is RadialBarSeries) { - series = seriesRenderer._series as RadialBarSeries; + series = seriesRenderer._series as RadialBarSeries; } - _oldPoint = (oldSeriesRenderer != null && - oldSeriesRenderer._oldRenderPoints != null && - (oldSeriesRenderer._oldRenderPoints!.length - 1 >= i)) - ? oldSeriesRenderer._oldRenderPoints![i] - : (isLegendToggle ? chartState._oldPoints![i] : null); - pointStartAngle = actualStartAngle; - final bool isDynamicUpdate = _oldPoint != null; - bool hide = false; - num? oldStart, oldEnd, oldRadius, oldInnerRadius, value; + _oldPoint = (_oldSeriesRenderer != null && + _oldSeriesRenderer!._oldRenderPoints != null && + (_oldSeriesRenderer!._oldRenderPoints!.length - 1 >= i)) + ? _oldSeriesRenderer!._oldRenderPoints![i] + : (_isLegendToggle ? chartState._oldPoints![i] : null); + pointStartAngle = _actualStartAngle; + isDynamicUpdate = _oldPoint != null; + hide = false; + actualDegree = 0; if (!isDynamicUpdate || ((_oldPoint.isVisible && point.isVisible) || (_oldPoint.isVisible && !point.isVisible) || (!_oldPoint.isVisible && point.isVisible))) { if (point.isVisible) { hide = false; - if (isDynamicUpdate && !isLegendToggle) { + if (isDynamicUpdate && !_isLegendToggle) { value = (point.y! > _oldPoint.y!) - ? _oldPoint.y! + (point.y! - _oldPoint.y!) * animationValue - : _oldPoint.y! - (_oldPoint.y! - point.y!) * animationValue; + ? _oldPoint.y! + (point.y! - _oldPoint.y!) * _animationValue + : _oldPoint.y! - (_oldPoint.y! - point.y!) * _animationValue; } - degree = (value ?? point.y!).abs() / (series.maximumValue ?? sum); - degree = (degree > 1 ? 1 : degree) * (360 - 0.001); - degree = isDynamicUpdate ? degree : degree * animationValue; + degree = (value ?? point.y!).abs() / (series.maximumValue ?? _sum); + degree = degree * (360 - 0.001); + actualDegree = degree.toDouble(); + degree = isDynamicUpdate ? degree : degree * _animationValue; pointEndAngle = pointStartAngle + degree; point.midAngle = (pointStartAngle + pointEndAngle) / 2; point.startAngle = pointStartAngle; @@ -314,29 +440,30 @@ class _RadialBarPainter extends CustomPainter { point.center = seriesRenderer._center!; point.innerRadius = seriesRenderer._innerRadius = seriesRenderer._innerRadius + - ((i == firstVisible) ? 0 : ringSize); - point.outerRadius = seriesRenderer._radius = ringSize < gap! + ((i == _firstVisible) ? 0 : _ringSize); + point.outerRadius = seriesRenderer._radius = _ringSize < _gap! ? 0 - : seriesRenderer._innerRadius + ringSize - gap; - if (isLegendToggle) { + : seriesRenderer._innerRadius + _ringSize - _gap!; + if (_isLegendToggle) { seriesRenderer._calculateVisiblePointLegendToggleAnimation( - point, _oldPoint, i, animationValue); + point, _oldPoint, i, _animationValue); } } //animate on hiding - else if (isLegendToggle && !point.isVisible && _oldPoint!.isVisible) { + else if (_isLegendToggle && !point.isVisible && _oldPoint!.isVisible) { hide = true; oldEnd = _oldPoint.endAngle; oldStart = _oldPoint.startAngle; - degree = _oldPoint.y!.abs() / (series.maximumValue ?? sum); - degree = (degree > 1 ? 1 : degree) * (360 - 0.001); + degree = _oldPoint.y!.abs() / (series.maximumValue ?? _sum); + degree = degree * (360 - 0.001); + actualDegree = degree.toDouble(); oldInnerRadius = _oldPoint.innerRadius! + ((_oldPoint.outerRadius! + _oldPoint.innerRadius!) / 2 - _oldPoint.innerRadius!) * - animationValue; + _animationValue; oldRadius = _oldPoint.outerRadius! - (_oldPoint.outerRadius! - (_oldPoint.outerRadius! + _oldPoint.innerRadius!) / 2) * - animationValue; + _animationValue; } if (seriesRenderer is RadialBarSeriesRenderer) { seriesRenderer._drawDataPoint( @@ -354,10 +481,18 @@ class _RadialBarPainter extends CustomPainter { i, canvas, index, - chartState._chart); + chartState._chart, + actualDegree); } } } + + _renderRadialBarSeries(canvas); + } + + /// Method to render the radial bar series + /// + void _renderRadialBarSeries(Canvas canvas) { if (seriesRenderer._renderList.isNotEmpty) { Shader? _chartShader; if (chartState._chart.onCreateShader != null) { @@ -366,6 +501,7 @@ class _RadialBarPainter extends CustomPainter { seriesRenderer._renderList[2], 'series'); _chartShader = chartState._chart.onCreateShader!(chartShaderDetails); } + for (int k = 0; k < seriesRenderer._renderPaths.length; k++) { _drawPath( canvas, @@ -374,9 +510,23 @@ class _RadialBarPainter extends CustomPainter { seriesRenderer._renderList[1], _chartShader!); } + + for (int k = 0; k < seriesRenderer._shadowPaths.length; k++) { + canvas.drawPath( + seriesRenderer._shadowPaths[k], seriesRenderer._shadowPaint); + } + + if (_chartShader != null && seriesRenderer._overFilledPaint != null) { + seriesRenderer._overFilledPaint!.shader = _chartShader; + } + for (int k = 0; k < seriesRenderer._overFilledPaths.length; k++) { + canvas.drawPath(seriesRenderer._overFilledPaths[k], + seriesRenderer._overFilledPaint!); + } + if (seriesRenderer._renderList[0].strokeColor != null && seriesRenderer._renderList[0].strokeWidth != null && - seriesRenderer._renderList[0].strokeWidth > 0) { + (seriesRenderer._renderList[0].strokeWidth > 0) == true) { final Paint paint = Paint(); paint.color = seriesRenderer._renderList[0].strokeColor; paint.strokeWidth = seriesRenderer._renderList[0].strokeWidth; @@ -393,21 +543,31 @@ class _RadialBarPainter extends CustomPainter { } /// Creates series renderer for RadialBar series +/// class RadialBarSeriesRenderer extends CircularSeriesRenderer { /// Calling the default constructor of RadialBarSeriesRenderer class. - RadialBarSeriesRenderer(); + /// + RadialBarSeriesRenderer() { + _shadowPaths = []; + _overFilledPaths = []; + } @override late CircularSeries _series; late num _innerRadius; - late num _radius; + late Color _fillColor, _strokeColor; + late double _opacity, _strokeWidth; + late List _shadowPaths; + late List _overFilledPaths; + late Paint _shadowPaint; + Paint? _overFilledPaint; @override Offset? _center; - /// finding first visible point + /// Method to find first visible point int? _getFirstVisiblePointIndex(RadialBarSeriesRenderer seriesRenderer) { for (int i = 0; i < seriesRenderer._renderPoints!.length; i++) { if (seriesRenderer._renderPoints![i].isVisible) { @@ -418,6 +578,7 @@ class RadialBarSeriesRenderer extends CircularSeriesRenderer { } /// Method for calculating animation for visible points on legend toggle + /// void _calculateVisiblePointLegendToggleAnimation(ChartPoint point, ChartPoint? _oldPoint, int i, num animationValue) { if (!_oldPoint!.isVisible && point.isVisible) { @@ -443,7 +604,8 @@ class RadialBarSeriesRenderer extends CircularSeriesRenderer { } } - /// To draw data points + /// To draw data points of the radial bar series + /// void _drawDataPoint( ChartPoint point, num? degree, @@ -459,10 +621,11 @@ class RadialBarSeriesRenderer extends CircularSeriesRenderer { int i, Canvas canvas, int index, - SfCircularChart chart) { + SfCircularChart chart, + double actualDegree) { late RadialBarSeries series; if (seriesRenderer._series is RadialBarSeries) { - series = seriesRenderer._series as RadialBarSeries; + series = seriesRenderer._series as RadialBarSeries; } _drawPath( canvas, @@ -480,7 +643,7 @@ class RadialBarSeriesRenderer extends CircularSeriesRenderer { 360 - 0.001, chart, true)); - if (_radius > 0 && degree! > 0) { + if (_radius > 0 && degree != null && degree > 0) { _renderRadialPoints( point, degree, @@ -496,11 +659,13 @@ class RadialBarSeriesRenderer extends CircularSeriesRenderer { i, canvas, index, - chart); + chart, + actualDegree); } } - /// To render radial data points + /// Method to render radial data points + /// void _renderRadialPoints( ChartPoint point, num? degree, @@ -516,7 +681,8 @@ class RadialBarSeriesRenderer extends CircularSeriesRenderer { int i, Canvas canvas, int index, - SfCircularChart chart) { + SfCircularChart chart, + double actualDegree) { if (point.isVisible) { final _Region pointRegion = _Region( _degreesToRadians(point.startAngle!), @@ -550,41 +716,109 @@ class RadialBarSeriesRenderer extends CircularSeriesRenderer { } final _StyleOptions? style = seriesRenderer._selectPoint(i, seriesRenderer, chart, point); - final Color fillColor = style != null && style.fill != null + _fillColor = style != null && style.fill != null ? style.fill! : (point.fill != Colors.transparent ? _series._renderer!.getPointColor( seriesRenderer, point, i, index, point.fill, _series.opacity)! : point.fill); - final Color strokeColor = style != null && style.strokeColor != null + _strokeColor = style != null && style.strokeColor != null ? style.strokeColor! : _series._renderer!.getPointStrokeColor( seriesRenderer, point, i, index, point.strokeColor); - final double strokeWidth = style != null && style.strokeWidth != null + _strokeWidth = style != null && style.strokeWidth != null ? style.strokeWidth!.toDouble() : _series._renderer! .getPointStrokeWidth( seriesRenderer, point, i, index, point.strokeWidth) .toDouble(); - final double opacity = style != null && style.opacity != null + _opacity = style != null && style.opacity != null ? style.opacity!.toDouble() : _series._renderer! .getOpacity(seriesRenderer, point, i, index, _series.opacity); seriesRenderer._innerRadialradius = - !(point.isVisible) || (seriesRenderer._innerRadialradius == null) + !point.isVisible || (seriesRenderer._innerRadialradius == null) ? _innerRadius : seriesRenderer._innerRadialradius; seriesRenderer._renderList.clear(); - final Path _renderPath = _getRoundedCornerArcPath( - hide ? oldInnerRadius! : _innerRadius, - hide ? oldRadius! : _radius, - _center!, - hide ? oldStart! : pointStartAngle, - hide ? oldEnd! : pointEndAngle!, + + _drawRadialBarPath( + canvas, + point, + chart, + seriesRenderer, + hide, + pointStartAngle, + pointEndAngle, + oldRadius, + oldInnerRadius, + oldStart, + oldEnd, degree, - _series.cornerStyle, - point); - seriesRenderer._renderPaths.add(_renderPath); + actualDegree); + } + + /// Method to draw the radial bar series path + /// + void _drawRadialBarPath( + Canvas canvas, + ChartPoint point, + SfCircularChart chart, + RadialBarSeriesRenderer seriesRenderer, + bool hide, + num pointStartAngle, + num? pointEndAngle, + num? oldRadius, + num? oldInnerRadius, + num? oldStart, + num? oldEnd, + num? degree, + double actualDegree) { + Path path; + if (degree! > 360) { + path = _getRoundedCornerArcPath( + hide ? oldInnerRadius! : _innerRadius, + hide ? oldRadius! : _radius, + _center!, + 0, + 360 - 0.001, + 360 - 0.001, + _series.cornerStyle, + point); + final double innerRadius = + hide ? oldInnerRadius!.toDouble() : _innerRadius.toDouble(); + final double outerRadius = + hide ? oldRadius!.toDouble() : _radius.toDouble(); + final double startAngle = + hide ? oldStart!.toDouble() : pointStartAngle.toDouble(); + final double endAngle = + hide ? oldEnd!.toDouble() : pointEndAngle!.toDouble(); + path.arcTo( + Rect.fromCircle(center: _center!, radius: outerRadius.toDouble()), + _degreesToRadians(startAngle).toDouble(), + _degreesToRadians(endAngle - startAngle).toDouble(), + true); + path.arcTo( + Rect.fromCircle(center: _center!, radius: innerRadius.toDouble()), + _degreesToRadians(endAngle.toDouble()).toDouble(), + (_degreesToRadians(startAngle.toDouble()) - + _degreesToRadians(endAngle.toDouble())) + .toDouble(), + false); + } else { + path = _getRoundedCornerArcPath( + hide ? oldInnerRadius! : _innerRadius, + hide ? oldRadius! : _radius, + _center!, + hide ? oldStart! : pointStartAngle, + hide ? oldEnd! : pointEndAngle!, + degree, + _series.cornerStyle, + point); + } + + seriesRenderer._renderPaths.add(path); + if (chart.onCreateShader != null && point.shader == null) { point._pathRect = Rect.fromCircle( center: _center!, @@ -596,10 +830,11 @@ class RadialBarSeriesRenderer extends CircularSeriesRenderer { radius: seriesRenderer._innerRadialradius!.toDouble(), ); seriesRenderer._renderList.add(_StyleOptions( - fill: fillColor, - strokeWidth: _chartState._animateCompleted ? strokeWidth : 0, - strokeColor: strokeColor, - opacity: opacity)); + fill: _fillColor, + strokeWidth: + _chartState._renderingDetails.animateCompleted ? _strokeWidth : 0, + strokeColor: _strokeColor, + opacity: _opacity)); seriesRenderer._renderList.add(point._pathRect); seriesRenderer._renderList.add(innerRect); } else { @@ -609,28 +844,137 @@ class RadialBarSeriesRenderer extends CircularSeriesRenderer { _drawPath( canvas, _StyleOptions( - fill: fillColor, - strokeWidth: _chartState._animateCompleted ? strokeWidth : 0, - strokeColor: strokeColor, - opacity: opacity), - _renderPath, + fill: _fillColor, + strokeWidth: _chartState._renderingDetails.animateCompleted + ? _strokeWidth + : 0, + strokeColor: _strokeColor, + opacity: _opacity), + path, point._pathRect, point.shader); + // ignore: unnecessary_null_comparison + if (point.shader != null && + // ignore: unnecessary_null_comparison + _strokeColor != null && + // ignore: unnecessary_null_comparison + _strokeWidth != null && + _strokeWidth > 0 && + _chartState._renderingDetails.animateCompleted) { + final Paint paint = Paint(); + paint.color = _strokeColor; + paint.strokeWidth = _strokeWidth; + paint.style = PaintingStyle.stroke; + canvas.drawPath(path, paint); + } + } + } + + final num? angle = hide ? oldEnd : pointEndAngle; + final num? startAngle = hide ? oldStart : pointStartAngle; + if (actualDegree > 360 && + angle != null && + startAngle != null && + angle >= startAngle + 180) { + _applyShadow(hide, angle, actualDegree, canvas, chart, point, + oldInnerRadius, oldRadius); + } + } + + /// Method to apply shadow at segment's end + void _applyShadow( + bool hide, + num? pointEndAngle, + double actualDegree, + Canvas canvas, + SfCircularChart chart, + ChartPoint point, + num? oldInnerRadius, + num? oldRadius) { + if (pointEndAngle != null && actualDegree > 360) { + final double innerRadius = + hide ? oldInnerRadius!.toDouble() : _innerRadius.toDouble(); + final double outerRadius = + hide ? oldRadius!.toDouble() : _radius.toDouble(); + final double radius = (innerRadius - outerRadius).abs() / 2; + final Offset? midPoint = _degreeToPoint( + pointEndAngle, (innerRadius + outerRadius) / 2, _center!); + if (radius > 0) { + double strokeWidth = radius * 0.2; + strokeWidth = strokeWidth < 3 ? 3 : (strokeWidth > 5 ? 5 : strokeWidth); + _shadowPaint = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = strokeWidth + ..maskFilter = + MaskFilter.blur(BlurStyle.normal, _getSigmaFromRadius(3)); + + _overFilledPaint = Paint()..color = _fillColor; if (point.shader != null) { - // ignore: unnecessary_null_comparison - if (strokeColor != null && - // ignore: unnecessary_null_comparison - strokeWidth != null && - strokeWidth > 0 && - _chartState._animateCompleted) { - final Paint paint = Paint(); - paint.color = strokeColor; - paint.strokeWidth = strokeWidth; - paint.style = PaintingStyle.stroke; - canvas.drawPath(_renderPath, paint); + _overFilledPaint!.shader = point.shader; + } + + if (_series.cornerStyle == CornerStyle.endCurve || + _series.cornerStyle == CornerStyle.bothCurve) { + pointEndAngle = + (pointEndAngle > 360 ? pointEndAngle : (pointEndAngle - 360)) + + 11.5; + final Path path = Path() + ..addArc( + Rect.fromCircle( + center: midPoint!, radius: radius - (radius * 0.05)), + _degreesToRadians(pointEndAngle + 22.5).toDouble(), + _degreesToRadians(118.125).toDouble()); + final Path overFilledPath = Path() + ..addArc( + Rect.fromCircle(center: midPoint, radius: radius), + _degreesToRadians(pointEndAngle - 20).toDouble(), + _degreesToRadians(225).toDouble()); + if (chart.onCreateShader != null && point.shader == null) { + _shadowPaths.add(path); + _overFilledPaths.add(overFilledPath); + } else { + canvas.drawPath(path, _shadowPaint); + canvas.drawPath(overFilledPath, _overFilledPaint!); + } + } else if (_series.cornerStyle == CornerStyle.bothFlat || + _series.cornerStyle == CornerStyle.startCurve) { + _overFilledPaint! + ..style = PaintingStyle.stroke + ..strokeWidth = strokeWidth + ..color = _fillColor; + final Offset? startPoint = _degreeToPoint( + pointEndAngle, outerRadius - (outerRadius * 0.025), _center!); + + final Offset? endPoint = _degreeToPoint( + pointEndAngle, innerRadius + (innerRadius * 0.025), _center!); + + final Offset? overFilledStartPoint = + _degreeToPoint(pointEndAngle - 2, outerRadius, _center!); + final Offset? overFilledEndPoint = + _degreeToPoint(pointEndAngle - 2, innerRadius, _center!); + if (chart.onCreateShader != null && point.shader == null) { + final Path path = Path() + ..moveTo(startPoint!.dx, startPoint.dy) + ..lineTo(endPoint!.dx, endPoint.dy); + path.close(); + final Path overFilledPath = Path() + ..moveTo(overFilledStartPoint!.dx, overFilledStartPoint.dy) + ..lineTo(overFilledEndPoint!.dx, overFilledEndPoint.dy); + path.close(); + _shadowPaths.add(path); + _overFilledPaths.add(overFilledPath); + } else { + canvas.drawLine(startPoint!, endPoint!, _shadowPaint); + canvas.drawLine( + overFilledStartPoint!, overFilledEndPoint!, _overFilledPaint!); } } } } } + + /// Method to convert the radius to sigma + double _getSigmaFromRadius(double radius) { + return radius * 0.57735 + 0.5; + } } diff --git a/packages/syncfusion_flutter_charts/lib/src/circular_chart/utils/helper.dart b/packages/syncfusion_flutter_charts/lib/src/circular_chart/utils/helper.dart index 7d34f8ad9..75669cc3a 100644 --- a/packages/syncfusion_flutter_charts/lib/src/circular_chart/utils/helper.dart +++ b/packages/syncfusion_flutter_charts/lib/src/circular_chart/utils/helper.dart @@ -2,13 +2,12 @@ part of charts; /// To get equivalent value for the percentage num? _percentToValue(String? value, num size) { - if (value != null) { - return value.contains('%') - ? (size / 100) * - (num.tryParse(value.replaceAll(RegExp('%'), '')))!.abs() - : (num.tryParse(value))?.abs(); - } - return null; + return value != null + ? value.contains('%') + ? (size / 100) * + (num.tryParse(value.replaceAll(RegExp('%'), '')))!.abs() + : (num.tryParse(value))?.abs() + : null; } /// Convert degree to radian @@ -197,13 +196,18 @@ _Region? _getCircularPointRegion(SfCircularChart chart, Offset? position, CircularSeriesRenderer seriesRenderer) { _Region? pointRegion; const num chartStartAngle = -.5 * pi; + num fromCenterX, + fromCenterY, + tapAngle, + pointStartAngle, + pointEndAngle, + distanceFromCenter; for (final _Region region in seriesRenderer._pointRegions) { - final num fromCenterX = position!.dx - region.center!.dx; - final num fromCenterY = position.dy - region.center!.dy; - num tapAngle = - (atan2(fromCenterY, fromCenterX) - chartStartAngle) % (2 * pi); - num pointStartAngle = region.start - _degreesToRadians(-90); - num pointEndAngle = region.end - _degreesToRadians(-90); + fromCenterX = position!.dx - region.center!.dx; + fromCenterY = position.dy - region.center!.dy; + tapAngle = (atan2(fromCenterY, fromCenterX) - chartStartAngle) % (2 * pi); + pointStartAngle = region.start - _degreesToRadians(-90); + pointEndAngle = region.end - _degreesToRadians(-90); if (chart.onDataLabelRender != null) { seriesRenderer._dataPoints[region.pointIndex].labelRenderEvent = false; } @@ -214,7 +218,7 @@ _Region? _getCircularPointRegion(SfCircularChart chart, Offset? position, tapAngle = tapAngle > pointStartAngle ? tapAngle : 2 * pi + tapAngle; } if (tapAngle >= pointStartAngle && tapAngle <= pointEndAngle) { - final num distanceFromCenter = + distanceFromCenter = sqrt(pow(fromCenterX.abs(), 2) + pow(fromCenterY.abs(), 2)); if (distanceFromCenter <= region.outerRadius && distanceFromCenter >= region.innerRadius!) { @@ -222,7 +226,7 @@ _Region? _getCircularPointRegion(SfCircularChart chart, Offset? position, } } } - // } + return pointRegion; } @@ -231,7 +235,7 @@ void _drawPath(Canvas canvas, _StyleOptions style, Path path, [Rect? rect, Shader? shader]) { final Paint paint = Paint(); if (shader != null) { - paint..shader = shader; + paint.shader = shader; } if (style.fill != null) { paint.color = style.fill == Colors.transparent @@ -290,11 +294,15 @@ void _canRepaintSeries(List currentSeriesRenderers, series.borderWidth != oldWidgetSeries.borderWidth || series.name != oldWidgetSeries.name || series.borderColor.value != oldWidgetSeries.borderColor.value || - seriesRenderer._currentInnerRadius != - oldWidgetSeriesRenderer._currentInnerRadius || - seriesRenderer._currentRadius != oldWidgetSeriesRenderer._currentRadius || - seriesRenderer._start != oldWidgetSeriesRenderer._start || - seriesRenderer._totalAngle != oldWidgetSeriesRenderer._totalAngle || + seriesRenderer._segmentRenderingValues['currentInnerRadius'] != + oldWidgetSeriesRenderer + ._segmentRenderingValues['currentInnerRadius'] || + seriesRenderer._segmentRenderingValues['currentRadius'] != + oldWidgetSeriesRenderer._segmentRenderingValues['currentRadius'] || + seriesRenderer._segmentRenderingValues['start'] != + oldWidgetSeriesRenderer._segmentRenderingValues['start'] || + seriesRenderer._segmentRenderingValues['totalAngle'] != + oldWidgetSeriesRenderer._segmentRenderingValues['totalAngle'] || seriesRenderer._dataPoints.length != oldWidgetSeriesRenderer._dataPoints.length || series.emptyPointSettings.borderWidth != @@ -362,10 +370,10 @@ num _findAngleDeviation(num innerRadius, num outerRadius, num totalAngle) { } /// It returns the actual label value for tooltip and data label etc -dynamic _getDecimalLabelValue(dynamic value, [int? showDigits]) { - if (value.toString().split('.').length > 1) { +String _getDecimalLabelValue(num? value, [int? showDigits]) { + if (value != null && value.toString().split('.').length > 1) { final String str = value.toString(); - final List list = str.split('.'); + final List list = str.split('.'); value = double.parse(value.toStringAsFixed(showDigits ?? 3)); value = (list[1] == '0' || list[1] == '00' || @@ -377,8 +385,8 @@ dynamic _getDecimalLabelValue(dynamic value, [int? showDigits]) { ? value.round() : value; } - final dynamic text = value; - return text.toString(); + + return value.toString(); } /// Method to rotate Sweep gradient diff --git a/packages/syncfusion_flutter_charts/lib/src/common/common.dart b/packages/syncfusion_flutter_charts/lib/src/common/common.dart index 564697e11..fd73d8b7a 100644 --- a/packages/syncfusion_flutter_charts/lib/src/common/common.dart +++ b/packages/syncfusion_flutter_charts/lib/src/common/common.dart @@ -38,6 +38,7 @@ class _ChartContainerBox extends RenderShiftedBox { } @override + // ignore: unnecessary_overrides void paint(PaintingContext context, Offset offset) { super.paint(context, offset); } @@ -176,6 +177,37 @@ class ChartTitle { ///} ///``` final double borderWidth; + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is ChartTitle && + other.textStyle == textStyle && + other.alignment == alignment && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.backgroundColor == backgroundColor; + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode { + final List values = [ + text, + textStyle, + alignment, + borderColor, + borderWidth, + backgroundColor + ]; + return hashList(values); + } } /// This class has the property of the chart text style. @@ -203,7 +235,7 @@ class ChartTextStyle extends TextStyle { inherit: true, ); - /// To set the color to chart text + /// To set the color of chart text. /// ///```dart ///Widget build(BuildContext context) { @@ -217,7 +249,7 @@ class ChartTextStyle extends TextStyle { @override final Color? color; - /// To set the font family to chart text + /// To set the font family of chart text. /// /// Defaults to `Roboto` /// @@ -233,7 +265,7 @@ class ChartTextStyle extends TextStyle { @override final String fontFamily; - /// To set the font style to chart text + /// To set the font style of chart text. /// ///```dart ///Widget build(BuildContext context) { @@ -247,7 +279,7 @@ class ChartTextStyle extends TextStyle { @override final FontStyle fontStyle; - /// To set the font weight to chart text + /// To set the font weight of chart text. /// /// Defaults to `FontStyle.normal` /// @@ -263,7 +295,7 @@ class ChartTextStyle extends TextStyle { @override final FontWeight fontWeight; - /// To set the fot size to chart text + /// To change the font size of chart text /// /// Defaults to `12` /// @@ -288,10 +320,10 @@ class ChartTextStyle extends TextStyle { ///legend item and for [SfCircularChart] type chart by default values mapped with ///xValueMapper will be displayed. /// -///You can customized with `isVisible`, `borderWidth`, `alignment`, `opacity`, `borderColor`, -///`padding` and so on. +/// Provides options such as isVisible, borderWidth, alignment, opacity, borderColor, +///padding and so on to customize the appearance of the legend. /// -///_Note:_ This is common for [SfCartesianChart] and [SfCircularChart] +@immutable class Legend { /// Creating an argument constructor of Legend class. Legend( @@ -317,6 +349,7 @@ class Legend { Color? iconBorderColor, double? iconBorderWidth, double? itemPadding, + this.offset, this.image}) : isVisible = isVisible ?? false, position = position ?? LegendPosition.auto, @@ -380,7 +413,7 @@ class Legend { ///Alignment of the legend. /// ///Alignment will work if the legend width is greater than - ///the total legend items’ width. + ///the total legend item's width. /// ///Defaults to `ChartAlignment.center` /// @@ -735,8 +768,26 @@ class Legend { ///``` final double itemPadding; - ///Padding of the legend items. + ///Places the legend in custom position. + /// + ///If the [offset] has been set, the legend is moved from its actual position. + /// For example, if the [position] is `top`, then the legend will be placed in the top + /// but in the position added to the actual top position. /// + ///Also, the legend will not take a dedicated position for it and will be drawn + /// on the top of the chart's plot area. + ///```dart + ///Widget build(BuildContext context) { + /// return Container( + /// child: SfCartesianChart( + /// legend: Legend( + /// isVisible: true, + /// offset: Offset(20,40)) + /// )); + ///} + ///``` + final Offset? offset; + ///Used to add image to the legend icon. /// ///Default image size is `10.0`. @@ -751,6 +802,70 @@ class Legend { ///} ///``` final ImageProvider? image; + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is Legend && + other.isVisible == isVisible && + other.position == position && + other.alignment == alignment && + other.backgroundColor == backgroundColor && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.opacity == opacity && + other.height == height && + other.width == width && + other.padding == padding && + other.iconHeight == iconHeight && + other.iconWidth == iconWidth && + other.toggleSeriesVisibility == toggleSeriesVisibility && + other.textStyle == textStyle && + other.isResponsive == isResponsive && + other.orientation == orientation && + other.title == title && + other.overflowMode == overflowMode && + other.legendItemBuilder == legendItemBuilder && + other.iconBorderColor == iconBorderColor && + other.iconBorderWidth == iconBorderWidth && + other.itemPadding == itemPadding && + other.image == image; + } + + @override + int get hashCode { + final List values = [ + isVisible, + position, + alignment, + backgroundColor, + borderColor, + borderWidth, + opacity, + height, + width, + padding, + iconHeight, + iconWidth, + toggleSeriesVisibility, + textStyle, + isResponsive, + orientation, + title, + overflowMode, + legendItemBuilder, + iconBorderColor, + iconBorderWidth, + itemPadding, + image + ]; + return hashList(values); + } } /// Legend renderer class for mutable fields and methods @@ -760,7 +875,7 @@ class LegendRenderer { //ignore: unused_field final Legend? _legend; - _LegendRenderer _renderer = _LegendRenderer(); + final _LegendRenderer _renderer = _LegendRenderer(); late LegendPosition _legendPosition; late LegendItemOrientation _orientation; } @@ -783,6 +898,7 @@ class _MeasureWidgetContext { ///aligned to the chart's width and it will placed at the bottom of the chart. /// ///Provides Options to customize the [text], [textStyle] and [alignment] properties. +@immutable class LegendTitle { /// Creating an argument constructor of LegendTitle class. LegendTitle({this.text, TextStyle? textStyle, ChartAlignment? alignment}) @@ -861,14 +977,28 @@ class LegendTitle { ///} ///``` final ChartAlignment alignment; -} -/// Returns the widget. -/// -/// Customize the appearance of legend items with your template by -/// using legendItemBuilder property of legend. -typedef LegendItemBuilder = Widget Function( - String legendText, dynamic series, dynamic point, int seriesIndex); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is LegendTitle && + other.textStyle == textStyle && + other.alignment == alignment && + other.text == text; + } + + @override + int get hashCode { + final List values = [text, textStyle, alignment]; + return hashList(values); + } +} ///Handling empty points in charts /// @@ -959,38 +1089,32 @@ class EmptyPointSettings { ///} ///``` final EmptyPointMode mode; -} - -/// Maps the index value. -typedef ChartIndexedValueMapper = R? Function(int index); - -/// Maps the data from data source. -typedef ChartValueMapper = R? Function(T datum, int index); - -///Signature for the callback that returns the shader from the data source based on the index. -/// Can get the data, index, color and rect values. -/// -/// -///T - Data of the current data point -/// -/// -///index - Index of the current data point -/// -/// -///rect - Rect value of the current data point slice -/// -///color - Color of the current data point -typedef ChartShaderMapper = Shader Function( - T datum, int index, Color color, Rect rect); - -/// Returns the widget. -typedef ChartWidgetBuilder = Widget Function(dynamic data, dynamic point, - dynamic series, int pointIndex, int seriesIndex); + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } -/// Returns the widget as a template of trackball -typedef ChartTrackballBuilder = Widget Function( - BuildContext context, TrackballDetails trackballDetails); + return other is EmptyPointSettings && + other.color == color && + other.mode == mode && + other.borderColor == borderColor && + other.borderWidth == borderWidth; + } -/// Custom renderer for series -typedef ChartSeriesRendererFactory = ChartSeriesRenderer Function( - ChartSeries series); + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode { + final List values = [ + color, + mode, + borderColor, + borderWidth + ]; + return hashList(values); + } +} diff --git a/packages/syncfusion_flutter_charts/lib/src/common/event_args.dart b/packages/syncfusion_flutter_charts/lib/src/common/event_args.dart index d31294f34..d60fda2ed 100644 --- a/packages/syncfusion_flutter_charts/lib/src/common/event_args.dart +++ b/packages/syncfusion_flutter_charts/lib/src/common/event_args.dart @@ -25,7 +25,7 @@ class TooltipArgs { double? locationY; /// Get the index of the current series. - final dynamic? seriesIndex; + final dynamic seriesIndex; /// Get the list of data points in the series. final List? dataPoints; @@ -37,9 +37,9 @@ class TooltipArgs { final num? viewportPointIndex; } -/// Holds the [onActualRangeChanged] event arguments. +/// Holds the `onActualRangeChanged` event arguments. /// -/// ActualRangeChangedArgs is the type argument for [onActualRangeChanged] event. Whenever the actual range is changed, the [onActualRangeChanged] event is +/// ActualRangeChangedArgs is the type argument for `onActualRangeChanged` event. Whenever the actual range is changed, the `onActualRangeChanged` event is /// triggered and provides options to set the visible minimum and maximum values. /// /// It has the public properties of axis name, axis type, actual minimum, and maximum, visible minimum and maximum and axis orientation. @@ -60,13 +60,13 @@ class ActualRangeChangedArgs { /// Get the axis type. final ChartAxis? axis; - /// Get the actual minimum range for an axis. + /// Get the actual minimum range of an axis. final dynamic actualMin; - /// Get the actual maximum range for an axis. + /// Get the actual maximum range of an axis. final dynamic actualMax; - /// Get the actual interval for an axis. + /// Get the actual interval of an axis. final dynamic actualInterval; /// Get and set the minimum visible range for an axis. @@ -75,10 +75,10 @@ class ActualRangeChangedArgs { /// Get and set the maximum visible range for an axis. dynamic visibleMax; - /// Get and set the interval for the visible range of an axis. + /// Get and set the interval for the visible range for an axis. dynamic visibleInterval; - /// Get the orientation for an axis. + /// Get the orientation of an axis. final AxisOrientation? orientation; } @@ -102,7 +102,7 @@ class AxisLabelRenderArgs { /// Get the axis name. final String? axisName; - /// Get the orientation for an axis. + /// Get the orientation of an axis. final AxisOrientation? orientation; /// Get the chart axis type and its properties. @@ -135,7 +135,7 @@ class AxisLabelRenderDetails { /// Get the axis name. final String? axisName; - /// Get the orientation for an axis. + /// Get the orientation of an axis. final AxisOrientation orientation; /// Get the chart axis type and its properties. @@ -204,7 +204,7 @@ class DataLabelRenderArgs { /// ) /// ``` /// - /// _Note_: Series type may vary based on the chart type. + /// _Note:_ Series type may vary based on the chart type. /// /// * Cartesian chart: CartesianSeries series; /// * Circular chart: CircularSeries series; @@ -327,7 +327,7 @@ class CrosshairRenderArgs { late Color lineColor; /// Get the visible range value. - final dynamic? value; + final dynamic value; /// Get the name of the axis. final String? axisName; @@ -398,6 +398,30 @@ class PointTapArgs { final num? viewportPointIndex; } +///Holds the arguments of `onPointTap`, `onPointDoubleTap` and `onPointLongPress` callbacks. +/// +///The user can fetch the series index, point index, viewport point index and data of the current point. +class ChartPointDetails { + /// Creating an argument constructor of PointTapArgs class. + ChartPointDetails( + [this.seriesIndex, + this.viewportPointIndex, + this.dataPoints, + this.pointIndex]); + + /// Get the series index. + final int? seriesIndex; + + /// Get the overall index value. + final int? pointIndex; + + /// Get the list of data points. + final List? dataPoints; + + /// Get the viewport index value. + final num? viewportPointIndex; +} + /// Holds the onAxisLabelTapped event arguments. /// /// This is the argument type of the onAxisLabelTapped event. Whenever the axis label is tapped, the onAxisLabelTapped event is triggered and provides options to get the axis type, label text, and axis name. @@ -502,20 +526,26 @@ class SelectionArgs { final int viewportPointIndex; } -/// Holds the onIndicatorRender event arguments. +@Deprecated('Use IndicatorRenderParams instead.') + +/// Holds the onRenderDetailsUpdate event arguments. /// -///Triggers when indicator is rendering. You can customize the [indicatorName], [signalLineColor], [signalLineWidth], and [lineDashArray]. +///Triggers when indicator is rendering. You can customize the [signalLineColor], [signalLineWidth], and [signalLineDashArray]. /// /// _Note:_ This is only applicable for [SfCartesianChart]. class IndicatorRenderArgs { /// Creating an argument constructor of IndicatorRenderArgs class. - IndicatorRenderArgs( - [this.indicator, this.index, this.seriesName, this.dataPoints]); + IndicatorRenderArgs([ + this.indicator, + this.index, + this.seriesName, + this.dataPoints, + ]); /// Get the technical indicator information. final TechnicalIndicators? indicator; - /// Get and set the indicator name. + /// Get the indicator name. late String indicatorName; /// Get the current index of the technical indicator. @@ -622,3 +652,121 @@ class ChartShaderDetails { ///Holds the doughnut and radial bar chart's inner rect value. final Rect? innerRect; } + +/// Holds the onRenderDetailsUpdate callback arguments. +class IndicatorRenderParams { + /// Creating an argument constructor of IndicatorRenderParams class. + IndicatorRenderParams(this.calculatedDataPoints, this.name, + this.signalLineWidth, this.signalLineColor, this.signalLineDashArray); + + ///Gets the calculated indicator data points details. + final List>? calculatedDataPoints; + + ///Gets the width of the signal line. + late double signalLineWidth; + + ///Gets the color of the signal line. + late Color signalLineColor; + + ///Gets the name of the indicator. + /// + ///If the user specifies a value for the `name` property in the `TechnicalIndicators` class, + /// that value can be fetched here. If it is null, then the name generated internally for the + /// indicator can be fetched here. + final String name; + + ///Gets the dash array of the signal line. + late List signalLineDashArray; +} + +/// Holds the onRenderDetailsUpdate callback arguments. +class BollingerBandIndicatorRenderParams extends IndicatorRenderParams { + /// Creating an argument constructor of BollingerBandIndicatorRenderParams class. + BollingerBandIndicatorRenderParams( + this.upperLineValues, + this.lowerLineValues, + List>? calculatedDataPoints, + String name, + double signalLineWidth, + Color signalLineColor, + List signalLineDashArray) + : super(calculatedDataPoints, name, signalLineWidth, signalLineColor, + signalLineDashArray); + + ///Gets the calculated upper line values of the Bollinger band indicator. + final List>? upperLineValues; + + ///Gets the calculated lower line values of the Bollinger band indicator. + final List>? lowerLineValues; +} + +/// Holds the onRenderDetailsUpdate callback arguments. +class MomentumIndicatorRenderParams extends IndicatorRenderParams { + /// Creating an argument constructor of MomentumIndicatorRenderParams class. + MomentumIndicatorRenderParams( + this.centerLineValue, + List>? calculatedDataPoints, + String name, + double signalLineWidth, + Color signalLineColor, + List signalLineDashArray) + : super(calculatedDataPoints, name, signalLineWidth, signalLineColor, + signalLineDashArray); + + ///Gets the calculated center line value of the Momentum indicator. + final double? centerLineValue; +} + +/// Holds the onRenderDetailsUpdate callback arguments. +class StochasticIndicatorRenderParams extends IndicatorRenderParams { + /// Creating an argument constructor of StochasticIndicatorRenderParams class. + StochasticIndicatorRenderParams( + this.periodLineValues, + List>? calculatedDataPoints, + String name, + double signalLineWidth, + Color signalLineColor, + List signalLineDashArray) + : super(calculatedDataPoints, name, signalLineWidth, signalLineColor, + signalLineDashArray); + + ///Gets the calculated period line values of the Stochastic indicator. + final List>? periodLineValues; +} + +/// Holds the onRenderDetailsUpdate callback arguments. +class MacdIndicatorRenderParams extends IndicatorRenderParams { + /// Creating an argument constructor of MacdIndicatorRenderParams class. + MacdIndicatorRenderParams( + this.macdLineValues, + this.macdHistogramValues, + List>? calculatedDataPoints, + String name, + double signalLineWidth, + Color signalLineColor, + List signalLineDashArray) + : super(calculatedDataPoints, name, signalLineWidth, signalLineColor, + signalLineDashArray); + + ///Gets the calculated Macd line values of the Macd indicator. + final List>? macdLineValues; + + ///Gets the calculated histogram values of the Macd indicator. + final List>? macdHistogramValues; +} + +/// Holds the ChartIndicator values +class TechnicalIndicatorRenderDetails { + /// Creating an argument constructor of ChartIndicator class. + TechnicalIndicatorRenderDetails( + this.signalLineColor, this.signalLineWidth, this.signalLineDashArray); + + ///Color of the signal line. + final Color? signalLineColor; + + ///Width of the signal line. + final double? signalLineWidth; + + ///Dash array of the signal line + final List? signalLineDashArray; +} diff --git a/packages/syncfusion_flutter_charts/lib/src/common/legend/legend.dart b/packages/syncfusion_flutter_charts/lib/src/common/legend/legend.dart index 92e5c8f26..b26d0e009 100644 --- a/packages/syncfusion_flutter_charts/lib/src/common/legend/legend.dart +++ b/packages/syncfusion_flutter_charts/lib/src/common/legend/legend.dart @@ -17,28 +17,29 @@ class _ChartLegend { /// To calculate legend bounds void _calculateLegendBounds(Size size) { legend = chart.legend; - final LegendRenderer legendRenderer = _chartState._legendRenderer; + final LegendRenderer legendRenderer = + _chartState._renderingDetails.legendRenderer; + final List<_MeasureWidgetContext> legendWidgetContext = + _chartState._renderingDetails.legendWidgetContext; + final _ChartLegend chartLegend = _chartState._renderingDetails.chartLegend; shouldRenderLegend = false; assert( - legend != null && legend!.width != null - ? !legend!.width!.contains(RegExp(r'[a-z]')) && - !legend!.width!.contains(RegExp(r'[A-Z]')) - : true, + !(legend != null && legend!.width != null) || + !legend!.width!.contains(RegExp(r'[a-z]')) && + !legend!.width!.contains(RegExp(r'[A-Z]')), 'Legend width must be number or percentage value, it should not contain any alphabets in the string.'); assert( - legend != null && legend!.height != null - ? !legend!.height!.contains(RegExp(r'[a-z]')) && - !legend!.height!.contains(RegExp(r'[A-Z]')) - : true, + !(legend != null && legend!.height != null) || + !legend!.height!.contains(RegExp(r'[a-z]')) && + !legend!.height!.contains(RegExp(r'[A-Z]')), 'Legend height must be number or percentage value, it should not contain any alphabets in the string.'); if (legend != null && legend!.isVisible!) { legendCollections = <_LegendRenderContext>[]; _calculateSeriesLegends(); // ignore: unnecessary_null_comparison - assert(legend!.itemPadding != null ? legend!.itemPadding >= 0 : true, + assert(!(legend!.itemPadding != null) || legend!.itemPadding >= 0, 'The padding between the legend and chart area should not be less than 0.'); - if (legendCollections!.isNotEmpty || - _chartState._legendWidgetContext.isNotEmpty) { + if (legendCollections!.isNotEmpty || legendWidgetContext.isNotEmpty) { num legendHeight = 0, legendWidth = 0, titleHeight = 0, @@ -54,7 +55,7 @@ class _ChartLegend { Size titleSize; const num titleSpace = 10; final num padding = legend!.itemPadding; - _chartState._chartLegend.isNeedScrollable = false; + chartLegend.isNeedScrollable = false; final bool isBottomOrTop = legendRenderer._legendPosition == LegendPosition.bottom || legendRenderer._legendPosition == LegendPosition.top; @@ -76,6 +77,13 @@ class _ChartLegend { : isBottomOrTop ? size.width : _percentageToValue('30%', size.width); + // To reduce the container width based on offset. + if (chartLegend.legend!.offset != null && + (chartLegend.legend!.offset?.dx.isNegative == false)) { + maxRenderWidth = maxRenderWidth! - + (chartLegend.legend!.offset?.dx as num) * 1.5 + + padding; + } if (legend!.title.text != null && legend!.title.text!.isNotEmpty) { titleSize = measureText(legend!.title.text!, legend!.title.textStyle); @@ -83,25 +91,24 @@ class _ChartLegend { } final bool isTemplate = legend!.legendItemBuilder != null; - final int length = isTemplate - ? _chartState._legendWidgetContext.length - : legendCollections!.length; + final int length = + isTemplate ? legendWidgetContext.length : legendCollections!.length; late _MeasureWidgetContext legendContext; late _LegendRenderContext legendRenderContext; String legendText; Size textSize; // ignore: unnecessary_null_comparison - assert(legend!.iconWidth != null ? legend!.iconWidth >= 0 : true, + assert(!(legend!.iconWidth != null) || legend!.iconWidth >= 0, 'The icon width of legend should not be less than 0.'); // ignore: unnecessary_null_comparison - assert(legend!.iconHeight != null ? legend!.iconHeight >= 0 : true, + assert(!(legend!.iconHeight != null) || legend!.iconHeight >= 0, 'The icon height of legend should not be less than 0.'); // ignore: unnecessary_null_comparison - assert(legend!.padding != null ? legend!.padding >= 0 : true, + assert(!(legend!.padding != null) || legend!.padding >= 0, 'The padding between legend text and legend icon should not be less than 0.'); for (int i = 0; i < length; i++) { if (isTemplate) { - legendContext = _chartState._legendWidgetContext[i]; + legendContext = legendWidgetContext[i]; currentWidth = legendContext.size!.width + padding; currentHeight = legendContext.size!.height + padding; } else { @@ -136,7 +143,7 @@ class _ChartLegend { if ((legendWidth + currentWidth) > maxRenderWidth!) { legendWidth = currentWidth; if (legendHeight + currentHeight > maxRenderHeight!) { - _chartState._chartLegend.isNeedScrollable = true; + chartLegend.isNeedScrollable = true; } else { legendHeight = legendHeight + currentHeight; } @@ -162,7 +169,7 @@ class _ChartLegend { if ((legendHeight + currentHeight) > maxRenderHeight!) { legendHeight = currentHeight; if (legendWidth + currentWidth > maxRenderWidth!) { - _chartState._chartLegend.isNeedScrollable = true; + chartLegend.isNeedScrollable = true; } else { legendWidth = legendWidth + currentWidth; } @@ -205,6 +212,7 @@ class _ChartLegend { bool isTrendlineadded = false; TrendlineRenderer? trendlineRenderer; final CartesianSeries series = seriesRenderer._series; + final _RenderingDetails _renderingDetails = _chartState._renderingDetails; if (trendline != null) { isTrendlineadded = true; trendlineRenderer = seriesRenderer._trendlineRenderer[trendlineIndex!]; @@ -230,8 +238,8 @@ class _ChartLegend { trendline: trendline, seriesIndex: index, trendlineIndex: isTrendlineadded ? trendlineIndex : null, - isSelect: _chartState._isTrendlineToggled - ? (isTrendlineadded ? !trendlineRenderer!._visible : true) + isSelect: _chartState._isTrendlineToggled == true + ? (!isTrendlineadded || !trendlineRenderer!._visible) : !series.isVisible, text: legendEventArgs?.text ?? series.legendItemText ?? @@ -248,17 +256,21 @@ class _ChartLegend { legendCollections!.add(legendRenderContext); if (!seriesRenderer._visible! && series.isVisibleInLegend && - (_chartState._widgetNeedUpdate || _chartState._initialRender) && + (_renderingDetails.widgetNeedUpdate || + _renderingDetails.initialRender!) && (seriesRenderer._oldSeries == null || (!series.isVisible && seriesRenderer._oldSeries!.isVisible))) { legendRenderContext.isSelect = true; - if (!_chartState._legendToggleStates.contains(legendRenderContext)) { - _chartState._legendToggleStates.add(legendRenderContext); + if (_chartState._renderingDetails.legendToggleStates + .contains(legendRenderContext) == + false) { + _chartState._renderingDetails.legendToggleStates + .add(legendRenderContext); } - } else if (_chartState._widgetNeedUpdate && + } else if (_renderingDetails.widgetNeedUpdate && (seriesRenderer._oldSeries != null && (series.isVisible && - !_chartState._legendToggling && + _chartState._legendToggling == false && seriesRenderer._visible!))) { final List visibleSeriesRenderers = _chartState._chartSeries.visibleSeriesRenderers; @@ -267,16 +279,17 @@ class _ChartLegend { series.name ?? 'Series $index'; final int seriesIndex = visibleSeriesRenderers.indexOf(seriesRenderer); - final legendToggle = <_LegendRenderContext>[] + final List<_LegendRenderContext> legendToggle = <_LegendRenderContext>[] //ignore: prefer_spread_collections - ..addAll(_chartState._legendToggleStates); - for (final legendContext in _chartState._legendToggleStates) { + ..addAll(_chartState._renderingDetails.legendToggleStates); + for (final _LegendRenderContext legendContext + in _chartState._renderingDetails.legendToggleStates) { if (seriesIndex == legendContext.seriesIndex && legendContext.text == legendItemText) { legendToggle.remove(legendContext); } } - _chartState._legendToggleStates = legendToggle; + _chartState._renderingDetails.legendToggleStates = legendToggle; } } } @@ -305,17 +318,18 @@ class _ChartLegend { final Trendline trendline = xYSeriesRenderer._series.trendlines![j]; if (trendline.isVisibleInLegend) { - _chartState._chartLegend._calculateLegends( + _chartState._renderingDetails.chartLegend._calculateLegends( chart, i, xYSeriesRenderer, trendline, j); } } } } } - if (chart.indicators.isNotEmpty) { + if (chart.indicators.isNotEmpty == true) { _calculateIndicatorLegends(); } - } else if (_chartState._chartSeries.visibleSeriesRenderers.isNotEmpty) { + } else if (_chartState._chartSeries.visibleSeriesRenderers.isNotEmpty == + true) { final dynamic seriesRenderer = _chartState._chartSeries.visibleSeriesRenderers[0]; for (int j = 0; j < seriesRenderer._renderPoints.length; j++) { @@ -328,7 +342,6 @@ class _ChartLegend { legendEventArgs.color = chartPoint.fill; chart.onLegendItemRender(legendEventArgs); } - legendCollections!.add(_LegendRenderContext( seriesRenderer: seriesRenderer, seriesIndex: j, @@ -366,6 +379,7 @@ class _ChartLegend { for (int i = 0; i < chart.indicators.length; i++) { final TechnicalIndicators indicator = chart.indicators[i]; + technicalIndicatorsRenderer = _chartState._technicalIndicatorRenderer[i]; final int count = indicatorTextCollection .contains(technicalIndicatorsRenderer?._indicatorType) ? _chartState._chartSeries?._getIndicatorId(indicatorTextCollection, @@ -401,9 +415,10 @@ class _ChartLegend { legendCollections!.add(legendRenderContext); if (!indicator.isVisible && indicator.isVisibleInLegend && - _chartState._initialRender) { + _chartState._renderingDetails.initialRender == true) { legendRenderContext.isSelect = true; - _chartState._legendToggleStates.add(legendRenderContext); + _chartState._renderingDetails.legendToggleStates + .add(legendRenderContext); } } } @@ -418,19 +433,22 @@ class _LegendContainer extends StatelessWidget { @override Widget build(BuildContext context) { + final _ChartLegend chartLegend = chartState._renderingDetails.chartLegend; final List<_LegendRenderContext> legendCollections = - chartState._chartLegend.legendCollections; + chartLegend.legendCollections!; final List legendWidgets = []; final Legend legend = chart.legend; num titleHeight = 0; - chartState._chartLegend.legendRepaintNotifier = ValueNotifier(0); + + final List<_MeasureWidgetContext> legendWidgetContext = + chartState._renderingDetails.legendWidgetContext; + chartLegend.legendRepaintNotifier = ValueNotifier(0); if (legend.legendItemBuilder != null) { - for (int i = 0; i < chartState._legendWidgetContext.length; i++) { + for (int i = 0; i < legendWidgetContext.length; i++) { final _MeasureWidgetContext legendRenderContext = - chartState._legendWidgetContext[i]; - if (legend.overflowMode == LegendItemOverflowMode.none - ? legendRenderContext.isRender - : true) { + legendWidgetContext[i]; + if (!(legend.overflowMode == LegendItemOverflowMode.none) || + legendRenderContext.isRender) { legendWidgets.add(_RenderLegend( index: i, template: legendRenderContext.widget!, @@ -441,9 +459,8 @@ class _LegendContainer extends StatelessWidget { } else { for (int i = 0; i < legendCollections.length; i++) { final _LegendRenderContext legendRenderContext = legendCollections[i]; - if (legend.overflowMode == LegendItemOverflowMode.none - ? legendRenderContext.isRender - : true) { + if (!(legend.overflowMode == LegendItemOverflowMode.none) || + legendRenderContext.isRender) { legendWidgets.add(_RenderLegend( index: i, size: legendRenderContext.size!, @@ -466,9 +483,12 @@ class _LegendContainer extends StatelessWidget { List legendWidgets, bool needLegendTitle, num titleHeight) { Widget widget; final Legend legend = chart.legend; - final LegendRenderer legedRenderer = chartState._legendRenderer; - final double legendHeight = chartState._chartLegend.legendSize.height; - if (chartState._chartLegend.isNeedScrollable) { + final _RenderingDetails renderingDetails = chartState._renderingDetails; + final _ChartLegend chartLegend = chartState._renderingDetails.chartLegend; + final LegendRenderer legedRenderer = + chartState._renderingDetails.legendRenderer; + final double legendHeight = chartLegend.legendSize.height; + if (chartLegend.isNeedScrollable) { widget = Container( height: needLegendTitle ? (legendHeight - titleHeight).abs() @@ -509,7 +529,7 @@ class _LegendContainer extends StatelessWidget { height: needLegendTitle ? (legendHeight - titleHeight).abs() : legendHeight, - width: chartState._chartLegend.legendSize.width, + width: chartLegend.legendSize.width, child: Wrap( direction: legedRenderer._orientation == LegendItemOrientation.horizontal @@ -520,7 +540,7 @@ class _LegendContainer extends StatelessWidget { if (needLegendTitle) { final ChartAlignment titleAlign = legend.title.alignment; final Color color = chart.legend.title.textStyle.color ?? - chartState._chartTheme.legendTitleColor; + renderingDetails.chartTheme.legendTitleColor; final double fontSize = chart.legend.title.textStyle.fontSize; final String fontFamily = chart.legend.title.textStyle.fontFamily; final FontStyle fontStyle = chart.legend.title.textStyle.fontStyle; diff --git a/packages/syncfusion_flutter_charts/lib/src/common/legend/renderer.dart b/packages/syncfusion_flutter_charts/lib/src/common/legend/renderer.dart index 1b49ef92d..0ae4a1866 100644 --- a/packages/syncfusion_flutter_charts/lib/src/common/legend/renderer.dart +++ b/packages/syncfusion_flutter_charts/lib/src/common/legend/renderer.dart @@ -51,12 +51,14 @@ class _LegendRenderer with _CustomizeLegend { @override void drawLegendItem(int index, _LegendRenderContext legendItem, Legend legend, dynamic chartState, Canvas canvas, Size size) { - final LegendRenderer legendRenderer = chartState._legendRenderer; + final LegendRenderer legendRenderer = + chartState._renderingDetails.legendRenderer; final dynamic chart = chartState._chart; final String legendText = legendItem.text; final List palette = chartState._chart.palette; TrendlineRenderer? trendlineRenderer; - Color color = legendItem.iconColor ?? palette[index % palette.length]; + Color color = legendItem.iconColor ?? + palette[legendItem.seriesIndex % palette.length]; color = legendRenderer._renderer.getLegendIconColor(index, legendItem, color); final Size textSize = legendItem.textSize!; @@ -71,8 +73,10 @@ class _LegendRenderer with _CustomizeLegend { ? !trendlineRenderer!._visible : legendItem.seriesRenderer is TechnicalIndicators ? !legendItem.indicatorRenderer!._visible! - : !legendItem.seriesRenderer._visible) - : !legendItem.point.isVisible; + : legendItem.seriesRenderer._visible == false) + : legendItem.point.isVisible == false; + final Color legendTextColor = + chartState._renderingDetails.chartTheme.legendTextColor; final TextStyle textStyle = legendItem.isSelect ? _getTextStyle( textStyle: legend.textStyle, @@ -80,8 +84,8 @@ class _LegendRenderer with _CustomizeLegend { fontColor: const Color.fromRGBO(211, 211, 211, 1)) : _getTextStyle( textStyle: legend.textStyle, - fontColor: legendRenderer._renderer.getLegendTextColor( - index, legendItem, chartState._chartTheme.legendTextColor)); + fontColor: legendRenderer._renderer + .getLegendTextColor(index, legendItem, legendTextColor)); canvas.clipRect(Rect.fromLTWH(0, 0, size.width, size.height)); _drawLegendShape( index, @@ -151,7 +155,8 @@ class _LegendRenderer with _CustomizeLegend { dynamic chartState) { final Path path = Path(); final LegendIconType actualIconType = iconType; - final LegendRenderer legendRenderer = chartState._legendRenderer; + final LegendRenderer legendRenderer = + chartState._renderingDetails.legendRenderer; PaintingStyle style = PaintingStyle.fill; final String seriesType = legendRenderContext.seriesRenderer is TechnicalIndicators @@ -184,7 +189,7 @@ class _LegendRenderer with _CustomizeLegend { style = _getPathAndStyle(iconType, style, path, location, width, height, legendRenderContext.seriesRenderer, chartState, canvas); // ignore: unnecessary_null_comparison - assert(legend.iconBorderWidth != null ? legend.iconBorderWidth >= 0 : true, + assert(!(legend.iconBorderWidth != null) || legend.iconBorderWidth >= 0, 'The icon border width of legend should not be less than 0.'); // ignore: unnecessary_null_comparison if (color != null) { @@ -309,7 +314,7 @@ class _LegendRenderer with _CustomizeLegend { (seriesType == 'line' || seriesType == 'fastline' || seriesType.contains('stackedline')) && - legendRenderContext.series.markerSettings.isVisible && + legendRenderContext.series.markerSettings.isVisible == true && legendRenderContext.series.markerSettings.shape != DataMarkerType.image ? _getIconType(legendRenderContext.series.markerSettings.shape) @@ -328,19 +333,23 @@ class _LegendRenderer with _CustomizeLegend { dynamic seriesRenderer, dynamic chartState, Canvas canvas) { - final double x = location.dx; - final double y = location.dy; + final double x = location.dx - + (iconType == LegendIconType.image ? 0 : width / 2).toDouble(); + final double y = location.dy - + (iconType == LegendIconType.image ? 0 : height / 2).toDouble(); + final Rect rect = Rect.fromLTWH(x, y, width, height); switch (iconType) { case LegendIconType.seriesType: - style = _calculateLegendShapes( - path, x, y, width, height, seriesRenderer._seriesType); + style = _calculateLegendShapes(path, rect, seriesRenderer._seriesType); break; case LegendIconType.circle: - ShapeMaker.drawCircle(path, x, y, width, height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.circle); break; case LegendIconType.rectangle: - ShapeMaker.drawRectangle(path, x, y, width, height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.rectangle); break; case LegendIconType.image: { @@ -365,27 +374,35 @@ class _LegendRenderer with _CustomizeLegend { break; } case LegendIconType.pentagon: - ShapeMaker.drawPentagon(path, x, y, width, height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.pentagon); break; case LegendIconType.verticalLine: - ShapeMaker.drawVerticalLine(path, x, y, width, height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.verticalLine); break; case LegendIconType.invertedTriangle: - ShapeMaker.drawInvertedTriangle(path, x, y, width, height); + ShapePainter.getShapesPath( + path: path, + rect: rect, + shapeType: ShapeMarkerType.invertedTriangle); break; case LegendIconType.horizontalLine: - ShapeMaker.drawHorizontalLine(path, x, y, width, height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.horizontalLine); break; case LegendIconType.diamond: - ShapeMaker.drawDiamond(path, x, y, width, height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.diamond); break; case LegendIconType.triangle: - ShapeMaker.drawTriangle(path, x, y, width, height); + ShapePainter.getShapesPath( + path: path, rect: rect, shapeType: ShapeMarkerType.triangle); break; } return style; @@ -407,6 +424,9 @@ class _LegendRenderer with _CustomizeLegend { Path path, Shader? shader) { final dynamic chart = chartState._chart; + final double x = location.dx - width / 2; + final double y = location.dy - height / 2; + final Rect rect = Rect.fromLTWH(x, y, width, height); if (seriesType.contains('line')) { if (iconType != LegendIconType.seriesType) { canvas.drawPath(path, fillPaint); @@ -417,8 +437,9 @@ class _LegendRenderer with _CustomizeLegend { final Paint paint = Paint() ..color = fillPaint.color.withOpacity(chart.legend.opacity) ..style = PaintingStyle.stroke - ..strokeWidth = - chart.legend.iconBorderWidth > 0 ? chart.legend.iconBorderWidth : 3; + ..strokeWidth = chart.legend.iconBorderWidth > 0 == true + ? chart.legend.iconBorderWidth + : 3; legendRenderContext.series.dashArray[0] != 0 ? canvas.drawPath( !kIsWeb @@ -428,75 +449,68 @@ class _LegendRenderer with _CustomizeLegend { paint) : canvas.drawPath(linePath, paint); } else if (seriesType == 'radialbar') { - final num radius = (width + height) / 2; + final double radius = (width + height) / 2; _drawPath( canvas, _StyleOptions( fill: Colors.grey[100]!, strokeWidth: fillPaint.strokeWidth, strokeColor: Colors.grey[300]!.withOpacity(0.5)), - _getArcPath( - (radius / 2) - 2, - radius / 2, - Offset(location.dx, location.dy), - 0, - 360 - 0.01, - 360 - 0.01, - chartState._chart, - true)); - const num pointStartAngle = -90; - num degree = + ShapePainter.getShapesPath( + rect: rect, + shapeType: ShapeMarkerType.radialBarSeries, + borderPaint: Paint(), + radius: radius)); + const double pointStartAngle = -90; + double degree = legendRenderContext.seriesRenderer._renderPoints[index].y.abs() / (legendRenderContext.series.maximumValue ?? - legendRenderContext.seriesRenderer._sumOfPoints); + legendRenderContext + .seriesRenderer._segmentRenderingValues['sumOfPoints']!); degree = (degree > 1 ? 1 : degree) * (360 - 0.001); - final num pointEndAngle = pointStartAngle + degree; + final double pointEndAngle = pointStartAngle + degree; _drawPath( canvas, _StyleOptions( fill: fillPaint.color, strokeWidth: fillPaint.strokeWidth, strokeColor: Colors.transparent), - _getArcPath( - (radius / 2) - 2, - radius / 2, - Offset(location.dx, location.dy), - pointStartAngle, - pointEndAngle, - degree, - chart, - true), + ShapePainter.getShapesPath( + rect: rect, + shapeType: ShapeMarkerType.radialBarSeries, + radius: radius, + startAngle: pointStartAngle, + degree: degree, + endAngle: pointEndAngle), null, - !legendRenderContext.isSelect ? shader ?? null : null); + !legendRenderContext.isSelect ? shader : null); } else { - final num radius = (width + height) / 2; + final double radius = (width + height) / 2; _drawPath( canvas, _StyleOptions( fill: fillPaint.color, strokeWidth: fillPaint.strokeWidth, strokeColor: Colors.grey[300]!.withOpacity(0.5)), - _getArcPath(radius / 4, radius / 2, Offset(location.dx, location.dy), - 0, 270, 270, chart, true), + ShapePainter.getShapesPath( + rect: rect, + shapeType: ShapeMarkerType.doughnutSeries, + borderPaint: Paint(), + radius: radius), null, - !legendRenderContext.isSelect ? shader ?? null : null); + !legendRenderContext.isSelect ? shader : null); _drawPath( canvas, _StyleOptions( fill: fillPaint.color, strokeWidth: fillPaint.strokeWidth, strokeColor: Colors.grey[300]!.withOpacity(0.5)), - _getArcPath( - radius / 4, - radius / 2, - Offset(location.dx + 1, location.dy - 1), - -5, - -85, - -85, - chart, - true), + ShapePainter.getShapesPath( + rect: rect, + shapeType: ShapeMarkerType.doughnutSeries, + radius: radius), null, - !legendRenderContext.isSelect ? shader ?? null : null); + !legendRenderContext.isSelect ? shader : null); } } } @@ -521,7 +535,7 @@ class _RenderLegend extends StatelessWidget { bool? isSelect; if (chart.legend.legendItemBuilder != null) { final _MeasureWidgetContext _measureWidgetContext = - chartState._legendWidgetContext[index]; + chartState._renderingDetails.legendWidgetContext[index]; isSelect = chart is SfCartesianChart ? chartState ._chartSeries @@ -533,13 +547,15 @@ class _RenderLegend extends StatelessWidget { ._renderPoints[_measureWidgetContext.pointIndex] .isVisible; } + final _ChartLegend chartLegend = chartState._renderingDetails.chartLegend; + final LegendRenderer legendRenderer = + chartState._renderingDetails.legendRenderer; return Container( height: size.height, - width: chartState._legendRenderer._orientation == - LegendItemOrientation.vertical && + width: legendRenderer._orientation == LegendItemOrientation.vertical && (chart.legend.overflowMode == LegendItemOverflowMode.scroll || chart.legend.overflowMode == LegendItemOverflowMode.none) - ? chartState._chartLegend.legendSize.width + ? chartLegend.legendSize.width : size.width, child: HandCursor( child: GestureDetector( @@ -558,16 +574,16 @@ class _RenderLegend extends StatelessWidget { painter: _ChartLegendPainter( chartState: chartState, legendIndex: index, - isSelect: chartState - ._chartLegend.legendCollections[index].isSelect, - notifier: chartState - ._chartLegend.legendRepaintNotifier))))); + isSelect: + chartLegend.legendCollections![index].isSelect, + notifier: chartLegend.legendRepaintNotifier))))); } /// To process chart on circular chart legend toggle void _processCircularPointsToggle() { LegendTapArgs legendTapArgs; const int seriesIndex = 0; + final _ChartLegend chartLegend = chartState._renderingDetails.chartLegend; if (chart.onLegendTapped != null) { if (chart != null) { legendTapArgs = LegendTapArgs(chart.series, seriesIndex, index); @@ -577,15 +593,15 @@ class _RenderLegend extends StatelessWidget { } chart.onLegendTapped(legendTapArgs); } - if (chart.legend.toggleSeriesVisibility) { + if (chart.legend.toggleSeriesVisibility == true) { if (chart.legend.legendItemBuilder != null) { - _legendToggleTemplateState( - chartState._legendWidgetContext[index], chartState, ''); + final _MeasureWidgetContext legendWidgetContext = + chartState._renderingDetails.legendWidgetContext[index]; + _legendToggleTemplateState(legendWidgetContext, chartState, ''); } else { - _legendToggleState( - chartState._chartLegend.legendCollections[index], chartState); + _legendToggleState(chartLegend.legendCollections![index], chartState); } - chartState._isLegendToggled = true; + chartState._renderingDetails.isLegendToggled = true; chartState._redraw(); } } @@ -597,28 +613,34 @@ class _RenderLegend extends StatelessWidget { _LegendRenderContext _legendRenderContext; if (chart.onLegendTapped != null) { if (chart.legend.legendItemBuilder != null) { - _measureWidgetContext = chartState._legendWidgetContext[index]; + _measureWidgetContext = + chartState._renderingDetails.legendWidgetContext[index]; legendTapArgs = LegendTapArgs( chartState._chartSeries .visibleSeriesRenderers[_measureWidgetContext.seriesIndex], _measureWidgetContext.seriesIndex!, 0); } else { - _legendRenderContext = chartState._chartLegend.legendCollections[index]; + _legendRenderContext = + chartState._renderingDetails.chartLegend.legendCollections[index]; legendTapArgs = LegendTapArgs( _legendRenderContext.series, _legendRenderContext.seriesIndex, 0); } chart.onLegendTapped(legendTapArgs); } - if (chart.legend.toggleSeriesVisibility) { + if (chart.legend.toggleSeriesVisibility == true) { if (chart.legend.legendItemBuilder != null) { _legendToggleTemplateState( - chartState._legendWidgetContext[index], chartState, ''); + chartState._renderingDetails.legendWidgetContext[index], + chartState, + ''); } else { _cartesianLegendToggleState( - chartState._chartLegend.legendCollections[index], chartState); + chartState._renderingDetails.chartLegend.legendCollections[index], + chartState); } - chartState._isLegendToggled = true; + chartState._renderingDetails.isLegendToggled = true; + chartState._renderingDetails.isImageDrawn = false; chartState._legendToggling = true; chartState._redraw(); } @@ -636,12 +658,13 @@ class _ChartLegendStylePainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { final Legend legend = chart.legend; + final Color legendBackgroundColor = + chartState._renderingDetails.chartTheme.legendBackgroundColor; if (legend.backgroundColor != null) { canvas.drawRect( Rect.fromLTWH(0, 0, size.width, size.height), Paint() - ..color = legend.backgroundColor ?? - chartState._chartTheme.legendBackgroundColor + ..color = legend.backgroundColor ?? legendBackgroundColor ..style = PaintingStyle.fill); } // ignore: unnecessary_null_comparison @@ -679,9 +702,10 @@ class _ChartLegendPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { final Legend legend = chart.legend; - final LegendRenderer legendRenderer = chartState._legendRenderer; + final LegendRenderer legendRenderer = + chartState._renderingDetails.legendRenderer; final _LegendRenderContext legendRenderContext = - chartState._chartLegend.legendCollections[legendIndex]; + chartState._renderingDetails.chartLegend.legendCollections[legendIndex]; legendRenderer._renderer.drawLegendItem( legendIndex, legendRenderContext, legend, chartState, canvas, size); } diff --git a/packages/syncfusion_flutter_charts/lib/src/common/rendering_details.dart b/packages/syncfusion_flutter_charts/lib/src/common/rendering_details.dart new file mode 100644 index 000000000..4a61a629a --- /dev/null +++ b/packages/syncfusion_flutter_charts/lib/src/common/rendering_details.dart @@ -0,0 +1,94 @@ +part of charts; + +/// Represents the rendering details of the chart +class _RenderingDetails { + /// Specifies the animation controller of chart + late AnimationController animationController; + + /// Specifies the animation controller of chart annotation controller + late AnimationController annotationController; + + /// Specifies the series repaint notifier + late ValueNotifier seriesRepaintNotifier; + + /// Specifies the chart element animatioon + late Animation chartElementAnimation; + + /// Specifies the context for legend + late List<_MeasureWidgetContext> legendWidgetContext; + + /// Specifies the context for legend toggle template + late List<_MeasureWidgetContext> legendToggleTemplateStates; + + /// Specifies the legend toggle states + late List<_LegendRenderContext> legendToggleStates; + + /// Specifies whether the legend is toggled + late bool isLegendToggled; + + /// Specifies the chart legend + late _ChartLegend chartLegend; + + /// Specifies the chart legend renderer + late LegendRenderer legendRenderer; + + /// Specifies the tooltip behavior renderer + late TooltipBehaviorRenderer tooltipBehaviorRenderer; + + /// Specifies the chart interaction + _ChartInteraction? currentActive; + + /// Specifies the tap position + Offset? tapPosition; + + /// Specifies the list of selection data + late List selectionData; + + /// Specifies the chart container rect + late Rect chartContainerRect; + + /// Specifies the chart area rect + late Rect chartAreaRect; + + /// Specifies the data label template region + late List dataLabelTemplateRegions; + + /// Specifies the list of template info + late List<_ChartTemplateInfo> templates; + + /// Specifies the chart template + _ChartTemplate? chartTemplate; + + /// Specifies the chart theme + late SfChartThemeData chartTheme; + + /// Specifies the exploded points + late List explodedPoints; + + /// Specifies whether the chart is rendered at load time + bool? initialRender; + + /// Specifies whether the animation is completed + late bool animateCompleted; + + /// Specifies whether the widget needs to updated + late bool widgetNeedUpdate; + + /// Specifies the previous orientation of the device + Orientation? oldDeviceOrientation; + + /// Specifies the current device orientation + late Orientation deviceOrientation; + + /// Specifies the previous chart size + Size? prevSize; + + /// Specifies whether the current chart size is changed + late bool didSizeChange; + + /// Specifies the list of chart widget + List? chartWidgets; + + /// Specifies the image drawn in the marker or not. + bool isImageDrawn = false; +} diff --git a/packages/syncfusion_flutter_charts/lib/src/common/series/chart_series.dart b/packages/syncfusion_flutter_charts/lib/src/common/series/chart_series.dart index 464259fce..71aebaa8e 100644 --- a/packages/syncfusion_flutter_charts/lib/src/common/series/chart_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/common/series/chart_series.dart @@ -24,7 +24,6 @@ class ChartSeries { this.animationDuration, this.borderColor, this.borderWidth, - this.selectionSettings, this.selectionBehavior, this.legendItemText, this.legendIconType, @@ -355,28 +354,6 @@ class ChartSeries { ///``` final LegendIconType? legendIconType; - ///Customizes the data points or series on selection. - /// - ///```dart - ///Widget build(BuildContext context) { - /// return Container( - /// child: SfCartesianChart( - /// series: >[ - /// BarSeries( - /// selectionSettings: SelectionSettings( - /// selectedColor: Colors.red, - /// unselectedColor: Colors.grey, - /// selectedOpacity : 0.8, - /// unselectedOpacity: 0.4 - /// ), - /// ), - /// ], - /// )); - ///} - ///``` - // ignore: deprecated_member_use_from_same_package - final SelectionSettings? selectionSettings; - ///Customizes the data points or series on selection. /// ///```dart diff --git a/packages/syncfusion_flutter_charts/lib/src/common/template/rendering.dart b/packages/syncfusion_flutter_charts/lib/src/common/template/rendering.dart index 50eb80298..127ac22b5 100644 --- a/packages/syncfusion_flutter_charts/lib/src/common/template/rendering.dart +++ b/packages/syncfusion_flutter_charts/lib/src/common/template/rendering.dart @@ -55,11 +55,13 @@ class _RenderTemplateState extends State<_RenderTemplate> ? widget.chartState._chartSeries .visibleSeriesRenderers[templateInfo.seriesIndex] : null; - final bool needsAnimate = widget.chartState._oldDeviceOrientation == - MediaQuery.of(context).orientation && - ((seriesRenderer != null && seriesRenderer is CartesianSeriesRenderer) - ? seriesRenderer._needAnimateSeriesElements - : true); + final Orientation? orientation = + widget.chartState._renderingDetails.oldDeviceOrientation; + final bool needsAnimate = + orientation == MediaQuery.of(context).orientation && + (!(seriesRenderer != null && + seriesRenderer is CartesianSeriesRenderer) || + seriesRenderer._needAnimateSeriesElements); animationController = AnimationController( duration: Duration(milliseconds: widget.template.animationDuration), vsync: this); @@ -118,7 +120,7 @@ class _ChartTemplateRenderObject extends SingleChildRenderObjectWidget { @override void updateRenderObject( BuildContext context, covariant _ChartTemplateRenderBox renderBox) { - renderBox..templateInfo = templateInfo; + renderBox.templateInfo = templateInfo; } } @@ -153,8 +155,6 @@ class _ChartTemplateRenderBox extends RenderShiftedBox { locationX = _templateInfo.location.dx; locationY = _templateInfo.location.dy; - /// Here we have added 5px padding to each templates - const int padding = 5; child!.layout(constraints, parentUsesSize: true); size = constraints.constrain(Size(child!.size.width, child!.size.height)); if (child!.parentData is BoxParentData) { @@ -170,9 +170,7 @@ class _ChartTemplateRenderBox extends RenderShiftedBox { (_templateInfo.verticalAlignment == ChartAlignment.near ? 0 : _templateInfo.verticalAlignment == ChartAlignment.center - ? _templateInfo.templateType == 'DataLabel' - ? child!.size.height + padding - : child!.size.height / 2 + ? child!.size.height / 2 : child!.size.height); if (_templateInfo.templateType == 'DataLabel' && _chartState is SfCartesianChartState) { @@ -183,26 +181,30 @@ class _ChartTemplateRenderBox extends RenderShiftedBox { seriesRenderer._series.dataLabelSettings); final CartesianChartPoint? point = seriesRenderer._dataPoints != null - ? (seriesRenderer._dataPoints.isNotEmpty) + ? (seriesRenderer._dataPoints.isNotEmpty == true) ? seriesRenderer._dataPoints[_templateInfo.pointIndex] : null : null; - if (seriesRenderer._isRectSeries || - seriesRenderer._seriesType.contains('hilo') || - seriesRenderer._seriesType.contains('candle') || - seriesRenderer._seriesType.contains('box')) { - seriesRenderer.sideBySideInfo = + if (seriesRenderer._isRectSeries == true || + seriesRenderer._seriesType.contains('hilo') == true || + seriesRenderer._seriesType.contains('candle') == true || + seriesRenderer._seriesType.contains('box') == true) { + seriesRenderer._sideBySideInfo = _calculateSideBySideInfo(seriesRenderer, _chartState); } seriesRenderer._hasDataLabelTemplate = true; - if (seriesRenderer._seriesType.contains('spline')) { + if (seriesRenderer._seriesType.contains('spline') == true) { + if (seriesRenderer._drawControlPoints.isNotEmpty == true) { + seriesRenderer._drawControlPoints.clear(); + } _calculateSplineAreaControlPoints(seriesRenderer); } if (point != null && seriesRenderer._seriesType != 'boxandwhisker') { if (point.region == null) { if (seriesRenderer._visibleDataPoints == null || seriesRenderer._visibleDataPoints.length >= - seriesRenderer._dataPoints.length) { + seriesRenderer._dataPoints.length == + true) { seriesRenderer._visibleDataPoints = >[]; } @@ -225,14 +227,15 @@ class _ChartTemplateRenderBox extends RenderShiftedBox { } final Rect rect = Rect.fromLTWH(locationX, locationY, size.width, size.height); - final bool isCollide = (_templateInfo.templateType == 'DataLabel') - ? _findingCollision(rect, _chartState._dataLabelTemplateRegions) - : false; + final List dataLabelTemplateRegions = + _chartState._renderingDetails.dataLabelTemplateRegions; + final bool isCollide = (_templateInfo.templateType == 'DataLabel') && + _findingCollision(rect, dataLabelTemplateRegions); if (!isCollide && _isTemplateWithinBounds(_templateInfo.clipRect, rect) && isLabelWithInRange) { (_templateInfo.templateType == 'DataLabel') - ? _chartState._dataLabelTemplateRegions.add(rect) + ? dataLabelTemplateRegions.add(rect) : _chartState._annotationRegions.add(rect); childParentData.offset = Offset(locationX, locationY); } else { @@ -284,7 +287,9 @@ class _ChartTemplateState extends State<_ChartTemplate> { Widget build(BuildContext context) { widget.state = this; Widget renderTemplate = Container(); - if (widget.chartState._animateCompleted && widget.templates.isNotEmpty) { + final bool animationCompleted = + widget.chartState._renderingDetails.animateCompleted; + if (animationCompleted && widget.templates.isNotEmpty) { final List renderWidgets = []; for (int i = 0; i < widget.templates.length; i++) { renderWidgets.add(_RenderTemplate( @@ -299,11 +304,6 @@ class _ChartTemplateState extends State<_ChartTemplate> { return renderTemplate; } - @override - void dispose() { - super.dispose(); - } - void templateRender() { if (mounted) { setState(() { diff --git a/packages/syncfusion_flutter_charts/lib/src/common/user_interaction/selection.dart b/packages/syncfusion_flutter_charts/lib/src/common/user_interaction/selection.dart deleted file mode 100644 index 35c3ab85c..000000000 --- a/packages/syncfusion_flutter_charts/lib/src/common/user_interaction/selection.dart +++ /dev/null @@ -1,272 +0,0 @@ -part of charts; - -@Deprecated('Use SelectionBehavior instead. ' - 'This feature was deprecated from next release onwards') - -///Provides options for the selection of series or data points. -/// -///By using this class, The color and width of the selected and unselected series or data points can be customized. -class SelectionSettings { - /// Creating an argument constructor of SelectionSettings class. - SelectionSettings( - {bool? enable, - this.selectedColor, - this.selectedBorderColor, - this.selectedBorderWidth, - this.unselectedColor, - this.unselectedBorderColor, - this.unselectedBorderWidth, - double? selectedOpacity, - double? unselectedOpacity, - this.selectionController}) - : enable = enable ?? false, - selectedOpacity = selectedOpacity ?? 1.0, - unselectedOpacity = unselectedOpacity ?? 0.5; - - ///Enables or disables the selection. - /// - ///By enabling this, each data point or series in the chart can be selected. - /// - ///Defaults to `false`. - /// - ///```dart - ///Widget build(BuildContext context) { - /// return Container( - /// child: SfCartesianChart( - /// series: >[ - /// BarSeries( - /// selectionSettings: SelectionSettings( - /// enable: true - /// ), - /// ), - /// ], - /// )); - ///} - ///``` - final bool enable; - - ///Color of the selected data points or series. - /// - ///```dart - ///Widget build(BuildContext context) { - /// return Container( - /// child: SfCartesianChart( - /// series: >[ - /// BarSeries( - /// selectionSettings: SelectionSettings( - /// selectedColor: Colors.red - /// ), - /// ), - /// ], - /// )); - ///} - ///``` - final Color? selectedColor; - - ///Border color of the selected data points or series. - ///```dart - ///Widget build(BuildContext context) { - /// return Container( - /// child: SfCartesianChart( - /// series: >[ - /// BarSeries( - /// selectionSettings: SelectionSettings( - /// selectedBorderColor: Colors.red, - /// ), - /// ), - /// ], - /// )); - ///} - ///``` - final Color? selectedBorderColor; - - ///Border width of the selected data points or series. - /// - ///```dart - ///Widget build(BuildContext context) { - /// return Container( - /// child: SfCartesianChart( - /// series: >[ - /// BarSeries( - /// selectionSettings: SelectionSettings( - /// selectedColor: Colors.red, - /// selectedBorderWidth: 2 - /// ), - /// ), - /// ], - /// )); - ///} - ///``` - final double? selectedBorderWidth; - - ///Color of the unselected data points or series. - /// - ///```dart - ///Widget build(BuildContext context) { - /// return Container( - /// child: SfCartesianChart( - /// series: >[ - /// BarSeries( - /// selectionSettings: SelectionSettings( - /// unselectedColor: Colors.grey, - /// ), - /// ), - /// ], - /// )); - ///} - ///``` - final Color? unselectedColor; - - ///Border color of the unselected data points or series. - /// - ///```dart - ///Widget build(BuildContext context) { - /// return Container( - /// child: SfCartesianChart( - /// series: >[ - /// BarSeries( - /// selectionSettings: SelectionSettings( - /// unselectedBorderColor: Colors.grey, - /// ), - /// ), - /// ], - /// )); - ///} - ///``` - final Color? unselectedBorderColor; - - ///Border width of the unselected data points or series. - /// - ///```dart - ///Widget build(BuildContext context) { - /// return Container( - /// child: SfCartesianChart( - /// series: >[ - /// BarSeries( - /// selectionSettings: SelectionSettings( - /// unselectedBorderWidth: 2 - /// ), - /// ), - /// ], - /// )); - ///} - ///``` - final double? unselectedBorderWidth; - - ///Opacity of the selected series or data point. - /// - ///Default to `1.0`. - /// - ///```dart - ///Widget build(BuildContext context) { - /// return Container( - /// child: SfCartesianChart( - /// series: >[ - /// BarSeries( - /// selectionSettings: SelectionSettings( - /// selectedOpacity: 0.5, - /// ), - /// ), - /// ], - /// )); - ///} - ///``` - final double selectedOpacity; - - ///Opacity of the unselected series or data point. - /// - ///Defaults to `0.5`. - /// - ///```dart - ///Widget build(BuildContext context) { - /// return Container( - /// child: SfCartesianChart( - /// series: >[ - /// BarSeries( - /// selectionSettings: SelectionSettings( - /// unselectedOpacity: 0.4, - /// ), - /// ), - /// ], - /// )); - ///} - ///`` - final double unselectedOpacity; - - /// Controller used to set the maximum and minimum values for the chart.By providing the selection controller, the maximum and - ///The minimum range of selected series or points can be customized - /// - ///```dart - ///Widget build(BuildContext context) { - /// return Container( - /// child: SfCartesianChart( - /// selectionSettings: SelectionSettings( - /// selectionController: controller, - /// ), - /// )); - ///} - ///``` - final RangeController? selectionController; - - dynamic _chartState; - - ////Selects or deselects the specified data point in the series. - /// - ///The following are the arguments to be passed. - ///* `pointIndex` - index of the data point that needs to be selected. - ///* `seriesIndex` - index of the series in which the data point is selected. - /// - ///Where the `pointIndex` is a required argument and `seriesIndex` is an optional argument. By default, 0 will - /// be considered as the series index. Thus it will take effect on the first series if no value is specified. - /// - ///For Circular, Pyramid and Funnel charts, seriesIndex should always be 0, as it has only one series. - /// - ///If the specified data point is already selected, it will be deselected, else it will be selected. - /// Selection type and multi-selection functionality is also applicable for this, but it is based on - /// the API values specified in [ChartSelectionBehavior]. - /// - ///_Note:_ - Even though, the [enable] property in [ChartSelectionBehavior] is set to false, this method - /// will work. - /// ```dart - /// Widget build(BuildContext context) { - /// selection = SelectionSettings(enable: true); - /// chart = SfCartesianChart(series:getChartData); - /// return Scaffold( - /// child: Column( - /// children: [ - /// FlatButton( - /// child: Text('Select'), - /// onPressed: select), - /// Container(child: chart) - /// ])); - ///} - /// void select() { - /// selection.selectionIndex(1, 0); - ///} - ///```dart - - void selectDataPoints(int pointIndex, [int seriesIndex = 0]) { - final dynamic seriesRenderer = - _chartState._chartSeries.visibleSeriesRenderers[seriesIndex]; - final SelectionBehaviorRenderer selectionBehaviorRenderer = - seriesRenderer._selectionBehaviorRenderer; - selectionBehaviorRenderer._selectionRenderer - ?.selectDataPoints(pointIndex, seriesIndex); - } - - /// provides the list of selected point indices for given series. - List getSelectedDataPoints(CartesianSeries _series) { - List selectedItems = []; - final dynamic seriesRenderer = - _chartState._chartSeries.visibleSeriesRenderers[0]; - final SelectionBehaviorRenderer selectionBehaviorRenderer = - seriesRenderer._selectionBehaviorRenderer; - final List selectedPoints = []; - selectedItems = - selectionBehaviorRenderer._selectionRenderer!.selectedSegments; - for (int i = 0; i < selectedItems.length; i++) { - selectedPoints.add(selectedItems[i].currentSegmentIndex!); - } - return selectedPoints; - } -} diff --git a/packages/syncfusion_flutter_charts/lib/src/common/user_interaction/selection_behavior.dart b/packages/syncfusion_flutter_charts/lib/src/common/user_interaction/selection_behavior.dart index 1ea703191..d4853c97c 100644 --- a/packages/syncfusion_flutter_charts/lib/src/common/user_interaction/selection_behavior.dart +++ b/packages/syncfusion_flutter_charts/lib/src/common/user_interaction/selection_behavior.dart @@ -15,10 +15,12 @@ class SelectionBehavior { this.unselectedBorderWidth, double? selectedOpacity, double? unselectedOpacity, - this.selectionController}) + this.selectionController, + bool? toggleSelection}) : enable = enable ?? false, selectedOpacity = selectedOpacity ?? 1.0, - unselectedOpacity = unselectedOpacity ?? 0.5; + unselectedOpacity = unselectedOpacity ?? 0.5, + toggleSelection = toggleSelection ?? true; ///Enables or disables the selection. /// @@ -205,6 +207,73 @@ class SelectionBehavior { ///``` final RangeController? selectionController; + ///Decides whether to deselect the selected item or not. + /// + ///Provides an option to decide, whether to deselect the selected data point/series + /// or remain selected, when interacted with it again. + /// + ///If set to true, deselection will be performed else the point will not get deselected. + /// This works even while calling public methods, in various selection modes, with + /// multi-selection, and also on dynamic changes. + /// + ///Defaults to `true`. + /// + ///```dart + ///Widget build(BuildContext context) { + /// return Container( + /// child: SfCartesianChart( + /// series: >[ + /// BarSeries( + /// selectionBehavior: SelectionBehavior( + /// enable: true, + /// toggleSelection: false, + /// ), + /// ), + /// ], + /// )); + ///} + ///``` + final bool toggleSelection; + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is SelectionBehavior && + other.enable == enable && + other.selectedColor == selectedColor && + other.selectedBorderColor == selectedBorderColor && + other.selectedBorderWidth == selectedBorderWidth && + other.unselectedColor == unselectedColor && + other.unselectedBorderColor == unselectedBorderColor && + other.unselectedBorderWidth == unselectedBorderWidth && + other.selectedOpacity == selectedOpacity && + other.unselectedOpacity == unselectedOpacity && + other.selectionController == selectionController; + } + + @override + int get hashCode { + final List values = [ + enable, + selectedColor, + selectedBorderColor, + selectedBorderWidth, + unselectedColor, + unselectedBorderColor, + unselectedBorderWidth, + selectedOpacity, + unselectedOpacity, + selectionController + ]; + return hashList(values); + } + dynamic _chartState; ////Selects or deselects the specified data point in the series. @@ -222,11 +291,11 @@ class SelectionBehavior { /// Selection type and multi-selection functionality is also applicable for this, but it is based on /// the API values specified in [ChartSelectionBehavior]. /// - ///_Note:_ - Even though, the [enable] property in [ChartSelectionBehavior] is set to false, this method + ///_Note:_ Even though, the [enable] property in [ChartSelectionBehavior] is set to false, this method /// will work. /// ```dart /// Widget build(BuildContext context) { - /// selection = SelectionSettings(enable: true); + /// selection = SelectionBehavior(enable: true); /// chart = SfCartesianChart(series:getChartData); /// return Scaffold( /// child: Column( @@ -246,9 +315,8 @@ class SelectionBehavior { final dynamic seriesRenderer = _chartState._chartSeries.visibleSeriesRenderers[seriesIndex]; assert( - seriesRenderer._chartState! is SfCartesianChartState - ? _getVisibleDataPointIndex(pointIndex, seriesRenderer) != null - : true, + seriesRenderer._chartState! is SfCartesianChartState == false || + _getVisibleDataPointIndex(pointIndex, seriesRenderer) != null, 'Provided point index is not in the visible range. Provide point index which is in the visible range.'); final SelectionBehaviorRenderer selectionBehaviorRenderer = seriesRenderer._selectionBehaviorRenderer; @@ -275,7 +343,7 @@ class SelectionBehavior { /// Selection renderer class for mutable fields and methods class SelectionBehaviorRenderer with ChartSelectionBehavior { - /// Creates an argument constructor for SelectionSettings renderer class + /// Creates an argument constructor for SelectionBehavior renderer class SelectionBehaviorRenderer( this._selectionBehavior, this._chart, this._sfChartState); @@ -283,7 +351,6 @@ class SelectionBehaviorRenderer with ChartSelectionBehavior { final dynamic _sfChartState; - // ignore: deprecated_member_use_from_same_package final dynamic _selectionBehavior; _SelectionRenderer? _selectionRenderer; @@ -294,7 +361,7 @@ class SelectionBehaviorRenderer with ChartSelectionBehavior { final CartesianSeriesRenderer seriesRenderer = _selectionRenderer!.seriesRenderer; final SfCartesianChartState chartState = _selectionRenderer!._chartState; - if (_selectionBehavior.enable && + if (_selectionBehavior.enable == true && _selectionBehavior.selectionController != null) { _selectionBehavior.selectionController.addListener(() { chartState._isRangeSelectionSlider = true; @@ -326,7 +393,7 @@ class SelectionBehaviorRenderer with ChartSelectionBehavior { } }); } - if (chartState._initialRender!) { + if (chartState._renderingDetails.initialRender!) { chartState._isRangeSelectionSlider = false; } _selectionRenderer!._chartState = chartState; diff --git a/packages/syncfusion_flutter_charts/lib/src/common/user_interaction/tooltip.dart b/packages/syncfusion_flutter_charts/lib/src/common/user_interaction/tooltip.dart index c0b295491..e4ece77bb 100644 --- a/packages/syncfusion_flutter_charts/lib/src/common/user_interaction/tooltip.dart +++ b/packages/syncfusion_flutter_charts/lib/src/common/user_interaction/tooltip.dart @@ -353,6 +353,65 @@ class TooltipBehavior { ///``` final bool shared; + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is TooltipBehavior && + other.textStyle == textStyle && + other.activationMode == activationMode && + other.animationDuration == animationDuration && + other.enable == enable && + other.opacity == opacity && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.duration == duration && + other.shouldAlwaysShow == shouldAlwaysShow && + other.elevation == elevation && + other.canShowMarker == canShowMarker && + other.textAlignment == textAlignment && + other.decimalPlaces == decimalPlaces && + other.tooltipPosition == tooltipPosition && + other.shared == shared && + other.color == color && + other.header == header && + other.format == format && + other.builder == builder && + other.shadowColor == shadowColor; + } + + @override + int get hashCode { + final List values = [ + textStyle, + activationMode, + animationDuration, + enable, + opacity, + borderColor, + borderWidth, + duration, + shouldAlwaysShow, + elevation, + canShowMarker, + textAlignment, + decimalPlaces, + tooltipPosition, + shared, + color, + header, + format, + builder, + shadowColor + ]; + return hashList(values); + } + dynamic _chartState; /// Displays the tooltip at the specified x and y-positions. @@ -367,7 +426,7 @@ class TooltipBehavior { final dynamic chartState = _chartState; final dynamic chart = chartState._chart; final TooltipBehaviorRenderer tooltipBehaviorRenderer = - _chartState._tooltipBehaviorRenderer; + _chartState._renderingDetails.tooltipBehaviorRenderer; bool? isInsidePointRegion; String text = ''; String trimmedText = ''; @@ -381,14 +440,13 @@ class TooltipBehavior { _chartState._chartAxis._axisRenderersCollection[i]._visibleLabels; for (int k = 0; k < labels.length; k++) { if (_chartState - ._chartAxis._axisRenderersCollection[i]._axis.isVisible && + ._chartAxis._axisRenderersCollection[i]._axis.isVisible == + true && labels[k]._labelRegion != null && labels[k]._labelRegion!.contains(Offset(x, y))) { _chartState._requireAxisTooltip = true; text = labels[k].text; trimmedText = labels[k].renderText ?? ''; - tooltipBehaviorRenderer._prevTooltipValue = - tooltipBehaviorRenderer._currentTooltipValue; axisLabelPosition = labels[k]._labelRegion!.center; // -3 to indicte axis tooltip tooltipBehaviorRenderer._currentTooltipValue = @@ -397,7 +455,7 @@ class TooltipBehavior { } } } - if (chart is SfCartesianChart && !_chartState._requireAxisTooltip) { + if (chart is SfCartesianChart && _chartState._requireAxisTooltip == false) { for (int i = 0; i < _chartState._chartSeries.visibleSeriesRenderers.length; i++) { @@ -412,7 +470,9 @@ class TooltipBehavior { seriesType.contains('column') || seriesType.contains('bar')) ? 0 - : _chartState._tooltipBehaviorRenderer._isHovering + : _chartState._renderingDetails.tooltipBehaviorRenderer + ._isHovering == + true ? 0 : 15; // regional padding to detect smooth touch seriesRenderer._regionalData! @@ -450,7 +510,7 @@ class TooltipBehavior { x != null && // ignore: unnecessary_null_comparison y != null && - _chartState._requireAxisTooltip) { + _chartState._requireAxisTooltip == true) { final SfTooltipState? tooltipState = tooltipBehaviorRenderer._chartTooltipState; if (trimmedText.contains('...')) { @@ -460,7 +520,10 @@ class TooltipBehavior { axisLabelPosition!, chart, text); } else { tooltipBehaviorRenderer._show = false; - hide(); + if (!shouldAlwaysShow) { + tooltipBehaviorRenderer._chartTooltipState + ?.hide(hideDelay: duration.toInt()); + } } } else if (tooltipBehaviorRenderer._chartTooltip != null && activationMode != ActivationMode.none && @@ -470,17 +533,20 @@ class TooltipBehavior { y != null) { final SfTooltipState? tooltipState = tooltipBehaviorRenderer._chartTooltipState; - if (!(chart is SfCartesianChart) || + if ((chart is SfCartesianChart) == false || tooltipBehaviorRenderer._isInteraction || (isInsidePointRegion ?? false)) { - tooltipState?.needMarker = chart is SfCartesianChart; + final bool isHovering = + _chartState._renderingDetails.tooltipBehaviorRenderer._isHovering; if (isInsidePointRegion == true || - _chartState._tooltipBehaviorRenderer._isHovering || - !(chart is SfCartesianChart)) { + isHovering || + (chart is SfCartesianChart) == false) { tooltipBehaviorRenderer._showTooltip(x, y); } else { tooltipBehaviorRenderer._show = false; - hide(); + if (chart.tooltipBehavior.shouldAlwaysShow == false) { + hide(); + } } } else if (tooltipBehaviorRenderer._renderBox != null) { tooltipBehaviorRenderer._show = true; @@ -519,8 +585,9 @@ class TooltipBehavior { void show(dynamic x, double y, [String? xAxisName, String? yAxisName]) { if (_chartState._chart is SfCartesianChart) { final dynamic chart = _chartState._chart; + final _RenderingDetails renderingDetails = _chartState._renderingDetails; final TooltipBehaviorRenderer tooltipBehaviorRenderer = - _chartState._tooltipBehaviorRenderer; + renderingDetails.tooltipBehaviorRenderer; bool? isInsidePointRegion = false; ChartAxisRenderer? xAxisRenderer, yAxisRenderer; if (xAxisName != null && yAxisName != null) { @@ -537,7 +604,8 @@ class TooltipBehavior { yAxisRenderer = _chartState._chartAxis._primaryYAxisRenderer; } final _ChartLocation position = _calculatePoint( - (x is DateTime && !(xAxisRenderer! is DateTimeCategoryAxisRenderer)) + (x is DateTime && + (xAxisRenderer! is DateTimeCategoryAxisRenderer) == false) ? x.millisecondsSinceEpoch : ((x is DateTime && xAxisRenderer! is DateTimeCategoryAxisRenderer) @@ -581,17 +649,16 @@ class TooltipBehavior { }); } } - if (_chartState._tooltipBehaviorRenderer._tooltipTemplate == null) { + if (renderingDetails.tooltipBehaviorRenderer._tooltipTemplate == null) { final SfTooltipState? tooltipState = tooltipBehaviorRenderer._chartTooltipState; if (isInsidePointRegion ?? false) { - tooltipState?.needMarker = chart is SfCartesianChart; tooltipBehaviorRenderer._showTooltip(position.x, position.y); } else { //to show tooltip when the position is out of point region tooltipBehaviorRenderer._show = true; tooltipState?.needMarker = false; - _chartState._tooltipBehaviorRenderer._showChartAreaTooltip( + renderingDetails.tooltipBehaviorRenderer._showChartAreaTooltip( Offset(position.x, position.y), xAxisRenderer, yAxisRenderer, @@ -611,7 +678,7 @@ class TooltipBehavior { final dynamic chartState = _chartState; final dynamic chart = chartState._chart; final TooltipBehaviorRenderer tooltipBehaviorRenderer = - _chartState._tooltipBehaviorRenderer; + _chartState._renderingDetails.tooltipBehaviorRenderer; dynamic x, y; if (chart is SfCartesianChart) { if (_validIndex(pointIndex, seriesIndex, chart)) { @@ -640,7 +707,7 @@ class TooltipBehavior { _chartState._circularArea ._showCircularTooltipTemplate(seriesIndex, pointIndex); } else if (chart.tooltipBehavior.builder == null && - chartState._animationCompleted && + chartState._animationCompleted == true && pointIndex >= 0 && (pointIndex + 1 <= _chartState._chartSeries.visibleSeriesRenderers[seriesIndex] @@ -670,9 +737,10 @@ class TooltipBehavior { y = position?.dy; tooltipBehaviorRenderer._showTooltip(x, y); } else { - if (chart is SfFunnelChart && chartState._animationCompleted) { + if (chart is SfFunnelChart && chartState._animationCompleted == true) { _chartState._funnelplotArea._showFunnelTooltipTemplate(pointIndex); - } else if (chart is SfPyramidChart && chartState._animationCompleted) { + } else if (chart is SfPyramidChart && + chartState._animationCompleted == true) { _chartState._chartPlotArea._showPyramidTooltipTemplate(pointIndex); } } @@ -683,7 +751,7 @@ class TooltipBehavior { /// Hides the tooltip if it is displayed. void hide() { final TooltipBehaviorRenderer tooltipBehaviorRenderer = - _chartState._tooltipBehaviorRenderer; + _chartState._renderingDetails.tooltipBehaviorRenderer; // ignore: unnecessary_null_comparison if (tooltipBehaviorRenderer != null) { tooltipBehaviorRenderer._showLocation = null; @@ -691,16 +759,13 @@ class TooltipBehavior { } if (builder != null) { // hides tooltip template - tooltipBehaviorRenderer._chartTooltipState?.widget.hide(); + tooltipBehaviorRenderer._chartTooltipState?.hide(hideDelay: 0); } else { //hides default tooltip tooltipBehaviorRenderer._currentTooltipValue = tooltipBehaviorRenderer._prevTooltipValue = null; - tooltipBehaviorRenderer._chartTooltipState?.widget.hide(); - if (tooltipBehaviorRenderer._renderBox != null) { - tooltipBehaviorRenderer._renderBox!.canResetPath = true; - } + tooltipBehaviorRenderer._chartTooltipState?.hide(hideDelay: 0); } } } @@ -714,7 +779,8 @@ class TooltipBehaviorRenderer with ChartBehavior { final dynamic _chartState; - TooltipBehavior get _tooltipBehavior => _chart?.tooltipBehavior; + TooltipBehavior get _tooltipBehavior => + _chart?.tooltipBehavior as TooltipBehavior; SfTooltip? _chartTooltip; @@ -745,16 +811,15 @@ class TooltipBehaviorRenderer with ChartBehavior { TooltipValue? _currentTooltipValue; CartesianSeriesRenderer? _seriesRenderer; - dynamic? _currentSeries, _dataPoint; + dynamic _currentSeries, _dataPoint; int? _pointIndex; late int _seriesIndex; late Color _markerColor; late DataMarkerType _markerType; - Timer? _timer; bool _show = false; Offset? _showLocation; Rect? _tooltipBounds; - String? _stringVal; + String? _stringVal, _header; set _stringValue(String? value) { _stringVal = value; } @@ -838,18 +903,12 @@ class TooltipBehaviorRenderer with ChartBehavior { void onExit(double xPos, double yPos) { if (_renderBox != null && _tooltipBehavior.builder != null) { _hideMouseTooltip(); - } else if (_tooltipTemplate != null) { - //ignore: unused_local_variable - _timer?.cancel(); - _timer = Timer( - Duration(milliseconds: _tooltipBehavior.duration.toInt()), () {}); } } /// To render chart tooltip // ignore:unused_element void _renderTooltipView(Offset position) { - _chartTooltipState?.needMarker = _chart is SfCartesianChart; if (_chart is SfCartesianChart) { _renderCartesianChartTooltip(position); } else if (_chart is SfCircularChart) { @@ -866,7 +925,7 @@ class TooltipBehaviorRenderer with ChartBehavior { _renderBox != null && _chartTooltipState != null) { _show = true; - _chartTooltipState?.needMarker = true; + _mouseTooltip = false; _isHovering ? _showMouseTooltip(x, y) : _showTooltipView(x, y); } } @@ -875,52 +934,81 @@ class TooltipBehaviorRenderer with ChartBehavior { void _showTooltipView(double x, double y) { if (_tooltipBehavior.enable && _renderBox != null && - _chartState._animationCompleted) { - _renderBox!.canResetPath = false; + _chartState._animationCompleted == true) { _renderTooltipView(Offset(x, y)); - final bool needAnimate = !(_chart is SfCartesianChart) && - (_currentTooltipValue?.pointIndex == - _presentTooltipValue?.pointIndex) && - _chartState._chartSeries.currentSeries.explode && - !(_tooltipBehavior.tooltipPosition == TooltipPosition.pointer); - if ((_presentTooltipValue != null && _currentTooltipValue == null) || - needAnimate) { - _renderBox!.stringValue = _stringVal; - _renderBox!.boundaryRect = _tooltipBounds!; - _chartTooltipState?.widget - .show(_showLocation, _tooltipBehavior.animationDuration); - _currentTooltipValue = _presentTooltipValue; + if (_presentTooltipValue != null && + _tooltipBehavior.tooltipPosition != TooltipPosition.pointer) { + _chartTooltipState!.boundaryRect = _tooltipBounds!; + if (_showLocation != null) { + _chartTooltipState?.needMarker = _chart is SfCartesianChart; + _resolveLocation(); + _chartTooltipState?.show( + tooltipHeader: _header, + tooltipContent: _stringVal, + tooltipData: _presentTooltipValue, + position: _showLocation, + duration: _tooltipBehavior.animationDuration); + } } else { if (_tooltipBehavior.tooltipPosition == TooltipPosition.pointer && - (!(_chart is SfCartesianChart) || _currentSeries._isRectSeries)) { - _renderBox!.stringValue = _stringVal; - _renderBox!.boundaryRect = _tooltipBounds!; - _chartTooltipState?.widget - .show(_showLocation, _tooltipBehavior.animationDuration); + ((_chart is SfCartesianChart) == false || + _currentSeries._isRectSeries == true)) { + _presentTooltipValue?.pointerPosition = _showLocation; + _chartTooltipState!.boundaryRect = _tooltipBounds!; + if (_showLocation != null) { + _chartTooltipState?.needMarker = _chart is SfCartesianChart; + _chartTooltipState?.show( + tooltipHeader: _header, + tooltipContent: _stringVal, + tooltipData: _presentTooltipValue, + position: _showLocation, + duration: _tooltipBehavior.animationDuration); + } _currentTooltipValue = _presentTooltipValue; } } - _timer?.cancel(); assert( // ignore: unnecessary_null_comparison - _tooltipBehavior.duration != null - ? _tooltipBehavior.duration >= 0 - : true, + !(_tooltipBehavior.duration != null) || + _tooltipBehavior.duration >= 0, 'The duration time for the tooltip must not be less than 0.'); if (!_tooltipBehavior.shouldAlwaysShow) { - _timer = Timer( - Duration(milliseconds: _tooltipBehavior.duration.toInt()), () { - _show = false; - _currentTooltipValue = _presentTooltipValue = null; - if (_chartTooltipState?.widget != null && _renderBox != null) { - _chartTooltipState?.widget.hide(); - _renderBox!.canResetPath = true; - } - }); + _show = false; + _currentTooltipValue = _presentTooltipValue = null; + if (_chartTooltipState != null && _renderBox != null) { + _chartTooltipState?.hide( + hideDelay: _tooltipBehavior.duration.toInt()); + } } } } + // this method resolves the position issue when the markerPoint is residing + // out of the axis cliprect + void _resolveLocation() { + if (_chart is SfCartesianChart && + _tooltipBehavior.tooltipPosition == TooltipPosition.auto && + (_seriesRenderer!._isRectSeries || + _seriesRenderer!._seriesType.contains('bubble') || + _seriesRenderer!._seriesType.contains('candle') || + _seriesRenderer!._seriesType.contains('boxandwhisker') || + _seriesRenderer!._seriesType.contains('waterfall'))) { + Offset position = _showLocation!; + final Rect bounds = _chartState._chartAxis._axisClipRect; + if (!_isPointWithInRect(position, bounds)) { + if (position.dy < bounds.top) { + position = Offset(position.dx, bounds.top); + } + if (position.dx < bounds.left) { + position = Offset(bounds.left, position.dy); + } else if (position.dx > bounds.right) { + position = Offset(bounds.right, position.dy); + } + } + _showLocation = position; + } + } + /// This method shows the tooltip for any logical pixel outside point region //ignore: unused_element void _showChartAreaTooltip(Offset position, ChartAxisRenderer xAxisRenderer, @@ -929,20 +1017,15 @@ class TooltipBehaviorRenderer with ChartBehavior { final ChartAxis xAxis = xAxisRenderer._axis, yAxis = yAxisRenderer._axis; if (_tooltipBehavior.enable && _renderBox != null && - _chartState._animationCompleted) { - _renderBox!.stringValue = _stringVal; - _renderBox!.boundaryRect = _tooltipBounds!; - _chartTooltipState?.widget - .show(_showLocation, _tooltipBehavior.animationDuration); - _renderBox!.canResetPath = false; - //render + _chartState._animationCompleted == true) { _tooltipBounds = _chartState._chartAxis._axisClipRect; - if (_chartState._chartAxis._axisClipRect.contains(position)) { + _chartTooltipState!.boundaryRect = _tooltipBounds!; + if (_isPointWithInRect(position, _chartState._chartAxis._axisClipRect)) { _currentSeries = _chartState._chartSeries.visibleSeriesRenderers[0]; _currentSeries = _chartState._chartSeries.visibleSeriesRenderers[0]; _renderBox!.normalPadding = 5; _renderBox!.inversePadding = 5; - _renderBox!.header = null; + _header = ''; dynamic xValue = _pointToXValue( _chartState._requireInvertedAxis, xAxisRenderer, @@ -969,41 +1052,50 @@ class TooltipBehaviorRenderer with ChartBehavior { } else if (xAxisRenderer is CategoryAxisRenderer) { xValue = xAxisRenderer._visibleLabels[xValue.toInt()].text; } else if (xAxisRenderer is NumericAxisRenderer) { - xValue = xValue.toStringAsFixed(2).contains('.00') + xValue = xValue.toStringAsFixed(2).contains('.00') == true ? xValue.floor() : xValue.toStringAsFixed(2); } if (yAxisRenderer is NumericAxisRenderer || yAxisRenderer is LogarithmicAxisRenderer) { - yValue = yValue.toStringAsFixed(2).contains('.00') + yValue = yValue.toStringAsFixed(2).contains('.00') == true ? yValue.floor() : yValue.toStringAsFixed(2); } _stringValue = ' $xValue : $yValue '; _showLocation = position; } - _timer?.cancel(); + + if (_showLocation != null && + _stringVal != null && + _tooltipBounds != null) { + _chartTooltipState?.needMarker = false; + _chartTooltipState?.show( + tooltipHeader: _header, + tooltipContent: _stringVal, + tooltipData: _presentTooltipValue, + position: _showLocation, + duration: _tooltipBehavior.animationDuration); + } + if (!_tooltipBehavior.shouldAlwaysShow) { - _timer = Timer( - Duration(milliseconds: _tooltipBehavior.duration.toInt()), () { - _show = false; - if (_chartTooltipState?.widget != null && _renderBox != null) { - _chartTooltipState?.widget.hide(); - _renderBox!.canResetPath = true; - } - }); + _show = false; + if (_chartTooltipState != null && _renderBox != null) { + _chartTooltipState?.hide(); + } } } } void _showTemplateTooltip(Offset position, [dynamic xValue, dynamic yValue]) { final dynamic chart = _chartState._chart; + _presentTooltipValue = null; _tooltipBounds = _chartState._chartAxis._axisClipRect; dynamic series; double yPadding = 0; - if (_chartState._chartAxis._axisClipRect.contains(position) && - _chartState._animationCompleted) { + if (_isPointWithInRect(position, _chartState._chartAxis._axisClipRect) && + _chartState._animationCompleted == true) { int? seriesIndex, pointIndex; int outlierIndex = -1; bool isTooltipRegion = false; @@ -1021,7 +1113,7 @@ class TooltipBehaviorRenderer with ChartBehavior { int j = 0; if (seriesRenderer._visible! && - series.enableTooltip && + series.enableTooltip == true && seriesRenderer._regionalData != null) { seriesRenderer._regionalData! .forEach((dynamic regionRect, dynamic values) { @@ -1075,6 +1167,7 @@ class TooltipBehaviorRenderer with ChartBehavior { yPadding = paddingData[0]!.dy; tooltipPosition = paddingData[1] ?? tooltipPosition; _showLocation = tooltipPosition; + _seriesRenderer = seriesRenderer; _renderBox!.normalPadding = _seriesRenderer is BubbleSeriesRenderer ? 0 : yPadding; _renderBox!.inversePadding = yPadding; @@ -1097,7 +1190,7 @@ class TooltipBehaviorRenderer with ChartBehavior { pointIndex != presentTooltip.pointIndex || outlierIndex != presentTooltip.outlierIndex || (_currentSeries != null && - _currentSeries._isRectSeries && + _currentSeries._isRectSeries == true && _tooltipBehavior.tooltipPosition != TooltipPosition.auto)) { //Current point is different than previous one so tooltip re-renders if (seriesIndex != null && pointIndex != null) { @@ -1105,26 +1198,22 @@ class TooltipBehaviorRenderer with ChartBehavior { TooltipValue(seriesIndex, pointIndex!, outlierIndex); } if (isTooltipRegion && _tooltipTemplate != null) { - _timer?.cancel(); - if (!_isHovering && _renderBox != null) { - _timer = Timer( - Duration(milliseconds: _tooltipBehavior.duration.toInt()), - _hideTooltipTemplate); - } _show = isTooltipRegion; _performTooltip(); + if (!_isHovering && _renderBox != null) { + _hideTooltipTemplate(); + } } } else { //Current point is same as previous one so timer is reset and tooltip is not re-rendered if (!_isHovering) { - _timer?.cancel(); - _timer = Timer( - Duration(milliseconds: _tooltipBehavior.duration.toInt()), - _hideTooltipTemplate); + _hideTooltipTemplate(); } } - if (!isTooltipRegion && !_isInteraction && chart.series.isNotEmpty) { + if (!isTooltipRegion && + !_isInteraction && + chart.series.isNotEmpty == true) { //to show tooltip temlate when the position resides outside point region final dynamic x = xValue ?? _pointToXValue( @@ -1158,7 +1247,7 @@ class TooltipBehaviorRenderer with ChartBehavior { _performTooltip(); } if (!isTooltipRegion) { - _hideOnTimer(); + _hideTooltipTemplate(); } } _isInteraction = false; @@ -1166,37 +1255,23 @@ class TooltipBehaviorRenderer with ChartBehavior { /// To hide the tooltip when the timer ends void _hide() { - _timer?.cancel(); - _timer = - Timer(Duration(milliseconds: _tooltipBehavior.duration.toInt()), () { + if (!_tooltipBehavior.shouldAlwaysShow) { _show = false; _currentTooltipValue = _presentTooltipValue = null; - if (_chartTooltipState?.widget != null && _renderBox != null) { - _chartTooltipState?.widget.hide(); - _renderBox!.canResetPath = true; + if (_chartTooltipState != null && _renderBox != null) { + _chartTooltipState?.hide(hideDelay: _tooltipBehavior.duration.toInt()); } - }); - } - - /// To hide tooltip template with timer - void _hideOnTimer() { - if (_prevTooltipValue == null && _currentTooltipValue == null) { - _hideTooltipTemplate(); - } else { - _timer?.cancel(); - _timer = Timer(Duration(milliseconds: _tooltipBehavior.duration.toInt()), - _hideTooltipTemplate); } } /// To hide tooltip templates void _hideTooltipTemplate() { - _presentTooltipValue = null; - if (!(_tooltipBehavior.shouldAlwaysShow)) { + if (_tooltipBehavior.shouldAlwaysShow == false) { _show = false; - _chartTooltipState?.widget.hide(); + _chartTooltipState?.hide(hideDelay: _tooltipBehavior.duration.toInt()); _prevTooltipValue = null; _currentTooltipValue = null; + _presentTooltipValue = null; } } @@ -1205,29 +1280,34 @@ class TooltipBehaviorRenderer with ChartBehavior { //for mouse hover the tooltip is redrawn only when the current tooltip value differs from the previous one if (_show && ((_prevTooltipValue == null && _currentTooltipValue == null) || - ((_chartState is SfCartesianChartState && - (_currentSeries?._isRectSeries ?? false) && - _tooltipBehavior.tooltipPosition != TooltipPosition.auto)) || + (_chartState is SfCartesianChartState && + (_currentSeries?._isRectSeries ?? false) == true && + _tooltipBehavior.tooltipPosition != TooltipPosition.auto) || (_prevTooltipValue?.seriesIndex != _currentTooltipValue?.seriesIndex || _prevTooltipValue?.outlierIndex != _currentTooltipValue?.outlierIndex || _prevTooltipValue?.pointIndex != _currentTooltipValue?.pointIndex))) { - final bool reRender = (_isHovering && + final bool reRender = _isHovering && _prevTooltipValue != null && _currentTooltipValue != null && _prevTooltipValue!.seriesIndex == _currentTooltipValue!.seriesIndex && _prevTooltipValue!.pointIndex == _currentTooltipValue!.pointIndex && - _prevTooltipValue!.outlierIndex == - _currentTooltipValue!.outlierIndex); + _prevTooltipValue!.outlierIndex == _currentTooltipValue!.outlierIndex; if (_tooltipBehavior.builder != null && _tooltipBounds != null) { - _renderBox!.stringValue = _stringVal; - _renderBox!.boundaryRect = _tooltipBounds!; - _chartTooltipState?.widget.show( - _showLocation, - (!reRender) ? _tooltipBehavior.animationDuration.toInt() : 0, - _tooltipTemplate); + _chartTooltipState!.boundaryRect = _tooltipBounds!; + if (_tooltipBehavior.tooltipPosition != TooltipPosition.auto) + _presentTooltipValue!.pointerPosition = _showLocation; + if (_showLocation != null) { + _resolveLocation(); + _chartTooltipState?.show( + tooltipData: _presentTooltipValue, + position: _showLocation, + duration: + (!reRender) ? _tooltipBehavior.animationDuration.toInt() : 0, + template: _tooltipTemplate); + } } } } @@ -1236,31 +1316,46 @@ class TooltipBehaviorRenderer with ChartBehavior { void _showMouseTooltip(double x, double y) { if (_tooltipBehavior.enable && _renderBox != null && - _chartState._animationCompleted) { - _renderBox!.canResetPath = false; + _chartState._animationCompleted == true) { _renderTooltipView(Offset(x, y)); - _timer?.cancel(); if (!_mouseTooltip) { - _hide(); + _chartTooltipState?.hide(hideDelay: _tooltipBehavior.duration.toInt()); + _currentTooltipValue = null; } else { - if (_presentTooltipValue != null && _currentTooltipValue == null) { - _renderBox!.stringValue = _stringVal; - _renderBox!.boundaryRect = _tooltipBounds!; - _chartTooltipState?.widget - .show(_showLocation, _tooltipBehavior.animationDuration); + if (_presentTooltipValue != null && + (_currentTooltipValue == null || + _tooltipBehavior.tooltipPosition == TooltipPosition.auto)) { + _chartTooltipState!.boundaryRect = _tooltipBounds!; + if (_tooltipBehavior.tooltipPosition != TooltipPosition.auto) + _presentTooltipValue!.pointerPosition = _showLocation; + if (_showLocation != null) { + _chartTooltipState?.needMarker = _chart is SfCartesianChart; + _resolveLocation(); + _chartTooltipState?.show( + tooltipHeader: _header, + tooltipContent: _stringVal, + tooltipData: _presentTooltipValue, + position: _showLocation, + duration: _tooltipBehavior.animationDuration); + } _currentTooltipValue = _presentTooltipValue; } else if (_presentTooltipValue != null && _currentTooltipValue != null && _tooltipBehavior.tooltipPosition != TooltipPosition.auto && - (!(_seriesRenderer is CartesianSeriesRenderer) || - _seriesRenderer!._isRectSeries) && - _currentTooltipValue!.seriesIndex == - _presentTooltipValue!.seriesIndex && - _currentTooltipValue!.pointIndex == - _presentTooltipValue!.pointIndex) { - _renderBox!.stringValue = _stringVal; - _renderBox!.boundaryRect = _tooltipBounds!; - _chartTooltipState?.widget.show(_showLocation, 0); + ((_seriesRenderer is CartesianSeriesRenderer) == false || + _seriesRenderer!._isRectSeries)) { + _presentTooltipValue!.pointerPosition = _showLocation; + _chartTooltipState!.boundaryRect = _tooltipBounds!; + if (_showLocation != null) { + _chartTooltipState?.needMarker = _chart is SfCartesianChart; + _resolveLocation(); + _chartTooltipState?.show( + tooltipHeader: _header, + tooltipContent: _stringVal, + tooltipData: _presentTooltipValue, + position: _showLocation, + duration: 0); + } } } } @@ -1278,11 +1373,12 @@ class TooltipBehaviorRenderer with ChartBehavior { final int seriesIndex = _chart is SfCartesianChart ? _currentSeries._segments[0]._seriesIndex : 0; - if ((_chart is SfCartesianChart && !_chartState._requireAxisTooltip) || - !(_chart is SfCartesianChart)) { + if ((_chart is SfCartesianChart && + _chartState._requireAxisTooltip == false) || + (_chart is SfCartesianChart) == false) { if (_chart.onTooltipRender != null && _dataPoint != null && - !(_dataPoint.isTooltipRenderEvent ?? false)) { + (_dataPoint.isTooltipRenderEvent ?? false) == false) { _dataPoint.isTooltipRenderEvent = true; tooltipArgs = TooltipArgs( seriesIndex, @@ -1334,13 +1430,14 @@ class TooltipBehaviorRenderer with ChartBehavior { /// To render a chart tooltip for circular series void _renderCircularChartTooltip(Offset position) { final SfCircularChart chart = _chartState._chart; - _tooltipBounds = _chartState._chartContainerRect; + _tooltipBounds = _chartState._renderingDetails.chartContainerRect; bool isContains = false; final _Region? pointRegion = _getCircularPointRegion( chart, position, _chartState._chartSeries.visibleSeriesRenderers[0]); if (pointRegion != null && _chartState._chartSeries.visibleSeriesRenderers[pointRegion.seriesIndex] - ._series.enableTooltip) { + ._series.enableTooltip == + true) { _prevTooltipValue = TooltipValue(pointRegion.seriesIndex, pointRegion.pointIndex); _presentTooltipValue = _prevTooltipValue; @@ -1378,7 +1475,7 @@ class TooltipBehaviorRenderer with ChartBehavior { .visibleSeriesRenderers[pointRegion.seriesIndex]._series.name : null : header; - _renderBox!.header = header ?? ''; + _header = header ?? ''; if (chart.tooltipBehavior.format != null) { final String resultantString = chart.tooltipBehavior.format! .replaceAll('point.x', chartPoint.x.toString()) @@ -1399,9 +1496,11 @@ class TooltipBehaviorRenderer with ChartBehavior { _getDecimalLabelValue(chartPoint.y, digits); _showLocation = location; } + if (chart.series[0].explode) { + _presentTooltipValue!.pointerPosition = _showLocation; + } isContains = true; } else { - chart.tooltipBehavior.hide(); isContains = false; } _mouseTooltip = isContains; @@ -1415,17 +1514,18 @@ class TooltipBehaviorRenderer with ChartBehavior { final dynamic chart = _chart; final dynamic chartState = _chartState; - _tooltipBounds = chartState._chartContainerRect; + _tooltipBounds = chartState._renderingDetails.chartContainerRect; bool isContains = false; const int seriesIndex = 0; _pointIndex = _chartState._tooltipPointIndex; - if (_pointIndex == null && _chartState._currentActive == null) { + if (_pointIndex == null && + _chartState._renderingDetails.currentActive == null) { int? _pointIndex; bool isPoint; final dynamic seriesRenderer = chartState._chartSeries.visibleSeriesRenderers[seriesIndex]; for (int j = 0; j < seriesRenderer._renderPoints.length; j++) { - if (seriesRenderer._renderPoints[j].isVisible) { + if (seriesRenderer._renderPoints[j].isVisible == true) { isPoint = _isPointInPolygon( seriesRenderer._renderPoints[j].pathRegion, position); if (isPoint) { @@ -1434,19 +1534,19 @@ class TooltipBehaviorRenderer with ChartBehavior { } } } - chartState._currentActive = _ChartInteraction( + chartState._renderingDetails.currentActive = _ChartInteraction( seriesIndex, _pointIndex, seriesRenderer._series, seriesRenderer._renderPoints[_pointIndex], ); } - _pointIndex ??= chartState._currentActive.pointIndex; + _pointIndex ??= chartState._renderingDetails.currentActive.pointIndex; _dataPoint = _chartState ._chartSeries.visibleSeriesRenderers[0]._dataPoints[_pointIndex]; _chartState._tooltipPointIndex = null; final int digits = chart.tooltipBehavior.decimalPlaces; - if (chart.tooltipBehavior.enable) { + if (chart.tooltipBehavior.enable == true) { _prevTooltipValue = TooltipValue(seriesIndex, _pointIndex!); _presentTooltipValue = _prevTooltipValue; if (_prevTooltipValue != null && @@ -1459,11 +1559,13 @@ class TooltipBehaviorRenderer with ChartBehavior { final Offset location = chart.tooltipBehavior.tooltipPosition == TooltipPosition.pointer && _chartState._chartSeries.visibleSeriesRenderers[seriesIndex] - ._series.explode + ._series.explode == + true ? chartPoint.symbolLocation : chart.tooltipBehavior.tooltipPosition == TooltipPosition.pointer && - !_chartState._chartSeries.visibleSeriesRenderers[seriesIndex] - ._series.explode + _chartState._chartSeries.visibleSeriesRenderers[seriesIndex] + ._series.explode == + false ? position : chartPoint.symbolLocation; _currentSeries = seriesIndex; @@ -1477,7 +1579,7 @@ class TooltipBehaviorRenderer with ChartBehavior { ._chartSeries.visibleSeriesRenderers[seriesIndex]._series.name : null : header; - _renderBox!.header = header ?? ''; + _header = header ?? ''; if (chart.tooltipBehavior.format != null) { final String resultantString = chart.tooltipBehavior.format .replaceAll('point.x', chartPoint.x.toString()) @@ -1497,9 +1599,11 @@ class TooltipBehaviorRenderer with ChartBehavior { } isContains = true; } else { - chart.tooltipBehavior.hide(); isContains = false; } + if (chart.series.explode == true) { + _presentTooltipValue!.pointerPosition = _showLocation; + } _mouseTooltip = isContains; if (!isContains) { _prevTooltipValue = _currentTooltipValue = null; @@ -1508,42 +1612,33 @@ class TooltipBehaviorRenderer with ChartBehavior { /// To show the axis label tooltip for trimmed axes label texts. void _showAxisTooltip(Offset position, dynamic chart, String text) { + final _RenderingDetails renderingDetails = _chartState._renderingDetails; if (_renderBox != null) { - _chartTooltipState?.needMarker = false; - _renderBox!.canResetPath = false; - _renderBox!.header = ''; + _header = ''; _stringValue = text; _showLocation = position; - _tooltipBounds = _chartState._containerRect; + _tooltipBounds = renderingDetails.chartContainerRect; _renderBox!.inversePadding = 0; - if ((_prevTooltipValue != null) && - (_prevTooltipValue!.pointIndex == _currentTooltipValue!.pointIndex)) { - _renderBox!.stringValue = _stringVal; - _renderBox!.boundaryRect = _tooltipBounds!; - _chartTooltipState?.widget.show(_showLocation, 0); - } else { - _renderBox!.stringValue = _stringVal; - _renderBox!.boundaryRect = _tooltipBounds!; - _chartTooltipState?.widget - .show(_showLocation, _tooltipBehavior.animationDuration); - _timer?.cancel(); + _chartTooltipState!.boundaryRect = _tooltipBounds!; + if (_showLocation != null) { + _chartTooltipState?.needMarker = false; + _chartTooltipState?.show( + tooltipHeader: _header, + tooltipContent: _stringVal, + tooltipData: _currentTooltipValue, + position: _showLocation, + duration: 0); + } + if (!_isHovering) { + _chartTooltipState?.hide(hideDelay: _tooltipBehavior.duration.toInt()); } - _timer = - Timer(Duration(milliseconds: _tooltipBehavior.duration.toInt()), () { - _show = false; - if (_chartTooltipState?.widget != null && _renderBox != null) { - _chartTooltipState?.widget.hide(); - _renderBox!.canResetPath = true; - } - }); } } /// To render a chart tooltip for cartesian series void _renderCartesianChartTooltip(Offset position) { - _tooltipBounds = _chartState._chartAxis._axisClipRect; bool isContains = false; - if (_chartState._chartAxis._axisClipRect.contains(position)) { + if (_isPointWithInRect(position, _chartState._chartAxis._axisClipRect)) { Offset? tooltipPosition; double touchPadding; Offset? padding; @@ -1599,7 +1694,8 @@ class TooltipBehaviorRenderer with ChartBehavior { } if (paddedRegion.contains(position) && - (isTrendLine! ? regionRect[4].isVisible : true)) { + (isTrendLine! ? regionRect[4].isVisible : true) == true) { + _tooltipBounds = _chartState._chartAxis._axisClipRect; if (_seriesRenderer!._seriesType != 'boxandwhisker' ? !region.contains(position) : (paddedRegion.contains(position) || @@ -1617,7 +1713,8 @@ class TooltipBehaviorRenderer with ChartBehavior { Color? seriesColor = _seriesRenderer!._seriesColor; if (_seriesRenderer!._seriesType == 'waterfall') { seriesColor = _getWaterfallSeriesColor( - _seriesRenderer!._series as WaterfallSeries, + _seriesRenderer!._series + as WaterfallSeries, _seriesRenderer!._dataPoints[_pointIndex!], seriesColor)!; } @@ -1664,16 +1761,50 @@ class TooltipBehaviorRenderer with ChartBehavior { count++; }); if (_tooltipBehavior.shared) { - markerGradients.add(_seriesRenderer!._series.gradient); - markerImages.add(_seriesRenderer!._markerSettingsRenderer?._image); - markerTypes.add(_seriesRenderer!._series.markerSettings.shape); + int indexValue = 0; + int tooltipElementsLength = 0; final Paint markerPaint = Paint(); markerPaint.color = _seriesRenderer!._series.markerSettings.borderColor ?? _seriesRenderer!._seriesColor ?? _seriesRenderer!._series.color! .withOpacity(_tooltipBehavior.opacity); + markerGradients.add(_seriesRenderer!._series.gradient); + markerImages.add(_seriesRenderer!._markerSettingsRenderer?._image); + markerTypes.add(_seriesRenderer!._series.markerSettings.shape); markerPaints.add(markerPaint); + if ((_seriesRenderer!._seriesType.contains('range') || + _seriesRenderer!._seriesType == 'hilo') && + !isTrendLine!) { + // Assigned value '2' for this variable because for range and + // hilo series there will be two display value types + // such as high and low. + tooltipElementsLength = 2; + indexValue = _getTooltipNewLineLength(tooltipElementsLength); + } else if (_seriesRenderer!._seriesType == 'hiloopenclose' || + _seriesRenderer!._seriesType == 'candle') { + // Assigned value '4' for this variable because for hiloopenclose + // and candle series there will be four display values + // such as high, low, open and close. + tooltipElementsLength = 4; + indexValue = _getTooltipNewLineLength(tooltipElementsLength); + } else if (_seriesRenderer!._seriesType == 'boxandwhisker') { + // Assigned value '1' or '6' this variable field because for the + // box and whiskers series there will be one display values if + // the outlier's tooltip is activated otherwise there will be + // six display values such as maximum, minimum, mean, median, + // lowerQuartile and upperQuartile. + tooltipElementsLength = outlierTooltip ? 1 : 6; + indexValue = _getTooltipNewLineLength(tooltipElementsLength); + } else { + indexValue = _getTooltipNewLineLength(tooltipElementsLength); + } + for (int j = 0; j < indexValue; j++) { + markerTypes.add(null); + markerImages.add(null); + markerGradients.add(null); + markerPaints.add(null); + } } } } @@ -1683,20 +1814,7 @@ class TooltipBehaviorRenderer with ChartBehavior { _renderBox!.markerPaints = markerPaints; _renderBox!.markerTypes = markerTypes; _seriesRenderer = _currentSeries ?? _seriesRenderer; - if (_presentTooltipValue != null && - _currentTooltipValue != null && - (_presentTooltipValue!.pointIndex != - _currentTooltipValue!.pointIndex || - _presentTooltipValue!.seriesIndex != - _currentTooltipValue!.seriesIndex)) { - _currentTooltipValue = null; - } else if (_seriesRenderer!._seriesType == 'boxandwhisker' && - _currentTooltipValue != null && - _presentTooltipValue!.outlierIndex != - _currentTooltipValue!.outlierIndex) { - _currentTooltipValue = null; - } - if (_currentSeries._isRectSeries && + if (_currentSeries._isRectSeries == true && _tooltipBehavior.tooltipPosition == TooltipPosition.pointer) { tooltipPosition = position; _showLocation = tooltipPosition; @@ -1713,7 +1831,7 @@ class TooltipBehaviorRenderer with ChartBehavior { : _currentSeries._series.name ?? _currentSeries._seriesName)) : header; - _renderBox!.header = header ?? ''; + _header = header ?? ''; _stringValue = ''; if (_tooltipBehavior.shared) { _textValues = []; @@ -1755,7 +1873,6 @@ class TooltipBehaviorRenderer with ChartBehavior { _stringValue = null; if (!_isHovering) { _presentTooltipValue = _currentTooltipValue = null; - _tooltipBehavior.hide(); } else { _mouseTooltip = isContains; } @@ -1774,7 +1891,7 @@ class TooltipBehaviorRenderer with ChartBehavior { final bool isTrendLine = values[values.length - 1].contains('true'); String resultantString; final ChartAxisRenderer axisRenderer = seriesRenderer._yAxisRenderer!; - final tooltip = _tooltipBehavior; + final TooltipBehavior tooltip = _tooltipBehavior; final int digits = seriesRenderer._chart.tooltipBehavior.decimalPlaces; String? minimumValue, maximumValue, @@ -1803,17 +1920,17 @@ class TooltipBehaviorRenderer with ChartBehavior { point.outliers![outlierTooltipIndex], axisRenderer._axis, digits) : null; boxPlotString = '\nMinimum : ' + - minimumValue! + + minimumValue + '\nMaximum : ' + - maximumValue! + + maximumValue + '\nMedian : ' + - medianValue! + + medianValue + '\nMean : ' + - meanValue! + + meanValue + '\nLQ : ' + - lowerQuartileValue! + + lowerQuartileValue + '\nHQ : ' + - upperQuartileValue!; + upperQuartileValue; } else if (seriesRenderer._seriesType.contains('range') || seriesRenderer._seriesType == 'hilo' || seriesRenderer._seriesType == 'hiloopenclose' || @@ -1837,7 +1954,7 @@ class TooltipBehaviorRenderer with ChartBehavior { .replaceAll('point.x', values[0]) .replaceAll('point.high', highValue!) .replaceAll('point.low', lowValue!) - .replaceAll('seriesRenderer._series.name', + .replaceAll('series.name', seriesRenderer._series.name ?? seriesRenderer._seriesName!)) : (seriesRenderer._seriesType.contains('hiloopenclose') || seriesRenderer._seriesType.contains('candle')) && @@ -1849,7 +1966,7 @@ class TooltipBehaviorRenderer with ChartBehavior { .replaceAll('point.open', openValue!) .replaceAll('point.close', closeValue!) .replaceAll( - 'seriesRenderer._series.name', + 'series.name', seriesRenderer._series.name ?? seriesRenderer._seriesName!)) : (seriesRenderer._seriesType.contains('boxandwhisker')) && @@ -1862,7 +1979,7 @@ class TooltipBehaviorRenderer with ChartBehavior { .replaceAll('point.upperQuartile', upperQuartileValue!) .replaceAll('point.mean', meanValue!) .replaceAll('point.median', medianValue!) - .replaceAll('seriesRenderer._series.name', + .replaceAll('series.name', seriesRenderer._series.name ?? seriesRenderer._seriesName!)) : seriesRenderer._seriesType.contains('stacked') ? tooltip.format!.replaceAll('point.cumulativeValue', cumulativeValue!) @@ -1896,6 +2013,27 @@ class TooltipBehaviorRenderer with ChartBehavior { } return resultantString; } + + // Returns the cumulative length of the number of new line character '\n' + // available in series name and tooltip format string. + int _getTooltipNewLineLength(int value) { + value += _seriesRenderer!._series.name != null + ? '\n'.allMatches(_seriesRenderer!._series.name!).length + : 0; + if (_tooltipBehavior.format != null) { + value += '\n'.allMatches(_tooltipBehavior.format!).length; + } + return value; + } + + //finds whether the point resides inside the given rect including its edges + bool _isPointWithInRect(Offset point, Rect rect) { + return point != null && + point.dx >= rect.left && + point.dx <= rect.right && + point.dy <= rect.bottom && + point.dy >= rect.top; + } } /// Holds the tooltip series and point index @@ -1913,4 +2051,20 @@ class TooltipValue { ///Index of outlier points. final int? outlierIndex; + + ///Position of the pointer when the tooltip position mode is set as pointer + Offset? pointerPosition; + + @override + //ignore: hash_and_equals + bool operator ==(Object other) { + if (other is TooltipValue) { + return seriesIndex == other.seriesIndex && + pointIndex == other.pointIndex && + outlierIndex == other.outlierIndex && + (pointerPosition == null || pointerPosition == other.pointerPosition); + } else { + return false; + } + } } diff --git a/packages/syncfusion_flutter_charts/lib/src/common/utils/helper.dart b/packages/syncfusion_flutter_charts/lib/src/common/utils/helper.dart index 7d62fdbb5..432491be8 100644 --- a/packages/syncfusion_flutter_charts/lib/src/common/utils/helper.dart +++ b/packages/syncfusion_flutter_charts/lib/src/common/utils/helper.dart @@ -1,10 +1,5 @@ part of charts; -///Signature for callback reporting that a data label is tapped. -/// -///Also refer `onDataLabelTapped` event and [DataLabelTapDetails] class. -typedef DataLabelTapCallback = void Function(DataLabelTapDetails onTapArgs); - /// `onDataLabelTapped` event for all series. void _dataLabelTapEvent(dynamic chart, DataLabelSettings dataLabelSettings, int pointIndex, dynamic point, Offset position, int seriesIndex) { @@ -33,7 +28,7 @@ Color _getSaturationColor(Color color) { CartesianChartPoint _getPointFromData( CartesianSeriesRenderer seriesRenderer, int pointIndex) { final XyDataSeries series = - seriesRenderer._series as XyDataSeries; + seriesRenderer._series as XyDataSeries; final ChartIndexedValueMapper? xValue = series.xValueMapper; final ChartIndexedValueMapper? yValue = series.yValueMapper; final dynamic xVal = xValue!(pointIndex); @@ -78,7 +73,7 @@ TextStyle _getTextStyle( if (textStyle != null) { return TextStyle( color: textStyle.color != null && - (takeFontColorValue == null ? true : !takeFontColorValue) + (takeFontColorValue == null || !takeFontColorValue) ? textStyle.color : fontColor, fontWeight: textStyle.fontWeight ?? fontWeight, @@ -117,32 +112,30 @@ TextStyle _getTextStyle( Widget? _getElements( dynamic _chartState, Widget chartWidget, BoxConstraints constraints) { final dynamic chart = _chartState._chart; + final _ChartLegend chartLegend = _chartState._renderingDetails.chartLegend; final LegendPosition legendPosition = - _chartState._legendRenderer._legendPosition; + _chartState._renderingDetails.legendRenderer._legendPosition; double legendHeight, legendWidth, chartHeight, chartWidth; Widget? element; - if (_chartState._chartLegend.shouldRenderLegend && - chart.legend.isResponsive) { - chartHeight = - constraints.maxHeight - _chartState._chartLegend.legendSize.height; - chartWidth = - constraints.maxWidth - _chartState._chartLegend.legendSize.width; - _chartState._chartLegend.shouldRenderLegend = - (legendPosition == LegendPosition.bottom || - legendPosition == LegendPosition.top) - ? (chartHeight > _chartState._chartLegend.legendSize.height) - : (chartWidth > _chartState._chartLegend.legendSize.width); + + if (chartLegend.shouldRenderLegend && chart.legend.isResponsive == true) { + chartHeight = constraints.maxHeight - chartLegend.legendSize.height; + chartWidth = constraints.maxWidth - chartLegend.legendSize.width; + chartLegend.shouldRenderLegend = (legendPosition == LegendPosition.bottom || + legendPosition == LegendPosition.top) + ? (chartHeight > chartLegend.legendSize.height) + : (chartWidth > chartLegend.legendSize.width); } - if (!_chartState._chartLegend.shouldRenderLegend) { + if (!chartLegend.shouldRenderLegend) { element = Container( child: chartWidget, width: constraints.maxWidth, height: constraints.maxHeight); } else { - legendHeight = _chartState._chartLegend.legendSize.height; - legendWidth = _chartState._chartLegend.legendSize.width; - chartHeight = _chartState._chartLegend.chartSize.height - legendHeight; - chartWidth = _chartState._chartLegend.chartSize.width - legendWidth; + legendHeight = chartLegend.legendSize.height; + legendWidth = chartLegend.legendSize.width; + chartHeight = chartLegend.chartSize.height - legendHeight; + chartWidth = chartLegend.chartSize.width - legendWidth; final Widget legendBorderWidget = CustomPaint(painter: _ChartLegendStylePainter(chartState: _chartState)); final Widget legendWidget = Container( @@ -198,22 +191,75 @@ Widget _getBottomAndTopLegend( final bool needPadding = chart is SfCircularChart || chart is SfPyramidChart || chart is SfFunnelChart; + final _ChartLegend chartLegend = _chartState._renderingDetails.chartLegend; final LegendPosition legendPosition = - _chartState._legendRenderer._legendPosition; - final double legendLeft = - (_chartState._chartLegend.chartSize.width < legendWidth) + _chartState._renderingDetails.legendRenderer._legendPosition; + final double legendLeft = (chartLegend.chartSize.width < legendWidth) + ? 0 + : ((chart.legend.alignment == ChartAlignment.near) ? 0 - : ((chart.legend.alignment == ChartAlignment.near) - ? 0 - : (chart.legend.alignment == ChartAlignment.center) - ? _chartState._chartLegend.chartSize.width / 2 - - _chartState._chartLegend.legendSize.width / 2 - : _chartState._chartLegend.chartSize.width - - _chartState._chartLegend.legendSize.width); - final EdgeInsets margin = (legendPosition == LegendPosition.top) - ? EdgeInsets.fromLTRB( - legendLeft + (needPadding ? padding : 0), legendPadding, 0, 0) - : EdgeInsets.fromLTRB(legendLeft, chartHeight + legendPadding, 0, 0); + : (chart.legend.alignment == ChartAlignment.center) + ? chartLegend.chartSize.width / 2 - + chartLegend.legendSize.width / 2 + : chartLegend.chartSize.width - chartLegend.legendSize.width); + bool needRender = true; + final EdgeInsets margin; + if (chartLegend.legend?.offset != null) { + if (chart.legend.offset.dx.isNegative == true) { + if (legendLeft + chart.legend.offset.dx < 0) { + needRender = false; + } else { + if (legendLeft + chart.legend.offset.dx > chartLegend.chartSize.width) { + needRender = false; + } + } + } + if (legendPosition == LegendPosition.top) { + if (chart.legend.offset.dy.isNegative == true) { + if (legendPadding + chart.legend.offset.dy < 0) { + needRender = false; + } + } else { + if (legendPadding + chart.legend.offset.dy > + chartLegend.chartSize.height) { + needRender = false; + } + } + } else if (legendPosition == LegendPosition.bottom) { + if (chart.legend.offset.dy.isNegative == true) { + if (chartHeight + legendPadding + chart.legend.offset.dy < 0) { + needRender = false; + } + } else { + if (chartHeight + + legendPadding + + chart.legend.offset.dy + + legendHeight / 2 > + chartLegend.chartSize.height) { + needRender = false; + } + } + } + } + if (chartLegend.legend?.offset == null) { + margin = (legendPosition == LegendPosition.top) + ? EdgeInsets.fromLTRB( + legendLeft + (needPadding ? padding : 0), legendPadding, 0, 0) + : EdgeInsets.fromLTRB(legendLeft, chartHeight + legendPadding, 0, 0); + } else { + if (needRender) { + margin = (legendPosition == LegendPosition.top) + ? EdgeInsets.fromLTRB( + legendLeft + (needPadding ? padding : 0) + chart.legend.offset.dx, + legendPadding + chart.legend.offset.dy, + 0, + 0) + : EdgeInsets.fromLTRB(legendLeft + chart.legend.offset.dx, + chartHeight + legendPadding + chart.legend.offset.dy, 0, 0); + } else { + margin = const EdgeInsets.all(0); + } + } legendWidget = Container( child: Stack(children: [ Container( @@ -229,37 +275,77 @@ Widget _getBottomAndTopLegend( ]), ); if (legendPosition == LegendPosition.top) { - element = Container( - height: constraints.maxHeight, - width: constraints.maxWidth, - child: Stack( - children: [ - legendWidget, - Container( - margin: EdgeInsets.fromLTRB( - needPadding ? padding : 0, legendHeight + padding, 0, 0), - height: chartHeight, - width: _chartState._chartLegend.chartSize.width, - child: chartWidget, - ) - ], - )); + if (chartLegend.legend?.offset == null) { + element = Container( + height: constraints.maxHeight, + width: constraints.maxWidth, + child: Stack( + children: [ + legendWidget, + Container( + margin: EdgeInsets.fromLTRB( + needPadding ? padding : 0, legendHeight + padding, 0, 0), + height: chartHeight, + width: chartLegend.chartSize.width, + child: chartWidget, + ) + ], + )); + } else { + if (needRender) { + element = Container( + height: constraints.maxHeight, + width: constraints.maxWidth, + child: Stack( + children: [ + chartWidget, + legendWidget, + ], + )); + } else { + element = Container( + height: constraints.maxHeight, + width: constraints.maxWidth, + child: chartWidget, + ); + } + } } else { - element = Container( - margin: EdgeInsets.fromLTRB( - needPadding ? padding : 0, needPadding ? padding : 0, 0, 0), - height: constraints.maxHeight, - width: constraints.maxWidth, - child: Stack( - children: [ - Container( - height: chartHeight, - width: _chartState._chartLegend.chartSize.width, - child: chartWidget, - ), - legendWidget - ], - )); + if (chartLegend.legend?.offset == null) { + element = Container( + margin: EdgeInsets.fromLTRB( + needPadding ? padding : 0, needPadding ? padding : 0, 0, 0), + height: constraints.maxHeight, + width: constraints.maxWidth, + child: Stack( + children: [ + Container( + height: chartHeight, + width: chartLegend.chartSize.width, + child: chartWidget, + ), + legendWidget, + ], + )); + } else { + if (needRender) { + element = Container( + height: constraints.maxHeight, + width: constraints.maxWidth, + child: Stack( + children: [ + chartWidget, + legendWidget, + ], + )); + } else { + element = Container( + height: constraints.maxHeight, + width: constraints.maxWidth, + child: chartWidget, + ); + } + } } return element; } @@ -278,24 +364,75 @@ Widget _getLeftAndRightLegend( const double legendPadding = 5; const double padding = 10; final dynamic chart = _chartState._chart; + bool needRender = true; final bool needPadding = chart is SfCircularChart || chart is SfPyramidChart || chart is SfFunnelChart; + final _ChartLegend chartLegend = _chartState._renderingDetails.chartLegend; final LegendPosition legendPosition = - _chartState._legendRenderer._legendPosition; - final double legendTop = - (_chartState._chartLegend.chartSize.height < legendHeight) + _chartState._renderingDetails.legendRenderer._legendPosition; + final double legendTop = (chartLegend.chartSize.height < legendHeight) + ? 0 + : ((chart.legend.alignment == ChartAlignment.near) ? 0 - : ((chart.legend.alignment == ChartAlignment.near) - ? 0 - : (chart.legend.alignment == ChartAlignment.center) - ? _chartState._chartLegend.chartSize.height / 2 - - _chartState._chartLegend.legendSize.height / 2 - : _chartState._chartLegend.chartSize.height - - _chartState._chartLegend.legendSize.height); - final EdgeInsets margin = (legendPosition == LegendPosition.left) - ? EdgeInsets.fromLTRB(legendPadding / 2, legendTop, 0, 0) - : EdgeInsets.fromLTRB(chartWidth + legendPadding, legendTop, 0, 0); + : (chart.legend.alignment == ChartAlignment.center) + ? chartLegend.chartSize.height / 2 - + chartLegend.legendSize.height / 2 + : chartLegend.chartSize.height - chartLegend.legendSize.height); + final EdgeInsets margin; + + if (chartLegend.legend?.offset != null) { + if (legendPosition == LegendPosition.left) { + if (chart.legend.offset.dx.isNegative == true) { + if (legendPadding / 2 + chart.legend.offset.dx < legendPadding) { + needRender = false; + } + } else { + if (chart.legend.offset.dx > chartWidth == true) { + needRender = false; + } + } + } else if (legendPosition == LegendPosition.right) { + if (chart.legend.offset.dx.isNegative == true) { + if (chartWidth + legendPadding + chart.legend.offset.dx < 0) { + needRender = false; + } + } else { + if (chartWidth + chart.legend.offset.dx - legendPadding > chartWidth) { + needRender = false; + } + } + } + + if (chart.legend.offset.dy.isNegative == true) { + if (legendTop + chart.legend.offset.dy < 0) { + needRender = false; + } + } else { + if (chart.legend.offset.dy + legendTop > chartLegend.chartSize.height == + true) { + needRender = false; + } + } + } + if (chartLegend.legend?.offset == null) { + margin = (legendPosition == LegendPosition.left) + ? EdgeInsets.fromLTRB(legendPadding / 2, legendTop, 0, 0) + : EdgeInsets.fromLTRB(chartWidth + legendPadding, legendTop, 0, 0); + } else { + if (needRender) { + margin = (legendPosition == LegendPosition.left) + ? EdgeInsets.fromLTRB(legendPadding / 2 + chart.legend.offset.dx, + legendTop + chart.legend.offset.dy, 0, 0) + : EdgeInsets.fromLTRB( + chartWidth + legendPadding + chart.legend.offset.dx, + legendTop + chart.legend.offset.dy, + 0, + 0); + } else { + margin = const EdgeInsets.all(0); + } + } legendWidget = Container( child: Stack(children: [ Container( @@ -313,39 +450,80 @@ Widget _getLeftAndRightLegend( ]), ); if (legendPosition == LegendPosition.left) { - element = Container( - height: constraints.maxHeight, - width: constraints.maxWidth, - child: Stack( - children: [ - legendWidget, - Container( - margin: EdgeInsets.fromLTRB( - legendWidth + (needPadding ? padding : 0), - needPadding ? chart.margin.top : 0, - 0, - 0), - height: _chartState._chartLegend.chartSize.height, - width: chartWidth, - child: chartWidget, - ) - ], - )); + if (chartLegend.legend?.offset == null) { + element = Container( + height: constraints.maxHeight, + width: constraints.maxWidth, + child: Stack( + children: [ + legendWidget, + Container( + margin: EdgeInsets.fromLTRB( + legendWidth + (needPadding ? padding : 0), + needPadding ? chart.margin.top : 0, + 0, + 0), + height: chartLegend.chartSize.height, + width: chartWidth, + child: chartWidget, + ) + ], + )); + } else { + if (needRender) { + element = Container( + height: constraints.maxHeight, + width: constraints.maxWidth, + child: Stack( + children: [ + chartWidget, + legendWidget, + ], + )); + } else { + element = Container( + height: constraints.maxHeight, + width: constraints.maxWidth, + child: chartWidget, + ); + } + } } else { - element = Container( - height: constraints.maxHeight, - width: constraints.maxWidth, - child: Stack( - children: [ - Container( - margin: EdgeInsets.only(top: needPadding ? chart.margin.top : 0), - height: _chartState._chartLegend.chartSize.height, - width: chartWidth, - child: chartWidget, - ), - legendWidget - ], - )); + if (chartLegend.legend?.offset == null) { + element = Container( + height: constraints.maxHeight, + width: constraints.maxWidth, + child: Stack( + children: [ + Container( + margin: + EdgeInsets.only(top: needPadding ? chart.margin.top : 0), + height: chartLegend.chartSize.height, + width: chartWidth, + child: chartWidget, + ), + legendWidget + ], + )); + } else { + if (needRender) { + element = Container( + height: constraints.maxHeight, + width: constraints.maxWidth, + child: Stack( + children: [ + chartWidget, + legendWidget, + ], + )); + } else { + element = Container( + height: constraints.maxHeight, + width: constraints.maxWidth, + child: chartWidget, + ); + } + } } return element; } @@ -369,7 +547,7 @@ class _MeasureWidgetSize extends StatelessWidget { @override Widget build(BuildContext context) { final List<_MeasureWidgetContext> templates = - chartState._legendWidgetContext; + chartState._renderingDetails.legendWidgetContext; templates.add(_MeasureWidgetContext( widget: currentWidget, key: currentKey, @@ -387,7 +565,7 @@ bool _legendToggleTemplateState( _MeasureWidgetContext currentItem, dynamic _chartState, String checkType) { bool needSelect = false; final List<_MeasureWidgetContext> legendToggles = - _chartState._legendToggleTemplateStates; + _chartState._renderingDetails.legendToggleTemplateStates; if (legendToggles.isNotEmpty) { for (int i = 0; i < legendToggles.length; i++) { final _MeasureWidgetContext item = legendToggles[i]; @@ -414,7 +592,7 @@ bool _legendToggleTemplateState( void _legendToggleState(_LegendRenderContext currentItem, dynamic _chartState) { bool needSelect = false; final List<_LegendRenderContext> legendToggles = - _chartState._legendToggleStates; + _chartState._renderingDetails.legendToggleStates; if (legendToggles.isNotEmpty) { for (int i = 0; i < legendToggles.length; i++) { final _LegendRenderContext item = legendToggles[i]; @@ -436,10 +614,11 @@ void _cartesianLegendToggleState( _LegendRenderContext currentItem, dynamic _chartState) { bool needSelect = false; final List<_LegendRenderContext> legendToggles = - _chartState._legendToggleStates; + _chartState._renderingDetails.legendToggleStates; if (currentItem.trendline == null || _chartState._chartSeries.visibleSeriesRenderers[currentItem.seriesIndex] - ._visible) { + ._visible == + true) { if (legendToggles.isNotEmpty) { for (int i = 0; i < legendToggles.length; i++) { final _LegendRenderContext item = legendToggles[i]; @@ -462,8 +641,8 @@ void _cartesianLegendToggleState( if (!needSelect) { if (!(currentItem.seriesRenderer is TechnicalIndicators ? !currentItem.indicatorRenderer!._visible! - : !currentItem.seriesRenderer._visible && - !_chartState._isTrendlineToggled)) { + : currentItem.seriesRenderer._visible == false && + _chartState._isTrendlineToggled == false)) { needSelect = false; final CartesianSeriesRenderer seriesRenderer = _chartState ._chartSeries.visibleSeriesRenderers[currentItem.seriesIndex]; @@ -510,20 +689,22 @@ bool _findingCollision(Rect rect, List regions, [Rect? pathRect]) { /// To get equivalent value for the percentage num _getValueByPercentage(num value1, num value2) { - return (value1.isNegative + return value1.isNegative ? (num.tryParse('-' + (num.tryParse(value1.toString().replaceAll(RegExp('-'), ''))! % value2) .toString()))! - : (value1 % value2)); + : (value1 % value2); } Widget _renderChartTitle(dynamic _chartState) { Widget titleWidget; final dynamic widget = _chartState._chart; - if (widget.title.text != null && widget.title.text.isNotEmpty) { + if (widget.title.text != null && widget.title.text.isNotEmpty == true) { + final SfChartThemeData chartTheme = + _chartState._renderingDetails.chartTheme; final Color color = - widget.title.textStyle.color ?? _chartState._chartTheme.titleTextColor; + widget.title.textStyle.color ?? chartTheme.titleTextColor; final double fontSize = widget.title.textStyle.fontSize; final String fontFamily = widget.title.textStyle.fontFamily; final FontStyle fontStyle = widget.title.textStyle.fontStyle; @@ -535,10 +716,9 @@ Widget _renderChartTitle(dynamic _chartState) { padding: const EdgeInsets.fromLTRB(0, 5, 0, 0), decoration: BoxDecoration( color: widget.title.backgroundColor ?? - _chartState._chartTheme.titleBackgroundColor, + chartTheme.titleBackgroundColor, border: Border.all( - color: widget.title.borderColor ?? - _chartState._chartTheme.titleTextColor, + color: widget.title.borderColor ?? chartTheme.titleTextColor, width: widget.title.borderWidth)), child: Text(widget.title.text, style: TextStyle( @@ -569,8 +749,10 @@ List _bindLegendTemplateWidgets(dynamic widgetState) { Widget legendWidget; final dynamic widget = widgetState._chart; final List templates = []; - widgetState._chartWidgets = []; - if (widget.legend.isVisible && widget.legend.legendItemBuilder != null) { + widgetState._renderingDetails.chartWidgets = []; + + if (widget.legend.isVisible == true && + widget.legend.legendItemBuilder != null) { for (int i = 0; i < widgetState._chartSeries.visibleSeriesRenderers.length; i++) { @@ -615,3 +797,133 @@ void _disposeAnimationController( animationController = null; } } + +void _calculatePointSeriesIndex( + dynamic chart, dynamic _chartState, Offset? position, + [_Region? pointRegion, ActivationMode? activationMode]) { + if (chart is SfCartesianChart) { + for (int i = 0; + i < _chartState._chartSeries.visibleSeriesRenderers.length; + i++) { + final CartesianSeriesRenderer seriesRenderer = + _chartState._chartSeries.visibleSeriesRenderers[i]; + final String _seriesType = seriesRenderer._seriesType; + int? pointIndex; + final double padding = (_seriesType == 'bubble') || + (_seriesType == 'scatter') || + (_seriesType == 'bar') || + (_seriesType == 'column' || + _seriesType == 'rangecolumn' || + _seriesType.contains('stackedcolumn') || + _seriesType.contains('stackedbar') || + _seriesType == 'waterfall') + ? 0 + : 15; + + /// Regional padding to detect smooth touch + seriesRenderer._regionalData! + .forEach((dynamic regionRect, dynamic values) { + final Rect region = regionRect[0]; + final double left = region.left - padding; + final double right = region.right + padding; + final double top = region.top - padding; + final double bottom = region.bottom + padding; + final Rect paddedRegion = Rect.fromLTRB(left, top, right, bottom); + if (paddedRegion.contains(position!)) { + pointIndex = regionRect[4].visiblePointIndex; + } + }); + + if (pointIndex != null) { + if ((seriesRenderer._series.onPointTap != null || + seriesRenderer._series.onPointDoubleTap != null || + seriesRenderer._series.onPointLongPress != null) && + activationMode != null) { + ChartPointDetails pointInteractionDetails; + pointInteractionDetails = ChartPointDetails( + i, + pointIndex!, + seriesRenderer._dataPoints, + seriesRenderer + ._visibleDataPoints![pointIndex!].overallDataPointIndex); + activationMode == ActivationMode.singleTap + ? seriesRenderer._series.onPointTap!(pointInteractionDetails) + : activationMode == ActivationMode.doubleTap + ? seriesRenderer + ._series.onPointDoubleTap!(pointInteractionDetails) + : seriesRenderer + ._series.onPointLongPress!(pointInteractionDetails); + } else { + PointTapArgs pointTapArgs; + pointTapArgs = PointTapArgs( + i, + pointIndex!, + seriesRenderer._dataPoints, + seriesRenderer + ._visibleDataPoints![pointIndex!].overallDataPointIndex); + chart.onPointTapped!(pointTapArgs); + } + } + } + } else if (chart is SfCircularChart) { + const int seriesIndex = 0; + if ((chart.series[seriesIndex].onPointTap != null || + chart.series[seriesIndex].onPointDoubleTap != null || + chart.series[seriesIndex].onPointLongPress != null) && + activationMode != null) { + ChartPointDetails pointInteractionDetails; + pointInteractionDetails = ChartPointDetails( + pointRegion?.seriesIndex, + pointRegion?.pointIndex, + _chartState + ._chartSeries.visibleSeriesRenderers[seriesIndex]._dataPoints, + pointRegion?.pointIndex); + activationMode == ActivationMode.singleTap + ? chart.series[seriesIndex].onPointTap!(pointInteractionDetails) + : activationMode == ActivationMode.doubleTap + ? chart.series[seriesIndex] + .onPointDoubleTap!(pointInteractionDetails) + : chart.series[seriesIndex] + .onPointLongPress!(pointInteractionDetails); + } else { + PointTapArgs pointTapArgs; + pointTapArgs = PointTapArgs( + pointRegion?.seriesIndex, + pointRegion?.pointIndex, + _chartState + ._chartSeries.visibleSeriesRenderers[seriesIndex]._dataPoints, + pointRegion?.pointIndex); + chart.onPointTapped!(pointTapArgs); + } + } else { + int? index; + const int seriesIndex = 0; + for (int i = 0; i < _chartState._renderPoints!.length; i++) { + if (_chartState._renderPoints![i].region != null && + _chartState._renderPoints![i].region!.contains(position) == true) { + index = i; + break; + } + } + if (index != null) { + if ((chart.series.onPointTap != null || + chart.series.onPointDoubleTap != null || + chart.series.onPointLongPress != null) && + activationMode != null) { + ChartPointDetails pointInteractionDetails; + pointInteractionDetails = ChartPointDetails( + seriesIndex, index, _chartState._dataPoints, index); + activationMode == ActivationMode.singleTap + ? chart.series.onPointTap!(pointInteractionDetails) + : activationMode == ActivationMode.doubleTap + ? chart.series.onPointDoubleTap!(pointInteractionDetails) + : chart.series.onPointLongPress!(pointInteractionDetails); + } else { + PointTapArgs pointTapArgs; + pointTapArgs = + PointTapArgs(seriesIndex, index, _chartState._dataPoints, index); + chart.onPointTapped!(pointTapArgs); + } + } + } +} diff --git a/packages/syncfusion_flutter_charts/lib/src/common/utils/typedef.dart b/packages/syncfusion_flutter_charts/lib/src/common/utils/typedef.dart new file mode 100644 index 000000000..115562952 --- /dev/null +++ b/packages/syncfusion_flutter_charts/lib/src/common/utils/typedef.dart @@ -0,0 +1,224 @@ +part of charts; + +/// typedef belongs SfCartesianChart + +/// Returns the TooltipArgs. +typedef ChartTooltipCallback = void Function(TooltipArgs tooltipArgs); + +/// Returns the ActualRangeChangedArgs. +typedef ChartActualRangeChangedCallback = void Function( + ActualRangeChangedArgs rangeChangedArgs); + +@Deprecated('Use ChartLabelFormatterCallback instead.') + +/// Returns the AxisLabelRenderArgs. +typedef ChartAxisLabelRenderCallback = void Function( + AxisLabelRenderArgs axisLabelRenderArgs); + +///Signature for the [axisLabelFormatter] callback that returns [ChartAxisLabel] class value to +/// customize the axis label text and style. +typedef ChartLabelFormatterCallback = ChartAxisLabel Function( + AxisLabelRenderDetails axisLabelRenderArgs); + +/// Returns the DataLabelRenderArgs. +typedef ChartDataLabelRenderCallback = void Function( + DataLabelRenderArgs dataLabelArgs); + +/// Returns the LegendRenderArgs. +typedef ChartLegendRenderCallback = void Function( + LegendRenderArgs legendRenderArgs); + +/// Returns the Trendline args +typedef ChartTrendlineRenderCallback = void Function( + TrendlineRenderArgs trendlineRenderArgs); + +///Returns the TrackballArgs. +typedef ChartTrackballCallback = void Function(TrackballArgs trackballArgs); + +/// Returns the CrosshairRenderArgs +typedef ChartCrosshairCallback = void Function( + CrosshairRenderArgs crosshairArgs); + +/// Returns the ZoomPanArgs. +typedef ChartZoomingCallback = void Function(ZoomPanArgs zoomingArgs); + +@Deprecated('Use ChartPointInteractionCallback instead.') + +/// Returns the PointTapArgs. +typedef ChartPointTapCallback = void Function(PointTapArgs pointTapArgs); + +/// Returns the ChartPointDetails. +typedef ChartPointInteractionCallback = void Function( + ChartPointDetails pointInteractionDetails); + +/// Returns the AxisLabelTapArgs. +typedef ChartAxisLabelTapCallback = void Function( + AxisLabelTapArgs axisLabelTapArgs); + +/// Returns the LegendTapArgs. +typedef ChartLegendTapCallback = void Function(LegendTapArgs legendTapArgs); + +/// Returns the SelectionArgs. +typedef ChartSelectionCallback = void Function(SelectionArgs selectionArgs); + +/// Returns the offset. +typedef ChartTouchInteractionCallback = void Function( + ChartTouchInteractionArgs tapArgs); + +/// Returns the IndicatorRenderArgs. +typedef ChartIndicatorRenderCallback = TechnicalIndicatorRenderDetails Function( + IndicatorRenderParams indicatorRenderParams); + +///Returns the MarkerRenderArgs. +typedef ChartMarkerRenderCallback = void Function(MarkerRenderArgs markerArgs); + +///Returns a widget which can be used to load more data to chart. +///called on dragging and when the visible range reaches the end. +typedef LoadMoreViewBuilderCallback = Widget Function( + BuildContext context, ChartSwipeDirection direction); + +/// A callback which gets called on swiping over plot area +typedef ChartPlotAreaSwipeCallback = void Function( + ChartSwipeDirection direction); + +/// Called when the series renderer is created +typedef SeriesRendererCreatedCallback = void Function( + ChartSeriesController controller); + +/// Returns the widget. +typedef ChartDataLabelTemplateBuilder = Widget Function( + T data, CartesianChartPoint point, int pointIndex, + {int seriesIndex, CartesianSeries series}); + +/// typedef common for all the chart types +/// +///Signature for callback reporting that a data label is tapped. +/// +///Also refer `onDataLabelTapped` event and [DataLabelTapDetails] class. +typedef DataLabelTapCallback = void Function(DataLabelTapDetails onTapArgs); + +/// Returns the widget. +/// +/// Customize the appearance of legend items with your template by +/// using legendItemBuilder property of legend. +typedef LegendItemBuilder = Widget Function( + String legendText, dynamic series, dynamic point, int seriesIndex); + +/// Maps the index value. +typedef ChartIndexedValueMapper = R? Function(int index); + +/// Maps the data from data source. +typedef ChartValueMapper = R? Function(T datum, int index); + +///Signature for the callback that returns the shader from the data source based on the index. +/// Can get the data, index, color and rect values. +/// +/// +///T - Data of the current data point +/// +/// +///index - Index of the current data point +/// +/// +///rect - Rect value of the current data point slice +/// +///color - Color of the current data point +typedef ChartShaderMapper = Shader Function( + T datum, int index, Color color, Rect rect); + +/// Returns the widget. +typedef ChartWidgetBuilder = Widget Function(dynamic data, dynamic point, + dynamic series, int pointIndex, int seriesIndex); + +/// Returns the widget as a template of trackball +typedef ChartTrackballBuilder = Widget Function( + BuildContext context, TrackballDetails trackballDetails); + +/// Custom renderer for series +typedef ChartSeriesRendererFactory = ChartSeriesRenderer Function( + ChartSeries series); + +/// typedef belongs SfCircularChart + +/// Returns the LegendRenderArgs. +typedef CircularLegendRenderCallback = void Function( + LegendRenderArgs legendRenderArgs); + +/// Returns the TooltipArgs. +typedef CircularTooltipCallback = void Function(TooltipArgs tooltipArgs); + +/// Returns the DataLabelRenderArgs. +typedef CircularDatalabelRenderCallback = void Function( + DataLabelRenderArgs dataLabelArgs); + +/// Returns the PointTapArgs. +typedef CircularPointTapCallback = void Function(PointTapArgs pointTapArgs); + +/// Returns the SelectionArgs. +typedef CircularSelectionCallback = void Function(SelectionArgs selectionArgs); + +/// Returns the offset. +typedef CircularTouchInteractionCallback = void Function( + ChartTouchInteractionArgs tapArgs); + +///Signature for the callback that returns the shader value to override the fill color of the data points. +typedef CircularShaderCallback = Shader Function( + ChartShaderDetails chartShaderDetails); + +/// Return the controller for circular series +typedef CircularSeriesRendererCreatedCallback = void Function( + CircularSeriesController controller); + +/// typedef belongs to SfFunnelChart + +/// Returns the LegendRenderArgs. +typedef FunnelLegendRenderCallback = void Function( + LegendRenderArgs legendRenderArganimateCompleteds); + +/// Returns the TooltipArgs. +typedef FunnelTooltipCallback = void Function(TooltipArgs tooltipArgs); + +/// Returns the DataLabelRenderArgs. +typedef FunnelDataLabelRenderCallback = void Function( + DataLabelRenderArgs dataLabelArgs); + +/// Returns the SelectionArgs. +typedef FunnelSelectionCallback = void Function(SelectionArgs selectionArgs); + +///Returns tha PointTapArgs. +typedef FunnelPointTapCallback = void Function(PointTapArgs pointTapArgs); + +/// Returns the Offset +typedef FunnelTouchInteractionCallback = void Function( + ChartTouchInteractionArgs tapArgs); + +/// Called when the renderer for the funnel series is created +typedef FunnelSeriesRendererCreatedCallback = void Function( + FunnelSeriesController controller); + +/// typedef belongs to SfPyramidChart + +/// Returns the LegendRenderArgs. +typedef PyramidLegendRenderCallback = void Function( + LegendRenderArgs legendRenderArganimateCompleteds); + +/// Returns the TooltipArgs. +typedef PyramidTooltipCallback = void Function(TooltipArgs tooltipArgs); + +/// Returns the DataLabelRenderArgs. +typedef PyramidDataLabelRenderCallback = void Function( + DataLabelRenderArgs dataLabelArgs); + +/// Returns the SelectionArgs. +typedef PyramidSelectionCallback = void Function(SelectionArgs selectionArgs); + +///Returns tha PointTapArgs. +typedef PyramidPointTapCallback = void Function(PointTapArgs pointTapArgs); + +/// Returns the Offset +typedef PyramidTouchInteractionCallback = void Function( + ChartTouchInteractionArgs tapArgs); + +/// Called when the pyramid series is created +typedef PyramidSeriesRendererCreatedCallback = void Function( + PyramidSeriesController controller); diff --git a/packages/syncfusion_flutter_charts/lib/src/funnel_chart/base/funnel_base.dart b/packages/syncfusion_flutter_charts/lib/src/funnel_chart/base/funnel_base.dart index e86372f0a..164f4427f 100644 --- a/packages/syncfusion_flutter_charts/lib/src/funnel_chart/base/funnel_base.dart +++ b/packages/syncfusion_flutter_charts/lib/src/funnel_chart/base/funnel_base.dart @@ -1,26 +1,5 @@ part of charts; -/// Returns the LegendRenderArgs. -typedef FunnelLegendRenderCallback = void Function( - LegendRenderArgs legendRenderArganimateCompleteds); - -/// Returns the TooltipArgs. -typedef FunnelTooltipCallback = void Function(TooltipArgs tooltipArgs); - -/// Returns the DataLabelRenderArgs. -typedef FunnelDataLabelRenderCallback = void Function( - DataLabelRenderArgs dataLabelArgs); - -/// Returns the SelectionArgs. -typedef FunnelSelectionCallback = void Function(SelectionArgs selectionArgs); - -///Returns tha PointTapArgs. -typedef FunnelPointTapCallback = void Function(PointTapArgs pointTapArgs); - -/// Returns the Offset -typedef FunnelTouchInteractionCallback = void Function( - ChartTouchInteractionArgs tapArgs); - ///Renders the funnel chart /// ///A funnel chart is a specialized chart type that demonstrates the flow of users through a business or sales process. @@ -41,7 +20,7 @@ class SfFunnelChart extends StatefulWidget { this.onLegendItemRender, this.onTooltipRender, this.onDataLabelRender, - this.onPointTapped, + @deprecated this.onPointTapped, this.onLegendTapped, this.onDataLabelTapped, this.onSelectionChanged, @@ -240,7 +219,7 @@ class SfFunnelChart extends StatefulWidget { /// child: SfFunnelChart( /// series: FunnelSeries( /// initialSelectedDataIndexes: [1,0], - /// selectionSettings: SelectionSettings( + /// selectionBehavior: SelectionBehavior( /// selectedColor: Colors.red, /// unselectedColor: Colors.grey /// ), @@ -263,7 +242,7 @@ class SfFunnelChart extends StatefulWidget { /// child: SfFunnelChart( /// selectionGesture: ActivationMode.singleTap, /// series: FunnelSeries( - /// selectionSettings: SelectionSettings( + /// selectionBehavior: SelectionBehavior( /// selectedColor: Colors.red, /// unselectedColor: Colors.grey /// ), @@ -283,7 +262,7 @@ class SfFunnelChart extends StatefulWidget { /// child: SfFunnelChart( /// enableMultiSelection: true, /// series: FunnelSeries( - /// selectionSettings: SelectionSettings( + /// selectionBehavior: SelectionBehavior( /// selectedColor: Colors.red, /// unselectedColor: Colors.grey /// ), @@ -378,6 +357,7 @@ class SfFunnelChart extends StatefulWidget { /// print(args.pointIndex); ///} ///``` + @Deprecated('Use onPointTap in FunnelSeries instead.') final FunnelPointTapCallback? onPointTapped; //Called when the data label is tapped. @@ -385,7 +365,7 @@ class SfFunnelChart extends StatefulWidget { ///Whenever the data label is tapped, `onDataLabelTapped` callback will be called. Provides options to /// get the position of the data label, series index, point index and its text. /// - ///_Note:_ - This callback will not be called, when the builder is specified for data label + ///_Note:_ This callback will not be called, when the builder is specified for data label /// (data label template). For this case, custom widget specified in the `DataLabelSettings.builder` property /// can be wrapped using the `GestureDetector` and this functionality can be achieved in the application level. ///```dart @@ -410,62 +390,38 @@ class SfFunnelChart extends StatefulWidget { /// class SfFunnelChartState extends State with TickerProviderStateMixin { - // Holds the animation controller list for all series - //ignore: unused_field - late List _controllerList; - late AnimationController - _animationController; // Animation controller for Annotations - //ignore: unused_field - late AnimationController _annotationController; - late ValueNotifier _seriesRepaintNotifier; - late List<_MeasureWidgetContext> - _legendWidgetContext; // To measure legend size and position - late List<_ChartTemplateInfo> _templates; // Chart Template info - late List _chartWidgets; - - /// Holds the information of chart theme arguments - late SfChartThemeData _chartTheme; //Here, we are using get keyword inorder to get the proper & updated instance of chart widget //When we initialize chart widget as a property to other classes like _ChartSeries, the chart widget is not updated properly and by using get we can rectify this. SfFunnelChart get _chart => widget; - late Rect _chartContainerRect; - late Rect _chartAreaRect; - _ChartTemplate? _chartTemplate; - _ChartInteraction? _currentActive; - bool? _initialRender; - late List<_LegendRenderContext> _legendToggleStates; - late List<_MeasureWidgetContext> _legendToggleTemplateStates; - late bool _isLegendToggled; - Offset? _tapPosition; - late bool _animateCompleted; - //ignore: unused_field - late Animation _chartElementAnimation; + + /// Specifies the funnel data label renderer _FunnelDataLabelRenderer? _renderDataLabel; - late bool _widgetNeedUpdate; - late List _explodedPoints; - late List _dataLabelTemplateRegions; - late List _selectionData; + + /// Specifies the tooltip point index int? _tooltipPointIndex; - //ignore: unused_field - late FunnelSeriesRenderer _seriesRenderer; - Orientation? _oldDeviceOrientation; - late Orientation _deviceOrientation; - Size? _prevSize; - bool _didSizeChange = false; - //Internal variables + + /// Specifies the series type late String _seriesType; + + /// Specifies the data points late List> _dataPoints; + + /// Specifies the render points late List> _renderPoints; + + /// Specifies the funnel series late _FunnelSeries _chartSeries; - late _ChartLegend _chartLegend; - //ignore: unused_field + + /// Specifies the funnel plot area late _FunnelPlotArea _funnelplotArea; - late TooltipBehaviorRenderer _tooltipBehaviorRenderer; - late LegendRenderer _legendRenderer; + + /// Specifies the chart rendering details + late _RenderingDetails _renderingDetails; // ignore: unused_element bool get _animationCompleted { - return _animationController.status != AnimationStatus.forward; + return _renderingDetails.animationController.status != + AnimationStatus.forward; } /// Called when this object is inserted into the tree. @@ -481,6 +437,8 @@ class SfFunnelChartState extends State @override void initState() { + _renderingDetails = _RenderingDetails(); + _renderingDetails.didSizeChange = false; _initializeDefaultValues(); // Create the series renderer while initial rendering // _createAndUpdateSeriesRenderer(); @@ -498,7 +456,7 @@ class SfFunnelChartState extends State @override void didChangeDependencies() { - _chartTheme = SfChartTheme.of(context); + _renderingDetails.chartTheme = SfChartTheme.of(context); super.didChangeDependencies(); } @@ -520,14 +478,13 @@ class SfFunnelChartState extends State void didUpdateWidget(SfFunnelChart oldWidget) { //Update and maintain the series state, when we update the series in the series collection // _createAndUpdateSeriesRenderer(oldWidget); - _initialRender = !widget.series.explode; super.didUpdateWidget(oldWidget); - if (_tooltipBehaviorRenderer._chartTooltipState != null) { - _tooltipBehaviorRenderer._show = false; + if (_renderingDetails.tooltipBehaviorRenderer._chartTooltipState != null) { + _renderingDetails.tooltipBehaviorRenderer._show = false; } - _isLegendToggled = false; - _widgetNeedUpdate = true; + _renderingDetails.isLegendToggled = false; + _renderingDetails.widgetNeedUpdate = true; } /// Describes the part of the user interface represented by this widget. @@ -543,13 +500,11 @@ class SfFunnelChartState extends State @override Widget build(BuildContext context) { - _prevSize = _prevSize ?? MediaQuery.of(context).size; - _didSizeChange = _prevSize != MediaQuery.of(context).size; - _prevSize = MediaQuery.of(context).size; - _oldDeviceOrientation = _oldDeviceOrientation == null - ? MediaQuery.of(context).orientation - : _deviceOrientation; - _deviceOrientation = MediaQuery.of(context).orientation; + _renderingDetails.oldDeviceOrientation = + _renderingDetails.oldDeviceOrientation == null + ? MediaQuery.of(context).orientation + : _renderingDetails.deviceOrientation; + _renderingDetails.deviceOrientation = MediaQuery.of(context).orientation; return RepaintBoundary( child: _ChartContainer( child: GestureDetector( @@ -586,7 +541,8 @@ class SfFunnelChartState extends State @override void dispose() { - _disposeAnimationController(_animationController, _repaintChartElements); + _disposeAnimationController( + _renderingDetails.animationController, _repaintChartElements); super.dispose(); } @@ -660,25 +616,24 @@ class SfFunnelChartState extends State /// To initialise chart default values void _initializeDefaultValues() { _chartSeries = _FunnelSeries(this); - _chartLegend = _ChartLegend(this); + _renderingDetails.chartLegend = _ChartLegend(this); _funnelplotArea = _FunnelPlotArea(chartState: this); - _initialRender = true; - _controllerList = []; - _annotationController = AnimationController(vsync: this); - _seriesRepaintNotifier = ValueNotifier(0); - _legendToggleStates = <_LegendRenderContext>[]; - _legendToggleTemplateStates = <_MeasureWidgetContext>[]; - _explodedPoints = []; - _animateCompleted = false; - _isLegendToggled = false; - _widgetNeedUpdate = false; - _dataLabelTemplateRegions = []; - _selectionData = []; - _legendWidgetContext = <_MeasureWidgetContext>[]; - _animationController = AnimationController(vsync: this) + _renderingDetails.initialRender = true; + _renderingDetails.annotationController = AnimationController(vsync: this); + _renderingDetails.seriesRepaintNotifier = ValueNotifier(0); + _renderingDetails.legendToggleStates = <_LegendRenderContext>[]; + _renderingDetails.legendToggleTemplateStates = <_MeasureWidgetContext>[]; + _renderingDetails.explodedPoints = []; + _renderingDetails.animateCompleted = false; + _renderingDetails.isLegendToggled = false; + _renderingDetails.widgetNeedUpdate = false; + _renderingDetails.dataLabelTemplateRegions = []; + _renderingDetails.selectionData = []; + _renderingDetails.legendWidgetContext = <_MeasureWidgetContext>[]; + _renderingDetails.animationController = AnimationController(vsync: this) ..addListener(_repaintChartElements); - _tooltipBehaviorRenderer = TooltipBehaviorRenderer(this); - _legendRenderer = LegendRenderer(widget.legend); + _renderingDetails.tooltipBehaviorRenderer = TooltipBehaviorRenderer(this); + _renderingDetails.legendRenderer = LegendRenderer(widget.legend); } // In this method, create and update the series renderer for each series // @@ -691,7 +646,7 @@ class SfFunnelChartState extends State ? _chartSeries.visibleSeriesRenderers[0] : null; - dynamic series; + FunnelSeries series; series = widget.series; // Create and update the series list here @@ -705,13 +660,12 @@ class SfFunnelChartState extends State if (seriesRenderers._controller == null && series.onRendererCreated != null) { seriesRenderers._controller = FunnelSeriesController(seriesRenderers); - series.onRendererCreated(seriesRenderers._controller); + series.onRendererCreated!(seriesRenderers._controller!); } } seriesRenderers._series = series; - seriesRenderers._isSelectionEnable = - series.selectionBehavior.enable || series.selectionSettings.enable; + seriesRenderers._isSelectionEnable = series.selectionBehavior.enable; seriesRenderers._chartState = this; _chartSeries.visibleSeriesRenderers ..clear() @@ -720,7 +674,7 @@ class SfFunnelChartState extends State } void _repaintChartElements() { - _seriesRepaintNotifier.value++; + _renderingDetails.seriesRepaintNotifier.value++; } /// To render chart elements @@ -728,16 +682,23 @@ class SfFunnelChartState extends State return Expanded(child: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { Widget element; + _renderingDetails.prevSize = + _renderingDetails.prevSize ?? constraints.biggest; + _renderingDetails.didSizeChange = + _renderingDetails.prevSize != constraints.biggest; + _renderingDetails.prevSize = constraints.biggest; if (widget.series.dataSource != null) { _initialize(constraints); _chartSeries._findVisibleSeries(); _chartSeries._processDataPoints(_chartSeries.visibleSeriesRenderers[0]); final List legendTemplates = _bindLegendTemplateWidgets(this); - if (legendTemplates.isNotEmpty && _legendWidgetContext.isEmpty) { + if (legendTemplates.isNotEmpty && + _renderingDetails.legendWidgetContext.isEmpty) { element = Container(child: Stack(children: legendTemplates)); SchedulerBinding.instance!.addPostFrameCallback((_) => _refresh()); } else { - _chartLegend._calculateLegendBounds(_chartLegend.chartSize); + _renderingDetails.chartLegend + ._calculateLegendBounds(_renderingDetails.chartLegend.chartSize); element = _getElements( this, _FunnelPlotArea(chartState: this), constraints)!; } @@ -750,11 +711,12 @@ class SfFunnelChartState extends State /// To refresh chart elements void _refresh() { - if (_legendWidgetContext.isNotEmpty) { - for (int i = 0; i < _legendWidgetContext.length; i++) { - final _MeasureWidgetContext templateContext = _legendWidgetContext[i]; - final RenderBox renderBox = - templateContext.context!.findRenderObject() as RenderBox; + if (_renderingDetails.legendWidgetContext.isNotEmpty) { + _MeasureWidgetContext templateContext; + RenderBox renderBox; + for (int i = 0; i < _renderingDetails.legendWidgetContext.length; i++) { + templateContext = _renderingDetails.legendWidgetContext[i]; + renderBox = templateContext.context!.findRenderObject() as RenderBox; templateContext.size = renderBox.size; } setState(() { @@ -766,9 +728,9 @@ class SfFunnelChartState extends State /// To redraw chart elements // ignore:unused_element void _redraw() { - _initialRender = false; - if (_tooltipBehaviorRenderer._chartTooltipState != null) { - _tooltipBehaviorRenderer._show = false; + _renderingDetails.initialRender = false; + if (_renderingDetails.tooltipBehaviorRenderer._chartTooltipState != null) { + _renderingDetails.tooltipBehaviorRenderer._show = false; } setState(() { /// The chart will be rebuilding again, When we do the legend toggle, zoom/pan the chart. @@ -777,17 +739,16 @@ class SfFunnelChartState extends State /// To initialize chart container void _initialize(BoxConstraints constraints) { - _chartWidgets = []; + _renderingDetails.chartWidgets = []; final num width = constraints.maxWidth; final num height = constraints.maxHeight; final EdgeInsets margin = widget.margin; - if (widget.legend.position == LegendPosition.auto) { - _legendRenderer._legendPosition = - height > width ? LegendPosition.bottom : LegendPosition.right; - } else { - _legendRenderer._legendPosition = widget.legend.position; - } - _chartLegend.chartSize = Size(width - margin.left - margin.right, + _renderingDetails.legendRenderer._legendPosition = + widget.legend.position == LegendPosition.auto + ? (height > width ? LegendPosition.bottom : LegendPosition.right) + : widget.legend.position; + _renderingDetails.chartLegend.chartSize = Size( + width - margin.left - margin.right, height - margin.top - margin.bottom); } } @@ -818,9 +779,10 @@ class _FunnelPlotArea extends StatelessWidget { onHover: (PointerEvent event) => _enableMouseHover ? _onHover(event) : null, onExit: (PointerEvent event) { - chartState._tooltipBehaviorRenderer._isHovering = false; + chartState._renderingDetails.tooltipBehaviorRenderer + ._isHovering = false; }, - child: Stack(children: [ + child: Stack(textDirection: TextDirection.ltr, children: [ _initializeChart(constraints, context), Listener( onPointerUp: (PointerUpEvent event) => _onTapUp(event), @@ -832,13 +794,23 @@ class _FunnelPlotArea extends StatelessWidget { onLongPress: _onLongPress, onDoubleTap: _onDoubleTap, onTapUp: (TapUpDetails details) { - chartState._tapPosition = + chartState._renderingDetails.tapPosition = renderBox.globalToLocal(details.globalPosition); if (chart.onPointTapped != null && // ignore: unnecessary_null_comparison seriesRenderer != null) { _calculatePointSeriesIndex(chart, seriesRenderer, - chartState._tapPosition!); + chartState._renderingDetails.tapPosition!); + } + if (chart.series.onPointTap != null && + // ignore: unnecessary_null_comparison + seriesRenderer != null) { + _calculatePointSeriesIndex( + chart, + seriesRenderer, + chartState._renderingDetails.tapPosition!, + null, + ActivationMode.singleTap); } }, child: Container( @@ -862,10 +834,10 @@ class _FunnelPlotArea extends StatelessWidget { void _calculateContainerSize(BoxConstraints constraints) { final num width = constraints.maxWidth; final num height = constraints.maxHeight; - chartState._chartContainerRect = + chartState._renderingDetails.chartContainerRect = Rect.fromLTWH(0, 0, width.toDouble(), height.toDouble()); final EdgeInsets margin = chart.margin; - chartState._chartAreaRect = Rect.fromLTWH( + chartState._renderingDetails.chartAreaRect = Rect.fromLTWH( margin.left, margin.top, width - margin.right - margin.left, @@ -880,7 +852,10 @@ class _FunnelPlotArea extends StatelessWidget { _bindTooltipWidgets(constraints); renderBox = context.findRenderObject() as RenderBox; chartState._funnelplotArea = this; - return Container(child: Stack(children: chartState._chartWidgets)); + return Container( + child: Stack( + textDirection: TextDirection.ltr, + children: chartState._renderingDetails.chartWidgets!)); } /// To calculate region path for rendering funnel chart @@ -901,19 +876,17 @@ class _FunnelPlotArea extends StatelessWidget { CustomPainter seriesPainter; Animation? seriesAnimation; FunnelSeries series; + SelectionBehaviorRenderer selectionBehaviorRenderer; + dynamic selectionBehavior; for (int i = 0; i < chartState._chartSeries.visibleSeriesRenderers.length; i++) { seriesRenderer = chartState._chartSeries.visibleSeriesRenderers[i]; series = seriesRenderer._series; series.selectionBehavior._chartState = chartState; - series.selectionSettings._chartState = chartState; chartState._chartSeries._initializeSeriesProperties(seriesRenderer); - SelectionBehaviorRenderer selectionBehaviorRenderer; - final dynamic selectionBehavior = seriesRenderer._selectionBehavior = - series.selectionBehavior.enable - ? series.selectionBehavior - : series.selectionSettings; + selectionBehavior = + seriesRenderer._selectionBehavior = series.selectionBehavior; selectionBehaviorRenderer = seriesRenderer._selectionBehaviorRenderer = SelectionBehaviorRenderer(selectionBehavior, chart, chartState); selectionBehaviorRenderer._selectionRenderer ??= _SelectionRenderer(); @@ -924,128 +897,126 @@ class _FunnelPlotArea extends StatelessWidget { for (int index = 0; index < series.initialSelectedDataIndexes.length; index++) { - chartState._selectionData + chartState._renderingDetails.selectionData .add(series.initialSelectedDataIndexes[index]); } } if (series.animationDuration > 0 && - !chartState._didSizeChange && - (chartState._oldDeviceOrientation == chartState._deviceOrientation) && - ((!chartState._widgetNeedUpdate && chartState._initialRender!) || - chartState._isLegendToggled)) { - chartState._animationController.duration = + !chartState._renderingDetails.didSizeChange && + (chartState._renderingDetails.oldDeviceOrientation == + chartState._renderingDetails.deviceOrientation) && + ((!chartState._renderingDetails.widgetNeedUpdate && + chartState._renderingDetails.initialRender!) || + chartState._renderingDetails.isLegendToggled)) { + chartState._renderingDetails.animationController.duration = Duration(milliseconds: series.animationDuration.toInt()); seriesAnimation = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation( - parent: chartState._animationController, + parent: chartState._renderingDetails.animationController, curve: const Interval(0.1, 0.8, curve: Curves.linear), )..addStatusListener((AnimationStatus status) { if (status == AnimationStatus.completed) { - chartState._animateCompleted = true; + chartState._renderingDetails.animateCompleted = true; if (chartState._renderDataLabel != null) { chartState._renderDataLabel!.state!.render(); } - if (chartState._chartTemplate != null && + if (chartState._renderingDetails.chartTemplate != null && // ignore: unnecessary_null_comparison - chartState._chartTemplate!.state != null) { - chartState._chartTemplate!.state.templateRender(); + chartState._renderingDetails.chartTemplate!.state != + null) { + chartState._renderingDetails.chartTemplate!.state + .templateRender(); } } })); - chartState._chartElementAnimation = + chartState._renderingDetails.chartElementAnimation = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation( - parent: chartState._animationController, + parent: chartState._renderingDetails.animationController, curve: const Interval(0.85, 1.0, curve: Curves.decelerate), )); - chartState._animationController.forward(from: 0.0); + chartState._renderingDetails.animationController.forward(from: 0.0); } else { - chartState._animateCompleted = true; + chartState._renderingDetails.animateCompleted = true; if (chartState._renderDataLabel?.state != null) { chartState._renderDataLabel?.state!.render(); } } - seriesRenderer._repaintNotifier = chartState._seriesRepaintNotifier; + seriesRenderer._repaintNotifier = + chartState._renderingDetails.seriesRepaintNotifier; seriesPainter = _FunnelChartPainter( chartState: chartState, seriesIndex: i, isRepaint: seriesRenderer._needsRepaint, - animationController: chartState._animationController, + animationController: chartState._renderingDetails.animationController, seriesAnimation: seriesAnimation, - notifier: chartState._seriesRepaintNotifier); - chartState._chartWidgets + notifier: chartState._renderingDetails.seriesRepaintNotifier); + chartState._renderingDetails.chartWidgets! .add(RepaintBoundary(child: CustomPaint(painter: seriesPainter))); chartState._renderDataLabel = _FunnelDataLabelRenderer( key: GlobalKey(), chartState: chartState, - show: !chartState._widgetNeedUpdate - ? chartState._animationController.status == + //ignore: avoid_bool_literals_in_conditional_expressions + show: !chartState._renderingDetails.widgetNeedUpdate + ? chartState._renderingDetails.animationController.status == AnimationStatus.completed || - chartState._animationController.duration == null + chartState._renderingDetails.animationController.duration == + null : true); - chartState._chartWidgets.add(chartState._renderDataLabel!); + chartState._renderingDetails.chartWidgets! + .add(chartState._renderDataLabel!); } } /// To bind tooltip widgets to chart void _bindTooltipWidgets(BoxConstraints constraints) { chart.tooltipBehavior._chartState = chartState; - final SfChartThemeData _chartTheme = chartState._chartTheme; - final tooltip = chart.tooltipBehavior; + final SfChartThemeData _chartTheme = + chartState._renderingDetails.chartTheme; + final TooltipBehavior tooltip = chart.tooltipBehavior; if (chart.tooltipBehavior.enable) { - chartState._tooltipBehaviorRenderer._prevTooltipValue = - chartState._tooltipBehaviorRenderer._currentTooltipValue = null; - chartState._tooltipBehaviorRenderer._chartTooltip = SfTooltip( - color: tooltip.color ?? _chartTheme.tooltipColor, - key: GlobalKey(), - textStyle: tooltip.textStyle, - animationDuration: tooltip.animationDuration, - enable: tooltip.enable, - opacity: tooltip.opacity, - borderColor: tooltip.borderColor, - borderWidth: tooltip.borderWidth, - duration: tooltip.duration, - shouldAlwaysShow: tooltip.shouldAlwaysShow, - elevation: tooltip.elevation, - canShowMarker: tooltip.canShowMarker, - textAlignment: tooltip.textAlignment, - decimalPlaces: tooltip.decimalPlaces, - labelColor: tooltip.textStyle.color ?? _chartTheme.tooltipLabelColor, - header: tooltip.header, - format: tooltip.format, - builder: tooltip.builder, - shadowColor: tooltip.shadowColor, - onTooltipRender: chart.onTooltipRender != null - ? chartState._tooltipBehaviorRenderer._tooltipRenderingEvent - : null); - chartState._chartWidgets - .add(chartState._tooltipBehaviorRenderer._chartTooltip!); - } - } - - void _calculatePointSeriesIndex(SfFunnelChart chart, - FunnelSeriesRenderer seriesRenderer, Offset touchPosition) { - PointTapArgs pointTapArgs; - int? index; - for (int i = 0; i < seriesRenderer._renderPoints.length; i++) { - if (seriesRenderer._renderPoints[i].region != null && - seriesRenderer._renderPoints[i].region!.contains(touchPosition)) { - index = i; - break; - } - } - if (index != null) { - pointTapArgs = PointTapArgs(0, index, seriesRenderer._dataPoints, index); - chart.onPointTapped!(pointTapArgs); + chartState._renderingDetails.tooltipBehaviorRenderer._prevTooltipValue = + chartState._renderingDetails.tooltipBehaviorRenderer + ._currentTooltipValue = null; + chartState._renderingDetails.tooltipBehaviorRenderer._chartTooltip = + SfTooltip( + color: tooltip.color ?? _chartTheme.tooltipColor, + key: GlobalKey(), + textStyle: tooltip.textStyle, + animationDuration: tooltip.animationDuration, + animationCurve: + const Interval(0.1, 0.8, curve: Curves.easeOutBack), + enable: tooltip.enable, + opacity: tooltip.opacity, + borderColor: tooltip.borderColor, + borderWidth: tooltip.borderWidth, + duration: tooltip.duration.toInt(), + shouldAlwaysShow: tooltip.shouldAlwaysShow, + elevation: tooltip.elevation, + canShowMarker: tooltip.canShowMarker, + textAlignment: tooltip.textAlignment, + decimalPlaces: tooltip.decimalPlaces, + labelColor: + tooltip.textStyle.color ?? _chartTheme.tooltipLabelColor, + header: tooltip.header, + format: tooltip.format, + shadowColor: tooltip.shadowColor, + onTooltipRender: chart.onTooltipRender != null + ? chartState._renderingDetails.tooltipBehaviorRenderer + ._tooltipRenderingEvent + : null); + chartState._renderingDetails.chartWidgets!.add( + chartState._renderingDetails.tooltipBehaviorRenderer._chartTooltip!); } } /// To perform pointer down event void _onTapDown(PointerDownEvent event) { // renderBox = context.findRenderObject(); - chartState._tooltipBehaviorRenderer._isHovering = false; - chartState._currentActive = null; - chartState._tapPosition = renderBox.globalToLocal(event.position); + chartState._renderingDetails.tooltipBehaviorRenderer._isHovering = false; + chartState._renderingDetails.currentActive = null; + chartState._renderingDetails.tapPosition = + renderBox.globalToLocal(event.position); bool isPoint = false; const int seriesIndex = 0; int? pointIndex; @@ -1058,7 +1029,7 @@ class _FunnelPlotArea extends StatelessWidget { } if (seriesRenderer._renderPoints[j].isVisible && !isPoint) { isPoint = _isPointInPolygon(seriesRenderer._renderPoints[j].pathRegion, - chartState._tapPosition); + chartState._renderingDetails.tapPosition!); if (isPoint) { pointIndex = j; if (chart.onDataLabelRender == null) { @@ -1067,10 +1038,10 @@ class _FunnelPlotArea extends StatelessWidget { } } } - doubleTapPosition = chartState._tapPosition!; + doubleTapPosition = chartState._renderingDetails.tapPosition!; // ignore: unnecessary_null_comparison - if (chartState._tapPosition != null && isPoint != null && isPoint) { - chartState._currentActive = _ChartInteraction( + if (chartState._renderingDetails.tapPosition != null && isPoint) { + chartState._renderingDetails.currentActive = _ChartInteraction( seriesIndex, pointIndex!, seriesRenderer._series, @@ -1079,8 +1050,9 @@ class _FunnelPlotArea extends StatelessWidget { } else { //hides the tooltip if the point of interaction is outside funnel region of the chart if (chart.tooltipBehavior.builder != null) { - chartState._tooltipBehaviorRenderer._show = false; - chartState._tooltipBehaviorRenderer._hideOnTimer(); + chartState._renderingDetails.tooltipBehaviorRenderer._show = false; + chartState._renderingDetails.tooltipBehaviorRenderer + ._hideTooltipTemplate(); } } if (chart.onChartTouchInteractionDown != null) { @@ -1104,16 +1076,29 @@ class _FunnelPlotArea extends StatelessWidget { /// To perform double tap touch interactions void _onDoubleTap() { const int seriesIndex = 0; - if (doubleTapPosition != null && chartState._currentActive != null) { - final int? pointIndex = chartState._currentActive!.pointIndex; - chartState._currentActive = _ChartInteraction( + if (doubleTapPosition != null && + chartState._renderingDetails.currentActive != null) { + if (chart.series.onPointDoubleTap != null && + // ignore: unnecessary_null_comparison + seriesRenderer != null) { + _calculatePointSeriesIndex( + chart, + seriesRenderer, + chartState._renderingDetails.tapPosition!, + null, + ActivationMode.doubleTap); + chartState._renderingDetails.tapPosition = null; + } + final int? pointIndex = + chartState._renderingDetails.currentActive!.pointIndex; + chartState._renderingDetails.currentActive = _ChartInteraction( seriesIndex, pointIndex, chartState._chartSeries.visibleSeriesRenderers[seriesIndex]._series, chartState._chartSeries.visibleSeriesRenderers[seriesIndex] ._renderPoints[pointIndex!]); - if (chartState._currentActive != null) { - if (chartState._currentActive!.series.explodeGesture == + if (chartState._renderingDetails.currentActive != null) { + if (chartState._renderingDetails.currentActive!.series.explodeGesture == ActivationMode.doubleTap) { chartState._chartSeries._pointExplode(pointIndex); final GlobalKey key = chartState._renderDataLabel!.key as GlobalKey; @@ -1125,12 +1110,12 @@ class _FunnelPlotArea extends StatelessWidget { chartState._chartSeries ._seriesPointSelection(pointIndex, ActivationMode.doubleTap); if (chart.tooltipBehavior.enable && - chartState._animateCompleted && + chartState._renderingDetails.animateCompleted && chart.tooltipBehavior.activationMode == ActivationMode.doubleTap) { if (chart.tooltipBehavior.builder != null) { _showFunnelTooltipTemplate(); } else { - chartState._tooltipBehaviorRenderer.onDoubleTap( + chartState._renderingDetails.tooltipBehaviorRenderer.onDoubleTap( doubleTapPosition!.dx.toDouble(), doubleTapPosition!.dy.toDouble()); } @@ -1141,9 +1126,22 @@ class _FunnelPlotArea extends StatelessWidget { /// To perform long press touch interactions void _onLongPress() { const int seriesIndex = 0; - if (chartState._tapPosition != null && chartState._currentActive != null) { - final int pointIndex = chartState._currentActive!.pointIndex!; - chartState._currentActive = _ChartInteraction( + if (chartState._renderingDetails.tapPosition != null && + chartState._renderingDetails.currentActive != null) { + if (chart.series.onPointLongPress != null && + // ignore: unnecessary_null_comparison + seriesRenderer != null) { + _calculatePointSeriesIndex( + chart, + seriesRenderer, + chartState._renderingDetails.tapPosition!, + null, + ActivationMode.longPress); + chartState._renderingDetails.tapPosition = null; + } + final int pointIndex = + chartState._renderingDetails.currentActive!.pointIndex!; + chartState._renderingDetails.currentActive = _ChartInteraction( seriesIndex, pointIndex, chartState._chartSeries.visibleSeriesRenderers[seriesIndex]._series, @@ -1152,8 +1150,8 @@ class _FunnelPlotArea extends StatelessWidget { pointRegion); chartState._chartSeries ._seriesPointSelection(pointIndex, ActivationMode.longPress); - if (chartState._currentActive != null) { - if (chartState._currentActive!.series.explodeGesture == + if (chartState._renderingDetails.currentActive != null) { + if (chartState._renderingDetails.currentActive!.series.explodeGesture == ActivationMode.longPress) { chartState._chartSeries._pointExplode(pointIndex); final GlobalKey key = chartState._renderDataLabel!.key as GlobalKey; @@ -1163,14 +1161,14 @@ class _FunnelPlotArea extends StatelessWidget { } } if (chart.tooltipBehavior.enable && - chartState._animateCompleted && + chartState._renderingDetails.animateCompleted && chart.tooltipBehavior.activationMode == ActivationMode.longPress) { if (chart.tooltipBehavior.builder != null) { _showFunnelTooltipTemplate(); } else { - chartState._tooltipBehaviorRenderer.onLongPress( - chartState._tapPosition!.dx.toDouble(), - chartState._tapPosition!.dy.toDouble()); + chartState._renderingDetails.tooltipBehaviorRenderer.onLongPress( + chartState._renderingDetails.tapPosition!.dx.toDouble(), + chartState._renderingDetails.tapPosition!.dy.toDouble()); } } } @@ -1178,41 +1176,43 @@ class _FunnelPlotArea extends StatelessWidget { /// To perform pointer up event void _onTapUp(PointerUpEvent event) { - chartState._tooltipBehaviorRenderer._isHovering = false; - chartState._tapPosition = renderBox.globalToLocal(event.position); + chartState._renderingDetails.tooltipBehaviorRenderer._isHovering = false; + chartState._renderingDetails.tapPosition = + renderBox.globalToLocal(event.position); ChartTouchInteractionArgs touchArgs; // ignore: unnecessary_null_comparison if (chart.onDataLabelTapped != null && seriesRenderer != null) { - _triggerFunnelDataLabelEvent( - chart, seriesRenderer, chartState, chartState._tapPosition!); + _triggerFunnelDataLabelEvent(chart, seriesRenderer, chartState, + chartState._renderingDetails.tapPosition!); } - if (chartState._tapPosition != null) { - if (chartState._currentActive != null && - chartState._currentActive!.series != null && - chartState._currentActive!.series.explodeGesture == + if (chartState._renderingDetails.tapPosition != null) { + if (chartState._renderingDetails.currentActive != null && + chartState._renderingDetails.currentActive!.series != null && + chartState._renderingDetails.currentActive!.series.explodeGesture == ActivationMode.singleTap) { - chartState._chartSeries - ._pointExplode(chartState._currentActive!.pointIndex!); + chartState._chartSeries._pointExplode( + chartState._renderingDetails.currentActive!.pointIndex!); final GlobalKey key = chartState._renderDataLabel!.key as GlobalKey; final _FunnelDataLabelRendererState _funnelDataLabelRendererState = key.currentState as _FunnelDataLabelRendererState; _funnelDataLabelRendererState.dataLabelRepaintNotifier.value++; } - if (chartState._tapPosition != null && - chartState._currentActive != null) { + if (chartState._renderingDetails.tapPosition != null && + chartState._renderingDetails.currentActive != null) { chartState._chartSeries._seriesPointSelection( - chartState._currentActive!.pointIndex!, ActivationMode.singleTap); + chartState._renderingDetails.currentActive!.pointIndex!, + ActivationMode.singleTap); } if (chart.tooltipBehavior.enable && - chartState._animateCompleted && + chartState._renderingDetails.animateCompleted && chart.tooltipBehavior.activationMode == ActivationMode.singleTap && - chartState._currentActive != null && - chartState._currentActive!.series != null) { + chartState._renderingDetails.currentActive != null && + chartState._renderingDetails.currentActive!.series != null) { if (chart.tooltipBehavior.builder != null) { _showFunnelTooltipTemplate(); } else { final Offset position = renderBox.globalToLocal(event.position); - chartState._tooltipBehaviorRenderer + chartState._renderingDetails.tooltipBehaviorRenderer .onTouchUp(position.dx.toDouble(), position.dy.toDouble()); } } @@ -1222,13 +1222,18 @@ class _FunnelPlotArea extends StatelessWidget { chart.onChartTouchInteractionUp!(touchArgs); } } - chartState._tapPosition = null; + if (chart.series.onPointTap == null && + chart.series.onPointDoubleTap == null && + chart.series.onPointLongPress == null) { + chartState._renderingDetails.tapPosition = null; + } } /// To perform event on mouse hover void _onHover(PointerEvent event) { - chartState._currentActive = null; - chartState._tapPosition = renderBox.globalToLocal(event.position); + chartState._renderingDetails.currentActive = null; + chartState._renderingDetails.tapPosition = + renderBox.globalToLocal(event.position); bool? isPoint; const int seriesIndex = 0; int? pointIndex; @@ -1237,15 +1242,15 @@ class _FunnelPlotArea extends StatelessWidget { for (int j = 0; j < seriesRenderer._renderPoints.length; j++) { if (seriesRenderer._renderPoints[j].isVisible) { isPoint = _isPointInPolygon(seriesRenderer._renderPoints[j].pathRegion, - chartState._tapPosition); + chartState._renderingDetails.tapPosition!); if (isPoint) { pointIndex = j; break; } } } - if (chartState._tapPosition != null && isPoint!) { - chartState._currentActive = _ChartInteraction( + if (chartState._renderingDetails.tapPosition != null && isPoint!) { + chartState._renderingDetails.currentActive = _ChartInteraction( seriesIndex, pointIndex!, chartState._chartSeries.visibleSeriesRenderers[seriesIndex]._series, @@ -1254,46 +1259,50 @@ class _FunnelPlotArea extends StatelessWidget { ); } else { //hides the tooltip if the point of interaction is outside funnel region of the chart - chartState._tooltipBehaviorRenderer._hide(); + chartState._renderingDetails.tooltipBehaviorRenderer._hide(); } - if (chartState._tapPosition != null) { + if (chartState._renderingDetails.tapPosition != null) { if (chart.tooltipBehavior.enable && - chartState._animateCompleted && - chartState._currentActive != null && - chartState._currentActive!.series != null) { - chartState._tooltipBehaviorRenderer._isHovering = true; + chartState._renderingDetails.animateCompleted && + chartState._renderingDetails.currentActive != null && + chartState._renderingDetails.currentActive!.series != null) { + chartState._renderingDetails.tooltipBehaviorRenderer._isHovering = true; if (chart.tooltipBehavior.builder != null) { _showFunnelTooltipTemplate(); } else { final Offset position = renderBox.globalToLocal(event.position); - chartState._tooltipBehaviorRenderer + chartState._renderingDetails.tooltipBehaviorRenderer .onEnter(position.dx.toDouble(), position.dy.toDouble()); } } else { - chartState._tooltipBehaviorRenderer._prevTooltipValue = null; - chartState._tooltipBehaviorRenderer._currentTooltipValue = null; + chartState._renderingDetails.tooltipBehaviorRenderer._prevTooltipValue = + null; + chartState._renderingDetails.tooltipBehaviorRenderer + ._currentTooltipValue = null; } } - chartState._tapPosition = null; + chartState._renderingDetails.tapPosition = null; } /// This method gets executed for showing tooltip when builder is provided in behavior void _showFunnelTooltipTemplate([int? pointIndex]) { - if (!chartState._tooltipBehaviorRenderer._isHovering) { + if (!chartState._renderingDetails.tooltipBehaviorRenderer._isHovering) { //assingning null for the previous and current tooltip values in case of touch interaction - chartState._tooltipBehaviorRenderer._prevTooltipValue = null; - chartState._tooltipBehaviorRenderer._currentTooltipValue = null; + chartState._renderingDetails.tooltipBehaviorRenderer._prevTooltipValue = + null; + chartState._renderingDetails.tooltipBehaviorRenderer + ._currentTooltipValue = null; } final FunnelSeries chartSeries = - chartState._currentActive?.series ?? chart.series; + chartState._renderingDetails.currentActive?.series ?? chart.series; final PointInfo point = pointIndex == null - ? chartState._currentActive?.point + ? chartState._renderingDetails.currentActive?.point : chartState ._chartSeries.visibleSeriesRenderers[0]._dataPoints[pointIndex]; final Offset location = chart.tooltipBehavior.tooltipPosition == TooltipPosition.pointer && !chartState._chartSeries.visibleSeriesRenderers[0]._series.explode - ? chartState._tapPosition! + ? chartState._renderingDetails.tapPosition! : point.symbolLocation; bool isPoint = false; for (int j = 0; j < seriesRenderer._renderPoints.length; j++) { @@ -1308,31 +1317,39 @@ class _FunnelPlotArea extends StatelessWidget { } // ignore: unnecessary_null_comparison if (location != null && isPoint && (chartSeries.enableTooltip)) { - chartState._tooltipBehaviorRenderer._showLocation = location; - chartState._tooltipBehaviorRenderer._renderBox?.boundaryRect = - chartState._chartContainerRect; + chartState._renderingDetails.tooltipBehaviorRenderer._showLocation = + location; + chartState._renderingDetails.tooltipBehaviorRenderer._chartTooltipState! + .boundaryRect = + chartState._renderingDetails.tooltipBehaviorRenderer._tooltipBounds = + chartState._renderingDetails.chartContainerRect; // tooltipTemplate.rect = Rect.fromLTWH(location.dx, location.dy, 0, 0); - chartState._tooltipBehaviorRenderer._tooltipTemplate = + chartState._renderingDetails.tooltipBehaviorRenderer._tooltipTemplate = chart.tooltipBehavior.builder!( - chartSeries.dataSource![ - pointIndex ?? chartState._currentActive!.pointIndex!], + chartSeries.dataSource![pointIndex ?? + chartState._renderingDetails.currentActive!.pointIndex!], point, chartSeries, 0, - pointIndex ?? chartState._currentActive!.pointIndex!); - if (chartState._tooltipBehaviorRenderer._isHovering) { + pointIndex ?? + chartState._renderingDetails.currentActive!.pointIndex!); + if (chartState._renderingDetails.tooltipBehaviorRenderer._isHovering) { //assingning values for the previous and current tooltip values on mouse hover - chartState._tooltipBehaviorRenderer._prevTooltipValue = - chartState._tooltipBehaviorRenderer._currentTooltipValue; - chartState._tooltipBehaviorRenderer._currentTooltipValue = TooltipValue( - 0, pointIndex ?? chartState._currentActive!.pointIndex!); + chartState._renderingDetails.tooltipBehaviorRenderer._prevTooltipValue = + chartState + ._renderingDetails.tooltipBehaviorRenderer._currentTooltipValue; + chartState._renderingDetails.tooltipBehaviorRenderer + ._currentTooltipValue = + TooltipValue( + 0, + pointIndex ?? + chartState._renderingDetails.currentActive!.pointIndex!); } else { - chartState._tooltipBehaviorRenderer._timer = Timer( - Duration(milliseconds: chart.tooltipBehavior.duration.toInt()), - chartState._tooltipBehaviorRenderer._hideTooltipTemplate); + chartState._renderingDetails.tooltipBehaviorRenderer + ._hideTooltipTemplate(); } - chartState._tooltipBehaviorRenderer._show = true; - chartState._tooltipBehaviorRenderer.._performTooltip(); + chartState._renderingDetails.tooltipBehaviorRenderer._show = true; + chartState._renderingDetails.tooltipBehaviorRenderer._performTooltip(); } } } diff --git a/packages/syncfusion_flutter_charts/lib/src/funnel_chart/base/series_base.dart b/packages/syncfusion_flutter_charts/lib/src/funnel_chart/base/series_base.dart index f6512d63f..1aa0e4423 100644 --- a/packages/syncfusion_flutter_charts/lib/src/funnel_chart/base/series_base.dart +++ b/packages/syncfusion_flutter_charts/lib/src/funnel_chart/base/series_base.dart @@ -77,8 +77,11 @@ class _FunnelSeries { final ChartIndexedValueMapper? textMapping = currentSeries.textFieldMapper; final List> points = seriesRenderer._renderPoints; + PointInfo currentPoint; + List<_MeasureWidgetContext> legendToggles; + _MeasureWidgetContext item; + _LegendRenderContext legendRenderContext; for (int i = 0; i < points.length; i++) { - PointInfo currentPoint; currentPoint = points[i]; // ignore: unnecessary_null_comparison currentPoint.fill = currentPoint.isEmpty && empty.color != null @@ -105,11 +108,11 @@ class _FunnelSeries { : currentPoint.y!.toString()); if (_chartState._chart.legend.legendItemBuilder != null) { - final List<_MeasureWidgetContext> legendToggles = - _chartState._legendToggleTemplateStates; + legendToggles = + _chartState._renderingDetails.legendToggleTemplateStates; if (legendToggles.isNotEmpty) { for (int j = 0; j < legendToggles.length; j++) { - final _MeasureWidgetContext item = legendToggles[j]; + item = legendToggles[j]; if (i == item.pointIndex) { currentPoint.isVisible = false; break; @@ -117,10 +120,12 @@ class _FunnelSeries { } } } else { - if (_chartState._legendToggleStates.isNotEmpty) { - for (int j = 0; j < _chartState._legendToggleStates.length; j++) { - final _LegendRenderContext legendRenderContext = - _chartState._legendToggleStates[j]; + if (_chartState._renderingDetails.legendToggleStates.isNotEmpty) { + for (int j = 0; + j < _chartState._renderingDetails.legendToggleStates.length; + j++) { + legendRenderContext = + _chartState._renderingDetails.legendToggleStates[j]; if (i == legendRenderContext.seriesIndex) { currentPoint.isVisible = false; break; @@ -143,9 +148,9 @@ class _FunnelSeries { /// To initialise series properties void _initializeSeriesProperties(FunnelSeriesRenderer seriesRenderer) { - final Rect chartAreaRect = _chartState._chartAreaRect; + final Rect chartAreaRect = _chartState._renderingDetails.chartAreaRect; final FunnelSeries series = seriesRenderer._series; - final bool reverse = seriesRenderer._seriesType == 'pyramid' ? true : false; + final bool reverse = seriesRenderer._seriesType == 'pyramid'; seriesRenderer._triangleSize = Size( _percentToValue(series.width, chartAreaRect.width)!.toDouble(), _percentToValue(series.height, chartAreaRect.height)!.toDouble()); @@ -164,10 +169,9 @@ class _FunnelSeries { double y; assert( // ignore: unnecessary_null_comparison - seriesRenderer._series.gapRatio != null - ? seriesRenderer._series.gapRatio >= 0 && - seriesRenderer._series.gapRatio <= 1 - : true, + !(seriesRenderer._series.gapRatio != null) || + seriesRenderer._series.gapRatio >= 0 && + seriesRenderer._series.gapRatio <= 1, 'The gap ratio for the funnel chart must be between 0 and 1.'); final double gapRatio = min(max(seriesRenderer._series.gapRatio, 0), 1); final double coEff = @@ -208,22 +212,23 @@ class _FunnelSeries { final SfFunnelChartState chartState = _chartState; final PointInfo point = seriesRenderer._renderPoints[pointIndex]; if (seriesRenderer._series.explode) { - if (chartState._explodedPoints.isNotEmpty) { + if (chartState._renderingDetails.explodedPoints.isNotEmpty) { existExplodedRegion = true; - final int previousIndex = chartState._explodedPoints[0]; + final int previousIndex = + chartState._renderingDetails.explodedPoints[0]; seriesRenderer._renderPoints[previousIndex].explodeDistance = 0; point.explodeDistance = previousIndex == pointIndex ? 0 : seriesRenderer._explodeDistance; - chartState._explodedPoints[0] = pointIndex; + chartState._renderingDetails.explodedPoints[0] = pointIndex; if (previousIndex == pointIndex) { - chartState._explodedPoints = []; + chartState._renderingDetails.explodedPoints = []; } - chartState._seriesRepaintNotifier.value++; + chartState._renderingDetails.seriesRepaintNotifier.value++; } if (!existExplodedRegion) { point.explodeDistance = seriesRenderer._explodeDistance; - chartState._explodedPoints.add(pointIndex); - chartState._seriesRepaintNotifier.value++; + chartState._renderingDetails.explodedPoints.add(pointIndex); + chartState._renderingDetails.seriesRepaintNotifier.value++; } _calculateFunnelPathRegion(pointIndex, seriesRenderer); } @@ -234,13 +239,13 @@ class _FunnelSeries { int pointIndex, FunnelSeriesRenderer seriesRenderer) { num lineWidth, topRadius, bottomRadius, endTop, endBottom, top, bottom; num? minRadius, bottomY; - late num endMin; + num endMin = 0; final Size area = seriesRenderer._triangleSize; const num offset = 0; final PointInfo currentPoint = seriesRenderer._renderPoints[pointIndex]; currentPoint.pathRegion = []; - final Rect rect = _chartState._chartContainerRect; + final Rect rect = _chartState._renderingDetails.chartContainerRect; //ignore: prefer_if_null_operators final num extraSpace = (currentPoint.explodeDistance != null ? currentPoint.explodeDistance! @@ -259,15 +264,14 @@ class _FunnelSeries { (area.height - neckSize.height)); topRadius = (area.width / 2) - lineWidth / 2; endTop = topRadius + lineWidth; - if (bottom > area.height - neckSize.height || - area.height == neckSize.height) { - lineWidth = neckSize.width; - } else { - lineWidth = neckSize.width + - (area.width - neckSize.width) * - ((area.height - neckSize.height - bottom) / - (area.height - neckSize.height)); - } + lineWidth = (bottom > area.height - neckSize.height || + area.height == neckSize.height) + ? neckSize.width + : neckSize.width + + (area.width - neckSize.width) * + ((area.height - neckSize.height - bottom) / + (area.height - neckSize.height)); + bottomRadius = (area.width / 2) - (lineWidth / 2); endBottom = bottomRadius + lineWidth; if (top >= area.height - neckSize.height) { @@ -282,6 +286,23 @@ class _FunnelSeries { top += seriesTop; bottom += seriesTop; bottomY = (bottomY != null) ? (bottomY + seriesTop) : null; + final List values = [ + emptySpaceAtLeft, + offset, + topRadius, + endTop, + endBottom, + bottomRadius, + endMin, + top, + bottom + ]; + _addPathToCurrentPoint(currentPoint, values, minRadius, bottomY); + _calculatePathSegment(seriesRenderer._seriesType, currentPoint); + } + + void _addPathToCurrentPoint(PointInfo currentPoint, List values, + num? minRadius, num? bottomY) { late num line1X, line1Y, line2X, @@ -294,23 +315,22 @@ class _FunnelSeries { line5Y, line6X, line6Y; - line1X = emptySpaceAtLeft + offset + topRadius; - line1Y = top; - line2X = emptySpaceAtLeft + offset + endTop; - line2Y = top; - line4X = emptySpaceAtLeft + offset + endBottom; - line4Y = bottom; - line5X = emptySpaceAtLeft + offset + bottomRadius; - line5Y = bottom; - line3X = emptySpaceAtLeft + offset + endBottom; - line3Y = bottom; - line6X = emptySpaceAtLeft + offset + bottomRadius; - line6Y = bottom; + line1X = values[0] + values[1] + values[2]; + line1Y = values[7]; + line2X = values[0] + values[1] + values[3]; + line2Y = values[7]; + line4X = values[0] + values[1] + values[4]; + line4Y = values[8]; + line5X = values[0] + values[1] + values[5]; + line5Y = values[8]; + line3X = values[0] + values[1] + values[4]; + line3Y = values[8]; + line6X = values[0] + values[1] + values[5]; + line6Y = values[8]; if (bottomY != null) { - line3X = emptySpaceAtLeft + offset + endMin; + line3X = values[0] + values[1] + values[6]; line3Y = bottomY; - line6X = - emptySpaceAtLeft + offset + ((minRadius != null) ? minRadius : 0); + line6X = values[0] + values[1] + ((minRadius != null) ? minRadius : 0); line6Y = bottomY; } currentPoint.pathRegion.add(Offset(line1X.toDouble(), line1Y.toDouble())); @@ -319,7 +339,6 @@ class _FunnelSeries { currentPoint.pathRegion.add(Offset(line4X.toDouble(), line4Y.toDouble())); currentPoint.pathRegion.add(Offset(line5X.toDouble(), line5Y.toDouble())); currentPoint.pathRegion.add(Offset(line6X.toDouble(), line6Y.toDouble())); - _calculatePathSegment(seriesRenderer._seriesType, currentPoint); } /// To calculate the funnel segments and render path @@ -368,7 +387,9 @@ class _FunnelSeries { canvas, _StyleOptions( fill: fillColor, - strokeWidth: _chartState._animateCompleted ? strokeWidth : 0, + strokeWidth: _chartState._renderingDetails.animateCompleted + ? strokeWidth + : 0, strokeColor: strokeColor, opacity: opacity), path); @@ -380,39 +401,60 @@ class _FunnelSeries { final SfFunnelChart chart = _chartState._chart; final FunnelSeriesRenderer seriesRenderer = _chartState._chartSeries.visibleSeriesRenderers[0]; - final List selectionData = _chartState._selectionData; + final List selectionData = _chartState._renderingDetails.selectionData; int? currentSelectedIndex; + const int seriesIndex = 0; if (seriesRenderer._isSelectionEnable && mode == chart.selectionGesture) { if (selectionData.isNotEmpty) { if (!chart.enableMultiSelection && - _chartState._selectionData.isNotEmpty && - _chartState._selectionData.length > 1) { - if (_chartState._selectionData.contains(pointIndex)) { + _chartState._renderingDetails.selectionData.isNotEmpty && + _chartState._renderingDetails.selectionData.length > 1) { + if (_chartState._renderingDetails.selectionData + .contains(pointIndex)) { currentSelectedIndex = pointIndex; } - _chartState._selectionData.clear(); + _chartState._renderingDetails.selectionData.clear(); if (currentSelectedIndex != null) { - _chartState._selectionData.add(pointIndex); + _chartState._renderingDetails.selectionData.add(pointIndex); } } + + int selectionIndex; for (int i = 0; i < selectionData.length; i++) { - final int selectionIndex = selectionData[i]; + selectionIndex = selectionData[i]; if (!chart.enableMultiSelection) { isPointAlreadySelected = selectionData.length == 1 && pointIndex == selectionIndex; - selectionData.removeAt(i); - _chartState._seriesRepaintNotifier.value++; + if (seriesRenderer._selectionBehavior.toggleSelection == true || + !isPointAlreadySelected) { + selectionData.removeAt(i); + } + _chartState._renderingDetails.seriesRepaintNotifier.value++; + if (chart.onSelectionChanged != null) { + chart.onSelectionChanged!(_getSelectionEventArgs( + seriesRenderer, seriesIndex, selectionIndex)); + } } else if (pointIndex == selectionIndex) { - selectionData.removeAt(i); + if (seriesRenderer._selectionBehavior.toggleSelection == true) { + selectionData.removeAt(i); + } isPointAlreadySelected = true; - _chartState._seriesRepaintNotifier.value++; + _chartState._renderingDetails.seriesRepaintNotifier.value++; + if (chart.onSelectionChanged != null) { + chart.onSelectionChanged!(_getSelectionEventArgs( + seriesRenderer, seriesIndex, selectionIndex)); + } break; } } } if (!isPointAlreadySelected) { selectionData.add(pointIndex); - _chartState._seriesRepaintNotifier.value++; + _chartState._renderingDetails.seriesRepaintNotifier.value++; + if (chart.onSelectionChanged != null) { + chart.onSelectionChanged!( + _getSelectionEventArgs(seriesRenderer, seriesIndex, pointIndex)); + } } } } @@ -424,20 +466,13 @@ class _FunnelSeries { SfFunnelChartState _chartState, PointInfo point) { _StyleOptions? pointStyle; - final SfFunnelChart chart = _chartState._chart; - final dynamic selection = seriesRenderer._series.selectionBehavior.enable - ? seriesRenderer._series.selectionBehavior - : seriesRenderer._series.selectionSettings; - const int seriesIndex = 0; - final List selectionData = _chartState._selectionData; - if (selection.enable) { + final dynamic selection = seriesRenderer._series.selectionBehavior; + final List selectionData = _chartState._renderingDetails.selectionData; + if (selection.enable == true) { if (selectionData.isNotEmpty) { + int selectionIndex; for (int i = 0; i < selectionData.length; i++) { - final int selectionIndex = selectionData[i]; - if (chart.onSelectionChanged != null) { - chart.onSelectionChanged!(_getSelectionEventArgs( - seriesRenderer, seriesIndex, selectionIndex)); - } + selectionIndex = selectionData[i]; if (currentPointIndex == selectionIndex) { pointStyle = _StyleOptions( fill: _selectionArgs != null @@ -472,8 +507,8 @@ class _FunnelSeries { /// To perform selection event and return selectionArgs SelectionArgs _getSelectionEventArgs( - dynamic seriesRenderer, int seriesIndex, int pointIndex) { - final SfFunnelChart chart = seriesRenderer._chartState!._chart; + FunnelSeriesRenderer seriesRenderer, int seriesIndex, int pointIndex) { + final SfFunnelChart chart = seriesRenderer._chartState._chart; if (pointIndex < chart.series.dataSource!.length) { final dynamic selectionBehavior = seriesRenderer._selectionBehavior; _selectionArgs = SelectionArgs( diff --git a/packages/syncfusion_flutter_charts/lib/src/funnel_chart/renderer/data_label_renderer.dart b/packages/syncfusion_flutter_charts/lib/src/funnel_chart/renderer/data_label_renderer.dart index 1dd31d311..ee06a4faf 100644 --- a/packages/syncfusion_flutter_charts/lib/src/funnel_chart/renderer/data_label_renderer.dart +++ b/packages/syncfusion_flutter_charts/lib/src/funnel_chart/renderer/data_label_renderer.dart @@ -38,8 +38,9 @@ class _FunnelDataLabelRendererState extends State<_FunnelDataLabelRenderer> @override Widget build(BuildContext context) { widget.state = this; - animationController.duration = - Duration(milliseconds: widget.chartState._initialRender! ? 500 : 0); + animationController.duration = Duration( + milliseconds: + widget.chartState._renderingDetails.initialRender! ? 500 : 0); final Animation dataLabelAnimation = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation( parent: animationController, @@ -129,6 +130,7 @@ void _renderFunnelDataLabel( TextStyle dataLabelStyle; final List renderDataLabelRegions = []; DataLabelSettingsRenderer dataLabelSettingsRenderer; + Size textSize; for (int pointIndex = 0; pointIndex < seriesRenderer._renderPoints.length; pointIndex++) { @@ -159,7 +161,7 @@ void _renderFunnelDataLabel( ? _getDataLabelTextStyle( seriesRenderer, point, chartState, animateOpacity) : dataLabelStyle; - final Size textSize = measureText(label!, dataLabelStyle); + textSize = measureText(label!, dataLabelStyle); ///Label check after event if (label != '') { @@ -282,14 +284,14 @@ void _renderOutsideFunnelDataLabel( Rect? rect; Offset labelLocation; // Maximum available space for rendering datalabel. - final int maximumAvailableWidth = 22; + const int maximumAvailableWidth = 22; final EdgeInsets margin = seriesRenderer._series.dataLabelSettings.margin; final ConnectorLineSettings connector = seriesRenderer._series.dataLabelSettings.connectorLineSettings; const num regionPadding = 10; connectorPath = Path(); - final num connectorLength = _percentToValue( - connector.length ?? '0%', _chartState._chartAreaRect.width / 2)! + + final num connectorLength = _percentToValue(connector.length ?? '0%', + _chartState._renderingDetails.chartAreaRect.width / 2)! + seriesRenderer._maximumDataLabelRegion.width / 2 - regionPadding; final List regions = @@ -304,10 +306,10 @@ void _renderOutsideFunnelDataLabel( connectorLength; final Offset endPoint = Offset( (dx + textSize.width + margin.left + margin.right) > - _chartState._chartAreaRect.right + _chartState._renderingDetails.chartAreaRect.right ? dx - (_percentToValue(seriesRenderer._series.explodeOffset, - _chartState._chartAreaRect.width)!) + _chartState._renderingDetails.chartAreaRect.width)!) : dx, (regions[1].dy + regions[2].dy) / 2); connectorPath.moveTo(startPoint.dx, startPoint.dy); @@ -318,7 +320,7 @@ void _renderOutsideFunnelDataLabel( rect = _getDataLabelRect(point.dataLabelPosition!, connector.type, margin, connectorPath, endPoint, textSize); if (rect != null) { - final Rect containerRect = _chartState._chartAreaRect; + final Rect containerRect = _chartState._renderingDetails.chartAreaRect; point.labelRect = rect; labelLocation = Offset(rect.left + margin.left, rect.top + rect.height / 2 - textSize.height / 2); @@ -430,9 +432,10 @@ void _drawFunnelLabel( ..color = connector.width <= 0 ? Colors.transparent : connector.color ?? - point.fill.withOpacity(!_chartState._isLegendToggled - ? animateOpacity - : dataLabel.opacity) + point.fill.withOpacity( + !_chartState._renderingDetails.isLegendToggled + ? animateOpacity + : dataLabel.opacity) ..strokeWidth = connector.width ..style = PaintingStyle.stroke); } @@ -449,7 +452,9 @@ void _drawFunnelLabel( if (strokeWidth != null && strokeWidth > 0) { rectPaint = Paint() ..color = strokeColor!.withOpacity( - !_chartState._isLegendToggled ? animateOpacity : dataLabel.opacity) + !_chartState._renderingDetails.isLegendToggled + ? animateOpacity + : dataLabel.opacity) ..style = PaintingStyle.stroke ..strokeWidth = strokeWidth; _drawLabelRect( @@ -462,11 +467,12 @@ void _drawFunnelLabel( if (labelFill != null) { _drawLabelRect( Paint() - ..color = labelFill.withOpacity(!_chartState._isLegendToggled - ? (animateOpacity - (1 - dataLabel.opacity)) < 0 - ? 0 - : animateOpacity - (1 - dataLabel.opacity) - : dataLabel.opacity) + ..color = labelFill + .withOpacity(!_chartState._renderingDetails.isLegendToggled + ? (animateOpacity - (1 - dataLabel.opacity)) < 0 + ? 0 + : animateOpacity - (1 - dataLabel.opacity) + : dataLabel.opacity) ..style = PaintingStyle.fill, Rect.fromLTRB( labelRect.left, labelRect.top, labelRect.right, labelRect.bottom), @@ -483,12 +489,14 @@ void _triggerFunnelDataLabelEvent( FunnelSeriesRenderer seriesRenderer, SfFunnelChartState chartState, Offset position) { - final int seriesIndex = 0; + const int seriesIndex = 0; + PointInfo point; + DataLabelSettings dataLabel; + Offset labelLocation; for (int index = 0; index < seriesRenderer._renderPoints.length; index++) { - final PointInfo point = seriesRenderer._renderPoints[index]; - final DataLabelSettings dataLabel = - seriesRenderer._series.dataLabelSettings; - final Offset labelLocation = point.symbolLocation; + point = seriesRenderer._renderPoints[index]; + dataLabel = seriesRenderer._series.dataLabelSettings; + labelLocation = point.symbolLocation; if (dataLabel.isVisible && seriesRenderer._renderPoints[index].labelRect != null && seriesRenderer._renderPoints[index].labelRect!.contains(position)) { diff --git a/packages/syncfusion_flutter_charts/lib/src/funnel_chart/renderer/funnel_series.dart b/packages/syncfusion_flutter_charts/lib/src/funnel_chart/renderer/funnel_series.dart index a894b1bc5..bbe571e78 100644 --- a/packages/syncfusion_flutter_charts/lib/src/funnel_chart/renderer/funnel_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/funnel_chart/renderer/funnel_series.dart @@ -6,6 +6,9 @@ class _FunnelSeriesBase extends ChartSeries this.key, this.onCreateRenderer, this.onRendererCreated, + this.onPointTap, + this.onPointDoubleTap, + this.onPointLongPress, this.dataSource, this.xValueMapper, this.yValueMapper, @@ -28,8 +31,6 @@ class _FunnelSeriesBase extends ChartSeries DataLabelSettings? dataLabelSettings, double? animationDuration, double? opacity, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, List? initialSelectedDataIndexes, }) : neckWidth = neckWidth ?? '20%', @@ -44,12 +45,10 @@ class _FunnelSeriesBase extends ChartSeries borderColor = borderColor ?? Colors.transparent, borderWidth = borderWidth ?? 0.0, legendIconType = legendIconType ?? LegendIconType.seriesType, - dataLabelSettings = dataLabelSettings ?? DataLabelSettings(), + dataLabelSettings = dataLabelSettings ?? const DataLabelSettings(), animationDuration = animationDuration ?? 1500, opacity = opacity ?? 1, initialSelectedDataIndexes = initialSelectedDataIndexes ?? [], - // ignore: deprecated_member_use_from_same_package - selectionSettings = selectionSettings ?? SelectionSettings(), selectionBehavior = selectionBehavior ?? SelectionBehavior(), super( name: name, @@ -448,25 +447,6 @@ class _FunnelSeriesBase extends ChartSeries @override final double opacity; - ///Customizes the selection of series. - /// - ///```dart - ///Widget build(BuildContext context) { - /// return Container( - /// child: SfFunnelChart( - /// series: FunnelSeries( - /// selectionSettings: SelectionSettings( - /// selectedColor: Colors.red, - /// unselectedColor: Colors.grey - /// ), - /// ) - /// )); - ///} - ///``` - @override - // ignore: deprecated_member_use_from_same_package - final SelectionSettings selectionSettings; - ///Customizes the selection of series. /// ///```dart @@ -605,6 +585,69 @@ class _FunnelSeriesBase extends ChartSeries ///``` final FunnelSeriesRendererCreatedCallback? onRendererCreated; + ///Called when tapped on the chart data point. + /// + ///The user can fetch the series index, point index, viewport point index and + /// data of the tapped data point. + ///```dart + ///Widget build(BuildContext context) { + /// ChartSeriesController _chartSeriesController; + /// return Container( + /// child: SfCartesianChart( + /// series: >[ + /// LineSeries( + /// onPointTap: (ChartPointDetails details) { + /// print(details.pointIndex); + /// }, + /// ), + /// ], + /// )); + ///} + ///``` + final ChartPointInteractionCallback? onPointTap; + + ///Called when double tapped on the chart data point. + /// + ///The user can fetch the series index, point index, viewport point index and + /// data of the double-tapped data point. + ///```dart + ///Widget build(BuildContext context) { + /// ChartSeriesController _chartSeriesController; + /// return Container( + /// child: SfCartesianChart( + /// series: >[ + /// LineSeries( + /// onPointDoubleTap: (ChartPointDetails details) { + /// print(details.pointIndex); + /// }, + /// ), + /// ], + /// )); + ///} + ///``` + final ChartPointInteractionCallback? onPointDoubleTap; + + ///Called when long pressed on the chart data point. + /// + ///The user can fetch the series index, point index, viewport point index and + /// data of the long-pressed data point. + ///```dart + ///Widget build(BuildContext context) { + /// ChartSeriesController _chartSeriesController; + /// return Container( + /// child: SfCartesianChart( + /// series: >[ + /// LineSeries( + /// onPointLongPress: (ChartPointDetails details) { + /// print(details.pointIndex); + /// }, + /// ), + /// ], + /// )); + ///} + ///``` + final ChartPointInteractionCallback? onPointLongPress; + /// To calculate empty point values if null values are provided @override void calculateEmptyPointValue( @@ -652,6 +695,9 @@ class FunnelSeries extends _FunnelSeriesBase { ValueKey? key, ChartSeriesRendererFactory? onCreateRenderer, FunnelSeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, List? dataSource, ChartValueMapper? xValueMapper, ChartValueMapper? yValueMapper, @@ -673,8 +719,6 @@ class FunnelSeries extends _FunnelSeriesBase { bool? explode, ActivationMode? explodeGesture, String? explodeOffset, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, num? explodeIndex, List? initialSelectedDataIndexes, @@ -682,6 +726,9 @@ class FunnelSeries extends _FunnelSeriesBase { key: key, onCreateRenderer: onCreateRenderer, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, dataSource: dataSource, xValueMapper: (int index) => xValueMapper!(dataSource![index], index), @@ -710,7 +757,6 @@ class FunnelSeries extends _FunnelSeriesBase { explodeIndex: explodeIndex, explodeGesture: explodeGesture, explodeOffset: explodeOffset, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, initialSelectedDataIndexes: initialSelectedDataIndexes); @@ -726,6 +772,86 @@ class FunnelSeries extends _FunnelSeriesBase { } return FunnelSeriesRenderer(); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is FunnelSeries && + other.onCreateRenderer == onCreateRenderer && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper && + other.pointColorMapper == pointColorMapper && + other.textFieldMapper == textFieldMapper && + other.name == name && + other.neckWidth == neckWidth && + other.neckHeight == neckHeight && + other.height == height && + other.width == width && + other.gapRatio == gapRatio && + other.legendIconType == legendIconType && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.animationDuration == animationDuration && + other.opacity == opacity && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.explode == explode && + other.explodeGesture == explodeGesture && + other.explodeOffset == explodeOffset && + other.selectionBehavior == selectionBehavior && + other.explodeIndex == explodeIndex && + listEquals( + other.initialSelectedDataIndexes, initialSelectedDataIndexes); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode { + final List values = [ + onCreateRenderer, + onRendererCreated, + onPointTap, + onPointDoubleTap, + onPointLongPress, + dataSource, + xValueMapper, + yValueMapper, + pointColorMapper, + textFieldMapper, + name, + neckWidth, + neckHeight, + height, + width, + gapRatio, + legendIconType, + emptyPointSettings, + dataLabelSettings, + animationDuration, + opacity, + borderColor, + borderWidth, + explode, + explodeGesture, + explodeOffset, + selectionBehavior, + explodeIndex, + initialSelectedDataIndexes + ]; + return hashList(values); + } } class _FunnelChartPainter extends CustomPainter { @@ -750,30 +876,33 @@ class _FunnelChartPainter extends CustomPainter { void paint(Canvas canvas, Size size) { seriesRenderer = chartState._chartSeries.visibleSeriesRenderers[seriesIndex]; + double animationFactor; + double factor; + double height; for (int pointIndex = 0; pointIndex < seriesRenderer._renderPoints.length; pointIndex++) { if (seriesRenderer._renderPoints[pointIndex].isVisible) { - final double animationFactor = - seriesAnimation != null ? seriesAnimation!.value : 1; + animationFactor = seriesAnimation != null ? seriesAnimation!.value : 1; if (seriesRenderer._series.animationDuration > 0 && - !chartState._isLegendToggled) { - final double factor = (chartState._chartAreaRect.top + - chartState._chartAreaRect.height) - + !chartState._renderingDetails.isLegendToggled) { + factor = (chartState._renderingDetails.chartAreaRect.top + + chartState._renderingDetails.chartAreaRect.height) - animationFactor * - (chartState._chartAreaRect.top + - chartState._chartAreaRect.height); - final double height = chartState._chartAreaRect.top + - chartState._chartAreaRect.height - + (chartState._renderingDetails.chartAreaRect.top + + chartState._renderingDetails.chartAreaRect.height); + height = chartState._renderingDetails.chartAreaRect.top + + chartState._renderingDetails.chartAreaRect.height - factor; canvas.clipRect(Rect.fromLTRB( 0, - chartState._chartAreaRect.top + - chartState._chartAreaRect.height - + chartState._renderingDetails.chartAreaRect.top + + chartState._renderingDetails.chartAreaRect.height - height, - chartState._chartAreaRect.left + chartState._chartAreaRect.width, - chartState._chartAreaRect.top + - chartState._chartAreaRect.height)); + chartState._renderingDetails.chartAreaRect.left + + chartState._renderingDetails.chartAreaRect.width, + chartState._renderingDetails.chartAreaRect.top + + chartState._renderingDetails.chartAreaRect.height)); } chartState._chartSeries ._calculateFunnelSegments(canvas, pointIndex, seriesRenderer); @@ -810,10 +939,6 @@ class FunnelSeriesRenderer extends ChartSeriesRenderer { bool _isSelectionEnable = false; } -/// Called when the renderer for the funnel series is created -typedef FunnelSeriesRendererCreatedCallback = void Function( - FunnelSeriesController controller); - ///We can redraw the series with updating or creating new points by using this controller.If we need to access the redrawing methods ///in this before we must get the ChartSeriesController onRendererCreated event. class FunnelSeriesController { @@ -916,8 +1041,9 @@ class FunnelSeriesController { /// Add or update the data points on dynamic series update void _addOrUpdateDataPoints(List indexes, bool needUpdate) { + int dataIndex; for (int i = 0; i < indexes.length; i++) { - final int dataIndex = indexes[i]; + dataIndex = indexes[i]; _addOrUpdateDataPoint(dataIndex, needUpdate); } } @@ -953,8 +1079,9 @@ class FunnelSeriesController { ///Remove the redudant index from the list final List indexList = removedDataIndexes.toSet().toList(); indexList.sort((int b, int a) => a.compareTo(b)); + int dataIndex; for (int i = 0; i < indexList.length; i++) { - final int dataIndex = indexList[i]; + dataIndex = indexList[i]; _removeDataPoint(dataIndex); } } @@ -979,10 +1106,10 @@ class FunnelSeriesController { chartState._renderDataLabel!.state!.render(); } if (seriesRenderer._series.dataLabelSettings.isVisible && - chartState._chartTemplate != null && + chartState._renderingDetails.chartTemplate != null && // ignore: unnecessary_null_comparison - chartState._chartTemplate!.state != null) { - chartState._chartTemplate!.state.templateRender(); + chartState._renderingDetails.chartTemplate!.state != null) { + chartState._renderingDetails.chartTemplate!.state.templateRender(); } } } diff --git a/packages/syncfusion_flutter_charts/lib/src/pyramid_chart/base/pyramid_base.dart b/packages/syncfusion_flutter_charts/lib/src/pyramid_chart/base/pyramid_base.dart index be8216b1a..59d51671f 100644 --- a/packages/syncfusion_flutter_charts/lib/src/pyramid_chart/base/pyramid_base.dart +++ b/packages/syncfusion_flutter_charts/lib/src/pyramid_chart/base/pyramid_base.dart @@ -1,26 +1,5 @@ part of charts; -/// Returns the LegendRenderArgs. -typedef PyramidLegendRenderCallback = void Function( - LegendRenderArgs legendRenderArganimateCompleteds); - -/// Returns the TooltipArgs. -typedef PyramidTooltipCallback = void Function(TooltipArgs tooltipArgs); - -/// Returns the DataLabelRenderArgs. -typedef PyramidDataLabelRenderCallback = void Function( - DataLabelRenderArgs dataLabelArgs); - -/// Returns the SelectionArgs. -typedef PyramidSelectionCallback = void Function(SelectionArgs selectionArgs); - -///Returns tha PointTapArgs. -typedef PyramidPointTapCallback = void Function(PointTapArgs pointTapArgs); - -/// Returns the Offset -typedef PyramidTouchInteractionCallback = void Function( - ChartTouchInteractionArgs tapArgs); - ///Renders the pyramid chart /// ///To render a pyramid chart, create an instance of PyramidSeries, and add it to the series property of SfPyramidChart @@ -39,7 +18,7 @@ class SfPyramidChart extends StatefulWidget { this.onLegendItemRender, this.onTooltipRender, this.onDataLabelRender, - this.onPointTapped, + @deprecated this.onPointTapped, this.onDataLabelTapped, this.onLegendTapped, this.onSelectionChanged, @@ -233,7 +212,7 @@ class SfPyramidChart extends StatefulWidget { /// child: SfPyramidChart( /// series: PyramidSeries( /// initialSelectedDataIndexes: [1,0], - /// selectionSettings: SelectionSettings( + /// selectionBehavior: SelectionBehavior( /// selectedColor: Colors.red, /// unselectedColor: Colors.grey /// ), @@ -256,7 +235,7 @@ class SfPyramidChart extends StatefulWidget { /// child: SfPyramidChart( /// selectionGesture: ActivationMode.singleTap, /// series: PyramidSeries( - /// selectionSettings: SelectionSettings( + /// selectionBehavior: SelectionBehavior( /// selectedColor: Colors.red, /// unselectedColor: Colors.grey /// ), @@ -276,7 +255,7 @@ class SfPyramidChart extends StatefulWidget { /// child: SfPyramidChart( /// enableMultiSelection: true, /// series: PyramidSeries( - /// selectionSettings: SelectionSettings( + /// selectionBehavior: SelectionBehavior( /// selectedColor: Colors.red, /// unselectedColor: Colors.grey /// ), @@ -329,6 +308,7 @@ class SfPyramidChart extends StatefulWidget { /// print(args.pointIndex); ///} ///``` + @Deprecated('Use onPointTap in PyramidSeries instead.') final PyramidPointTapCallback? onPointTapped; //Called when the data label is tapped. @@ -336,7 +316,7 @@ class SfPyramidChart extends StatefulWidget { ///Whenever the data label is tapped, `onDataLabelTapped` callback will be called. Provides options to /// get the position of the data label, series index, point index and its text. /// - ///_Note:_ - This callback will not be called, when the builder is specified for data label + ///_Note:_ This callback will not be called, when the builder is specified for data label /// (data label template). For this case, custom widget specified in the `DataLabelSettings.builder` property /// can be wrapped using the `GestureDetector` and this functionality can be achieved in the application level. /// @@ -403,64 +383,26 @@ class SfPyramidChart extends StatefulWidget { /// class SfPyramidChartState extends State with TickerProviderStateMixin { - //ignore: unused_field - late List _controllerList; - //ignore: unused_field - late AnimationController - _animationController; // Animation controller for series - - //ignore: unused_field - late AnimationController _annotationController; // Controller for Annotations - - late ValueNotifier _seriesRepaintNotifier; - late List<_MeasureWidgetContext> - _legendWidgetContext; // To measure legend size and position - late List<_ChartTemplateInfo> _templates; // Chart Template info - late List _chartWidgets; - //ignore: unused_field - late PyramidSeriesRenderer _seriesRenderer; - - /// Holds the information of chart theme arguments - late SfChartThemeData _chartTheme; - late Rect _chartContainerRect; - late Rect _chartAreaRect; - _ChartTemplate? _chartTemplate; - _ChartInteraction? _currentActive; - bool? _initialRender; - late List<_LegendRenderContext> _legendToggleStates; - late List<_MeasureWidgetContext> _legendToggleTemplateStates; - late bool _isLegendToggled; - Offset? _tapPosition; - bool? _animateCompleted; - //ignore: unused_field - late Animation _chartElementAnimation; _PyramidDataLabelRenderer? _renderDataLabel; - late bool _widgetNeedUpdate; - late List _explodedPoints; - late List _dataLabelTemplateRegions; - late List _selectionData; int? _tooltipPointIndex; - Orientation? _oldDeviceOrientation; - late Orientation _deviceOrientation; - Size? _prevSize; - bool _didSizeChange = false; //Internal variables late String _seriesType; late List> _dataPoints; List>? _renderPoints; late _PyramidSeries _chartSeries; - late _ChartLegend _chartLegend; - //ignore: unused_field late _PyramidPlotArea _chartPlotArea; - late TooltipBehaviorRenderer _tooltipBehaviorRenderer; - late LegendRenderer _legendRenderer; + //Here, we are using get keyword inorder to get the proper & updated instance of chart widget //When we initialize chart widget as a property to other classes like _ChartSeries, the chart widget is not updated properly and by using get we can rectify this. SfPyramidChart get _chart => widget; + /// Specifies the chart rendering details + late _RenderingDetails _renderingDetails; + // ignore: unused_element bool get _animationCompleted { - return _animationController.status != AnimationStatus.forward; + return _renderingDetails.animationController.status != + AnimationStatus.forward; } /// Called when this object is inserted into the tree. @@ -476,6 +418,8 @@ class SfPyramidChartState extends State @override void initState() { + _renderingDetails = _RenderingDetails(); + _renderingDetails.didSizeChange = false; _initializeDefaultValues(); //Update and maintain the series state, when we update the series in the series collection // _createAndUpdateSeriesRenderer(); @@ -493,7 +437,7 @@ class SfPyramidChartState extends State @override void didChangeDependencies() { - _chartTheme = SfChartTheme.of(context); + _renderingDetails.chartTheme = SfChartTheme.of(context); super.didChangeDependencies(); } @@ -514,13 +458,11 @@ class SfPyramidChartState extends State void didUpdateWidget(SfPyramidChart oldWidget) { //Update and maintain the series state, when we update the series in the series collection // _createAndUpdateSeriesRenderer(oldWidget); - _initialRender = !widget.series.explode; - if (_tooltipBehaviorRenderer._chartTooltipState != null) { - _tooltipBehaviorRenderer._show = false; + if (_renderingDetails.tooltipBehaviorRenderer._chartTooltipState != null) { + _renderingDetails.tooltipBehaviorRenderer._show = false; } super.didUpdateWidget(oldWidget); - _isLegendToggled = false; - _widgetNeedUpdate = true; + _renderingDetails.widgetNeedUpdate = true; } /// Describes the part of the user interface represented by this widget. @@ -536,13 +478,11 @@ class SfPyramidChartState extends State @override Widget build(BuildContext context) { - _prevSize = _prevSize ?? MediaQuery.of(context).size; - _didSizeChange = _prevSize != MediaQuery.of(context).size; - _prevSize = MediaQuery.of(context).size; - _oldDeviceOrientation = _oldDeviceOrientation == null - ? MediaQuery.of(context).orientation - : _deviceOrientation; - _deviceOrientation = MediaQuery.of(context).orientation; + _renderingDetails.oldDeviceOrientation = + _renderingDetails.oldDeviceOrientation == null + ? MediaQuery.of(context).orientation + : _renderingDetails.deviceOrientation; + _renderingDetails.deviceOrientation = MediaQuery.of(context).orientation; return RepaintBoundary( child: _ChartContainer( child: GestureDetector( @@ -579,7 +519,8 @@ class SfPyramidChartState extends State @override void dispose() { - _disposeAnimationController(_animationController, _repaintChartElements); + _disposeAnimationController( + _renderingDetails.animationController, _repaintChartElements); super.dispose(); } @@ -653,25 +594,24 @@ class SfPyramidChartState extends State /// To intialize default chart elements value void _initializeDefaultValues() { _chartSeries = _PyramidSeries(this); - _chartLegend = _ChartLegend(this); + _renderingDetails.chartLegend = _ChartLegend(this); _chartPlotArea = _PyramidPlotArea(chartState: this); - _initialRender = true; - _controllerList = []; - _annotationController = AnimationController(vsync: this); - _seriesRepaintNotifier = ValueNotifier(0); - _legendToggleStates = <_LegendRenderContext>[]; - _legendToggleTemplateStates = <_MeasureWidgetContext>[]; - _explodedPoints = []; - _animateCompleted = false; - _isLegendToggled = false; - _widgetNeedUpdate = false; - _legendWidgetContext = <_MeasureWidgetContext>[]; - _dataLabelTemplateRegions = []; - _selectionData = []; - _animationController = AnimationController(vsync: this) + _renderingDetails.initialRender = true; + _renderingDetails.annotationController = AnimationController(vsync: this); + _renderingDetails.seriesRepaintNotifier = ValueNotifier(0); + _renderingDetails.legendToggleStates = <_LegendRenderContext>[]; + _renderingDetails.legendToggleTemplateStates = <_MeasureWidgetContext>[]; + _renderingDetails.explodedPoints = []; + _renderingDetails.animateCompleted = false; + _renderingDetails.isLegendToggled = false; + _renderingDetails.widgetNeedUpdate = false; + _renderingDetails.legendWidgetContext = <_MeasureWidgetContext>[]; + _renderingDetails.dataLabelTemplateRegions = []; + _renderingDetails.selectionData = []; + _renderingDetails.animationController = AnimationController(vsync: this) ..addListener(_repaintChartElements); - _tooltipBehaviorRenderer = TooltipBehaviorRenderer(this); - _legendRenderer = LegendRenderer(widget.legend); + _renderingDetails.tooltipBehaviorRenderer = TooltipBehaviorRenderer(this); + _renderingDetails.legendRenderer = LegendRenderer(widget.legend); } // In this method, create and update the series renderer for each series // @@ -683,7 +623,7 @@ class SfPyramidChartState extends State oldWidget != null && oldWidget.series != null ? _chartSeries.visibleSeriesRenderers[0] : null; - dynamic series; + PyramidSeries series; series = widget.series; // Create and update the series list here @@ -698,13 +638,12 @@ class SfPyramidChartState extends State series.onRendererCreated != null) { seriesRenderers._controller = PyramidSeriesController(seriesRenderers); - series.onRendererCreated(seriesRenderers._controller); + series.onRendererCreated!(seriesRenderers._controller!); } } seriesRenderers._series = series; - seriesRenderers._isSelectionEnable = - series.selectionBehavior.enable || series.selectionSettings.enable; + seriesRenderers._isSelectionEnable = series.selectionBehavior.enable; seriesRenderers._chartState = this; _chartSeries.visibleSeriesRenderers ..clear() @@ -713,7 +652,7 @@ class SfPyramidChartState extends State } void _repaintChartElements() { - _seriesRepaintNotifier.value++; + _renderingDetails.seriesRepaintNotifier.value++; } /// To render chart elements @@ -721,17 +660,24 @@ class SfPyramidChartState extends State return Expanded(child: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { Widget element; + _renderingDetails.prevSize = + _renderingDetails.prevSize ?? constraints.biggest; + _renderingDetails.didSizeChange = + _renderingDetails.prevSize != constraints.biggest; + _renderingDetails.prevSize = constraints.biggest; if (widget.series.dataSource != null) { _initialize(constraints); _chartSeries._findVisibleSeries(); _chartSeries._processDataPoints(); final List legendTemplates = _bindLegendTemplateWidgets(this); - if (legendTemplates.isNotEmpty && _legendWidgetContext.isEmpty) { + if (legendTemplates.isNotEmpty && + _renderingDetails.legendWidgetContext.isEmpty) { element = Container(child: Stack(children: legendTemplates)); SchedulerBinding.instance!.addPostFrameCallback((_) => _refresh()); } else { - _chartLegend._calculateLegendBounds(_chartLegend.chartSize); + _renderingDetails.chartLegend + ._calculateLegendBounds(_renderingDetails.chartLegend.chartSize); element = _getElements( this, _PyramidPlotArea(chartState: this), constraints)!; } @@ -744,12 +690,13 @@ class SfPyramidChartState extends State void _refresh() { final List<_MeasureWidgetContext> legendWidgetContexts = - _legendWidgetContext; + _renderingDetails.legendWidgetContext; if (legendWidgetContexts.isNotEmpty) { + _MeasureWidgetContext templateContext; + RenderBox renderBox; for (int i = 0; i < legendWidgetContexts.length; i++) { - final _MeasureWidgetContext templateContext = legendWidgetContexts[i]; - final RenderBox renderBox = - templateContext.context!.findRenderObject() as RenderBox; + templateContext = legendWidgetContexts[i]; + renderBox = templateContext.context!.findRenderObject() as RenderBox; templateContext.size = renderBox.size; } setState(() { @@ -760,9 +707,9 @@ class SfPyramidChartState extends State // ignore:unused_element void _redraw() { - _initialRender = false; - if (_tooltipBehaviorRenderer._chartTooltipState != null) { - _tooltipBehaviorRenderer._show = false; + _renderingDetails.initialRender = false; + if (_renderingDetails.tooltipBehaviorRenderer._chartTooltipState != null) { + _renderingDetails.tooltipBehaviorRenderer._show = false; } setState(() { /// The chart will be rebuilding again, When we do the legend toggle, zoom/pan the chart. @@ -771,18 +718,18 @@ class SfPyramidChartState extends State /// To intialize chart container area void _initialize(BoxConstraints constraints) { - _chartWidgets = []; + _renderingDetails.chartWidgets = []; final num width = constraints.maxWidth; final num height = constraints.maxHeight; final EdgeInsets margin = widget.margin; - final LegendRenderer legendRenderer = _legendRenderer; - if (widget.legend.position == LegendPosition.auto) { - legendRenderer._legendPosition = - height > width ? LegendPosition.bottom : LegendPosition.right; - } else { - legendRenderer._legendPosition = widget.legend.position; - } - _chartLegend.chartSize = Size(width - margin.left - margin.right, + final LegendRenderer legendRenderer = _renderingDetails.legendRenderer; + legendRenderer._legendPosition = + widget.legend.position == LegendPosition.auto + ? (height > width ? LegendPosition.bottom : LegendPosition.right) + : widget.legend.position; + + _renderingDetails.chartLegend.chartSize = Size( + width - margin.left - margin.right, height - margin.top - margin.bottom); } } @@ -813,9 +760,10 @@ class _PyramidPlotArea extends StatelessWidget { onHover: (PointerEvent event) => _enableMouseHover ? _onHover(event) : null, onExit: (PointerEvent event) { - chartState._tooltipBehaviorRenderer._isHovering = false; + chartState._renderingDetails.tooltipBehaviorRenderer + ._isHovering = false; }, - child: Stack(children: [ + child: Stack(textDirection: TextDirection.ltr, children: [ _initializeChart(constraints, context), Listener( onPointerUp: (PointerUpEvent event) => _onTapUp(event), @@ -826,13 +774,23 @@ class _PyramidPlotArea extends StatelessWidget { onLongPress: _onLongPress, onDoubleTap: _onDoubleTap, onTapUp: (TapUpDetails details) { - chartState._tapPosition = + chartState._renderingDetails.tapPosition = renderBox.globalToLocal(details.globalPosition); if (chart.onPointTapped != null && // ignore: unnecessary_null_comparison seriesRenderer != null) { + _calculatePointSeriesIndex(chart, seriesRenderer, + chartState._renderingDetails.tapPosition!); + } + if (chart.series.onPointTap != null && + // ignore: unnecessary_null_comparison + seriesRenderer != null) { _calculatePointSeriesIndex( - chart, seriesRenderer, chartState._tapPosition!); + chart, + seriesRenderer, + chartState._renderingDetails.tapPosition!, + null, + ActivationMode.singleTap); } }, child: Container( @@ -859,10 +817,10 @@ class _PyramidPlotArea extends StatelessWidget { void _calculateContainerSize(BoxConstraints constraints) { final num width = constraints.maxWidth; final num height = constraints.maxHeight; - chartState._chartContainerRect = + chartState._renderingDetails.chartContainerRect = Rect.fromLTWH(0, 0, width.toDouble(), height.toDouble()); final EdgeInsets margin = chart.margin; - chartState._chartAreaRect = Rect.fromLTWH( + chartState._renderingDetails.chartAreaRect = Rect.fromLTWH( margin.left, margin.top, width - margin.right - margin.left, @@ -877,7 +835,10 @@ class _PyramidPlotArea extends StatelessWidget { _bindTooltipWidgets(constraints); renderBox = context.findRenderObject() as RenderBox; chartState._chartPlotArea = this; - return Container(child: Stack(children: chartState._chartWidgets)); + return Container( + child: Stack( + textDirection: TextDirection.ltr, + children: chartState._renderingDetails.chartWidgets!)); } /// To calculate region path of pyramid @@ -901,17 +862,15 @@ class _PyramidPlotArea extends StatelessWidget { PyramidSeries series; final List visibleSeriesRenderers = chartState._chartSeries.visibleSeriesRenderers; + SelectionBehaviorRenderer selectionBehaviorRenderer; + dynamic selectionBehavior; for (int i = 0; i < visibleSeriesRenderers.length; i++) { seriesRenderer = visibleSeriesRenderers[i]; series = seriesRenderer._series; series.selectionBehavior._chartState = chartState; - series.selectionSettings._chartState = chartState; chartState._chartSeries._initializeSeriesProperties(seriesRenderer); - SelectionBehaviorRenderer selectionBehaviorRenderer; - final dynamic selectionBehavior = seriesRenderer._selectionBehavior = - series.selectionBehavior.enable - ? series.selectionBehavior - : series.selectionSettings; + selectionBehavior = + seriesRenderer._selectionBehavior = series.selectionBehavior; selectionBehaviorRenderer = seriesRenderer._selectionBehaviorRenderer = SelectionBehaviorRenderer(selectionBehavior, chart, chartState); selectionBehaviorRenderer = seriesRenderer._selectionBehaviorRenderer; @@ -923,67 +882,76 @@ class _PyramidPlotArea extends StatelessWidget { for (int index = 0; index < series.initialSelectedDataIndexes.length; index++) { - chartState._selectionData + chartState._renderingDetails.selectionData .add(series.initialSelectedDataIndexes[index]); } } if (series.animationDuration > 0 && - !chartState._didSizeChange && - (chartState._deviceOrientation == chartState._oldDeviceOrientation) && - ((!chartState._widgetNeedUpdate && chartState._initialRender!) || - chartState._isLegendToggled)) { - chartState._animationController.duration = + !chartState._renderingDetails.didSizeChange && + (chartState._renderingDetails.deviceOrientation == + chartState._renderingDetails.oldDeviceOrientation) && + ((!chartState._renderingDetails.widgetNeedUpdate && + chartState._renderingDetails.initialRender!) || + chartState._renderingDetails.isLegendToggled)) { + chartState._renderingDetails.animationController.duration = Duration(milliseconds: series.animationDuration.toInt()); seriesAnimation = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation( - parent: chartState._animationController, + parent: chartState._renderingDetails.animationController, curve: const Interval(0.1, 0.8, curve: Curves.linear), )..addStatusListener((AnimationStatus status) { if (status == AnimationStatus.completed) { - chartState._animateCompleted = true; + chartState._renderingDetails.animateCompleted = true; if (chartState._renderDataLabel != null) { chartState._renderDataLabel!.state?.render(); } - if (chartState._chartTemplate != null && + if (chartState._renderingDetails.chartTemplate != null && // ignore: unnecessary_null_comparison - chartState._chartTemplate!.state != null) { - chartState._chartTemplate!.state.templateRender(); + chartState._renderingDetails.chartTemplate!.state != + null) { + chartState._renderingDetails.chartTemplate!.state + .templateRender(); } } })); - chartState._chartElementAnimation = + chartState._renderingDetails.chartElementAnimation = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation( - parent: chartState._animationController, + parent: chartState._renderingDetails.animationController, curve: const Interval(0.85, 1.0, curve: Curves.decelerate), )); - chartState._animationController.forward(from: 0.0); + chartState._renderingDetails.animationController.forward(from: 0.0); } else { - chartState._animateCompleted = true; + chartState._renderingDetails.animateCompleted = true; if (chartState._renderDataLabel != null) { chartState._renderDataLabel!.state?.render(); } } - seriesRenderer._repaintNotifier = chartState._seriesRepaintNotifier; + seriesRenderer._repaintNotifier = + chartState._renderingDetails.seriesRepaintNotifier; if (seriesRenderer._seriesType == 'pyramid') { seriesPainter = _PyramidChartPainter( chartState: chartState, seriesIndex: i, isRepaint: seriesRenderer._needsRepaint, - animationController: chartState._animationController, + animationController: + chartState._renderingDetails.animationController, seriesAnimation: seriesAnimation, - notifier: chartState._seriesRepaintNotifier); + notifier: chartState._renderingDetails.seriesRepaintNotifier); } - chartState._chartWidgets + chartState._renderingDetails.chartWidgets! .add(RepaintBoundary(child: CustomPaint(painter: seriesPainter))); chartState._renderDataLabel = _PyramidDataLabelRenderer( key: GlobalKey(), chartState: chartState, - show: !chartState._widgetNeedUpdate - ? chartState._animationController.status == + //ignore: avoid_bool_literals_in_conditional_expressions + show: !chartState._renderingDetails.widgetNeedUpdate + ? chartState._renderingDetails.animationController.status == AnimationStatus.completed || - chartState._animationController.duration == null + chartState._renderingDetails.animationController.duration == + null : true); - chartState._chartWidgets.add(chartState._renderDataLabel!); + chartState._renderingDetails.chartWidgets! + .add(chartState._renderDataLabel!); } } @@ -991,63 +959,52 @@ class _PyramidPlotArea extends StatelessWidget { void _bindTooltipWidgets(BoxConstraints constraints) { final TooltipBehavior tooltip = chart.tooltipBehavior; final TooltipBehaviorRenderer tooltipBehaviorRenderer = - chartState._tooltipBehaviorRenderer; + chartState._renderingDetails.tooltipBehaviorRenderer; tooltip._chartState = chartState; if (tooltip.enable) { - final SfChartThemeData _chartTheme = chartState._chartTheme; + final SfChartThemeData _chartTheme = + chartState._renderingDetails.chartTheme; tooltipBehaviorRenderer._prevTooltipValue = tooltipBehaviorRenderer._currentTooltipValue = null; - chartState._tooltipBehaviorRenderer._chartTooltip = SfTooltip( - color: tooltip.color ?? _chartTheme.tooltipColor, - key: GlobalKey(), - textStyle: tooltip.textStyle, - animationDuration: tooltip.animationDuration, - enable: tooltip.enable, - opacity: tooltip.opacity, - borderColor: tooltip.borderColor, - borderWidth: tooltip.borderWidth, - duration: tooltip.duration, - shouldAlwaysShow: tooltip.shouldAlwaysShow, - elevation: tooltip.elevation, - canShowMarker: tooltip.canShowMarker, - textAlignment: tooltip.textAlignment, - decimalPlaces: tooltip.decimalPlaces, - labelColor: tooltip.textStyle.color ?? _chartTheme.tooltipLabelColor, - header: tooltip.header, - format: tooltip.format, - builder: tooltip.builder, - shadowColor: tooltip.shadowColor, - onTooltipRender: chart.onTooltipRender != null - ? chartState._tooltipBehaviorRenderer._tooltipRenderingEvent - : null); - chartState._chartWidgets.add(tooltipBehaviorRenderer._chartTooltip!); - } - } - - /// Find point index for selection - void _calculatePointSeriesIndex(SfPyramidChart chart, - PyramidSeriesRenderer seriesRenderer, Offset touchPosition) { - PointTapArgs pointTapArgs; - int? index; - for (int i = 0; i < seriesRenderer._renderPoints!.length; i++) { - if (seriesRenderer._renderPoints![i].region != null && - seriesRenderer._renderPoints![i].region!.contains(touchPosition)) { - index = i; - break; - } - } - if (index != null) { - pointTapArgs = PointTapArgs(0, index, seriesRenderer._dataPoints, index); - chart.onPointTapped!(pointTapArgs); + chartState._renderingDetails.tooltipBehaviorRenderer._chartTooltip = + SfTooltip( + color: tooltip.color ?? _chartTheme.tooltipColor, + key: GlobalKey(), + textStyle: tooltip.textStyle, + animationDuration: tooltip.animationDuration, + animationCurve: + const Interval(0.1, 0.8, curve: Curves.easeOutBack), + enable: tooltip.enable, + opacity: tooltip.opacity, + borderColor: tooltip.borderColor, + borderWidth: tooltip.borderWidth, + duration: tooltip.duration.toInt(), + shouldAlwaysShow: tooltip.shouldAlwaysShow, + elevation: tooltip.elevation, + canShowMarker: tooltip.canShowMarker, + textAlignment: tooltip.textAlignment, + decimalPlaces: tooltip.decimalPlaces, + labelColor: + tooltip.textStyle.color ?? _chartTheme.tooltipLabelColor, + header: tooltip.header, + format: tooltip.format, + shadowColor: tooltip.shadowColor, + onTooltipRender: chart.onTooltipRender != null + ? chartState._renderingDetails.tooltipBehaviorRenderer + ._tooltipRenderingEvent + : null); + chartState._renderingDetails.chartWidgets! + .add(tooltipBehaviorRenderer._chartTooltip!); } } /// To perform pointer down event void _onTapDown(PointerDownEvent event) { - chartState._tooltipBehaviorRenderer._isHovering = false; + chartState._renderingDetails.tooltipBehaviorRenderer._isHovering = false; //renderBox = context.findRenderObject(); - chartState._currentActive = null; - chartState._tapPosition = renderBox.globalToLocal(event.position); + chartState._renderingDetails.currentActive = null; + chartState._renderingDetails.tapPosition = + renderBox.globalToLocal(event.position); bool isPoint = false; const int seriesIndex = 0; late int pointIndex; @@ -1062,7 +1019,7 @@ class _PyramidPlotArea extends StatelessWidget { } if (seriesRenderer._renderPoints![j].isVisible && !isPoint) { isPoint = _isPointInPolygon(seriesRenderer._renderPoints![j].pathRegion, - chartState._tapPosition); + chartState._renderingDetails.tapPosition!); if (isPoint) { pointIndex = j; if (chart.onDataLabelRender == null) { @@ -1071,9 +1028,9 @@ class _PyramidPlotArea extends StatelessWidget { } } } - doubleTapPosition = chartState._tapPosition; - if (chartState._tapPosition != null && isPoint) { - chartState._currentActive = _ChartInteraction( + doubleTapPosition = chartState._renderingDetails.tapPosition; + if (chartState._renderingDetails.tapPosition != null && isPoint) { + chartState._renderingDetails.currentActive = _ChartInteraction( seriesIndex, pointIndex, visibleSeriesRenderers[seriesIndex]._series, @@ -1081,8 +1038,9 @@ class _PyramidPlotArea extends StatelessWidget { ); } else { //hides the tooltip if the point of interaction is outside pyramid region of the chart - chartState._tooltipBehaviorRenderer._show = false; - chartState._tooltipBehaviorRenderer._hideOnTimer(); + chartState._renderingDetails.tooltipBehaviorRenderer._show = false; + chartState._renderingDetails.tooltipBehaviorRenderer + ._hideTooltipTemplate(); } if (chart.onChartTouchInteractionDown != null) { touchArgs = ChartTouchInteractionArgs(); @@ -1105,17 +1063,30 @@ class _PyramidPlotArea extends StatelessWidget { /// To perform double tap touch interactions void _onDoubleTap() { const int seriesIndex = 0; - if (doubleTapPosition != null && chartState._currentActive != null) { - final int pointIndex = chartState._currentActive!.pointIndex!; + if (doubleTapPosition != null && + chartState._renderingDetails.currentActive != null) { + if (chart.series.onPointDoubleTap != null && + // ignore: unnecessary_null_comparison + seriesRenderer != null) { + _calculatePointSeriesIndex( + chart, + seriesRenderer, + chartState._renderingDetails.tapPosition!, + null, + ActivationMode.doubleTap); + chartState._renderingDetails.tapPosition = null; + } + final int pointIndex = + chartState._renderingDetails.currentActive!.pointIndex!; final List visibleSeriesRenderers = chartState._chartSeries.visibleSeriesRenderers; - chartState._currentActive = _ChartInteraction( + chartState._renderingDetails.currentActive = _ChartInteraction( seriesIndex, pointIndex, visibleSeriesRenderers[seriesIndex]._series, visibleSeriesRenderers[seriesIndex]._renderPoints![pointIndex]); - if (chartState._currentActive != null) { - if (chartState._currentActive!.series.explodeGesture == + if (chartState._renderingDetails.currentActive != null) { + if (chartState._renderingDetails.currentActive!.series.explodeGesture == ActivationMode.doubleTap) { chartState._chartSeries._pointExplode(pointIndex); final GlobalKey key = chartState._renderDataLabel!.key as GlobalKey; @@ -1127,12 +1098,12 @@ class _PyramidPlotArea extends StatelessWidget { chartState._chartSeries ._seriesPointSelection(pointIndex, ActivationMode.doubleTap); if (chart.tooltipBehavior.enable && - chartState._animateCompleted! && + chartState._renderingDetails.animateCompleted && chart.tooltipBehavior.activationMode == ActivationMode.doubleTap) { if (chart.tooltipBehavior.builder != null) { _showPyramidTooltipTemplate(); } else { - chartState._tooltipBehaviorRenderer.onDoubleTap( + chartState._renderingDetails.tooltipBehaviorRenderer.onDoubleTap( doubleTapPosition!.dx.toDouble(), doubleTapPosition!.dy.toDouble()); } @@ -1143,11 +1114,24 @@ class _PyramidPlotArea extends StatelessWidget { /// To perform long press touch interactions void _onLongPress() { const int seriesIndex = 0; - if (chartState._tapPosition != null && chartState._currentActive != null) { + if (chartState._renderingDetails.tapPosition != null && + chartState._renderingDetails.currentActive != null) { + if (chart.series.onPointLongPress != null && + // ignore: unnecessary_null_comparison + seriesRenderer != null) { + _calculatePointSeriesIndex( + chart, + seriesRenderer, + chartState._renderingDetails.tapPosition!, + null, + ActivationMode.longPress); + chartState._renderingDetails.tapPosition = null; + } final List visibleSeriesRenderers = chartState._chartSeries.visibleSeriesRenderers; - final int pointIndex = chartState._currentActive!.pointIndex!; - chartState._currentActive = _ChartInteraction( + final int pointIndex = + chartState._renderingDetails.currentActive!.pointIndex!; + chartState._renderingDetails.currentActive = _ChartInteraction( seriesIndex, pointIndex, visibleSeriesRenderers[seriesIndex]._series, @@ -1155,8 +1139,8 @@ class _PyramidPlotArea extends StatelessWidget { pointRegion); chartState._chartSeries ._seriesPointSelection(pointIndex, ActivationMode.longPress); - if (chartState._currentActive != null) { - if (chartState._currentActive!.series.explodeGesture == + if (chartState._renderingDetails.currentActive != null) { + if (chartState._renderingDetails.currentActive!.series.explodeGesture == ActivationMode.longPress) { chartState._chartSeries._pointExplode(pointIndex); final GlobalKey key = chartState._renderDataLabel!.key as GlobalKey; @@ -1166,14 +1150,14 @@ class _PyramidPlotArea extends StatelessWidget { } } if (chart.tooltipBehavior.enable && - chartState._animateCompleted! && + chartState._renderingDetails.animateCompleted && chart.tooltipBehavior.activationMode == ActivationMode.longPress) { if (chart.tooltipBehavior.builder != null) { _showPyramidTooltipTemplate(); } else { - chartState._tooltipBehaviorRenderer.onLongPress( - chartState._tapPosition!.dx.toDouble(), - chartState._tapPosition!.dy.toDouble()); + chartState._renderingDetails.tooltipBehaviorRenderer.onLongPress( + chartState._renderingDetails.tapPosition!.dx.toDouble(), + chartState._renderingDetails.tapPosition!.dy.toDouble()); } } } @@ -1181,29 +1165,30 @@ class _PyramidPlotArea extends StatelessWidget { /// To perform pointer up event void _onTapUp(PointerUpEvent event) { - chartState._tooltipBehaviorRenderer._isHovering = false; + chartState._renderingDetails.tooltipBehaviorRenderer._isHovering = false; bool isPoint = false; - chartState._tapPosition = renderBox.globalToLocal(event.position); + chartState._renderingDetails.tapPosition = + renderBox.globalToLocal(event.position); for (int j = 0; j < seriesRenderer._renderPoints!.length; j++) { if (seriesRenderer._renderPoints![j].isVisible) { isPoint = _isPointInPolygon(seriesRenderer._renderPoints![j].pathRegion, - chartState._tapPosition); + chartState._renderingDetails.tapPosition!); if (isPoint) { break; } } } final _ChartInteraction? currentActive = - isPoint ? chartState._currentActive! : null; + isPoint ? chartState._renderingDetails.currentActive! : null; ChartTouchInteractionArgs touchArgs; if (currentActive != null) { // ignore: unnecessary_null_comparison if (chart.onDataLabelTapped != null && seriesRenderer != null) { - _triggerPyramidDataLabelEvent( - chart, seriesRenderer, chartState, chartState._tapPosition!); + _triggerPyramidDataLabelEvent(chart, seriesRenderer, chartState, + chartState._renderingDetails.tapPosition!); } - if (chartState._tapPosition != null && - chartState._currentActive != null) { + if (chartState._renderingDetails.tapPosition != null && + chartState._renderingDetails.currentActive != null) { if (currentActive.series != null && currentActive.series.explodeGesture == ActivationMode.singleTap) { chartState._chartSeries._pointExplode(currentActive.pointIndex!); @@ -1220,14 +1205,14 @@ class _PyramidPlotArea extends StatelessWidget { } if (chart.tooltipBehavior.enable && - chartState._animateCompleted! && + chartState._renderingDetails.animateCompleted && chart.tooltipBehavior.activationMode == ActivationMode.singleTap && currentActive.series != null) { if (chart.tooltipBehavior.builder != null) { _showPyramidTooltipTemplate(); } else { final Offset position = renderBox.globalToLocal(event.position); - chartState._tooltipBehaviorRenderer + chartState._renderingDetails.tooltipBehaviorRenderer .onTouchUp(position.dx.toDouble(), position.dy.toDouble()); } } @@ -1238,13 +1223,18 @@ class _PyramidPlotArea extends StatelessWidget { touchArgs.position = renderBox.globalToLocal(event.position); chart.onChartTouchInteractionUp!(touchArgs); } - chartState._tapPosition = null; + if (chart.series.onPointTap == null && + chart.series.onPointDoubleTap == null && + chart.series.onPointLongPress == null) { + chartState._renderingDetails.tapPosition = null; + } } /// To perform event on mouse hover void _onHover(PointerEvent event) { - chartState._currentActive = null; - chartState._tapPosition = renderBox.globalToLocal(event.position); + chartState._renderingDetails.currentActive = null; + chartState._renderingDetails.tapPosition = + renderBox.globalToLocal(event.position); bool? isPoint; const int seriesIndex = 0; int? pointIndex; @@ -1252,19 +1242,21 @@ class _PyramidPlotArea extends StatelessWidget { chartState._chartSeries.visibleSeriesRenderers[seriesIndex]; final TooltipBehavior tooltip = chart.tooltipBehavior; final TooltipBehaviorRenderer tooltipBehaviorRenderer = - chartState._tooltipBehaviorRenderer; + chartState._renderingDetails.tooltipBehaviorRenderer; for (int j = 0; j < seriesRenderer._renderPoints!.length; j++) { if (seriesRenderer._renderPoints![j].isVisible) { isPoint = _isPointInPolygon(seriesRenderer._renderPoints![j].pathRegion, - chartState._tapPosition); + chartState._renderingDetails.tapPosition!); if (isPoint) { pointIndex = j; break; } } } - if (chartState._tapPosition != null && isPoint != null && isPoint) { - chartState._currentActive = _ChartInteraction( + if (chartState._renderingDetails.tapPosition != null && + isPoint != null && + isPoint) { + chartState._renderingDetails.currentActive = _ChartInteraction( seriesIndex, pointIndex!, chartState._chartSeries.visibleSeriesRenderers[seriesIndex]._series, @@ -1274,12 +1266,13 @@ class _PyramidPlotArea extends StatelessWidget { } else if (tooltip.builder != null) { tooltipBehaviorRenderer._hide(); } - if (chartState._tapPosition != null) { + if (chartState._renderingDetails.tapPosition != null) { if (tooltip.enable && - chartState._currentActive != null && - chartState._currentActive!.series != null) { + chartState._renderingDetails.currentActive != null && + chartState._renderingDetails.currentActive!.series != null) { tooltipBehaviorRenderer._isHovering = true; - if (tooltip.builder != null && chartState._animateCompleted!) { + if (tooltip.builder != null && + chartState._renderingDetails.animateCompleted) { _showPyramidTooltipTemplate(); } else { final Offset position = renderBox.globalToLocal(event.position); @@ -1292,14 +1285,14 @@ class _PyramidPlotArea extends StatelessWidget { tooltipBehaviorRenderer._hide(); } } - chartState._tapPosition = null; + chartState._renderingDetails.tapPosition = null; } /// This method gets executed for showing tooltip when builder is provided in behavior void _showPyramidTooltipTemplate([int? pointIndex]) { final TooltipBehavior tooltip = chart.tooltipBehavior; final TooltipBehaviorRenderer tooltipBehaviorRenderer = - chartState._tooltipBehaviorRenderer; + chartState._renderingDetails.tooltipBehaviorRenderer; if (!tooltipBehaviorRenderer._isHovering) { //assingning null for the previous and current tooltip values in case of touch interaction @@ -1307,21 +1300,21 @@ class _PyramidPlotArea extends StatelessWidget { tooltipBehaviorRenderer._currentTooltipValue = null; } final PyramidSeries chartSeries = - chartState._currentActive?.series ?? chart.series; + chartState._renderingDetails.currentActive?.series ?? chart.series; final PointInfo point = pointIndex == null - ? chartState._currentActive?.point + ? chartState._renderingDetails.currentActive?.point : chartState ._chartSeries.visibleSeriesRenderers[0]._dataPoints[pointIndex]; final Offset? location = chart.tooltipBehavior.tooltipPosition == TooltipPosition.pointer && !chartState._chartSeries.visibleSeriesRenderers[0]._series.explode - ? chartState._tapPosition! + ? chartState._renderingDetails.tapPosition! : point.symbolLocation; bool isPoint = false; for (int j = 0; j < seriesRenderer._renderPoints!.length; j++) { if (seriesRenderer._renderPoints![j].isVisible) { isPoint = _isPointInPolygon( - seriesRenderer._renderPoints![j].pathRegion, location); + seriesRenderer._renderPoints![j].pathRegion, location!); if (isPoint) { pointIndex = j; break; @@ -1330,28 +1323,34 @@ class _PyramidPlotArea extends StatelessWidget { } if (location != null && isPoint && (chartSeries.enableTooltip)) { tooltipBehaviorRenderer._showLocation = location; - chartState._tooltipBehaviorRenderer._renderBox!.boundaryRect = - chartState._chartContainerRect; + chartState._renderingDetails.tooltipBehaviorRenderer._chartTooltipState! + .boundaryRect = + chartState._renderingDetails.tooltipBehaviorRenderer._tooltipBounds = + chartState._renderingDetails.chartContainerRect; tooltipBehaviorRenderer._tooltipTemplate = tooltip.builder!( - chartSeries.dataSource![ - pointIndex ?? chartState._currentActive!.pointIndex!], + chartSeries.dataSource![pointIndex ?? + chartState._renderingDetails.currentActive!.pointIndex!], point, chartSeries, - chartState._currentActive?.seriesIndex ?? 0, - pointIndex ?? chartState._currentActive!.pointIndex!); + chartState._renderingDetails.currentActive?.seriesIndex ?? 0, + pointIndex ?? + chartState._renderingDetails.currentActive!.pointIndex!); if (tooltipBehaviorRenderer._isHovering) { //assingning values for the previous and current tooltip values on mouse hover tooltipBehaviorRenderer._prevTooltipValue = tooltipBehaviorRenderer._currentTooltipValue; tooltipBehaviorRenderer._currentTooltipValue = TooltipValue( - 0, pointIndex ?? chartState._currentActive!.pointIndex!); + 0, + pointIndex ?? + chartState._renderingDetails.currentActive!.pointIndex!); } else { - chartState._tooltipBehaviorRenderer._timer = Timer( - Duration(milliseconds: chart.tooltipBehavior.duration.toInt()), - chartState._tooltipBehaviorRenderer._hideTooltipTemplate); + chartState._renderingDetails.tooltipBehaviorRenderer + ._hideTooltipTemplate(); } tooltipBehaviorRenderer._show = true; tooltipBehaviorRenderer._performTooltip(); + tooltipBehaviorRenderer._chartTooltipState! + .hide(hideDelay: tooltip.duration.toInt()); } } } diff --git a/packages/syncfusion_flutter_charts/lib/src/pyramid_chart/base/series_base.dart b/packages/syncfusion_flutter_charts/lib/src/pyramid_chart/base/series_base.dart index d249cd049..41dd6b1fc 100644 --- a/packages/syncfusion_flutter_charts/lib/src/pyramid_chart/base/series_base.dart +++ b/packages/syncfusion_flutter_charts/lib/src/pyramid_chart/base/series_base.dart @@ -79,8 +79,11 @@ class _PyramidSeries { final ChartIndexedValueMapper? textMapping = currentSeries.textFieldMapper; final List> points = seriesRenderer._renderPoints!; + PointInfo currentPoint; + List<_MeasureWidgetContext> legendToggles; + _MeasureWidgetContext item; + _LegendRenderContext legendRenderContext; for (int i = 0; i < points.length; i++) { - PointInfo currentPoint; currentPoint = points[i]; currentPoint.fill = currentPoint.isEmpty && empty.color != null // ignore: unnecessary_null_comparison @@ -105,11 +108,11 @@ class _PyramidSeries { : currentPoint.y.toString()); if (_chartState._chart.legend.legendItemBuilder != null) { - final List<_MeasureWidgetContext> legendToggles = - _chartState._legendToggleTemplateStates; + legendToggles = + _chartState._renderingDetails.legendToggleTemplateStates; if (legendToggles.isNotEmpty) { for (int j = 0; j < legendToggles.length; j++) { - final _MeasureWidgetContext item = legendToggles[j]; + item = legendToggles[j]; if (i == item.pointIndex) { currentPoint.isVisible = false; break; @@ -117,10 +120,12 @@ class _PyramidSeries { } } } else { - if (_chartState._legendToggleStates.isNotEmpty) { - for (int j = 0; j < _chartState._legendToggleStates.length; j++) { - final _LegendRenderContext legendRenderContext = - _chartState._legendToggleStates[j]; + if (_chartState._renderingDetails.legendToggleStates.isNotEmpty) { + for (int j = 0; + j < _chartState._renderingDetails.legendToggleStates.length; + j++) { + legendRenderContext = + _chartState._renderingDetails.legendToggleStates[j]; if (i == legendRenderContext.seriesIndex) { currentPoint.isVisible = false; break; @@ -144,8 +149,8 @@ class _PyramidSeries { /// To initialise the series properties in chart void _initializeSeriesProperties(PyramidSeriesRenderer seriesRenderer) { final PyramidSeries series = seriesRenderer._series; - final Rect chartAreaRect = _chartState._chartAreaRect; - final bool reverse = seriesRenderer._seriesType == 'pyramid' ? true : false; + final Rect chartAreaRect = _chartState._renderingDetails.chartAreaRect; + final bool reverse = seriesRenderer._seriesType == 'pyramid'; seriesRenderer._triangleSize = Size( _percentToValue(series.width, chartAreaRect.width)!.toDouble(), _percentToValue(series.height, chartAreaRect.height)!.toDouble()); @@ -214,10 +219,9 @@ class _PyramidSeries { double y; assert( // ignore: unnecessary_null_comparison - seriesRenderer._series.gapRatio != null - ? seriesRenderer._series.gapRatio >= 0 && - seriesRenderer._series.gapRatio <= 1 - : true, + !(seriesRenderer._series.gapRatio != null) || + seriesRenderer._series.gapRatio >= 0 && + seriesRenderer._series.gapRatio <= 1, 'The gap ratio for the pyramid chart must be between 0 and 1.'); final double gapRatio = min(max(seriesRenderer._series.gapRatio, 0), 1); final double coEff = @@ -245,22 +249,23 @@ class _PyramidSeries { final SfPyramidChartState chartState = _chartState; final PointInfo point = seriesRenderer._renderPoints![pointIndex]; if (seriesRenderer._series.explode) { - if (chartState._explodedPoints.isNotEmpty) { + if (chartState._renderingDetails.explodedPoints.isNotEmpty) { existExplodedRegion = true; - final int previousIndex = chartState._explodedPoints[0]; + final int previousIndex = + chartState._renderingDetails.explodedPoints[0]; seriesRenderer._renderPoints![previousIndex].explodeDistance = 0; point.explodeDistance = previousIndex == pointIndex ? 0 : seriesRenderer._explodeDistance; - chartState._explodedPoints[0] = pointIndex; + chartState._renderingDetails.explodedPoints[0] = pointIndex; if (previousIndex == pointIndex) { - chartState._explodedPoints = []; + chartState._renderingDetails.explodedPoints = []; } - chartState._seriesRepaintNotifier.value++; + chartState._renderingDetails.seriesRepaintNotifier.value++; } if (!existExplodedRegion) { point.explodeDistance = seriesRenderer._explodeDistance; - chartState._explodedPoints.add(pointIndex); - chartState._seriesRepaintNotifier.value++; + chartState._renderingDetails.explodedPoints.add(pointIndex); + chartState._renderingDetails.seriesRepaintNotifier.value++; } _calculatePathRegion(pointIndex, seriesRenderer); } @@ -274,7 +279,7 @@ class _PyramidSeries { currentPoint.pathRegion = []; final SfPyramidChartState chartState = _chartState; final Size area = seriesRenderer._triangleSize; - final Rect rect = chartState._chartContainerRect; + final Rect rect = chartState._renderingDetails.chartContainerRect; final num seriesTop = rect.top + (rect.height - area.height) / 2; const num offset = 0; // ignore: prefer_if_null_operators @@ -352,7 +357,9 @@ class _PyramidSeries { canvas, _StyleOptions( fill: fillColor, - strokeWidth: _chartState._animateCompleted! ? strokeWidth : 0, + strokeWidth: _chartState._renderingDetails.animateCompleted + ? strokeWidth + : 0, strokeColor: strokeColor, opacity: opacity), path); @@ -379,37 +386,61 @@ class _PyramidSeries { _chartState._chartSeries.visibleSeriesRenderers[0]; final SfPyramidChartState chartState = _chartState; int? currentSelectedIndex; + const int seriesIndex = 0; if (seriesRenderer._isSelectionEnable && mode == chart.selectionGesture) { - if (chartState._selectionData.isNotEmpty) { + if (chartState._renderingDetails.selectionData.isNotEmpty) { if (!chart.enableMultiSelection && - _chartState._selectionData.isNotEmpty && - _chartState._selectionData.length > 1) { - if (_chartState._selectionData.contains(pointIndex)) { + _chartState._renderingDetails.selectionData.isNotEmpty && + _chartState._renderingDetails.selectionData.length > 1) { + if (_chartState._renderingDetails.selectionData + .contains(pointIndex)) { currentSelectedIndex = pointIndex; } - _chartState._selectionData.clear(); + _chartState._renderingDetails.selectionData.clear(); if (currentSelectedIndex != null) { - _chartState._selectionData.add(pointIndex); + _chartState._renderingDetails.selectionData.add(pointIndex); } } - for (int i = 0; i < chartState._selectionData.length; i++) { - final int selectionIndex = chartState._selectionData[i]; + + int selectionIndex; + for (int i = 0; + i < chartState._renderingDetails.selectionData.length; + i++) { + selectionIndex = chartState._renderingDetails.selectionData[i]; if (!chart.enableMultiSelection) { - isPointAlreadySelected = chartState._selectionData.length == 1 && - pointIndex == selectionIndex; - chartState._selectionData.removeAt(i); - chartState._seriesRepaintNotifier.value++; + isPointAlreadySelected = + chartState._renderingDetails.selectionData.length == 1 && + pointIndex == selectionIndex; + if (seriesRenderer._selectionBehavior.toggleSelection == true || + !isPointAlreadySelected) { + chartState._renderingDetails.selectionData.removeAt(i); + } + chartState._renderingDetails.seriesRepaintNotifier.value++; + if (chart.onSelectionChanged != null) { + chart.onSelectionChanged!(_getSelectionEventArgs( + seriesRenderer, seriesIndex, selectionIndex)); + } } else if (pointIndex == selectionIndex) { - chartState._selectionData.removeAt(i); + if (seriesRenderer._selectionBehavior.toggleSelection == true) { + chartState._renderingDetails.selectionData.removeAt(i); + } isPointAlreadySelected = true; - chartState._seriesRepaintNotifier.value++; + chartState._renderingDetails.seriesRepaintNotifier.value++; + if (chart.onSelectionChanged != null) { + chart.onSelectionChanged!(_getSelectionEventArgs( + seriesRenderer, seriesIndex, selectionIndex)); + } break; } } } if (!isPointAlreadySelected) { - chartState._selectionData.add(pointIndex); - chartState._seriesRepaintNotifier.value++; + chartState._renderingDetails.selectionData.add(pointIndex); + chartState._renderingDetails.seriesRepaintNotifier.value++; + if (chart.onSelectionChanged != null) { + chart.onSelectionChanged!( + _getSelectionEventArgs(seriesRenderer, seriesIndex, pointIndex)); + } } } } @@ -421,18 +452,14 @@ class _PyramidSeries { SfPyramidChart chart, PointInfo point) { _StyleOptions? pointStyle; - final dynamic selection = seriesRenderer._series.selectionBehavior.enable - ? seriesRenderer._series.selectionBehavior - : seriesRenderer._series.selectionSettings; - const int seriesIndex = 0; - if (selection.enable) { - if (_chartState._selectionData.isNotEmpty) { - for (int i = 0; i < _chartState._selectionData.length; i++) { - final int selectionIndex = _chartState._selectionData[i]; - if (chart.onSelectionChanged != null) { - chart.onSelectionChanged!(_getSelectionEventArgs( - seriesRenderer, seriesIndex, selectionIndex)); - } + final dynamic selection = seriesRenderer._series.selectionBehavior; + if (selection.enable == true) { + if (_chartState._renderingDetails.selectionData.isNotEmpty) { + int selectionIndex; + for (int i = 0; + i < _chartState._renderingDetails.selectionData.length; + i++) { + selectionIndex = _chartState._renderingDetails.selectionData[i]; if (currentPointIndex == selectionIndex) { pointStyle = _StyleOptions( fill: _selectionArgs != null @@ -446,7 +473,8 @@ class _PyramidSeries { : selection!.selectedBorderColor, opacity: selection.selectedOpacity); break; - } else if (i == _chartState._selectionData.length - 1) { + } else if (i == + _chartState._renderingDetails.selectionData.length - 1) { pointStyle = _StyleOptions( fill: _selectionArgs != null ? _selectionArgs!.unselectedColor @@ -467,9 +495,11 @@ class _PyramidSeries { /// To perform selection event and return selectionArgs SelectionArgs _getSelectionEventArgs( - dynamic seriesRenderer, int seriesIndex, int pointIndex) { - final SfPyramidChart chart = seriesRenderer._chartState!._chart; + PyramidSeriesRenderer seriesRenderer, int seriesIndex, int pointIndex) { + final SfPyramidChart chart = seriesRenderer._chartState._chart; + // ignore: unnecessary_null_comparison if (seriesRenderer != null && + //ignore: unnecessary_null_comparison chart.series != null && pointIndex < chart.series.dataSource!.length) { final dynamic selectionBehavior = seriesRenderer._selectionBehavior; diff --git a/packages/syncfusion_flutter_charts/lib/src/pyramid_chart/renderer/data_label_renderer.dart b/packages/syncfusion_flutter_charts/lib/src/pyramid_chart/renderer/data_label_renderer.dart index 61d13cb00..7992ddbf9 100644 --- a/packages/syncfusion_flutter_charts/lib/src/pyramid_chart/renderer/data_label_renderer.dart +++ b/packages/syncfusion_flutter_charts/lib/src/pyramid_chart/renderer/data_label_renderer.dart @@ -40,8 +40,9 @@ class _PyramidDataLabelRendererState extends State<_PyramidDataLabelRenderer> @override Widget build(BuildContext context) { widget.state = this; - animationController.duration = - Duration(milliseconds: widget.chartState._initialRender! ? 500 : 0); + animationController.duration = Duration( + milliseconds: + widget.chartState._renderingDetails.initialRender! ? 500 : 0); final Animation dataLabelAnimation = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation( parent: animationController, @@ -129,6 +130,7 @@ void _renderPyramidDataLabel( DataLabelRenderArgs dataLabelArgs; TextStyle dataLabelStyle; final List renderDataLabelRegions = []; + Size textSize; for (int pointIndex = 0; pointIndex < seriesRenderer._renderPoints!.length; pointIndex++) { @@ -158,7 +160,7 @@ void _renderPyramidDataLabel( ? _getDataLabelTextStyle( seriesRenderer, point, chartState, animateOpacity) : dataLabelStyle; - final Size textSize = measureText(label!, dataLabelStyle); + textSize = measureText(label!, dataLabelStyle); ///Label check after event if (label != '') { @@ -284,8 +286,8 @@ void _renderOutsidePyramidDataLabel( seriesRenderer._series.dataLabelSettings.connectorLineSettings; const num regionPadding = 12; connectorPath = Path(); - final num connectorLength = _percentToValue( - connector.length ?? '0%', _chartState._chartAreaRect.width / 2)! + + final num connectorLength = _percentToValue(connector.length ?? '0%', + _chartState._renderingDetails.chartAreaRect.width / 2)! + seriesRenderer._maximumDataLabelRegion.width / 2 - regionPadding; final Offset startPoint = Offset( @@ -297,10 +299,10 @@ void _renderOutsidePyramidDataLabel( connectorLength; final Offset endPoint = Offset( (dx + textSize.width + margin.left + margin.right > - _chartState._chartAreaRect.right) + _chartState._renderingDetails.chartAreaRect.right) ? dx - (_percentToValue(seriesRenderer._series.explodeOffset, - _chartState._chartAreaRect.width)!) + _chartState._renderingDetails.chartAreaRect.width)!) : dx, seriesRenderer._renderPoints![pointIndex].symbolLocation.dy); connectorPath.moveTo(startPoint.dx, startPoint.dy); @@ -314,7 +316,7 @@ void _renderOutsidePyramidDataLabel( point.labelRect = rect; labelLocation = Offset(rect.left + margin.left, rect.top + rect.height / 2 - textSize.height / 2); - final Rect containerRect = _chartState._chartAreaRect; + final Rect containerRect = _chartState._renderingDetails.chartAreaRect; if (seriesRenderer._series.dataLabelSettings.builder == null) { Rect? lastRenderedLabelRegion; if (renderDataLabelRegions.isNotEmpty) { @@ -360,7 +362,7 @@ void _renderOutsidePyramidDataLabel( rect.left - connectorLinePadding, rect.top + rect.height / 2); connectorPath.lineTo(rect.left, rect.top + rect.height / 2); } - if (rect.bottom < _chartState._chartAreaRect.bottom) { + if (rect.bottom < _chartState._renderingDetails.chartAreaRect.bottom) { _drawPyramidLabel( rect, labelLocation, @@ -416,9 +418,10 @@ void _drawPyramidLabel( ..color = connector.width <= 0 ? Colors.transparent : connector.color ?? - point.fill.withOpacity(!chartState._isLegendToggled - ? animateOpacity - : dataLabel.opacity) + point.fill.withOpacity( + !chartState._renderingDetails.isLegendToggled + ? animateOpacity + : dataLabel.opacity) ..strokeWidth = connector.width ..style = PaintingStyle.stroke); } @@ -435,7 +438,9 @@ void _drawPyramidLabel( if (strokeWidth != null && strokeWidth > 0) { rectPaint = Paint() ..color = strokeColor!.withOpacity( - !chartState._isLegendToggled ? animateOpacity : dataLabel.opacity) + !chartState._renderingDetails.isLegendToggled + ? animateOpacity + : dataLabel.opacity) ..style = PaintingStyle.stroke ..strokeWidth = strokeWidth; _drawLabelRect( @@ -448,11 +453,12 @@ void _drawPyramidLabel( if (labelFill != null) { _drawLabelRect( Paint() - ..color = labelFill.withOpacity(!chartState._isLegendToggled - ? (animateOpacity - (1 - dataLabel.opacity)) < 0 - ? 0 - : animateOpacity - (1 - dataLabel.opacity) - : dataLabel.opacity) + ..color = labelFill + .withOpacity(!chartState._renderingDetails.isLegendToggled + ? (animateOpacity - (1 - dataLabel.opacity)) < 0 + ? 0 + : animateOpacity - (1 - dataLabel.opacity) + : dataLabel.opacity) ..style = PaintingStyle.fill, Rect.fromLTRB( labelRect.left, labelRect.top, labelRect.right, labelRect.bottom), @@ -469,14 +475,16 @@ void _triggerPyramidDataLabelEvent( PyramidSeriesRenderer seriesRenderer, SfPyramidChartState chartState, Offset position) { - final int seriesIndex = 0; + const int seriesIndex = 0; + DataLabelSettings dataLabel; + PointInfo point; + Offset labelLocation; for (int pointIndex = 0; pointIndex < seriesRenderer._renderPoints!.length; pointIndex++) { - final DataLabelSettings dataLabel = - seriesRenderer._series.dataLabelSettings; - final PointInfo point = seriesRenderer._renderPoints![pointIndex]; - final Offset labelLocation = point.symbolLocation; + dataLabel = seriesRenderer._series.dataLabelSettings; + point = seriesRenderer._renderPoints![pointIndex]; + labelLocation = point.symbolLocation; if (dataLabel.isVisible && seriesRenderer._renderPoints![pointIndex].labelRect != null && seriesRenderer._renderPoints![pointIndex].labelRect! diff --git a/packages/syncfusion_flutter_charts/lib/src/pyramid_chart/renderer/pyramid_series.dart b/packages/syncfusion_flutter_charts/lib/src/pyramid_chart/renderer/pyramid_series.dart index b7cca0b5f..1efc700c3 100644 --- a/packages/syncfusion_flutter_charts/lib/src/pyramid_chart/renderer/pyramid_series.dart +++ b/packages/syncfusion_flutter_charts/lib/src/pyramid_chart/renderer/pyramid_series.dart @@ -6,6 +6,9 @@ class _PyramidSeriesBase extends ChartSeries this.key, this.onCreateRenderer, this.onRendererCreated, + this.onPointTap, + this.onPointDoubleTap, + this.onPointLongPress, this.dataSource, this.xValueMapper, this.yValueMapper, @@ -27,8 +30,6 @@ class _PyramidSeriesBase extends ChartSeries DataLabelSettings? dataLabelSettings, double? animationDuration, double? opacity, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, List? initialSelectedDataIndexes, }) : height = height ?? '80%', @@ -42,12 +43,10 @@ class _PyramidSeriesBase extends ChartSeries borderColor = borderColor ?? Colors.transparent, borderWidth = borderWidth ?? 0.0, legendIconType = legendIconType ?? LegendIconType.seriesType, - dataLabelSettings = dataLabelSettings ?? DataLabelSettings(), + dataLabelSettings = dataLabelSettings ?? const DataLabelSettings(), animationDuration = animationDuration ?? 1500, opacity = opacity ?? 1, initialSelectedDataIndexes = initialSelectedDataIndexes ?? [], - // ignore: deprecated_member_use_from_same_package - selectionSettings = selectionSettings ?? SelectionSettings(), selectionBehavior = selectionBehavior ?? SelectionBehavior(), super( name: name, @@ -421,25 +420,6 @@ class _PyramidSeriesBase extends ChartSeries @override final double opacity; - ///Customizes the selection of series. - /// - ///```dart - ///Widget build(BuildContext context) { - /// return Container( - /// child: SfPyramidChart( - /// series: PyramidSeries( - /// selectionSettings: SelectionSettings( - /// selectedColor: Colors.red, - /// unselectedColor: Colors.grey - /// ), - /// ) - /// )); - ///} - ///``` - @override - // ignore: deprecated_member_use_from_same_package - final SelectionSettings selectionSettings; - ///Customizes the selection of series. /// ///```dart @@ -579,10 +559,73 @@ class _PyramidSeriesBase extends ChartSeries ///``` final PyramidSeriesRendererCreatedCallback? onRendererCreated; + ///Called when tapped on the chart data point. + /// + ///The user can fetch the series index, point index, viewport point index and + /// data of the tapped data point. + ///```dart + ///Widget build(BuildContext context) { + /// ChartSeriesController _chartSeriesController; + /// return Container( + /// child: SfCartesianChart( + /// series: >[ + /// LineSeries( + /// onPointTap: (ChartPointDetails details) { + /// print(details.pointIndex); + /// }, + /// ), + /// ], + /// )); + ///} + ///``` + final ChartPointInteractionCallback? onPointTap; + + ///Called when double tapped on the chart data point. + /// + ///The user can fetch the series index, point index, viewport point index and + /// data of the double-tapped data point. + ///```dart + ///Widget build(BuildContext context) { + /// ChartSeriesController _chartSeriesController; + /// return Container( + /// child: SfCartesianChart( + /// series: >[ + /// LineSeries( + /// onPointDoubleTap: (ChartPointDetails details) { + /// print(details.pointIndex); + /// }, + /// ), + /// ], + /// )); + ///} + ///``` + final ChartPointInteractionCallback? onPointDoubleTap; + + ///Called when long pressed on the chart data point. + /// + ///The user can fetch the series index, point index, viewport point index and + /// data of the long-pressed data point. + ///```dart + ///Widget build(BuildContext context) { + /// ChartSeriesController _chartSeriesController; + /// return Container( + /// child: SfCartesianChart( + /// series: >[ + /// LineSeries( + /// onPointLongPress: (ChartPointDetails details) { + /// print(details.pointIndex); + /// }, + /// ), + /// ], + /// )); + ///} + ///``` + final ChartPointInteractionCallback? onPointLongPress; + @override void calculateEmptyPointValue( int pointIndex, dynamic currentPoint, dynamic seriesRenderer) { - final List dataPoints = seriesRenderer._dataPoints; + final List> dataPoints = seriesRenderer._dataPoints; final EmptyPointSettings empty = emptyPointSettings; final int pointLength = dataPoints.length; final PointInfo point = dataPoints[pointIndex]; @@ -619,12 +662,16 @@ class _PyramidSeriesBase extends ChartSeries /// /// Provides the property of color, [opacity], border color and border width for customizing the appearance. /// +@immutable class PyramidSeries extends _PyramidSeriesBase { /// Creating an argument constructor of PyramidSeries class. PyramidSeries({ ValueKey? key, ChartSeriesRendererFactory? onCreateRenderer, PyramidSeriesRendererCreatedCallback? onRendererCreated, + ChartPointInteractionCallback? onPointTap, + ChartPointInteractionCallback? onPointDoubleTap, + ChartPointInteractionCallback? onPointLongPress, List? dataSource, ChartValueMapper? xValueMapper, ChartValueMapper? yValueMapper, @@ -646,14 +693,15 @@ class PyramidSeries extends _PyramidSeriesBase { num? explodeIndex, ActivationMode? explodeGesture, String? explodeOffset, - // ignore: deprecated_member_use_from_same_package - SelectionSettings? selectionSettings, SelectionBehavior? selectionBehavior, List? initialSelectedDataIndexes, }) : super( key: key, onCreateRenderer: onCreateRenderer, onRendererCreated: onRendererCreated, + onPointTap: onPointTap, + onPointDoubleTap: onPointDoubleTap, + onPointLongPress: onPointLongPress, dataSource: dataSource, xValueMapper: (int index) => xValueMapper!(dataSource![index], index), yValueMapper: (int index) => yValueMapper!(dataSource![index], index), @@ -679,7 +727,6 @@ class PyramidSeries extends _PyramidSeriesBase { explodeIndex: explodeIndex, explodeOffset: explodeOffset, explodeGesture: explodeGesture, - selectionSettings: selectionSettings, selectionBehavior: selectionBehavior, initialSelectedDataIndexes: initialSelectedDataIndexes, ); @@ -696,6 +743,82 @@ class PyramidSeries extends _PyramidSeriesBase { } return PyramidSeriesRenderer(); } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (other.runtimeType != runtimeType) { + return false; + } + + return other is PyramidSeries && + other.onCreateRenderer == onCreateRenderer && + other.onRendererCreated == onRendererCreated && + other.onPointTap == onPointTap && + other.onPointDoubleTap == onPointDoubleTap && + other.onPointLongPress == onPointLongPress && + other.dataSource == dataSource && + other.xValueMapper == xValueMapper && + other.yValueMapper == yValueMapper && + other.pointColorMapper == pointColorMapper && + other.textFieldMapper == textFieldMapper && + other.name == name && + other.height == height && + other.width == width && + other.pyramidMode == pyramidMode && + other.gapRatio == gapRatio && + other.legendIconType == legendIconType && + other.emptyPointSettings == emptyPointSettings && + other.dataLabelSettings == dataLabelSettings && + other.animationDuration == animationDuration && + other.opacity == opacity && + other.borderColor == borderColor && + other.borderWidth == borderWidth && + other.explode == explode && + other.explodeIndex == explodeIndex && + other.explodeGesture == explodeGesture && + other.explodeOffset == explodeOffset && + other.selectionBehavior == selectionBehavior && + listEquals( + other.initialSelectedDataIndexes, initialSelectedDataIndexes); + } + + @override + int get hashCode { + final List values = [ + onCreateRenderer, + onRendererCreated, + onPointTap, + onPointDoubleTap, + onPointLongPress, + dataSource, + xValueMapper, + yValueMapper, + pointColorMapper, + textFieldMapper, + name, + height, + width, + pyramidMode, + gapRatio, + legendIconType, + emptyPointSettings, + dataLabelSettings, + animationDuration, + opacity, + borderColor, + borderWidth, + explode, + explodeIndex, + explodeGesture, + explodeOffset, + selectionBehavior, + initialSelectedDataIndexes + ]; + return hashList(values); + } } class _PyramidChartPainter extends CustomPainter { @@ -721,30 +844,31 @@ class _PyramidChartPainter extends CustomPainter { seriesRenderer = chartState._chartSeries.visibleSeriesRenderers[seriesIndex]; + double animationFactor, factor, height; for (int pointIndex = 0; pointIndex < seriesRenderer._renderPoints!.length; pointIndex++) { if (seriesRenderer._renderPoints![pointIndex].isVisible) { - final double animationFactor = - seriesAnimation != null ? seriesAnimation!.value : 1; + animationFactor = seriesAnimation != null ? seriesAnimation!.value : 1; if (seriesRenderer._series.animationDuration > 0 && - !chartState._isLegendToggled) { - final double factor = (chartState._chartAreaRect.top + - chartState._chartAreaRect.height) - + !chartState._renderingDetails.isLegendToggled) { + factor = (chartState._renderingDetails.chartAreaRect.top + + chartState._renderingDetails.chartAreaRect.height) - animationFactor * - (chartState._chartAreaRect.top + - chartState._chartAreaRect.height); - final double height = chartState._chartAreaRect.top + - chartState._chartAreaRect.height - + (chartState._renderingDetails.chartAreaRect.top + + chartState._renderingDetails.chartAreaRect.height); + height = chartState._renderingDetails.chartAreaRect.top + + chartState._renderingDetails.chartAreaRect.height - factor; canvas.clipRect(Rect.fromLTRB( 0, - chartState._chartAreaRect.top + - chartState._chartAreaRect.height - + chartState._renderingDetails.chartAreaRect.top + + chartState._renderingDetails.chartAreaRect.height - height, - chartState._chartAreaRect.left + chartState._chartAreaRect.width, - chartState._chartAreaRect.top + - chartState._chartAreaRect.height)); + chartState._renderingDetails.chartAreaRect.left + + chartState._renderingDetails.chartAreaRect.width, + chartState._renderingDetails.chartAreaRect.top + + chartState._renderingDetails.chartAreaRect.height)); } chartState._chartSeries ._calculatePyramidSegments(canvas, pointIndex, seriesRenderer); @@ -782,10 +906,6 @@ class PyramidSeriesRenderer extends ChartSeriesRenderer { bool _isSelectionEnable = false; } -/// Called when the pyramid series is created -typedef PyramidSeriesRendererCreatedCallback = void Function( - PyramidSeriesController controller); - ///We can redraw the series with updating or creating new points by using this controller.If we need to access the redrawing methods ///in this before we must get the ChartSeriesController onRendererCreated event. class PyramidSeriesController { @@ -888,8 +1008,9 @@ class PyramidSeriesController { /// Add or update the data points on dynamic series update void _addOrUpdateDataPoints(List indexes, bool needUpdate) { + int dataIndex; for (int i = 0; i < indexes.length; i++) { - final int dataIndex = indexes[i]; + dataIndex = indexes[i]; _addOrUpdateDataPoint(dataIndex, needUpdate); } } @@ -925,8 +1046,9 @@ class PyramidSeriesController { ///Remove the redudant index from the list final List indexList = removedDataIndexes.toSet().toList(); indexList.sort((int b, int a) => a.compareTo(b)); + int dataIndex; for (int i = 0; i < indexList.length; i++) { - final int dataIndex = indexList[i]; + dataIndex = indexList[i]; _removeDataPoint(dataIndex); } } @@ -951,10 +1073,10 @@ class PyramidSeriesController { _chartState._renderDataLabel!.state?.render(); } if (seriesRenderer._series.dataLabelSettings.isVisible && - _chartState._chartTemplate != null && + _chartState._renderingDetails.chartTemplate != null && // ignore: unnecessary_null_comparison - _chartState._chartTemplate!.state != null) { - _chartState._chartTemplate!.state.templateRender(); + _chartState._renderingDetails.chartTemplate!.state != null) { + _chartState._renderingDetails.chartTemplate!.state.templateRender(); } } } diff --git a/packages/syncfusion_flutter_charts/lib/src/pyramid_chart/utils/helper.dart b/packages/syncfusion_flutter_charts/lib/src/pyramid_chart/utils/helper.dart index 56a790e36..38fb1326e 100644 --- a/packages/syncfusion_flutter_charts/lib/src/pyramid_chart/utils/helper.dart +++ b/packages/syncfusion_flutter_charts/lib/src/pyramid_chart/utils/helper.dart @@ -1,7 +1,7 @@ part of charts; /// Method for checking if point is within polygon -bool _isPointInPolygon(List polygon, dynamic point) { +bool _isPointInPolygon(List polygon, Offset point) { bool p = false; int i = -1; final int l = polygon.length; @@ -26,20 +26,20 @@ void _findTemplates(dynamic _chartState) { const num lineLength = 10; PointInfo point; Widget labelWidget; - _chartState._templates = <_ChartTemplateInfo>[]; - _chartState._dataLabelTemplateRegions = []; + _chartState._renderingDetails.dataLabelTemplateRegions = []; + _chartState._renderingDetails.templates = <_ChartTemplateInfo>[]; dynamic series; + dynamic seriesRenderer; + ChartAlignment labelAlign; for (int k = 0; k < _chartState._chartSeries.visibleSeriesRenderers.length; k++) { - final dynamic seriesRenderer = - _chartState._chartSeries.visibleSeriesRenderers[k]; + seriesRenderer = _chartState._chartSeries.visibleSeriesRenderers[k]; series = seriesRenderer._series; - if (series.dataLabelSettings.isVisible && + if (series.dataLabelSettings.isVisible == true && series.dataLabelSettings.builder != null) { for (int i = 0; i < seriesRenderer._renderPoints.length; i++) { point = seriesRenderer._renderPoints[i]; - ChartAlignment labelAlign; if (point.isVisible) { labelWidget = series.dataLabelSettings .builder(series.dataSource[i], point, series, i, k); @@ -58,13 +58,13 @@ void _findTemplates(dynamic _chartState) { ? ChartAlignment.far : ChartAlignment.near; } - _chartState._templates.add(_ChartTemplateInfo( + _chartState._renderingDetails.templates.add(_ChartTemplateInfo( key: GlobalKey(), templateType: 'DataLabel', pointIndex: i, seriesIndex: k, needMeasure: true, - clipRect: _chartState._chartAreaRect, + clipRect: _chartState._renderingDetails.chartAreaRect, animationDuration: 500, widget: labelWidget, horizontalAlignment: labelAlign, @@ -78,17 +78,21 @@ void _findTemplates(dynamic _chartState) { /// To render a template void _renderTemplates(dynamic _chartState) { - if (_chartState._templates.isNotEmpty) { - for (int i = 0; i < _chartState._templates.length; i++) { - final _ChartTemplateInfo chartTemplateInfo = _chartState._templates[i]; + if (_chartState._renderingDetails.templates.isNotEmpty == true) { + _ChartTemplateInfo chartTemplateInfo; + for (int i = 0; i < _chartState._renderingDetails.templates.length; i++) { + chartTemplateInfo = _chartState._renderingDetails.templates[i]; chartTemplateInfo.animationDuration = - !_chartState._initialRender ? 0 : chartTemplateInfo.animationDuration; + _chartState._renderingDetails.initialRender == false + ? 0 + : chartTemplateInfo.animationDuration; } - _chartState._chartTemplate = _ChartTemplate( - templates: _chartState._templates, - render: _chartState._animateCompleted, + _chartState._renderingDetails.chartTemplate = _ChartTemplate( + templates: _chartState._renderingDetails.templates, + render: _chartState._renderingDetails.animateCompleted, chartState: _chartState); - _chartState._chartWidgets.add(_chartState._chartTemplate); + _chartState._renderingDetails.chartWidgets + .add(_chartState._renderingDetails.chartTemplate); } } @@ -100,21 +104,20 @@ Color _getPyramidFunnelColor(PointInfo currentPoint, final DataLabelSettings dataLabel = series.dataLabelSettings; final DataLabelSettingsRenderer dataLabelSettingsRenderer = seriesRenderer._dataLabelSettingsRenderer; - if (currentPoint.renderPosition == null || - currentPoint.renderPosition == ChartDataLabelPosition.inside && - !currentPoint.saturationRegionOutside) { - color = _innerColor(dataLabelSettingsRenderer._color, currentPoint.fill, - _chartState._chartTheme); - } else { - color = _outerColor( - dataLabelSettingsRenderer._color, - dataLabel.useSeriesColor - ? currentPoint.fill - : (_chartState._chart.backgroundColor != null - ? _chartState._chartTheme.plotAreaBackgroundColor - : null), - _chartState._chartTheme); - } + color = (currentPoint.renderPosition == null || + currentPoint.renderPosition == ChartDataLabelPosition.inside && + !currentPoint.saturationRegionOutside) + ? _innerColor(dataLabelSettingsRenderer._color, currentPoint.fill, + _chartState._renderingDetails.chartTheme) + : _outerColor( + dataLabelSettingsRenderer._color, + dataLabel.useSeriesColor + ? currentPoint.fill + : (_chartState._chart.backgroundColor != null + ? _chartState + ._renderingDetails.chartTheme.plotAreaBackgroundColor + : null), + _chartState._renderingDetails.chartTheme); return _getSaturationColor(color); } @@ -174,14 +177,16 @@ TextStyle _getDataLabelTextStyle( /// To check the point explosion bool _isNeedExplode(int pointIndex, dynamic series, dynamic _chartState) { bool isNeedExplode = false; - if (series.explode) { - if (_chartState._initialRender) { + if (series.explode == true) { + if (_chartState._renderingDetails.initialRender == true) { if (pointIndex == series.explodeIndex) { - _chartState._explodedPoints.add(pointIndex); + _chartState._renderingDetails.explodedPoints.add(pointIndex); isNeedExplode = true; } - } else if (_chartState._widgetNeedUpdate || _chartState._isLegendToggled) { - isNeedExplode = _chartState._explodedPoints.contains(pointIndex); + } else if (_chartState._renderingDetails.widgetNeedUpdate == true || + _chartState._renderingDetails.isLegendToggled == true) { + isNeedExplode = + _chartState._renderingDetails.explodedPoints.contains(pointIndex); } } return isNeedExplode; diff --git a/packages/syncfusion_flutter_charts/lib/src/sparkline/marker.dart b/packages/syncfusion_flutter_charts/lib/src/sparkline/marker.dart index a77b1054a..4cd32d634 100644 --- a/packages/syncfusion_flutter_charts/lib/src/sparkline/marker.dart +++ b/packages/syncfusion_flutter_charts/lib/src/sparkline/marker.dart @@ -12,6 +12,7 @@ import 'utils/enum.dart'; /// Provides the options of [color], [borderWidth], [borderColor] and [shape] /// of the marker to customize the appearance. /// +@immutable class SparkChartMarker { /// Creates an instance of spark chart marker to add and customizes the marker /// in spark chart widget. To make, the marker visible, set `displayeMode` @@ -30,7 +31,7 @@ class SparkChartMarker { /// ); /// } /// ``` - SparkChartMarker( + const SparkChartMarker( {this.displayMode = SparkChartMarkerDisplayMode.none, this.borderColor, this.borderWidth = 2, diff --git a/packages/syncfusion_flutter_charts/lib/src/sparkline/plot_band.dart b/packages/syncfusion_flutter_charts/lib/src/sparkline/plot_band.dart index 02b103a47..f9d9514de 100644 --- a/packages/syncfusion_flutter_charts/lib/src/sparkline/plot_band.dart +++ b/packages/syncfusion_flutter_charts/lib/src/sparkline/plot_band.dart @@ -12,6 +12,7 @@ import 'package:flutter/rendering.dart'; /// Provides the property of [start], [end], [color], [borderColor], and /// [borderWidth] to customize the appearance. /// +@immutable class SparkChartPlotBand { /// Creates an instance of spark chart plot band to add and customizes the /// plot band in spark chart widget. To make, the plot band visible, define @@ -29,7 +30,7 @@ class SparkChartPlotBand { /// ); /// } /// ``` - SparkChartPlotBand( + const SparkChartPlotBand( {this.color = const Color.fromRGBO(191, 212, 252, 0.5), this.start, this.end, diff --git a/packages/syncfusion_flutter_charts/lib/src/sparkline/renderers/renderer_base.dart b/packages/syncfusion_flutter_charts/lib/src/sparkline/renderers/renderer_base.dart index 35920e9a4..a03b49ae7 100644 --- a/packages/syncfusion_flutter_charts/lib/src/sparkline/renderers/renderer_base.dart +++ b/packages/syncfusion_flutter_charts/lib/src/sparkline/renderers/renderer_base.dart @@ -9,9 +9,10 @@ import '../utils/enum.dart'; import '../utils/helper.dart'; /// Represents the render object for spark chart +@immutable abstract class SfSparkChartRenderObjectWidget extends LeafRenderObjectWidget { /// Creates the render object for spark chart - SfSparkChartRenderObjectWidget( + const SfSparkChartRenderObjectWidget( {Key? key, this.data, this.dataCount, @@ -100,7 +101,9 @@ abstract class SfSparkChartRenderObjectWidget extends LeafRenderObjectWidget { abstract class RenderSparkChart extends RenderBox { /// Creates the render object widget RenderSparkChart( - {Widget? child, + { + //ignore: avoid_unused_constructor_parameters + Widget? child, List? data, int? dataCount, SparkChartIndexedValueMapper? xValueMapper, @@ -504,7 +507,7 @@ abstract class RenderSparkChart extends RenderBox { } } else { dynamic xValue; - dynamic yValue; + num? yValue; late String labelX; dynamic actualX; if (xValueMapper != null && @@ -527,6 +530,7 @@ abstract class RenderSparkChart extends RenderBox { yValue = yValueMapper!(i); labelY = _getDataLabel(yValue); + // ignore: unnecessary_null_comparison if (xValue != null && yValue != null) { currentPoint = SparkChartPoint(x: xValue, y: yValue); currentPoint.actualX = actualX; @@ -560,11 +564,11 @@ abstract class RenderSparkChart extends RenderBox { final double value = axisCrossesAt!; double? axisLineHeight = areaSize!.height - ((areaSize!.height / diffY!) * (-minY!)); - axisLineHeight = ((minY! < 0 && maxY! <= 0) + axisLineHeight = minY! < 0 && maxY! <= 0 ? 0 : (minY! < 0 && maxY! > 0) ? axisHeight - : areaSize!.height); + : areaSize!.height; if (value >= minY! && value <= maxY!) { axisLineHeight = areaSize!.height - (areaSize!.height * ((value - minY!) / diffY!)).roundToDouble(); @@ -575,7 +579,7 @@ abstract class RenderSparkChart extends RenderBox { /// Inverse the data Points void inverseDataPoints() { final List temp = dataPoints!.reversed.toList(); - reversedDataLabels = List.from(dataLabels!.reversed); + reversedDataLabels = List.from(dataLabels!.reversed); dataLabels!.clear(); dataLabels!.addAll(reversedDataLabels!); dataPoints!.clear(); @@ -605,7 +609,7 @@ abstract class RenderSparkChart extends RenderBox { x = dataPoints![i].x.toDouble(); y = dataPoints![i].y.toDouble(); visiblePoint = transformToCoordinatePoint(minX!, maxX!, minY!, maxY!, - diffX!, diffY!, areaSize, x, y, dataPoints!.length); + diffX!, diffY!, areaSize!, x, y, dataPoints!.length); coordinatePoints!.add(visiblePoint); } coordinatePoints = sortScreenCoordiantePoints(coordinatePoints!); @@ -625,8 +629,8 @@ abstract class RenderSparkChart extends RenderBox { : (plotBand!.end ?? maxY!) > maxY! ? maxY : (plotBand!.end ?? maxY); - plotBandStartHeight = (height - ((height / diffY!) * (start! - minY!))); - plotBandEndHeight = (height - ((height / diffY!) * (end! - minY!))); + plotBandStartHeight = height - ((height / diffY!) * (start! - minY!)); + plotBandEndHeight = height - ((height / diffY!) * (end! - minY!)); } /// Method to render axis line diff --git a/packages/syncfusion_flutter_charts/lib/src/sparkline/renderers/spark_area_renderer.dart b/packages/syncfusion_flutter_charts/lib/src/sparkline/renderers/spark_area_renderer.dart index 0cbfeb3ec..33cea2fd9 100644 --- a/packages/syncfusion_flutter_charts/lib/src/sparkline/renderers/spark_area_renderer.dart +++ b/packages/syncfusion_flutter_charts/lib/src/sparkline/renderers/spark_area_renderer.dart @@ -12,10 +12,10 @@ import 'renderer_base.dart'; class SfSparkAreaChartRenderObjectWidget extends SfSparkChartRenderObjectWidget { /// Creates the render object for spark chart - SfSparkAreaChartRenderObjectWidget({ + const SfSparkAreaChartRenderObjectWidget({ Key? key, - double? borderWidth, - Color? borderColor, + this.borderWidth, + this.borderColor, List? data, int? dataCount, SparkChartIndexedValueMapper? xValueMapper, @@ -32,19 +32,14 @@ class SfSparkAreaChartRenderObjectWidget Color? negativePointColor, Color? color, SparkChartPlotBand? plotBand, - SparkChartMarker? marker, - SparkChartLabelDisplayMode? labelDisplayMode, - TextStyle? labelStyle, + this.marker, + this.labelDisplayMode, + this.labelStyle, ThemeData? themeData, SparkChartDataDetails? sparkChartDataDetails, List? coordinatePoints, List? dataPoints, - }) : borderWidth = borderWidth, - borderColor = borderColor, - marker = marker, - labelDisplayMode = labelDisplayMode, - labelStyle = labelStyle, - super( + }) : super( key: key, data: data, dataCount: dataCount, @@ -279,17 +274,19 @@ class _RenderSparkAreaChart extends RenderSparkChart { void processDataSource() { super.processDataSource(); if (dataPoints != null && dataPoints!.isNotEmpty) { - final List temp = List.from(dataPoints!); - final List tempDataLabels = List.from(dataLabels!); + final List temp = + List.from(dataPoints!); + final List tempDataLabels = List.from(dataLabels!); dataLabels!.clear(); dataPoints!.clear(); - final SparkChartPoint point1 = SparkChartPoint(x: temp[0].x, y: minY); + final SparkChartPoint point1 = + SparkChartPoint(x: temp[0].x, y: minY!.toDouble()); point1.labelX = temp[0].labelX; point1.labelY = temp[0].labelY; dataPoints!.add(point1); dataPoints!.addAll(temp); final SparkChartPoint point2 = - SparkChartPoint(x: temp[temp.length - 1].x, y: minY); + SparkChartPoint(x: temp[temp.length - 1].x, y: minY!.toDouble()); point2.labelX = temp[temp.length - 1].labelX; point2.labelY = temp[temp.length - 1].labelY; dataPoints!.add(point2); @@ -348,24 +345,24 @@ class _RenderSparkAreaChart extends RenderSparkChart { : (coordinatePoints![i].dy + size.height))); if (dataPoints![i].dataLabelOffset!.dx <= offset.dx) { dataPoints![i].dataLabelOffset = - Offset((offset.dx), dataPoints![i].dataLabelOffset!.dy); + Offset(offset.dx, dataPoints![i].dataLabelOffset!.dy); } if (dataPoints![i].dataLabelOffset!.dx >= offset.dx + areaSize!.width) { dataPoints![i].dataLabelOffset = Offset( - ((offset.dx + areaSize!.width) - size.width), + (offset.dx + areaSize!.width) - size.width, dataPoints![i].dataLabelOffset!.dy); } if (dataPoints![i].dataLabelOffset!.dy <= offset.dy) { dataPoints![i].dataLabelOffset = Offset( dataPoints![i].dataLabelOffset!.dx, - (offset.dy + + offset.dy + (marker != null && marker!.displayMode != SparkChartMarkerDisplayMode.none ? marker!.size / 2 + size.height - : size.height))); + : size.height)); } if (dataPoints![i].dataLabelOffset!.dy >= @@ -437,11 +434,11 @@ class _RenderSparkAreaChart extends RenderSparkChart { _lowPoint!, axisCrossesAt!, themeData!, - lowPointColor!, - highPointColor!, - negativePointColor!, - firstPointColor!, - lastPointColor!); + lowPointColor, + highPointColor, + negativePointColor, + firstPointColor, + lastPointColor); } if (labelDisplayMode != null && labelDisplayMode != SparkChartLabelDisplayMode.none) { diff --git a/packages/syncfusion_flutter_charts/lib/src/sparkline/renderers/spark_bar_renderer.dart b/packages/syncfusion_flutter_charts/lib/src/sparkline/renderers/spark_bar_renderer.dart index 647b29674..b058b7b7c 100644 --- a/packages/syncfusion_flutter_charts/lib/src/sparkline/renderers/spark_bar_renderer.dart +++ b/packages/syncfusion_flutter_charts/lib/src/sparkline/renderers/spark_bar_renderer.dart @@ -10,10 +10,10 @@ import 'renderer_base.dart'; /// Represents the render object for spark chart class SfSparkBarChartRenderObjectWidget extends SfSparkChartRenderObjectWidget { /// Creates the render object for spark chart - SfSparkBarChartRenderObjectWidget( + const SfSparkBarChartRenderObjectWidget( {Key? key, - double? borderWidth, - Color? borderColor, + this.borderWidth, + this.borderColor, List? data, int? dataCount, SparkChartIndexedValueMapper? xValueMapper, @@ -30,17 +30,13 @@ class SfSparkBarChartRenderObjectWidget extends SfSparkChartRenderObjectWidget { Color? negativePointColor, Color? color, SparkChartPlotBand? plotBand, - SparkChartLabelDisplayMode? labelDisplayMode, - TextStyle? labelStyle, + this.labelDisplayMode, + this.labelStyle, ThemeData? themeData, SparkChartDataDetails? sparkChartDataDetails, List? coordinatePoints, List? dataPoints}) - : borderWidth = borderWidth, - borderColor = borderColor, - labelDisplayMode = labelDisplayMode, - labelStyle = labelStyle, - super( + : super( key: key, data: data, dataCount: dataCount, @@ -282,15 +278,12 @@ class _RenderSparkBarChart extends RenderSparkChart { final double xInterval = dataPoints!.length > 1 ? dataPoints![1].x.toDouble() - dataPoints![0].x.toDouble() : dataPoints!.length.toDouble(); - final double columnSpace = 0.5; // Default space for column and winloss - final double space = columnSpace * 2; - final axisBaseValue = minY! < 0 ? minY : 0; + const double columnSpace = 0.5; // Default space for column and winloss + const double space = columnSpace * 2; + final double? axisBaseValue = minY! < 0 ? minY : 0; double visibleXPoint; Rect rect; - double top; - double x, y, y2; - double columnHeight; - double currentColumnHeight; + double top, x, y, y2, columnHeight, currentColumnHeight, yPoint; double columnWidth = areaSize!.width / (((maxX! - minX!) / xInterval) + 1); columnWidth -= space; diffY = maxY! - axisBaseValue!; @@ -321,7 +314,7 @@ class _RenderSparkBarChart extends RenderSparkChart { rect = Rect.fromLTRB(visibleXPoint, top, visibleXPoint + columnWidth, top + (y2 - axisHeight!).abs()); _segments!.add(rect); - final double yPoint = y >= axisCrossesAt ? rect.top : rect.bottom; + yPoint = y >= axisCrossesAt ? rect.top : rect.bottom; coordinatePoints!.add(Offset(visibleXPoint + columnWidth / 2, yPoint)); } } @@ -355,8 +348,8 @@ class _RenderSparkBarChart extends RenderSparkChart { final double end = (plotBand!.end ?? maxY!) > maxY! ? maxY! : (plotBand!.end ?? maxY!); final double baseValue = minY! < 0 ? minY! : 0; - plotBandStartHeight = (height - ((height / diffY!) * (start - baseValue))); - plotBandEndHeight = (height - ((height / diffY!) * (end - baseValue))); + plotBandStartHeight = height - ((height / diffY!) * (start - baseValue)); + plotBandEndHeight = height - ((height / diffY!) * (end - baseValue)); } /// Method to render bar series @@ -414,16 +407,16 @@ class _RenderSparkBarChart extends RenderSparkChart { if (labelDisplayMode != SparkChartLabelDisplayMode.none && labelStyle != null) { size = getTextSize(dataLabels![i], labelStyle!); - yPosition = (dataPoints![i].y > 0 + yPosition = dataPoints![i].y > 0 ? ((_segments![i].topCenter.dy + offset.dy) - size.height) - : ((_segments![i].bottomCenter.dy + offset.dy))); + : (_segments![i].bottomCenter.dy + offset.dy); dataPoints![i].dataLabelOffset = Offset( (offset.dx + _segments![i].topCenter.dx) - size.width / 2, yPosition); if (dataPoints![i].dataLabelOffset!.dy <= offset.dy) { dataPoints![i].dataLabelOffset = Offset( - dataPoints![i].dataLabelOffset!.dx, (offset.dy + size.height)); + dataPoints![i].dataLabelOffset!.dx, offset.dy + size.height); } if (dataPoints![i].dataLabelOffset!.dy >= offset.dy + areaSize!.height) { diff --git a/packages/syncfusion_flutter_charts/lib/src/sparkline/renderers/spark_line_renderer.dart b/packages/syncfusion_flutter_charts/lib/src/sparkline/renderers/spark_line_renderer.dart index bc127dcfc..16d8e39b5 100644 --- a/packages/syncfusion_flutter_charts/lib/src/sparkline/renderers/spark_line_renderer.dart +++ b/packages/syncfusion_flutter_charts/lib/src/sparkline/renderers/spark_line_renderer.dart @@ -12,7 +12,7 @@ import 'renderer_base.dart'; class SfSparkLineChartRenderObjectWidget extends SfSparkChartRenderObjectWidget { /// Creates the render object for spark chart - SfSparkLineChartRenderObjectWidget( + const SfSparkLineChartRenderObjectWidget( {Key? key, this.width, this.dashArray, @@ -303,14 +303,14 @@ class _RenderSparkLineChart extends RenderSparkChart { if (labelDisplayMode != SparkChartLabelDisplayMode.none && labelStyle != null) { size = getTextSize(dataLabels![i], labelStyle!); - yPosition = (marker != null && + yPosition = marker != null && marker!.displayMode != SparkChartMarkerDisplayMode.none ? (dataPoints![i].y > 0 ? (coordinatePoints![i].dy - size.height - marker!.size / 2) : (coordinatePoints![i].dy + marker!.size / 2)) : dataPoints![i].y > 0 ? (coordinatePoints![i].dy - size.height) - : (coordinatePoints![i].dy)); + : (coordinatePoints![i].dy); dataPoints![i].dataLabelOffset = Offset( (offset.dx + coordinatePoints![i].dx) - size.width / 2, offset.dy + yPosition); @@ -341,14 +341,14 @@ class _RenderSparkLineChart extends RenderSparkChart { labelStyle != null) { size = getTextSize(dataLabels![i], labelStyle!); - yPosition = (marker != null && + yPosition = marker != null && marker!.displayMode != SparkChartMarkerDisplayMode.none ? (dataPoints![i].y > 0 ? (coordinatePoints![i].dy - size.height - marker!.size / 2) : (coordinatePoints![i].dy + marker!.size / 2)) : dataPoints![i].y > 0 ? (coordinatePoints![i].dy - size.height) - : (coordinatePoints![i].dy)); + : (coordinatePoints![i].dy); dataPoints![i].dataLabelOffset = Offset( (offset.dx + coordinatePoints![i].dx) - size.width / 2, offset.dy + yPosition); @@ -365,22 +365,22 @@ class _RenderSparkLineChart extends RenderSparkChart { SparkChartPoint dataPoint, Size size, Offset offset) { if (dataPoint.dataLabelOffset!.dx <= offset.dx) { dataPoint.dataLabelOffset = - Offset((offset.dx), dataPoint.dataLabelOffset!.dy); + Offset(offset.dx, dataPoint.dataLabelOffset!.dy); } if (dataPoint.dataLabelOffset!.dx >= offset.dx + areaSize!.width) { dataPoint.dataLabelOffset = Offset( - ((offset.dx + areaSize!.width) - size.width), + (offset.dx + areaSize!.width) - size.width, dataPoint.dataLabelOffset!.dy); } if (dataPoint.dataLabelOffset!.dy <= offset.dy) { dataPoint.dataLabelOffset = Offset( dataPoint.dataLabelOffset!.dx, - (offset.dy + + offset.dy + (marker != null && marker!.displayMode != SparkChartMarkerDisplayMode.none ? marker!.size / 2 + size.height - : size.height))); + : size.height)); } if (dataPoint.dataLabelOffset!.dy >= offset.dy + areaSize!.height) { diff --git a/packages/syncfusion_flutter_charts/lib/src/sparkline/renderers/spark_win_loss_renderer.dart b/packages/syncfusion_flutter_charts/lib/src/sparkline/renderers/spark_win_loss_renderer.dart index 6cd9a5c5c..7db69b80a 100644 --- a/packages/syncfusion_flutter_charts/lib/src/sparkline/renderers/spark_win_loss_renderer.dart +++ b/packages/syncfusion_flutter_charts/lib/src/sparkline/renderers/spark_win_loss_renderer.dart @@ -11,7 +11,7 @@ import 'renderer_base.dart'; class SfSparkWinLossChartRenderObjectWidget extends SfSparkChartRenderObjectWidget { /// Creates the render object for spark chart - SfSparkWinLossChartRenderObjectWidget( + const SfSparkWinLossChartRenderObjectWidget( {Key? key, List? data, int? dataCount, @@ -19,9 +19,9 @@ class SfSparkWinLossChartRenderObjectWidget SparkChartIndexedValueMapper? yValueMapper, Color? color, SparkChartPlotBand? plotBand, - double? borderWidth, - Color? borderColor, - Color? tiePointColor, + this.borderWidth, + this.borderColor, + this.tiePointColor, bool? isInversed, double? axisCrossesAt, Color? axisLineColor, @@ -36,10 +36,7 @@ class SfSparkWinLossChartRenderObjectWidget ThemeData? themeData, List? coordinatePoints, List? dataPoints}) - : borderWidth = borderWidth, - borderColor = borderColor, - tiePointColor = tiePointColor, - super( + : super( key: key, data: data, dataCount: dataCount, @@ -236,10 +233,10 @@ class _RenderSparkWinLossChart extends RenderSparkChart { final double xInterval = dataPoints!.length > 1 ? dataPoints![1].x.toDouble() - dataPoints![0].x.toDouble() : dataPoints!.length.toDouble(); - final double columnSpace = 0.5; // Default space for column and winloss - final double space = columnSpace * 2; - final double winLossFactor = 0.5; - final double heightFactor = 40; + const double columnSpace = 0.5; // Default space for column and winloss + const double space = columnSpace * 2; + const double winLossFactor = 0.5; + const double heightFactor = 40; double visibleXPoint; double rectHeight; double bottom; @@ -298,7 +295,7 @@ class _RenderSparkWinLossChart extends RenderSparkChart { _segments[i].top + offset.dy, _segments[i].right + offset.dx, _segments[i].bottom + offset.dy); - if (dataPoints![i].y < axisCrossesAt) { + if (dataPoints![i].y < axisCrossesAt!) { canvas.drawRect(rect, negativePointPaint); } else if (dataPoints![i].y == axisCrossesAt) { canvas.drawRect(rect, tiePointPaint); diff --git a/packages/syncfusion_flutter_charts/lib/src/sparkline/series/spark_area_base.dart b/packages/syncfusion_flutter_charts/lib/src/sparkline/series/spark_area_base.dart index e75c32a78..59f477aec 100644 --- a/packages/syncfusion_flutter_charts/lib/src/sparkline/series/spark_area_base.dart +++ b/packages/syncfusion_flutter_charts/lib/src/sparkline/series/spark_area_base.dart @@ -654,7 +654,7 @@ class _SfSparkAreaChartState extends State { /// Method to return the spark area chart widget Widget _getSparkAreaChart() { return SparkChartContainer( - child: Stack(children: [ + child: Stack(children: [ SfSparkAreaChartRenderObjectWidget( data: widget._sparkChartDataDetails.data, dataCount: widget._sparkChartDataDetails.dataCount, diff --git a/packages/syncfusion_flutter_charts/lib/src/sparkline/trackball/spark_chart_trackball.dart b/packages/syncfusion_flutter_charts/lib/src/sparkline/trackball/spark_chart_trackball.dart index c6b17824d..3afe54dc2 100644 --- a/packages/syncfusion_flutter_charts/lib/src/sparkline/trackball/spark_chart_trackball.dart +++ b/packages/syncfusion_flutter_charts/lib/src/sparkline/trackball/spark_chart_trackball.dart @@ -13,6 +13,7 @@ import '../utils/enum.dart'; /// Provides option to customizes the [activationMode], [width], [color], /// [labelStyle], [backgroundColor], [borderColor], [borderWidth]. /// +@immutable class SparkChartTrackball { /// Creates an instance of spark chart trackball to enable the trackball /// on the closest data point from the touch position. @@ -30,7 +31,7 @@ class SparkChartTrackball { /// ); /// } /// ``` - SparkChartTrackball( + const SparkChartTrackball( {this.width = 2, this.color, this.dashArray, diff --git a/packages/syncfusion_flutter_charts/lib/src/sparkline/trackball/trackball_renderer.dart b/packages/syncfusion_flutter_charts/lib/src/sparkline/trackball/trackball_renderer.dart index c62513973..13e35d3fd 100644 --- a/packages/syncfusion_flutter_charts/lib/src/sparkline/trackball/trackball_renderer.dart +++ b/packages/syncfusion_flutter_charts/lib/src/sparkline/trackball/trackball_renderer.dart @@ -11,9 +11,10 @@ import '../utils/helper.dart'; import 'spark_chart_trackball.dart'; /// Represents the trackball renderer +@immutable class SparkChartTrackballRenderer extends StatefulWidget { /// Creates the trackball renderer - SparkChartTrackballRenderer( + const SparkChartTrackballRenderer( {Key? key, this.trackball, this.coordinatePoints, @@ -114,9 +115,9 @@ class _SparckChartTrackballRendererState onHover: (PointerHoverEvent event) => _enableMouseHover ? _enableAndUpdateTrackball(context, event.position) : null, - onExit: (event) => _hide(), + onExit: (PointerExitEvent event) => _hide(), child: Listener( - onPointerUp: (event) => _hide(), + onPointerUp: (PointerUpEvent event) => _hide(), child: GestureDetector( onVerticalDragStart: (widget.trackball != null && widget.trackball!.activationMode != SparkChartActivationMode.doubleTap) ? (DragStartDetails details) => @@ -300,9 +301,9 @@ class TrackballPainter extends CustomPainter { borderRadius = _getBorderRadius(borderRadius, textWidth); Rect labelRect = Rect.fromLTWH(screenPoint.dx, screenPoint.dy, textSize.width + 15, textSize.height + 10); - final double tooltipPadding = 5; - final double pointerWidth = 5; - final double pointerLength = 7; + const double tooltipPadding = 5; + const double pointerWidth = 5; + const double pointerLength = 7; final double totalWidth = areaBounds.right - areaBounds.left; final double totalHeight = areaBounds.bottom - areaBounds.top; final bool isTop = _rendererState._isTop!; @@ -316,8 +317,8 @@ class TrackballPainter extends CustomPainter { xPosition = screenPoint.dx + pointerLength + tooltipPadding; yPosition = screenPoint.dy - labelRect.height / 2; if ((xPosition + labelRect.width) > totalWidth) { - xPosition = ((xPosition - labelRect.width - (2 * tooltipPadding)) - - (2 * pointerLength)); + xPosition = (xPosition - labelRect.width - 2 * tooltipPadding) - + 2 * pointerLength; isRight = true; } else if (xPosition >= totalWidth) { xPosition = totalWidth - (xPosition + labelRect.width); @@ -330,7 +331,7 @@ class TrackballPainter extends CustomPainter { yPosition = totalHeight - labelRect.height; } } else { - final double padding = 2; + const double padding = 2; xPosition = screenPoint.dx - (labelRect.width / 2); final double tooltipRight = screenPoint.dx + (labelRect.width) / 2; @@ -428,9 +429,9 @@ class TrackballPainter extends CustomPainter { bool isTop, bool isBottom) { final Color backgroundColor = - (_rendererState._themeData!.brightness == Brightness.light + _rendererState._themeData!.brightness == Brightness.light ? const Color.fromRGBO(79, 79, 79, 1) - : const Color.fromRGBO(255, 255, 255, 1)); + : const Color.fromRGBO(255, 255, 255, 1); final Paint paint = Paint() ..color = _trackball!.backgroundColor ?? backgroundColor; final Path path = Path(); diff --git a/packages/syncfusion_flutter_charts/lib/src/sparkline/utils/helper.dart b/packages/syncfusion_flutter_charts/lib/src/sparkline/utils/helper.dart index 83ee20ebe..ba5ccc50a 100644 --- a/packages/syncfusion_flutter_charts/lib/src/sparkline/utils/helper.dart +++ b/packages/syncfusion_flutter_charts/lib/src/sparkline/utils/helper.dart @@ -48,7 +48,7 @@ void drawText(Canvas canvas, String dataLabel, Offset point, TextStyle style) { tp.layout(); canvas.save(); canvas.translate(point.dx, point.dy); - final Offset labelOffset = const Offset(0.0, 0.0); + const Offset labelOffset = Offset(0.0, 0.0); tp.paint(canvas, labelOffset); canvas.restore(); } @@ -91,11 +91,13 @@ Path? _dashPath( } const double intialValue = 0.0; final Path path = Path(); + double distance, length; + bool draw; for (final PathMetric measurePath in source.computeMetrics()) { - double distance = intialValue; - bool draw = true; + distance = intialValue; + draw = true; while (distance < measurePath.length) { - final double length = dashArray!.next; + length = dashArray!.next; if (draw) { path.addPath( measurePath.extractPath(distance, distance + length), Offset.zero); @@ -157,12 +159,13 @@ Path drawTriangle(Path path, double x, double y, double size) { /// Method to find the sorted spark chart points List sortSparkChartPoints(List dataPoints) { - final List sortedPoints = List.from(dataPoints); + final List sortedPoints = + List.from(dataPoints); sortedPoints.sort((SparkChartPoint firstPoint, SparkChartPoint secondPoint) { firstPoint.x.compareTo(secondPoint.x); - if (firstPoint.x < secondPoint.x) { + if (firstPoint.x < secondPoint.x == true) { return -1; - } else if (firstPoint.x > secondPoint.x) { + } else if (firstPoint.x > secondPoint.x == true) { return 1; } else { return 0; @@ -196,7 +199,7 @@ Offset transformToCoordinatePoint( double maxY, double diffX, double diffY, - size, + Size size, double x, double y, int dataLength) { @@ -211,26 +214,18 @@ Offset transformToCoordinatePoint( /// Calculates and return the spark chart layout size Size getLayoutSize(BoxConstraints constraints, BuildContext context) { - final double minHeight = 270; - final double minWidth = 480; + const double minHeight = 270; + const double minWidth = 480; double height = constraints.maxHeight; double width = constraints.maxWidth; final double deviceWidth = MediaQuery.of(context).size.width; final double deviceHeight = MediaQuery.of(context).size.height; if (height == double.infinity) { - if (width != double.infinity) { - height = (width / 16) * 9; - } else { - height = minHeight; - } + height = width != double.infinity ? (width / 16) * 9 : minHeight; } if (width == double.infinity) { - if (height != double.infinity) { - width = (height / 9) * 16; - } else { - width = minWidth; - } + width = height != double.infinity ? (height / 9) * 16 : minWidth; } if (width > deviceWidth) { @@ -266,16 +261,15 @@ class DashArrayIntervalList { /// Represents the spark chart point class SparkChartPoint { /// Creates the spark chart point - SparkChartPoint({this.x, this.y}); + SparkChartPoint({this.x, this.y = 0}); /// Specifies the x point - dynamic? x; + dynamic x; /// Specifies the y point - dynamic? y; + num y; /// Specifes the pixel location of data label - Offset? dataLabelOffset; /// Specifies the x label @@ -285,7 +279,7 @@ class SparkChartPoint { String? labelY; /// Specifies the actual x value - dynamic? actualX; + dynamic actualX; /// Specifies the color of that particular segment in bar series Color? color; @@ -345,6 +339,7 @@ class _SparKChartContainerBox extends RenderShiftedBox { } @override + // ignore: unnecessary_overrides void paint(PaintingContext context, Offset offset) { super.paint(context, offset); } @@ -416,7 +411,7 @@ void renderMarker( final SparkChartMarkerShape markerShape = marker.shape; final double markerSize = marker.size; final SparkChartMarkerDisplayMode markerDisplayMode = marker.displayMode; - final themeBasedColor = + final Color themeBasedColor = themeData.brightness == Brightness.light ? Colors.white : Colors.black; Path markerPath; final Offset lastMarkerOffset = Offset( @@ -636,7 +631,7 @@ TextStyle _getTextStyle( _getDataLabelSaturationColor(dataLabelOffset, coordinateOffset, theme, offset, seriesColor, type, segment, yValue); - final _textStyle = TextStyle( + final TextStyle _textStyle = TextStyle( color: fontColor, fontFamily: font.fontFamily, fontSize: font.fontSize, diff --git a/packages/syncfusion_flutter_charts/pubspec.yaml b/packages/syncfusion_flutter_charts/pubspec.yaml index 0b9938b76..1d43786fd 100644 --- a/packages/syncfusion_flutter_charts/pubspec.yaml +++ b/packages/syncfusion_flutter_charts/pubspec.yaml @@ -1,6 +1,6 @@ name: syncfusion_flutter_charts -description: Syncfusion Flutter Charts library includes data visualization widgets such as cartesian and circular charts, to create real-time, interactive, high-performance, animated charts. -version: 19.1.54 +description: A Flutter Charts library which includes data visualization widgets such as cartesian and circular charts, to create real-time, interactive, high-performance, animated charts. +version: 18.3.40 homepage: https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_flutter_charts environment: diff --git a/packages/syncfusion_flutter_core/README.md b/packages/syncfusion_flutter_core/README.md index 1928004ba..a2648827c 100644 --- a/packages/syncfusion_flutter_core/README.md +++ b/packages/syncfusion_flutter_core/README.md @@ -10,6 +10,7 @@ Syncfusion Flutter Core is a dependent package for the following Syncfusion Flut * [syncfusion_flutter_xlsio](https://pub.dev/packages/syncfusion_flutter_xlsio) * [syncfusion_flutter_datepicker](https://pub.dev/packages/syncfusion_flutter_datepicker) * [syncfusion_flutter_maps](https://pub.dev/packages/syncfusion_flutter_maps) +* [syncfusion_flutter_treemap](https://pub.dev/packages/syncfusion_flutter_treemap) * [syncfusion_flutter_gauges](https://pub.dev/packages/syncfusion_flutter_gauges) * [syncfusion_flutter_sliders](https://pub.dev/packages/syncfusion_flutter_sliders) * [syncfusion_flutter_signaturepad](https://pub.dev/packages/syncfusion_flutter_signaturepad) @@ -31,14 +32,19 @@ Syncfusion Flutter Core is a dependent package for the following Syncfusion Flut Explore the full capabilities of our Flutter widgets on your device by installing our sample browser applications from the below app stores, and view samples code in GitHub.

- - + + +

- - + + +

- +

+ +

+ ## Other useful links Take a look at the following to learn more about Syncfusion Flutter widgets: diff --git a/packages/syncfusion_flutter_core/example/lib/main.dart b/packages/syncfusion_flutter_core/example/lib/main.dart index c9626ad0d..8e7d27b1a 100644 --- a/packages/syncfusion_flutter_core/example/lib/main.dart +++ b/packages/syncfusion_flutter_core/example/lib/main.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -// import 'package:syncfusion_flutter_charts/charts.dart'; +import 'package:syncfusion_flutter_charts/charts.dart'; void main() { return runApp(ChartApp()); @@ -29,7 +29,7 @@ class _MyHomePageState extends State<_MyHomePage> { Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Syncfusion Flutter Chart')), - body: Container()); - // body: SfCartesianChart()) // Commented until the chart moves to null safety; + body: + SfCartesianChart()); // Commented until the chart moves to null safety; } } diff --git a/packages/syncfusion_flutter_core/example/linux/.gitignore b/packages/syncfusion_flutter_core/example/linux/.gitignore new file mode 100644 index 000000000..d3896c984 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/packages/syncfusion_flutter_core/example/linux/CMakeLists.txt b/packages/syncfusion_flutter_core/example/linux/CMakeLists.txt new file mode 100644 index 000000000..290c3e841 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/linux/CMakeLists.txt @@ -0,0 +1,106 @@ +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +set(BINARY_NAME "example") +set(APPLICATION_ID "com.example.example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Application build +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) +apply_standard_settings(${BINARY_NAME}) +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) +add_dependencies(${BINARY_NAME} flutter_assemble) +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/packages/syncfusion_flutter_core/example/linux/flutter/CMakeLists.txt b/packages/syncfusion_flutter_core/example/linux/flutter/CMakeLists.txt new file mode 100644 index 000000000..a1da1b9e5 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,91 @@ +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) +pkg_check_modules(BLKID REQUIRED IMPORTED_TARGET blkid) +pkg_check_modules(LZMA REQUIRED IMPORTED_TARGET liblzma) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO + PkgConfig::BLKID + PkgConfig::LZMA +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + linux-x64 ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/packages/syncfusion_flutter_core/example/linux/flutter/generated_plugin_registrant.cc b/packages/syncfusion_flutter_core/example/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000..d38195aa0 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,9 @@ +// +// Generated file. Do not edit. +// + +#include "generated_plugin_registrant.h" + + +void fl_register_plugins(FlPluginRegistry* registry) { +} diff --git a/packages/syncfusion_flutter_core/example/linux/flutter/generated_plugin_registrant.h b/packages/syncfusion_flutter_core/example/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000..9bf747894 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,13 @@ +// +// Generated file. Do not edit. +// + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/syncfusion_flutter_core/example/linux/flutter/generated_plugins.cmake b/packages/syncfusion_flutter_core/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 000000000..51436ae8c --- /dev/null +++ b/packages/syncfusion_flutter_core/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,15 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/packages/syncfusion_flutter_core/example/linux/main.cc b/packages/syncfusion_flutter_core/example/linux/main.cc new file mode 100644 index 000000000..e7c5c5437 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/packages/syncfusion_flutter_core/example/linux/my_application.cc b/packages/syncfusion_flutter_core/example/linux/my_application.cc new file mode 100644 index 000000000..543eaca72 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/linux/my_application.cc @@ -0,0 +1,104 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen *screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "example"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } + else { + gtk_window_set_title(window, "example"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar ***arguments, int *exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject *object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + nullptr)); +} diff --git a/packages/syncfusion_flutter_core/example/linux/my_application.h b/packages/syncfusion_flutter_core/example/linux/my_application.h new file mode 100644 index 000000000..72271d5e4 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/packages/syncfusion_flutter_core/example/macos/.gitignore b/packages/syncfusion_flutter_core/example/macos/.gitignore new file mode 100644 index 000000000..d2fd37723 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/macos/.gitignore @@ -0,0 +1,6 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/xcuserdata/ diff --git a/packages/syncfusion_flutter_core/example/macos/Flutter/Flutter-Debug.xcconfig b/packages/syncfusion_flutter_core/example/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 000000000..c2efd0b60 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/syncfusion_flutter_core/example/macos/Flutter/Flutter-Release.xcconfig b/packages/syncfusion_flutter_core/example/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 000000000..c2efd0b60 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/syncfusion_flutter_core/example/macos/Flutter/GeneratedPluginRegistrant.swift b/packages/syncfusion_flutter_core/example/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 000000000..cccf817a5 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,10 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { +} diff --git a/packages/syncfusion_flutter_core/example/macos/Runner.xcodeproj/project.pbxproj b/packages/syncfusion_flutter_core/example/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000..cc89c8782 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,572 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* example.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 0930; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + 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_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/packages/syncfusion_flutter_core/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/syncfusion_flutter_core/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/syncfusion_flutter_core/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/syncfusion_flutter_core/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000..ae8ff59d9 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/syncfusion_flutter_core/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/syncfusion_flutter_core/example/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..1d526a16e --- /dev/null +++ b/packages/syncfusion_flutter_core/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/syncfusion_flutter_core/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/syncfusion_flutter_core/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/syncfusion_flutter_core/example/macos/Runner/AppDelegate.swift b/packages/syncfusion_flutter_core/example/macos/Runner/AppDelegate.swift new file mode 100644 index 000000000..d53ef6437 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/packages/syncfusion_flutter_core/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/syncfusion_flutter_core/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..a2ec33f19 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/syncfusion_flutter_core/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/packages/syncfusion_flutter_core/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..3c4935a7ca84f0976aca34b7f2895d65fb94d1ea GIT binary patch literal 46993 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 literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_core/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/packages/syncfusion_flutter_core/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..ed4cc16421680a50164ba74381b4b35ceaa0ccfc GIT binary patch literal 3276 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 N;OXk;vd$@?2>?>Ex^Vyi literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_core/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/packages/syncfusion_flutter_core/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 0000000000000000000000000000000000000000..bcbf36df2f2aaaa0a63c7dabc94e600184229d0d GIT binary patch literal 5933 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 f$KZHO*s&vt|9^w-6P?|#0pRK8NSwWJ?9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!ItFh?!xdN1Q+aGJ{c&& zS>O>_%)r1c48n{Iv*t(u1=&kHeO=ifbFy+6aSK)V_AxLppYn8Z42d|rc6w}vOsL55 z`t&mC&y2@JTEyg!eDiFX^k#CC!jq%>erB=yHqUP0XcDOTw6ko}L zX;EmMrq(fKk*eygEuA616;0)>@A{TK|55PV@70 z$OfzS*(VJxQev3J?yY?O=ul(v`fp}?u9z`JK3ugibK>)DyCwImZOF4d{xK%%Ks1*} zv$oa)9anR%lXIBUqYnhLmT>VOzHfNP?ZwJNZ!5$s9M08RynIvaXw>@G^T9@r9^KH1 zVy??F&uuk)bH9Y4pQY!hP58i_H6 znl-NcuCpLV6ZWU;4C zu@9exF&OZi`Bovq_m%T+WhU2kvkz@^_LpycBvqm3bMpLw8X-Or5sL>0AKE1$(k_L=_Zc=CUq#=x1-QZf)G7nHu@fmsQ1eN_N3+nTEz`4HI4Z6uVlE zJH+X&det8JU?tO?upcM4Z=cV!JV;yF>FfL5Q$M|W_2Z!P`S=}Wzp|_1^#d%e?_H`> zV@%vA$+bFVqhw9`U;TfP|5|PD{||OiYdor8P*i??|NJcb%kzT_73*7WE?Ua5hAnR2 z=7WE=PhTlJ#ZeRznjTUb;`E(wkMZrj4e|Hilz-mK>9cZHQY**5TUPw~u}k;u73KI}xAx!0m-)GVia|x^d3p~s_9gh83jA&Ra<8rM%`>U3x69t&NzbwWY}7Ar?)FK#IZ0z|d0H0EkRO w3{9;}4Xg|ebq&m|3=9_N6z8I7$jwj5OsmAL;bP(Gi$Dzwp00i_>zopr02+f8CIA2c literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_core/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/packages/syncfusion_flutter_core/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 0000000000000000000000000000000000000000..e71a726136a47ed24125c7efc79d68a4a01961b4 GIT binary patch literal 14800 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/syncfusion_flutter_core/example/macos/Runner/Configs/AppInfo.xcconfig b/packages/syncfusion_flutter_core/example/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 000000000..cf9be60ca --- /dev/null +++ b/packages/syncfusion_flutter_core/example/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = example + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.example + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2021 com.example. All rights reserved. diff --git a/packages/syncfusion_flutter_core/example/macos/Runner/Configs/Debug.xcconfig b/packages/syncfusion_flutter_core/example/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 000000000..36b0fd946 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/packages/syncfusion_flutter_core/example/macos/Runner/Configs/Release.xcconfig b/packages/syncfusion_flutter_core/example/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 000000000..dff4f4956 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/packages/syncfusion_flutter_core/example/macos/Runner/Configs/Warnings.xcconfig b/packages/syncfusion_flutter_core/example/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 000000000..42bcbf478 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/packages/syncfusion_flutter_core/example/macos/Runner/DebugProfile.entitlements b/packages/syncfusion_flutter_core/example/macos/Runner/DebugProfile.entitlements new file mode 100644 index 000000000..dddb8a30c --- /dev/null +++ b/packages/syncfusion_flutter_core/example/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/packages/syncfusion_flutter_core/example/macos/Runner/Info.plist b/packages/syncfusion_flutter_core/example/macos/Runner/Info.plist new file mode 100644 index 000000000..4789daa6a --- /dev/null +++ b/packages/syncfusion_flutter_core/example/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/packages/syncfusion_flutter_core/example/macos/Runner/MainFlutterWindow.swift b/packages/syncfusion_flutter_core/example/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 000000000..2722837ec --- /dev/null +++ b/packages/syncfusion_flutter_core/example/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/packages/syncfusion_flutter_core/example/macos/Runner/Release.entitlements b/packages/syncfusion_flutter_core/example/macos/Runner/Release.entitlements new file mode 100644 index 000000000..852fa1a47 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/packages/syncfusion_flutter_core/example/pubspec.yaml b/packages/syncfusion_flutter_core/example/pubspec.yaml index 120187e69..5f4dcb4b4 100644 --- a/packages/syncfusion_flutter_core/example/pubspec.yaml +++ b/packages/syncfusion_flutter_core/example/pubspec.yaml @@ -8,15 +8,10 @@ environment: dependencies: flutter: sdk: flutter - syncfusion_flutter_charts: - path: ../../syncfusion_flutter_charts + syncfusion_flutter_charts: ^19.1.57 cupertino_icons: ^0.1.3 -dev_dependencies: - flutter_test: - sdk: flutter - flutter: uses-material-design: true diff --git a/packages/syncfusion_flutter_core/example/web/favicon.png b/packages/syncfusion_flutter_core/example/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_core/example/web/icons/Icon-192.png b/packages/syncfusion_flutter_core/example/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_core/example/web/icons/Icon-512.png b/packages/syncfusion_flutter_core/example/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_core/example/web/index.html b/packages/syncfusion_flutter_core/example/web/index.html new file mode 100644 index 000000000..1460b5e9b --- /dev/null +++ b/packages/syncfusion_flutter_core/example/web/index.html @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + example + + + + + + + + diff --git a/packages/syncfusion_flutter_core/example/web/manifest.json b/packages/syncfusion_flutter_core/example/web/manifest.json new file mode 100644 index 000000000..8c012917d --- /dev/null +++ b/packages/syncfusion_flutter_core/example/web/manifest.json @@ -0,0 +1,23 @@ +{ + "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" + } + ] +} diff --git a/packages/syncfusion_flutter_core/example/windows/.gitignore b/packages/syncfusion_flutter_core/example/windows/.gitignore new file mode 100644 index 000000000..d492d0d98 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/packages/syncfusion_flutter_core/example/windows/CMakeLists.txt b/packages/syncfusion_flutter_core/example/windows/CMakeLists.txt new file mode 100644 index 000000000..abf90408e --- /dev/null +++ b/packages/syncfusion_flutter_core/example/windows/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required(VERSION 3.15) +project(example LANGUAGES CXX) + +set(BINARY_NAME "example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() + +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build +add_subdirectory("runner") + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/packages/syncfusion_flutter_core/example/windows/flutter/CMakeLists.txt b/packages/syncfusion_flutter_core/example/windows/flutter/CMakeLists.txt new file mode 100644 index 000000000..b02c5485c --- /dev/null +++ b/packages/syncfusion_flutter_core/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.15) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/packages/syncfusion_flutter_core/example/windows/flutter/generated_plugin_registrant.cc b/packages/syncfusion_flutter_core/example/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000..4bfa0f3a3 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,9 @@ +// +// Generated file. Do not edit. +// + +#include "generated_plugin_registrant.h" + + +void RegisterPlugins(flutter::PluginRegistry* registry) { +} diff --git a/packages/syncfusion_flutter_core/example/windows/flutter/generated_plugin_registrant.h b/packages/syncfusion_flutter_core/example/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000..9846246b4 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,13 @@ +// +// Generated file. Do not edit. +// + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/syncfusion_flutter_core/example/windows/flutter/generated_plugins.cmake b/packages/syncfusion_flutter_core/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 000000000..4d10c2518 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,15 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/packages/syncfusion_flutter_core/example/windows/runner/CMakeLists.txt b/packages/syncfusion_flutter_core/example/windows/runner/CMakeLists.txt new file mode 100644 index 000000000..977e38b5d --- /dev/null +++ b/packages/syncfusion_flutter_core/example/windows/runner/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.15) +project(runner LANGUAGES CXX) + +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "run_loop.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) +apply_standard_settings(${BINARY_NAME}) +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/packages/syncfusion_flutter_core/example/windows/runner/Runner.rc b/packages/syncfusion_flutter_core/example/windows/runner/Runner.rc new file mode 100644 index 000000000..51812dcd4 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "A new Flutter project." "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2021 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "example.exe" "\0" + VALUE "ProductName", "example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/packages/syncfusion_flutter_core/example/windows/runner/flutter_window.cpp b/packages/syncfusion_flutter_core/example/windows/runner/flutter_window.cpp new file mode 100644 index 000000000..c42272304 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/windows/runner/flutter_window.cpp @@ -0,0 +1,64 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(RunLoop* run_loop, + const flutter::DartProject& project) + : run_loop_(run_loop), project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + run_loop_->RegisterFlutterInstance(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + run_loop_->UnregisterFlutterInstance(flutter_controller_->engine()); + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opporutunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/packages/syncfusion_flutter_core/example/windows/runner/flutter_window.h b/packages/syncfusion_flutter_core/example/windows/runner/flutter_window.h new file mode 100644 index 000000000..b663ddd50 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/windows/runner/flutter_window.h @@ -0,0 +1,39 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "run_loop.h" +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow driven by the |run_loop|, hosting a + // Flutter view running |project|. + explicit FlutterWindow(RunLoop* run_loop, + const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The run loop driving events for this window. + RunLoop* run_loop_; + + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/packages/syncfusion_flutter_core/example/windows/runner/main.cpp b/packages/syncfusion_flutter_core/example/windows/runner/main.cpp new file mode 100644 index 000000000..b637809bd --- /dev/null +++ b/packages/syncfusion_flutter_core/example/windows/runner/main.cpp @@ -0,0 +1,42 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "run_loop.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + RunLoop run_loop; + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(&run_loop, project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + run_loop.Run(); + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/packages/syncfusion_flutter_core/example/windows/runner/resource.h b/packages/syncfusion_flutter_core/example/windows/runner/resource.h new file mode 100644 index 000000000..66a65d1e4 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/packages/syncfusion_flutter_core/example/windows/runner/resources/app_icon.ico b/packages/syncfusion_flutter_core/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_core/example/windows/runner/run_loop.cpp b/packages/syncfusion_flutter_core/example/windows/runner/run_loop.cpp new file mode 100644 index 000000000..2d6636ab6 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/windows/runner/run_loop.cpp @@ -0,0 +1,66 @@ +#include "run_loop.h" + +#include + +#include + +RunLoop::RunLoop() {} + +RunLoop::~RunLoop() {} + +void RunLoop::Run() { + bool keep_running = true; + TimePoint next_flutter_event_time = TimePoint::clock::now(); + while (keep_running) { + std::chrono::nanoseconds wait_duration = + std::max(std::chrono::nanoseconds(0), + next_flutter_event_time - TimePoint::clock::now()); + ::MsgWaitForMultipleObjects( + 0, nullptr, FALSE, static_cast(wait_duration.count() / 1000), + QS_ALLINPUT); + bool processed_events = false; + MSG message; + // All pending Windows messages must be processed; MsgWaitForMultipleObjects + // won't return again for items left in the queue after PeekMessage. + while (::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) { + processed_events = true; + if (message.message == WM_QUIT) { + keep_running = false; + break; + } + ::TranslateMessage(&message); + ::DispatchMessage(&message); + // Allow Flutter to process messages each time a Windows message is + // processed, to prevent starvation. + next_flutter_event_time = + std::min(next_flutter_event_time, ProcessFlutterMessages()); + } + // If the PeekMessage loop didn't run, process Flutter messages. + if (!processed_events) { + next_flutter_event_time = + std::min(next_flutter_event_time, ProcessFlutterMessages()); + } + } +} + +void RunLoop::RegisterFlutterInstance( + flutter::FlutterEngine* flutter_instance) { + flutter_instances_.insert(flutter_instance); +} + +void RunLoop::UnregisterFlutterInstance( + flutter::FlutterEngine* flutter_instance) { + flutter_instances_.erase(flutter_instance); +} + +RunLoop::TimePoint RunLoop::ProcessFlutterMessages() { + TimePoint next_event_time = TimePoint::max(); + for (auto instance : flutter_instances_) { + std::chrono::nanoseconds wait_duration = instance->ProcessMessages(); + if (wait_duration != std::chrono::nanoseconds::max()) { + next_event_time = + std::min(next_event_time, TimePoint::clock::now() + wait_duration); + } + } + return next_event_time; +} diff --git a/packages/syncfusion_flutter_core/example/windows/runner/run_loop.h b/packages/syncfusion_flutter_core/example/windows/runner/run_loop.h new file mode 100644 index 000000000..000d36246 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/windows/runner/run_loop.h @@ -0,0 +1,40 @@ +#ifndef RUNNER_RUN_LOOP_H_ +#define RUNNER_RUN_LOOP_H_ + +#include + +#include +#include + +// A runloop that will service events for Flutter instances as well +// as native messages. +class RunLoop { + public: + RunLoop(); + ~RunLoop(); + + // Prevent copying + RunLoop(RunLoop const&) = delete; + RunLoop& operator=(RunLoop const&) = delete; + + // Runs the run loop until the application quits. + void Run(); + + // Registers the given Flutter instance for event servicing. + void RegisterFlutterInstance( + flutter::FlutterEngine* flutter_instance); + + // Unregisters the given Flutter instance from event servicing. + void UnregisterFlutterInstance( + flutter::FlutterEngine* flutter_instance); + + private: + using TimePoint = std::chrono::steady_clock::time_point; + + // Processes all currently pending messages for registered Flutter instances. + TimePoint ProcessFlutterMessages(); + + std::set flutter_instances_; +}; + +#endif // RUNNER_RUN_LOOP_H_ diff --git a/packages/syncfusion_flutter_core/example/windows/runner/runner.exe.manifest b/packages/syncfusion_flutter_core/example/windows/runner/runner.exe.manifest new file mode 100644 index 000000000..c977c4a42 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/packages/syncfusion_flutter_core/example/windows/runner/utils.cpp b/packages/syncfusion_flutter_core/example/windows/runner/utils.cpp new file mode 100644 index 000000000..d19bdbbcc --- /dev/null +++ b/packages/syncfusion_flutter_core/example/windows/runner/utils.cpp @@ -0,0 +1,64 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/packages/syncfusion_flutter_core/example/windows/runner/utils.h b/packages/syncfusion_flutter_core/example/windows/runner/utils.h new file mode 100644 index 000000000..3879d5475 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/packages/syncfusion_flutter_core/example/windows/runner/win32_window.cpp b/packages/syncfusion_flutter_core/example/windows/runner/win32_window.cpp new file mode 100644 index 000000000..c10f08dc7 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/windows/runner/win32_window.cpp @@ -0,0 +1,245 @@ +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/packages/syncfusion_flutter_core/example/windows/runner/win32_window.h b/packages/syncfusion_flutter_core/example/windows/runner/win32_window.h new file mode 100644 index 000000000..17ba43112 --- /dev/null +++ b/packages/syncfusion_flutter_core/example/windows/runner/win32_window.h @@ -0,0 +1,98 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/syncfusion_flutter_core/lib/analysis_options.yaml b/packages/syncfusion_flutter_core/lib/analysis_options.yaml index f9af2f605..214c38912 100644 --- a/packages/syncfusion_flutter_core/lib/analysis_options.yaml +++ b/packages/syncfusion_flutter_core/lib/analysis_options.yaml @@ -13,74 +13,234 @@ include: package:pedantic/analysis_options.yaml # See the configuration guide for more # https://github.com/dart-lang/sdk/tree/master/pkg/analyzer#configuring-the-analyzer # -# There are four similar analysis options files in the flutter repos: -# - analysis_options.yaml -# - packages/flutter/lib/analysis_options_user.yaml (this file) +# There are other similar analysis options files in the flutter repos, +# which should be kept in sync with this file: +# +# - analysis_options.yaml (this file) +# - packages/flutter/lib/analysis_options_user.yaml # - https://github.com/flutter/plugins/blob/master/analysis_options.yaml # - https://github.com/flutter/engine/blob/master/analysis_options.yaml # -# This file contains the analysis options used by "flutter analyze" and the -# dartanalyzer when analyzing code outside the flutter repository. It isn't named -# 'analysis_options.yaml' because otherwise editors would use it when analyzing -# the flutter tool itself. -# -# When editing, make sure you keep this and /analysis_options.yaml consistent. +# This file contains the analysis options used by Flutter tools, such as IntelliJ, +# Android Studio, and the `flutter analyze` command. analyzer: + strong-mode: + implicit-casts: false + implicit-dynamic: false errors: # treat missing required parameters as a warning (not a hint) missing_required_param: warning + # treat missing returns as a warning (not a hint) + missing_return: warning + # allow having TODOs in the code + todo: ignore + # allow self-reference to deprecated members (we do this because otherwise we have + # to annotate every member in every test, assert, etc, when we deprecate something) + deprecated_member_use_from_same_package: ignore + # Ignore analyzer hints for updating pubspecs when using Future or + # Stream and not importing dart:async + # Please see https://github.com/flutter/flutter/pull/24528 for details. + sdk_version_async_exported_from_core: ignore + + #The below rules are ignored manually omit_local_variable_types: ignore + argument_type_not_assignable: ignore + overridden_fields: ignore include_file_not_found: ignore invalid_dependency: ignore + exclude: + - "bin/cache/**" + # the following two are relative to the stocks example and the flutter package respectively + # see https://github.com/dart-lang/sdk/issues/28463 + - "lib/i18n/messages_*.dart" + - "lib/src/http/**" + - "test_fixes/**" + linter: rules: # these rules are documented on and in the same order as # the Dart Lint rules page to make maintenance easier # https://github.com/dart-lang/linter/blob/master/example/all.yaml - # - always_declare_return_types - # - always_specify_types - # - annotate_overrides - # - avoid_as - - avoid_web_libraries_in_flutter + - always_declare_return_types + - always_put_control_body_on_new_line + # - always_put_required_named_parameters_first # we prefer having parameters in the same order as fields https://github.com/flutter/flutter/issues/10219 + - always_require_non_null_named_parameters + - always_specify_types + # - always_use_package_imports # we do this commonly + - annotate_overrides + # - avoid_annotating_with_dynamic # conflicts with always_specify_types + # - avoid_as # required for implicit-casts: true + - avoid_bool_literals_in_conditional_expressions + # - avoid_catches_without_on_clauses # we do this commonly + # - avoid_catching_errors # we do this commonly + - avoid_classes_with_only_static_members + # - avoid_double_and_int_checks # only useful when targeting JS runtime + - avoid_empty_else + - avoid_equals_and_hash_code_on_mutable_classes + # - avoid_escaping_inner_quotes # not yet tested + - avoid_field_initializers_in_const_classes + - avoid_function_literals_in_foreach_calls + # - avoid_implementing_value_types # not yet tested + - avoid_init_to_null + # - avoid_js_rounded_ints # only useful when targeting JS runtime + - avoid_null_checks_in_equality_operators + # - avoid_positional_boolean_parameters # not yet tested + # - avoid_print # not yet tested + # - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356) + # - avoid_redundant_argument_values # not yet tested + - avoid_relative_lib_imports + - avoid_renaming_method_parameters + - avoid_return_types_on_setters + # - avoid_returning_null # there are plenty of valid reasons to return null + # - avoid_returning_null_for_future # not yet tested + - avoid_returning_null_for_void + # - avoid_returning_this # there are plenty of valid reasons to return this + # - avoid_setters_without_getters # not yet tested + - avoid_shadowing_type_parameters + - avoid_single_cascade_in_expression_statements + - avoid_slow_async_io + # - avoid_type_to_string # we do this commonly + - avoid_types_as_parameter_names + # - avoid_types_on_closure_parameters # conflicts with always_specify_types + # - avoid_unnecessary_containers # not yet tested + - avoid_unused_constructor_parameters + - avoid_void_async + # - avoid_web_libraries_in_flutter # not yet tested - await_only_futures + - camel_case_extensions - camel_case_types - cancel_subscriptions - - close_sinks - # - comment_references # we do not presume as to what people want to reference in their dartdocs - # - constant_identifier_names # https://github.com/dart-lang/linter/issues/204 + # - cascade_invocations # not yet tested + - cast_nullable_to_non_nullable + # - close_sinks # not reliable enough + # - comment_references # blocked on https://github.com/flutter/flutter/issues/20765 + # - constant_identifier_names # needs an opt-out https://github.com/dart-lang/linter/issues/204 - control_flow_in_finally + # - curly_braces_in_flow_control_structures # not required by flutter style + # - diagnostic_describe_all_properties # not yet tested + # - directives_ordering + # - do_not_use_environment # we do this commonly + - empty_catches + - empty_constructor_bodies - empty_statements + - exhaustive_cases + # - file_names # not yet tested + - flutter_style_todos - hash_and_equals - implementation_imports - # - invariant_booleans - # - iterable_contains_unrelated_type - # - library_prefixes - # - list_remove_unrelated_type - # - literal_only_boolean_expressions + # - invariant_booleans # too many false positives: https://github.com/dart-lang/linter/issues/811 + - iterable_contains_unrelated_type + # - join_return_with_assignment # not required by flutter style + - leading_newlines_in_multiline_strings + - library_names + - library_prefixes + # - lines_longer_than_80_chars # not required by flutter style + - list_remove_unrelated_type + # - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181 + # - missing_whitespace_between_adjacent_strings # not yet tested + - no_adjacent_strings_in_list + # - no_default_cases # too many false positives + - no_duplicate_case_values + - no_logic_in_create_state + # - no_runtimeType_toString # ok in tests; we enable this only in packages/ - non_constant_identifier_names - # - one_member_abstracts - # - only_throw_errors - # - overridden_fields + - null_check_on_nullable_type_parameter + # - null_closures # not required by flutter style + # - omit_local_variable_types # opposite of always_specify_types + # - one_member_abstracts # too many false positives + # - only_throw_errors # https://github.com/flutter/flutter/issues/5792 + - overridden_fields - package_api_docs - - package_names + # - package_names # non conforming packages in sdk - package_prefixed_library_names + # - parameter_assignments # we do this commonly + - prefer_adjacent_string_concatenation + - prefer_asserts_in_initializer_lists + # - prefer_asserts_with_message # not required by flutter style + - prefer_collection_literals + - prefer_conditional_assignment + - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_declarations + - prefer_const_literals_to_create_immutables + # - prefer_constructors_over_static_methods # far too many false positives + - prefer_contains + # - prefer_double_quotes # opposite of prefer_single_quotes + - prefer_equal_for_default_values + # - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods + - prefer_final_fields + # - prefer_final_in_for_each + # - prefer_final_locals + - prefer_for_elements_to_map_fromIterable + - prefer_foreach + # - prefer_function_declarations_over_variables # not yet tested + - prefer_generic_function_type_aliases + - prefer_if_elements_to_conditional_expressions + - prefer_if_null_operators + - prefer_initializing_formals + - prefer_inlined_adds + # - prefer_int_literals # not yet tested + # - prefer_interpolation_to_compose_strings # not yet tested + - prefer_is_empty + - prefer_is_not_empty + - prefer_is_not_operator + - prefer_iterable_whereType # - prefer_mixin # https://github.com/dart-lang/language/issues/32 - # - public_member_api_docs + # - prefer_null_aware_operators # disable until NNBD, see https://github.com/flutter/flutter/pull/32711#issuecomment-492930932 + # - prefer_relative_imports # not yet tested + - prefer_single_quotes + - prefer_spread_collections + - prefer_typing_uninitialized_variables + - prefer_void_to_null + # - provide_deprecation_message # not yet tested + # - public_member_api_docs # enabled on a case-by-case basis; see e.g. packages/analysis_options.yaml + - recursive_getters + # - sized_box_for_whitespace # not yet tested + - slash_for_doc_comments + # - sort_child_properties_last # not yet tested # - sort_constructors_first - # - sort_unnamed_constructors_first - # - super_goes_last # no longer needed w/ Dart 2 + # - sort_pub_dependencies # prevents separating pinned transitive dependencies + - sort_unnamed_constructors_first - test_types_in_equals - throw_in_finally + - tighten_type_of_initializing_formals # - type_annotate_public_apis # subset of always_specify_types - # - unawaited_futures + - type_init_formals + # - unawaited_futures # too many false positives + # - unnecessary_await_in_return # not yet tested - unnecessary_brace_in_string_interps + - unnecessary_const + # - unnecessary_final # conflicts with prefer_final_locals - unnecessary_getters_setters + # - unnecessary_lambdas # has false positives: https://github.com/dart-lang/linter/issues/498 + - unnecessary_new + - unnecessary_null_aware_assignments + # - unnecessary_null_checks # not yet tested + - unnecessary_null_in_if_null_operators + - unnecessary_nullable_for_final_variable_declarations + - unnecessary_overrides + - unnecessary_parenthesis + # - unnecessary_raw_strings # not yet tested - unnecessary_statements - - avoid_init_to_null + - unnecessary_string_escapes + - unnecessary_string_interpolations + - unnecessary_this - unrelated_type_equality_checks - - empty_constructor_bodies + # - unsafe_html # not yet tested + - use_full_hex_values_for_flutter_colors + # - use_function_type_syntax_for_parameters # not yet tested + - use_is_even_rather_than_modulo + # - use_key_in_widget_constructors # not yet tested + # - use_late_for_private_fields_and_variables # not yet tested + - use_raw_strings + - use_rethrow_when_possible + # - use_setters_to_change_properties # not yet tested + # - use_string_buffers # has false positives: https://github.com/dart-lang/sdk/issues/34182 + # - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review + - valid_regexps + - void_checks # The below linters are added manually - directives_ordering @@ -89,5 +249,5 @@ linter: - public_member_api_docs - prefer_final_in_for_each - sort_constructors_first - - avoid_as + # - avoid_as - deprecated from flutter 2.2.0 stable version - prefer_final_locals diff --git a/packages/syncfusion_flutter_core/lib/core.dart b/packages/syncfusion_flutter_core/lib/core.dart index c0e8cb9d5..7bb6198d7 100644 --- a/packages/syncfusion_flutter_core/lib/core.dart +++ b/packages/syncfusion_flutter_core/lib/core.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:vector_math/vector_math.dart' as vector; export './src/slider_controller.dart'; +export './src/utils/shape_helper.dart'; part './src/license.dart'; part './src/calendar/calendar_helper.dart'; diff --git a/packages/syncfusion_flutter_core/lib/legend_internal.dart b/packages/syncfusion_flutter_core/lib/legend_internal.dart new file mode 100644 index 000000000..aeb1292a3 --- /dev/null +++ b/packages/syncfusion_flutter_core/lib/legend_internal.dart @@ -0,0 +1,3 @@ +library legend_internal; + +export 'src/legend/legend.dart'; diff --git a/packages/syncfusion_flutter_core/lib/src/calendar/calendar_helper.dart b/packages/syncfusion_flutter_core/lib/src/calendar/calendar_helper.dart index f70f4ce5e..5412f10e0 100644 --- a/packages/syncfusion_flutter_core/lib/src/calendar/calendar_helper.dart +++ b/packages/syncfusion_flutter_core/lib/src/calendar/calendar_helper.dart @@ -73,8 +73,8 @@ dynamic getNextMonthDate(dynamic date) { /// else return first date/last date when the date before of first date or after /// last date dynamic getValidDate(dynamic minDate, dynamic maxDate, dynamic date) { - if (date.isAfter(minDate)) { - if (date.isBefore(maxDate)) { + if (date.isAfter(minDate) == true) { + if (date.isBefore(maxDate) == true) { return date; } else { return maxDate; @@ -112,7 +112,7 @@ bool isDateWithInDateRange(dynamic startDate, dynamic endDate, dynamic date) { return false; } - if (startDate.isAfter(endDate)) { + if (startDate.isAfter(endDate) == true) { final dynamic temp = startDate; startDate = endDate; endDate = temp; @@ -127,17 +127,19 @@ bool isDateWithInDateRange(dynamic startDate, dynamic endDate, dynamic date) { /// Check the date before/same of last date bool isSameOrBeforeDate(dynamic lastDate, dynamic date) { - return isSameDate(lastDate, date) || lastDate.isAfter(date); + return isSameDate(lastDate, date) || lastDate.isAfter(date) == true; } /// Check the date after/same of first date bool isSameOrAfterDate(dynamic firstDate, dynamic date) { - return isSameDate(firstDate, date) || firstDate.isBefore(date); + return isSameDate(firstDate, date) || firstDate.isBefore(date) == true; } /// Get the visible dates based on the date value and visible dates count. +// ignore: always_specify_types List getVisibleDates(dynamic date, List? nonWorkingDays, int firstDayOfWeek, int visibleDatesCount) { + // ignore: always_specify_types List datesCollection; if (date is HijriDateTime) { datesCollection = []; @@ -188,7 +190,8 @@ dynamic getFirstDayOfWeekDate( } } - int value = -currentDate.weekday + firstDayOfWeek - numberOfWeekDays; + // ignore: avoid_as + int value = -(currentDate.weekday as int) + firstDayOfWeek - numberOfWeekDays; if (value.abs() >= numberOfWeekDays) { value += numberOfWeekDays; } diff --git a/packages/syncfusion_flutter_core/lib/src/calendar/custom_looping_widget.dart b/packages/syncfusion_flutter_core/lib/src/calendar/custom_looping_widget.dart index d3ae3bf09..c7fa501be 100644 --- a/packages/syncfusion_flutter_core/lib/src/calendar/custom_looping_widget.dart +++ b/packages/syncfusion_flutter_core/lib/src/calendar/custom_looping_widget.dart @@ -142,7 +142,8 @@ class _CustomScrollViewLayout extends RenderWrap { height = height / 3; } - // sets the position as zero to restrict the view update when the view refreshed without swiping the view + // sets the position as zero to restrict the view update when the view + // refreshed without swiping the view if (_position == width || _position == -width) { if (_currentChild.parentData.offset.dx == width) { _position = 0; @@ -153,9 +154,12 @@ class _CustomScrollViewLayout extends RenderWrap { } } - firstChildParentData = _firstChild.parentData; - lastChildParentData = _lastChild.parentData; - currentChildParentData = _currentChild.parentData; + // ignore: avoid_as + firstChildParentData = _firstChild.parentData as WrapParentData; + // ignore: avoid_as + lastChildParentData = _lastChild.parentData as WrapParentData; + // ignore: avoid_as + currentChildParentData = _currentChild.parentData as WrapParentData; if (_navigationDirection == CustomScrollDirection.horizontal) { currentChildXPos = width; lastChildXPos = width * 2; diff --git a/packages/syncfusion_flutter_core/lib/src/calendar/hijri_date_time.dart b/packages/syncfusion_flutter_core/lib/src/calendar/hijri_date_time.dart index 1d7736f02..ace7cdcc5 100644 --- a/packages/syncfusion_flutter_core/lib/src/calendar/hijri_date_time.dart +++ b/packages/syncfusion_flutter_core/lib/src/calendar/hijri_date_time.dart @@ -1753,6 +1753,7 @@ const List _kDateCollection = [ /// final HijriDateTime date = HijriDateTime(1442, 02, 10); /// /// ``` +@immutable class HijriDateTime { /// Creates a instance for HijriDateTime instance with given data. HijriDateTime(this.year, this.month, this.day) @@ -1823,16 +1824,16 @@ class HijriDateTime { /// Returns true if [this] occurs at the same moment as [other]. /// - /// The comparison is independent of whether the time is in UTC or in the local - /// time zone. + /// The comparison is independent of whether the time is in UTC or in the + /// local time zone. bool isBefore(HijriDateTime other) { return _date.millisecondsSinceEpoch < other._date.millisecondsSinceEpoch; } /// Returns true if [this] occurs at the same moment as [other]. /// - /// The comparison is independent of whether the time is in UTC or in the local - /// time zone. + /// The comparison is independent of whether the time is in UTC or in the + /// local time zone. bool isAtSameMomentAs(HijriDateTime other) { return _date.millisecondsSinceEpoch == other._date.millisecondsSinceEpoch; } @@ -1861,7 +1862,9 @@ class HijriDateTime { } String _twoDigits(int n) { - if (n >= 10) return '$n'; + if (n >= 10) { + return '$n'; + } return '0$n'; } @@ -1873,7 +1876,8 @@ class HijriDateTime { /// returns the previous date from the given value. HijriDateTime _getPreviousDate(HijriDateTime date, int day) { if (day <= 0) { - date = getPreviousMonthDate(date); + // ignore: avoid_as + date = getPreviousMonthDate(date) as HijriDateTime; final int? monthLength = date.getNumberOfDatesInMonth(); /// Return same date when dates count in month not specified. @@ -1892,7 +1896,8 @@ class HijriDateTime { HijriDateTime _getNextDate(int? monthLength, HijriDateTime date, int day) { if (monthLength != null && day > monthLength) { day -= monthLength; - date = getNextMonthDate(date); + // ignore: avoid_as + date = getNextMonthDate(date) as HijriDateTime; monthLength = date.getNumberOfDatesInMonth(); /// Return same date when dates count in month not specified. @@ -1961,7 +1966,8 @@ class HijriDateTime { return false; } - final HijriDateTime otherStyle = other; + // ignore: avoid_as, test_types_in_equals + final HijriDateTime otherStyle = other as HijriDateTime; return otherStyle.month == month && otherStyle.year == year && otherStyle.day == day; @@ -2007,7 +2013,7 @@ HijriDateTime convertToHijriDate(DateTime date) { year = c - 4716; final int modifiedJulianDate = julianNumber - 2400000; // date calculation for year after 2077 - final double iYear = 10631.0 / 30.0; + const double iYear = 10631.0 / 30.0; int z = julianNumber - 1948084; final int cyc = (z / 10631.0).floor(); z = z - 10631 * cyc; diff --git a/packages/syncfusion_flutter_core/lib/src/legend/legend.dart b/packages/syncfusion_flutter_core/lib/src/legend/legend.dart new file mode 100644 index 000000000..6a115e3c2 --- /dev/null +++ b/packages/syncfusion_flutter_core/lib/src/legend/legend.dart @@ -0,0 +1,2278 @@ +import 'dart:async'; +import 'dart:ui' as ui; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/scheduler.dart'; + +import '../utils/shape_helper.dart'; + +/// Callback which returns toggled indices and teh current toggled index. +typedef ToggledIndicesChangedCallback = void Function( + List indices, int currentIndex); + +/// Called with the details of single legend item. +typedef ItemRenderCallback = void Function(ItemRendererDetails); + +/// Signature to return a [Widget] for the given value. +typedef LegendPointerBuilder = Widget Function( + BuildContext context, dynamic value); + +enum _LegendType { vector, solidBar, gradientBar } + +/// Positions the legend in the different directions. +enum LegendPosition { + /// Places the legend at left. + left, + + /// Places the legend at right. + right, + + /// Places the legend at top. + top, + + /// Places the legend at bottom. + bottom, +} + +/// Behavior of the legend items when it overflows. +enum LegendOverflowMode { + /// It will place all the legend items in single line and enables scrolling. + scroll, + + /// It will wrap and place the remaining legend items to next line. + wrap, + + /// Exceeding items will be clipped. + none, +} + +/// Option to place the labels either between the bars or +/// on the bar in bar legend. +enum LegendLabelsPlacement { + /// [LegendLabelsPlacement.Item] places labels in the center + /// of the bar. + onItem, + + /// [LegendLabelsPlacement.betweenItems] places labels + /// in-between two bars. + betweenItems +} + +/// Placement of edge labels in the bar legend. +enum LegendEdgeLabelsPlacement { + /// Places the edge labels in inside of the legend items. + inside, + + /// Place the edge labels in the center of the starting position of the + /// legend bars. + center +} + +/// Behavior of the labels when it overflowed from the shape. +enum LegendLabelOverflow { + /// It hides the overflowed labels. + hide, + + /// It does not make any change even if the labels overflowed. + visible, + + /// It trims the labels based on the available space in their respective + /// legend item. + ellipsis +} + +/// Applies gradient or solid color for the bar segments. +enum LegendPaintingStyle { + /// Applies solid color for bar segments. + solid, + + /// Applies gradient color for bar segments. + gradient +} + +/// Specifies the alignment of legend. +enum LegendAlignment { + /// Denotes near. + near, + + /// Denotes center. + center, + + /// Denotes far. + far, +} + +/// Details of single legend item. +class ItemRendererDetails { + /// Creates [ItemRendererDetails]. + ItemRendererDetails({ + required this.index, + required this.text, + required this.color, + required this.iconType, + this.iconBorder, + }); + + /// Index of the legend item. + final int index; + + /// Particular legend item text. + String text; + + /// Particular legend icon color. + Color? color; + + /// Particular legend icon type. + ShapeMarkerType iconType; + + /// Border of the icon. + BorderSide? iconBorder; +} + +/// Represents the class of items in legends. +class LegendItem { + /// Creates a [LegendItem]. + const LegendItem({ + required this.text, + this.color, + this.shader, + this.imageProvider, + this.iconType, + }) : assert(color != null || shader != null || imageProvider != null); + + /// Specifies the text of the legend. + final String text; + + /// Specifies the color of the icon. + final Color? color; + + /// Specifies the shader of the icon. + final Shader? shader; + + /// Identifies an image. + final ImageProvider? imageProvider; + + /// Specifies the type of the icon. + final ShapeMarkerType? iconType; +} + +/// Represents the class for legends. +class SfLegend extends StatefulWidget { + /// Creates a [SfLegend]. + const SfLegend({ + Key? key, + required this.items, + this.title, + this.color, + this.border, + this.position = LegendPosition.top, + this.overflowMode = LegendOverflowMode.wrap, + this.spacing = 5.0, + this.itemSpacing = 10.0, + this.iconSize = const Size(8.0, 8.0), + this.iconBorder, + this.direction, + this.width, + this.height, + this.alignment = LegendAlignment.center, + this.offset, + this.padding = const EdgeInsets.all(10.0), + this.textStyle, + this.iconType = ShapeMarkerType.circle, + this.imageProvider, + this.toggledIconColor, + this.toggledTextOpacity = 0.5, + this.onToggledIndicesChanged, + this.onItemRenderer, + this.isComplex = false, + this.toggledIndices, + required this.child, + }) : _type = _LegendType.vector, + segmentSize = null, + labelsPlacement = null, + edgeLabelsPlacement = null, + labelOverflow = null, + segmentPaintingStyle = null, + itemBuilder = null, + itemCount = 0, + toggledItemColor = null, + pointerBuilder = null, + pointerSize = Size.zero, + pointerColor = null, + pointerController = null, + assert(itemSpacing >= 0), + assert(spacing >= 0), + assert(!isComplex || (isComplex && offset == null)), + super(key: key); + + /// Creates a [SfLegend]. + const SfLegend.builder({ + Key? key, + required this.itemCount, + required this.itemBuilder, + this.title, + this.color, + this.border, + this.offset, + this.padding, + this.position = LegendPosition.top, + this.overflowMode = LegendOverflowMode.wrap, + this.itemSpacing = 10.0, + this.spacing = 5.0, + this.direction, + this.alignment = LegendAlignment.center, + this.toggledItemColor, + this.onToggledIndicesChanged, + this.isComplex = false, + this.toggledIndices, + required this.child, + }) : _type = _LegendType.vector, + items = null, + iconSize = Size.zero, + textStyle = null, + imageProvider = null, + iconType = null, + iconBorder = null, + segmentSize = null, + labelsPlacement = null, + width = null, + height = null, + edgeLabelsPlacement = null, + labelOverflow = null, + segmentPaintingStyle = null, + toggledIconColor = null, + toggledTextOpacity = 0.5, + onItemRenderer = null, + pointerBuilder = null, + pointerSize = Size.zero, + pointerColor = null, + pointerController = null, + assert(!isComplex || (isComplex && offset == null)), + super(key: key); + + /// Creates a [SfLegend]. + const SfLegend.bar({ + Key? key, + required this.items, + this.title, + this.color, + this.border, + this.position = LegendPosition.top, + this.overflowMode = LegendOverflowMode.scroll, + this.itemSpacing = 2.0, + this.direction, + this.offset, + this.padding = const EdgeInsets.all(10.0), + this.textStyle, + this.segmentSize, + this.labelsPlacement, + this.edgeLabelsPlacement = LegendEdgeLabelsPlacement.inside, + this.labelOverflow = LegendLabelOverflow.visible, + this.segmentPaintingStyle = LegendPaintingStyle.solid, + this.isComplex = false, + this.toggledIndices, + this.pointerBuilder, + this.pointerSize = const Size(16.0, 12.0), + this.pointerColor, + this.pointerController, + required this.child, + }) : _type = segmentPaintingStyle == LegendPaintingStyle.solid + ? _LegendType.solidBar + : _LegendType.gradientBar, + iconType = null, + imageProvider = null, + iconSize = Size.zero, + iconBorder = null, + spacing = 0.0, + itemBuilder = null, + itemCount = 0, + alignment = null, + width = null, + height = null, + toggledIconColor = null, + toggledItemColor = null, + toggledTextOpacity = 0.0, + onToggledIndicesChanged = null, + onItemRenderer = null, + assert(itemSpacing >= 0), + assert(!isComplex || (isComplex && offset == null)), + super(key: key); + + /// Specifies the legend items. + final List? items; + + /// Enables a title for the legends to provide a small note about the legends. + final Widget? title; + + /// The color to fill in the background of the legend. + final Color? color; + + /// A border to draw surround the legend. + final BorderSide? border; + + /// Positions the legend in the different directions. + final LegendPosition position; + + /// Wraps or scrolls the legend items when it overflows. + final LegendOverflowMode overflowMode; + + /// Specifies the space between the legend text and the icon. + final double spacing; + + /// Specifies the space between the each legend items. + final double itemSpacing; + + /// Specifies the shape of the legend icon. + final ShapeMarkerType? iconType; + + /// Identifies an image. + final ImageProvider? imageProvider; + + /// Specifies the size of the legend icon. + final Size iconSize; + + /// Specifies border of the icon. + final BorderSide? iconBorder; + + /// Arranges the legend items in either horizontal or vertical direction. + final Axis? direction; + + /// Specifies the alignment of legend. + final LegendAlignment? alignment; + + /// Specifies the width of legend. + final double? width; + + /// Specifies the height of legend. + final double? height; + + /// Places the legend in custom position. + final Offset? offset; + + /// Sets the padding around the legend. + final EdgeInsetsGeometry? padding; + + /// Customizes the legend item's text style. + final TextStyle? textStyle; + + /// specifies the segment size in case of bar legend. + final Size? segmentSize; + + /// Place the labels either between the segments or on the segments. + final LegendLabelsPlacement? labelsPlacement; + + /// Place the edge labels either inside or outside of the bar legend. + final LegendEdgeLabelsPlacement? edgeLabelsPlacement; + + /// Trims or removes the legend text when it is overflowed from the + /// bar legend. + final LegendLabelOverflow? labelOverflow; + + /// Applies gradient or solid color for the bar segments. + final LegendPaintingStyle? segmentPaintingStyle; + + /// Widget builder for legend items. + final IndexedWidgetBuilder? itemBuilder; + + /// Specifies the item count. + final int itemCount; + + /// Specifies the child. + final Widget child; + + /// Callback on toggle index changed. + final ToggledIndicesChangedCallback? onToggledIndicesChanged; + + /// Called every time while rendering a legend item. + final ItemRenderCallback? onItemRenderer; + + /// Specifies the toggle item color. + final Color? toggledIconColor; + + /// Specifies the toggle item's text color opacity. + final double toggledTextOpacity; + + /// Specifies the toggle item's color. Applicable for vector builder. + final Color? toggledItemColor; + + /// Avoid the legend rendering is its size is greater than its child. + final bool isComplex; + + /// Returns a widget for the given value. + /// Pointer which is used to denote the exact color on the segment + /// for the hovered shape or bubble. The [pointerBuilder] will be called + /// when the user interacts with the shapes or bubbles i.e., while tapping in + /// touch devices and hovering in the mouse enabled devices. + final LegendPointerBuilder? pointerBuilder; + + /// Set the pointer size for the pointer support in the bar legend. + final Size pointerSize; + + /// Set the pointer color for the pointer support in the bar legend. + final Color? pointerColor; + + /// Specifies the pointer controller. + final PointerController? pointerController; + + /// Represents the toggled item indices. + final List? toggledIndices; + + /// Specifies the legend type. + final _LegendType _type; + + @override + _SfLegendState createState() => _SfLegendState(); +} + +class _SfLegendState extends State { + bool _omitLegend = false; + TextStyle? _textStyle; + + Widget _buildResponsiveLayout(Widget? current, + [BoxConstraints? baseConstraints]) { + if (current == null) { + return widget.child; + } + + if (widget.offset == null) { + switch (widget.position) { + case LegendPosition.top: + current = Column(children: [ + Align( + alignment: _getEffectiveLegendItemsAlignment( + widget.position, widget.alignment ?? LegendAlignment.center), + child: current, + ), + _buildChild(baseConstraints) + ]); + break; + case LegendPosition.bottom: + current = Column(children: [ + _buildChild(baseConstraints), + Align( + alignment: _getEffectiveLegendItemsAlignment( + widget.position, widget.alignment ?? LegendAlignment.center), + child: current, + ) + ]); + break; + case LegendPosition.left: + current = Row(children: [ + Align( + alignment: _getEffectiveLegendItemsAlignment(widget.position, + widget.alignment ?? LegendAlignment.center), + child: current), + _buildChild(baseConstraints) + ]); + break; + case LegendPosition.right: + current = Row(children: [ + _buildChild(baseConstraints), + Align( + alignment: _getEffectiveLegendItemsAlignment(widget.position, + widget.alignment ?? LegendAlignment.center), + child: current) + ]); + break; + } + } else { + current = Stack( + children: [ + widget.child, + Align( + alignment: _getEffectiveAlignment(widget.position), + child: Padding(padding: _getEffectiveEdgeInsets(), child: current), + ), + ], + ); + } + return current; + } + + Widget _buildChild([BoxConstraints? baseConstraints]) { + Widget? current; + if (!widget.isComplex) { + current = widget.child; + } else { + current = LayoutBuilder( + builder: (BuildContext context, BoxConstraints childConstraints) { + Widget? child; + if ((baseConstraints!.biggest - childConstraints.biggest) <= + childConstraints.biggest) { + child = widget.child; + } else { + SchedulerBinding.instance!.addPostFrameCallback( + (Duration timeStamp) { + setState(() { + _omitLegend = true; + }); + }, + ); + } + + return SizedBox( + width: childConstraints.maxWidth, + height: childConstraints.maxHeight, + child: child, + ); + }, + ); + } + + return Expanded(child: current); + } + + Widget? _buildLegend() { + Widget current; + if (_omitLegend) { + return null; + } + + switch (widget._type) { + case _LegendType.vector: + current = _VectorLegend( + items: widget.items, + direction: widget.direction, + iconBorder: widget.iconBorder, + iconSize: widget.iconSize, + iconType: widget.iconType, + imageProvider: widget.imageProvider, + itemBuilder: widget.itemBuilder, + itemCount: widget.itemCount, + itemSpacing: widget.itemSpacing, + overflowMode: widget.overflowMode, + onItemRenderer: widget.onItemRenderer, + onToggledIndicesChanged: widget.onToggledIndicesChanged, + position: widget.position, + spacing: widget.spacing, + textStyle: _textStyle, + toggledIndices: widget.toggledIndices, + toggledIconColor: widget.toggledIconColor, + toggledItemColor: widget.toggledItemColor, + toggledTextOpacity: widget.toggledTextOpacity, + ); + break; + case _LegendType.solidBar: + current = _SolidBarLegend( + items: widget.items, + labelsPlacement: widget.labelsPlacement, + direction: widget.direction, + segmentSize: widget.segmentSize, + position: widget.position, + itemSpacing: widget.itemSpacing, + padding: widget.padding, + edgeLabelsPlacement: widget.edgeLabelsPlacement, + labelOverflow: widget.labelOverflow, + textStyle: _textStyle, + ); + break; + case _LegendType.gradientBar: + current = _GradientBarLegend( + items: widget.items, + labelsPlacement: widget.labelsPlacement, + direction: widget.direction, + segmentSize: widget.segmentSize, + position: widget.position, + itemSpacing: widget.itemSpacing, + padding: widget.padding, + edgeLabelsPlacement: widget.edgeLabelsPlacement, + labelOverflow: widget.labelOverflow, + textStyle: _textStyle, + pointerBuilder: widget.pointerBuilder, + pointerColor: widget.pointerColor, + pointerSize: widget.pointerSize, + pointController: widget.pointerController, + ); + break; + } + + if (widget.padding != null) { + current = Padding(padding: widget.padding!, child: current); + } + + if (widget.title == null) { + if (widget.overflowMode == LegendOverflowMode.scroll) { + current = SingleChildScrollView( + scrollDirection: widget.position == LegendPosition.top || + widget.position == LegendPosition.bottom + ? Axis.horizontal + : Axis.vertical, + child: current, + ); + } + } else { + if (widget.position == LegendPosition.top || + widget.position == LegendPosition.bottom) { + current = Column( + mainAxisAlignment: widget.position == LegendPosition.top + ? MainAxisAlignment.start + : MainAxisAlignment.end, + children: [ + widget.title!, + if (widget.overflowMode == LegendOverflowMode.scroll) + SingleChildScrollView( + scrollDirection: Axis.horizontal, child: current) + else + current + ], + ); + } else { + current = Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + widget.title!, + Flexible( + fit: FlexFit.loose, + child: widget.overflowMode == LegendOverflowMode.scroll + ? SingleChildScrollView( + scrollDirection: Axis.vertical, child: current) + : current, + ), + ], + ); + } + } + + if (widget.color != null || widget.border != null) { + current = DecoratedBox( + decoration: BoxDecoration( + color: widget.color, + border: widget.border != null + ? Border.all( + color: widget.border!.color, width: widget.border!.width) + : null, + ), + child: current, + ); + } + + if (widget.width != null && widget.height != null) { + current = Container( + width: widget.width, + height: widget.height, + child: current, + ); + } + + return current; + } + + AlignmentGeometry _getEffectiveAlignment(LegendPosition position) { + switch (position) { + case LegendPosition.top: + return Alignment.topCenter; + case LegendPosition.bottom: + return Alignment.bottomCenter; + case LegendPosition.left: + return Alignment.centerLeft; + case LegendPosition.right: + return Alignment.centerRight; + } + } + + AlignmentGeometry _getEffectiveLegendItemsAlignment( + LegendPosition position, LegendAlignment alignment) { + switch (position) { + case LegendPosition.top: + case LegendPosition.bottom: + if (alignment == LegendAlignment.near) { + return Alignment.centerLeft; + } else if (alignment == LegendAlignment.far) { + return Alignment.centerRight; + } else { + return Alignment.center; + } + case LegendPosition.left: + case LegendPosition.right: + if (alignment == LegendAlignment.near) { + return Alignment.topCenter; + } else if (alignment == LegendAlignment.far) { + return Alignment.bottomCenter; + } else { + return Alignment.center; + } + } + } + + EdgeInsetsGeometry _getEffectiveEdgeInsets() { + final Offset offset = widget.offset!; + final LegendPosition legendPosition = widget.position; + switch (legendPosition) { + case LegendPosition.top: + return EdgeInsets.only( + left: offset.dx > 0 ? offset.dx * 2 : 0, + right: offset.dx < 0 ? offset.dx.abs() * 2 : 0, + top: offset.dy > 0 ? offset.dy : 0); + case LegendPosition.left: + return EdgeInsets.only( + top: offset.dy > 0 ? offset.dy * 2 : 0, + bottom: offset.dy < 0 ? offset.dy.abs() * 2 : 0, + left: offset.dx > 0 ? offset.dx : 0); + case LegendPosition.right: + return EdgeInsets.only( + top: offset.dy > 0 ? offset.dy * 2 : 0, + bottom: offset.dy < 0 ? offset.dy.abs() * 2 : 0, + right: offset.dx < 0 ? offset.dx.abs() : 0); + case LegendPosition.bottom: + return EdgeInsets.only( + left: offset.dx > 0 ? offset.dx * 2 : 0, + right: offset.dx < 0 ? offset.dx.abs() * 2 : 0, + bottom: offset.dy < 0 ? offset.dy.abs() : 0); + } + } + + @override + Widget build(BuildContext context) { + final ThemeData themeData = Theme.of(context); + if (widget.itemBuilder == null) { + _textStyle = themeData.textTheme.caption! + .copyWith( + color: themeData.textTheme.caption!.color!.withOpacity(0.87)) + .merge(widget.textStyle); + } + if (!widget.isComplex) { + return _buildResponsiveLayout(_buildLegend()); + } + + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return _buildResponsiveLayout(_buildLegend(), constraints); + }, + ); + } +} + +class _VectorLegend extends StatefulWidget { + const _VectorLegend({ + this.items, + this.direction, + this.iconBorder, + this.iconSize = Size.zero, + this.iconType, + this.imageProvider, + this.itemBuilder, + this.itemCount, + this.itemSpacing, + this.overflowMode, + this.onItemRenderer, + this.onToggledIndicesChanged, + this.position, + this.spacing, + this.textStyle, + this.toggledIconColor, + this.toggledItemColor, + this.toggledTextOpacity, + this.toggledIndices, + }); + + /// Specifies the legend items. + final List? items; + + /// Specifies the shape of the legend icon. + final ShapeMarkerType? iconType; + + /// Identifies an image. + final ImageProvider? imageProvider; + + /// Specifies the size of the legend icon. + final Size iconSize; + + /// Specifies border of the icon. + final BorderSide? iconBorder; + + /// Customizes the legend item's text style. + final TextStyle? textStyle; + + /// Specifies the space between the legend text and the icon. + final double? spacing; + + /// Specifies the toggle item's text color opacity. + final double? toggledTextOpacity; + + /// Wraps or scrolls the legend items when it overflows. + final LegendOverflowMode? overflowMode; + + /// Callback on toggle index changed. + final ToggledIndicesChangedCallback? onToggledIndicesChanged; + + /// Called every time while rendering a legend item. + final ItemRenderCallback? onItemRenderer; + + /// Widget builder for legend items. + final IndexedWidgetBuilder? itemBuilder; + + /// Specifies the item count. + final int? itemCount; + + /// Specifies the toggle item color. + final Color? toggledIconColor; + + /// Specifies the toggle item's color. Applicable for vector builder. + final Color? toggledItemColor; + + /// Positions the legend in the different directions. + final LegendPosition? position; + + /// Specifies the space between the each legend items. + final double? itemSpacing; + + /// Arranges the legend items in either horizontal or vertical direction. + final Axis? direction; + + final List? toggledIndices; + + @override + _VectorLegendState createState() => _VectorLegendState(); +} + +class _VectorLegendState extends State<_VectorLegend> + with SingleTickerProviderStateMixin { + List _buildLegendItems(ThemeData themeData) { + final List items = []; + if (widget.items != null) { + final int length = widget.items!.length; + for (int index = 0; index < length; index++) { + final LegendItem item = widget.items![index]; + items.add(_LegendItem( + index: index, + text: item.text, + textStyle: widget.textStyle, + iconType: item.iconType ?? widget.iconType, + imageProvider: item.imageProvider ?? widget.imageProvider, + shader: item.shader, + iconSize: widget.iconSize, + iconColor: item.color, + iconBorder: widget.iconBorder, + spacing: widget.spacing, + toggledIndices: widget.toggledIndices, + toggledColor: _getEffectiveToggledColor(themeData), + toggledTextOpacity: widget.toggledTextOpacity, + onToggledIndicesChanged: widget.onToggledIndicesChanged, + onItemRenderer: widget.onItemRenderer, + )); + } + } else if (widget.itemCount != null && + widget.itemCount! > 0 && + widget.itemBuilder != null) { + for (int index = 0; index < widget.itemCount!; index++) { + items.add(_LegendItem( + index: index, + itemBuilder: widget.itemBuilder!, + toggledColor: _getEffectiveToggledColor(themeData), + toggledIndices: widget.toggledIndices, + onToggledIndicesChanged: widget.onToggledIndicesChanged, + )); + } + } + return items; + } + + Color? _getEffectiveToggledColor(ThemeData themeData) { + Color? toggledColor; + if (widget.onToggledIndicesChanged != null) { + toggledColor = widget.toggledIconColor ?? widget.toggledItemColor; + if (toggledColor == null || toggledColor == Colors.transparent) { + toggledColor = themeData.brightness == Brightness.light + ? const Color.fromRGBO(230, 230, 230, 1) + : const Color.fromRGBO(66, 66, 66, 1); + } + } + + return toggledColor; + } + + @override + void dispose() { + widget.toggledIndices?.clear(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final ThemeData themeData = Theme.of(context); + final Widget current = Wrap( + direction: widget.direction ?? + (widget.position == LegendPosition.top || + widget.position == LegendPosition.bottom + ? Axis.horizontal + : Axis.vertical), + spacing: widget.itemSpacing!, + runSpacing: 6, + runAlignment: WrapAlignment.center, + alignment: WrapAlignment.start, + children: _buildLegendItems(themeData), + ); + + if (widget.overflowMode == LegendOverflowMode.none) { + return SingleChildScrollView( + scrollDirection: widget.position == LegendPosition.top || + widget.position == LegendPosition.bottom + ? Axis.horizontal + : Axis.vertical, + physics: const NeverScrollableScrollPhysics(), + child: current, + ); + } + + return current; + } +} + +/// Represents the class for generating legend item. +class _LegendItem extends StatefulWidget { + /// Creates a [LegendItem]. + const _LegendItem({ + required this.index, + this.itemBuilder, + this.text, + this.textStyle, + this.iconType, + this.imageProvider, + this.shader, + this.iconSize = Size.zero, + this.iconColor, + this.iconBorder, + this.toggledColor, + this.spacing, + this.toggledTextOpacity, + required this.toggledIndices, + required this.onToggledIndicesChanged, + this.onItemRenderer, + }); + + /// Specifies the item index. + final int index; + + /// Widget builder for legend item. + final IndexedWidgetBuilder? itemBuilder; + + /// Specifies the text of the items. + final String? text; + + /// Specifies the style of the text. + final TextStyle? textStyle; + + /// Specifies the shape of the legend icon. + final ShapeMarkerType? iconType; + + /// Identifies an image. + final ImageProvider? imageProvider; + + /// Specifies the shader of the icon. + final Shader? shader; + + /// Specifies the size of the legend icon. + final Size iconSize; + + /// Specifies the color of the icon. + final Color? iconColor; + + /// Specifies the border of the icon. + final BorderSide? iconBorder; + + /// Specifies the space between the legend text and the icon. + final double? spacing; + + /// Specifies the toggled indices. + final List? toggledIndices; + + /// Specifies the toggled item color. + final Color? toggledColor; + + /// Specifies the toggle item's text color opacity. + final double? toggledTextOpacity; + + /// Callback on toggle index changed. + final ToggledIndicesChangedCallback? onToggledIndicesChanged; + + /// Called every time while rendering a legend item. + final ItemRenderCallback? onItemRenderer; + + @override + _LegendItemState createState() => _LegendItemState(); +} + +class _LegendItemState extends State<_LegendItem> + with SingleTickerProviderStateMixin { + late AnimationController _toggleAnimationController; + late Animation _toggleAnimation; + late ColorTween _iconColorTween; + late Tween _opacityTween; + + ImageInfo? _imageInfo; + ImageStream? _imageStream; + Completer? _completer; + Future? _obtainImage; + + Widget _buildCustomPaint( + ItemRendererDetails details, AsyncSnapshot snapshot) { + Widget current = CustomPaint( + size: widget.iconSize, + painter: _LegendIconShape( + color: details.color, + iconType: details.iconType, + iconBorder: details.iconBorder, + image: snapshot.data, + shader: widget.shader, + ), + ); + + if (widget.shader != null && + details.color != null && + !_toggleAnimationController.isDismissed) { + current = _buildShaderMask(details.color!, current); + } + + return current; + } + + Widget _buildShaderMask(Color color, Widget current) { + return ShaderMask( + blendMode: BlendMode.srcATop, + shaderCallback: (Rect bounds) { + return LinearGradient(colors: [color, color]) + .createShader(bounds); + }, + child: current, + ); + } + + void _handleTapUp(TapUpDetails details) { + if (widget.toggledIndices != null) { + final List toggledIndices = List.from(widget.toggledIndices!); + if (!toggledIndices.contains(widget.index)) { + toggledIndices.add(widget.index); + _toggleAnimationController.forward(); + } else { + toggledIndices.remove(widget.index); + _toggleAnimationController.reverse(); + } + widget.onToggledIndicesChanged?.call(toggledIndices, widget.index); + } + } + + Future? _retrieveImageFromProvider() async { + if (widget.iconType != ShapeMarkerType.image || + widget.imageProvider == null) { + return null; + } + + _completer ??= Completer(); + _imageStream?.removeListener(imageStreamListener(_completer!)); + _imageStream = widget.imageProvider!.resolve(const ImageConfiguration()); + _imageStream!.addListener(imageStreamListener(_completer!)); + _imageInfo?.dispose(); + _imageInfo = await _completer!.future; + return _imageInfo!.image; + } + + ImageStreamListener imageStreamListener(Completer completer) { + return ImageStreamListener((ImageInfo image, bool synchronousCall) { + completer.complete(image); + }); + } + + void rebuild() { + setState(() { + // Rebuilding the widget to update the UI while toggling. + }); + } + + @override + void initState() { + _toggleAnimationController = AnimationController( + vsync: this, duration: const Duration(milliseconds: 250)); + _toggleAnimation = CurvedAnimation( + parent: _toggleAnimationController, curve: Curves.easeInOut); + _toggleAnimation.addListener(rebuild); + + final Color? begin = widget.shader == null && widget.imageProvider == null + ? widget.iconColor + : null; + _iconColorTween = ColorTween(begin: begin, end: widget.toggledColor); + _opacityTween = Tween(begin: 1.0, end: widget.toggledTextOpacity); + + _obtainImage = _retrieveImageFromProvider(); + super.initState(); + } + + @override + void didUpdateWidget(_LegendItem oldWidget) { + if (widget.iconColor != oldWidget.iconColor) { + final Color? begin = widget.shader == null && widget.imageProvider == null + ? widget.iconColor + : null; + _iconColorTween.begin = begin; + } + + if (widget.toggledColor != oldWidget.toggledColor && + widget.toggledColor != null) { + _iconColorTween.end = widget.toggledColor; + } + + if (widget.toggledTextOpacity != oldWidget.toggledTextOpacity) { + _opacityTween = Tween(begin: 1.0, end: widget.toggledTextOpacity); + } + + if (widget.imageProvider != oldWidget.imageProvider) { + _obtainImage = _retrieveImageFromProvider(); + } + + super.didUpdateWidget(oldWidget); + } + + @override + void dispose() { + _toggleAnimation.removeListener(rebuild); + _toggleAnimationController.dispose(); + _imageStream?.removeListener(imageStreamListener(_completer!)); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: _obtainImage, + builder: (BuildContext context, AsyncSnapshot snapshot) { + Widget current; + if (widget.toggledIndices != null) { + if (widget.toggledIndices!.contains(widget.index)) { + _toggleAnimationController.forward(); + } else { + _toggleAnimationController.reverse(); + } + } + + if (widget.itemBuilder != null) { + current = widget.itemBuilder!.call(context, widget.index); + if (widget.onToggledIndicesChanged != null) { + final Color? color = _iconColorTween.evaluate(_toggleAnimation); + if (color != null) { + current = _buildShaderMask(color, current); + } + } + } else { + final ItemRendererDetails details = ItemRendererDetails( + index: widget.index, + text: widget.text!, + color: _iconColorTween.evaluate(_toggleAnimation), + iconType: widget.iconType!, + iconBorder: widget.iconBorder, + ); + widget.onItemRenderer?.call(details); + current = Row( + mainAxisSize: MainAxisSize.min, + children: [ + _buildCustomPaint(details, snapshot), + SizedBox(width: widget.spacing), + Text( + details.text, + style: widget.textStyle!.copyWith( + color: widget.textStyle!.color!.withOpacity( + _opacityTween.evaluate(_toggleAnimation), + ), + ), + ) + ], + ); + } + + if (widget.onToggledIndicesChanged != null) { + current = MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTapUp: _handleTapUp, + behavior: HitTestBehavior.opaque, + child: current, + ), + ); + } + + return current; + }, + ); + } +} + +/// Represents the class for rendering icon shape. +class _LegendIconShape extends CustomPainter { + /// Represents [LegendIconShape] + _LegendIconShape({ + required this.color, + required this.iconType, + this.iconBorder, + this.image, + this.shader, + }); + + /// Specifies the color of the icon. + final Color? color; + + /// Specifies the icon type. + final ShapeMarkerType? iconType; + + /// Specifies the border of the icon. + final BorderSide? iconBorder; + + /// Identifies an image. + final ui.Image? image; + + /// Specifies the shader of the icon. + final Shader? shader; + + Paint _getFillPaint() { + final Paint paint = Paint(); + if (shader != null) { + paint.shader = shader!; + } else if (color != null) { + paint.color = color!; + } + + return paint; + } + + @override + void paint(Canvas canvas, Size size) { + if (iconType == ShapeMarkerType.image && image != null) { + paintImage(canvas: canvas, rect: Offset.zero & size, image: image!); + } else { + ShapePainter.paint( + canvas: canvas, + rect: Offset.zero & size, + shapeType: iconType!, + paint: _getFillPaint(), + borderPaint: iconBorder?.toPaint(), + ); + } + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return true; + } +} + +class _SolidBarLegend extends StatefulWidget { + const _SolidBarLegend({ + required this.items, + this.direction, + this.position, + this.itemSpacing, + this.padding, + this.labelOverflow, + this.labelsPlacement, + this.edgeLabelsPlacement, + this.segmentSize, + this.textStyle, + }); + + /// Specifies the legend items. + final List? items; + + /// Arranges the legend items in either horizontal or vertical direction. + final Axis? direction; + + /// Positions the legend in the different directions. + final LegendPosition? position; + + /// Specifies the space between the each legend items. + final double? itemSpacing; + + /// Sets the padding around the legend. + final EdgeInsetsGeometry? padding; + + /// Trims or removes the legend text when it is overflowed from the + /// bar legend. + final LegendLabelOverflow? labelOverflow; + + /// Option to place the labels either between the bars or + /// on the bar in bar legend. + final LegendLabelsPlacement? labelsPlacement; + + /// Place the edge labels either inside or outside of the bar legend. + final LegendEdgeLabelsPlacement? edgeLabelsPlacement; + + /// Specifies the segment size in case of bar legend. + final Size? segmentSize; + + /// Customizes the legend item's text style. + final TextStyle? textStyle; + + @override + _SolidBarLegendState createState() => _SolidBarLegendState(); +} + +class _SolidBarLegendState extends State<_SolidBarLegend> { + late Axis _direction; + late TextDirection _textDirection; + late TextPainter _textPainter; + bool _isOverlapSegmentText = false; + late Size _segmentSize; + + @override + void initState() { + _textPainter = TextPainter(textDirection: TextDirection.ltr); + super.initState(); + } + + @override + Widget build(BuildContext context) { + _segmentSize = widget.segmentSize ?? const Size(80.0, 12.0); + final TextDirection textDirection = Directionality.of(context); + _direction = widget.direction ?? + (widget.position == LegendPosition.top || + widget.position == LegendPosition.bottom + ? Axis.horizontal + : Axis.vertical); + _textDirection = textDirection == TextDirection.ltr + ? textDirection + : (_direction == Axis.vertical ? TextDirection.ltr : textDirection); + _textPainter.textScaleFactor = MediaQuery.of(context).textScaleFactor; + + final Widget child = Directionality( + textDirection: _textDirection, + child: Wrap( + direction: _direction, + spacing: widget.itemSpacing!, + runSpacing: 6, + runAlignment: WrapAlignment.center, + alignment: WrapAlignment.start, + children: _getBarSegments(), + ), + ); + + return child; + } + + List _getBarSegments() { + final List legendItems = []; + + if (widget.items != null) { + final int length = widget.items!.length; + String? currentText; + for (int i = 0; i < length; i++) { + _isOverlapSegmentText = false; + final LegendItem item = widget.items![i]; + if (widget.labelsPlacement == LegendLabelsPlacement.betweenItems) { + if (i == length - 1) { + currentText = _getTrimmedText(item.text, currentText, i, length); + } else { + if (i == 0) { + final List firstSegmentLabels = + _getStartSegmentLabel(item.text); + currentText = firstSegmentLabels.length > 1 + ? firstSegmentLabels[1] + : firstSegmentLabels[0]; + } else { + currentText = item.text; + } + + currentText = _getTrimmedText( + currentText, widget.items![i + 1].text, i, length); + } + } else { + currentText = item.text; + if (_direction == Axis.horizontal && + widget.labelsPlacement == LegendLabelsPlacement.onItem) { + _isOverlapSegmentText = + _getTextWidth(currentText) > _segmentSize.width; + } + } + + legendItems.add(_getSegment(currentText, item.color!, i, length, item)); + } + } + + return legendItems; + } + + List _getStartSegmentLabel(String startSegmentLabel) { + if (startSegmentLabel.isNotEmpty && + widget.labelsPlacement == LegendLabelsPlacement.betweenItems) { + final List splitText = startSegmentLabel.split('},{'); + if (splitText.length > 1) { + splitText[0] = splitText[0].replaceAll('{', ''); + splitText[1] = splitText[1].replaceAll('}', ''); + } + return splitText; + } else { + return [startSegmentLabel]; + } + } + + String _getTrimmedText( + String currentText, String? nextText, int index, int length) { + if (widget.labelOverflow == LegendLabelOverflow.visible || + currentText.isEmpty || + (nextText != null && nextText.isEmpty) || + nextText == null) { + return currentText; + } + + final Size barSize = _segmentSize; + double refCurrentTextWidth; + double refNextTextWidth; + if (_direction == Axis.horizontal && + widget.labelsPlacement == LegendLabelsPlacement.betweenItems) { + bool isLastInsideItem = false; + if (index == length - 1) { + isLastInsideItem = + widget.edgeLabelsPlacement == LegendEdgeLabelsPlacement.inside; + refNextTextWidth = _getTextWidth(nextText) / 2; + refCurrentTextWidth = isLastInsideItem + ? _getTextWidth(currentText) + : _getTextWidth(currentText) / 2; + } else { + refCurrentTextWidth = _getTextWidth(currentText) / 2; + refNextTextWidth = index + 1 == length - 1 && + widget.edgeLabelsPlacement == LegendEdgeLabelsPlacement.inside + ? _getTextWidth(nextText) + : _getTextWidth(nextText) / 2; + } + _isOverlapSegmentText = refCurrentTextWidth + refNextTextWidth > + barSize.width + widget.itemSpacing!; + if (widget.labelOverflow == LegendLabelOverflow.ellipsis) { + final double textWidth = refCurrentTextWidth + refNextTextWidth; + return _getTrimText( + currentText, + widget.textStyle!, + _segmentSize.width + widget.itemSpacing! / 2, + _textPainter, + textWidth, + refNextTextWidth, + isLastInsideItem); + } + } + + return currentText; + } + + double _getTextWidth(String text) { + _textPainter.text = TextSpan(text: text, style: widget.textStyle); + _textPainter.layout(); + return _textPainter.width; + } + + /// Returns the bar legend icon and label. + Widget _getSegment(String text, Color color, int index, int length, + [LegendItem? startText]) { + final Color iconColor = color; + return _getBarWithLabel(iconColor, index, text, length, startText); + } + + Widget _getBarWithLabel(Color iconColor, int index, String text, + int dataSourceLength, LegendItem? startText) { + Offset textOffset = _getTextOffset(index, text, dataSourceLength); + final CrossAxisAlignment crossAxisAlignment = + _getCrossAxisAlignment(index, dataSourceLength); + if (_direction == Axis.horizontal) { + textOffset = + _textDirection == TextDirection.rtl ? -textOffset : textOffset; + return Container( + width: _segmentSize.width, + child: Column( + crossAxisAlignment: crossAxisAlignment, + children: [ + Padding( + // Gap between segment text and icon. + padding: const EdgeInsets.only(bottom: 7.0), + child: Container( + height: _segmentSize.height, + color: iconColor, + ), + ), + _getTextWidget(index, text, startText, textOffset), + ], + ), + ); + } else { + return _getVerticalBar( + crossAxisAlignment, iconColor, index, text, startText, textOffset); + } + } + + Widget _getVerticalBar(CrossAxisAlignment crossAxisAlignment, Color iconColor, + int index, String text, LegendItem? newText, Offset textOffset) { + return Container( + height: _segmentSize.width, + child: Row( + crossAxisAlignment: crossAxisAlignment, + children: [ + Padding( + // Gap between segment text and icon. + padding: const EdgeInsets.only(right: 7.0), + child: Container( + width: _segmentSize.height, + color: iconColor, + ), + ), + _getTextWidget(index, text, newText, textOffset), + ], + ), + ); + } + + CrossAxisAlignment _getCrossAxisAlignment(int index, int length) { + if (widget.labelsPlacement == LegendLabelsPlacement.onItem && + widget.labelOverflow != LegendLabelOverflow.visible) { + return CrossAxisAlignment.center; + } else { + return CrossAxisAlignment.start; + } + } + + Widget _getTextWidget( + int index, String text, LegendItem? startText, Offset legendOffset) { + if (index == 0 && + startText != null && + startText.text.isNotEmpty && + startText.text[0] == '{' && + widget.labelsPlacement == LegendLabelsPlacement.betweenItems) { + return _getStartSegmentText(startText, text, legendOffset); + } else { + return _getAlignedTextWidget(legendOffset, text, _isOverlapSegmentText); + } + } + + Widget _getStartSegmentText( + LegendItem startText, String text, Offset legendOffset) { + bool isStartTextOverlapping = false; + String startSegmentLabel; + + final List firstSegmentLabels = + _getStartSegmentLabel(startText.text); + startSegmentLabel = firstSegmentLabels[0]; + + if (_direction == Axis.horizontal && + widget.labelOverflow != LegendLabelOverflow.visible && + startSegmentLabel.isNotEmpty && + text.isNotEmpty) { + final double refStartTextWidth = + widget.edgeLabelsPlacement == LegendEdgeLabelsPlacement.inside + ? _getTextWidth(startSegmentLabel) + : _getTextWidth(startSegmentLabel) / 2; + final double refCurrentTextWidth = _getTextWidth(text) / 2; + isStartTextOverlapping = refStartTextWidth + refCurrentTextWidth > + _segmentSize.width + widget.itemSpacing!; + if (widget.labelsPlacement == LegendLabelsPlacement.betweenItems && + widget.labelOverflow == LegendLabelOverflow.ellipsis) { + startSegmentLabel = _getTrimText( + startSegmentLabel, + widget.textStyle!, + _segmentSize.width + widget.itemSpacing! / 2, + _textPainter, + refStartTextWidth + refCurrentTextWidth, + refCurrentTextWidth); + } + } + + Offset startTextOffset = _getStartTextOffset(startSegmentLabel); + startTextOffset = + _textDirection == TextDirection.rtl && _direction == Axis.horizontal + ? -startTextOffset + : startTextOffset; + return Stack( + children: [ + _getAlignedTextWidget( + startTextOffset, startSegmentLabel, isStartTextOverlapping), + _getAlignedTextWidget(legendOffset, text, _isOverlapSegmentText), + ], + ); + } + + Widget _getAlignedTextWidget(Offset offset, String text, bool isOverlapping) { + if ((widget.labelOverflow == LegendLabelOverflow.hide && isOverlapping) || + text.isEmpty) { + return const SizedBox(width: 0.0, height: 0.0); + } + + return Directionality( + textDirection: TextDirection.ltr, + child: offset != Offset.zero + ? Transform.translate( + offset: offset, + child: Text( + text, + softWrap: false, + overflow: TextOverflow.visible, + style: widget.textStyle, + ), + ) + : Text( + text, + textAlign: TextAlign.center, + softWrap: false, + overflow: widget.labelOverflow == LegendLabelOverflow.ellipsis && + widget.labelsPlacement == LegendLabelsPlacement.onItem + ? TextOverflow.ellipsis + : TextOverflow.visible, + style: widget.textStyle, + ), + ); + } + + Offset _getTextOffset(int index, String text, int dataSourceLength) { + if (widget.labelsPlacement == LegendLabelsPlacement.onItem && + widget.labelOverflow != LegendLabelOverflow.visible) { + return Offset.zero; + } + + if (_direction == Axis.horizontal) { + return _getHorizontalTextOffset(index, text, dataSourceLength); + } else { + return _getVerticalTextOffset(index, text, dataSourceLength); + } + } + + Offset _getVerticalTextOffset(int index, String text, int dataSourceLength) { + _textPainter.text = TextSpan(text: text, style: widget.textStyle); + _textPainter.layout(); + if (widget.labelsPlacement == LegendLabelsPlacement.betweenItems) { + if (index == dataSourceLength - 1) { + if (widget.edgeLabelsPlacement == LegendEdgeLabelsPlacement.inside) { + return Offset(0.0, _segmentSize.width - _textPainter.height); + } + return Offset(0.0, _segmentSize.width - _textPainter.height / 2); + } + + return Offset( + 0.0, + _segmentSize.width - + _textPainter.height / 2 + + widget.itemSpacing! / 2); + } else { + return Offset(0.0, _segmentSize.width / 2 - _textPainter.height / 2); + } + } + + Offset _getHorizontalTextOffset( + int index, String text, int dataSourceLength) { + _textPainter.text = TextSpan(text: text, style: widget.textStyle); + _textPainter.layout(); + if (widget.labelsPlacement == LegendLabelsPlacement.betweenItems) { + final double width = _textDirection == TextDirection.rtl && + _segmentSize.width < _textPainter.width + ? _textPainter.width + : _segmentSize.width; + if (index == dataSourceLength - 1) { + if (widget.edgeLabelsPlacement == LegendEdgeLabelsPlacement.inside) { + return Offset(width - _textPainter.width, 0.0); + } + return Offset(width - _textPainter.width / 2, 0.0); + } + + return Offset( + width - _textPainter.width / 2 + widget.itemSpacing! / 2, 0.0); + } else { + final double xPosition = _textDirection == TextDirection.rtl && + _segmentSize.width < _textPainter.width + ? _textPainter.width / 2 - _segmentSize.width / 2 + : _segmentSize.width / 2 - _textPainter.width / 2; + return Offset(xPosition, 0.0); + } + } + + Offset _getStartTextOffset(String text) { + _textPainter.text = TextSpan(text: text, style: widget.textStyle); + _textPainter.layout(); + if (widget.edgeLabelsPlacement == LegendEdgeLabelsPlacement.inside) { + return Offset.zero; + } + + if (_direction == Axis.horizontal) { + return Offset(-_textPainter.width / 2, 0.0); + } else { + return Offset(0.0, -_textPainter.height / 2); + } + } +} + +String _getTrimText(String text, TextStyle style, double maxWidth, + TextPainter painter, double width, + [double? nextTextHalfWidth, bool isInsideLastItem = false]) { + final int actualTextLength = text.length; + String trimmedText = text; + int trimLength = 3; // 3 dots + while (width > maxWidth) { + if (trimmedText.length <= 4) { + trimmedText = trimmedText[0] + '...'; + painter.text = TextSpan(style: style, text: trimmedText); + painter.layout(); + break; + } else { + trimmedText = text.replaceRange( + actualTextLength - trimLength, actualTextLength, '...'); + painter.text = TextSpan(style: style, text: trimmedText); + painter.layout(); + trimLength++; + } + + if (isInsideLastItem && nextTextHalfWidth != null) { + width = painter.width + nextTextHalfWidth; + } else { + width = nextTextHalfWidth != null + ? painter.width / 2 + nextTextHalfWidth + : painter.width; + } + } + + return trimmedText; +} + +class _GradientBarLegend extends StatefulWidget { + const _GradientBarLegend({ + required this.items, + this.labelsPlacement, + this.segmentSize, + this.direction, + this.position, + this.itemSpacing, + this.padding, + this.edgeLabelsPlacement, + this.labelOverflow, + this.textStyle, + this.pointerBuilder, + this.pointerColor, + this.pointerSize, + this.pointController, + }); + + /// specifies the segment size in case of bar legend. + final Size? segmentSize; + + /// Arranges the legend items in either horizontal or vertical direction. + final Axis? direction; + + /// Positions the legend in the different directions. + final LegendPosition? position; + + /// Specifies the space between the each legend items. + final double? itemSpacing; + + /// Specifies the legend items. + final List? items; + + /// Sets the padding around the legend. + final EdgeInsetsGeometry? padding; + + /// Place the edge labels either inside or outside of the bar legend. + final LegendEdgeLabelsPlacement? edgeLabelsPlacement; + + /// Trims or removes the legend text when it is overflowed from the + /// bar legend. + final LegendLabelOverflow? labelOverflow; + + /// Customizes the legend item's text style. + final TextStyle? textStyle; + + /// Specifies the label placement. + final LegendLabelsPlacement? labelsPlacement; + + /// Returns a widget for the given value. + /// Pointer which is used to denote the exact color on the segment + /// for the hovered shape or bubble. The [pointerBuilder] will be called + /// when the user interacts with the shapes or bubbles i.e., while tapping in + /// touch devices and hovering in the mouse enabled devices. + final LegendPointerBuilder? pointerBuilder; + + /// Set the pointer size for the pointer support in the bar legend. + final Size? pointerSize; + + /// Set the pointer color for the pointer support in the bar legend. + final Color? pointerColor; + + /// Specifies the pointer controller. + final PointerController? pointController; + + @override + _GradientBarLegendState createState() => _GradientBarLegendState(); +} + +class _GradientBarLegendState extends State<_GradientBarLegend> { + late List _colors; + late List<_GradientBarLabel> _labels; + + late Axis _direction; + late Size _segmentSize; + late TextPainter _textPainter; + late double _referenceArea; + bool _isRTL = false; + bool _isOverlapSegmentText = false; + + void _updateSegmentSize(double shortestSide) { + if (_direction == Axis.horizontal) { + final double availableWidth = widget.padding != null + ? shortestSide - widget.padding!.horizontal + : shortestSide; + _segmentSize = widget.segmentSize == null + ? Size(availableWidth, 12.0) + : Size( + widget.segmentSize!.width > availableWidth + ? availableWidth + : widget.segmentSize!.width, + widget.segmentSize!.height); + return; + } + + final double availableHeight = widget.padding != null + ? shortestSide - widget.padding!.vertical + : shortestSide; + _segmentSize = widget.segmentSize == null + ? Size(12.0, availableHeight) + : Size( + widget.segmentSize!.width, + widget.segmentSize!.height > availableHeight + ? availableHeight + : widget.segmentSize!.height); + } + + void _collectLabelsAndColors() { + _labels.clear(); + _colors.clear(); + + /// Creating new instance at this point, since we are modifying + /// the same list during the run time. + _colors = []; + _referenceArea = _direction == Axis.horizontal + ? _segmentSize.width + : _segmentSize.height; + if (widget.items != null) { + final int length = widget.items!.length; + + final double slab = _referenceArea / + (widget.labelsPlacement == LegendLabelsPlacement.betweenItems && + widget.items![0].text[0] != '{' + ? length - 1 + : length); + + for (int i = 0; i < length; i++) { + _isOverlapSegmentText = false; + final LegendItem item = widget.items![i]; + String text; + if (i == 0) { + final List firstSegmentLabels = _getStartSegmentLabel(item); + text = firstSegmentLabels.length > 1 + ? firstSegmentLabels[1] + : firstSegmentLabels[0]; + } else { + text = item.text; + } + + if (widget.items![0].text[0] == '{' && + widget.labelsPlacement == LegendLabelsPlacement.betweenItems) { + _collectRageColorMapperLabels(i, item, text, slab, length); + } else { + final int positionIndex = + widget.labelsPlacement == LegendLabelsPlacement.onItem + ? i + 1 + : i; + if (widget.labelsPlacement == LegendLabelsPlacement.onItem) { + text = _getTrimmedText(text, i, length, slab); + } else if (i < length - 1) { + text = _getTrimmedText( + text, i, length, slab, widget.items![i + 1].text); + } + + _labels.add(_GradientBarLabel( + text, + _getTextOffset(text, positionIndex, length - 1, slab), + _isOverlapSegmentText)); + } + _colors.add(item.color!); + } + } + } + + void _collectRageColorMapperLabels( + int i, LegendItem item, String text, double slab, int length) { + if (i == 0 && + widget.labelsPlacement == LegendLabelsPlacement.betweenItems) { + String startText; + final List firstSegmentLabels = _getStartSegmentLabel(item); + startText = firstSegmentLabels[0]; + + if (_direction == Axis.horizontal && + widget.labelsPlacement == LegendLabelsPlacement.betweenItems && + startText.isNotEmpty && + text.isNotEmpty) { + final double refCurrentTextWidth = + widget.edgeLabelsPlacement == LegendEdgeLabelsPlacement.inside + ? _getTextWidth(startText) + : _getTextWidth(startText) / 2; + final double refNextTextWidth = _getTextWidth(text) / 2; + _isOverlapSegmentText = refCurrentTextWidth + refNextTextWidth > slab; + if (widget.labelOverflow == LegendLabelOverflow.ellipsis) { + if (widget.labelsPlacement == LegendLabelsPlacement.betweenItems) { + final double textWidth = refCurrentTextWidth + refNextTextWidth; + startText = _getTrimText(startText, widget.textStyle!, slab, + _textPainter, textWidth, refNextTextWidth); + } + } + } + + _labels.add(_GradientBarLabel(startText, + _getTextOffset(startText, i, length, slab), _isOverlapSegmentText)); + } else if (i < length - 1) { + text = _getTrimmedText(text, i, length, slab, widget.items![i + 1].text); + } + + // For range color mapper, slab is equals to the color mapper + // length. So adding +1 to point out its position index. + _labels.add(_GradientBarLabel(text, + _getTextOffset(text, i + 1, length, slab), _isOverlapSegmentText)); + } + + String _getTrimmedText(String currentText, int index, int length, double slab, + [String? nextText]) { + if (widget.labelOverflow == LegendLabelOverflow.visible || + currentText.isEmpty || + (nextText != null && nextText.isEmpty) || + nextText == null) { + return currentText; + } + + if (_direction == Axis.horizontal && + widget.labelsPlacement == LegendLabelsPlacement.betweenItems) { + double refCurrentTextWidth; + double refNextTextWidth; + bool isLastInsideItem = false; + if (index == length - 1) { + refNextTextWidth = _getTextWidth(nextText) / 2; + + if (widget.edgeLabelsPlacement == LegendEdgeLabelsPlacement.inside) { + refCurrentTextWidth = _getTextWidth(currentText); + isLastInsideItem = true; + } else { + refCurrentTextWidth = _getTextWidth(currentText) / 2; + isLastInsideItem = false; + } + } else { + refCurrentTextWidth = _getTextWidth(currentText) / 2; + refNextTextWidth = index + 1 == length - 1 && + widget.edgeLabelsPlacement == LegendEdgeLabelsPlacement.inside + ? _getTextWidth(nextText) + : _getTextWidth(nextText) / 2; + } + _isOverlapSegmentText = refCurrentTextWidth + refNextTextWidth > slab; + if (widget.labelOverflow == LegendLabelOverflow.ellipsis && + _isOverlapSegmentText) { + if (widget.labelsPlacement == LegendLabelsPlacement.betweenItems) { + final double textWidth = refCurrentTextWidth + refNextTextWidth; + return _getTrimText(currentText, widget.textStyle!, slab, + _textPainter, textWidth, refNextTextWidth, isLastInsideItem); + } + } + } else if (_direction == Axis.horizontal && + widget.labelsPlacement == LegendLabelsPlacement.onItem) { + final double textWidth = _getTextWidth(currentText); + _isOverlapSegmentText = textWidth > slab; + if (_isOverlapSegmentText) { + return _getTrimText( + currentText, widget.textStyle!, slab, _textPainter, textWidth); + } + } + + return currentText; + } + + double _getTextWidth(String text) { + _textPainter.text = TextSpan(text: text, style: widget.textStyle); + _textPainter.layout(); + return _textPainter.width; + } + + List _getStartSegmentLabel(LegendItem item) { + if (item.text[0] == '{' && + widget.labelsPlacement == LegendLabelsPlacement.betweenItems) { + final List splitText = item.text.split('},{'); + if (splitText.length > 1) { + splitText[0] = splitText[0].replaceAll('{', ''); + splitText[1] = splitText[1].replaceAll('}', ''); + } + + return splitText; + } else { + return [(item.text)]; + } + } + + Offset _getTextOffset( + String? text, int positionIndex, int length, double slab) { + _textPainter.text = TextSpan(text: text, style: widget.textStyle); + _textPainter.layout(); + final bool canAdjustLabelToCenter = + widget.edgeLabelsPlacement == LegendEdgeLabelsPlacement.center && + (positionIndex == 0 || positionIndex == length) || + (positionIndex > 0 && positionIndex < length) || + widget.labelsPlacement == LegendLabelsPlacement.onItem; + if (_direction == Axis.horizontal) { + return _getHorizontalOffset( + canAdjustLabelToCenter, positionIndex, slab, length); + } else { + final double referenceTextWidth = canAdjustLabelToCenter + ? _textPainter.height / 2 + : (positionIndex == length ? _textPainter.height : 0.0); + if (widget.labelsPlacement == LegendLabelsPlacement.betweenItems) { + return Offset(0.0, slab * positionIndex - referenceTextWidth); + } + + return Offset( + 0.0, (slab * positionIndex) - referenceTextWidth - slab / 2); + } + } + + Offset _getHorizontalOffset( + bool canAdjustLabelToCenter, int positionIndex, double slab, int length) { + if (_isRTL) { + final double referenceTextWidth = canAdjustLabelToCenter + ? -_textPainter.width / 2 + : (positionIndex == 0 ? -_textPainter.width : 0.0); + double dx = + _segmentSize.width - (slab * positionIndex - referenceTextWidth); + + if (widget.labelsPlacement == LegendLabelsPlacement.betweenItems) { + return Offset(dx, 0.0); + } + + dx = _segmentSize.width - (slab * positionIndex); + + return Offset(dx + slab / 2 - _textPainter.width / 2, 0.0); + } + + final double referenceTextWidth = canAdjustLabelToCenter + ? _textPainter.width / 2 + : (positionIndex == length ? _textPainter.width : 0.0); + if (widget.labelsPlacement == LegendLabelsPlacement.betweenItems) { + return Offset(slab * positionIndex - referenceTextWidth, 0.0); + } + + return Offset( + slab * positionIndex - _textPainter.width / 2 - slab / 2, 0.0); + } + + Widget _buildGradientBar() { + return _direction == Axis.horizontal + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: _getChildren()) + : Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: _getChildren()); + } + + List _getChildren() { + double? labelBoxWidth = _segmentSize.width; + double? labelBoxHeight; + Alignment startAlignment = Alignment.centerLeft; + Alignment endAlignment = Alignment.centerRight; + + if (_direction == Axis.vertical) { + labelBoxWidth = null; + labelBoxHeight = _segmentSize.height; + startAlignment = Alignment.topCenter; + endAlignment = Alignment.bottomCenter; + } + + if (_isRTL && _direction == Axis.horizontal) { + final Alignment temp = startAlignment; + startAlignment = endAlignment; + endAlignment = temp; + } + + final ThemeData themeData = Theme.of(context); + return [ + if (widget.pointerSize != Size.zero && + (kIsWeb || + themeData.platform == TargetPlatform.macOS || + themeData.platform == TargetPlatform.windows || + themeData.platform == TargetPlatform.linux)) + _buildPointer(themeData), + Container( + width: _segmentSize.width, + height: _segmentSize.height, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: startAlignment, end: endAlignment, colors: _colors), + ), + ), + SizedBox( + width: _direction == Axis.vertical ? 7.0 : 0.0, + height: _direction == Axis.horizontal ? 7.0 : 0.0), + Container( + width: labelBoxWidth, height: labelBoxHeight, child: _getLabels()), + ]; + } + + Widget _buildPointer(ThemeData themeData) { + Widget? current; + Matrix4 _matrix4; + + if (widget.pointController!.position != null) { + if (widget.pointerBuilder != null && + widget.pointController!.colorValue != null) { + current = SizedBox( + width: widget.pointerSize!.width, + height: widget.pointerSize!.height, + child: widget.pointerBuilder! + .call(context, widget.pointController!.colorValue), + ); + } else { + current = CustomPaint( + size: widget.pointerSize!, + painter: _LegendIconShape( + color: widget.pointerColor ?? + (themeData.brightness == Brightness.light + ? const Color.fromRGBO(0, 0, 0, 0.54) + : const Color.fromRGBO(255, 255, 255, 0.7)), + iconType: ShapeMarkerType.invertedTriangle, + ), + ); + } + + if (widget.position == LegendPosition.left || + widget.position == LegendPosition.right) { + current = RotatedBox(quarterTurns: 3, child: current); + } + + if (widget.position == LegendPosition.top || + widget.position == LegendPosition.bottom) { + _matrix4 = Matrix4.identity() + ..translate( + widget.pointController!.position!.dx * _segmentSize.width - + (widget.pointerSize!.width / 2), + 0.0); + if (_isRTL) { + _matrix4.invert(); + } + current = Transform(transform: _matrix4, child: current); + } else { + _matrix4 = Matrix4.identity() + ..translate( + 0.0, + widget.pointController!.position!.dy * _segmentSize.height - + (widget.pointerSize!.width / 2)); + current = Transform(transform: _matrix4, child: current); + } + } else { + current = widget.position == LegendPosition.left || + widget.position == LegendPosition.right + ? SizedBox( + height: widget.pointerSize!.width, + width: widget.pointerSize!.height) + : SizedBox( + height: widget.pointerSize!.height, + width: widget.pointerSize!.width); + } + + return current; + } + + Widget _getLabels() { + return Stack( + textDirection: TextDirection.ltr, + children: List.generate(_labels.length, (int index) { + if ((widget.labelOverflow == LegendLabelOverflow.hide && + _labels[index].isOverlapping) || + _labels[index].label.isEmpty) { + return const SizedBox(height: 0.0, width: 0.0); + } + + return Directionality( + textDirection: TextDirection.ltr, + child: Transform.translate( + offset: _labels[index].offset, + child: Text( + _labels[index].label, + style: widget.textStyle, + softWrap: false, + ), + ), + ); + }), + ); + } + + void _rebuild() { + setState(() { + // Rebuilding to update the legend pointer. + }); + } + + @override + void initState() { + _colors = []; + _labels = <_GradientBarLabel>[]; + widget.pointController!.addListener(_rebuild); + super.initState(); + } + + @override + void dispose() { + _colors.clear(); + _labels.clear(); + widget.pointController!.removeListener(_rebuild); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + TextDirection textDirection = Directionality.of(context); + _isRTL = textDirection == TextDirection.rtl; + _textPainter = TextPainter( + textDirection: TextDirection.ltr, + textScaleFactor: MediaQuery.of(context).textScaleFactor); + _direction = widget.direction ?? + (widget.position == LegendPosition.top || + widget.position == LegendPosition.bottom + ? Axis.horizontal + : Axis.vertical); + textDirection = _isRTL + ? (_direction == Axis.vertical ? TextDirection.ltr : textDirection) + : textDirection; + + final Widget child = Directionality( + textDirection: textDirection, + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + final double width = + constraints.hasBoundedWidth ? constraints.maxWidth : 300; + final double height = + constraints.hasBoundedHeight ? constraints.maxHeight : 300; + _updateSegmentSize(Size(width, height).shortestSide); + _collectLabelsAndColors(); + return _buildGradientBar(); + }), + ); + + return child; + } +} + +class _GradientBarLabel { + _GradientBarLabel(this.label, + [this.offset = Offset.zero, this.isOverlapping = false]); + + String label; + Offset offset; + bool isOverlapping; +} + +/// Controller for legend +class PointerController extends ChangeNotifier { + /// Gets or Sets the pointer offset value. + Offset? get position => _position; + Offset? _position; + set position(Offset? value) { + if (_position == value) { + return; + } + _position = value; + notifyListeners(); + } + + /// Specifies the color value. + dynamic colorValue; +} diff --git a/packages/syncfusion_flutter_core/lib/src/localizations/global_localizations.dart b/packages/syncfusion_flutter_core/lib/src/localizations/global_localizations.dart index 71cc26b25..f07ef69f4 100644 --- a/packages/syncfusion_flutter_core/lib/src/localizations/global_localizations.dart +++ b/packages/syncfusion_flutter_core/lib/src/localizations/global_localizations.dart @@ -14,8 +14,9 @@ abstract class SfLocalizations { /// Label that is displayed when there are no events for a /// selected date in a calendar widget. - /// This label is displayed when there are no events on today date and display date in schedule view and - /// displayed under agenda section in month view. + /// This label is displayed when there are no events on today date and + /// display date in schedule view and displayed under agenda section + /// in month view. String get noEventsCalendarLabel; /// A label that is shown on a spanned appointment. This label will be diff --git a/packages/syncfusion_flutter_core/lib/src/test/tooltip_tests.dart b/packages/syncfusion_flutter_core/lib/src/test/tooltip_tests.dart deleted file mode 100644 index cd54d6a19..000000000 --- a/packages/syncfusion_flutter_core/lib/src/test/tooltip_tests.dart +++ /dev/null @@ -1,248 +0,0 @@ -part of tooltip_internal; - -///This method runs the test scenarios for sftooltip widget. -void sfTooltipTest() { - group('Tooltip position', () { - _TooltipContainer? tooltipContainer; - TooltipRenderBox? renderBox; - SfTooltipState tooltipState; - testWidgets('tooltip show position', (WidgetTester tester) async { - tooltipContainer = _TooltipContainer('category_default'); - await tester.pumpWidget(tooltipContainer!); - final SfTooltip tooltip = tooltipContainer!.tooltip; - tooltipState = - // ignore: avoid_as - (tooltip.key! as GlobalKey).currentState! as SfTooltipState; - tooltipState.renderBox?.stringValue = 'test'; - tooltipState.renderBox?.boundaryRect = Rect.fromLTWH(0, 0, 400, 300); - tooltip.show(Offset(0, 100), 100); - renderBox = tooltipState.renderBox; - await tester.pump(const Duration(seconds: 3)); - }); - - test('to test tooltip position', () { - expect(renderBox?._x, 0); - expect(renderBox?._y, 100); - }); - - test('to test tooltip direction', () { - expect(renderBox?._isLeft, true); - expect(renderBox?._isOutOfBoundInTop, false); - expect(renderBox?._isRight, false); - expect(renderBox?._isTop, true); - }); - - tooltipContainer = renderBox = null; - testWidgets('tooltip show position', (WidgetTester tester) async { - tooltipContainer = _TooltipContainer('category_default'); - await tester.pumpWidget(tooltipContainer!); - final SfTooltip tooltip = tooltipContainer!.tooltip; - tooltipState = - // ignore: avoid_as - (tooltip.key! as GlobalKey).currentState! as SfTooltipState; - tooltipState.renderBox?.stringValue = 'test'; - tooltipState.renderBox?.boundaryRect = Rect.fromLTWH(0, 0, 400, 300); - tooltip.show(Offset(100, 100), 100); - renderBox = tooltipState.renderBox; - await tester.pump(const Duration(seconds: 3)); - }); - - test('to test tooltip position', () { - expect(renderBox?._x, 100); - expect(renderBox?._y, 100); - }); - - test('to test tooltip direction', () { - expect(renderBox?._isLeft, false); - expect(renderBox?._isOutOfBoundInTop, false); - expect(renderBox?._isRight, false); - expect(renderBox?._isTop, true); - }); - - tooltipContainer = renderBox = null; - testWidgets('tooltip show position', (WidgetTester tester) async { - tooltipContainer = _TooltipContainer('category_default'); - await tester.pumpWidget(tooltipContainer!); - final SfTooltip tooltip = tooltipContainer!.tooltip; - tooltipState = - // ignore: avoid_as - (tooltip.key! as GlobalKey).currentState! as SfTooltipState; - tooltipState.renderBox?.stringValue = 'test'; - tooltipState.renderBox?.inversePadding = 0.0; - tooltipState.renderBox?.boundaryRect = Rect.fromLTWH(0, 0, 400, 300); - tooltip.show(Offset(100, 0), 100); - renderBox = tooltipState.renderBox; - await tester.pump(const Duration(seconds: 3)); - }); - - test('to test tooltip position', () { - expect(renderBox?._x, 100); - expect(renderBox?._y, 0); - }); - - test('to test tooltip direction', () { - expect(renderBox?._isLeft, false); - expect(renderBox?._isOutOfBoundInTop, false); - expect(renderBox?._isRight, false); - expect(renderBox?._isTop, false); - }); - - tooltipContainer = renderBox = null; - testWidgets('tooltip show position', (WidgetTester tester) async { - tooltipContainer = _TooltipContainer('category_default'); - await tester.pumpWidget(tooltipContainer!); - final SfTooltip tooltip = tooltipContainer!.tooltip; - tooltipState = - // ignore: avoid_as - (tooltip.key! as GlobalKey).currentState! as SfTooltipState; - tooltipState.renderBox?.stringValue = 'test'; - tooltipState.renderBox?.inversePadding = 0.0; - tooltipState.renderBox?.boundaryRect = Rect.fromLTWH(0, 0, 400, 300); - tooltip.show(Offset(0, 0), 100); - renderBox = tooltipState.renderBox; - await tester.pump(const Duration(seconds: 3)); - }); - - test('to test tooltip position', () { - expect(renderBox?._x, 0); - expect(renderBox?._y, 0); - }); - - test('to test tooltip direction', () { - expect(renderBox?._isLeft, true); - expect(renderBox?._isOutOfBoundInTop, false); - expect(renderBox?._isRight, false); - expect(renderBox?._isTop, false); - }); - tooltipContainer = renderBox = null; - testWidgets('tooltip show position', (WidgetTester tester) async { - tooltipContainer = _TooltipContainer('category_default'); - await tester.pumpWidget(tooltipContainer!); - final SfTooltip tooltip = tooltipContainer!.tooltip; - tooltipState = - // ignore: avoid_as - (tooltip.key! as GlobalKey).currentState! as SfTooltipState; - tooltipState.renderBox?.stringValue = 'test'; - tooltipState.renderBox?.inversePadding = 0.0; - tooltipState.renderBox?.boundaryRect = Rect.fromLTWH(0, 0, 400, 300); - tooltip.show(Offset(10, 0), 00); - renderBox = tooltipState.renderBox; - await tester.pump(const Duration(seconds: 3)); - }); - - test('to test tooltip position', () { - expect(renderBox?._x, 10); - expect(renderBox?._y, 0); - }); - - test('to test tooltip direction', () { - expect(renderBox?._isLeft, true); - expect(renderBox?._isOutOfBoundInTop, false); - expect(renderBox?._isRight, false); - expect(renderBox?._isTop, false); - }); - - tooltipContainer = renderBox = null; - testWidgets('tooltip show position', (WidgetTester tester) async { - tooltipContainer = _TooltipContainer('category_default'); - await tester.pumpWidget(tooltipContainer!); - final SfTooltip tooltip = tooltipContainer!.tooltip; - tooltipState = - // ignore: avoid_as - (tooltip.key! as GlobalKey).currentState! as SfTooltipState; - tooltipState.renderBox?.stringValue = 'test'; - tooltipState.renderBox?.header = 'test'; - tooltipState.renderBox?.inversePadding = 0.0; - tooltipState.renderBox?.boundaryRect = Rect.fromLTWH(0, 0, 400, 300); - tooltip.show(Offset(399, 100), 00); - renderBox = tooltipState.renderBox; - await tester.pump(const Duration(seconds: 3)); - }); - - test('to test tooltip position', () { - expect(renderBox?._x, 399); - expect(renderBox?._y, 100); - }); - - test('to test tooltip direction', () { - expect(renderBox?._isLeft, false); - expect(renderBox?._isOutOfBoundInTop, false); - expect(renderBox?._isRight, true); - expect(renderBox?._isTop, true); - }); - - tooltipContainer = renderBox = null; - testWidgets('tooltip show position with template', - (WidgetTester tester) async { - tooltipContainer = _TooltipContainer('template'); - await tester.pumpWidget(tooltipContainer!); - final SfTooltip tooltip = tooltipContainer!.tooltip; - tooltipState = - // ignore: avoid_as - (tooltip.key! as GlobalKey).currentState! as SfTooltipState; - final Widget template = Container( - height: 30, width: 50, color: Colors.red, child: Text('test')); - tooltipState.renderBox?.stringValue = 'test'; - tooltipState.renderBox?.inversePadding = 0.0; - tooltipState.renderBox?.boundaryRect = Rect.fromLTWH(0, 0, 400, 300); - tooltip.show(Offset(100, 100), 100, template); - renderBox = tooltipState.renderBox; - await tester.pump(const Duration(seconds: 3)); - }); - - test('to test tooltip position', () { - expect(renderBox?._x, 100); - expect(renderBox?._y, 100); - }); - - test('to test tooltip direction', () { - expect(renderBox?._isLeft, false); - expect(renderBox?._isOutOfBoundInTop, false); - expect(renderBox?._isRight, false); - expect(renderBox?._isTop, false); - }); - }); -} - -// ignore: must_be_immutable -class _TooltipContainer extends StatelessWidget { - // ignore: prefer_const_constructors_in_immutables - _TooltipContainer(String sampleName) { - tooltip = SfTooltip( - shouldAlwaysShow: true, - key: GlobalKey(), - builder: sampleName == 'template' - ? () { - print('building'); - } - : null, - ); - } - late SfTooltip tooltip; - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Tooltip test', - home: Scaffold( - floatingActionButton: FloatingActionButton( - onPressed: () {}, - ), - // appBar: AppBar( - // title: const Text('Test Chart Widget'), - // ), - body: Center( - child: Container( - // color: Colors.blue, - margin: const EdgeInsets.fromLTRB(0, 0, 0, 0), - // height: 300, - // width: 400, - child: Stack(children: [ - Container( - color: Colors.orange, - ), - tooltip - ]), - ))), - ); - } -} diff --git a/packages/syncfusion_flutter_core/lib/src/theme/barcodes_theme.dart b/packages/syncfusion_flutter_core/lib/src/theme/barcodes_theme.dart index 23532df19..b2ef8a7c2 100644 --- a/packages/syncfusion_flutter_core/lib/src/theme/barcodes_theme.dart +++ b/packages/syncfusion_flutter_core/lib/src/theme/barcodes_theme.dart @@ -125,6 +125,7 @@ class SfBarcodeTheme extends InheritedTheme { /// and [SfThemeData](https://pub.dev/documentation/syncfusion_flutter_core/latest/theme/SfThemeData-class.html), /// for customizing the visual appearance of the barcode widgets. /// +@immutable class SfBarcodeThemeData with Diagnosticable { /// Initialize the SfBarcode theme data factory SfBarcodeThemeData({ diff --git a/packages/syncfusion_flutter_core/lib/src/theme/calendar_theme.dart b/packages/syncfusion_flutter_core/lib/src/theme/calendar_theme.dart index f8fb947f8..9bdbf6e1e 100644 --- a/packages/syncfusion_flutter_core/lib/src/theme/calendar_theme.dart +++ b/packages/syncfusion_flutter_core/lib/src/theme/calendar_theme.dart @@ -101,6 +101,7 @@ class SfCalendarTheme extends InheritedTheme { /// ); /// } /// ``` +@immutable class SfCalendarThemeData with Diagnosticable { /// Create a [SfCalendarThemeData] that's used to configure a /// [SfCalendarTheme]. @@ -117,6 +118,7 @@ class SfCalendarThemeData with Diagnosticable { Color? selectionBorderColor, Color? todayHighlightColor, Color? viewHeaderBackgroundColor, + Color? weekNumberBackgroundColor, TextStyle? todayTextStyle, TextStyle? agendaDayTextStyle, TextStyle? agendaDateTextStyle, @@ -129,6 +131,7 @@ class SfCalendarThemeData with Diagnosticable { TextStyle? leadingDatesTextStyle, TextStyle? blackoutDatesTextStyle, TextStyle? displayNameTextStyle, + TextStyle? weekNumberTextStyle, }) { brightness = brightness ?? Brightness.light; final bool isLight = brightness == Brightness.light; @@ -136,23 +139,23 @@ class SfCalendarThemeData with Diagnosticable { headerBackgroundColor ??= Colors.transparent; agendaBackgroundColor ??= Colors.transparent; agendaDayTextStyle ??= isLight - ? TextStyle( + ? const TextStyle( color: Colors.black54, fontWeight: FontWeight.w500, fontSize: 10, fontFamily: 'Roboto') - : TextStyle( + : const TextStyle( color: Colors.white70, fontWeight: FontWeight.w500, fontSize: 10, fontFamily: 'Roboto'); agendaDateTextStyle ??= isLight - ? TextStyle( + ? const TextStyle( color: Colors.black, fontSize: 18, fontFamily: 'Roboto', fontWeight: FontWeight.normal) - : TextStyle( + : const TextStyle( color: Colors.white, fontSize: 18, fontFamily: 'Roboto', @@ -162,23 +165,28 @@ class SfCalendarThemeData with Diagnosticable { trailingDatesBackgroundColor ??= Colors.transparent; leadingDatesBackgroundColor ??= Colors.transparent; viewHeaderBackgroundColor ??= Colors.transparent; + weekNumberBackgroundColor = Colors.grey.withOpacity(0.19); cellBorderColor ??= isLight ? Colors.black.withOpacity(0.16) : Colors.white30; todayTextStyle ??= isLight - ? TextStyle(color: Colors.white, fontSize: 13, fontFamily: 'Roboto') - : TextStyle(color: Colors.black, fontSize: 13, fontFamily: 'Roboto'); + ? const TextStyle( + color: Colors.white, fontSize: 13, fontFamily: 'Roboto') + : const TextStyle( + color: Colors.black, fontSize: 13, fontFamily: 'Roboto'); headerTextStyle ??= isLight ? const TextStyle( color: Colors.black87, fontSize: 18, fontFamily: 'Roboto') - : TextStyle(color: Colors.white, fontSize: 18, fontFamily: 'Roboto'); + : const TextStyle( + color: Colors.white, fontSize: 18, fontFamily: 'Roboto'); activeDatesTextStyle ??= isLight ? const TextStyle( color: Colors.black87, fontSize: 13, fontFamily: 'Roboto') - : TextStyle(color: Colors.white, fontSize: 13, fontFamily: 'Roboto'); + : const TextStyle( + color: Colors.white, fontSize: 13, fontFamily: 'Roboto'); timeTextStyle ??= isLight - ? TextStyle( + ? const TextStyle( color: Colors.black54, fontWeight: FontWeight.w500, fontSize: 10) - : TextStyle( + : const TextStyle( color: Colors.white38, fontWeight: FontWeight.w500, fontSize: 10); viewHeaderDateTextStyle ??= isLight ? const TextStyle( @@ -186,7 +194,7 @@ class SfCalendarThemeData with Diagnosticable { fontSize: 15, fontWeight: FontWeight.w400, fontFamily: 'Roboto') - : TextStyle( + : const TextStyle( color: Colors.white, fontSize: 15, fontWeight: FontWeight.w400, @@ -197,55 +205,65 @@ class SfCalendarThemeData with Diagnosticable { fontSize: 11, fontWeight: FontWeight.w400, fontFamily: 'Roboto') - : TextStyle( + : const TextStyle( color: Colors.white, fontSize: 11, fontWeight: FontWeight.w400, fontFamily: 'Roboto'); trailingDatesTextStyle ??= isLight - ? TextStyle(color: Colors.black54, fontSize: 13, fontFamily: 'Roboto') - : TextStyle(color: Colors.white70, fontSize: 13, fontFamily: 'Roboto'); + ? const TextStyle( + color: Colors.black54, fontSize: 13, fontFamily: 'Roboto') + : const TextStyle( + color: Colors.white70, fontSize: 13, fontFamily: 'Roboto'); leadingDatesTextStyle ??= isLight - ? TextStyle(color: Colors.black54, fontSize: 13, fontFamily: 'Roboto') - : TextStyle(color: Colors.white70, fontSize: 13, fontFamily: 'Roboto'); + ? const TextStyle( + color: Colors.black54, fontSize: 13, fontFamily: 'Roboto') + : const TextStyle( + color: Colors.white70, fontSize: 13, fontFamily: 'Roboto'); displayNameTextStyle ??= isLight - ? TextStyle( + ? const TextStyle( color: Colors.black, fontSize: 10, fontWeight: FontWeight.w500, fontFamily: 'Roboto') - : TextStyle( + : const TextStyle( color: Colors.white, fontSize: 10, fontWeight: FontWeight.w500, fontFamily: 'Roboto'); + weekNumberTextStyle = isLight + ? const TextStyle( + color: Colors.black87, fontSize: 13, fontFamily: 'Roboto') + : const TextStyle( + color: Colors.white, fontSize: 13, fontFamily: 'Roboto'); return SfCalendarThemeData.raw( - brightness: brightness, - backgroundColor: backgroundColor, - headerTextStyle: headerTextStyle, - headerBackgroundColor: headerBackgroundColor, - agendaBackgroundColor: agendaBackgroundColor, - viewHeaderDateTextStyle: viewHeaderDateTextStyle, - viewHeaderDayTextStyle: viewHeaderDayTextStyle, - agendaDayTextStyle: agendaDayTextStyle, - agendaDateTextStyle: agendaDateTextStyle, - cellBorderColor: cellBorderColor, - timeTextStyle: timeTextStyle, - activeDatesTextStyle: activeDatesTextStyle, - activeDatesBackgroundColor: activeDatesBackgroundColor, - todayBackgroundColor: todayBackgroundColor, - trailingDatesBackgroundColor: trailingDatesBackgroundColor, - leadingDatesBackgroundColor: leadingDatesBackgroundColor, - trailingDatesTextStyle: trailingDatesTextStyle, - blackoutDatesTextStyle: blackoutDatesTextStyle, - displayNameTextStyle: displayNameTextStyle, - leadingDatesTextStyle: leadingDatesTextStyle, - todayTextStyle: todayTextStyle, - todayHighlightColor: todayHighlightColor, - viewHeaderBackgroundColor: viewHeaderBackgroundColor, - selectionBorderColor: selectionBorderColor, - ); + brightness: brightness, + backgroundColor: backgroundColor, + headerTextStyle: headerTextStyle, + headerBackgroundColor: headerBackgroundColor, + agendaBackgroundColor: agendaBackgroundColor, + viewHeaderDateTextStyle: viewHeaderDateTextStyle, + viewHeaderDayTextStyle: viewHeaderDayTextStyle, + agendaDayTextStyle: agendaDayTextStyle, + agendaDateTextStyle: agendaDateTextStyle, + cellBorderColor: cellBorderColor, + timeTextStyle: timeTextStyle, + activeDatesTextStyle: activeDatesTextStyle, + activeDatesBackgroundColor: activeDatesBackgroundColor, + todayBackgroundColor: todayBackgroundColor, + trailingDatesBackgroundColor: trailingDatesBackgroundColor, + leadingDatesBackgroundColor: leadingDatesBackgroundColor, + trailingDatesTextStyle: trailingDatesTextStyle, + blackoutDatesTextStyle: blackoutDatesTextStyle, + displayNameTextStyle: displayNameTextStyle, + leadingDatesTextStyle: leadingDatesTextStyle, + todayTextStyle: todayTextStyle, + todayHighlightColor: todayHighlightColor, + viewHeaderBackgroundColor: viewHeaderBackgroundColor, + weekNumberBackgroundColor: weekNumberBackgroundColor, + selectionBorderColor: selectionBorderColor, + weekNumberTextStyle: weekNumberTextStyle); } /// Create a [SfCalendarThemeData] given a set of exact values. @@ -254,32 +272,33 @@ class SfCalendarThemeData with Diagnosticable { /// This will rarely be used directly. It is used by [lerp] to /// create intermediate themes based on two themes created with the /// [SfCalendarThemeData] constructor. - const SfCalendarThemeData.raw({ - required this.brightness, - required this.backgroundColor, - required this.headerTextStyle, - required this.headerBackgroundColor, - required this.agendaBackgroundColor, - required this.cellBorderColor, - required this.viewHeaderDateTextStyle, - required this.viewHeaderDayTextStyle, - required this.viewHeaderBackgroundColor, - required this.agendaDayTextStyle, - required this.agendaDateTextStyle, - required this.timeTextStyle, - required this.activeDatesTextStyle, - required this.activeDatesBackgroundColor, - required this.todayBackgroundColor, - required this.trailingDatesBackgroundColor, - required this.leadingDatesBackgroundColor, - required this.trailingDatesTextStyle, - required this.blackoutDatesTextStyle, - required this.displayNameTextStyle, - required this.leadingDatesTextStyle, - required this.todayTextStyle, - required this.todayHighlightColor, - required this.selectionBorderColor, - }); + const SfCalendarThemeData.raw( + {required this.brightness, + required this.backgroundColor, + required this.headerTextStyle, + required this.headerBackgroundColor, + required this.agendaBackgroundColor, + required this.cellBorderColor, + required this.viewHeaderDateTextStyle, + required this.viewHeaderDayTextStyle, + required this.viewHeaderBackgroundColor, + required this.agendaDayTextStyle, + required this.agendaDateTextStyle, + required this.timeTextStyle, + required this.activeDatesTextStyle, + required this.activeDatesBackgroundColor, + required this.todayBackgroundColor, + required this.trailingDatesBackgroundColor, + required this.leadingDatesBackgroundColor, + required this.trailingDatesTextStyle, + required this.blackoutDatesTextStyle, + required this.displayNameTextStyle, + required this.leadingDatesTextStyle, + required this.todayTextStyle, + required this.todayHighlightColor, + required this.weekNumberBackgroundColor, + required this.selectionBorderColor, + required this.weekNumberTextStyle}); /// The brightness of the overall theme of the /// application for the calendar widgets. @@ -802,6 +821,52 @@ class SfCalendarThemeData with Diagnosticable { /// ``` final TextStyle trailingDatesTextStyle; + /// Specifies the background for the week number panel. + /// + /// ```dart + ///Widget build(BuildContext context) { + /// return Scaffold( + /// appBar: AppBar(), + /// body: Center( + /// child: SfTheme( + /// data: SfThemeData( + /// calendarThemeData: SfCalendarThemeData( + /// weekNumberBackgroundColor: Colors.blue, + /// weekNumberTextStyle: TextStyle(color: Colors.grey, + /// fontSize: 20), + /// ) + /// ), + /// child: SfCalendar(), + /// ), + /// ) + /// ); + ///} + /// ``` + final Color? weekNumberBackgroundColor; + + /// Specifies the text style for the week number text in calendar. + /// + /// ```dart + ///Widget build(BuildContext context) { + /// return Scaffold( + /// appBar: AppBar(), + /// body: Center( + /// child: SfTheme( + /// data: SfThemeData( + /// calendarThemeData: SfCalendarThemeData( + /// weekNumberBackgroundColor: Colors.blue, + /// weekNumberTextStyle: TextStyle(color: Colors.grey, + /// fontSize: 20), + /// ) + /// ), + /// child: SfCalendar(), + /// ), + /// ) + /// ); + ///} + /// ``` + final TextStyle weekNumberTextStyle; + /// Creates a copy of this theme but with the given /// fields replaced with the new values. SfCalendarThemeData copyWith({ @@ -826,8 +891,10 @@ class SfCalendarThemeData with Diagnosticable { TextStyle? displayNameTextStyle, TextStyle? leadingDatesTextStyle, TextStyle? todayTextStyle, + TextStyle? weekNumberTextStyle, Color? todayHighlightColor, Color? viewHeaderBackgroundColor, + Color? weekNumberBackgroundColor, Color? selectionBorderColor, }) { return SfCalendarThemeData.raw( @@ -862,9 +929,12 @@ class SfCalendarThemeData with Diagnosticable { leadingDatesTextStyle: leadingDatesTextStyle ?? this.leadingDatesTextStyle, todayTextStyle: todayTextStyle ?? this.todayTextStyle, + weekNumberTextStyle: weekNumberTextStyle ?? this.weekNumberTextStyle, todayHighlightColor: todayHighlightColor ?? this.todayHighlightColor, viewHeaderBackgroundColor: viewHeaderBackgroundColor ?? this.viewHeaderBackgroundColor, + weekNumberBackgroundColor: + weekNumberBackgroundColor ?? this.weekNumberBackgroundColor, selectionBorderColor: selectionBorderColor ?? this.selectionBorderColor, ); } @@ -895,7 +965,9 @@ class SfCalendarThemeData with Diagnosticable { todayHighlightColor: Color.lerp(a.todayHighlightColor, b.todayHighlightColor, t), viewHeaderBackgroundColor: Color.lerp( - a.viewHeaderBackgroundColor, b.viewHeaderBackgroundColor, t)); + a.viewHeaderBackgroundColor, b.viewHeaderBackgroundColor, t), + weekNumberBackgroundColor: Color.lerp( + a.weekNumberBackgroundColor, b.weekNumberBackgroundColor, t)); } @override @@ -927,8 +999,10 @@ class SfCalendarThemeData with Diagnosticable { other.blackoutDatesTextStyle == blackoutDatesTextStyle && other.leadingDatesTextStyle == leadingDatesTextStyle && other.todayTextStyle == todayTextStyle && + other.weekNumberTextStyle == weekNumberTextStyle && other.todayHighlightColor == todayHighlightColor && other.viewHeaderBackgroundColor == viewHeaderBackgroundColor && + other.weekNumberBackgroundColor == weekNumberBackgroundColor && other.selectionBorderColor == selectionBorderColor; } @@ -954,8 +1028,10 @@ class SfCalendarThemeData with Diagnosticable { blackoutDatesTextStyle, leadingDatesTextStyle, todayTextStyle, + weekNumberTextStyle, todayHighlightColor, viewHeaderBackgroundColor, + weekNumberBackgroundColor, selectionBorderColor, ]; return hashList(values); @@ -991,6 +1067,9 @@ class SfCalendarThemeData with Diagnosticable { properties.add(ColorProperty( 'viewHeaderBackgroundColor', viewHeaderBackgroundColor, defaultValue: defaultData.viewHeaderBackgroundColor)); + properties.add(ColorProperty( + 'weekNumberBackgroundColor', weekNumberBackgroundColor, + defaultValue: defaultData.weekNumberBackgroundColor)); properties.add(ColorProperty('selectionBorderColor', selectionBorderColor, defaultValue: defaultData.selectionBorderColor)); } diff --git a/packages/syncfusion_flutter_core/lib/src/theme/charts_theme.dart b/packages/syncfusion_flutter_core/lib/src/theme/charts_theme.dart index ba305c6b8..8c25dbc20 100644 --- a/packages/syncfusion_flutter_core/lib/src/theme/charts_theme.dart +++ b/packages/syncfusion_flutter_core/lib/src/theme/charts_theme.dart @@ -117,6 +117,7 @@ class SfChartTheme extends InheritedTheme { /// and [SfThemeData](https://pub.dev/documentation/syncfusion_flutter_core/latest/theme/SfThemeData-class.html), /// for customizing the visual appearance of the chart widget. /// +@immutable class SfChartThemeData with Diagnosticable { /// Creating an argument constructor of SfChartThemeData class. factory SfChartThemeData( diff --git a/packages/syncfusion_flutter_core/lib/src/theme/datagrid_theme.dart b/packages/syncfusion_flutter_core/lib/src/theme/datagrid_theme.dart index 8542026e3..a1e178ed0 100644 --- a/packages/syncfusion_flutter_core/lib/src/theme/datagrid_theme.dart +++ b/packages/syncfusion_flutter_core/lib/src/theme/datagrid_theme.dart @@ -103,6 +103,7 @@ class SfDataGridTheme extends InheritedTheme { /// } /// ``` /// +@immutable class SfDataGridThemeData with Diagnosticable { /// Create a [SfDataGridThemeData] that's used to configure a /// [SfDataGridTheme]. @@ -118,6 +119,8 @@ class SfDataGridThemeData with Diagnosticable { Color? headerHoverColor, Color? headerColor, double? frozenPaneElevation, + Color? rowHoverColor, + TextStyle? rowHoverTextStyle, }) { brightness = brightness ?? Brightness.light; final bool isLight = brightness == Brightness.light; @@ -145,17 +148,33 @@ class SfDataGridThemeData with Diagnosticable { frozenPaneLineWidth ??= 2; headerHoverColor ??= isLight - ? Color.fromRGBO(245, 245, 245, 1) - : Color.fromRGBO(66, 66, 66, 1); + ? const Color.fromRGBO(245, 245, 245, 1) + : const Color.fromRGBO(66, 66, 66, 1); sortIconColor ??= isLight ? Colors.black54 : Colors.white54; headerColor ??= isLight - ? Color.fromRGBO(255, 255, 255, 1) - : Color.fromRGBO(33, 33, 33, 1); + ? const Color.fromRGBO(255, 255, 255, 1) + : const Color.fromRGBO(33, 33, 33, 1); frozenPaneElevation ??= 5.0; + rowHoverColor ??= isLight + ? const Color.fromRGBO(0, 0, 0, 0.08) + : const Color.fromRGBO(255, 255, 255, 0.12); + + rowHoverTextStyle ??= isLight + ? const TextStyle( + fontFamily: 'Roboto', + fontWeight: FontWeight.w400, + fontSize: 14, + color: Colors.black87) + : const TextStyle( + fontFamily: 'Roboto', + fontWeight: FontWeight.w400, + fontSize: 14, + color: Color.fromRGBO(255, 255, 255, 1)); + return SfDataGridThemeData.raw( brightness: brightness, gridLineColor: gridLineColor, @@ -168,6 +187,8 @@ class SfDataGridThemeData with Diagnosticable { sortIconColor: sortIconColor, headerColor: headerColor, frozenPaneElevation: frozenPaneElevation, + rowHoverColor: rowHoverColor, + rowHoverTextStyle: rowHoverTextStyle, ); } @@ -190,6 +211,8 @@ class SfDataGridThemeData with Diagnosticable { required this.headerColor, required this.headerHoverColor, required this.frozenPaneElevation, + required this.rowHoverColor, + required this.rowHoverTextStyle, }); /// The brightness of the overall theme of the @@ -337,6 +360,12 @@ class SfDataGridThemeData with Diagnosticable { /// Defaults to 5.0. The value is always non-negative. final double frozenPaneElevation; + /// The color for the row when a pointer is hovering over it. + final Color rowHoverColor; + + /// The default [TextStyle] for the row when a pointer is hovering over it. + final TextStyle rowHoverTextStyle; + /// Creates a copy of this theme but with the given /// fields replaced with the new values. SfDataGridThemeData copyWith({ @@ -351,6 +380,10 @@ class SfDataGridThemeData with Diagnosticable { Color? headerHoverColor, Color? headerColor, double? frozenPaneElevation, + Color? columnResizeIndicatorColor, + double? columnResizeIndicatorStrokeWidth, + Color? rowHoverColor, + TextStyle? rowHoverTextStyle, }) { return SfDataGridThemeData.raw( brightness: brightness ?? this.brightness, @@ -364,6 +397,8 @@ class SfDataGridThemeData with Diagnosticable { headerColor: headerColor ?? this.headerColor, headerHoverColor: headerHoverColor ?? this.headerHoverColor, frozenPaneElevation: frozenPaneElevation ?? this.frozenPaneElevation, + rowHoverColor: rowHoverColor ?? this.rowHoverColor, + rowHoverTextStyle: rowHoverTextStyle ?? this.rowHoverTextStyle, ); } @@ -388,7 +423,10 @@ class SfDataGridThemeData with Diagnosticable { headerHoverColor: Color.lerp(a.headerHoverColor, b.headerHoverColor, t), headerColor: Color.lerp(a.headerColor, b.headerColor, t), frozenPaneElevation: - lerpDouble(a.frozenPaneElevation, b.frozenPaneElevation, t)); + lerpDouble(a.frozenPaneElevation, b.frozenPaneElevation, t), + rowHoverColor: Color.lerp(a.rowHoverColor, b.rowHoverColor, t), + rowHoverTextStyle: + TextStyle.lerp(a.rowHoverTextStyle, b.rowHoverTextStyle, t)); } @override @@ -412,7 +450,8 @@ class SfDataGridThemeData with Diagnosticable { other.headerHoverColor == headerHoverColor && other.headerColor == headerColor && other.frozenPaneElevation == frozenPaneElevation && - other.frozenPaneElevation == frozenPaneElevation; + other.rowHoverColor == rowHoverColor && + other.rowHoverTextStyle == rowHoverTextStyle; } @override @@ -428,6 +467,8 @@ class SfDataGridThemeData with Diagnosticable { headerHoverColor, headerColor, frozenPaneElevation, + rowHoverColor, + rowHoverTextStyle, ]; return hashList(values); } @@ -459,6 +500,11 @@ class SfDataGridThemeData with Diagnosticable { defaultValue: defaultData.headerColor)); properties.add(DoubleProperty('frozenPaneElevation', frozenPaneElevation, defaultValue: defaultData.frozenPaneElevation)); + properties.add(ColorProperty('rowHoverColor', rowHoverColor, + defaultValue: defaultData.rowHoverColor)); + properties.add(DiagnosticsProperty( + 'rowHoverTextStyle', rowHoverTextStyle, + defaultValue: defaultData.rowHoverTextStyle)); } } @@ -476,6 +522,7 @@ class DataGridCurrentCellStyle { final double borderWidth; @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(Object other) { if (identical(this, other)) { return true; @@ -489,6 +536,7 @@ class DataGridCurrentCellStyle { } @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes int get hashCode { final List values = [ borderColor, diff --git a/packages/syncfusion_flutter_core/lib/src/theme/datapager_theme.dart b/packages/syncfusion_flutter_core/lib/src/theme/datapager_theme.dart index 074acdb80..3e66e2583 100644 --- a/packages/syncfusion_flutter_core/lib/src/theme/datapager_theme.dart +++ b/packages/syncfusion_flutter_core/lib/src/theme/datapager_theme.dart @@ -47,6 +47,7 @@ class SfDataPagerTheme extends InheritedTheme { /// this class to configure a [SfDataPagerTheme] widget /// /// To obtain the current theme, use [SfDataPagerTheme.of]. +@immutable class SfDataPagerThemeData with Diagnosticable { /// Create a [SfDataPagerThemeData] that's used to configure a /// [SfDataPagerTheme]. @@ -66,17 +67,17 @@ class SfDataPagerThemeData with Diagnosticable { final bool isLight = brightness == Brightness.light; backgroundColor ??= isLight - ? Color.fromRGBO(255, 255, 255, 1) - : Color.fromRGBO(33, 33, 33, 1); + ? const Color.fromRGBO(255, 255, 255, 1) + : const Color.fromRGBO(33, 33, 33, 1); itemColor ??= isLight - ? Color.fromRGBO(255, 255, 255, 1) - : Color.fromRGBO(33, 33, 33, 1); + ? const Color.fromRGBO(255, 255, 255, 1) + : const Color.fromRGBO(33, 33, 33, 1); itemTextStyle ??= TextStyle( color: isLight - ? Color.fromRGBO(0, 0, 0, 0.87) - : Color.fromRGBO(255, 255, 255, 1), + ? const Color.fromRGBO(0, 0, 0, 0.87) + : const Color.fromRGBO(255, 255, 255, 1), fontSize: 14, fontFamily: 'Roboto', fontWeight: FontWeight.w400); @@ -85,9 +86,9 @@ class SfDataPagerThemeData with Diagnosticable { itemBorderColor ??= Colors.transparent; - selectedItemColor ??= Color.fromRGBO(33, 150, 243, 1); + selectedItemColor ??= const Color.fromRGBO(33, 150, 243, 1); - selectedItemTextStyle ??= TextStyle( + selectedItemTextStyle ??= const TextStyle( color: Color.fromRGBO(255, 255, 255, 1), fontSize: 14, fontFamily: 'Roboto', @@ -97,8 +98,8 @@ class SfDataPagerThemeData with Diagnosticable { disabledItemTextStyle ??= TextStyle( color: isLight - ? Color.fromRGBO(0, 0, 0, 0.36) - : Color.fromRGBO(255, 255, 255, 0.36)); + ? const Color.fromRGBO(0, 0, 0, 0.36) + : const Color.fromRGBO(255, 255, 255, 0.36)); return SfDataPagerThemeData.raw( brightness: brightness, @@ -168,7 +169,8 @@ class SfDataPagerThemeData with Diagnosticable { /// The width of the border in page item. final double? itemBorderWidth; - ///If non null, the corners of the page item are rounded by this [ItemBorderRadius]. + /// If non null, the corners of the page item are rounded by + /// this [ItemBorderRadius]. /// /// Applies only to boxes with rectangular shapes; /// see also: diff --git a/packages/syncfusion_flutter_core/lib/src/theme/daterangepicker_theme.dart b/packages/syncfusion_flutter_core/lib/src/theme/daterangepicker_theme.dart index b5338e2e4..8883cd257 100644 --- a/packages/syncfusion_flutter_core/lib/src/theme/daterangepicker_theme.dart +++ b/packages/syncfusion_flutter_core/lib/src/theme/daterangepicker_theme.dart @@ -104,6 +104,7 @@ class SfDateRangePickerTheme extends InheritedTheme { /// ); /// } /// ``` +@immutable class SfDateRangePickerThemeData with Diagnosticable { /// Create a [SfDateRangePickerThemeData] that's used to configure a /// [SfDateRangePickerTheme]. @@ -117,6 +118,7 @@ class SfDateRangePickerThemeData with Diagnosticable { Color? todayHighlightColor, Color? selectionColor, Color? rangeSelectionColor, + Color? weekNumberBackgroundColor, TextStyle? viewHeaderTextStyle, TextStyle? headerTextStyle, TextStyle? trailingDatesTextStyle, @@ -133,45 +135,69 @@ class SfDateRangePickerThemeData with Diagnosticable { TextStyle? todayCellTextStyle, TextStyle? weekendDatesTextStyle, TextStyle? specialDatesTextStyle, + TextStyle? weekNumberTextStyle, }) { brightness = brightness ?? Brightness.light; final bool isLight = brightness == Brightness.light; backgroundColor ??= Colors.transparent; headerBackgroundColor ??= Colors.transparent; viewHeaderBackgroundColor ??= Colors.transparent; + weekNumberBackgroundColor = Colors.grey.withOpacity(0.19); viewHeaderTextStyle ??= isLight - ? TextStyle(color: Colors.black87, fontSize: 11, fontFamily: 'Roboto') - : TextStyle(color: Colors.white, fontSize: 11, fontFamily: 'Roboto'); + ? const TextStyle( + color: Colors.black87, fontSize: 11, fontFamily: 'Roboto') + : const TextStyle( + color: Colors.white, fontSize: 11, fontFamily: 'Roboto'); headerTextStyle ??= isLight - ? TextStyle(color: Colors.black87, fontSize: 16, fontFamily: 'Roboto') - : TextStyle(color: Colors.white, fontSize: 16, fontFamily: 'Roboto'); + ? const TextStyle( + color: Colors.black87, fontSize: 16, fontFamily: 'Roboto') + : const TextStyle( + color: Colors.white, fontSize: 16, fontFamily: 'Roboto'); trailingDatesTextStyle ??= isLight - ? TextStyle(color: Colors.black54, fontSize: 13, fontFamily: 'Roboto') - : TextStyle(color: Colors.white54, fontSize: 13, fontFamily: 'Roboto'); + ? const TextStyle( + color: Colors.black54, fontSize: 13, fontFamily: 'Roboto') + : const TextStyle( + color: Colors.white54, fontSize: 13, fontFamily: 'Roboto'); leadingCellTextStyle ??= isLight - ? TextStyle(color: Colors.black54, fontSize: 13, fontFamily: 'Roboto') - : TextStyle(color: Colors.white54, fontSize: 13, fontFamily: 'Roboto'); + ? const TextStyle( + color: Colors.black54, fontSize: 13, fontFamily: 'Roboto') + : const TextStyle( + color: Colors.white54, fontSize: 13, fontFamily: 'Roboto'); activeDatesTextStyle ??= isLight - ? TextStyle(color: Colors.black87, fontSize: 13, fontFamily: 'Roboto') - : TextStyle(color: Colors.white, fontSize: 13, fontFamily: 'Roboto'); + ? const TextStyle( + color: Colors.black87, fontSize: 13, fontFamily: 'Roboto') + : const TextStyle( + color: Colors.white, fontSize: 13, fontFamily: 'Roboto'); cellTextStyle ??= isLight - ? TextStyle(color: Colors.black87, fontSize: 13, fontFamily: 'Roboto') - : TextStyle(color: Colors.white, fontSize: 13, fontFamily: 'Roboto'); + ? const TextStyle( + color: Colors.black87, fontSize: 13, fontFamily: 'Roboto') + : const TextStyle( + color: Colors.white, fontSize: 13, fontFamily: 'Roboto'); leadingDatesTextStyle ??= isLight - ? TextStyle(color: Colors.black54, fontSize: 13, fontFamily: 'Roboto') - : TextStyle(color: Colors.white54, fontSize: 13, fontFamily: 'Roboto'); + ? const TextStyle( + color: Colors.black54, fontSize: 13, fontFamily: 'Roboto') + : const TextStyle( + color: Colors.white54, fontSize: 13, fontFamily: 'Roboto'); rangeSelectionTextStyle ??= isLight - ? TextStyle(color: Colors.black87, fontSize: 13, fontFamily: 'Roboto') - : TextStyle(color: Colors.white, fontSize: 13, fontFamily: 'Roboto'); + ? const TextStyle( + color: Colors.black87, fontSize: 13, fontFamily: 'Roboto') + : const TextStyle( + color: Colors.white, fontSize: 13, fontFamily: 'Roboto'); disabledDatesTextStyle ??= isLight - ? TextStyle(color: Colors.black26, fontSize: 13, fontFamily: 'Roboto') - : TextStyle(color: Colors.white38, fontSize: 13, fontFamily: 'Roboto'); + ? const TextStyle( + color: Colors.black26, fontSize: 13, fontFamily: 'Roboto') + : const TextStyle( + color: Colors.white38, fontSize: 13, fontFamily: 'Roboto'); disabledCellTextStyle ??= isLight - ? TextStyle(color: Colors.black26, fontSize: 13, fontFamily: 'Roboto') - : TextStyle(color: Colors.white38, fontSize: 13, fontFamily: 'Roboto'); + ? const TextStyle( + color: Colors.black26, fontSize: 13, fontFamily: 'Roboto') + : const TextStyle( + color: Colors.white38, fontSize: 13, fontFamily: 'Roboto'); selectionTextStyle ??= isLight - ? TextStyle(color: Colors.white, fontSize: 13, fontFamily: 'Roboto') - : TextStyle(color: Colors.black, fontSize: 13, fontFamily: 'Roboto'); + ? const TextStyle( + color: Colors.white, fontSize: 13, fontFamily: 'Roboto') + : const TextStyle( + color: Colors.black, fontSize: 13, fontFamily: 'Roboto'); todayTextStyle ??= isLight ? const TextStyle(fontSize: 13, fontFamily: 'Roboto') : const TextStyle(fontSize: 13, fontFamily: 'Roboto'); @@ -183,34 +209,40 @@ class SfDateRangePickerThemeData with Diagnosticable { color: Color(0xFF339413), fontSize: 13, fontFamily: 'Roboto') : const TextStyle( color: Color(0xFFEA75FF), fontSize: 13, fontFamily: 'Roboto'); + weekNumberTextStyle = isLight + ? const TextStyle( + color: Colors.black87, fontSize: 13, fontFamily: 'Roboto') + : const TextStyle( + color: Colors.white, fontSize: 13, fontFamily: 'Roboto'); return SfDateRangePickerThemeData.raw( - brightness: brightness, - backgroundColor: backgroundColor, - viewHeaderTextStyle: viewHeaderTextStyle, - headerTextStyle: headerTextStyle, - trailingDatesTextStyle: trailingDatesTextStyle, - leadingCellTextStyle: leadingCellTextStyle, - activeDatesTextStyle: activeDatesTextStyle, - cellTextStyle: cellTextStyle, - rangeSelectionTextStyle: rangeSelectionTextStyle, - rangeSelectionColor: rangeSelectionColor, - leadingDatesTextStyle: leadingDatesTextStyle, - disabledDatesTextStyle: disabledDatesTextStyle, - disabledCellTextStyle: disabledCellTextStyle, - selectionColor: selectionColor, - selectionTextStyle: selectionTextStyle, - startRangeSelectionColor: startRangeSelectionColor, - endRangeSelectionColor: endRangeSelectionColor, - headerBackgroundColor: headerBackgroundColor, - viewHeaderBackgroundColor: viewHeaderBackgroundColor, - blackoutDatesTextStyle: blackoutDatesTextStyle, - todayHighlightColor: todayHighlightColor, - todayTextStyle: todayTextStyle, - todayCellTextStyle: todayCellTextStyle, - weekendDatesTextStyle: weekendDatesTextStyle, - specialDatesTextStyle: specialDatesTextStyle, - ); + brightness: brightness, + backgroundColor: backgroundColor, + viewHeaderTextStyle: viewHeaderTextStyle, + headerTextStyle: headerTextStyle, + trailingDatesTextStyle: trailingDatesTextStyle, + leadingCellTextStyle: leadingCellTextStyle, + activeDatesTextStyle: activeDatesTextStyle, + cellTextStyle: cellTextStyle, + rangeSelectionTextStyle: rangeSelectionTextStyle, + rangeSelectionColor: rangeSelectionColor, + weekNumberBackgroundColor: weekNumberBackgroundColor, + leadingDatesTextStyle: leadingDatesTextStyle, + disabledDatesTextStyle: disabledDatesTextStyle, + disabledCellTextStyle: disabledCellTextStyle, + selectionColor: selectionColor, + selectionTextStyle: selectionTextStyle, + startRangeSelectionColor: startRangeSelectionColor, + endRangeSelectionColor: endRangeSelectionColor, + headerBackgroundColor: headerBackgroundColor, + viewHeaderBackgroundColor: viewHeaderBackgroundColor, + blackoutDatesTextStyle: blackoutDatesTextStyle, + todayHighlightColor: todayHighlightColor, + todayTextStyle: todayTextStyle, + todayCellTextStyle: todayCellTextStyle, + weekendDatesTextStyle: weekendDatesTextStyle, + specialDatesTextStyle: specialDatesTextStyle, + weekNumberTextStyle: weekNumberTextStyle); } /// Create a [SfDateRangePickerThemeData] given a set of exact values. @@ -219,33 +251,34 @@ class SfDateRangePickerThemeData with Diagnosticable { /// This will rarely be used directly. It is used by [lerp] to /// create intermediate themes based on two themes created with the /// [SfDateRangePickerThemeData] constructor. - const SfDateRangePickerThemeData.raw({ - required this.brightness, - required this.backgroundColor, - required this.viewHeaderTextStyle, - required this.headerTextStyle, - required this.trailingDatesTextStyle, - required this.leadingCellTextStyle, - required this.activeDatesTextStyle, - required this.cellTextStyle, - required this.rangeSelectionTextStyle, - required this.rangeSelectionColor, - required this.leadingDatesTextStyle, - required this.disabledDatesTextStyle, - required this.disabledCellTextStyle, - required this.selectionColor, - required this.selectionTextStyle, - required this.startRangeSelectionColor, - required this.endRangeSelectionColor, - required this.headerBackgroundColor, - required this.viewHeaderBackgroundColor, - required this.blackoutDatesTextStyle, - required this.todayHighlightColor, - required this.todayTextStyle, - required this.todayCellTextStyle, - required this.weekendDatesTextStyle, - required this.specialDatesTextStyle, - }); + const SfDateRangePickerThemeData.raw( + {required this.brightness, + required this.backgroundColor, + required this.viewHeaderTextStyle, + required this.headerTextStyle, + required this.trailingDatesTextStyle, + required this.leadingCellTextStyle, + required this.activeDatesTextStyle, + required this.cellTextStyle, + required this.rangeSelectionTextStyle, + required this.rangeSelectionColor, + required this.leadingDatesTextStyle, + required this.disabledDatesTextStyle, + required this.disabledCellTextStyle, + required this.selectionColor, + required this.selectionTextStyle, + required this.startRangeSelectionColor, + required this.endRangeSelectionColor, + required this.headerBackgroundColor, + required this.viewHeaderBackgroundColor, + required this.weekNumberBackgroundColor, + required this.blackoutDatesTextStyle, + required this.todayHighlightColor, + required this.todayTextStyle, + required this.todayCellTextStyle, + required this.weekendDatesTextStyle, + required this.specialDatesTextStyle, + required this.weekNumberTextStyle}); /// The brightness of the overall theme of the /// application for the date picker widget. @@ -424,7 +457,8 @@ class SfDateRangePickerThemeData with Diagnosticable { /// ``` final TextStyle cellTextStyle; - /// Specify the date picker in-between selected range text style in month view when selection mode as range/multi-range selection. + /// Specify the date picker in-between selected range text style in month + /// view when selection mode as range/multi-range selection. /// /// ```dart /// Widget build(BuildContext context) { @@ -531,7 +565,8 @@ class SfDateRangePickerThemeData with Diagnosticable { /// ``` final Color? selectionColor; - /// Specify the date picker in-between selected range background color in month view when selection mode as range/multi-range selection. + /// Specify the date picker in-between selected range background color in + /// month view when selection mode as range/multi-range selection. /// /// ```dart /// Widget build(BuildContext context) { @@ -552,6 +587,27 @@ class SfDateRangePickerThemeData with Diagnosticable { /// ``` final Color? rangeSelectionColor; + /// Specify the background color of the week number panel in month view. + /// + /// ```dart + /// Widget build(BuildContext context) { + /// return Scaffold( + /// appBar: AppBar(), + /// body: Center( + /// child: SfTheme( + /// data: SfThemeData( + /// dateRangePickerThemeData: SfDateRangePickerThemeData( + /// weekNumberBackgroundColor: Colors.blue, + /// ) + /// ), + /// child: SfDateRangePicker(), + /// ), + /// ) + /// ); + /// } + /// ``` + final Color? weekNumberBackgroundColor; + /// Specify the date picker selected cell text style for the single and /// multiple selection and also for the start and end range of single /// and multi-range selection. @@ -576,7 +632,8 @@ class SfDateRangePickerThemeData with Diagnosticable { /// ``` final TextStyle selectionTextStyle; - /// Specify the date picker start date of selected range background color in month view when selection mode as range/multi-range selection. + /// Specify the date picker start date of selected range background color + /// in month view when selection mode as range/multi-range selection. /// /// ```dart /// Widget build(BuildContext context) { @@ -597,7 +654,8 @@ class SfDateRangePickerThemeData with Diagnosticable { /// ``` final Color? startRangeSelectionColor; - /// Specify the date picker end date of selected range background color in month view when selection mode as range/multi-range selection. + /// Specify the date picker end date of selected range background color + /// in month view when selection mode as range/multi-range selection. /// /// ```dart /// Widget build(BuildContext context) { @@ -787,6 +845,29 @@ class SfDateRangePickerThemeData with Diagnosticable { /// ``` final TextStyle specialDatesTextStyle; + /// Specifies the text style for the week number text in month view. + /// + /// ```dart + /// Widget build(BuildContext context) { + /// return Scaffold( + /// appBar: AppBar(), + /// body: Center( + /// child: SfTheme( + /// data: SfThemeData( + /// dateRangePickerThemeData: SfDateRangePickerThemeData( + /// weekNumberBackgroundColor: Colors.blue, + /// weekNumberTextStyle: TextStyle(color: Colors.grey, + /// fontSize: 20), + /// ) + /// ), + /// child: SfDateRangePicker(), + /// ), + /// ) + /// ); + /// } + /// ``` + final TextStyle weekNumberTextStyle; + /// Creates a copy of this theme but with the given /// fields replaced with the new values. SfDateRangePickerThemeData copyWith({ @@ -804,6 +885,7 @@ class SfDateRangePickerThemeData with Diagnosticable { TextStyle? disabledCellTextStyle, Color? selectionColor, Color? rangeSelectionColor, + Color? weekNumberBackgroundColor, TextStyle? selectionTextStyle, Color? startRangeSelectionColor, Color? endRangeSelectionColor, @@ -815,6 +897,7 @@ class SfDateRangePickerThemeData with Diagnosticable { TextStyle? todayCellTextStyle, TextStyle? weekendDatesTextStyle, TextStyle? specialDatesTextStyle, + TextStyle? weekNumberTextStyle, }) { return SfDateRangePickerThemeData.raw( brightness: brightness ?? this.brightness, @@ -829,6 +912,8 @@ class SfDateRangePickerThemeData with Diagnosticable { rangeSelectionTextStyle: rangeSelectionTextStyle ?? this.rangeSelectionTextStyle, rangeSelectionColor: rangeSelectionColor ?? this.rangeSelectionColor, + weekNumberBackgroundColor: + weekNumberBackgroundColor ?? this.weekNumberBackgroundColor, leadingDatesTextStyle: leadingDatesTextStyle ?? this.leadingDatesTextStyle, disabledDatesTextStyle: @@ -854,6 +939,7 @@ class SfDateRangePickerThemeData with Diagnosticable { weekendDatesTextStyle ?? this.weekendDatesTextStyle, specialDatesTextStyle: specialDatesTextStyle ?? this.specialDatesTextStyle, + weekNumberTextStyle: weekNumberTextStyle ?? this.weekNumberTextStyle, ); } @@ -878,6 +964,8 @@ class SfDateRangePickerThemeData with Diagnosticable { a.viewHeaderBackgroundColor, b.viewHeaderBackgroundColor, t), todayHighlightColor: Color.lerp(a.todayHighlightColor, b.todayHighlightColor, t), + weekNumberBackgroundColor: Color.lerp( + a.weekNumberBackgroundColor, b.weekNumberBackgroundColor, t), ); } @@ -900,6 +988,7 @@ class SfDateRangePickerThemeData with Diagnosticable { other.cellTextStyle == cellTextStyle && other.rangeSelectionTextStyle == rangeSelectionTextStyle && other.rangeSelectionColor == rangeSelectionColor && + other.weekNumberBackgroundColor == weekNumberBackgroundColor && other.leadingDatesTextStyle == leadingDatesTextStyle && other.disabledDatesTextStyle == disabledDatesTextStyle && other.disabledCellTextStyle == disabledCellTextStyle && @@ -914,7 +1003,8 @@ class SfDateRangePickerThemeData with Diagnosticable { other.todayTextStyle == todayTextStyle && other.todayCellTextStyle == todayCellTextStyle && other.weekendDatesTextStyle == weekendDatesTextStyle && - other.specialDatesTextStyle == specialDatesTextStyle; + other.specialDatesTextStyle == specialDatesTextStyle && + other.weekNumberTextStyle == weekNumberTextStyle; } @override @@ -929,6 +1019,7 @@ class SfDateRangePickerThemeData with Diagnosticable { cellTextStyle, rangeSelectionTextStyle, rangeSelectionColor, + weekNumberBackgroundColor, leadingDatesTextStyle, disabledDatesTextStyle, disabledCellTextStyle, @@ -944,6 +1035,7 @@ class SfDateRangePickerThemeData with Diagnosticable { todayCellTextStyle, weekendDatesTextStyle, specialDatesTextStyle, + weekNumberTextStyle ]; return hashList(values); } @@ -958,6 +1050,9 @@ class SfDateRangePickerThemeData with Diagnosticable { defaultValue: defaultData.backgroundColor)); properties.add(ColorProperty('rangeSelectionColor', rangeSelectionColor, defaultValue: defaultData.rangeSelectionColor)); + properties.add(ColorProperty( + 'weekNumberBackgroundColor', weekNumberBackgroundColor, + defaultValue: defaultData.weekNumberBackgroundColor)); properties.add(ColorProperty('selectionColor', selectionColor, defaultValue: defaultData.selectionColor)); properties.add(ColorProperty( diff --git a/packages/syncfusion_flutter_core/lib/src/theme/gauges_theme.dart b/packages/syncfusion_flutter_core/lib/src/theme/gauges_theme.dart index bb6782a01..ce6d64d61 100644 --- a/packages/syncfusion_flutter_core/lib/src/theme/gauges_theme.dart +++ b/packages/syncfusion_flutter_core/lib/src/theme/gauges_theme.dart @@ -117,6 +117,7 @@ class SfGaugeTheme extends InheritedTheme { /// and [SfThemeData](https://pub.dev/documentation/syncfusion_flutter_core/latest/theme/SfThemeData-class.html), /// for customizing the visual appearance of the radial gauges widgets. /// +@immutable class SfGaugeThemeData with Diagnosticable { /// Initialize the gauge theme data factory SfGaugeThemeData({ diff --git a/packages/syncfusion_flutter_core/lib/src/theme/maps_theme.dart b/packages/syncfusion_flutter_core/lib/src/theme/maps_theme.dart index bd4f1ddf4..5c41f3ea3 100644 --- a/packages/syncfusion_flutter_core/lib/src/theme/maps_theme.dart +++ b/packages/syncfusion_flutter_core/lib/src/theme/maps_theme.dart @@ -125,6 +125,7 @@ class SfMapsTheme extends InheritedTheme { /// ); /// } /// ``` +@immutable class SfMapsThemeData with Diagnosticable { /// Returns a new instance of [SfMapsThemeData.raw] for the given values. /// @@ -188,7 +189,7 @@ class SfMapsThemeData with Diagnosticable { ? const Color.fromRGBO(117, 117, 117, 1) : const Color.fromRGBO(245, 245, 245, 1); tooltipStrokeWidth ??= 1.0; - tooltipBorderRadius ??= BorderRadius.all(Radius.circular(4.0)); + tooltipBorderRadius ??= const BorderRadius.all(Radius.circular(4.0)); toggledItemColor ??= isLight ? const Color.fromRGBO(245, 245, 245, 1) : const Color.fromRGBO(66, 66, 66, 1); diff --git a/packages/syncfusion_flutter_core/lib/src/theme/pdfviewer_theme.dart b/packages/syncfusion_flutter_core/lib/src/theme/pdfviewer_theme.dart index 71d811644..835b606a7 100644 --- a/packages/syncfusion_flutter_core/lib/src/theme/pdfviewer_theme.dart +++ b/packages/syncfusion_flutter_core/lib/src/theme/pdfviewer_theme.dart @@ -25,7 +25,8 @@ class SfPdfViewerTheme extends InheritedTheme { const SfPdfViewerTheme({Key? key, required this.data, required this.child}) : super(key: key, child: child); - /// Specifies the color and typography values for descendant [SfPdfViewer] widgets. + /// Specifies the color and typography values for descendant + /// [SfPdfViewer] widgets. /// /// ```dart /// Widget build(BuildContext context) { @@ -62,11 +63,11 @@ class SfPdfViewerTheme extends InheritedTheme { @override final Widget child; - /// The data from the closest [SfPdfViewerTheme] instance that encloses the given - /// context. + /// The data from the closest [SfPdfViewerTheme] instance that encloses + /// the given context. /// - /// Defaults to [SfThemeData.pdfViewerThemeData] if there is no [SfPdfViewerTheme] - /// in the given build context. + /// Defaults to [SfThemeData.pdfViewerThemeData] if there is no + /// [SfPdfViewerTheme] in the given build context. /// static SfPdfViewerThemeData? of(BuildContext context) { final SfPdfViewerTheme? pdfViewerTheme = @@ -106,6 +107,7 @@ class SfPdfViewerTheme extends InheritedTheme { /// ); /// } /// ``` +@immutable class SfPdfViewerThemeData with Diagnosticable { /// Creating an argument constructor of SfPdfViewerThemeData class. factory SfPdfViewerThemeData( @@ -118,20 +120,21 @@ class SfPdfViewerThemeData with Diagnosticable { PdfPaginationDialogStyle? paginationDialogStyle}) { brightness = brightness ?? Brightness.light; final bool isLight = brightness == Brightness.light; - backgroundColor ??= isLight ? Color(0xFFD6D6D6) : Color(0xFF303030); - scrollStatusStyle ??= PdfScrollStatusStyle( + backgroundColor ??= + isLight ? const Color(0xFFD6D6D6) : const Color(0xFF303030); + scrollStatusStyle ??= const PdfScrollStatusStyle( backgroundColor: Color(0xFF757575), pageInfoTextStyle: TextStyle(fontFamily: 'Roboto', fontSize: 16, color: Colors.white)); scrollHeadStyle ??= isLight - ? PdfScrollHeadStyle( + ? const PdfScrollHeadStyle( backgroundColor: Color(0xFFFAFAFA), pageNumberTextStyle: TextStyle(fontSize: 12, color: Colors.black)) - : PdfScrollHeadStyle( + : const PdfScrollHeadStyle( backgroundColor: Color(0xFF424242), pageNumberTextStyle: TextStyle(fontSize: 12, color: Colors.white)); bookmarkViewStyle ??= isLight - ? PdfBookmarkViewStyle( + ? const PdfBookmarkViewStyle( backgroundColor: Colors.white, headerBarColor: Color(0xFFFAFAFA), closeIconColor: Colors.black54, @@ -153,13 +156,13 @@ class SfPdfViewerThemeData with Diagnosticable { ), ) : PdfBookmarkViewStyle( - backgroundColor: Color(0xFF212121), - headerBarColor: Color(0xFF424242), + backgroundColor: const Color(0xFF212121), + headerBarColor: const Color(0xFF424242), closeIconColor: Colors.white54, backIconColor: Colors.white54, navigationIconColor: Colors.white54, - selectionColor: Color.fromRGBO(255, 255, 255, 0.12), - titleSeparatorColor: Color.fromRGBO(255, 255, 255, 0.16), + selectionColor: const Color.fromRGBO(255, 255, 255, 0.12), + titleSeparatorColor: const Color.fromRGBO(255, 255, 255, 0.16), headerTextStyle: TextStyle( fontSize: 16, fontFamily: 'Roboto', @@ -176,33 +179,33 @@ class SfPdfViewerThemeData with Diagnosticable { paginationDialogStyle ??= isLight ? PdfPaginationDialogStyle( backgroundColor: Colors.white, - headerTextStyle: TextStyle( + headerTextStyle: const TextStyle( fontFamily: 'Roboto', fontSize: 20, fontWeight: FontWeight.w500, color: Colors.black87), - inputFieldTextStyle: TextStyle( + inputFieldTextStyle: const TextStyle( fontFamily: 'Roboto', fontSize: 16, color: Colors.black87), hintTextStyle: TextStyle( fontFamily: 'Roboto', fontSize: 16, color: Colors.black87.withOpacity(0.54)), - pageInfoTextStyle: TextStyle( + pageInfoTextStyle: const TextStyle( fontFamily: 'Roboto', fontSize: 12, color: Color.fromRGBO(0, 0, 0, 0.6)), - validationTextStyle: TextStyle( + validationTextStyle: const TextStyle( fontFamily: 'Roboto', fontSize: 12, color: Color(0xFFC33F38)), - okTextStyle: TextStyle( + okTextStyle: const TextStyle( fontFamily: 'Roboto', fontSize: 14, fontWeight: FontWeight.w500), - cancelTextStyle: TextStyle( + cancelTextStyle: const TextStyle( fontFamily: 'Roboto', fontSize: 14, fontWeight: FontWeight.w500)) : PdfPaginationDialogStyle( - backgroundColor: Color(0xFF424242), + backgroundColor: const Color(0xFF424242), headerTextStyle: TextStyle( fontFamily: 'Roboto', fontSize: 20, @@ -215,16 +218,16 @@ class SfPdfViewerThemeData with Diagnosticable { hintTextStyle: TextStyle( fontFamily: 'Roboto', fontSize: 16, - color: Color(0xFFB3B3B3).withOpacity(0.54)), - pageInfoTextStyle: TextStyle( + color: const Color(0xFFB3B3B3).withOpacity(0.54)), + pageInfoTextStyle: const TextStyle( fontFamily: 'Roboto', fontSize: 12, color: Color(0xFFB3B3B3)), - validationTextStyle: TextStyle( + validationTextStyle: const TextStyle( fontFamily: 'Roboto', fontSize: 12, color: Color(0xFFFF8781)), - okTextStyle: TextStyle( + okTextStyle: const TextStyle( fontFamily: 'Roboto', fontSize: 14, fontWeight: FontWeight.w500), - cancelTextStyle: TextStyle( + cancelTextStyle: const TextStyle( fontFamily: 'Roboto', fontSize: 14, fontWeight: FontWeight.w500)); @@ -441,8 +444,8 @@ class SfPdfViewerThemeData with Diagnosticable { /// ``` final PdfPaginationDialogStyle paginationDialogStyle; - /// Creates a copy of this [SfPdfViewer] theme data object with the matching fields - /// replaced with the non-null parameter values. + /// Creates a copy of this [SfPdfViewer] theme data object with the + /// matching fields replaced with the non-null parameter values. SfPdfViewerThemeData copyWith( {Brightness? brightness, Color? backgroundColor, @@ -551,12 +554,14 @@ class PdfScrollStatusStyle { final TextStyle? pageInfoTextStyle; @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes int get hashCode { final List values = [backgroundColor, pageInfoTextStyle]; return hashList(values); } @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(Object other) { if (identical(this, other)) { return true; @@ -595,6 +600,7 @@ class PdfScrollHeadStyle { final TextStyle? pageNumberTextStyle; @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes int get hashCode { final List values = [ backgroundColor, @@ -604,6 +610,7 @@ class PdfScrollHeadStyle { } @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(Object other) { if (identical(this, other)) { return true; @@ -672,6 +679,7 @@ class PdfBookmarkViewStyle { final TextStyle? headerTextStyle; @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes int get hashCode { final List values = [ backgroundColor, @@ -688,6 +696,7 @@ class PdfBookmarkViewStyle { } @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(Object other) { if (identical(this, other)) { return true; @@ -729,10 +738,11 @@ class PdfBookmarkViewStyle { } } -/// Holds the color and text styles for the pagination dialog in the [SfPdfViewer]. +/// Holds the color and text styles for the pagination dialog in +/// the [SfPdfViewer]. class PdfPaginationDialogStyle { - /// Creates a [PdfPaginationDialogStyle] that's used to configure styles for the - /// pagination dialog in [SfPdfViewer]. + /// Creates a [PdfPaginationDialogStyle] that's used to configure styles for + /// the pagination dialog in [SfPdfViewer]. const PdfPaginationDialogStyle( {this.backgroundColor, this.headerTextStyle, @@ -752,10 +762,12 @@ class PdfPaginationDialogStyle { /// The style for the input text field of pagination dialog in [SfPdfViewer]. final TextStyle? inputFieldTextStyle; - /// The style for the hint text of pagination dialog text field in [SfPdfViewer]. + /// The style for the hint text of pagination dialog text field + /// in [SfPdfViewer]. final TextStyle? hintTextStyle; - /// The style for the page information text of pagination dialog in [SfPdfViewer]. + /// The style for the page information text of pagination dialog + /// in [SfPdfViewer]. final TextStyle? pageInfoTextStyle; /// The style for the validation text of pagination dialog in [SfPdfViewer]. @@ -768,6 +780,7 @@ class PdfPaginationDialogStyle { final TextStyle? cancelTextStyle; @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes int get hashCode { final List values = [ backgroundColor, @@ -783,6 +796,7 @@ class PdfPaginationDialogStyle { } @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(Object other) { if (identical(this, other)) { return true; diff --git a/packages/syncfusion_flutter_core/lib/src/theme/range_selector_theme.dart b/packages/syncfusion_flutter_core/lib/src/theme/range_selector_theme.dart index 990841de8..36f5f6d75 100644 --- a/packages/syncfusion_flutter_core/lib/src/theme/range_selector_theme.dart +++ b/packages/syncfusion_flutter_core/lib/src/theme/range_selector_theme.dart @@ -1,6 +1,8 @@ import 'dart:ui'; + import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; + import '../../theme.dart'; /// Applies a theme to descendant Syncfusion range selector widgets. @@ -124,7 +126,7 @@ class SfRangeSelectorTheme extends InheritedTheme { /// the left thumb, and the right thumb and the [max] value. /// For RTL, the inactive side of the range selector is between the [max] value /// and the left thumb, and the right thumb and the [min] value. -/// * The "divisors", which is a shape that renders on the track based on the +/// * The "dividers", which is a shape that renders on the track based on the /// given [interval] value. /// * The "ticks", which is a shape that rendered based on /// given [interval] value. Basically, it is rendered below the track. @@ -174,12 +176,12 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { Color? disabledActiveMinorTickColor, Color? disabledInactiveMinorTickColor, Color? overlayColor, - Color? inactiveDivisorColor, - Color? activeDivisorColor, + Color? inactiveDividerColor, + Color? activeDividerColor, Color? disabledActiveTrackColor, Color? disabledInactiveTrackColor, - Color? disabledActiveDivisorColor, - Color? disabledInactiveDivisorColor, + Color? disabledActiveDividerColor, + Color? disabledInactiveDividerColor, Color? disabledThumbColor, Color? activeRegionColor, Color? inactiveRegionColor, @@ -187,16 +189,16 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { Color? overlappingTooltipStrokeColor, Color? thumbStrokeColor, Color? overlappingThumbStrokeColor, - Color? activeDivisorStrokeColor, - Color? inactiveDivisorStrokeColor, + Color? activeDividerStrokeColor, + Color? inactiveDividerStrokeColor, double? trackCornerRadius, double? overlayRadius, double? thumbRadius, - double? activeDivisorRadius, - double? inactiveDivisorRadius, + double? activeDividerRadius, + double? inactiveDividerRadius, double? thumbStrokeWidth, - double? activeDivisorStrokeWidth, - double? inactiveDivisorStrokeWidth}) { + double? activeDividerStrokeWidth, + double? inactiveDividerStrokeWidth}) { brightness = brightness ?? Brightness.light; final bool isLight = brightness == Brightness.light; activeTrackHeight ??= 6.0; @@ -234,13 +236,13 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { tooltipTextStyle: tooltipTextStyle, inactiveTrackColor: inactiveTrackColor, activeTrackColor: activeTrackColor, - inactiveDivisorColor: inactiveDivisorColor, - activeDivisorColor: activeDivisorColor, + inactiveDividerColor: inactiveDividerColor, + activeDividerColor: activeDividerColor, thumbColor: thumbColor, thumbStrokeColor: thumbStrokeColor, overlappingThumbStrokeColor: overlappingThumbStrokeColor, - activeDivisorStrokeColor: activeDivisorStrokeColor, - inactiveDivisorStrokeColor: inactiveDivisorStrokeColor, + activeDividerStrokeColor: activeDividerStrokeColor, + inactiveDividerStrokeColor: inactiveDividerStrokeColor, overlayColor: overlayColor, activeTickColor: activeTickColor, inactiveTickColor: inactiveTickColor, @@ -252,8 +254,8 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { disabledInactiveMinorTickColor: disabledInactiveMinorTickColor, disabledActiveTrackColor: disabledActiveTrackColor, disabledInactiveTrackColor: disabledInactiveTrackColor, - disabledActiveDivisorColor: disabledActiveDivisorColor, - disabledInactiveDivisorColor: disabledInactiveDivisorColor, + disabledActiveDividerColor: disabledActiveDividerColor, + disabledInactiveDividerColor: disabledInactiveDividerColor, disabledThumbColor: disabledThumbColor, activeRegionColor: activeRegionColor, inactiveRegionColor: inactiveRegionColor, @@ -261,11 +263,11 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { overlappingTooltipStrokeColor: overlappingTooltipStrokeColor, overlayRadius: overlayRadius, thumbRadius: thumbRadius, - activeDivisorRadius: activeDivisorRadius, - inactiveDivisorRadius: inactiveDivisorRadius, + activeDividerRadius: activeDividerRadius, + inactiveDividerRadius: inactiveDividerRadius, thumbStrokeWidth: thumbStrokeWidth, - activeDivisorStrokeWidth: activeDivisorStrokeWidth, - inactiveDivisorStrokeWidth: inactiveDivisorStrokeWidth, + activeDividerStrokeWidth: activeDividerStrokeWidth, + inactiveDividerStrokeWidth: inactiveDividerStrokeWidth, trackCornerRadius: trackCornerRadius); } @@ -291,8 +293,8 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { required Color? thumbColor, required Color? thumbStrokeColor, required Color? overlappingThumbStrokeColor, - required Color? activeDivisorStrokeColor, - required Color? inactiveDivisorStrokeColor, + required Color? activeDividerStrokeColor, + required Color? inactiveDividerStrokeColor, required Color activeTickColor, required Color inactiveTickColor, required Color disabledActiveTickColor, @@ -302,12 +304,12 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { required Color disabledActiveMinorTickColor, required Color disabledInactiveMinorTickColor, required Color? overlayColor, - required Color? inactiveDivisorColor, - required Color? activeDivisorColor, + required Color? inactiveDividerColor, + required Color? activeDividerColor, required Color? disabledActiveTrackColor, required Color? disabledInactiveTrackColor, - required Color? disabledActiveDivisorColor, - required Color? disabledInactiveDivisorColor, + required Color? disabledActiveDividerColor, + required Color? disabledInactiveDividerColor, required Color disabledThumbColor, required this.activeRegionColor, required this.inactiveRegionColor, @@ -316,11 +318,11 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { required double? trackCornerRadius, required double overlayRadius, required double thumbRadius, - required double? activeDivisorRadius, - required double? inactiveDivisorRadius, + required double? activeDividerRadius, + required double? inactiveDividerRadius, required double? thumbStrokeWidth, - required double? activeDivisorStrokeWidth, - required double? inactiveDivisorStrokeWidth, + required double? activeDividerStrokeWidth, + required double? inactiveDividerStrokeWidth, }) : super.raw( brightness: brightness, activeTrackHeight: activeTrackHeight, @@ -334,13 +336,13 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { tooltipTextStyle: tooltipTextStyle, inactiveTrackColor: inactiveTrackColor, activeTrackColor: activeTrackColor, - inactiveDivisorColor: inactiveDivisorColor, - activeDivisorColor: activeDivisorColor, + inactiveDividerColor: inactiveDividerColor, + activeDividerColor: activeDividerColor, thumbColor: thumbColor, thumbStrokeColor: thumbStrokeColor, overlappingThumbStrokeColor: overlappingThumbStrokeColor, - activeDivisorStrokeColor: activeDivisorStrokeColor, - inactiveDivisorStrokeColor: inactiveDivisorStrokeColor, + activeDividerStrokeColor: activeDividerStrokeColor, + inactiveDividerStrokeColor: inactiveDividerStrokeColor, overlayColor: overlayColor, activeTickColor: activeTickColor, inactiveTickColor: inactiveTickColor, @@ -352,18 +354,18 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { disabledInactiveMinorTickColor: disabledInactiveMinorTickColor, disabledActiveTrackColor: disabledActiveTrackColor, disabledInactiveTrackColor: disabledInactiveTrackColor, - disabledActiveDivisorColor: disabledActiveDivisorColor, - disabledInactiveDivisorColor: disabledInactiveDivisorColor, + disabledActiveDividerColor: disabledActiveDividerColor, + disabledInactiveDividerColor: disabledInactiveDividerColor, disabledThumbColor: disabledThumbColor, tooltipBackgroundColor: tooltipBackgroundColor, overlappingTooltipStrokeColor: overlappingTooltipStrokeColor, overlayRadius: overlayRadius, thumbRadius: thumbRadius, - activeDivisorRadius: activeDivisorRadius, - inactiveDivisorRadius: inactiveDivisorRadius, + activeDividerRadius: activeDividerRadius, + inactiveDividerRadius: inactiveDividerRadius, thumbStrokeWidth: thumbStrokeWidth, - activeDivisorStrokeWidth: activeDivisorStrokeWidth, - inactiveDivisorStrokeWidth: inactiveDivisorStrokeWidth, + activeDividerStrokeWidth: activeDividerStrokeWidth, + inactiveDividerStrokeWidth: inactiveDividerStrokeWidth, trackCornerRadius: trackCornerRadius); /// Specifies the color for the active region of the @@ -461,8 +463,8 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { Color? thumbColor, Color? thumbStrokeColor, Color? overlappingThumbStrokeColor, - Color? activeDivisorStrokeColor, - Color? inactiveDivisorStrokeColor, + Color? activeDividerStrokeColor, + Color? inactiveDividerStrokeColor, Color? activeTickColor, Color? inactiveTickColor, Color? disabledActiveTickColor, @@ -472,12 +474,12 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { Color? disabledActiveMinorTickColor, Color? disabledInactiveMinorTickColor, Color? overlayColor, - Color? inactiveDivisorColor, - Color? activeDivisorColor, + Color? inactiveDividerColor, + Color? activeDividerColor, Color? disabledActiveTrackColor, Color? disabledInactiveTrackColor, - Color? disabledActiveDivisorColor, - Color? disabledInactiveDivisorColor, + Color? disabledActiveDividerColor, + Color? disabledInactiveDividerColor, Color? disabledThumbColor, Color? activeRegionColor, Color? inactiveRegionColor, @@ -486,11 +488,11 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { double? trackCornerRadius, double? overlayRadius, double? thumbRadius, - double? activeDivisorRadius, - double? inactiveDivisorRadius, + double? activeDividerRadius, + double? inactiveDividerRadius, double? thumbStrokeWidth, - double? activeDivisorStrokeWidth, - double? inactiveDivisorStrokeWidth, + double? activeDividerStrokeWidth, + double? inactiveDividerStrokeWidth, }) { return SfRangeSelectorThemeData.raw( brightness: brightness ?? this.brightness, @@ -509,10 +511,10 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { thumbStrokeColor: thumbStrokeColor ?? this.thumbStrokeColor, overlappingThumbStrokeColor: overlappingThumbStrokeColor ?? this.overlappingThumbStrokeColor, - activeDivisorStrokeColor: - activeDivisorStrokeColor ?? this.activeDivisorStrokeColor, - inactiveDivisorStrokeColor: - inactiveDivisorStrokeColor ?? this.inactiveDivisorStrokeColor, + activeDividerStrokeColor: + activeDividerStrokeColor ?? this.activeDividerStrokeColor, + inactiveDividerStrokeColor: + inactiveDividerStrokeColor ?? this.inactiveDividerStrokeColor, activeTickColor: activeTickColor ?? this.activeTickColor, inactiveTickColor: inactiveTickColor ?? this.inactiveTickColor, disabledActiveTickColor: @@ -527,16 +529,16 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { disabledInactiveMinorTickColor: disabledInactiveMinorTickColor ?? this.disabledInactiveMinorTickColor, overlayColor: overlayColor ?? this.overlayColor, - inactiveDivisorColor: inactiveDivisorColor ?? this.inactiveDivisorColor, - activeDivisorColor: activeDivisorColor ?? this.activeDivisorColor, + inactiveDividerColor: inactiveDividerColor ?? this.inactiveDividerColor, + activeDividerColor: activeDividerColor ?? this.activeDividerColor, disabledActiveTrackColor: disabledActiveTrackColor ?? this.disabledActiveTrackColor, disabledInactiveTrackColor: disabledInactiveTrackColor ?? this.disabledInactiveTrackColor, - disabledActiveDivisorColor: - disabledActiveDivisorColor ?? this.disabledActiveDivisorColor, - disabledInactiveDivisorColor: - disabledInactiveDivisorColor ?? this.disabledInactiveDivisorColor, + disabledActiveDividerColor: + disabledActiveDividerColor ?? this.disabledActiveDividerColor, + disabledInactiveDividerColor: + disabledInactiveDividerColor ?? this.disabledInactiveDividerColor, disabledThumbColor: disabledThumbColor ?? this.disabledThumbColor, activeRegionColor: activeRegionColor ?? this.activeRegionColor, inactiveRegionColor: inactiveRegionColor ?? this.inactiveRegionColor, @@ -547,14 +549,14 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { trackCornerRadius: trackCornerRadius ?? this.trackCornerRadius, overlayRadius: overlayRadius ?? this.overlayRadius, thumbRadius: thumbRadius ?? this.thumbRadius, - activeDivisorRadius: activeDivisorRadius ?? this.activeDivisorRadius, - inactiveDivisorRadius: - inactiveDivisorRadius ?? this.inactiveDivisorRadius, + activeDividerRadius: activeDividerRadius ?? this.activeDividerRadius, + inactiveDividerRadius: + inactiveDividerRadius ?? this.inactiveDividerRadius, thumbStrokeWidth: thumbStrokeWidth ?? this.thumbStrokeWidth, - activeDivisorStrokeWidth: - activeDivisorStrokeWidth ?? this.activeDivisorStrokeWidth, - inactiveDivisorStrokeWidth: - inactiveDivisorStrokeWidth ?? this.inactiveDivisorStrokeWidth, + activeDividerStrokeWidth: + activeDividerStrokeWidth ?? this.activeDividerStrokeWidth, + inactiveDividerStrokeWidth: + inactiveDividerStrokeWidth ?? this.inactiveDividerStrokeWidth, ); } @@ -588,10 +590,10 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { thumbStrokeColor: Color.lerp(a.thumbStrokeColor, b.thumbStrokeColor, t), overlappingThumbStrokeColor: Color.lerp( a.overlappingThumbStrokeColor, b.overlappingThumbStrokeColor, t), - activeDivisorStrokeColor: Color.lerp( - a.activeDivisorStrokeColor, b.activeDivisorStrokeColor, t), - inactiveDivisorStrokeColor: Color.lerp( - a.inactiveDivisorStrokeColor, b.inactiveDivisorStrokeColor, t), + activeDividerStrokeColor: Color.lerp( + a.activeDividerStrokeColor, b.activeDividerStrokeColor, t), + inactiveDividerStrokeColor: Color.lerp( + a.inactiveDividerStrokeColor, b.inactiveDividerStrokeColor, t), activeTickColor: Color.lerp(a.activeTickColor, b.activeTickColor, t), inactiveTickColor: Color.lerp(a.inactiveTickColor, b.inactiveTickColor, t), @@ -610,18 +612,18 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { b.disabledInactiveMinorTickColor, t), overlayColor: Color.lerp(a.overlayColor, b.overlayColor, t), - inactiveDivisorColor: - Color.lerp(a.inactiveDivisorColor, b.inactiveDivisorColor, t), - activeDivisorColor: - Color.lerp(a.activeDivisorColor, b.activeDivisorColor, t), + inactiveDividerColor: + Color.lerp(a.inactiveDividerColor, b.inactiveDividerColor, t), + activeDividerColor: + Color.lerp(a.activeDividerColor, b.activeDividerColor, t), disabledActiveTrackColor: Color.lerp( a.disabledActiveTrackColor, b.disabledActiveTrackColor, t), disabledInactiveTrackColor: Color.lerp( a.disabledInactiveTrackColor, b.disabledInactiveTrackColor, t), - disabledActiveDivisorColor: Color.lerp( - a.disabledActiveDivisorColor, b.disabledActiveDivisorColor, t), - disabledInactiveDivisorColor: Color.lerp( - a.disabledInactiveDivisorColor, b.disabledInactiveDivisorColor, t), + disabledActiveDividerColor: Color.lerp( + a.disabledActiveDividerColor, b.disabledActiveDividerColor, t), + disabledInactiveDividerColor: Color.lerp( + a.disabledInactiveDividerColor, b.disabledInactiveDividerColor, t), disabledThumbColor: Color.lerp(a.disabledThumbColor, b.disabledThumbColor, t), activeRegionColor: @@ -630,15 +632,21 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { Color.lerp(a.inactiveRegionColor, b.inactiveRegionColor, t), tooltipBackgroundColor: Color.lerp(a.tooltipBackgroundColor, b.tooltipBackgroundColor, t), + // ignore: lines_longer_than_80_chars overlappingTooltipStrokeColor: Color.lerp(a.overlappingTooltipStrokeColor, b.overlappingTooltipStrokeColor, t), + // ignore: lines_longer_than_80_chars trackCornerRadius: lerpDouble(a.trackCornerRadius, b.trackCornerRadius, t), overlayRadius: lerpDouble(a.overlayRadius, b.overlayRadius, t), thumbRadius: lerpDouble(a.thumbRadius, b.thumbRadius, t), - activeDivisorRadius: lerpDouble(a.activeDivisorRadius, b.activeDivisorRadius, t), - inactiveDivisorRadius: lerpDouble(a.inactiveDivisorRadius, b.inactiveDivisorRadius, t), + // ignore: lines_longer_than_80_chars + activeDividerRadius: lerpDouble(a.activeDividerRadius, b.activeDividerRadius, t), + // ignore: lines_longer_than_80_chars + inactiveDividerRadius: lerpDouble(a.inactiveDividerRadius, b.inactiveDividerRadius, t), thumbStrokeWidth: lerpDouble(a.thumbStrokeWidth, b.thumbStrokeWidth, t), - activeDivisorStrokeWidth: lerpDouble(a.activeDivisorStrokeWidth, b.activeDivisorStrokeWidth, t), - inactiveDivisorStrokeWidth: lerpDouble(a.inactiveDivisorStrokeWidth, b.inactiveDivisorStrokeWidth, t)); + // ignore: lines_longer_than_80_chars + activeDividerStrokeWidth: lerpDouble(a.activeDividerStrokeWidth, b.activeDividerStrokeWidth, t), + // ignore: lines_longer_than_80_chars + inactiveDividerStrokeWidth: lerpDouble(a.inactiveDividerStrokeWidth, b.inactiveDividerStrokeWidth, t)); } @override @@ -666,8 +674,8 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { other.thumbColor == thumbColor && other.thumbStrokeColor == thumbStrokeColor && other.overlappingThumbStrokeColor == overlappingThumbStrokeColor && - other.activeDivisorStrokeColor == activeDivisorStrokeColor && - other.inactiveDivisorStrokeColor == inactiveDivisorStrokeColor && + other.activeDividerStrokeColor == activeDividerStrokeColor && + other.inactiveDividerStrokeColor == inactiveDividerStrokeColor && other.activeTickColor == activeTickColor && other.inactiveTickColor == inactiveTickColor && other.disabledActiveTickColor == disabledActiveTickColor && @@ -678,12 +686,12 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { other.disabledInactiveMinorTickColor == disabledInactiveMinorTickColor && other.overlayColor == overlayColor && - other.inactiveDivisorColor == inactiveDivisorColor && - other.activeDivisorColor == activeDivisorColor && + other.inactiveDividerColor == inactiveDividerColor && + other.activeDividerColor == activeDividerColor && other.disabledActiveTrackColor == disabledActiveTrackColor && other.disabledInactiveTrackColor == disabledInactiveTrackColor && - other.disabledActiveDivisorColor == disabledActiveDivisorColor && - other.disabledInactiveDivisorColor == disabledInactiveDivisorColor && + other.disabledActiveDividerColor == disabledActiveDividerColor && + other.disabledInactiveDividerColor == disabledInactiveDividerColor && other.disabledThumbColor == disabledThumbColor && other.activeRegionColor == activeRegionColor && other.inactiveRegionColor == inactiveRegionColor && @@ -692,11 +700,11 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { other.trackCornerRadius == trackCornerRadius && other.overlayRadius == overlayRadius && other.thumbRadius == thumbRadius && - other.activeDivisorRadius == activeDivisorRadius && - other.inactiveDivisorRadius == inactiveDivisorRadius && + other.activeDividerRadius == activeDividerRadius && + other.inactiveDividerRadius == inactiveDividerRadius && other.thumbStrokeWidth == thumbStrokeWidth && - other.activeDivisorStrokeWidth == activeDivisorStrokeWidth && - other.inactiveDivisorStrokeWidth == inactiveDivisorStrokeWidth; + other.activeDividerStrokeWidth == activeDividerStrokeWidth && + other.inactiveDividerStrokeWidth == inactiveDividerStrokeWidth; } @override @@ -717,8 +725,8 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { thumbColor, thumbStrokeColor, overlappingThumbStrokeColor, - activeDivisorStrokeColor, - inactiveDivisorStrokeColor, + activeDividerStrokeColor, + inactiveDividerStrokeColor, activeTickColor, inactiveTickColor, disabledActiveTickColor, @@ -728,12 +736,12 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { disabledActiveMinorTickColor, disabledInactiveMinorTickColor, overlayColor, - inactiveDivisorColor, - activeDivisorColor, + inactiveDividerColor, + activeDividerColor, disabledActiveTrackColor, disabledInactiveTrackColor, - disabledActiveDivisorColor, - disabledInactiveDivisorColor, + disabledActiveDividerColor, + disabledInactiveDividerColor, disabledThumbColor, activeRegionColor, inactiveRegionColor, @@ -741,12 +749,12 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { overlappingTooltipStrokeColor, trackCornerRadius, overlayRadius, - activeDivisorRadius, - inactiveDivisorRadius, + activeDividerRadius, + inactiveDividerRadius, thumbRadius, thumbStrokeWidth, - activeDivisorStrokeWidth, - inactiveDivisorStrokeWidth, + activeDividerStrokeWidth, + inactiveDividerStrokeWidth, ]); } @@ -789,11 +797,11 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { 'overlappingThumbStrokeColor', overlappingThumbStrokeColor, defaultValue: defaultData.overlappingThumbStrokeColor)); properties.add(ColorProperty( - 'activeDivisorStrokeColor', activeDivisorStrokeColor, - defaultValue: defaultData.activeDivisorStrokeColor)); + 'activeDividerStrokeColor', activeDividerStrokeColor, + defaultValue: defaultData.activeDividerStrokeColor)); properties.add(ColorProperty( - 'inactiveDivisorStrokeColor', inactiveDivisorStrokeColor, - defaultValue: defaultData.inactiveDivisorStrokeColor)); + 'inactiveDividerStrokeColor', inactiveDividerStrokeColor, + defaultValue: defaultData.inactiveDividerStrokeColor)); properties.add(ColorProperty('activeTickColor', activeTickColor, defaultValue: defaultData.activeTickColor)); properties.add(ColorProperty('inactiveTickColor', inactiveTickColor, @@ -817,10 +825,10 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { defaultValue: defaultData.disabledInactiveMinorTickColor)); properties.add(ColorProperty('overlayColor', overlayColor, defaultValue: defaultData.overlayColor)); - properties.add(ColorProperty('inactiveDivisorColor', inactiveDivisorColor, - defaultValue: defaultData.inactiveDivisorColor)); - properties.add(ColorProperty('activeDivisorColor', activeDivisorColor, - defaultValue: defaultData.activeDivisorColor)); + properties.add(ColorProperty('inactiveDividerColor', inactiveDividerColor, + defaultValue: defaultData.inactiveDividerColor)); + properties.add(ColorProperty('activeDividerColor', activeDividerColor, + defaultValue: defaultData.activeDividerColor)); properties.add(ColorProperty( 'disabledActiveTrackColor', disabledActiveTrackColor, defaultValue: defaultData.disabledActiveTrackColor)); @@ -828,11 +836,11 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { 'disabledInactiveTrackColor', disabledInactiveTrackColor, defaultValue: defaultData.disabledInactiveTrackColor)); properties.add(ColorProperty( - 'disabledActiveDivisorColor', disabledActiveDivisorColor, - defaultValue: defaultData.disabledActiveDivisorColor)); + 'disabledActiveDividerColor', disabledActiveDividerColor, + defaultValue: defaultData.disabledActiveDividerColor)); properties.add(ColorProperty( - 'disabledInactiveDivisorColor', disabledInactiveDivisorColor, - defaultValue: defaultData.disabledInactiveDivisorColor)); + 'disabledInactiveDividerColor', disabledInactiveDividerColor, + defaultValue: defaultData.disabledInactiveDividerColor)); properties.add(ColorProperty('disabledThumbColor', disabledThumbColor, defaultValue: defaultData.disabledThumbColor)); properties.add(ColorProperty('activeRegionColor', activeRegionColor, @@ -851,18 +859,18 @@ class SfRangeSelectorThemeData extends SfRangeSliderThemeData { defaultValue: defaultData.overlayRadius)); properties.add(DoubleProperty('thumbRadius', thumbRadius, defaultValue: defaultData.thumbRadius)); - properties.add(DoubleProperty('activeDivisorRadius', activeDivisorRadius, - defaultValue: defaultData.activeDivisorRadius)); + properties.add(DoubleProperty('activeDividerRadius', activeDividerRadius, + defaultValue: defaultData.activeDividerRadius)); properties.add(DoubleProperty( - 'inactiveDivisorRadius', inactiveDivisorRadius, - defaultValue: defaultData.inactiveDivisorRadius)); + 'inactiveDividerRadius', inactiveDividerRadius, + defaultValue: defaultData.inactiveDividerRadius)); properties.add(DoubleProperty('thumbStrokeWidth', thumbStrokeWidth, defaultValue: defaultData.thumbStrokeWidth)); properties.add(DoubleProperty( - 'activeDivisorStrokeWidth', activeDivisorStrokeWidth, - defaultValue: defaultData.activeDivisorStrokeWidth)); + 'activeDividerStrokeWidth', activeDividerStrokeWidth, + defaultValue: defaultData.activeDividerStrokeWidth)); properties.add(DoubleProperty( - 'inactiveDivisorStrokeWidth', inactiveDivisorStrokeWidth, - defaultValue: defaultData.inactiveDivisorStrokeWidth)); + 'inactiveDividerStrokeWidth', inactiveDividerStrokeWidth, + defaultValue: defaultData.inactiveDividerStrokeWidth)); } } diff --git a/packages/syncfusion_flutter_core/lib/src/theme/range_slider_theme.dart b/packages/syncfusion_flutter_core/lib/src/theme/range_slider_theme.dart index c531e4e7a..f095d442d 100644 --- a/packages/syncfusion_flutter_core/lib/src/theme/range_slider_theme.dart +++ b/packages/syncfusion_flutter_core/lib/src/theme/range_slider_theme.dart @@ -1,6 +1,8 @@ import 'dart:ui'; + import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; + import '../../theme.dart'; /// Applies a theme to descendant Syncfusion range slider widgets. @@ -62,7 +64,7 @@ class SfRangeSliderTheme extends InheritedTheme { /// left thumb, and the right thumb and the [max] value. /// For RTL, the inactive side of the range slider is between /// the [max] value and the left thumb, and the right thumb and the [min] value. -/// * The "divisors", which is a shape that renders on the track based on +/// * The "dividers", which is a shape that renders on the track based on /// the given [interval] value. /// * The "ticks", which is a shape that rendered based on /// given [interval] value. @@ -110,12 +112,12 @@ class SfRangeSliderThemeData extends SfSliderThemeData { Color? disabledActiveMinorTickColor, Color? disabledInactiveMinorTickColor, Color? overlayColor, - Color? inactiveDivisorColor, - Color? activeDivisorColor, + Color? inactiveDividerColor, + Color? activeDividerColor, Color? disabledActiveTrackColor, Color? disabledInactiveTrackColor, - Color? disabledActiveDivisorColor, - Color? disabledInactiveDivisorColor, + Color? disabledActiveDividerColor, + Color? disabledInactiveDividerColor, Color? disabledThumbColor, Color? activeRegionColor, Color? inactiveRegionColor, @@ -123,16 +125,16 @@ class SfRangeSliderThemeData extends SfSliderThemeData { Color? overlappingTooltipStrokeColor, Color? thumbStrokeColor, Color? overlappingThumbStrokeColor, - Color? activeDivisorStrokeColor, - Color? inactiveDivisorStrokeColor, + Color? activeDividerStrokeColor, + Color? inactiveDividerStrokeColor, double? trackCornerRadius, double? overlayRadius, double? thumbRadius, - double? activeDivisorRadius, - double? inactiveDivisorRadius, + double? activeDividerRadius, + double? inactiveDividerRadius, double? thumbStrokeWidth, - double? activeDivisorStrokeWidth, - double? inactiveDivisorStrokeWidth}) { + double? activeDividerStrokeWidth, + double? inactiveDividerStrokeWidth}) { brightness = brightness ?? Brightness.light; final bool isLight = brightness == Brightness.light; activeTrackHeight ??= 6.0; @@ -168,13 +170,13 @@ class SfRangeSliderThemeData extends SfSliderThemeData { tooltipTextStyle: tooltipTextStyle, inactiveTrackColor: inactiveTrackColor, activeTrackColor: activeTrackColor, - inactiveDivisorColor: inactiveDivisorColor, - activeDivisorColor: activeDivisorColor, + inactiveDividerColor: inactiveDividerColor, + activeDividerColor: activeDividerColor, thumbColor: thumbColor, thumbStrokeColor: thumbStrokeColor, overlappingThumbStrokeColor: overlappingThumbStrokeColor, - activeDivisorStrokeColor: activeDivisorStrokeColor, - inactiveDivisorStrokeColor: inactiveDivisorStrokeColor, + activeDividerStrokeColor: activeDividerStrokeColor, + inactiveDividerStrokeColor: inactiveDividerStrokeColor, overlayColor: overlayColor, activeTickColor: activeTickColor, inactiveTickColor: inactiveTickColor, @@ -186,18 +188,18 @@ class SfRangeSliderThemeData extends SfSliderThemeData { disabledInactiveMinorTickColor: disabledInactiveMinorTickColor, disabledActiveTrackColor: disabledActiveTrackColor, disabledInactiveTrackColor: disabledInactiveTrackColor, - disabledActiveDivisorColor: disabledActiveDivisorColor, - disabledInactiveDivisorColor: disabledInactiveDivisorColor, + disabledActiveDividerColor: disabledActiveDividerColor, + disabledInactiveDividerColor: disabledInactiveDividerColor, disabledThumbColor: disabledThumbColor, tooltipBackgroundColor: tooltipBackgroundColor, overlappingTooltipStrokeColor: overlappingTooltipStrokeColor, overlayRadius: overlayRadius, thumbRadius: thumbRadius, - activeDivisorRadius: activeDivisorRadius, - inactiveDivisorRadius: inactiveDivisorRadius, + activeDividerRadius: activeDividerRadius, + inactiveDividerRadius: inactiveDividerRadius, thumbStrokeWidth: thumbStrokeWidth, - activeDivisorStrokeWidth: activeDivisorStrokeWidth, - inactiveDivisorStrokeWidth: inactiveDivisorStrokeWidth, + activeDividerStrokeWidth: activeDividerStrokeWidth, + inactiveDividerStrokeWidth: inactiveDividerStrokeWidth, trackCornerRadius: trackCornerRadius); } @@ -223,8 +225,8 @@ class SfRangeSliderThemeData extends SfSliderThemeData { required Color? thumbColor, required Color? thumbStrokeColor, required this.overlappingThumbStrokeColor, - required Color? activeDivisorStrokeColor, - required Color? inactiveDivisorStrokeColor, + required Color? activeDividerStrokeColor, + required Color? inactiveDividerStrokeColor, required Color activeTickColor, required Color inactiveTickColor, required Color disabledActiveTickColor, @@ -234,23 +236,23 @@ class SfRangeSliderThemeData extends SfSliderThemeData { required Color disabledActiveMinorTickColor, required Color disabledInactiveMinorTickColor, required Color? overlayColor, - required Color? inactiveDivisorColor, - required Color? activeDivisorColor, + required Color? inactiveDividerColor, + required Color? activeDividerColor, required Color? disabledActiveTrackColor, required Color? disabledInactiveTrackColor, - required Color? disabledActiveDivisorColor, - required Color? disabledInactiveDivisorColor, + required Color? disabledActiveDividerColor, + required Color? disabledInactiveDividerColor, required Color disabledThumbColor, required Color? tooltipBackgroundColor, required this.overlappingTooltipStrokeColor, required double? trackCornerRadius, required double overlayRadius, required double thumbRadius, - required double? activeDivisorRadius, - required double? inactiveDivisorRadius, + required double? activeDividerRadius, + required double? inactiveDividerRadius, required double? thumbStrokeWidth, - required double? activeDivisorStrokeWidth, - required double? inactiveDivisorStrokeWidth, + required double? activeDividerStrokeWidth, + required double? inactiveDividerStrokeWidth, }) : super.raw( brightness: brightness, activeTrackHeight: activeTrackHeight, @@ -264,12 +266,12 @@ class SfRangeSliderThemeData extends SfSliderThemeData { tooltipTextStyle: tooltipTextStyle, inactiveTrackColor: inactiveTrackColor, activeTrackColor: activeTrackColor, - inactiveDivisorColor: inactiveDivisorColor, - activeDivisorColor: activeDivisorColor, + inactiveDividerColor: inactiveDividerColor, + activeDividerColor: activeDividerColor, thumbColor: thumbColor, thumbStrokeColor: thumbStrokeColor, - activeDivisorStrokeColor: activeDivisorStrokeColor, - inactiveDivisorStrokeColor: inactiveDivisorStrokeColor, + activeDividerStrokeColor: activeDividerStrokeColor, + inactiveDividerStrokeColor: inactiveDividerStrokeColor, overlayColor: overlayColor, activeTickColor: activeTickColor, inactiveTickColor: inactiveTickColor, @@ -281,17 +283,17 @@ class SfRangeSliderThemeData extends SfSliderThemeData { disabledInactiveMinorTickColor: disabledInactiveMinorTickColor, disabledActiveTrackColor: disabledActiveTrackColor, disabledInactiveTrackColor: disabledInactiveTrackColor, - disabledActiveDivisorColor: disabledActiveDivisorColor, - disabledInactiveDivisorColor: disabledInactiveDivisorColor, + disabledActiveDividerColor: disabledActiveDividerColor, + disabledInactiveDividerColor: disabledInactiveDividerColor, disabledThumbColor: disabledThumbColor, tooltipBackgroundColor: tooltipBackgroundColor, overlayRadius: overlayRadius, thumbRadius: thumbRadius, - activeDivisorRadius: activeDivisorRadius, - inactiveDivisorRadius: inactiveDivisorRadius, + activeDividerRadius: activeDividerRadius, + inactiveDividerRadius: inactiveDividerRadius, thumbStrokeWidth: thumbStrokeWidth, - activeDivisorStrokeWidth: activeDivisorStrokeWidth, - inactiveDivisorStrokeWidth: inactiveDivisorStrokeWidth, + activeDividerStrokeWidth: activeDividerStrokeWidth, + inactiveDividerStrokeWidth: inactiveDividerStrokeWidth, trackCornerRadius: trackCornerRadius); /// Specifies the stroke color for the thumbs when they overlap in the @@ -375,8 +377,8 @@ class SfRangeSliderThemeData extends SfSliderThemeData { Color? thumbColor, Color? thumbStrokeColor, Color? overlappingThumbStrokeColor, - Color? activeDivisorStrokeColor, - Color? inactiveDivisorStrokeColor, + Color? activeDividerStrokeColor, + Color? inactiveDividerStrokeColor, Color? activeTickColor, Color? inactiveTickColor, Color? disabledActiveTickColor, @@ -386,12 +388,12 @@ class SfRangeSliderThemeData extends SfSliderThemeData { Color? disabledActiveMinorTickColor, Color? disabledInactiveMinorTickColor, Color? overlayColor, - Color? inactiveDivisorColor, - Color? activeDivisorColor, + Color? inactiveDividerColor, + Color? activeDividerColor, Color? disabledActiveTrackColor, Color? disabledInactiveTrackColor, - Color? disabledActiveDivisorColor, - Color? disabledInactiveDivisorColor, + Color? disabledActiveDividerColor, + Color? disabledInactiveDividerColor, Color? disabledThumbColor, Color? activeRegionColor, Color? inactiveRegionColor, @@ -400,11 +402,11 @@ class SfRangeSliderThemeData extends SfSliderThemeData { double? trackCornerRadius, double? overlayRadius, double? thumbRadius, - double? activeDivisorRadius, - double? inactiveDivisorRadius, + double? activeDividerRadius, + double? inactiveDividerRadius, double? thumbStrokeWidth, - double? activeDivisorStrokeWidth, - double? inactiveDivisorStrokeWidth, + double? activeDividerStrokeWidth, + double? inactiveDividerStrokeWidth, }) { return SfRangeSliderThemeData.raw( brightness: brightness ?? this.brightness, @@ -423,10 +425,10 @@ class SfRangeSliderThemeData extends SfSliderThemeData { thumbStrokeColor: thumbStrokeColor ?? this.thumbStrokeColor, overlappingThumbStrokeColor: overlappingThumbStrokeColor ?? this.overlappingThumbStrokeColor, - activeDivisorStrokeColor: - activeDivisorStrokeColor ?? this.activeDivisorStrokeColor, - inactiveDivisorStrokeColor: - inactiveDivisorStrokeColor ?? this.inactiveDivisorStrokeColor, + activeDividerStrokeColor: + activeDividerStrokeColor ?? this.activeDividerStrokeColor, + inactiveDividerStrokeColor: + inactiveDividerStrokeColor ?? this.inactiveDividerStrokeColor, activeTickColor: activeTickColor ?? this.activeTickColor, inactiveTickColor: inactiveTickColor ?? this.inactiveTickColor, disabledActiveTickColor: @@ -441,16 +443,16 @@ class SfRangeSliderThemeData extends SfSliderThemeData { disabledInactiveMinorTickColor: disabledInactiveMinorTickColor ?? this.disabledInactiveMinorTickColor, overlayColor: overlayColor ?? this.overlayColor, - inactiveDivisorColor: inactiveDivisorColor ?? this.inactiveDivisorColor, - activeDivisorColor: activeDivisorColor ?? this.activeDivisorColor, + inactiveDividerColor: inactiveDividerColor ?? this.inactiveDividerColor, + activeDividerColor: activeDividerColor ?? this.activeDividerColor, disabledActiveTrackColor: disabledActiveTrackColor ?? this.disabledActiveTrackColor, disabledInactiveTrackColor: disabledInactiveTrackColor ?? this.disabledInactiveTrackColor, - disabledActiveDivisorColor: - disabledActiveDivisorColor ?? this.disabledActiveDivisorColor, - disabledInactiveDivisorColor: - disabledInactiveDivisorColor ?? this.disabledInactiveDivisorColor, + disabledActiveDividerColor: + disabledActiveDividerColor ?? this.disabledActiveDividerColor, + disabledInactiveDividerColor: + disabledInactiveDividerColor ?? this.disabledInactiveDividerColor, disabledThumbColor: disabledThumbColor ?? this.disabledThumbColor, tooltipBackgroundColor: tooltipBackgroundColor ?? this.tooltipBackgroundColor, @@ -459,14 +461,14 @@ class SfRangeSliderThemeData extends SfSliderThemeData { trackCornerRadius: trackCornerRadius ?? this.trackCornerRadius, overlayRadius: overlayRadius ?? this.overlayRadius, thumbRadius: thumbRadius ?? this.thumbRadius, - activeDivisorRadius: activeDivisorRadius ?? this.activeDivisorRadius, - inactiveDivisorRadius: - inactiveDivisorRadius ?? this.inactiveDivisorRadius, + activeDividerRadius: activeDividerRadius ?? this.activeDividerRadius, + inactiveDividerRadius: + inactiveDividerRadius ?? this.inactiveDividerRadius, thumbStrokeWidth: thumbStrokeWidth ?? this.thumbStrokeWidth, - activeDivisorStrokeWidth: - activeDivisorStrokeWidth ?? this.activeDivisorStrokeWidth, - inactiveDivisorStrokeWidth: - inactiveDivisorStrokeWidth ?? this.inactiveDivisorStrokeWidth, + activeDividerStrokeWidth: + activeDividerStrokeWidth ?? this.activeDividerStrokeWidth, + inactiveDividerStrokeWidth: + inactiveDividerStrokeWidth ?? this.inactiveDividerStrokeWidth, ); } @@ -500,10 +502,10 @@ class SfRangeSliderThemeData extends SfSliderThemeData { thumbStrokeColor: Color.lerp(a.thumbStrokeColor, b.thumbStrokeColor, t), overlappingThumbStrokeColor: Color.lerp( a.overlappingThumbStrokeColor, b.overlappingThumbStrokeColor, t), - activeDivisorStrokeColor: Color.lerp( - a.activeDivisorStrokeColor, b.activeDivisorStrokeColor, t), - inactiveDivisorStrokeColor: Color.lerp( - a.inactiveDivisorStrokeColor, b.inactiveDivisorStrokeColor, t), + activeDividerStrokeColor: Color.lerp( + a.activeDividerStrokeColor, b.activeDividerStrokeColor, t), + inactiveDividerStrokeColor: Color.lerp( + a.inactiveDividerStrokeColor, b.inactiveDividerStrokeColor, t), activeTickColor: Color.lerp(a.activeTickColor, b.activeTickColor, t), inactiveTickColor: Color.lerp(a.inactiveTickColor, b.inactiveTickColor, t), @@ -522,32 +524,42 @@ class SfRangeSliderThemeData extends SfSliderThemeData { b.disabledInactiveMinorTickColor, t), overlayColor: Color.lerp(a.overlayColor, b.overlayColor, t), - inactiveDivisorColor: - Color.lerp(a.inactiveDivisorColor, b.inactiveDivisorColor, t), - activeDivisorColor: - Color.lerp(a.activeDivisorColor, b.activeDivisorColor, t), + inactiveDividerColor: + Color.lerp(a.inactiveDividerColor, b.inactiveDividerColor, t), + activeDividerColor: + Color.lerp(a.activeDividerColor, b.activeDividerColor, t), disabledActiveTrackColor: Color.lerp( a.disabledActiveTrackColor, b.disabledActiveTrackColor, t), disabledInactiveTrackColor: Color.lerp( a.disabledInactiveTrackColor, b.disabledInactiveTrackColor, t), - disabledActiveDivisorColor: Color.lerp( - a.disabledActiveDivisorColor, b.disabledActiveDivisorColor, t), - disabledInactiveDivisorColor: Color.lerp( - a.disabledInactiveDivisorColor, b.disabledInactiveDivisorColor, t), + disabledActiveDividerColor: Color.lerp( + a.disabledActiveDividerColor, b.disabledActiveDividerColor, t), + disabledInactiveDividerColor: Color.lerp( + a.disabledInactiveDividerColor, b.disabledInactiveDividerColor, t), disabledThumbColor: Color.lerp(a.disabledThumbColor, b.disabledThumbColor, t), tooltipBackgroundColor: Color.lerp(a.tooltipBackgroundColor, b.tooltipBackgroundColor, t), overlappingTooltipStrokeColor: Color.lerp( - a.overlappingTooltipStrokeColor, b.overlappingTooltipStrokeColor, t), - trackCornerRadius: lerpDouble(a.trackCornerRadius, b.trackCornerRadius, t), + // ignore: lines_longer_than_80_chars + a.overlappingTooltipStrokeColor, + b.overlappingTooltipStrokeColor, + t), + // ignore: lines_longer_than_80_chars + trackCornerRadius: + lerpDouble(a.trackCornerRadius, b.trackCornerRadius, t), overlayRadius: lerpDouble(a.overlayRadius, b.overlayRadius, t), thumbRadius: lerpDouble(a.thumbRadius, b.thumbRadius, t), - activeDivisorRadius: lerpDouble(a.activeDivisorRadius, b.activeDivisorRadius, t), - inactiveDivisorRadius: lerpDouble(a.inactiveDivisorRadius, b.inactiveDivisorRadius, t), + // ignore: lines_longer_than_80_chars + activeDividerRadius: + lerpDouble(a.activeDividerRadius, b.activeDividerRadius, t), + // ignore: lines_longer_than_80_chars + inactiveDividerRadius: lerpDouble(a.inactiveDividerRadius, b.inactiveDividerRadius, t), thumbStrokeWidth: lerpDouble(a.thumbStrokeWidth, b.thumbStrokeWidth, t), - activeDivisorStrokeWidth: lerpDouble(a.activeDivisorStrokeWidth, b.activeDivisorStrokeWidth, t), - inactiveDivisorStrokeWidth: lerpDouble(a.inactiveDivisorStrokeWidth, b.inactiveDivisorStrokeWidth, t)); + // ignore: lines_longer_than_80_chars + activeDividerStrokeWidth: lerpDouble(a.activeDividerStrokeWidth, b.activeDividerStrokeWidth, t), + // ignore: lines_longer_than_80_chars + inactiveDividerStrokeWidth: lerpDouble(a.inactiveDividerStrokeWidth, b.inactiveDividerStrokeWidth, t)); } @override @@ -575,8 +587,8 @@ class SfRangeSliderThemeData extends SfSliderThemeData { other.thumbColor == thumbColor && other.thumbStrokeColor == thumbStrokeColor && other.overlappingThumbStrokeColor == overlappingThumbStrokeColor && - other.activeDivisorStrokeColor == activeDivisorStrokeColor && - other.inactiveDivisorStrokeColor == inactiveDivisorStrokeColor && + other.activeDividerStrokeColor == activeDividerStrokeColor && + other.inactiveDividerStrokeColor == inactiveDividerStrokeColor && other.activeTickColor == activeTickColor && other.inactiveTickColor == inactiveTickColor && other.disabledActiveTickColor == disabledActiveTickColor && @@ -587,23 +599,23 @@ class SfRangeSliderThemeData extends SfSliderThemeData { other.disabledInactiveMinorTickColor == disabledInactiveMinorTickColor && other.overlayColor == overlayColor && - other.inactiveDivisorColor == inactiveDivisorColor && - other.activeDivisorColor == activeDivisorColor && + other.inactiveDividerColor == inactiveDividerColor && + other.activeDividerColor == activeDividerColor && other.disabledActiveTrackColor == disabledActiveTrackColor && other.disabledInactiveTrackColor == disabledInactiveTrackColor && - other.disabledActiveDivisorColor == disabledActiveDivisorColor && - other.disabledInactiveDivisorColor == disabledInactiveDivisorColor && + other.disabledActiveDividerColor == disabledActiveDividerColor && + other.disabledInactiveDividerColor == disabledInactiveDividerColor && other.disabledThumbColor == disabledThumbColor && other.tooltipBackgroundColor == tooltipBackgroundColor && other.overlappingTooltipStrokeColor == overlappingTooltipStrokeColor && other.trackCornerRadius == trackCornerRadius && other.overlayRadius == overlayRadius && other.thumbRadius == thumbRadius && - other.activeDivisorRadius == activeDivisorRadius && - other.inactiveDivisorRadius == inactiveDivisorRadius && + other.activeDividerRadius == activeDividerRadius && + other.inactiveDividerRadius == inactiveDividerRadius && other.thumbStrokeWidth == thumbStrokeWidth && - other.activeDivisorStrokeWidth == activeDivisorStrokeWidth && - other.inactiveDivisorStrokeWidth == inactiveDivisorStrokeWidth; + other.activeDividerStrokeWidth == activeDividerStrokeWidth && + other.inactiveDividerStrokeWidth == inactiveDividerStrokeWidth; } @override @@ -624,8 +636,8 @@ class SfRangeSliderThemeData extends SfSliderThemeData { thumbColor, thumbStrokeColor, overlappingThumbStrokeColor, - activeDivisorStrokeColor, - inactiveDivisorStrokeColor, + activeDividerStrokeColor, + inactiveDividerStrokeColor, activeTickColor, inactiveTickColor, disabledActiveTickColor, @@ -635,23 +647,23 @@ class SfRangeSliderThemeData extends SfSliderThemeData { disabledActiveMinorTickColor, disabledInactiveMinorTickColor, overlayColor, - inactiveDivisorColor, - activeDivisorColor, + inactiveDividerColor, + activeDividerColor, disabledActiveTrackColor, disabledInactiveTrackColor, - disabledActiveDivisorColor, - disabledInactiveDivisorColor, + disabledActiveDividerColor, + disabledInactiveDividerColor, disabledThumbColor, tooltipBackgroundColor, overlappingTooltipStrokeColor, trackCornerRadius, overlayRadius, - activeDivisorRadius, - inactiveDivisorRadius, + activeDividerRadius, + inactiveDividerRadius, thumbRadius, thumbStrokeWidth, - activeDivisorStrokeWidth, - inactiveDivisorStrokeWidth, + activeDividerStrokeWidth, + inactiveDividerStrokeWidth, ]); } @@ -694,11 +706,11 @@ class SfRangeSliderThemeData extends SfSliderThemeData { 'overlappingThumbStrokeColor', overlappingThumbStrokeColor, defaultValue: defaultData.overlappingThumbStrokeColor)); properties.add(ColorProperty( - 'activeDivisorStrokeColor', activeDivisorStrokeColor, - defaultValue: defaultData.activeDivisorStrokeColor)); + 'activeDividerStrokeColor', activeDividerStrokeColor, + defaultValue: defaultData.activeDividerStrokeColor)); properties.add(ColorProperty( - 'inactiveDivisorStrokeColor', inactiveDivisorStrokeColor, - defaultValue: defaultData.inactiveDivisorStrokeColor)); + 'inactiveDividerStrokeColor', inactiveDividerStrokeColor, + defaultValue: defaultData.inactiveDividerStrokeColor)); properties.add(ColorProperty('activeTickColor', activeTickColor, defaultValue: defaultData.activeTickColor)); properties.add(ColorProperty('inactiveTickColor', inactiveTickColor, @@ -722,10 +734,10 @@ class SfRangeSliderThemeData extends SfSliderThemeData { defaultValue: defaultData.disabledInactiveMinorTickColor)); properties.add(ColorProperty('overlayColor', overlayColor, defaultValue: defaultData.overlayColor)); - properties.add(ColorProperty('inactiveDivisorColor', inactiveDivisorColor, - defaultValue: defaultData.inactiveDivisorColor)); - properties.add(ColorProperty('activeDivisorColor', activeDivisorColor, - defaultValue: defaultData.activeDivisorColor)); + properties.add(ColorProperty('inactiveDividerColor', inactiveDividerColor, + defaultValue: defaultData.inactiveDividerColor)); + properties.add(ColorProperty('activeDividerColor', activeDividerColor, + defaultValue: defaultData.activeDividerColor)); properties.add(ColorProperty( 'disabledActiveTrackColor', disabledActiveTrackColor, defaultValue: defaultData.disabledActiveTrackColor)); @@ -733,11 +745,11 @@ class SfRangeSliderThemeData extends SfSliderThemeData { 'disabledInactiveTrackColor', disabledInactiveTrackColor, defaultValue: defaultData.disabledInactiveTrackColor)); properties.add(ColorProperty( - 'disabledActiveDivisorColor', disabledActiveDivisorColor, - defaultValue: defaultData.disabledActiveDivisorColor)); + 'disabledActiveDividerColor', disabledActiveDividerColor, + defaultValue: defaultData.disabledActiveDividerColor)); properties.add(ColorProperty( - 'disabledInactiveDivisorColor', disabledInactiveDivisorColor, - defaultValue: defaultData.disabledInactiveDivisorColor)); + 'disabledInactiveDividerColor', disabledInactiveDividerColor, + defaultValue: defaultData.disabledInactiveDividerColor)); properties.add(ColorProperty('disabledThumbColor', disabledThumbColor, defaultValue: defaultData.disabledThumbColor)); properties.add(ColorProperty( @@ -752,18 +764,18 @@ class SfRangeSliderThemeData extends SfSliderThemeData { defaultValue: defaultData.overlayRadius)); properties.add(DoubleProperty('thumbRadius', thumbRadius, defaultValue: defaultData.thumbRadius)); - properties.add(DoubleProperty('activeDivisorRadius', activeDivisorRadius, - defaultValue: defaultData.activeDivisorRadius)); + properties.add(DoubleProperty('activeDividerRadius', activeDividerRadius, + defaultValue: defaultData.activeDividerRadius)); properties.add(DoubleProperty( - 'inactiveDivisorRadius', inactiveDivisorRadius, - defaultValue: defaultData.inactiveDivisorRadius)); + 'inactiveDividerRadius', inactiveDividerRadius, + defaultValue: defaultData.inactiveDividerRadius)); properties.add(DoubleProperty('thumbStrokeWidth', thumbStrokeWidth, defaultValue: defaultData.thumbStrokeWidth)); properties.add(DoubleProperty( - 'activeDivisorStrokeWidth', activeDivisorStrokeWidth, - defaultValue: defaultData.activeDivisorStrokeWidth)); + 'activeDividerStrokeWidth', activeDividerStrokeWidth, + defaultValue: defaultData.activeDividerStrokeWidth)); properties.add(DoubleProperty( - 'inactiveDivisorStrokeWidth', inactiveDivisorStrokeWidth, - defaultValue: defaultData.inactiveDivisorStrokeWidth)); + 'inactiveDividerStrokeWidth', inactiveDividerStrokeWidth, + defaultValue: defaultData.inactiveDividerStrokeWidth)); } } diff --git a/packages/syncfusion_flutter_core/lib/src/theme/slider_theme.dart b/packages/syncfusion_flutter_core/lib/src/theme/slider_theme.dart index 327162c69..2ff383f23 100644 --- a/packages/syncfusion_flutter_core/lib/src/theme/slider_theme.dart +++ b/packages/syncfusion_flutter_core/lib/src/theme/slider_theme.dart @@ -1,6 +1,8 @@ import 'dart:ui'; + import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; + import '../../theme.dart'; /// Applies a theme to descendant Syncfusion slider widget. @@ -57,7 +59,7 @@ class SfSliderTheme extends InheritedTheme { /// * The "active" side of the slider is between the [min] value and thumb. /// * The "inactive" side of the slider is between the thumb and /// the [max] value. -/// * The "divisors", which is a shape that renders on the track based on +/// * The "dividers", which is a shape that renders on the track based on /// the given [interval] value. /// * The "ticks", which is a shape that rendered based on /// given [interval] value. @@ -77,6 +79,7 @@ class SfSliderTheme extends InheritedTheme { /// * [SfTheme](https://pub.dev/documentation/syncfusion_flutter_core/latest/theme/SfTheme-class.html) and /// [SfThemeData](https://pub.dev/documentation/syncfusion_flutter_core/latest/theme/SfThemeData-class.html), /// for customizing the visual appearance of the slider. +@immutable class SfSliderThemeData with Diagnosticable { /// Returns a new instance of [SfSliderThemeData.raw] for the given values. /// @@ -104,25 +107,25 @@ class SfSliderThemeData with Diagnosticable { Color? disabledActiveMinorTickColor, Color? disabledInactiveMinorTickColor, Color? overlayColor, - Color? inactiveDivisorColor, - Color? activeDivisorColor, + Color? inactiveDividerColor, + Color? activeDividerColor, Color? disabledActiveTrackColor, Color? disabledInactiveTrackColor, - Color? disabledActiveDivisorColor, - Color? disabledInactiveDivisorColor, + Color? disabledActiveDividerColor, + Color? disabledInactiveDividerColor, Color? disabledThumbColor, Color? tooltipBackgroundColor, Color? thumbStrokeColor, - Color? activeDivisorStrokeColor, - Color? inactiveDivisorStrokeColor, + Color? activeDividerStrokeColor, + Color? inactiveDividerStrokeColor, double? trackCornerRadius, double? overlayRadius, double? thumbRadius, - double? activeDivisorRadius, - double? inactiveDivisorRadius, + double? activeDividerRadius, + double? inactiveDividerRadius, double? thumbStrokeWidth, - double? activeDivisorStrokeWidth, - double? inactiveDivisorStrokeWidth}) { + double? activeDividerStrokeWidth, + double? inactiveDividerStrokeWidth}) { brightness = brightness ?? Brightness.light; activeTrackHeight ??= 6.0; inactiveTrackHeight ??= 4.0; @@ -151,12 +154,12 @@ class SfSliderThemeData with Diagnosticable { tooltipTextStyle: tooltipTextStyle, inactiveTrackColor: inactiveTrackColor, activeTrackColor: activeTrackColor, - inactiveDivisorColor: inactiveDivisorColor, - activeDivisorColor: activeDivisorColor, + inactiveDividerColor: inactiveDividerColor, + activeDividerColor: activeDividerColor, thumbColor: thumbColor, thumbStrokeColor: thumbStrokeColor, - activeDivisorStrokeColor: activeDivisorStrokeColor, - inactiveDivisorStrokeColor: inactiveDivisorStrokeColor, + activeDividerStrokeColor: activeDividerStrokeColor, + inactiveDividerStrokeColor: inactiveDividerStrokeColor, overlayColor: overlayColor, activeTickColor: activeTickColor, inactiveTickColor: inactiveTickColor, @@ -168,17 +171,17 @@ class SfSliderThemeData with Diagnosticable { disabledInactiveMinorTickColor: disabledInactiveMinorTickColor, disabledActiveTrackColor: disabledActiveTrackColor, disabledInactiveTrackColor: disabledInactiveTrackColor, - disabledActiveDivisorColor: disabledActiveDivisorColor, - disabledInactiveDivisorColor: disabledInactiveDivisorColor, + disabledActiveDividerColor: disabledActiveDividerColor, + disabledInactiveDividerColor: disabledInactiveDividerColor, disabledThumbColor: disabledThumbColor, tooltipBackgroundColor: tooltipBackgroundColor, overlayRadius: overlayRadius, thumbRadius: thumbRadius, - activeDivisorRadius: activeDivisorRadius, - inactiveDivisorRadius: inactiveDivisorRadius, + activeDividerRadius: activeDividerRadius, + inactiveDividerRadius: inactiveDividerRadius, thumbStrokeWidth: thumbStrokeWidth, - activeDivisorStrokeWidth: activeDivisorStrokeWidth, - inactiveDivisorStrokeWidth: inactiveDivisorStrokeWidth, + activeDividerStrokeWidth: activeDividerStrokeWidth, + inactiveDividerStrokeWidth: inactiveDividerStrokeWidth, trackCornerRadius: trackCornerRadius); } @@ -203,8 +206,8 @@ class SfSliderThemeData with Diagnosticable { required this.activeTrackColor, required this.thumbColor, required this.thumbStrokeColor, - required this.activeDivisorStrokeColor, - required this.inactiveDivisorStrokeColor, + required this.activeDividerStrokeColor, + required this.inactiveDividerStrokeColor, required this.activeTickColor, required this.inactiveTickColor, required this.disabledActiveTickColor, @@ -214,22 +217,22 @@ class SfSliderThemeData with Diagnosticable { required this.disabledActiveMinorTickColor, required this.disabledInactiveMinorTickColor, required this.overlayColor, - required this.inactiveDivisorColor, - required this.activeDivisorColor, + required this.inactiveDividerColor, + required this.activeDividerColor, required this.disabledActiveTrackColor, required this.disabledInactiveTrackColor, - required this.disabledActiveDivisorColor, - required this.disabledInactiveDivisorColor, + required this.disabledActiveDividerColor, + required this.disabledInactiveDividerColor, required this.disabledThumbColor, required this.tooltipBackgroundColor, required this.trackCornerRadius, required this.overlayRadius, required this.thumbRadius, - required this.activeDivisorRadius, - required this.inactiveDivisorRadius, + required this.activeDividerRadius, + required this.inactiveDividerRadius, required this.thumbStrokeWidth, - required this.activeDivisorStrokeWidth, - required this.inactiveDivisorStrokeWidth, + required this.activeDividerStrokeWidth, + required this.inactiveDividerStrokeWidth, }); /// Creates a copy of this theme but with the given fields @@ -249,8 +252,8 @@ class SfSliderThemeData with Diagnosticable { Color? activeTrackColor, Color? thumbColor, Color? thumbStrokeColor, - Color? activeDivisorStrokeColor, - Color? inactiveDivisorStrokeColor, + Color? activeDividerStrokeColor, + Color? inactiveDividerStrokeColor, Color? activeTickColor, Color? inactiveTickColor, Color? disabledActiveTickColor, @@ -260,12 +263,12 @@ class SfSliderThemeData with Diagnosticable { Color? disabledActiveMinorTickColor, Color? disabledInactiveMinorTickColor, Color? overlayColor, - Color? inactiveDivisorColor, - Color? activeDivisorColor, + Color? inactiveDividerColor, + Color? activeDividerColor, Color? disabledActiveTrackColor, Color? disabledInactiveTrackColor, - Color? disabledActiveDivisorColor, - Color? disabledInactiveDivisorColor, + Color? disabledActiveDividerColor, + Color? disabledInactiveDividerColor, Color? disabledThumbColor, Color? activeRegionColor, Color? inactiveRegionColor, @@ -273,11 +276,11 @@ class SfSliderThemeData with Diagnosticable { double? trackCornerRadius, double? overlayRadius, double? thumbRadius, - double? activeDivisorRadius, - double? inactiveDivisorRadius, + double? activeDividerRadius, + double? inactiveDividerRadius, double? thumbStrokeWidth, - double? activeDivisorStrokeWidth, - double? inactiveDivisorStrokeWidth, + double? activeDividerStrokeWidth, + double? inactiveDividerStrokeWidth, }) { return SfSliderThemeData.raw( brightness: brightness ?? this.brightness, @@ -294,10 +297,10 @@ class SfSliderThemeData with Diagnosticable { activeTrackColor: activeTrackColor ?? this.activeTrackColor, thumbColor: thumbColor ?? this.thumbColor, thumbStrokeColor: thumbStrokeColor ?? this.thumbStrokeColor, - activeDivisorStrokeColor: - activeDivisorStrokeColor ?? this.activeDivisorStrokeColor, - inactiveDivisorStrokeColor: - inactiveDivisorStrokeColor ?? this.inactiveDivisorStrokeColor, + activeDividerStrokeColor: + activeDividerStrokeColor ?? this.activeDividerStrokeColor, + inactiveDividerStrokeColor: + inactiveDividerStrokeColor ?? this.inactiveDividerStrokeColor, activeTickColor: activeTickColor ?? this.activeTickColor, inactiveTickColor: inactiveTickColor ?? this.inactiveTickColor, disabledActiveTickColor: @@ -312,30 +315,30 @@ class SfSliderThemeData with Diagnosticable { disabledInactiveMinorTickColor: disabledInactiveMinorTickColor ?? this.disabledInactiveMinorTickColor, overlayColor: overlayColor ?? this.overlayColor, - inactiveDivisorColor: inactiveDivisorColor ?? this.inactiveDivisorColor, - activeDivisorColor: activeDivisorColor ?? this.activeDivisorColor, + inactiveDividerColor: inactiveDividerColor ?? this.inactiveDividerColor, + activeDividerColor: activeDividerColor ?? this.activeDividerColor, disabledActiveTrackColor: disabledActiveTrackColor ?? this.disabledActiveTrackColor, disabledInactiveTrackColor: disabledInactiveTrackColor ?? this.disabledInactiveTrackColor, - disabledActiveDivisorColor: - disabledActiveDivisorColor ?? this.disabledActiveDivisorColor, - disabledInactiveDivisorColor: - disabledInactiveDivisorColor ?? this.disabledInactiveDivisorColor, + disabledActiveDividerColor: + disabledActiveDividerColor ?? this.disabledActiveDividerColor, + disabledInactiveDividerColor: + disabledInactiveDividerColor ?? this.disabledInactiveDividerColor, disabledThumbColor: disabledThumbColor ?? this.disabledThumbColor, tooltipBackgroundColor: tooltipBackgroundColor ?? this.tooltipBackgroundColor, trackCornerRadius: trackCornerRadius ?? this.trackCornerRadius, overlayRadius: overlayRadius ?? this.overlayRadius, thumbRadius: thumbRadius ?? this.thumbRadius, - activeDivisorRadius: activeDivisorRadius ?? this.activeDivisorRadius, - inactiveDivisorRadius: - inactiveDivisorRadius ?? this.inactiveDivisorRadius, + activeDividerRadius: activeDividerRadius ?? this.activeDividerRadius, + inactiveDividerRadius: + inactiveDividerRadius ?? this.inactiveDividerRadius, thumbStrokeWidth: thumbStrokeWidth ?? this.thumbStrokeWidth, - activeDivisorStrokeWidth: - activeDivisorStrokeWidth ?? this.activeDivisorStrokeWidth, - inactiveDivisorStrokeWidth: - inactiveDivisorStrokeWidth ?? this.inactiveDivisorStrokeWidth, + activeDividerStrokeWidth: + activeDividerStrokeWidth ?? this.activeDividerStrokeWidth, + inactiveDividerStrokeWidth: + inactiveDividerStrokeWidth ?? this.inactiveDividerStrokeWidth, ); } @@ -367,10 +370,10 @@ class SfSliderThemeData with Diagnosticable { activeTrackColor: Color.lerp(a.activeTrackColor, b.activeTrackColor, t), thumbColor: Color.lerp(a.thumbColor, b.thumbColor, t), thumbStrokeColor: Color.lerp(a.thumbStrokeColor, b.thumbStrokeColor, t), - activeDivisorStrokeColor: Color.lerp( - a.activeDivisorStrokeColor, b.activeDivisorStrokeColor, t), - inactiveDivisorStrokeColor: Color.lerp( - a.inactiveDivisorStrokeColor, b.inactiveDivisorStrokeColor, t), + activeDividerStrokeColor: Color.lerp( + a.activeDividerStrokeColor, b.activeDividerStrokeColor, t), + inactiveDividerStrokeColor: Color.lerp( + a.inactiveDividerStrokeColor, b.inactiveDividerStrokeColor, t), activeTickColor: Color.lerp(a.activeTickColor, b.activeTickColor, t), inactiveTickColor: Color.lerp(a.inactiveTickColor, b.inactiveTickColor, t), @@ -389,18 +392,18 @@ class SfSliderThemeData with Diagnosticable { b.disabledInactiveMinorTickColor, t), overlayColor: Color.lerp(a.overlayColor, b.overlayColor, t), - inactiveDivisorColor: - Color.lerp(a.inactiveDivisorColor, b.inactiveDivisorColor, t), - activeDivisorColor: - Color.lerp(a.activeDivisorColor, b.activeDivisorColor, t), + inactiveDividerColor: + Color.lerp(a.inactiveDividerColor, b.inactiveDividerColor, t), + activeDividerColor: + Color.lerp(a.activeDividerColor, b.activeDividerColor, t), disabledActiveTrackColor: Color.lerp( a.disabledActiveTrackColor, b.disabledActiveTrackColor, t), disabledInactiveTrackColor: Color.lerp( a.disabledInactiveTrackColor, b.disabledInactiveTrackColor, t), - disabledActiveDivisorColor: Color.lerp( - a.disabledActiveDivisorColor, b.disabledActiveDivisorColor, t), - disabledInactiveDivisorColor: Color.lerp( - a.disabledInactiveDivisorColor, b.disabledInactiveDivisorColor, t), + disabledActiveDividerColor: Color.lerp( + a.disabledActiveDividerColor, b.disabledActiveDividerColor, t), + disabledInactiveDividerColor: Color.lerp( + a.disabledInactiveDividerColor, b.disabledInactiveDividerColor, t), disabledThumbColor: Color.lerp(a.disabledThumbColor, b.disabledThumbColor, t), tooltipBackgroundColor: @@ -409,14 +412,16 @@ class SfSliderThemeData with Diagnosticable { lerpDouble(a.trackCornerRadius, b.trackCornerRadius, t), overlayRadius: lerpDouble(a.overlayRadius, b.overlayRadius, t), thumbRadius: lerpDouble(a.thumbRadius, b.thumbRadius, t), - activeDivisorRadius: - lerpDouble(a.activeDivisorRadius, b.activeDivisorRadius, t), - inactiveDivisorRadius: - lerpDouble(a.inactiveDivisorRadius, b.inactiveDivisorRadius, t), + activeDividerRadius: + lerpDouble(a.activeDividerRadius, b.activeDividerRadius, t), + inactiveDividerRadius: + lerpDouble(a.inactiveDividerRadius, b.inactiveDividerRadius, t), thumbStrokeWidth: lerpDouble(a.thumbStrokeWidth, b.thumbStrokeWidth, t), - activeDivisorStrokeWidth: - lerpDouble(a.activeDivisorStrokeWidth, b.activeDivisorStrokeWidth, t), - inactiveDivisorStrokeWidth: lerpDouble(a.inactiveDivisorStrokeWidth, b.inactiveDivisorStrokeWidth, t)); + activeDividerStrokeWidth: + // ignore: lines_longer_than_80_chars + lerpDouble(a.activeDividerStrokeWidth, b.activeDividerStrokeWidth, t), + // ignore: lines_longer_than_80_chars + inactiveDividerStrokeWidth: lerpDouble(a.inactiveDividerStrokeWidth, b.inactiveDividerStrokeWidth, t)); } @override @@ -443,8 +448,8 @@ class SfSliderThemeData with Diagnosticable { other.activeTrackColor == activeTrackColor && other.thumbColor == thumbColor && other.thumbStrokeColor == thumbStrokeColor && - other.activeDivisorStrokeColor == activeDivisorStrokeColor && - other.inactiveDivisorStrokeColor == inactiveDivisorStrokeColor && + other.activeDividerStrokeColor == activeDividerStrokeColor && + other.inactiveDividerStrokeColor == inactiveDividerStrokeColor && other.activeTickColor == activeTickColor && other.inactiveTickColor == inactiveTickColor && other.disabledActiveTickColor == disabledActiveTickColor && @@ -455,22 +460,22 @@ class SfSliderThemeData with Diagnosticable { other.disabledInactiveMinorTickColor == disabledInactiveMinorTickColor && other.overlayColor == overlayColor && - other.inactiveDivisorColor == inactiveDivisorColor && - other.activeDivisorColor == activeDivisorColor && + other.inactiveDividerColor == inactiveDividerColor && + other.activeDividerColor == activeDividerColor && other.disabledActiveTrackColor == disabledActiveTrackColor && other.disabledInactiveTrackColor == disabledInactiveTrackColor && - other.disabledActiveDivisorColor == disabledActiveDivisorColor && - other.disabledInactiveDivisorColor == disabledInactiveDivisorColor && + other.disabledActiveDividerColor == disabledActiveDividerColor && + other.disabledInactiveDividerColor == disabledInactiveDividerColor && other.disabledThumbColor == disabledThumbColor && other.tooltipBackgroundColor == tooltipBackgroundColor && other.trackCornerRadius == trackCornerRadius && other.overlayRadius == overlayRadius && other.thumbRadius == thumbRadius && - other.activeDivisorRadius == activeDivisorRadius && - other.inactiveDivisorRadius == inactiveDivisorRadius && + other.activeDividerRadius == activeDividerRadius && + other.inactiveDividerRadius == inactiveDividerRadius && other.thumbStrokeWidth == thumbStrokeWidth && - other.activeDivisorStrokeWidth == activeDivisorStrokeWidth && - other.inactiveDivisorStrokeWidth == inactiveDivisorStrokeWidth; + other.activeDividerStrokeWidth == activeDividerStrokeWidth && + other.inactiveDividerStrokeWidth == inactiveDividerStrokeWidth; } @override @@ -490,8 +495,8 @@ class SfSliderThemeData with Diagnosticable { activeTrackColor, thumbColor, thumbStrokeColor, - activeDivisorStrokeColor, - inactiveDivisorStrokeColor, + activeDividerStrokeColor, + inactiveDividerStrokeColor, activeTickColor, inactiveTickColor, disabledActiveTickColor, @@ -501,22 +506,22 @@ class SfSliderThemeData with Diagnosticable { disabledActiveMinorTickColor, disabledInactiveMinorTickColor, overlayColor, - inactiveDivisorColor, - activeDivisorColor, + inactiveDividerColor, + activeDividerColor, disabledActiveTrackColor, disabledInactiveTrackColor, - disabledActiveDivisorColor, - disabledInactiveDivisorColor, + disabledActiveDividerColor, + disabledInactiveDividerColor, disabledThumbColor, tooltipBackgroundColor, trackCornerRadius, overlayRadius, - activeDivisorRadius, - inactiveDivisorRadius, + activeDividerRadius, + inactiveDividerRadius, thumbRadius, thumbStrokeWidth, - activeDivisorStrokeWidth, - inactiveDivisorStrokeWidth, + activeDividerStrokeWidth, + inactiveDividerStrokeWidth, ]); } @@ -556,11 +561,11 @@ class SfSliderThemeData with Diagnosticable { properties.add(ColorProperty('thumbStrokeColor', thumbStrokeColor, defaultValue: defaultData.thumbStrokeColor)); properties.add(ColorProperty( - 'activeDivisorStrokeColor', activeDivisorStrokeColor, - defaultValue: defaultData.activeDivisorStrokeColor)); + 'activeDividerStrokeColor', activeDividerStrokeColor, + defaultValue: defaultData.activeDividerStrokeColor)); properties.add(ColorProperty( - 'inactiveDivisorStrokeColor', inactiveDivisorStrokeColor, - defaultValue: defaultData.inactiveDivisorStrokeColor)); + 'inactiveDividerStrokeColor', inactiveDividerStrokeColor, + defaultValue: defaultData.inactiveDividerStrokeColor)); properties.add(ColorProperty('activeTickColor', activeTickColor, defaultValue: defaultData.activeTickColor)); properties.add(ColorProperty('inactiveTickColor', inactiveTickColor, @@ -584,10 +589,10 @@ class SfSliderThemeData with Diagnosticable { defaultValue: defaultData.disabledInactiveMinorTickColor)); properties.add(ColorProperty('overlayColor', overlayColor, defaultValue: defaultData.overlayColor)); - properties.add(ColorProperty('inactiveDivisorColor', inactiveDivisorColor, - defaultValue: defaultData.inactiveDivisorColor)); - properties.add(ColorProperty('activeDivisorColor', activeDivisorColor, - defaultValue: defaultData.activeDivisorColor)); + properties.add(ColorProperty('inactiveDividerColor', inactiveDividerColor, + defaultValue: defaultData.inactiveDividerColor)); + properties.add(ColorProperty('activeDividerColor', activeDividerColor, + defaultValue: defaultData.activeDividerColor)); properties.add(ColorProperty( 'disabledActiveTrackColor', disabledActiveTrackColor, defaultValue: defaultData.disabledActiveTrackColor)); @@ -595,11 +600,11 @@ class SfSliderThemeData with Diagnosticable { 'disabledInactiveTrackColor', disabledInactiveTrackColor, defaultValue: defaultData.disabledInactiveTrackColor)); properties.add(ColorProperty( - 'disabledActiveDivisorColor', disabledActiveDivisorColor, - defaultValue: defaultData.disabledActiveDivisorColor)); + 'disabledActiveDividerColor', disabledActiveDividerColor, + defaultValue: defaultData.disabledActiveDividerColor)); properties.add(ColorProperty( - 'disabledInactiveDivisorColor', disabledInactiveDivisorColor, - defaultValue: defaultData.disabledInactiveDivisorColor)); + 'disabledInactiveDividerColor', disabledInactiveDividerColor, + defaultValue: defaultData.disabledInactiveDividerColor)); properties.add(ColorProperty('disabledThumbColor', disabledThumbColor, defaultValue: defaultData.disabledThumbColor)); properties.add(ColorProperty( @@ -611,19 +616,19 @@ class SfSliderThemeData with Diagnosticable { defaultValue: defaultData.overlayRadius)); properties.add(DoubleProperty('thumbRadius', thumbRadius, defaultValue: defaultData.thumbRadius)); - properties.add(DoubleProperty('activeDivisorRadius', activeDivisorRadius, - defaultValue: defaultData.activeDivisorRadius)); + properties.add(DoubleProperty('activeDividerRadius', activeDividerRadius, + defaultValue: defaultData.activeDividerRadius)); properties.add(DoubleProperty( - 'inactiveDivisorRadius', inactiveDivisorRadius, - defaultValue: defaultData.inactiveDivisorRadius)); + 'inactiveDividerRadius', inactiveDividerRadius, + defaultValue: defaultData.inactiveDividerRadius)); properties.add(DoubleProperty('thumbStrokeWidth', thumbStrokeWidth, defaultValue: defaultData.thumbStrokeWidth)); properties.add(DoubleProperty( - 'activeDivisorStrokeWidth', activeDivisorStrokeWidth, - defaultValue: defaultData.activeDivisorStrokeWidth)); + 'activeDividerStrokeWidth', activeDividerStrokeWidth, + defaultValue: defaultData.activeDividerStrokeWidth)); properties.add(DoubleProperty( - 'inactiveDivisorStrokeWidth', inactiveDivisorStrokeWidth, - defaultValue: defaultData.inactiveDivisorStrokeWidth)); + 'inactiveDividerStrokeWidth', inactiveDividerStrokeWidth, + defaultValue: defaultData.inactiveDividerStrokeWidth)); } /// Specifies the light and dark theme for the [SfSlider], @@ -733,12 +738,12 @@ class SfSliderThemeData with Diagnosticable { /// ``` final double inactiveTrackHeight; - /// Specifies the radius for the active divisor in the [SfSlider], + /// Specifies the radius for the active divider in the [SfSlider], /// [SfRangeSlider], and [SfRangeSelector]. /// - /// By default, active divisor will be in same height of active track. + /// By default, active divider will be in same height of active track. /// - /// This snippet shows how to set active divisor radius + /// This snippet shows how to set active divider radius /// in [SfRangeSliderThemeData]. /// /// ```dart @@ -748,7 +753,7 @@ class SfSliderThemeData with Diagnosticable { /// body: Center( /// child: SfRangeSliderTheme( /// data: SfRangeSliderThemeData( - /// activeDivisorRadius: 5, + /// activeDividerRadius: 5, /// ), /// child: SfRangeSlider( /// min: 2.0, @@ -764,14 +769,14 @@ class SfSliderThemeData with Diagnosticable { /// ) /// ) /// ``` - final double? activeDivisorRadius; + final double? activeDividerRadius; - /// Specifies the radius for the inactive divisor in the [SfSlider], + /// Specifies the radius for the inactive divider in the [SfSlider], /// [SfRangeSlider], and [SfRangeSelector]. /// - /// By default, inactive divisor will be in same height of inactive track. + /// By default, inactive divider will be in same height of inactive track. /// - /// This snippet shows how to set inactive divisor radius + /// This snippet shows how to set inactive divider radius /// in [SfRangeSliderThemeData]. /// /// ```dart @@ -781,7 +786,7 @@ class SfSliderThemeData with Diagnosticable { /// body: Center( /// child: SfRangeSliderTheme( /// data: SfRangeSliderThemeData( - /// inactiveDivisorRadius: 5, + /// inactiveDividerRadius: 5, /// ), /// child: SfRangeSlider( /// min: 2.0, @@ -797,7 +802,7 @@ class SfSliderThemeData with Diagnosticable { /// ) /// ) /// ``` - final double? inactiveDivisorRadius; + final double? inactiveDividerRadius; /// Specifies the stroke width for the thumb in the [SfSlider], /// [SfRangeSlider], and [SfRangeSelector]. @@ -833,12 +838,12 @@ class SfSliderThemeData with Diagnosticable { /// ``` final double? thumbStrokeWidth; - /// Specifies the stroke width for the active divisor in the [SfSlider], + /// Specifies the stroke width for the active divider in the [SfSlider], /// [SfRangeSlider], and [SfRangeSelector]. /// /// Defaults to `null`. /// - /// This snippet shows how to set active divisor stroke width + /// This snippet shows how to set active divider stroke width /// in [SfRangeSliderThemeData]. /// /// ```dart @@ -848,8 +853,8 @@ class SfSliderThemeData with Diagnosticable { /// body: Center( /// child: SfRangeSliderTheme( /// data: SfRangeSliderThemeData( - /// activeDivisorStrokeWidth: 2, - /// activeDivisorStrokeColor: Colors.red, + /// activeDividerStrokeWidth: 2, + /// activeDividerStrokeColor: Colors.red, /// ), /// child: SfRangeSlider( /// min: 2.0, @@ -865,14 +870,14 @@ class SfSliderThemeData with Diagnosticable { /// ) /// ) /// ``` - final double? activeDivisorStrokeWidth; + final double? activeDividerStrokeWidth; - /// Specifies the stroke width for the inactive divisor in the [SfSlider], + /// Specifies the stroke width for the inactive divider in the [SfSlider], /// [SfRangeSlider], and [SfRangeSelector]. /// /// Defaults to `null`. /// - /// This snippet shows how to set inactive divisor stroke width + /// This snippet shows how to set inactive divider stroke width /// in [SfRangeSliderThemeData]. /// /// ```dart @@ -882,8 +887,8 @@ class SfSliderThemeData with Diagnosticable { /// body: Center( /// child: SfRangeSliderTheme( /// data: SfRangeSliderThemeData( - /// inactiveDivisorStrokeWidth: 2, - /// inactiveDivisorStrokeColor: Colors.red, + /// inactiveDividerStrokeWidth: 2, + /// inactiveDividerStrokeColor: Colors.red, /// ), /// child: SfRangeSlider( /// min: 2.0, @@ -899,7 +904,7 @@ class SfSliderThemeData with Diagnosticable { /// ) /// ) /// ``` - final double? inactiveDivisorStrokeWidth; + final double? inactiveDividerStrokeWidth; /// Specifies the size for tick. /// Specifies the size for the major ticks in the [SfSlider], @@ -1313,7 +1318,7 @@ class SfSliderThemeData with Diagnosticable { /// ``` final Color? thumbStrokeColor; - /// Specifies the stroke color for the active divisor in the [SfSlider], + /// Specifies the stroke color for the active divider in the [SfSlider], /// [SfRangeSlider], and [SfRangeSelector]. /// /// Defaults to `null`. @@ -1323,7 +1328,7 @@ class SfSliderThemeData with Diagnosticable { /// /// The active side of the [SfSlider] is between the [min] value and thumb. /// - /// This snippet shows how to set active divisor stroke color + /// This snippet shows how to set active divider stroke color /// in [SfRangeSliderThemeData]. /// /// ```dart @@ -1333,8 +1338,8 @@ class SfSliderThemeData with Diagnosticable { /// body: Center( /// child: SfRangeSliderTheme( /// data: SfRangeSliderThemeData( - /// activeDivisorStrokeWidth: 2, - /// activeDivisorStrokeColor: Colors.red, + /// activeDividerStrokeWidth: 2, + /// activeDividerStrokeColor: Colors.red, /// ), /// child: SfRangeSlider( /// min: 2.0, @@ -1350,9 +1355,9 @@ class SfSliderThemeData with Diagnosticable { /// ) /// ) /// ``` - final Color? activeDivisorStrokeColor; + final Color? activeDividerStrokeColor; - /// Specifies the stroke color for the inactive divisor in the [SfSlider], + /// Specifies the stroke color for the inactive divider in the [SfSlider], /// [SfRangeSlider], and [SfRangeSelector]. /// /// Defaults to `null`. @@ -1366,7 +1371,7 @@ class SfSliderThemeData with Diagnosticable { /// The inactive side of the [SfSlider] is between the /// [max] value and the thumb. /// - /// This snippet shows how to set inactive divisor stroke + /// This snippet shows how to set inactive divider stroke /// color in [SfRangeSliderThemeData]. /// /// ```dart @@ -1376,8 +1381,8 @@ class SfSliderThemeData with Diagnosticable { /// body: Center( /// child: SfRangeSliderTheme( /// data: SfRangeSliderThemeData( - /// inactiveDivisorStrokeWidth: 2, - /// inactiveDivisorStrokeColor: Colors.red, + /// inactiveDividerStrokeWidth: 2, + /// inactiveDividerStrokeColor: Colors.red, /// ), /// child: SfRangeSlider( /// min: 2.0, @@ -1393,7 +1398,7 @@ class SfSliderThemeData with Diagnosticable { /// ) /// ) /// ``` - final Color? inactiveDivisorStrokeColor; + final Color? inactiveDividerStrokeColor; /// Specifies the color for active tick. /// Specifies the color for the active major ticks in the [SfSlider], @@ -1737,7 +1742,7 @@ class SfSliderThemeData with Diagnosticable { /// ``` final Color? overlayColor; - /// Specifies the color for the inactive divisors in the [SfSlider], + /// Specifies the color for the inactive dividers in the [SfSlider], /// [SfRangeSlider], and [SfRangeSelector]. /// /// The inactive side of the [SfRangeSlider] and [SfRangeSelector] is @@ -1751,7 +1756,7 @@ class SfSliderThemeData with Diagnosticable { /// The inactive side of the [SfSlider] is between the /// [max] value and the thumb. /// - /// This snippet shows how to set inactive divisors + /// This snippet shows how to set inactive dividers /// color in [SfRangeSliderThemeData]. /// /// ```dart @@ -1761,14 +1766,14 @@ class SfSliderThemeData with Diagnosticable { /// body: Center( /// child: SfRangeSliderTheme( /// data: SfRangeSliderThemeData( - /// inactiveDivisorColor: Colors.red[200], + /// inactiveDividerColor: Colors.red[200], /// ), /// child: SfRangeSlider( /// min: 2.0, /// max: 10.0, /// values: _values, /// interval: 1, - /// showDivisors: true, + /// showDividers: true, /// onChanged: (SfRangeValues newValues){ /// setState(() { /// _values = newValues; @@ -1779,9 +1784,9 @@ class SfSliderThemeData with Diagnosticable { /// ) /// ) /// ``` - final Color? inactiveDivisorColor; + final Color? inactiveDividerColor; - /// Specifies the color for the active divisors in the [SfSlider], + /// Specifies the color for the active dividers in the [SfSlider], /// [SfRangeSlider], and [SfRangeSelector]. /// /// The active side of the [SfRangeSlider] and [SfRangeSelector] is @@ -1790,7 +1795,7 @@ class SfSliderThemeData with Diagnosticable { /// The active side of the [SfSlider] is between the [min] /// value and the thumb. /// - /// This snippet shows how to set active divisors + /// This snippet shows how to set active dividers /// color in [SfRangeSliderThemeData]. /// /// ```dart @@ -1800,14 +1805,14 @@ class SfSliderThemeData with Diagnosticable { /// body: Center( /// child: SfRangeSliderTheme( /// data: SfRangeSliderThemeData( - /// activeDivisorColor: Colors.red, + /// activeDividerColor: Colors.red, /// ), /// child: SfRangeSlider( /// min: 2.0, /// max: 10.0, /// values: _values, /// interval: 1, - /// showDivisors: true, + /// showDividers: true, /// onChanged: (SfRangeValues newValues){ /// setState(() { /// _values = newValues; @@ -1818,7 +1823,7 @@ class SfSliderThemeData with Diagnosticable { /// ) /// ) /// ``` - final Color? activeDivisorColor; + final Color? activeDividerColor; /// Specifies the color for the disabled active track in the [SfSlider], /// [SfRangeSlider], and [SfRangeSelector]. @@ -1889,7 +1894,7 @@ class SfSliderThemeData with Diagnosticable { /// ``` final Color? disabledInactiveTrackColor; - /// Specifies the color for the disabled active divisors in the [SfSlider], + /// Specifies the color for the disabled active dividers in the [SfSlider], /// [SfRangeSlider], and [SfRangeSelector]. /// /// The active side of the [SfRangeSlider] and [SfRangeSelector] @@ -1897,7 +1902,7 @@ class SfSliderThemeData with Diagnosticable { /// /// The active side of the [SfSlider] is between the [min] value and thumb. /// - /// This snippet shows how to set disabled active divisor + /// This snippet shows how to set disabled active divider /// color in [SfRangeSliderThemeData]. /// /// ```dart @@ -1907,7 +1912,7 @@ class SfSliderThemeData with Diagnosticable { /// body: Center( /// child: SfRangeSliderTheme( /// data: SfRangeSliderThemeData( - /// disabledActiveDivisorColor: Colors.purple, + /// disabledActiveDividerColor: Colors.purple, /// ), /// child: SfRangeSlider( /// min: 2.0, @@ -1915,16 +1920,16 @@ class SfSliderThemeData with Diagnosticable { /// values: _values, /// interval: 2, /// showTicks: true, - /// showDivisors: true, + /// showDividers: true, /// minorTicksPerInterval: 1, /// ) /// ), /// ) /// ) /// ``` - final Color? disabledActiveDivisorColor; + final Color? disabledActiveDividerColor; - /// Specifies the color for the disabled inactive divisors in the [SfSlider], + /// Specifies the color for the disabled inactive dividers in the [SfSlider], /// [SfRangeSlider], and [SfRangeSelector]. /// /// The inactive side of the [SfRangeSlider] and [SfRangeSelector] is between @@ -1937,7 +1942,7 @@ class SfSliderThemeData with Diagnosticable { /// The inactive side of the [SfSlider] is between the /// [max] value and the thumb. /// - /// This snippet shows how to set disabled inactive divisor + /// This snippet shows how to set disabled inactive divider /// color in [SfRangeSliderThemeData]. /// /// ```dart @@ -1947,7 +1952,7 @@ class SfSliderThemeData with Diagnosticable { /// body: Center( /// child: SfRangeSliderTheme( /// data: SfRangeSliderThemeData( - /// disabledInactiveDivisorColor: Colors.purple[200], + /// disabledInactiveDividerColor: Colors.purple[200], /// ), /// child: SfRangeSlider( /// min: 2.0, @@ -1955,14 +1960,14 @@ class SfSliderThemeData with Diagnosticable { /// values: _values, /// interval: 2, /// showTicks: true, - /// showDivisors: true, + /// showDividers: true, /// minorTicksPerInterval: 1, /// ) /// ), /// ) /// ) /// ``` - final Color? disabledInactiveDivisorColor; + final Color? disabledInactiveDividerColor; /// Specifies the color for the disabled thumb in the [SfSlider], /// [SfRangeSlider], and [SfRangeSelector]. diff --git a/packages/syncfusion_flutter_core/lib/src/theme/theme_widget.dart b/packages/syncfusion_flutter_core/lib/src/theme/theme_widget.dart index 58d4a7639..d453105b3 100644 --- a/packages/syncfusion_flutter_core/lib/src/theme/theme_widget.dart +++ b/packages/syncfusion_flutter_core/lib/src/theme/theme_widget.dart @@ -156,6 +156,7 @@ class _SfInheritedTheme extends InheritedTheme { /// ); /// } /// ``` +@immutable class SfThemeData with Diagnosticable { /// Creating an argument constructor of SfThemeData class. factory SfThemeData( diff --git a/packages/syncfusion_flutter_core/lib/src/tooltip/tooltip.dart b/packages/syncfusion_flutter_core/lib/src/tooltip/tooltip.dart index a3bc6840f..72f2656a8 100644 --- a/packages/syncfusion_flutter_core/lib/src/tooltip/tooltip.dart +++ b/packages/syncfusion_flutter_core/lib/src/tooltip/tooltip.dart @@ -5,9 +5,11 @@ part of tooltip_internal; /// This class provides options for customizing the properties of the tooltip. class SfTooltip extends StatefulWidget { /// Creating an argument constructor of SfTooltip class. + // ignore: prefer_const_constructors_in_immutables SfTooltip( {this.textStyle = const TextStyle(), this.animationDuration = 500, + this.animationCurve = const Interval(0.0, 1.0, curve: Curves.linear), this.enable = true, this.opacity = 1, this.borderColor = Colors.black, @@ -22,7 +24,6 @@ class SfTooltip extends StatefulWidget { this.labelColor = Colors.white, this.header, this.format, - this.builder, this.shadowColor, Key? key, this.onTooltipRender}) @@ -84,6 +85,11 @@ class SfTooltip extends StatefulWidget { ///Defaults to `500`. final int animationDuration; + ///Curve for animating the tooltip. + /// + ///Defaults to interval of `0` to `1` with `Curves.linear`. + final Interval animationCurve; + ///Toggles the visibility of the marker in the tooltip. /// ///Defaults to `true`. @@ -94,11 +100,6 @@ class SfTooltip extends StatefulWidget { ///Defaults to `0`. final double borderWidth; - ///Builder of the tooltip. - /// - ///Defaults to `null`. - final dynamic builder; - ///Elevation of the tooltip. /// ///Defaults to `0`. @@ -106,7 +107,8 @@ class SfTooltip extends StatefulWidget { ///Shows or hides the tooltip. /// - ///By default, the tooltip will be hidden on touch. To avoid this, set this property to true. + ///By default, the tooltip will be hidden on touch. + ///To avoid this, set this property to true. /// ///Defaults to `false`. final bool shouldAlwaysShow; @@ -114,52 +116,16 @@ class SfTooltip extends StatefulWidget { ///Duration for displaying the tooltip. /// ///Defaults to `3000`. - final double duration; + final int duration; ///Alignment of the text in the tooltip final dynamic textAlignment; - /// Occurs while tooltip is rendered. You can customize the text, position and header. + /// Occurs while tooltip is rendered. + /// You can customize the text, position and header. /// Here, you can get the text, header, x and y-positions. final void Function(TooltipRenderArgs tooltipRenderArgs)? onTooltipRender; - /// Displays the tooltip at the position. - /// - /// - /// *position - the x and y position at which the tooltip needs to be shown. - /// *duration - the duration in milliseconds for which the tooltip animation needs to happen. - /// *template - the widget that will be - void show(Offset? position, int duration, [Widget? template]) { - if (position != null) { - // ignore: avoid_as - final GlobalKey tooltipKey = key as GlobalKey; - // ignore: avoid_as - final SfTooltipState state = tooltipKey.currentState as SfTooltipState; - state.animationController!.duration = Duration(milliseconds: duration); - state.renderBox?.calculateLocation(position); - state._show = true; - if (template == null) { - // ignore: invalid_use_of_protected_member - state.setState(() {}); - } else { - state._template = template; - // ignore: invalid_use_of_protected_member - state.setState(() {}); - } - } - } - - /// Hides the tooltip if it is currently displayed. - void hide([int? duration]) { - // ignore: avoid_as - final GlobalKey tooltipKey = key! as GlobalKey; - // ignore: avoid_as - final SfTooltipState state = tooltipKey.currentState as SfTooltipState; - state._show = false; - state.animationController!.duration = Duration(milliseconds: duration ?? 0); - state.animationController!.reverse(from: 1.0); - } - @override SfTooltipState createState() => SfTooltipState(); } @@ -181,13 +147,98 @@ class SfTooltipState extends State Widget? _template; + Timer? _timer; + + bool _hidden = false, _animating = false, _didUpdate = false; + + Object? _previousTooltipData; + + ///Setter for the boundary rect within which the tooltip could be shown + set boundaryRect(Rect value) { + if (renderBox != null) { + if (renderBox!._boundaryRect == value) { + return; + } + renderBox!._boundaryRect = value; + } + } + + /// Displays the tooltip at the position. + /// + /// + /// *position - the x and y position at which the tooltip needs to be shown. + /// *duration - the duration in milliseconds for which the tooltip animation + /// needs to happen. + /// *template - the widget that will be rendered instead of default tooltip + /// *tooltipData - the data which allows this widget to decide whether it is + /// activated for the same point + void show( + {int? duration, + Offset? position, + Object? tooltipData, + String? tooltipContent, + String? tooltipHeader, + Widget? template}) { + duration ??= widget.animationDuration; + _hidden = false; + animationController!.duration = Duration(milliseconds: duration); + if (renderBox != null) { + renderBox!._position = position; + } + _timer?.cancel(); + if (_previousTooltipData == null || + !(_previousTooltipData == tooltipData)) { + _show = true; + _template = template; + _previousTooltipData = tooltipData; + _animating = true; + if (mounted) { + setState(() { + animationController!.duration = Duration(milliseconds: duration!); + if (animationController?.status != AnimationStatus.forward) { + if (tooltipContent != null) { + renderBox!._stringValue = tooltipContent; + } + if (tooltipHeader != null) { + renderBox!._header = tooltipHeader; + } + animationController!.forward(from: 0.0); + } + }); + } + } else { + if (!_animating) { + animationController!.duration = const Duration(milliseconds: 0); + } + } + } + + /// Hides the tooltip if it is currently displayed. + /// *duration - the duration in milliseconds for which the tooltip animation + /// needs to happen. + /// *hideDelay - the duration in milliseconds after which the tooltip needs + /// to be hidden + void hide({int? duration, int? hideDelay}) { + if (!_hidden || hideDelay == 0) { + _timer?.cancel(); + _hidden = true; + _timer = Timer(Duration(milliseconds: hideDelay ?? widget.duration), () { + _previousTooltipData = null; + if (animationController != null) { + animationController!.duration = Duration(milliseconds: duration ?? 0); + animationController!.reverse(from: 1.0); + } + }); + } + } + @override void initState() { _show = false; needMarker = widget.canShowMarker; animationController = AnimationController( - duration: Duration(milliseconds: widget.animationDuration), - vsync: this); + duration: Duration(milliseconds: widget.animationDuration), vsync: this) + ..addStatusListener(_animationStatusListener); super.initState(); } @@ -196,17 +247,23 @@ class SfTooltipState extends State final Animation tooltipAnimation = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation( parent: animationController!, - curve: const Interval(0.1, 0.8, curve: Curves.easeOutBack), + curve: widget.animationCurve, )); - if (_show) { - animationController!.forward(from: 0.0); + if (_show && !_didUpdate) { + if (animationController?.status != AnimationStatus.forward) { + animationController!.forward(from: 0.0); + } else { + animationController!.reset(); + animationController!.forward(from: 0.0); + } } - _template = widget.builder != null ? (_template ?? Container()) : null; + _didUpdate = false; + _template = _template != null ? (_template ?? Container()) : null; return AnimatedBuilder( animation: animationController!, builder: (BuildContext context, Widget? child) { if (renderBox != null) { - renderBox!.animationFactor = animationController!.value; + renderBox!.animationFactor = tooltipAnimation.value; } return child!; }, @@ -218,17 +275,31 @@ class SfTooltipState extends State ); } + @override + void didUpdateWidget(SfTooltip oldWidget) { + _didUpdate = true; + super.didUpdateWidget(oldWidget); + } + @override void dispose() { + animationController!.removeStatusListener(_animationStatusListener); animationController!.dispose(); animationController = null; super.dispose(); } + + void _animationStatusListener(AnimationStatus status) { + if (status == AnimationStatus.completed) { + _animating = false; + } + } } /// Single child render object widget classfor rendering the tooltip class TooltipRenderObject extends SingleChildRenderObjectWidget { /// Creating an argument constructor of TooltipRenderObject class. + // ignore: prefer_const_constructors_in_immutables TooltipRenderObject( {Widget? template, required SfTooltipState tooltipState, @@ -259,7 +330,8 @@ class TooltipRenderObject extends SingleChildRenderObjectWidget { } } -/// tooltip render box class. This class holds the properties needed to render the tooltip widget. +/// tooltip render box class. This class holds the properties needed to render +/// the tooltip widget. class TooltipRenderBox extends RenderShiftedBox { /// Creating an argument constructor of TooltipRenderBox class. TooltipRenderBox( @@ -352,41 +424,23 @@ class TooltipRenderBox extends RenderShiftedBox { ///Getter for size of marker rendered in tooltip double get markerSize => _markerSize; - bool _canResetPath = false; - - ///Setter for the boolean determining the reset of tooltip path - set canResetPath(bool value) { - if (_canResetPath == value) { - return; - } - _canResetPath = value; - } - Rect _boundaryRect = const Rect.fromLTWH(0, 0, 0, 0); - ///Setter for the boundary rect within which the tooltip could be shown - set boundaryRect(Rect value) { - if (_boundaryRect == value) { - return; - } - _boundaryRect = value; - } - - List _markerTypes = []; + List _markerTypes = []; ///Setter for the tooltip marker type set markerTypes(List types) { _markerTypes = types; } - List _markerPaints = []; + List _markerPaints = []; ///Setter for tooltip marker paint set markerPaints(List paints) { _markerPaints = paints; } - List _markerImages = []; + List _markerImages = []; ///Setter for the marker image of tooltip when the data marker type is image set markerImages(List images) { @@ -402,6 +456,7 @@ class TooltipRenderBox extends RenderShiftedBox { final double _pointerLength = 10; double? _xPos, _yPos, _x, _y; + Offset? _position; double _nosePointX = 0, _nosePointY = 0, _borderRadius = 5, _totalWidth = 0; bool _isLeft = false, _isRight = false, @@ -409,7 +464,7 @@ class TooltipRenderBox extends RenderShiftedBox { _isOutOfBoundInTop = false; late double _markerPointY; Rect? _tooltipRect; - Path _arrowPath = Path(); + final Path _arrowPath = Path(); late Size _templateSize; @override @@ -426,20 +481,25 @@ class TooltipRenderBox extends RenderShiftedBox { if (_tooltipState._show) { if (child != null) { _isOutOfBoundInTop = false; - size = Size.copy(_boundaryRect.size); child!.layout(constraints, parentUsesSize: true); size = Size.copy(child!.size); } } else { - size = Size.zero; + size = Size(constraints.maxWidth.isFinite ? constraints.maxWidth : 0, + constraints.maxHeight.isFinite ? constraints.maxHeight : 0); child?.layout(constraints); } } @override void paint(PaintingContext context, Offset offset) { - _isOutOfBoundInTop = false; - context.canvas.translate(offset.dx, offset.dy); + final Offset parentOffset = parentData is BoxParentData + //ignore: avoid_as + ? (parentData! as BoxParentData).offset + : Offset.zero; + final Offset relativeOffset = offset - parentOffset; + calculateLocation(_position != null ? (_position!) : parentOffset); + context.canvas.translate(relativeOffset.dx, relativeOffset.dy); if (_tooltipState._show) { if ((_animationFactor == 0 || _tooltip.animationDuration == 0) && _tooltipState.widget.onTooltipRender != null) { @@ -448,18 +508,20 @@ class TooltipRenderBox extends RenderShiftedBox { _tooltipState.widget.onTooltipRender!(tooltipRenderArgs); _x = tooltipRenderArgs.location!.dx; _y = tooltipRenderArgs.location!.dy; - stringValue = tooltipRenderArgs.text; - header = tooltipRenderArgs.header; + _stringValue = tooltipRenderArgs.text; + _header = tooltipRenderArgs.header; } - if (_tooltip.builder == null) { + if (_tooltipState._template == null) { _renderDefaultTooltipView(context.canvas); } else { - _renderTemplateTooltipView(context, offset); + _renderTemplateTooltipView(context, relativeOffset); } } - context.canvas.translate(-offset.dx, -offset.dy); + context.canvas.translate(-relativeOffset.dx, -relativeOffset.dy); } + /// renders the tooltip templaate view with tooltip background, arrow and + /// template void _renderTemplateTooltipView(PaintingContext context, Offset offset) { const double arrowHeight = 5.0; _templateSize = Size.copy(child!.size); @@ -471,57 +533,127 @@ class TooltipRenderBox extends RenderShiftedBox { double top = _y!; double paddingTop = 0; + final Rect bounds = _boundaryRect.translate(-offset.dx, -offset.dy); final Offset tooltipLocation = - _getTemplateLocation(_tooltipRect!, _boundaryRect); - final Offset arrowLocation = Offset(_x! - _templateSize.width / 2, + _getTemplateLocation(_tooltipRect!, bounds, offset); + Offset arrowLocation = Offset(_x! - _templateSize.width / 2, _isOutOfBoundInTop ? _y! : _y! - arrowHeight); - if (_y! < _boundaryRect.top) { - paddingTop = _boundaryRect.top + arrowHeight; + if (_y! < bounds.top + offset.dy) { + paddingTop = bounds.top + offset.dy + arrowHeight; top = tooltipLocation.dy; + arrowLocation = Offset(arrowLocation.dx, tooltipLocation.dy); } top = _isOutOfBoundInTop ? top + arrowHeight : _tooltipRect!.top; - if (_y! >= _boundaryRect.top) { + if (_y! >= bounds.top + offset.dy) { paddingTop = top; } - context.pushTransform(true, Offset(_x!, _y!) + offset, + final Offset renderOffset = (_isOutOfBoundInTop + ? Offset(tooltipLocation.dx, tooltipLocation.dy + paddingTop) + : tooltipLocation) + + offset; + context.pushTransform(true, Offset(_x!, arrowLocation.dy) + offset, Matrix4.diagonal3Values(_animationFactor, _animationFactor, 1), - (tooltipTemplateContext, tooltipTemplateOffset) { - tooltipTemplateContext.paintChild( - child!, - (_isOutOfBoundInTop - ? Offset(tooltipLocation.dx, tooltipLocation.dy + paddingTop) - : tooltipLocation) + - offset); - _renderArrow(tooltipTemplateContext.canvas, arrowLocation + offset); + (PaintingContext tooltipTemplateContext, Offset tooltipTemplateOffset) { + _renderArrowAndTemplatePath( + tooltipTemplateContext.canvas, arrowLocation + offset, renderOffset); + tooltipTemplateContext.paintChild(child!, renderOffset); }); } - void _renderArrow(Canvas canvas, Offset location) { + /// This method renders the path for tooltip template mode + void _renderArrowAndTemplatePath( + Canvas canvas, Offset location, Offset templateLocation) { + final Paint strokePaint = Paint() + ..style = PaintingStyle.stroke + ..color = + _tooltip.borderWidth == 0 ? Colors.transparent : _tooltip.borderColor + ..strokeWidth = _tooltip.borderWidth; + final Paint fillPaint = Paint() + ..color = (_tooltip.color).withOpacity(_tooltip.opacity) + ..style = PaintingStyle.fill; const double currentHeight = 5.0; const double arrowWidth = 8; - const double padding = 2; + const double padding = 0.1; final Size currentSize = Size(_templateSize.width, currentHeight); final num templateHeight = _templateSize.height; final num arrowHeight = currentSize.height + padding; final num centerTemplateY = _isOutOfBoundInTop ? location.dy + currentSize.height + templateHeight / 2 + padding : location.dy - templateHeight / 2 - padding; - final double locationY = _isOutOfBoundInTop + double locationY = _isOutOfBoundInTop ? centerTemplateY - (templateHeight / 2) - arrowHeight : centerTemplateY + templateHeight / 2; final num centerX = location.dx + currentSize.width / 2; - _arrowPath = Path() - ..moveTo(centerX + (_isOutOfBoundInTop ? 0 : -arrowWidth), locationY) - ..lineTo(centerX + (_isOutOfBoundInTop ? -arrowWidth : arrowWidth), - (locationY) + (_isOutOfBoundInTop ? arrowHeight : 0)) - ..lineTo(centerX + (_isOutOfBoundInTop ? arrowWidth : 0), - locationY + arrowHeight) - ..close(); - canvas.drawPath( - _arrowPath, - Paint() - ..color = (_tooltip.color).withOpacity(_tooltip.opacity) - ..style = PaintingStyle.fill); + final Path path = Path(); + final RRect rect = RRect.fromLTRBAndCorners( + templateLocation.dx, + templateLocation.dy, + templateLocation.dx + _templateSize.width, + templateLocation.dy + _templateSize.height, + topLeft: Radius.circular(_borderRadius), + topRight: Radius.circular(_borderRadius), + bottomRight: Radius.circular(_borderRadius), + bottomLeft: Radius.circular(_borderRadius)); + bool isVTypeArrow = true; + final String side = + (centerX < rect.left + rect.width / 2) ? 'left' : 'right'; + if (!_isOutOfBoundInTop) { + locationY += arrowHeight - padding; + } + path.moveTo(rect.left + rect.tlRadiusX, rect.top); + path.arcToPoint(Offset(rect.left, rect.top + rect.tlRadiusY), + radius: rect.tlRadius, clockwise: false); + path.lineTo(rect.left, rect.bottom - rect.blRadiusY); + path.arcToPoint(Offset(rect.left + rect.blRadiusX, rect.bottom), + radius: rect.blRadius, clockwise: false); + if ((centerX > rect.left + rect.blRadiusX + arrowWidth / 2) && + (centerX < rect.right - rect.brRadiusX - arrowWidth / 2)) { + path.lineTo(centerX - arrowWidth / 2, rect.bottom); + } else { + isVTypeArrow = false; + if (side == 'right') { + path.lineTo(rect.right - rect.brRadiusX - arrowWidth * 2, rect.bottom); + } + } + if (!_isOutOfBoundInTop) { + //bottom arrow + path.lineTo(centerX.toDouble(), locationY); + path.lineTo( + isVTypeArrow + ? (centerX + arrowWidth / 2) + : side == 'left' + ? (rect.left + rect.blRadiusX + arrowWidth * 2) + : (rect.right - rect.brRadiusX), + rect.bottom); + } + path.lineTo(rect.right - rect.brRadiusX, rect.bottom); + path.arcToPoint(Offset(rect.right, rect.bottom - rect.brRadiusY), + radius: rect.brRadius, clockwise: false); + path.lineTo(rect.right, rect.top + rect.trRadiusY); + path.arcToPoint(Offset(rect.right - rect.brRadiusX, rect.top), + radius: rect.trRadius, clockwise: false); + if (isVTypeArrow) { + path.lineTo(centerX + arrowWidth / 2, rect.top); + } else { + if (side == 'left') { + path.lineTo(rect.left + rect.tlRadiusX + arrowWidth * 2, rect.top); + } + } + if (_isOutOfBoundInTop) { + //top arrow + path.lineTo(centerX.toDouble(), locationY); + path.lineTo( + isVTypeArrow + ? (centerX - arrowWidth / 2) + : side == 'left' + ? (rect.left + rect.blRadiusX) + : (rect.right - rect.brRadiusX - arrowWidth * 2), + rect.top); + } + path.lineTo(rect.left + rect.tlRadiusX, rect.top); + + canvas.drawPath(path, fillPaint); + canvas.drawPath(path, strokePaint); } /// To get the location of chart tooltip @@ -530,6 +662,8 @@ class TooltipRenderBox extends RenderShiftedBox { _y = position?.dy; } + /// renders the default tooltip view with a tooltip rect, arrow, header and + /// content void _renderDefaultTooltipView(Canvas canvas) { _isLeft = false; _isRight = false; @@ -587,7 +721,7 @@ class TooltipRenderBox extends RenderShiftedBox { } } - /// calculate tooltip rect and arrow head + /// calculate tooltip rect and arrow head for default tooltip mode Rect _calculateBackgroundRect( Canvas canvas, double height, double width, double headerTextHeight) { double widthPadding = 15; @@ -656,6 +790,8 @@ class TooltipRenderBox extends RenderShiftedBox { return Rect.fromLTWH(_xPos!, _yPos!, rect.width, rect.height); } + /// This method renders the tooltip background on which the content is to be + /// displayed void _drawTooltipBackground( Canvas canvas, bool isTop, @@ -674,63 +810,63 @@ class TooltipRenderBox extends RenderShiftedBox { final double animationFactor = tooltipAnimation == null ? 1 : tooltipAnimation.value; backgroundPath.reset(); - if (!_canResetPath) { - if (isLeft) { - startX = rectF.left + (2 * borderRadius); - endX = startX + _pointerLength; - } else if (isRight) { - startX = endX - _pointerLength; - endX = rectF.right - (2 * borderRadius); - } - - final Rect rect = Rect.fromLTWH( - rectF.width / 2 + (rectF.left - rectF.width / 2 * animationFactor), - rectF.height / 2 + (rectF.top - rectF.height / 2 * animationFactor), - rectF.width * animationFactor, - rectF.height * animationFactor); - - _tooltipRect = rect; - - final RRect tooltipRect = RRect.fromRectAndCorners( - rect, - bottomLeft: Radius.circular(borderRadius), - bottomRight: Radius.circular(borderRadius), - topLeft: Radius.circular(borderRadius), - topRight: Radius.circular(borderRadius), - ); - _drawTooltipPath(canvas, tooltipRect, rect, backgroundPath, isTop, isLeft, - isRight, startX, endX, animationFactor, xPosition, yPosition); + if (isLeft) { + startX = rectF.left + (2 * borderRadius); + endX = startX + _pointerLength; + } else if (isRight) { + startX = endX - _pointerLength; + endX = rectF.right - (2 * borderRadius); + } - final TextStyle textStyle = _tooltip.textStyle.copyWith( - color: _tooltip.textStyle.color?.withOpacity(_tooltip.opacity) ?? - _tooltip.labelColor, - fontSize: (_tooltip.textStyle.fontSize ?? 12.0) * animationFactor, - ); - final Size result = measureText(_stringValue!, textStyle); - _drawTooltipText(canvas, tooltipRect, textStyle, result, animationFactor); + final Rect rect = Rect.fromLTWH( + rectF.width / 2 + (rectF.left - rectF.width / 2 * animationFactor), + rectF.height / 2 + (rectF.top - rectF.height / 2 * animationFactor), + rectF.width * animationFactor, + rectF.height * animationFactor); + + _tooltipRect = rect; + + final RRect tooltipRect = RRect.fromRectAndCorners( + rect, + bottomLeft: Radius.circular(borderRadius), + bottomRight: Radius.circular(borderRadius), + topLeft: Radius.circular(borderRadius), + topRight: Radius.circular(borderRadius), + ); + _drawTooltipPath(canvas, tooltipRect, rect, backgroundPath, isTop, isLeft, + isRight, startX, endX, animationFactor, xPosition, yPosition); - if (_tooltip.canShowMarker && - _tooltipState.needMarker && - _markerTypes.isNotEmpty) { - if (_markerTypes.length == 1) { + final TextStyle textStyle = _tooltip.textStyle.copyWith( + color: _tooltip.textStyle.color?.withOpacity(_tooltip.opacity) ?? + _tooltip.labelColor, + fontSize: (_tooltip.textStyle.fontSize ?? 12.0) * animationFactor, + ); + final Size result = measureText(_stringValue!, textStyle); + _drawTooltipText(canvas, tooltipRect, textStyle, result, animationFactor); + + if (_tooltip.canShowMarker && + _tooltipState.needMarker && + _markerTypes.isNotEmpty) { + if (_markerTypes.length == 1) { + final Offset markerPoint = Offset( + tooltipRect.left + tooltipRect.width / 2 - result.width / 2, + ((tooltipRect.top + tooltipRect.height) - result.height / 2) - + markerSize); + _drawMarkers(markerPoint, canvas, animationFactor, 0); + } else { + double height = 0; + Size textSize = const Size(0, 0); + final List textValues = _stringValue!.split('\n'); + for (int i = 0; i < _markerTypes.length && i < textValues.length; i++) { + String str = ''; + str += textValues[i]; + final Size result1 = measureText(str, textStyle); final Offset markerPoint = Offset( tooltipRect.left + tooltipRect.width / 2 - result.width / 2, - ((tooltipRect.top + tooltipRect.height) - result.height / 2) - - markerSize); - _drawMarkers(markerPoint, canvas, animationFactor, 0); - } else { - Size textSize = const Size(0, 0); - final List textValues = _stringValue!.split('\n'); - for (int i = 0; - i < _markerTypes.length && i < textValues.length; - i++) { - String str = ''; - str += textValues[i]; - final Size result1 = measureText(str, textStyle); - final Offset markerPoint = Offset( - tooltipRect.left + tooltipRect.width / 2 - result1.width / 2, - (_markerPointY + textSize.height) - markerSize); - textSize = result1; + (_markerPointY + height) - markerSize); + textSize = result1; + height += textSize.height; + if (_markerTypes[i] != null) { _drawMarkers(markerPoint, canvas, animationFactor, i); } } @@ -740,6 +876,8 @@ class TooltipRenderBox extends RenderShiftedBox { _yPos = null; } + /// This method renders the tooltip marker shapes at the specific line indices + /// of the marker paint list void _drawMarkers( Offset markerPoint, Canvas canvas, double animationFactor, int i) { if (_markerImages[i] == null) { @@ -764,6 +902,7 @@ class TooltipRenderBox extends RenderShiftedBox { ..style = PaintingStyle.fill; } canvas.drawPath(markerPath, _markerPaints[i]!); + // ignore: omit_local_variable_types final Paint markerBorderPaint = Paint(); markerBorderPaint.color = Colors.white.withOpacity(_tooltip.opacity); markerBorderPaint.strokeWidth = 1; @@ -771,6 +910,7 @@ class TooltipRenderBox extends RenderShiftedBox { canvas.drawPath(markerPath, markerBorderPaint); } else { _markerSize *= 2 * animationFactor; + // ignore: omit_local_variable_types final Rect positionRect = Rect.fromLTWH(markerPoint.dx - _markerSize / 2, markerPoint.dy - _markerSize / 2, _markerSize, _markerSize); paintImage( @@ -781,7 +921,7 @@ class TooltipRenderBox extends RenderShiftedBox { } } - /// draw the tooltip rect path + /// This method renders the tooltip rect and arrow for default tooltip mode void _drawTooltipPath( Canvas canvas, RRect tooltipRect, @@ -858,7 +998,8 @@ class TooltipRenderBox extends RenderShiftedBox { canvas.drawPath(tooltipPath, strokePaint); } - /// draw tooltip header, divider,text + /// This method renders the tooltip header text, content text and the divider + /// line for default tooltip mode void _drawTooltipText(Canvas canvas, RRect tooltipRect, TextStyle textStyle, Size result, double animationFactor) { const double padding = 10; @@ -931,15 +1072,17 @@ class TooltipRenderBox extends RenderShiftedBox { } } - ///draw tooltip text - void _drawText(dynamic tooltip, Canvas canvas, String text, Offset point, + /// This method paints the given text at the required offset for default + /// tooltip mode + void _drawText(SfTooltip tooltip, Canvas canvas, String text, Offset point, TextStyle style, [int? maxLines, int? rotation]) { TextAlign tooltipTextAlign = TextAlign.start; double pointX = point.dx; + // ignore: unnecessary_null_comparison if (tooltip != null && tooltip.format != null && - tooltip.format.isNotEmpty) { + tooltip.format!.isNotEmpty) { if (tooltip.textAlignment == 'near') { tooltipTextAlign = TextAlign.start; pointX = _tooltipRect!.left; @@ -974,34 +1117,36 @@ class TooltipRenderBox extends RenderShiftedBox { canvas.restore(); } - /// It returns the offset values of tooltip location - Offset _getTemplateLocation(Rect tooltipRect, Rect bounds) { + /// This method returns the offset values of tooltip location in the template + /// mode + Offset _getTemplateLocation(Rect tooltipRect, Rect bounds, Offset offset) { double left = tooltipRect.left, top = tooltipRect.top; - if (tooltipRect.left < bounds.left) { - left = bounds.left; + const int padding = 5; + if (tooltipRect.left < bounds.left + offset.dx) { + left = bounds.left + offset.dx + padding; } - if (tooltipRect.top < bounds.top) { - top = bounds.top; + if (tooltipRect.top < bounds.top + offset.dy) { + top = bounds.top + offset.dy; _isOutOfBoundInTop = true; } - if (tooltipRect.left + tooltipRect.width > bounds.left + bounds.width) { - left = (bounds.left + bounds.width) - tooltipRect.width; + if (tooltipRect.left + tooltipRect.width > + bounds.left + offset.dx + bounds.width) { + left = (bounds.left + bounds.width + offset.dx) - + tooltipRect.width - + padding; } - if (tooltipRect.top + tooltipRect.height > bounds.top + bounds.height) { - top = (bounds.top + bounds.height) - tooltipRect.height; + if (tooltipRect.top + tooltipRect.height > + bounds.top + offset.dy + bounds.height) { + top = (bounds.top + offset.dy + bounds.height) - tooltipRect.height; } return Offset(left, top); } } -/// It returns the path of marker shapes +/// This method returns the path of marker shapes at the position at which the +/// marker needs to be rendered Path _getMarkerShapesPath( DataMarkerType markerType, Offset position, dynamic image, Size size) { - // [CartesianSeriesRenderer seriesRenderer, - // [int index, - // // TrackballBehavior trackballBehavior, - // Animation animationController]) { - final Path path = Path(); switch (markerType) { case DataMarkerType.circle: diff --git a/packages/syncfusion_flutter_core/lib/src/utils/helper.dart b/packages/syncfusion_flutter_core/lib/src/utils/helper.dart index e3e60b195..92f289f58 100644 --- a/packages/syncfusion_flutter_core/lib/src/utils/helper.dart +++ b/packages/syncfusion_flutter_core/lib/src/utils/helper.dart @@ -2,7 +2,8 @@ part of core; /// Holds the arguments for the event onTooltipRender. /// -/// Event is triggered when the tooltip is rendered, which allows you to customize tooltip arguments. +/// Event is triggered when the tooltip is rendered, which allows you to +/// customize tooltip arguments. class TooltipRenderArgs { /// Creating an argument constructor of TooltipArgs class. TooltipRenderArgs(this.header, this.text, this.location); @@ -34,7 +35,8 @@ enum TooltipAlignment { /// Data marker shapes. /// /// Data marker supports the below shapes. -/// If the shape is DataMarkerType.image, specify the image path in the imageUrl property of markerSettings. +/// If the shape is DataMarkerType.image, specify the image path in the +/// imageUrl property of markerSettings. enum DataMarkerType { ///- DataMarkerType.cicle, will render marker shape circle. circle, @@ -60,7 +62,8 @@ enum DataMarkerType { ///- DataMarkerType.triangle, will render marker shape triangle. triangle, - ///- DataMarkerType.invertedTriangle, will render marker shape invertedTriangle. + ///- DataMarkerType.invertedTriangle, will render + /// marker shape invertedTriangle. invertedTriangle, ///- DataMarkerType.none, will skip rendering marker. diff --git a/packages/syncfusion_flutter_core/lib/src/utils/shape_helper.dart b/packages/syncfusion_flutter_core/lib/src/utils/shape_helper.dart new file mode 100644 index 000000000..61a109257 --- /dev/null +++ b/packages/syncfusion_flutter_core/lib/src/utils/shape_helper.dart @@ -0,0 +1,1486 @@ +import 'dart:math'; +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +/// Apply the different marker pointer. +enum ShapeMarkerType { + /// Draw a image. + image, + + /// ShapeMarkerType.circle points the value with circle. + circle, + + /// ShapeMarkerType.rectangle points the value with rectangle + rectangle, + + /// ShapeMarkerType.diamond points the value with diamond. + diamond, + + /// ShapeMarkerType.Triangle points the value with triangle. + triangle, + + /// ShapeMarkerType.InvertedTriangle points the value with inverted triangle. + invertedTriangle, + + /// ShapeMarkerType.VerticalTriangle points the value with triangle. + verticalTriangle, + + /// ShapeMarkerType.VerticalInvertedTriangle points the value with triangle. + verticalInvertedTriangle, + + /// ShapeMarkerType.pentagon points the value with pentagon. + pentagon, + + /// ShapeMarkerType.verticalLine points the value with vertical line. + verticalLine, + + /// ShapeMarkerType.horizontalLine points the value with horizontal line. + horizontalLine, + + /// ShapeMarkerType.lineSeries which draws the line series shape. + lineSeries, + + /// ShapeMarkerType.lineSeriesWithMarker which draws the + /// line series shape with marker. + lineSeriesWithMarker, + + /// ShapeMarkerType.fastLineSeries which draws the fast + /// line series shape. + fastLineSeries, + + /// ShapeMarkerType.fastLineSeriesWithMarker which draws the fast + /// line series marker. + fastLineSeriesWithMarker, + + /// ShapeMarkerType.stackedLineSeries which draws the stacked + /// line series shape. + stackedLineSeries, + + /// ShapeMarkerType.stackedLineSeriesMarker which draws the stacked + /// line series marker. + stackedLineSeriesWithMarker, + + /// ShapeMarkerType.stackedLine100Series which draws + /// the stacked line 100 series shape. + stackedLine100Series, + + /// ShapeMarkerType.stackedLine100SeriesWithMarker which draws + /// the stacked line 100 series marker. + stackedLine100SeriesWithMarker, + + /// ShapeMarkerType.splineSeries which draws the spline series marker. + splineSeries, + + /// ShapeMarkerType.splineAreaSeries which draws the + /// spline area series marker. + splineAreaSeries, + + /// ShapeMarkerType.splineRangeAreaSeries which draws the + /// spline range area series marker. + splineRangeAreaSeries, + + /// ShapeMarkerType.areaSeriesType which draws the area series marker. + areaSeries, + + /// ShapeMarkerType.stackedAreaSeries which draws the + /// stacked area series marker. + stackedAreaSeries, + + /// ShapeMarkerType.rangeAreaSeries which draws the + /// range area series marker. + rangeAreaSeries, + + /// ShapeMarkerType.stackedArea100Series which draws the + /// stacked area 100 series marker. + stackedArea100Series, + + /// ShapeMarkerType.stepAreaSeries which draws the + /// step area series marker. + stepAreaSeries, + + /// ShapeMarkerType.stepLineSeries which draws the + /// step line series marker. + stepLineSeries, + + /// ShapeMarkerType.bubbleSeries which draws the bubble series marker. + bubbleSeries, + + /// ShapeMarkerType.columnSeries which draws the column series marker. + columnSeries, + + /// ShapeMarkerType.stackedColumnSeries which draws the + /// stacked column series marker. + stackedColumnSeries, + + /// ShapeMarkerType.stackedColumn100Series which draws the + /// stacked column 100 series marker. + stackedColumn100Series, + + /// ShapeMarkerType.rangeColumnSeries which draws the + /// range column series marker. + rangeColumnSeries, + + /// ShapeMarkerType.histogramSeries which draws the + /// histogram series marker. + histogramSeries, + + /// ShapeMarkerType.barSeries which draws the bar series marker. + barSeries, + + /// ShapeMarkerType.stackedBarSeries which draws the + /// stacked bar series marker. + stackedBarSeries, + + /// ShapeMarkerType.stackedBar100Series which draws the + /// stacked bar 100 series marker. + stackedBar100Series, + + /// ShapeMarkerType.hiloSeries which draws the hilo series marker. + hiloSeries, + + /// ShapeMarkerType.hiloOpenCloseSeries which draws the + /// hilo open close series marker. + hiloOpenCloseSeries, + + /// ShapeMarkerType.candleSeries which draws the candle series marker. + candleSeries, + + /// ShapeMarkerType.waterfallSeries which draws the + /// waterfall series marker. + waterfallSeries, + + /// ShapeMarkerType.boxAndWhiskerSeries which draws the + /// box and whisker series marker. + boxAndWhiskerSeries, + + /// ShapeMarkerType.pieSeries which draws the pie series marker. + pieSeries, + + /// ShapeMarkerType.doughnutSeries which draws the doughnut + /// series marker. + doughnutSeries, + + /// ShapeMarkerType.radialBarSeries which draws the radial bar + /// series marker. + radialBarSeries, + + /// ShapeMarkerType.pyramidSeries which draws the pyramid series marker. + pyramidSeries, + + /// ShapeMarkerType.funnelSeries which draws the funnel series marker. + funnelSeries +} + +/// Represents the Shape marker painter. +class ShapePainter { + /// Draws the different marker shapes. + static void paint( + {required Canvas canvas, + required Rect rect, + required ShapeMarkerType shapeType, + required Paint paint, + Path? path, + double? elevation, + Color? elevationColor, + Paint? borderPaint}) { + _processShapes( + canvas: canvas, + rect: rect, + shapeType: shapeType, + paint: paint, + path: path ?? Path(), + borderPaint: borderPaint, + isNeedToReturnPath: false, + elevation: elevation, + elevationColor: elevationColor); + } + + /// Get the various shape path + static Path getShapesPath( + {Canvas? canvas, + Paint? paint, + Paint? borderPaint, + required Rect rect, + required ShapeMarkerType shapeType, + Path? path, + double? pentagonRotation = -pi / 2, + double? radius, + double? degree, + double? startAngle, + double? endAngle}) { + return _processShapes( + canvas: canvas ?? Canvas(PictureRecorder()), + paint: paint ?? Paint(), + borderPaint: borderPaint, + rect: rect, + path: path ?? Path(), + shapeType: shapeType, + isNeedToReturnPath: true, + pentagonRotation: pentagonRotation, + radius: radius, + degree: degree, + startAngle: startAngle, + endAngle: endAngle); + } + + static Path _processShapes( + {required Canvas canvas, + required Rect rect, + required ShapeMarkerType shapeType, + required Paint paint, + required bool isNeedToReturnPath, + required Path path, + double? elevation, + Color? elevationColor, + Paint? borderPaint, + double? pentagonRotation = -pi / 2, + double? radius, + double? degree, + double? startAngle, + double? endAngle}) { + switch (shapeType) { + case ShapeMarkerType.circle: + return _processCircleShape( + canvas: canvas, + rect: rect, + path: path, + isNeedToReturnPath: isNeedToReturnPath, + elevation: elevation, + elevationColor: elevationColor, + paint: paint, + borderPaint: borderPaint); + case ShapeMarkerType.rectangle: + return _processRectangleShape( + canvas: canvas, + rect: rect, + path: path, + isNeedToReturnPath: isNeedToReturnPath, + elevation: elevation, + elevationColor: elevationColor, + paint: paint, + borderPaint: borderPaint); + case ShapeMarkerType.diamond: + return _processDiamondShape( + canvas: canvas, + rect: rect, + path: path, + isNeedToReturnPath: isNeedToReturnPath, + elevation: elevation, + elevationColor: elevationColor, + paint: paint, + borderPaint: borderPaint); + case ShapeMarkerType.triangle: + return _processTriangleShape( + canvas: canvas, + rect: rect, + path: path, + isNeedToReturnPath: isNeedToReturnPath, + elevation: elevation, + elevationColor: elevationColor, + paint: paint, + borderPaint: borderPaint); + case ShapeMarkerType.invertedTriangle: + return _processInvertedTriangleShape( + canvas: canvas, + rect: rect, + path: path, + isNeedToReturnPath: isNeedToReturnPath, + elevation: elevation, + elevationColor: elevationColor, + paint: paint, + borderPaint: borderPaint); + case ShapeMarkerType.verticalTriangle: + return _processVerticalTriangleShape( + canvas: canvas, + rect: rect, + path: path, + isNeedToReturnPath: isNeedToReturnPath, + elevation: elevation, + elevationColor: elevationColor, + paint: paint, + borderPaint: borderPaint); + case ShapeMarkerType.verticalInvertedTriangle: + return _processVerticalInvertedTriangleShape( + canvas: canvas, + rect: rect, + path: path, + isNeedToReturnPath: isNeedToReturnPath, + elevation: elevation, + elevationColor: elevationColor, + paint: paint, + borderPaint: borderPaint); + case ShapeMarkerType.pentagon: + return _processPentagonShape( + canvas: canvas, + rect: rect, + path: path, + isNeedToReturnPath: isNeedToReturnPath, + rotation: pentagonRotation, + elevation: elevation, + elevationColor: elevationColor, + paint: paint, + borderPaint: borderPaint); + case ShapeMarkerType.verticalLine: + return _processVerticalLineShape( + canvas: canvas, + rect: rect, + path: path, + isNeedToReturnPath: isNeedToReturnPath, + borderPaint: borderPaint); + case ShapeMarkerType.horizontalLine: + return _processHorizontalLineShape( + canvas: canvas, + rect: rect, + path: path, + isNeedToReturnPath: isNeedToReturnPath, + borderPaint: borderPaint); + case ShapeMarkerType.lineSeries: + case ShapeMarkerType.fastLineSeries: + case ShapeMarkerType.stackedLineSeries: + case ShapeMarkerType.stackedLine100Series: + return _processLineShape( + canvas: canvas, + path: path, + rect: rect, + borderPaint: paint, + isNeedToReturnPath: isNeedToReturnPath, + isNeedMarker: false); + case ShapeMarkerType.lineSeriesWithMarker: + case ShapeMarkerType.fastLineSeriesWithMarker: + case ShapeMarkerType.stackedLineSeriesWithMarker: + case ShapeMarkerType.stackedLine100SeriesWithMarker: + return _processLineShape( + canvas: canvas, + path: path, + rect: rect, + borderPaint: paint, + isNeedToReturnPath: isNeedToReturnPath, + isNeedMarker: true); + case ShapeMarkerType.splineSeries: + return _processSplineShape( + canvas: canvas, + rect: rect, + path: path, + paint: paint, + isNeedToReturnPath: isNeedToReturnPath); + case ShapeMarkerType.splineAreaSeries: + case ShapeMarkerType.splineRangeAreaSeries: + return _processSplineAreaShape( + canvas: canvas, + rect: rect, + path: path, + paint: paint, + borderPaint: borderPaint, + isNeedToReturnPath: isNeedToReturnPath); + case ShapeMarkerType.areaSeries: + case ShapeMarkerType.stackedAreaSeries: + case ShapeMarkerType.rangeAreaSeries: + case ShapeMarkerType.stackedArea100Series: + return _processAreaShape( + canvas: canvas, + rect: rect, + path: path, + paint: paint, + borderPaint: borderPaint, + isNeedToReturnPath: isNeedToReturnPath); + case ShapeMarkerType.stepAreaSeries: + return _processStepAreaShape( + canvas: canvas, + rect: rect, + path: path, + paint: paint, + borderPaint: borderPaint, + isNeedToReturnPath: isNeedToReturnPath); + case ShapeMarkerType.stepLineSeries: + return _processStepLineShape( + canvas: canvas, + rect: rect, + path: path, + paint: paint, + borderPaint: borderPaint, + isNeedToReturnPath: isNeedToReturnPath); + case ShapeMarkerType.bubbleSeries: + return _processBubbleShape( + canvas: canvas, + rect: rect, + path: path, + paint: paint, + borderPaint: borderPaint, + isNeedToReturnPath: isNeedToReturnPath); + case ShapeMarkerType.columnSeries: + case ShapeMarkerType.stackedColumnSeries: + case ShapeMarkerType.stackedColumn100Series: + case ShapeMarkerType.rangeColumnSeries: + case ShapeMarkerType.histogramSeries: + return _processColumnShape( + canvas: canvas, + rect: rect, + path: path, + paint: paint, + borderPaint: borderPaint, + isNeedToReturnPath: isNeedToReturnPath); + case ShapeMarkerType.barSeries: + case ShapeMarkerType.stackedBarSeries: + case ShapeMarkerType.stackedBar100Series: + return _processBarShape( + canvas: canvas, + rect: rect, + path: path, + paint: paint, + borderPaint: borderPaint, + isNeedToReturnPath: isNeedToReturnPath); + case ShapeMarkerType.hiloSeries: + return _processHiloShape( + canvas: canvas, + rect: rect, + path: path, + paint: paint, + borderPaint: borderPaint, + isNeedToReturnPath: isNeedToReturnPath); + case ShapeMarkerType.hiloOpenCloseSeries: + case ShapeMarkerType.candleSeries: + return _processHiloOpenCloseShape( + canvas: canvas, + rect: rect, + path: path, + paint: paint, + borderPaint: borderPaint, + isNeedToReturnPath: isNeedToReturnPath); + case ShapeMarkerType.waterfallSeries: + case ShapeMarkerType.boxAndWhiskerSeries: + return _processWaterfallShape( + canvas: canvas, + rect: rect, + path: path, + paint: paint, + borderPaint: borderPaint, + isNeedToReturnPath: isNeedToReturnPath); + case ShapeMarkerType.pieSeries: + return _processPieShape( + canvas: canvas, + rect: rect, + path: path, + paint: paint, + borderPaint: borderPaint, + isNeedToReturnPath: isNeedToReturnPath); + case ShapeMarkerType.doughnutSeries: + return _processDoughnutShape( + canvas: canvas, + rect: rect, + radius: radius!, + path: path, + paint: paint, + borderPaint: borderPaint, + isNeedToReturnPath: isNeedToReturnPath); + case ShapeMarkerType.radialBarSeries: + return _processRadialBarShape( + rect: rect, + canvas: canvas, + radius: radius, + path: path, + paint: paint, + borderPaint: borderPaint, + degree: degree, + isNeedToReturnPath: isNeedToReturnPath, + startAngle: startAngle, + endAngle: endAngle); + case ShapeMarkerType.pyramidSeries: + return _processPyramidShape( + canvas: canvas, + rect: rect, + path: path, + paint: paint, + borderPaint: borderPaint, + isNeedToReturnPath: isNeedToReturnPath); + case ShapeMarkerType.funnelSeries: + return _processFunnelShape( + canvas: canvas, + rect: rect, + path: path, + paint: paint, + borderPaint: borderPaint, + isNeedToReturnPath: isNeedToReturnPath); + case ShapeMarkerType.image: + return Path(); + } + } + + /// Draw the circle shape marker. + static Path _processCircleShape( + {required Canvas canvas, + required Rect rect, + required Paint paint, + required bool isNeedToReturnPath, + required Path path, + double? elevation, + Color? elevationColor, + Paint? borderPaint}) { + path.addOval(rect); + + if (isNeedToReturnPath) { + return path; + } + + if (elevation != null && elevation > 0 && elevationColor != null) { + canvas.drawShadow(path, elevationColor, elevation, true); + } + + canvas.drawPath(path, paint); + + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + return path; + } + + /// Draw the rectangle shape marker. + static Path _processRectangleShape( + {required Canvas canvas, + required Rect rect, + required Paint paint, + required bool isNeedToReturnPath, + required Path path, + double? elevation, + Color? elevationColor, + Paint? borderPaint}) { + path.addRect(rect); + + if (isNeedToReturnPath) { + return path; + } + + if (elevation != null && elevation > 0 && elevationColor != null) { + canvas.drawShadow(path, elevationColor, elevation, true); + } + + canvas.drawPath(path, paint); + + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + return path; + } + + /// Draw the inverted triangle shape marker. + static Path _processInvertedTriangleShape( + {required Canvas canvas, + required Rect rect, + required bool isNeedToReturnPath, + required Path path, + double? elevation, + Color? elevationColor, + required Paint paint, + Paint? borderPaint}) { + path.moveTo(rect.left, rect.top); + path.lineTo(rect.left + rect.width, rect.top); + path.lineTo(rect.left + (rect.width / 2), rect.top + rect.height); + path.close(); + + if (isNeedToReturnPath) { + return path; + } + + if (elevation != null && elevation > 0 && elevationColor != null) { + canvas.drawShadow(path, elevationColor, elevation, true); + } + + canvas.drawPath(path, paint); + + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + + return path; + } + + /// Draw the triangle shape marker. + static Path _processTriangleShape( + {required Canvas canvas, + required Rect rect, + required bool isNeedToReturnPath, + required Path path, + double? elevation, + Color? elevationColor, + required Paint paint, + Paint? borderPaint}) { + path.moveTo(rect.left + (rect.width / 2), rect.top); + path.lineTo(rect.left, rect.top + rect.height); + path.lineTo(rect.left + rect.width, rect.top + rect.height); + path.close(); + + if (isNeedToReturnPath) { + return path; + } + + if (elevation != null && elevation > 0 && elevationColor != null) { + canvas.drawShadow(path, elevationColor, elevation, true); + } + + canvas.drawPath(path, paint); + + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + return path; + } + + ///Draw the triangle shape marker + static Path _processVerticalTriangleShape( + {required Canvas canvas, + required Rect rect, + required bool isNeedToReturnPath, + required Path path, + double? elevation, + Color? elevationColor, + required Paint paint, + Paint? borderPaint}) { + path.moveTo(rect.left, rect.top + (rect.height / 2)); + path.lineTo(rect.left + rect.width, rect.top); + path.lineTo(rect.left + rect.width, rect.top + rect.height); + path.close(); + + if (isNeedToReturnPath) { + return path; + } + + if (elevation != null && elevation > 0 && elevationColor != null) { + canvas.drawShadow(path, elevationColor, elevation, true); + } + + canvas.drawPath(path, paint); + + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + return path; + } + + ///Draw the vertical inverted triangle shape marker + static Path _processVerticalInvertedTriangleShape( + {required Canvas canvas, + required Rect rect, + required bool isNeedToReturnPath, + required Path path, + double? elevation, + Color? elevationColor, + required Paint paint, + Paint? borderPaint}) { + path.moveTo(rect.left, rect.top); + path.lineTo(rect.left + rect.width, rect.top + (rect.height / 2)); + path.lineTo(rect.left, rect.top + rect.height); + path.close(); + + if (isNeedToReturnPath) { + return path; + } + + if (elevation != null && elevation > 0 && elevationColor != null) { + canvas.drawShadow(path, elevationColor, elevation, true); + } + + canvas.drawPath(path, paint); + + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + return path; + } + + /// Draw the diamond shape marker. + static Path _processDiamondShape( + {required Canvas canvas, + required Rect rect, + required bool isNeedToReturnPath, + required Path path, + double? elevation, + Color? elevationColor, + required Paint paint, + Paint? borderPaint}) { + path.moveTo(rect.left + rect.width / 2.0, rect.top); + path.lineTo(rect.left, rect.top + rect.height / 2.0); + path.lineTo(rect.left + rect.width / 2.0, rect.top + rect.height); + path.lineTo(rect.left + rect.width, rect.top + rect.height / 2.0); + path.close(); + + if (isNeedToReturnPath) { + return path; + } + + if (elevation != null && elevation > 0 && elevationColor != null) { + canvas.drawShadow(path, elevationColor, elevation, true); + } + + canvas.drawPath(path, paint); + + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + + return path; + } + + ///Draw the pentagon shape marker + static Path _processPentagonShape( + {required Canvas canvas, + required Rect rect, + required bool isNeedToReturnPath, + required Path path, + double? elevation, + Color? elevationColor, + required Paint paint, + Paint? borderPaint, + double? rotation}) { + const int numberOfSides = 5; + final double left = rect.left + rect.width / 2; + final double top = rect.top + rect.height / 2; + final double radius = rect.width / 2; + + for (int i = 0; i <= numberOfSides; i++) { + final double angle = ((i / 5) * pi * 2 + rotation!).toDouble(); + i == 0 + ? path.moveTo(cos(angle) * radius + left, sin(angle) * radius + top) + : path.lineTo(cos(angle) * radius + left, sin(angle) * radius + top); + } + + if (isNeedToReturnPath) { + return path; + } + + if (elevation != null && elevation > 0 && elevationColor != null) { + canvas.drawShadow(path, elevationColor, elevation, true); + } + canvas.drawPath(path, paint); + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + return path; + } + + /// Draw the vertical line shape marker. + static Path _processVerticalLineShape( + {required Canvas canvas, + required bool isNeedToReturnPath, + required Path path, + required Rect rect, + required Paint? borderPaint}) { + final double left = rect.left + rect.width / 2; + final double top = rect.top + rect.height / 2; + + path.moveTo(left, top + rect.height / 2); + path.lineTo(left, top - rect.height / 2); + + if (isNeedToReturnPath) { + return path; + } + + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + + return path; + } + + /// Draw the horizontal line shape marker. + static Path _processHorizontalLineShape( + {required Canvas canvas, + required Rect rect, + required bool isNeedToReturnPath, + required Path path, + required Paint? borderPaint}) { + final double left = rect.left + rect.width / 2; + final double top = rect.top + rect.height / 2; + + path.moveTo(left - rect.width / 2, top); + path.lineTo(left + rect.width / 2, top); + + if (isNeedToReturnPath) { + return path; + } + + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + return path; + } + + /// Draw the step line series type. + static Path _processStepLineShape( + {required Canvas canvas, + required Rect rect, + required bool isNeedToReturnPath, + required Path path, + Paint? paint, + Paint? borderPaint}) { + final double x = rect.left + rect.width / 2; + final double y = rect.top + rect.height / 2; + final double width = rect.width; + final double height = rect.height; + + const num padding = 10; + path.moveTo(x - (width / 2) - (padding / 4), y + (height / 2)); + path.lineTo(x - (width / 2) + (width / 10), y + (height / 2)); + path.lineTo(x - (width / 2) + (width / 10), y); + path.lineTo(x - (width / 10), y); + path.lineTo(x - (width / 10), y + (height / 2)); + path.lineTo(x + (width / 5), y + (height / 2)); + path.lineTo(x + (width / 5), y - (height / 2)); + path.lineTo(x + (width / 2), y - (height / 2)); + path.lineTo(x + (width / 2), y + (height / 2)); + path.lineTo(x + (width / 2) + (padding / 4), y + (height / 2)); + + if (isNeedToReturnPath) { + return path; + } + + canvas.drawPath(path, paint!); + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + return path; + } + + /// Draw the pie series type. + static Path _processPieShape( + {required Canvas canvas, + required Rect rect, + required bool isNeedToReturnPath, + required Path path, + Paint? paint, + Paint? borderPaint}) { + final double x = rect.left + rect.width / 2; + final double y = rect.top + rect.height / 2; + final double width = rect.width; + final double height = rect.height; + + final double r = min(height, width) / 2; + path.moveTo(x, y); + path.lineTo(x + r, y); + path.arcTo( + Rect.fromCircle(center: Offset(x, y), radius: r), + _degreesToRadians(0).toDouble(), + _degreesToRadians(270).toDouble(), + false); + path.close(); + path.moveTo(x + width / 10, y - height / 10); + path.lineTo(x + r, y - height / 10); + path.arcTo( + Rect.fromCircle(center: Offset(x + 2, y - 2), radius: r), + _degreesToRadians(-5).toDouble(), + _degreesToRadians(-80).toDouble(), + false); + path.close(); + + if (isNeedToReturnPath) { + return path; + } + + canvas.drawPath(path, paint!); + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + + return path; + } + + /// Draw the doughnut series type. + static Path _processDoughnutShape( + {required Canvas canvas, + required Rect rect, + required double radius, + required bool isNeedToReturnPath, + required Path path, + Paint? paint, + Paint? borderPaint}) { + final double x = rect.left + rect.width / 2; + final double y = rect.top + rect.height / 2; + late Path path1, path2; + if (borderPaint != null) { + path1 = _getArcPath( + path, radius / 4, radius / 2, Offset(x, y), 0, 270, 270, true); + } else { + path2 = _getArcPath(path, radius / 4, radius / 2, Offset(x + 1, y - 1), + -5, -85, -85, true); + } + + if (isNeedToReturnPath) { + return path; + } + + canvas.drawPath(path1, paint!); + if (borderPaint != null) { + canvas.drawPath(path1, borderPaint); + } + canvas.drawPath(path2, paint); + if (borderPaint != null) { + canvas.drawPath(path2, borderPaint); + } + + return path; + } + + /// Draw the radial bar series type. + static Path _processRadialBarShape( + {required Canvas canvas, + required Rect rect, + required bool isNeedToReturnPath, + required Path path, + double? degree, + double? startAngle, + double? endAngle, + double? radius, + Paint? paint, + Paint? borderPaint}) { + final double x = rect.left + rect.width / 2; + final double y = rect.top + rect.height / 2; + + late Path path1, path2; + + radius ??= (rect.width + rect.height) / 2; + + if (borderPaint != null) { + path1 = _getArcPath(path, (radius / 2) - 2, radius / 2, Offset(x, y), 0, + 360 - 0.01, 360 - 0.01, true); + } else { + path2 = _getArcPath(path, (radius / 2) - 2, radius / 2, Offset(x, y), + startAngle!, endAngle!, degree!, true); + } + + if (isNeedToReturnPath) { + return path; + } + + canvas.drawPath(path1, paint!); + + if (borderPaint != null) { + canvas.drawPath(path1, borderPaint); + } + + canvas.drawPath(path2, paint); + + if (borderPaint != null) { + canvas.drawPath(path2, borderPaint); + } + + return path; + } + + /// Get the path of arc + static Path _getArcPath( + Path path, + double innerRadius, + double radius, + Offset center, + double startAngle, + double endAngle, + double degree, + bool isAnimate) { + startAngle = _degreesToRadians(startAngle); + endAngle = _degreesToRadians(endAngle); + degree = _degreesToRadians(degree); + + final Point innerRadiusStartPoint = Point( + innerRadius * cos(startAngle) + center.dx, + innerRadius * sin(startAngle) + center.dy); + final Point innerRadiusEndPoint = Point( + innerRadius * cos(endAngle) + center.dx, + innerRadius * sin(endAngle) + center.dy); + + final Point radiusStartPoint = Point( + radius * cos(startAngle) + center.dx, + radius * sin(startAngle) + center.dy); + + if (isAnimate) { + path.moveTo(innerRadiusStartPoint.x, innerRadiusStartPoint.y); + } + + final bool isFullCircle = + // ignore: unnecessary_null_comparison + startAngle != null && + // ignore: unnecessary_null_comparison + endAngle != null && + endAngle - startAngle == 2 * pi; + + final num midpointAngle = (endAngle + startAngle) / 2; + + if (isFullCircle) { + path.arcTo( + Rect.fromCircle(center: center, radius: radius.toDouble()), + startAngle.toDouble(), + midpointAngle.toDouble() - startAngle.toDouble(), + true); + path.arcTo( + Rect.fromCircle(center: center, radius: radius.toDouble()), + midpointAngle.toDouble(), + endAngle.toDouble() - midpointAngle.toDouble(), + true); + } else { + path.lineTo(radiusStartPoint.x, radiusStartPoint.y); + path.arcTo(Rect.fromCircle(center: center, radius: radius.toDouble()), + startAngle.toDouble(), degree.toDouble(), true); + } + + if (isFullCircle) { + path.arcTo( + Rect.fromCircle(center: center, radius: innerRadius.toDouble()), + endAngle.toDouble(), + midpointAngle.toDouble() - endAngle.toDouble(), + true); + path.arcTo( + Rect.fromCircle(center: center, radius: innerRadius.toDouble()), + midpointAngle.toDouble(), + startAngle - midpointAngle.toDouble(), + true); + } else { + path.lineTo(innerRadiusEndPoint.x, innerRadiusEndPoint.y); + path.arcTo( + Rect.fromCircle(center: center, radius: innerRadius.toDouble()), + endAngle.toDouble(), + startAngle.toDouble() - endAngle.toDouble(), + true); + path.lineTo(radiusStartPoint.x, radiusStartPoint.y); + } + return path; + } + + /// Convert degree to radian + static double _degreesToRadians(double deg) => deg * (pi / 180); + + /// Draw the hilo series type. + static Path _processHiloShape( + {required Canvas canvas, + required Rect rect, + required bool isNeedToReturnPath, + required Path path, + Paint? paint, + Paint? borderPaint}) { + final double x = rect.left + rect.width / 2; + final double y = rect.top + rect.height / 2; + final double height = rect.height; + + path.moveTo(x, y + height / 2); + path.lineTo(x, y - height / 2); + + if (isNeedToReturnPath) { + return path; + } + + canvas.drawPath(path, paint!); + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + + return path; + } + + /// Draw the hilo open close series type. + static Path _processHiloOpenCloseShape( + {required Canvas canvas, + required Rect rect, + required bool isNeedToReturnPath, + required Path path, + Paint? paint, + Paint? borderPaint}) { + final double x = rect.left + rect.width / 2; + final double y = rect.top + rect.height / 2; + final double width = rect.width; + + path.moveTo(x - width / 2, y); + path.lineTo(x + width / 2, y); + + if (isNeedToReturnPath) { + return path; + } + + canvas.drawPath(path, paint!); + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + return path; + } + + /// Draw the waterfall series type. + static Path _processWaterfallShape( + {required Canvas canvas, + required Rect rect, + required bool isNeedToReturnPath, + required Path path, + Paint? paint, + Paint? borderPaint}) { + final double x = rect.left + rect.width / 2; + final double y = rect.top + rect.height / 2; + final double width = rect.width; + final double height = rect.height; + path.addRect(Rect.fromLTRB( + x - width / 2, y - height / 2, x + width / 2, y + height / 2)); + + if (isNeedToReturnPath) { + return path; + } + + canvas.drawPath(path, paint!); + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + + return path; + } + + /// Draw the pyramid series type. + static Path _processPyramidShape( + {required Canvas canvas, + required Rect rect, + required bool isNeedToReturnPath, + required Path path, + Paint? paint, + Paint? borderPaint}) { + final double x = rect.left + rect.width / 2; + final double y = rect.top + rect.height / 2; + final double width = rect.width; + final double height = rect.height; + + path.moveTo(x - width / 2, y + height / 2); + path.lineTo(x + width / 2, y + height / 2); + path.lineTo(x, y - height / 2); + path.lineTo(x - width / 2, y + height / 2); + path.close(); + if (isNeedToReturnPath) { + return path; + } + + canvas.drawPath(path, paint!); + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + + return path; + } + + /// Draw the funnel series type. + static Path _processFunnelShape( + {required Canvas canvas, + required Rect rect, + required bool isNeedToReturnPath, + required Path path, + Paint? paint, + Paint? borderPaint}) { + final double x = rect.left + rect.width / 2; + final double y = rect.top + rect.height / 2; + final double width = rect.width; + final double height = rect.height; + + path.moveTo(x + width / 2, y - height / 2); + path.lineTo(x, y + height / 2); + path.lineTo(x - width / 2, y - height / 2); + path.lineTo(x + width / 2, y - height / 2); + path.close(); + + if (isNeedToReturnPath) { + return path; + } + + canvas.drawPath(path, paint!); + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + + return path; + } + + /// Draw the bubble series type. + static Path _processBubbleShape( + {required Canvas canvas, + required Rect rect, + required bool isNeedToReturnPath, + required Path path, + Paint? paint, + Paint? borderPaint}) { + final double x = rect.left + rect.width / 2; + final double y = rect.top + rect.height / 2; + final double width = rect.width; + final double height = rect.height; + + path.addArc(Rect.fromLTWH(x - width / 2, y - height / 2, width, height), + 0.0, 2 * pi); + + if (isNeedToReturnPath) { + return path; + } + + canvas.drawPath(path, paint!); + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + + return path; + } + + /// Draw the step are series type. + static Path _processStepAreaShape( + {required Canvas canvas, + required Rect rect, + required bool isNeedToReturnPath, + required Path path, + Paint? paint, + Paint? borderPaint}) { + final double x = rect.left + rect.width / 2; + final double y = rect.top + rect.height / 2; + final double width = rect.width; + final double height = rect.height; + + const num padding = 10; + path.moveTo(x - (width / 2) - (padding / 4), y + (height / 2)); + path.lineTo(x - (width / 2) - (padding / 4), y - (height / 4)); + path.lineTo(x - (width / 2) + (width / 10), y - (height / 4)); + path.lineTo(x - (width / 2) + (width / 10), y - (height / 2)); + path.lineTo(x - (width / 10), y - (height / 2)); + path.lineTo(x - (width / 10), y); + path.lineTo(x + (width / 5), y); + path.lineTo(x + (width / 5), y - (height / 3)); + path.lineTo(x + (width / 2), y - (height / 3)); + path.lineTo(x + (width / 2), y + (height / 2)); + path.close(); + + if (isNeedToReturnPath) { + return path; + } + + canvas.drawPath(path, paint!); + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + + return path; + } + + /// Draw the spline area series type. + static Path _processSplineAreaShape( + {required Canvas canvas, + required Rect rect, + required bool isNeedToReturnPath, + required Path path, + Paint? paint, + Paint? borderPaint}) { + final double x = rect.left + rect.width / 2; + final double y = rect.top + rect.height / 2; + final double width = rect.width; + final double height = rect.height; + + path.moveTo(x - width / 2, y + height / 2); + path.quadraticBezierTo(x, y - height, x, y + height / 5); + path.quadraticBezierTo( + x + width / 2, y - height / 2, x + width / 2, y + height / 2); + + if (isNeedToReturnPath) { + return path; + } + + canvas.drawPath(path, paint!); + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + + return path; + } + + /// Draw the line series type. + static Path _processLineShape( + {required Canvas canvas, + required Rect rect, + required bool isNeedToReturnPath, + required Path path, + Paint? borderPaint, + bool? isNeedMarker}) { + final double left = rect.left + rect.width / 2; + final double top = rect.top + rect.height / 2; + + path.moveTo(left - rect.width / 1.5, top); + path.lineTo(left + rect.width / 1.5, top); + + if (isNeedToReturnPath) { + return path; + } + if (borderPaint != null) { + canvas.drawPath(path, borderPaint..style = PaintingStyle.stroke); + } + + if (isNeedMarker! && borderPaint != null) { + path.close(); + path.addOval(Rect.fromCenter( + center: Offset(left, top), + width: rect.width / 1.5, + height: rect.height / 1.5)); + canvas.drawPath(path, borderPaint..style = PaintingStyle.fill); + } + return path; + } + + /// Draw the column series type. + static Path _processColumnShape( + {required Canvas canvas, + required Rect rect, + required bool isNeedToReturnPath, + required Path path, + Paint? paint, + Paint? borderPaint}) { + final double left = rect.left + rect.width / 2; + final double top = rect.top + rect.height / 2; + + const num padding = 10; + const num temp = padding / 2; + const num space = 3; + + path.moveTo(left - space * (rect.width / temp), top - (rect.height / temp)); + path.lineTo( + left + space * (-rect.width / padding), top - (rect.height / temp)); + path.lineTo( + left + space * (-rect.width / padding), top + (rect.height / 2)); + path.lineTo(left - space * (rect.width / temp), top + (rect.height / 2)); + path.close(); + + path.moveTo(left - (rect.width / padding) - (rect.width / (padding * 2)), + top - (rect.height / 4) - (padding / 2)); + path.lineTo(left + (rect.width / padding) + (rect.width / (padding * 2)), + top - (rect.height / 4) - (padding / 2)); + path.lineTo(left + (rect.width / padding) + (rect.width / (padding * 2)), + top + (rect.height / 2)); + path.lineTo(left - (rect.width / padding) - (rect.width / (padding * 2)), + top + (rect.height / 2)); + path.close(); + + path.moveTo(left + space * (rect.width / padding), top); + path.lineTo(left + space * (rect.width / temp), top); + path.lineTo(left + space * (rect.width / temp), top + (rect.height / 2)); + path.lineTo(left + space * (rect.width / padding), top + (rect.height / 2)); + path.close(); + + if (isNeedToReturnPath) { + return path; + } + + canvas.drawPath(path, paint!); + + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + return path; + } + + /// Draw the area series type. + static Path _processAreaShape( + {required Canvas canvas, + required Rect rect, + required bool isNeedToReturnPath, + required Path path, + Paint? paint, + Paint? borderPaint}) { + final double x = rect.left + rect.width / 2; + final double y = rect.top + rect.height / 2; + final double width = rect.width; + final double height = rect.height; + + const num padding = 10; + path.moveTo(x - (width / 2) - (padding / 4), y + (height / 2)); + path.lineTo(x - (width / 4) - (padding / 8), y - (height / 2)); + path.lineTo(x, y + (height / 4)); + path.lineTo( + x + (width / 4) + (padding / 8), y - (height / 2) + (height / 4)); + path.lineTo(x + (height / 2) + (padding / 4), y + (height / 2)); + path.close(); + + if (isNeedToReturnPath) { + return path; + } + + canvas.drawPath(path, paint!); + + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + + return path; + } + + /// Draw the bar series type. + static Path _processBarShape( + {required Canvas canvas, + required Rect rect, + required bool isNeedToReturnPath, + required Path path, + Paint? paint, + Paint? borderPaint}) { + final double x = rect.left + rect.width / 2; + final double y = rect.top + rect.height / 2; + final double width = rect.width; + final double height = rect.height; + + const num padding = 10; + + path.moveTo(x - (width / 2) - padding / 4, y - 3 * (height / 5)); + path.lineTo(x + 3 * (width / 10), y - 3 * (height / 5)); + path.lineTo(x + 3 * (width / 10), y - 3 * (height / 10)); + path.lineTo(x - (width / 2) - padding / 4, y - 3 * (height / 10)); + path.close(); + path.moveTo( + x - (width / 2) - (padding / 4), y - (height / 5) + (padding / 20)); + path.lineTo( + x + (width / 2) + (padding / 4), y - (height / 5) + (padding / 20)); + path.lineTo( + x + (width / 2) + (padding / 4), y + (height / 10) + (padding / 20)); + path.lineTo( + x - (width / 2) - (padding / 4), y + (height / 10) + (padding / 20)); + path.close(); + path.moveTo( + x - (width / 2) - (padding / 4), y + (height / 5) + (padding / 10)); + path.lineTo(x - width / 4, y + (height / 5) + (padding / 10)); + path.lineTo(x - width / 4, y + (height / 2) + (padding / 10)); + path.lineTo( + x - (width / 2) - (padding / 4), y + (height / 2) + (padding / 10)); + path.close(); + + if (isNeedToReturnPath) { + return path; + } + + canvas.drawPath(path, paint!); + + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + + return path; + } + + /// Draw the spline series type. + static Path _processSplineShape( + {required Canvas canvas, + required Rect rect, + required bool isNeedToReturnPath, + required Path path, + Paint? paint, + Paint? borderPaint}) { + final double x = rect.left + rect.width / 2; + final double y = rect.top + rect.height / 2; + final double width = rect.width; + final double height = rect.height; + + path.moveTo(x - width / 2, y + height / 5); + path.quadraticBezierTo(x, y - height, x, y + height / 5); + path.moveTo(x, y + height / 5); + path.quadraticBezierTo( + x + width / 2, y + height / 2, x + width / 2, y - height / 2); + + if (isNeedToReturnPath) { + return path; + } + + canvas.drawPath(path, paint!); + + if (borderPaint != null) { + canvas.drawPath(path, borderPaint); + } + + return path; + } +} diff --git a/packages/syncfusion_flutter_core/lib/tooltip_internal.dart b/packages/syncfusion_flutter_core/lib/tooltip_internal.dart index b0ef76cdd..816366e89 100644 --- a/packages/syncfusion_flutter_core/lib/tooltip_internal.dart +++ b/packages/syncfusion_flutter_core/lib/tooltip_internal.dart @@ -1,5 +1,6 @@ library tooltip_internal; +import 'dart:async'; import 'dart:ui'; import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter/rendering.dart'; diff --git a/packages/syncfusion_flutter_core/pubspec.yaml b/packages/syncfusion_flutter_core/pubspec.yaml index a50b905e8..54bea81f1 100644 --- a/packages/syncfusion_flutter_core/pubspec.yaml +++ b/packages/syncfusion_flutter_core/pubspec.yaml @@ -1,6 +1,6 @@ name: syncfusion_flutter_core description: Syncfusion Flutter Core is a dependent package for all the Syncfusion Flutter widgets. -version: 19.1.54 +version: 18.3.40 homepage: https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_flutter_core environment: @@ -11,7 +11,6 @@ dependencies: flutter: sdk: flutter -dev_dependencies: pedantic: ">=1.9.0 <2.0.0" flutter: diff --git a/packages/syncfusion_flutter_datagrid/CHANGELOG.md b/packages/syncfusion_flutter_datagrid/CHANGELOG.md index 7c92200fd..219b04037 100644 --- a/packages/syncfusion_flutter_datagrid/CHANGELOG.md +++ b/packages/syncfusion_flutter_datagrid/CHANGELOG.md @@ -1,5 +1,30 @@ ## Unreleased +**Features** +* Provided the support to edit cell values. An editor widget can be loaded based on the column type to edit cell values. +* Provided the support to fit the rows and columns based on the value of the cells to improve readability. +* Provided the support to highlight a row when mouse hovers over it in Web and Desktop platforms. +* Provided the support to show an additional row that can be displayed below to last row. Widgets can also be displayed in the footer row. +* Provided the support to listen the vertical and horizontal scroll changes. +* Provided the support to write the entire logic for custom sorting instead of performing built-in sorting. + +**Breaking changes** +* [GridTextColumn](https://pub.dev/documentation/syncfusion_flutter_datagrid/latest/datagrid/GridTextColumn-class.html) class has been deprecated. Use [GridColumn](https://pub.dev/documentation/syncfusion_flutter_datagrid/latest/datagrid/GridColumn-class.html) instead. + +## [19.1.67-beta] - 06/08/2021 + +**Bugs** + +* Now, the background color for row is applied when transparent color is set. + +## [19.1.56-beta] - 04/13/2021 + +**Bugs** + +* The column headers are now visible when the rows are empty. + +## [19.1.55-beta] - 04/06/2021 + **Bugs** * Now, in Flutter 2.0, text can be typed in TextField widget when you load it in cells in web platform. diff --git a/packages/syncfusion_flutter_datagrid/README.md b/packages/syncfusion_flutter_datagrid/README.md index f94f126ba..74155b214 100644 --- a/packages/syncfusion_flutter_datagrid/README.md +++ b/packages/syncfusion_flutter_datagrid/README.md @@ -6,8 +6,6 @@ The Flutter DataTable or DataGrid is used to display and manipulate data in a ta **Disclaimer:** This is a commercial package. To use this package, you need to have either a Syncfusion commercial license or [Free Syncfusion Community license](https://www.syncfusion.com/products/communitylicense). For more details, please check the [LICENSE](https://github.com/syncfusion/flutter-examples/blob/master/LICENSE) file. -**Note:** Our packages are now compatible with Flutter for web. However, this will be in beta until Flutter for web becomes stable. - ## Table of contents - [DataGrid features](#datagrid-features) - [Coming soon](#coming-soon) @@ -27,9 +25,13 @@ The Flutter DataTable or DataGrid is used to display and manipulate data in a ta ![Flutter DataGrid shows different column types](https://cdn.syncfusion.com/content/images/Flutter/pub_images/flutter-datagrid-column-types.png) -**Column sizing** - Set the width of columns with various sizing options. +**Editing** - Allows users to edit cell values. An editor widget can be loaded based on the column type to edit cell values. + +![Editing in Flutter DataGrid](https://cdn.syncfusion.com/content/images/Flutter/pub_images/flutter-datagrid-editing.png) -**Row height** - Set the height for header and data rows. Also, set the different height for specific rows. +**Column sizing** - Set the width of columns with various sizing options. Fit the columns based on the value of the cells to improve readability. + +**Row height** - Set the height for header and data rows. Fit the rows based on the value of the cells to improve readability. Also, set the different height for specific rows. ![Flutter DataGrid shows rows in auto-fit](https://cdn.syncfusion.com/content/images/Flutter/pub_images/flutter-datagrid-auto-row-height.png) @@ -50,7 +52,7 @@ The Flutter DataTable or DataGrid is used to display and manipulate data in a ta ![Flutter datagrid shows multiple column headers](https://cdn.syncfusion.com/content/images/Flutter/pub_images/flutter-datagrid-stacked-headers.png) -**Load more** - Display an interactive view when the grid reaches its maximum offset while scrolling down. Tapping the interactive view triggers a callback to add more data from the data source of the grid at run time. +**Load more** - Display an interactive view when the grid reaches its maximum offset while scrolling down. ![infinite scrolling in Flutter datagrid](https://cdn.syncfusion.com/content/images/Flutter/pub_images/flutter-datagrid-load-more.gif) @@ -58,6 +60,10 @@ The Flutter DataTable or DataGrid is used to display and manipulate data in a ta ![Flutter DataGrid shows rows in page segments](https://cdn.syncfusion.com/content/images/Flutter/pub_images/flutter-datagrid-paging.png) +**Footer** - Show an additional row that can be displayed below to last row. Widgets can also be displayed in the footer row. + +[Footer view in Flutter DataGrid](https://cdn.syncfusion.com/content/images/Flutter/pub_images/flutter-datagrid-footer-view.png) + **Freeze Panes** - Freeze the rows and columns when scrolling the grid. ![First row and column are frozen in flutter datagrid](https://cdn.syncfusion.com/content/images/Flutter/pub_images/flutter-datagrid-freeze-panes.gif) @@ -74,8 +80,7 @@ The Flutter DataTable or DataGrid is used to display and manipulate data in a ta ## Coming soon -* Editing -* Column resizing +* Column resizing * Column drag and drop * Grouping * Row drag and drop @@ -85,12 +90,17 @@ The Flutter DataTable or DataGrid is used to display and manipulate data in a ta Explore the full capabilities of our Flutter widgets on your device by installing our sample browser applications from the following app stores, and view sample code in GitHub.

- - + + + +

+

+ + +

- - +

## Other useful links @@ -131,7 +141,7 @@ Create the collection of employee data with the required number of data objects. ```dart List employees = []; -EmployeeDataSource employeeDataSource; +late EmployeeDataSource employeeDataSource; @override void initState() { @@ -178,13 +188,13 @@ class EmployeeDataSource extends DataGridSource { .toList(); } - List _employees; + List _employees = []; @override List get rows => _employees; @override - DataGridRowAdapter buildRow(DataGridRow row) { + DataGridRowAdapter? buildRow(DataGridRow row) { return DataGridRowAdapter( cells: row.getCells().map((dataGridCell) { return Container( diff --git a/packages/syncfusion_flutter_datagrid/example/lib/main.dart b/packages/syncfusion_flutter_datagrid/example/lib/main.dart index 3e45e7249..9ad1bda7a 100644 --- a/packages/syncfusion_flutter_datagrid/example/lib/main.dart +++ b/packages/syncfusion_flutter_datagrid/example/lib/main.dart @@ -47,7 +47,7 @@ class _MyHomePageState extends State { source: employeeDataSource, columnWidthMode: ColumnWidthMode.fill, columns: [ - GridTextColumn( + GridColumn( columnName: 'id', label: Container( padding: EdgeInsets.all(16.0), @@ -55,13 +55,13 @@ class _MyHomePageState extends State { child: Text( 'ID', ))), - GridTextColumn( + GridColumn( columnName: 'name', label: Container( padding: EdgeInsets.all(8.0), alignment: Alignment.center, child: Text('Name'))), - GridTextColumn( + GridColumn( columnName: 'designation', label: Container( padding: EdgeInsets.all(8.0), @@ -70,7 +70,7 @@ class _MyHomePageState extends State { 'Designation', overflow: TextOverflow.ellipsis, ))), - GridTextColumn( + GridColumn( columnName: 'salary', label: Container( padding: EdgeInsets.all(8.0), diff --git a/packages/syncfusion_flutter_datagrid/example/linux/.gitignore b/packages/syncfusion_flutter_datagrid/example/linux/.gitignore new file mode 100644 index 000000000..d3896c984 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/packages/syncfusion_flutter_datagrid/example/linux/CMakeLists.txt b/packages/syncfusion_flutter_datagrid/example/linux/CMakeLists.txt new file mode 100644 index 000000000..290c3e841 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/linux/CMakeLists.txt @@ -0,0 +1,106 @@ +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +set(BINARY_NAME "example") +set(APPLICATION_ID "com.example.example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Application build +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) +apply_standard_settings(${BINARY_NAME}) +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) +add_dependencies(${BINARY_NAME} flutter_assemble) +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/packages/syncfusion_flutter_datagrid/example/linux/flutter/CMakeLists.txt b/packages/syncfusion_flutter_datagrid/example/linux/flutter/CMakeLists.txt new file mode 100644 index 000000000..a1da1b9e5 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,91 @@ +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) +pkg_check_modules(BLKID REQUIRED IMPORTED_TARGET blkid) +pkg_check_modules(LZMA REQUIRED IMPORTED_TARGET liblzma) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO + PkgConfig::BLKID + PkgConfig::LZMA +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + linux-x64 ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/packages/syncfusion_flutter_datagrid/example/linux/flutter/generated_plugin_registrant.cc b/packages/syncfusion_flutter_datagrid/example/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000..d38195aa0 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,9 @@ +// +// Generated file. Do not edit. +// + +#include "generated_plugin_registrant.h" + + +void fl_register_plugins(FlPluginRegistry* registry) { +} diff --git a/packages/syncfusion_flutter_datagrid/example/linux/flutter/generated_plugin_registrant.h b/packages/syncfusion_flutter_datagrid/example/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000..9bf747894 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,13 @@ +// +// Generated file. Do not edit. +// + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/syncfusion_flutter_datagrid/example/linux/flutter/generated_plugins.cmake b/packages/syncfusion_flutter_datagrid/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 000000000..51436ae8c --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,15 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/packages/syncfusion_flutter_datagrid/example/linux/main.cc b/packages/syncfusion_flutter_datagrid/example/linux/main.cc new file mode 100644 index 000000000..e7c5c5437 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/packages/syncfusion_flutter_datagrid/example/linux/my_application.cc b/packages/syncfusion_flutter_datagrid/example/linux/my_application.cc new file mode 100644 index 000000000..543eaca72 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/linux/my_application.cc @@ -0,0 +1,104 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen *screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "example"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } + else { + gtk_window_set_title(window, "example"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar ***arguments, int *exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject *object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + nullptr)); +} diff --git a/packages/syncfusion_flutter_datagrid/example/linux/my_application.h b/packages/syncfusion_flutter_datagrid/example/linux/my_application.h new file mode 100644 index 000000000..72271d5e4 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/packages/syncfusion_flutter_datagrid/example/macos/.gitignore b/packages/syncfusion_flutter_datagrid/example/macos/.gitignore new file mode 100644 index 000000000..d2fd37723 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/macos/.gitignore @@ -0,0 +1,6 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/xcuserdata/ diff --git a/packages/syncfusion_flutter_datagrid/example/macos/Flutter/Flutter-Debug.xcconfig b/packages/syncfusion_flutter_datagrid/example/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 000000000..c2efd0b60 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/syncfusion_flutter_datagrid/example/macos/Flutter/Flutter-Release.xcconfig b/packages/syncfusion_flutter_datagrid/example/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 000000000..c2efd0b60 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/packages/syncfusion_flutter_datagrid/example/macos/Flutter/GeneratedPluginRegistrant.swift b/packages/syncfusion_flutter_datagrid/example/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 000000000..cccf817a5 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,10 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { +} diff --git a/packages/syncfusion_flutter_datagrid/example/macos/Runner.xcodeproj/project.pbxproj b/packages/syncfusion_flutter_datagrid/example/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000..cc89c8782 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,572 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* example.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 0930; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + 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_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + 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_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + 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_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/packages/syncfusion_flutter_datagrid/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/syncfusion_flutter_datagrid/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/syncfusion_flutter_datagrid/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/syncfusion_flutter_datagrid/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000..ae8ff59d9 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/syncfusion_flutter_datagrid/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/syncfusion_flutter_datagrid/example/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..1d526a16e --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/syncfusion_flutter_datagrid/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/syncfusion_flutter_datagrid/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/packages/syncfusion_flutter_datagrid/example/macos/Runner/AppDelegate.swift b/packages/syncfusion_flutter_datagrid/example/macos/Runner/AppDelegate.swift new file mode 100644 index 000000000..d53ef6437 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/packages/syncfusion_flutter_datagrid/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/syncfusion_flutter_datagrid/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..a2ec33f19 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/syncfusion_flutter_datagrid/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/packages/syncfusion_flutter_datagrid/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..3c4935a7ca84f0976aca34b7f2895d65fb94d1ea GIT binary patch literal 46993 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 literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_datagrid/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/packages/syncfusion_flutter_datagrid/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..ed4cc16421680a50164ba74381b4b35ceaa0ccfc GIT binary patch literal 3276 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 N;OXk;vd$@?2>?>Ex^Vyi literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_datagrid/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/packages/syncfusion_flutter_datagrid/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 0000000000000000000000000000000000000000..bcbf36df2f2aaaa0a63c7dabc94e600184229d0d GIT binary patch literal 5933 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 f$KZHO*s&vt|9^w-6P?|#0pRK8NSwWJ?9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!ItFh?!xdN1Q+aGJ{c&& zS>O>_%)r1c48n{Iv*t(u1=&kHeO=ifbFy+6aSK)V_AxLppYn8Z42d|rc6w}vOsL55 z`t&mC&y2@JTEyg!eDiFX^k#CC!jq%>erB=yHqUP0XcDOTw6ko}L zX;EmMrq(fKk*eygEuA616;0)>@A{TK|55PV@70 z$OfzS*(VJxQev3J?yY?O=ul(v`fp}?u9z`JK3ugibK>)DyCwImZOF4d{xK%%Ks1*} zv$oa)9anR%lXIBUqYnhLmT>VOzHfNP?ZwJNZ!5$s9M08RynIvaXw>@G^T9@r9^KH1 zVy??F&uuk)bH9Y4pQY!hP58i_H6 znl-NcuCpLV6ZWU;4C zu@9exF&OZi`Bovq_m%T+WhU2kvkz@^_LpycBvqm3bMpLw8X-Or5sL>0AKE1$(k_L=_Zc=CUq#=x1-QZf)G7nHu@fmsQ1eN_N3+nTEz`4HI4Z6uVlE zJH+X&det8JU?tO?upcM4Z=cV!JV;yF>FfL5Q$M|W_2Z!P`S=}Wzp|_1^#d%e?_H`> zV@%vA$+bFVqhw9`U;TfP|5|PD{||OiYdor8P*i??|NJcb%kzT_73*7WE?Ua5hAnR2 z=7WE=PhTlJ#ZeRznjTUb;`E(wkMZrj4e|Hilz-mK>9cZHQY**5TUPw~u}k;u73KI}xAx!0m-)GVia|x^d3p~s_9gh83jA&Ra<8rM%`>U3x69t&NzbwWY}7Ar?)FK#IZ0z|d0H0EkRO w3{9;}4Xg|ebq&m|3=9_N6z8I7$jwj5OsmAL;bP(Gi$Dzwp00i_>zopr02+f8CIA2c literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_datagrid/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/packages/syncfusion_flutter_datagrid/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 0000000000000000000000000000000000000000..e71a726136a47ed24125c7efc79d68a4a01961b4 GIT binary patch literal 14800 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/syncfusion_flutter_datagrid/example/macos/Runner/Configs/AppInfo.xcconfig b/packages/syncfusion_flutter_datagrid/example/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 000000000..cf9be60ca --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = example + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.example + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2021 com.example. All rights reserved. diff --git a/packages/syncfusion_flutter_datagrid/example/macos/Runner/Configs/Debug.xcconfig b/packages/syncfusion_flutter_datagrid/example/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 000000000..36b0fd946 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/packages/syncfusion_flutter_datagrid/example/macos/Runner/Configs/Release.xcconfig b/packages/syncfusion_flutter_datagrid/example/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 000000000..dff4f4956 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/packages/syncfusion_flutter_datagrid/example/macos/Runner/Configs/Warnings.xcconfig b/packages/syncfusion_flutter_datagrid/example/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 000000000..42bcbf478 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/packages/syncfusion_flutter_datagrid/example/macos/Runner/DebugProfile.entitlements b/packages/syncfusion_flutter_datagrid/example/macos/Runner/DebugProfile.entitlements new file mode 100644 index 000000000..dddb8a30c --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/packages/syncfusion_flutter_datagrid/example/macos/Runner/Info.plist b/packages/syncfusion_flutter_datagrid/example/macos/Runner/Info.plist new file mode 100644 index 000000000..4789daa6a --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/packages/syncfusion_flutter_datagrid/example/macos/Runner/MainFlutterWindow.swift b/packages/syncfusion_flutter_datagrid/example/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 000000000..2722837ec --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/packages/syncfusion_flutter_datagrid/example/macos/Runner/Release.entitlements b/packages/syncfusion_flutter_datagrid/example/macos/Runner/Release.entitlements new file mode 100644 index 000000000..852fa1a47 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/packages/syncfusion_flutter_datagrid/example/pubspec.yaml b/packages/syncfusion_flutter_datagrid/example/pubspec.yaml index e46c3711a..59832701d 100644 --- a/packages/syncfusion_flutter_datagrid/example/pubspec.yaml +++ b/packages/syncfusion_flutter_datagrid/example/pubspec.yaml @@ -9,13 +9,9 @@ dependencies: flutter: sdk: flutter syncfusion_flutter_datagrid: - path: ../../syncfusion_flutter_datagrid + path: ../ cupertino_icons: ^1.0.2 -dev_dependencies: - flutter_test: - sdk: flutter - flutter: uses-material-design: true \ No newline at end of file diff --git a/packages/syncfusion_flutter_datagrid/example/web/favicon.png b/packages/syncfusion_flutter_datagrid/example/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_datagrid/example/web/icons/Icon-192.png b/packages/syncfusion_flutter_datagrid/example/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_datagrid/example/web/icons/Icon-512.png b/packages/syncfusion_flutter_datagrid/example/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_datagrid/example/web/index.html b/packages/syncfusion_flutter_datagrid/example/web/index.html new file mode 100644 index 000000000..1460b5e9b --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/web/index.html @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + example + + + + + + + + diff --git a/packages/syncfusion_flutter_datagrid/example/web/manifest.json b/packages/syncfusion_flutter_datagrid/example/web/manifest.json new file mode 100644 index 000000000..8c012917d --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/web/manifest.json @@ -0,0 +1,23 @@ +{ + "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" + } + ] +} diff --git a/packages/syncfusion_flutter_datagrid/example/windows/.gitignore b/packages/syncfusion_flutter_datagrid/example/windows/.gitignore new file mode 100644 index 000000000..d492d0d98 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/packages/syncfusion_flutter_datagrid/example/windows/CMakeLists.txt b/packages/syncfusion_flutter_datagrid/example/windows/CMakeLists.txt new file mode 100644 index 000000000..abf90408e --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/windows/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required(VERSION 3.15) +project(example LANGUAGES CXX) + +set(BINARY_NAME "example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() + +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build +add_subdirectory("runner") + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/packages/syncfusion_flutter_datagrid/example/windows/flutter/CMakeLists.txt b/packages/syncfusion_flutter_datagrid/example/windows/flutter/CMakeLists.txt new file mode 100644 index 000000000..b02c5485c --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.15) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/packages/syncfusion_flutter_datagrid/example/windows/flutter/generated_plugin_registrant.cc b/packages/syncfusion_flutter_datagrid/example/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000..4bfa0f3a3 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,9 @@ +// +// Generated file. Do not edit. +// + +#include "generated_plugin_registrant.h" + + +void RegisterPlugins(flutter::PluginRegistry* registry) { +} diff --git a/packages/syncfusion_flutter_datagrid/example/windows/flutter/generated_plugin_registrant.h b/packages/syncfusion_flutter_datagrid/example/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000..9846246b4 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,13 @@ +// +// Generated file. Do not edit. +// + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/syncfusion_flutter_datagrid/example/windows/flutter/generated_plugins.cmake b/packages/syncfusion_flutter_datagrid/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 000000000..4d10c2518 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,15 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/packages/syncfusion_flutter_datagrid/example/windows/runner/CMakeLists.txt b/packages/syncfusion_flutter_datagrid/example/windows/runner/CMakeLists.txt new file mode 100644 index 000000000..977e38b5d --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/windows/runner/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.15) +project(runner LANGUAGES CXX) + +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "run_loop.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) +apply_standard_settings(${BINARY_NAME}) +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/packages/syncfusion_flutter_datagrid/example/windows/runner/Runner.rc b/packages/syncfusion_flutter_datagrid/example/windows/runner/Runner.rc new file mode 100644 index 000000000..51812dcd4 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "A new Flutter project." "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2021 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "example.exe" "\0" + VALUE "ProductName", "example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/packages/syncfusion_flutter_datagrid/example/windows/runner/flutter_window.cpp b/packages/syncfusion_flutter_datagrid/example/windows/runner/flutter_window.cpp new file mode 100644 index 000000000..c42272304 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/windows/runner/flutter_window.cpp @@ -0,0 +1,64 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(RunLoop* run_loop, + const flutter::DartProject& project) + : run_loop_(run_loop), project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + run_loop_->RegisterFlutterInstance(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + run_loop_->UnregisterFlutterInstance(flutter_controller_->engine()); + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opporutunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/packages/syncfusion_flutter_datagrid/example/windows/runner/flutter_window.h b/packages/syncfusion_flutter_datagrid/example/windows/runner/flutter_window.h new file mode 100644 index 000000000..b663ddd50 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/windows/runner/flutter_window.h @@ -0,0 +1,39 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "run_loop.h" +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow driven by the |run_loop|, hosting a + // Flutter view running |project|. + explicit FlutterWindow(RunLoop* run_loop, + const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The run loop driving events for this window. + RunLoop* run_loop_; + + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/packages/syncfusion_flutter_datagrid/example/windows/runner/main.cpp b/packages/syncfusion_flutter_datagrid/example/windows/runner/main.cpp new file mode 100644 index 000000000..b637809bd --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/windows/runner/main.cpp @@ -0,0 +1,42 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "run_loop.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + RunLoop run_loop; + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(&run_loop, project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + run_loop.Run(); + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/packages/syncfusion_flutter_datagrid/example/windows/runner/resource.h b/packages/syncfusion_flutter_datagrid/example/windows/runner/resource.h new file mode 100644 index 000000000..66a65d1e4 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/packages/syncfusion_flutter_datagrid/example/windows/runner/resources/app_icon.ico b/packages/syncfusion_flutter_datagrid/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/packages/syncfusion_flutter_datagrid/example/windows/runner/run_loop.cpp b/packages/syncfusion_flutter_datagrid/example/windows/runner/run_loop.cpp new file mode 100644 index 000000000..2d6636ab6 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/windows/runner/run_loop.cpp @@ -0,0 +1,66 @@ +#include "run_loop.h" + +#include + +#include + +RunLoop::RunLoop() {} + +RunLoop::~RunLoop() {} + +void RunLoop::Run() { + bool keep_running = true; + TimePoint next_flutter_event_time = TimePoint::clock::now(); + while (keep_running) { + std::chrono::nanoseconds wait_duration = + std::max(std::chrono::nanoseconds(0), + next_flutter_event_time - TimePoint::clock::now()); + ::MsgWaitForMultipleObjects( + 0, nullptr, FALSE, static_cast(wait_duration.count() / 1000), + QS_ALLINPUT); + bool processed_events = false; + MSG message; + // All pending Windows messages must be processed; MsgWaitForMultipleObjects + // won't return again for items left in the queue after PeekMessage. + while (::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) { + processed_events = true; + if (message.message == WM_QUIT) { + keep_running = false; + break; + } + ::TranslateMessage(&message); + ::DispatchMessage(&message); + // Allow Flutter to process messages each time a Windows message is + // processed, to prevent starvation. + next_flutter_event_time = + std::min(next_flutter_event_time, ProcessFlutterMessages()); + } + // If the PeekMessage loop didn't run, process Flutter messages. + if (!processed_events) { + next_flutter_event_time = + std::min(next_flutter_event_time, ProcessFlutterMessages()); + } + } +} + +void RunLoop::RegisterFlutterInstance( + flutter::FlutterEngine* flutter_instance) { + flutter_instances_.insert(flutter_instance); +} + +void RunLoop::UnregisterFlutterInstance( + flutter::FlutterEngine* flutter_instance) { + flutter_instances_.erase(flutter_instance); +} + +RunLoop::TimePoint RunLoop::ProcessFlutterMessages() { + TimePoint next_event_time = TimePoint::max(); + for (auto instance : flutter_instances_) { + std::chrono::nanoseconds wait_duration = instance->ProcessMessages(); + if (wait_duration != std::chrono::nanoseconds::max()) { + next_event_time = + std::min(next_event_time, TimePoint::clock::now() + wait_duration); + } + } + return next_event_time; +} diff --git a/packages/syncfusion_flutter_datagrid/example/windows/runner/run_loop.h b/packages/syncfusion_flutter_datagrid/example/windows/runner/run_loop.h new file mode 100644 index 000000000..000d36246 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/windows/runner/run_loop.h @@ -0,0 +1,40 @@ +#ifndef RUNNER_RUN_LOOP_H_ +#define RUNNER_RUN_LOOP_H_ + +#include + +#include +#include + +// A runloop that will service events for Flutter instances as well +// as native messages. +class RunLoop { + public: + RunLoop(); + ~RunLoop(); + + // Prevent copying + RunLoop(RunLoop const&) = delete; + RunLoop& operator=(RunLoop const&) = delete; + + // Runs the run loop until the application quits. + void Run(); + + // Registers the given Flutter instance for event servicing. + void RegisterFlutterInstance( + flutter::FlutterEngine* flutter_instance); + + // Unregisters the given Flutter instance from event servicing. + void UnregisterFlutterInstance( + flutter::FlutterEngine* flutter_instance); + + private: + using TimePoint = std::chrono::steady_clock::time_point; + + // Processes all currently pending messages for registered Flutter instances. + TimePoint ProcessFlutterMessages(); + + std::set flutter_instances_; +}; + +#endif // RUNNER_RUN_LOOP_H_ diff --git a/packages/syncfusion_flutter_datagrid/example/windows/runner/runner.exe.manifest b/packages/syncfusion_flutter_datagrid/example/windows/runner/runner.exe.manifest new file mode 100644 index 000000000..c977c4a42 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/packages/syncfusion_flutter_datagrid/example/windows/runner/utils.cpp b/packages/syncfusion_flutter_datagrid/example/windows/runner/utils.cpp new file mode 100644 index 000000000..d19bdbbcc --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/windows/runner/utils.cpp @@ -0,0 +1,64 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/packages/syncfusion_flutter_datagrid/example/windows/runner/utils.h b/packages/syncfusion_flutter_datagrid/example/windows/runner/utils.h new file mode 100644 index 000000000..3879d5475 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/packages/syncfusion_flutter_datagrid/example/windows/runner/win32_window.cpp b/packages/syncfusion_flutter_datagrid/example/windows/runner/win32_window.cpp new file mode 100644 index 000000000..c10f08dc7 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/windows/runner/win32_window.cpp @@ -0,0 +1,245 @@ +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/packages/syncfusion_flutter_datagrid/example/windows/runner/win32_window.h b/packages/syncfusion_flutter_datagrid/example/windows/runner/win32_window.h new file mode 100644 index 000000000..17ba43112 --- /dev/null +++ b/packages/syncfusion_flutter_datagrid/example/windows/runner/win32_window.h @@ -0,0 +1,98 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/packages/syncfusion_flutter_datagrid/lib/datagrid.dart b/packages/syncfusion_flutter_datagrid/lib/datagrid.dart index 1836ded5c..7e6cbacf6 100644 --- a/packages/syncfusion_flutter_datagrid/lib/datagrid.dart +++ b/packages/syncfusion_flutter_datagrid/lib/datagrid.dart @@ -11,11 +11,13 @@ /// * [Knowledge base](https://www.syncfusion.com/kb/flutter/sfdatagrid) library datagrid; +import 'dart:async'; import 'dart:collection'; import 'dart:core'; import 'dart:math'; import 'dart:ui'; import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; @@ -101,4 +103,4 @@ part './src/grid_common/scroll_axis_base/scroll_info.dart'; part './src/grid_common/scroll_axis_base/sorted_range_value_list.dart'; part './src/grid_common/scroll_axis_base/visible_line_info.dart'; part './src/grid_common/utility/double_span.dart'; -part './src/grid_common/utility/int_32_span.dart'; +part './src/grid_common/utility/int_32_span.dart'; \ No newline at end of file diff --git a/packages/syncfusion_flutter_datagrid/lib/src/cell_renderer/grid_cell_renderer_base.dart b/packages/syncfusion_flutter_datagrid/lib/src/cell_renderer/grid_cell_renderer_base.dart index 6340979a2..39f8e4819 100644 --- a/packages/syncfusion_flutter_datagrid/lib/src/cell_renderer/grid_cell_renderer_base.dart +++ b/packages/syncfusion_flutter_datagrid/lib/src/cell_renderer/grid_cell_renderer_base.dart @@ -2,10 +2,18 @@ part of datagrid; /// The base class for the cell renderer that used to display the widget. abstract class GridCellRendererBase { + /// Creates the [GridCellRendererBase] for [SfDataGrid] widget. + GridCellRendererBase() { + _isEditable = true; + } + late _DataGridStateDetails _dataGridStateDetails; + /// Decide to enable the editing in the renderer. + late bool _isEditable; + /// Called when the child widgets for the GridCell are prepared. - Widget? onPrepareWidgets(DataCellBase dataColumn) { + Widget? onPrepareWidgets(DataCellBase dataCell) { return null; } diff --git a/packages/syncfusion_flutter_datagrid/lib/src/cell_renderer/grid_cell_stacked_header_renderer.dart b/packages/syncfusion_flutter_datagrid/lib/src/cell_renderer/grid_cell_stacked_header_renderer.dart index 5ed20efee..3e8808b7f 100644 --- a/packages/syncfusion_flutter_datagrid/lib/src/cell_renderer/grid_cell_stacked_header_renderer.dart +++ b/packages/syncfusion_flutter_datagrid/lib/src/cell_renderer/grid_cell_stacked_header_renderer.dart @@ -11,17 +11,17 @@ class _GridStackedHeaderCellRenderer @override void onInitializeDisplayWidget(DataCellBase dataCell) { - final dataGridSettings = _dataGridStateDetails(); - final isLight = + final _DataGridSettings dataGridSettings = _dataGridStateDetails(); + final bool isLight = dataGridSettings.dataGridThemeData!.brightness == Brightness.light; Widget? label = DefaultTextStyle( style: isLight - ? TextStyle( + ? const TextStyle( fontFamily: 'Roboto', fontWeight: FontWeight.w500, fontSize: 14, color: Colors.black87) - : TextStyle( + : const TextStyle( fontFamily: 'Roboto', fontWeight: FontWeight.w500, fontSize: 14, @@ -32,8 +32,8 @@ class _GridStackedHeaderCellRenderer key: dataCell._key!, dataCell: dataCell, backgroundColor: isLight - ? Color.fromRGBO(255, 255, 255, 1) - : Color.fromRGBO(33, 33, 33, 1), + ? const Color.fromRGBO(255, 255, 255, 1) + : const Color.fromRGBO(33, 33, 33, 1), isDirty: dataGridSettings.container._isDirty || dataCell._isDirty, child: label, ); diff --git a/packages/syncfusion_flutter_datagrid/lib/src/cell_renderer/grid_cell_text_field_renderer.dart b/packages/syncfusion_flutter_datagrid/lib/src/cell_renderer/grid_cell_text_field_renderer.dart index eb54e4ad7..e8f7bfbe6 100644 --- a/packages/syncfusion_flutter_datagrid/lib/src/cell_renderer/grid_cell_text_field_renderer.dart +++ b/packages/syncfusion_flutter_datagrid/lib/src/cell_renderer/grid_cell_text_field_renderer.dart @@ -3,11 +3,12 @@ part of datagrid; /// A cell renderer which displays the String value in the cell. /// /// This renderer is typically used for [GridTextColumn]. -class GridCellTextFieldRenderer +class GridCellTextFieldRenderer extends GridVirtualizingCellRendererBase { /// Creates the [GridCellTextFieldRenderer] for [SfDataGrid] widget. GridCellTextFieldRenderer(_DataGridStateDetails dataGridStateDetails) { _dataGridStateDetails = dataGridStateDetails; + super._isEditable = true; } @override @@ -23,29 +24,33 @@ class GridCellTextFieldRenderer _DataGridSettings dataGridSettings, DataCellBase dataCell) { final DataRowBase? dataRow = dataCell._dataRow; if (dataRow != null && dataRow.isSelectedRow) { - return dataGridSettings.dataGridThemeData!.brightness == Brightness.light - ? TextStyle( - fontFamily: 'Roboto', - fontWeight: FontWeight.w400, - fontSize: 14, - color: const Color.fromRGBO(0, 0, 0, 0.87)) - : TextStyle( - fontFamily: 'Roboto', - fontWeight: FontWeight.w400, - fontSize: 14, - color: const Color.fromRGBO(255, 255, 255, 1)); + return dataRow._isHoveredRow + ? dataGridSettings.dataGridThemeData!.rowHoverTextStyle + : dataGridSettings.dataGridThemeData!.brightness == Brightness.light + ? const TextStyle( + fontFamily: 'Roboto', + fontWeight: FontWeight.w400, + fontSize: 14, + color: Color.fromRGBO(0, 0, 0, 0.87)) + : const TextStyle( + fontFamily: 'Roboto', + fontWeight: FontWeight.w400, + fontSize: 14, + color: Color.fromRGBO(255, 255, 255, 1)); } else { - return dataGridSettings.dataGridThemeData!.brightness == Brightness.light - ? TextStyle( - fontFamily: 'Roboto', - fontWeight: FontWeight.w400, - fontSize: 14, - color: Colors.black87) - : TextStyle( - fontFamily: 'Roboto', - fontWeight: FontWeight.w400, - fontSize: 14, - color: Color.fromRGBO(255, 255, 255, 1)); + return dataRow!._isHoveredRow + ? dataGridSettings.dataGridThemeData!.rowHoverTextStyle + : dataGridSettings.dataGridThemeData!.brightness == Brightness.light + ? const TextStyle( + fontFamily: 'Roboto', + fontWeight: FontWeight.w400, + fontSize: 14, + color: Colors.black87) + : const TextStyle( + fontFamily: 'Roboto', + fontWeight: FontWeight.w400, + fontSize: 14, + color: Color.fromRGBO(255, 255, 255, 1)); } } } diff --git a/packages/syncfusion_flutter_datagrid/lib/src/cell_renderer/grid_header_cell_renderer.dart b/packages/syncfusion_flutter_datagrid/lib/src/cell_renderer/grid_header_cell_renderer.dart index c6afd6201..52bca93ea 100644 --- a/packages/syncfusion_flutter_datagrid/lib/src/cell_renderer/grid_header_cell_renderer.dart +++ b/packages/syncfusion_flutter_datagrid/lib/src/cell_renderer/grid_header_cell_renderer.dart @@ -30,12 +30,12 @@ class GridHeaderCellRenderer _dataGridStateDetails().dataGridThemeData!; TextStyle getDefaultHeaderTextStyle() { return themeData.brightness == Brightness.light - ? TextStyle( + ? const TextStyle( fontFamily: 'Roboto', fontWeight: FontWeight.w500, fontSize: 14, color: Colors.black87) - : TextStyle( + : const TextStyle( fontFamily: 'Roboto', fontWeight: FontWeight.w500, fontSize: 14, diff --git a/packages/syncfusion_flutter_datagrid/lib/src/cell_renderer/grid_virtualizing_cell_renderer_base.dart b/packages/syncfusion_flutter_datagrid/lib/src/cell_renderer/grid_virtualizing_cell_renderer_base.dart index 9235a629d..5bfed7232 100644 --- a/packages/syncfusion_flutter_datagrid/lib/src/cell_renderer/grid_virtualizing_cell_renderer_base.dart +++ b/packages/syncfusion_flutter_datagrid/lib/src/cell_renderer/grid_virtualizing_cell_renderer_base.dart @@ -12,7 +12,10 @@ abstract class GridVirtualizingCellRendererBase