diff --git a/packages/syncfusion_flutter_barcodes/pubspec.yaml b/packages/syncfusion_flutter_barcodes/pubspec.yaml index 6ecc1eda6..9784f57d1 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: 20.4.38 +version: 21.1.35 homepage: https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_flutter_barcodes environment: @@ -9,7 +9,7 @@ environment: dependencies: flutter: sdk: flutter - syncfusion_flutter_core: ^20.4.38 + syncfusion_flutter_core: ^21.1.35 dev_dependencies: flutter_test: diff --git a/packages/syncfusion_flutter_calendar/pubspec.yaml b/packages/syncfusion_flutter_calendar/pubspec.yaml index abe8179b3..8d6c61f70 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: 20.4.38 +version: 21.1.35 homepage: https://github.com/syncfusion/flutter-examples environment: @@ -9,12 +9,12 @@ environment: dependencies: flutter: sdk: flutter - timezone: 0.8.0 - syncfusion_flutter_core: ^20.4.38 + timezone: 0.9.0 + syncfusion_flutter_core: ^21.1.35 - syncfusion_flutter_datepicker: ^20.4.38 + syncfusion_flutter_datepicker: ^21.1.35 - intl: ">=0.17.0 <0.20.0" + intl: ">=0.15.0 <0.20.0" dev_dependencies: flutter_test: @@ -22,4 +22,4 @@ dev_dependencies: flutter: assets: - - packages/timezone/data/2020a.tzf + - packages/timezone/data/latest_all.tzf diff --git a/packages/syncfusion_flutter_charts/CHANGELOG.md b/packages/syncfusion_flutter_charts/CHANGELOG.md index 0ab9f8a3b..120c611e1 100644 --- a/packages/syncfusion_flutter_charts/CHANGELOG.md +++ b/packages/syncfusion_flutter_charts/CHANGELOG.md @@ -1,10 +1,35 @@ ## Unreleased +**Bugs** +* #FB40960 - Fixed an issue where the data label template was not updating properly when changing the visible range dynamically. +* #FB41822 - Fixed an issue of the candle series width decreasing when adding the non-rectangular series. + +## [20.4.54] - 03/15/2023 + +**Bugs** +* #FB41625 - Resolved null check exception that occurred when changing the data source of a circular series with a toggled legend. + +## [20.4.53] - 03/07/2023 + +**Bugs** +* #FB40694 - Resolved the range error exception in the FastLineSeries with the floatAllPoints trackball display mode. + +## [20.4.50] - 02/14/2023 + +**Bugs** +* #FB40202 - The builder data labels are not positioned when using multiple series in the SfCartesianChart has been resolved. + +## [20.4.43] - 01/10/2023 + +**Bug** +* #FB39500 - The issue with zooming and panning on the chart not working properly on the macOS trackpad has been resolved. + +## [20.3.61] - 12/13/2022 **Bugs** * #FB37705 - Now, the circular data label builder will render properly with connector lines. -## [20.3.69] - 12/06/2022 +## [20.3.60] - 12/06/2022 **Bugs** * #FB39502 - Now, the series is rendered with both the [primaryXAxis](https://pub.dev/documentation/syncfusion_flutter_charts/latest/charts/SfCartesianChart/primaryXAxis.html) and [primaryYAxis](https://pub.dev/documentation/syncfusion_flutter_charts/latest/charts/SfCartesianChart/primaryYAxis.html) as [LogarithmicAxis](https://pub.dev/documentation/syncfusion_flutter_charts/latest/charts/LogarithmicAxis-class.html). @@ -651,4 +676,4 @@ Initial release. * Line, spline, area, column, bar, bubble, scatter, step line, fast line, pie, doughnut and radial bar chart types. * Numeric, category and date time axis types. * User interactive features like zooming and panning, trackball, crosshair, selection and tooltip. -* Additional features like animation, marker, data label, empty points, legend, annotation and much more. +* Additional features like animation, marker, data label, empty points, legend, annotation and much more. \ No newline at end of file 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 0eb502b35..ca1ea2aab 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 @@ -134,7 +134,9 @@ class CartesianAxisWidget extends StatefulWidget { /// Creates an instance for cartesian axis widget // ignore: prefer_const_constructors_in_immutables CartesianAxisWidget( - {required this.stateProperties, required this.renderType}); + {required this.stateProperties, + required this.renderType, + required this.dataLabelTemplateNotifier}); /// Specifies the cartesian state properties final CartesianStateProperties stateProperties; @@ -146,6 +148,9 @@ class CartesianAxisWidget extends StatefulWidget { // ignore: library_private_types_in_public_api late _CartesianAxisWidgetState state; + /// Specifies the data label template notifier + ValueNotifier dataLabelTemplateNotifier; + @override State createState() => _CartesianAxisWidgetState(); } @@ -308,6 +313,10 @@ class _CartesianAxisWidgetState extends State void _repaintAxisElements() { _animateAxis(); axisRepaintNotifier.value++; + if (animationController.status == AnimationStatus.completed) { + widget.stateProperties.renderingDetails.dataLabelTemplateRegions.clear(); + widget.dataLabelTemplateNotifier.value++; + } } } 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 886581036..2d276eeca 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 @@ -2263,36 +2263,64 @@ class SfCartesianChartState extends State /// Represents the container area // ignore: must_be_immutable -class ContainerArea extends StatelessWidget { +class ContainerArea extends StatefulWidget { /// Creates an instance for container area // ignore: prefer_const_constructors_in_immutables ContainerArea(this._stateProperties); final CartesianStateProperties _stateProperties; + @override + State createState() => _ContainerAreaState(); +} + +class _ContainerAreaState extends State { + @override + void initState() { + dataLabelTemplateNotifier = ValueNotifier(0); + super.initState(); + } + + @override + void dispose() { + dataLabelTemplateNotifier.dispose(); + super.dispose(); + } + /// Gets the chart from state properties - SfCartesianChart get chart => _stateProperties.chart; - RenderingDetails get _renderingDetails => _stateProperties.renderingDetails; + SfCartesianChart get chart => widget._stateProperties.chart; + + RenderingDetails get _renderingDetails => + widget._stateProperties.renderingDetails; /// Specifies the render box late RenderBox renderBox; + Offset? _touchPosition; + + late ValueNotifier dataLabelTemplateNotifier; + Offset? _tapDownDetails; + Offset? _mousePointerDetails; + late CartesianSeries _series; + late XyDataSeriesRenderer _seriesRenderer; + Offset? _zoomStartPosition; - bool get _enableMouseHover => _stateProperties.enableMouseHover; + + bool get _enableMouseHover => widget._stateProperties.enableMouseHover; /// Get trackball rendering Details TrackballRenderingDetails get trackballRenderingDetails => TrackballHelper.getRenderingDetails( - _stateProperties.trackballBehaviorRenderer); + widget._stateProperties.trackballBehaviorRenderer); @override Widget build(BuildContext context) { final ZoomingBehaviorDetails zoomingBehaviorDetails = ZoomPanBehaviorHelper.getRenderingDetails( - _stateProperties.zoomPanBehaviorRenderer); + widget._stateProperties.zoomPanBehaviorRenderer); //this boolean prohibits both x and y scrolls for the parent widget final bool isXYPanMode = (chart.crosshairBehavior.enable && @@ -2302,12 +2330,13 @@ class ContainerArea extends StatelessWidget { chart.zoomPanBehavior.enablePanning) && chart.zoomPanBehavior.zoomMode == ZoomMode.xy); - final bool requireInvertedAxis = _stateProperties.seriesRenderers.isNotEmpty - ? (chart.isTransposed ^ - getSeriesType(_stateProperties.seriesRenderers[0]) - .toLowerCase() - .contains('bar')) - : chart.isTransposed; + final bool requireInvertedAxis = + widget._stateProperties.seriesRenderers.isNotEmpty + ? (chart.isTransposed ^ + getSeriesType(widget._stateProperties.seriesRenderers[0]) + .toLowerCase() + .contains('bar')) + : chart.isTransposed; //this boolean prohibits x scrolls for the parent widget final bool isXPan = (chart.trackballBehavior.enable && @@ -2341,8 +2370,8 @@ class ContainerArea extends StatelessWidget { onExit: (PointerEvent event) => _performMouseExit(event), child: Listener( onPointerDown: (PointerDownEvent event) { - if (_stateProperties.chartState.mounted) { - _stateProperties.pointerDeviceKind = event.kind; + if (widget._stateProperties.chartState.mounted) { + widget._stateProperties.pointerDeviceKind = event.kind; _performPointerDown(event); ChartTouchInteractionArgs touchArgs; if (chart.onChartTouchInteractionDown != null) { @@ -2354,7 +2383,7 @@ class ContainerArea extends StatelessWidget { } }, onPointerMove: (PointerMoveEvent event) { - if (_stateProperties.chartState.mounted) { + if (widget._stateProperties.chartState.mounted) { _performPointerMove(event); ChartTouchInteractionArgs touchArgs; if (chart.onChartTouchInteractionMove != null) { @@ -2366,10 +2395,10 @@ class ContainerArea extends StatelessWidget { } }, onPointerUp: (PointerUpEvent event) { - if (_stateProperties.chartState.mounted) { - _stateProperties.isTouchUp = true; + if (widget._stateProperties.chartState.mounted) { + widget._stateProperties.isTouchUp = true; _performPointerUp(event); - _stateProperties.isTouchUp = false; + widget._stateProperties.isTouchUp = false; ChartTouchInteractionArgs touchArgs; if (chart.onChartTouchInteractionUp != null) { touchArgs = ChartTouchInteractionArgs(); @@ -2380,29 +2409,29 @@ class ContainerArea extends StatelessWidget { } }, onPointerSignal: (PointerSignalEvent event) { - if (_stateProperties.chartState.mounted && + if (widget._stateProperties.chartState.mounted && event is PointerScrollEvent) { _performPointerEvent(event); } }, // To handle the trackpad zooming onPointerPanZoomUpdate: (PointerPanZoomUpdateEvent event) { - if (_stateProperties.chartState.mounted) { + if (widget._stateProperties.chartState.mounted) { _performPointerEvent(event); } }, child: GestureDetector( onTapDown: (TapDownDetails details) { - if (_stateProperties.chartState.mounted) { + if (widget._stateProperties.chartState.mounted) { final Offset position = renderBox.globalToLocal(details.globalPosition); _touchPosition = position; } }, onTap: () { - if (_stateProperties.chartState.mounted && - _stateProperties.chartSeries.visibleSeriesRenderers - .isNotEmpty && + if (widget._stateProperties.chartState.mounted && + widget._stateProperties.chartSeries + .visibleSeriesRenderers.isNotEmpty && _touchPosition != null && chart.selectionGesture == ActivationMode.singleTap && @@ -2435,11 +2464,11 @@ class ContainerArea extends StatelessWidget { } }, onTapUp: (TapUpDetails details) { - if (_stateProperties.chartState.mounted) { + if (widget._stateProperties.chartState.mounted) { final Offset position = renderBox.globalToLocal(details.globalPosition); final List - visibleSeriesRenderer = _stateProperties + visibleSeriesRenderer = widget._stateProperties .chartSeries.visibleSeriesRenderers; final CartesianSeriesRenderer? cartesianSeriesRenderer = _findSeries(position); @@ -2449,8 +2478,12 @@ class ContainerArea extends StatelessWidget { .series .onPointTap != null) { - calculatePointSeriesIndex(chart, _stateProperties, - position, null, ActivationMode.singleTap); + calculatePointSeriesIndex( + chart, + widget._stateProperties, + position, + null, + ActivationMode.singleTap); } if (chart.onAxisLabelTapped != null) { _triggerAxisLabelEvent(position); @@ -2462,28 +2495,28 @@ class ContainerArea extends StatelessWidget { } }, onDoubleTap: () { - if (_stateProperties.chartState.mounted) { + if (widget._stateProperties.chartState.mounted) { _performDoubleTap(); } }, onLongPressMoveUpdate: (LongPressMoveUpdateDetails details) { - if (_stateProperties.chartState.mounted) { + if (widget._stateProperties.chartState.mounted) { _performLongPressMoveUpdate(details); } }, onLongPress: () { - if (_stateProperties.chartState.mounted) { + if (widget._stateProperties.chartState.mounted) { _performLongPress(); } }, onLongPressEnd: (LongPressEndDetails details) { - if (_stateProperties.chartState.mounted) { + if (widget._stateProperties.chartState.mounted) { _performLongPressEnd(); } }, onPanDown: (DragDownDetails details) { - if (_stateProperties.chartState.mounted) { + if (widget._stateProperties.chartState.mounted) { _performPanDown(details); } }, @@ -2531,16 +2564,16 @@ class ContainerArea extends StatelessWidget { void _calculateContainerSize(BoxConstraints constraints) { final double width = constraints.maxWidth; final double height = constraints.maxHeight; - _stateProperties.renderingDetails.chartContainerRect = + widget._stateProperties.renderingDetails.chartContainerRect = Rect.fromLTWH(0, 0, width, height); } /// Calculate container bounds void _calculateBounds() { - _stateProperties.chartSeries.processData(); - _stateProperties.chartAxis.measureAxesBounds(); - _stateProperties.rangeChangeBySlider = false; - _stateProperties.rangeChangedByChart = false; + widget._stateProperties.chartSeries.processData(); + widget._stateProperties.chartAxis.measureAxesBounds(); + widget._stateProperties.rangeChangeBySlider = false; + widget._stateProperties.rangeChangedByChart = false; } /// To calculate the trendline region @@ -2564,8 +2597,8 @@ class ContainerArea extends StatelessWidget { /// To render chart widgets Widget _renderWidgets(BoxConstraints constraints, BuildContext context) { - _stateProperties.renderingDetails.chartWidgets = []; - _stateProperties.renderDatalabelRegions = []; + widget._stateProperties.renderingDetails.chartWidgets = []; + widget._stateProperties.renderDatalabelRegions = []; _bindAxisWidgets('outside'); _bindPlotBandWidgets(true); _bindSeriesWidgets(); @@ -2577,34 +2610,34 @@ class ContainerArea extends StatelessWidget { _bindInteractionWidgets(constraints, context); _bindLoadMoreIndicatorWidget(); renderBox = context.findRenderObject()! as RenderBox; - _stateProperties.containerArea = this; - _stateProperties.legendRefresh = false; + widget._stateProperties.containerArea = widget; + widget._stateProperties.legendRefresh = false; // ignore: avoid_unnecessary_containers return Container( child: Stack( textDirection: TextDirection.ltr, - children: _stateProperties.renderingDetails.chartWidgets!)); + children: widget._stateProperties.renderingDetails.chartWidgets!)); } void _bindLoadMoreIndicatorWidget() { - _stateProperties.renderingDetails.chartWidgets!.add(StatefulBuilder( + widget._stateProperties.renderingDetails.chartWidgets!.add(StatefulBuilder( builder: (BuildContext context, StateSetter stateSetter) { Widget renderWidget; - _stateProperties.loadMoreViewStateSetter = stateSetter; - renderWidget = _stateProperties.isLoadMoreIndicator + widget._stateProperties.loadMoreViewStateSetter = stateSetter; + renderWidget = widget._stateProperties.isLoadMoreIndicator ? Center( - child: _stateProperties.chart.loadMoreIndicatorBuilder!( - context, _stateProperties.swipeDirection)) + child: widget._stateProperties.chart.loadMoreIndicatorBuilder!( + context, widget._stateProperties.swipeDirection)) : renderWidget = Container(); return renderWidget; })); } void _bindPlotBandWidgets(bool shouldRenderAboveSeries) { - _stateProperties.renderingDetails.chartWidgets!.add(RepaintBoundary( + widget._stateProperties.renderingDetails.chartWidgets!.add(RepaintBoundary( child: CustomPaint( painter: getPlotBandPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, shouldRenderAboveSeries: shouldRenderAboveSeries)))); } @@ -2613,10 +2646,10 @@ class ContainerArea extends StatelessWidget { final Map> trendlineAnimations = >{}; for (int i = 0; - i < _stateProperties.chartSeries.visibleSeriesRenderers.length; + i < widget._stateProperties.chartSeries.visibleSeriesRenderers.length; i++) { - _seriesRenderer = _stateProperties.chartSeries.visibleSeriesRenderers[i] - as XyDataSeriesRenderer; + _seriesRenderer = widget._stateProperties.chartSeries + .visibleSeriesRenderers[i] as XyDataSeriesRenderer; final SeriesRendererDetails seriesRendererDetails = SeriesHelper.getSeriesRendererDetails(_seriesRenderer); _series = seriesRendererDetails.series; @@ -2633,8 +2666,10 @@ class ContainerArea extends StatelessWidget { final Trendline trendline = _series.trendlines![j]; if (trendline.animationDuration > 0 && (_renderingDetails.oldDeviceOrientation == null || - _stateProperties.renderingDetails.oldDeviceOrientation == - _stateProperties.renderingDetails.deviceOrientation) && + widget._stateProperties.renderingDetails + .oldDeviceOrientation == + widget._stateProperties.renderingDetails + .deviceOrientation) && seriesRendererDetails.needsAnimation == true && seriesRendererDetails.oldSeries == null) { final int totalAnimationDuration = @@ -2660,35 +2695,37 @@ class ContainerArea extends StatelessWidget { } } if (isTrendline) { - _stateProperties.renderingDetails.chartWidgets!.add(RepaintBoundary( + widget._stateProperties.renderingDetails.chartWidgets! + .add(RepaintBoundary( child: CustomPaint( painter: TrendlinePainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, trendlineAnimations: trendlineAnimations, - notifier: _stateProperties.repaintNotifiers['trendline']!)), + notifier: + widget._stateProperties.repaintNotifiers['trendline']!)), )); } } /// To bind the widget for data label void _bindDataLabelWidgets() { - _stateProperties.renderDataLabel = DataLabelRenderer( - stateProperties: _stateProperties, - show: _stateProperties.renderingDetails.animateCompleted); - _stateProperties.renderingDetails.chartWidgets! - .add(_stateProperties.renderDataLabel!); + widget._stateProperties.renderDataLabel = DataLabelRenderer( + stateProperties: widget._stateProperties, + show: widget._stateProperties.renderingDetails.animateCompleted); + widget._stateProperties.renderingDetails.chartWidgets! + .add(widget._stateProperties.renderDataLabel!); } /// To render a template void _renderTemplates() { - _stateProperties.annotationRegions = []; - _stateProperties.renderingDetails.templates = []; + widget._stateProperties.annotationRegions = []; + widget._stateProperties.renderingDetails.templates = []; _renderDataLabelTemplates(); if (chart.annotations != null && chart.annotations!.isNotEmpty) { for (int i = 0; i < chart.annotations!.length; i++) { final CartesianChartAnnotation annotation = chart.annotations![i]; final ChartLocation location = - getAnnotationLocation(annotation, _stateProperties); + getAnnotationLocation(annotation, widget._stateProperties); final ChartTemplateInfo chartTemplateInfo = ChartTemplateInfo( key: GlobalKey(), animationDuration: 200, @@ -2698,26 +2735,29 @@ class ContainerArea extends StatelessWidget { verticalAlignment: annotation.verticalAlignment, horizontalAlignment: annotation.horizontalAlignment, clipRect: annotation.region == AnnotationRegion.chart - ? _stateProperties.renderingDetails.chartContainerRect - : _stateProperties.chartAxis.axisClipRect, + ? widget._stateProperties.renderingDetails.chartContainerRect + : widget._stateProperties.chartAxis.axisClipRect, location: Offset(location.x.toDouble(), location.y.toDouble())); - _stateProperties.renderingDetails.templates.add(chartTemplateInfo); + widget._stateProperties.renderingDetails.templates + .add(chartTemplateInfo); } } if (_renderingDetails.templates.isNotEmpty) { final int templateLength = - _stateProperties.renderingDetails.templates.length; + widget._stateProperties.renderingDetails.templates.length; for (int i = 0; - i < _stateProperties.renderingDetails.templates.length; + i < widget._stateProperties.renderingDetails.templates.length; i++) { final ChartTemplateInfo templateInfo = - _stateProperties.renderingDetails.templates[i]; - _stateProperties.renderingDetails.chartWidgets!.add(RenderTemplate( - template: templateInfo, - templateIndex: i, - templateLength: templateLength, - stateProperties: _stateProperties)); + widget._stateProperties.renderingDetails.templates[i]; + widget._stateProperties.renderingDetails.chartWidgets!.add( + RenderTemplate( + template: templateInfo, + templateIndex: i, + templateLength: templateLength, + stateProperties: widget._stateProperties, + notifier: dataLabelTemplateNotifier)); } } } @@ -2726,12 +2766,13 @@ class ContainerArea extends StatelessWidget { void _renderDataLabelTemplates() { Widget? labelWidget; CartesianChartPoint point; - _stateProperties.renderingDetails.dataLabelTemplateRegions = []; + widget._stateProperties.renderingDetails.dataLabelTemplateRegions = + []; for (int i = 0; - i < _stateProperties.chartSeries.visibleSeriesRenderers.length; + i < widget._stateProperties.chartSeries.visibleSeriesRenderers.length; i++) { final CartesianSeriesRenderer seriesRenderer = - _stateProperties.chartSeries.visibleSeriesRenderers[i]; + widget._stateProperties.chartSeries.visibleSeriesRenderers[i]; final SeriesRendererDetails seriesRendererDetails = SeriesHelper.getSeriesRendererDetails(seriesRenderer); final XyDataSeries series = @@ -2775,7 +2816,7 @@ class ContainerArea extends StatelessWidget { for (int k = 0; k < dataLabelTemplateYValues.length; k++) { padding = (k == 0 && dataLabelTemplateYValues.length > 1 && - !_stateProperties.requireInvertedAxis) + !widget._stateProperties.requireInvertedAxis) ? 20 : 0; final ChartLocation location = calculatePoint( @@ -2783,20 +2824,30 @@ class ContainerArea extends StatelessWidget { dataLabelTemplateYValues[k], seriesRendererDetails.xAxisDetails!, seriesRendererDetails.yAxisDetails!, - _stateProperties.requireInvertedAxis, + widget._stateProperties.requireInvertedAxis, series, - _stateProperties.chartAxis.axisClipRect); + widget._stateProperties.chartAxis.axisClipRect); final ChartTemplateInfo templateInfo = ChartTemplateInfo( key: GlobalKey(), templateType: 'DataLabel', pointIndex: j, seriesIndex: i, - clipRect: _stateProperties.chartAxis.axisClipRect, + clipRect: widget._stateProperties.chartAxis.axisClipRect, animationDuration: (series.animationDuration + 1000.0).floor(), widget: labelWidget, - location: Offset(location.x, location.y + padding)); - _stateProperties.renderingDetails.templates.add(templateInfo); + location: Offset(location.x, location.y + padding), + labelLocation: k == 0 + ? 'labelLocation' + : k == 1 + ? 'labelLocation2' + : k == 2 + ? 'labelLocation3' + : k == 3 + ? 'labelLocation4' + : 'labelLocation5'); + widget._stateProperties.renderingDetails.templates + .add(templateInfo); } } } @@ -2807,14 +2858,14 @@ class ContainerArea extends StatelessWidget { /// To bind a series of widgets for all series void _bindSeriesWidgets() { - _stateProperties.painterKeys = []; - _stateProperties.animationCompleteCount = 0; - _stateProperties.renderingDetails.animateCompleted = false; + widget._stateProperties.painterKeys = []; + widget._stateProperties.animationCompleteCount = 0; + widget._stateProperties.renderingDetails.animateCompleted = false; for (int i = 0; - i < _stateProperties.chartSeries.visibleSeriesRenderers.length; + i < widget._stateProperties.chartSeries.visibleSeriesRenderers.length; i++) { - _seriesRenderer = _stateProperties.chartSeries.visibleSeriesRenderers[i] - as XyDataSeriesRenderer; + _seriesRenderer = widget._stateProperties.chartSeries + .visibleSeriesRenderers[i] as XyDataSeriesRenderer; final SeriesRendererDetails seriesRendererDetails = SeriesHelper.getSeriesRendererDetails(_seriesRenderer); seriesRendererDetails.animationCompleted = false; @@ -2825,9 +2876,9 @@ class ContainerArea extends StatelessWidget { // ignore: unnecessary_type_check if (_seriesRenderer is XyDataSeriesRenderer) { seriesRendererDetails.animationController = - AnimationController(vsync: _stateProperties.chartState) + AnimationController(vsync: widget._stateProperties.chartState) ..addListener(seriesRendererDetails.repaintSeriesElement); - _stateProperties + widget._stateProperties .controllerList[seriesRendererDetails.animationController] = seriesRendererDetails.repaintSeriesElement; seriesRendererDetails.animationController @@ -2836,13 +2887,13 @@ class ContainerArea extends StatelessWidget { } // ignore: unnecessary_null_comparison if (_seriesRenderer != null && seriesRendererDetails.visible! == true) { - _calculateTrendlineRegion(_stateProperties, _seriesRenderer); + _calculateTrendlineRegion(widget._stateProperties, _seriesRenderer); seriesRendererDetails.selectionBehavior = _series.selectionBehavior; final dynamic selectionBehavior = seriesRendererDetails.selectionBehavior; seriesRendererDetails.selectionBehaviorRenderer = SelectionBehaviorRenderer( - selectionBehavior, chart, _stateProperties); + selectionBehavior, chart, widget._stateProperties); final SelectionBehaviorRenderer? selectionBehaviorRenderer = seriesRendererDetails.selectionBehaviorRenderer; SelectionHelper.setSelectionBehaviorRenderer( @@ -2854,7 +2905,7 @@ class ContainerArea extends StatelessWidget { selectionDetails.selectionRenderer ??= SelectionRenderer(); selectionDetails.selectionRenderer!.chart = chart; selectionDetails.selectionRenderer!.stateProperties = - _stateProperties; + widget._stateProperties; selectionDetails.selectionRenderer!.seriesRendererDetails = seriesRendererDetails; _series = seriesRendererDetails.series; @@ -2862,9 +2913,9 @@ class ContainerArea extends StatelessWidget { selectionDetails.selectRange(); } selectionDetails.selectionRenderer!.selectedSegments = - _stateProperties.selectedSegments; + widget._stateProperties.selectedSegments; selectionDetails.selectionRenderer!.unselectedSegments = - _stateProperties.unselectedSegments; + widget._stateProperties.unselectedSegments; //To determine whether initialSelectedDataIndexes collection is updated dynamically bool isSelecetedIndexesUpdated = false; if (_series.initialSelectedDataIndexes != null && @@ -2888,36 +2939,38 @@ class ContainerArea extends StatelessWidget { int totalSelectedSegment = 0; int? selectedSeriesIndex; if (selectionBehavior.enable == true && - _stateProperties.selectedSegments.isNotEmpty && - _stateProperties.unselectedSegments.isNotEmpty) { - for (int j = 0; j < _stateProperties.selectedSegments.length; j++) { + widget._stateProperties.selectedSegments.isNotEmpty && + widget._stateProperties.unselectedSegments.isNotEmpty) { + for (int j = 0; + j < widget._stateProperties.selectedSegments.length; + j++) { final SegmentProperties segmentProperties = SegmentHelper.getSegmentProperties( - _stateProperties.selectedSegments[j]); + widget._stateProperties.selectedSegments[j]); if (segmentProperties.seriesIndex == i) { totalSelectedSegment += 1; selectedSeriesIndex = segmentProperties.seriesIndex; } } for (int k = 0; - k < _stateProperties.unselectedSegments.length; + k < widget._stateProperties.unselectedSegments.length; k++) { if (SegmentHelper.getSegmentProperties( - _stateProperties.unselectedSegments[k]) + widget._stateProperties.unselectedSegments[k]) .seriesIndex == i) { totalSelectedSegment += 1; } } } - if (_stateProperties.isRangeSelectionSlider == false && + if (widget._stateProperties.isRangeSelectionSlider == false && selectionBehavior.enable == true && (isSelecetedIndexesUpdated || (!_renderingDetails.initialRender! && (totalSelectedSegment != 0 && (totalSelectedSegment < - SeriesHelper.getSeriesRendererDetails( - _stateProperties.seriesRenderers[i]) + SeriesHelper.getSeriesRendererDetails(widget + ._stateProperties.seriesRenderers[i]) .dataPoints .length))))) { int segmentLength = seriesRendererDetails.dataPoints.length; @@ -2931,7 +2984,7 @@ class ContainerArea extends StatelessWidget { for (int j = 0; j < segmentLength; j++) { final ChartSegment segment = ColumnSegment(); SegmentHelper.setSegmentProperties( - segment, SegmentProperties(_stateProperties, segment)); + segment, SegmentProperties(widget._stateProperties, segment)); final SegmentProperties segmentProperties = SegmentHelper.getSegmentProperties(segment); segment.currentSegmentIndex = j; @@ -2965,21 +3018,23 @@ class ContainerArea extends StatelessWidget { !_renderingDetails.didSizeChange && !_renderingDetails.didLocaleChange && (_renderingDetails.oldDeviceOrientation == null || - _stateProperties.legendRefresh || - _stateProperties.renderingDetails.oldDeviceOrientation == - _stateProperties.renderingDetails.deviceOrientation) && + widget._stateProperties.legendRefresh || + widget._stateProperties.renderingDetails.oldDeviceOrientation == + widget + ._stateProperties.renderingDetails.deviceOrientation) && (_renderingDetails.initialRender! || - _stateProperties.legendRefresh || + widget._stateProperties.legendRefresh || ((seriesType == 'column' || seriesType == 'bar') && - _stateProperties.legendToggling) || - (!_stateProperties.legendToggling && + widget._stateProperties.legendToggling) || + (!widget._stateProperties.legendToggling && seriesRendererDetails.needsAnimation == true && - _stateProperties.renderingDetails.widgetNeedUpdate))) { + widget + ._stateProperties.renderingDetails.widgetNeedUpdate))) { if ((seriesType == 'column' || seriesType == 'bar') && - _stateProperties.legendToggling) { + widget._stateProperties.legendToggling) { seriesRendererDetails.needAnimateSeriesElements = true; } - _stateProperties.forwardAnimation(seriesRendererDetails); + widget._stateProperties.forwardAnimation(seriesRendererDetails); } else { seriesRendererDetails.animationController.duration = Duration.zero; seriesRendererDetails.seriesAnimation = @@ -2992,41 +3047,45 @@ class ContainerArea extends StatelessWidget { parent: seriesRendererDetails.animationController, curve: const Interval(1.0, 1.0, curve: Curves.decelerate), )); - _stateProperties.animationCompleteCount++; + widget._stateProperties.animationCompleteCount++; seriesRendererDetails.animationCompleted = true; - setAnimationStatus(_stateProperties); + setAnimationStatus(widget._stateProperties); } } // ignore: avoid_unnecessary_containers - _stateProperties.renderingDetails.chartWidgets!.add(Container( + widget._stateProperties.renderingDetails.chartWidgets!.add(Container( child: RepaintBoundary( child: CustomPaint( painter: _getSeriesPainter( i, seriesRendererDetails.animationController, _seriesRenderer), )))); } - _stateProperties.renderingDetails.chartWidgets!.add(Container( + widget._stateProperties.renderingDetails.chartWidgets!.add(Container( color: Colors.red, child: RepaintBoundary( child: CustomPaint( painter: ZoomRectPainter( - stateProperties: _stateProperties, - notifier: _stateProperties.repaintNotifiers['zoom']))))); - _stateProperties.legendToggling = false; + stateProperties: widget._stateProperties, + notifier: + widget._stateProperties.repaintNotifiers['zoom']))))); + widget._stateProperties.legendToggling = false; } /// Bind the axis widgets void _bindAxisWidgets(String renderType) { // ignore: unnecessary_null_comparison - if (_stateProperties.chartAxis.axisRenderersCollection != null && - _stateProperties.chartAxis.axisRenderersCollection.isNotEmpty && - _stateProperties.chartAxis.axisRenderersCollection.length > 1) { + if (widget._stateProperties.chartAxis.axisRenderersCollection != null && + widget._stateProperties.chartAxis.axisRenderersCollection.isNotEmpty && + widget._stateProperties.chartAxis.axisRenderersCollection.length > 1) { final CartesianAxisWidget axisWidget = CartesianAxisWidget( - stateProperties: _stateProperties, renderType: renderType); + stateProperties: widget._stateProperties, + renderType: renderType, + dataLabelTemplateNotifier: dataLabelTemplateNotifier, + ); renderType == 'outside' - ? _stateProperties.renderOutsideAxis = axisWidget - : _stateProperties.renderInsideAxis = axisWidget; - _stateProperties.renderingDetails.chartWidgets!.add(axisWidget); + ? widget._stateProperties.renderOutsideAxis = axisWidget + : widget._stateProperties.renderInsideAxis = axisWidget; + widget._stateProperties.renderingDetails.chartWidgets!.add(axisWidget); } } @@ -3035,10 +3094,13 @@ class ContainerArea extends StatelessWidget { CartesianSeriesRenderer? seriesRenderer; SelectionBehaviorRenderer? selectionBehaviorRenderer; outerLoop: - for (int i = _stateProperties.chartSeries.visibleSeriesRenderers.length - 1; + for (int i = + widget._stateProperties.chartSeries.visibleSeriesRenderers.length - + 1; i >= 0; i--) { - seriesRenderer = _stateProperties.chartSeries.visibleSeriesRenderers[i]; + seriesRenderer = + widget._stateProperties.chartSeries.visibleSeriesRenderers[i]; final SeriesRendererDetails seriesRendererDetails = SeriesHelper.getSeriesRendererDetails(seriesRenderer); final String seriesType = seriesRendererDetails.seriesType; @@ -3069,9 +3131,11 @@ class ContainerArea extends StatelessWidget { selectionBehaviorRenderer = seriesRendererDetails.selectionBehaviorRenderer; bool isSelect = false; - seriesRenderer = _stateProperties.chartSeries.visibleSeriesRenderers[i]; - for (int k = - _stateProperties.chartSeries.visibleSeriesRenderers.length - 1; + seriesRenderer = + widget._stateProperties.chartSeries.visibleSeriesRenderers[i]; + for (int k = widget._stateProperties.chartSeries.visibleSeriesRenderers + .length - + 1; k >= 0; k--) { isSelect = seriesRendererDetails.isSelectionEnable == true && @@ -3082,12 +3146,15 @@ class ContainerArea extends StatelessWidget { (SelectionHelper.getRenderingDetails(selectionBehaviorRenderer!) .selectionRenderer! .isSeriesContainsPoint( - SeriesHelper.getSeriesRendererDetails(_stateProperties - .chartSeries.visibleSeriesRenderers[i]), + SeriesHelper.getSeriesRendererDetails(widget + ._stateProperties + .chartSeries + .visibleSeriesRenderers[i]), position) == true); if (isSelect) { - return _stateProperties.chartSeries.visibleSeriesRenderers[i]; + return widget + ._stateProperties.chartSeries.visibleSeriesRenderers[i]; } else if (seriesRendererDetails.visible != null && seriesRendererDetails.visible! == true && seriesRendererDetails.visibleDataPoints != null && @@ -3095,11 +3162,14 @@ class ContainerArea extends StatelessWidget { SelectionHelper.getRenderingDetails(selectionBehaviorRenderer!) .selectionRenderer! .isSeriesContainsPoint( - SeriesHelper.getSeriesRendererDetails(_stateProperties - .chartSeries.visibleSeriesRenderers[i]), + SeriesHelper.getSeriesRendererDetails(widget + ._stateProperties + .chartSeries + .visibleSeriesRenderers[i]), position) == true) { - return _stateProperties.chartSeries.visibleSeriesRenderers[i]; + return widget + ._stateProperties.chartSeries.visibleSeriesRenderers[i]; } } } @@ -3109,23 +3179,24 @@ class ContainerArea extends StatelessWidget { /// To perform the pointer down event void _performPointerDown(PointerDownEvent event) { - _stateProperties.canSetRangeController = true; + widget._stateProperties.canSetRangeController = true; TooltipHelper.getRenderingDetails( - _stateProperties.renderingDetails.tooltipBehaviorRenderer) + widget._stateProperties.renderingDetails.tooltipBehaviorRenderer) .isHovering = false; _tapDownDetails = event.position; if (chart.zoomPanBehavior.enablePinching == true) { ZoomPanArgs? zoomStartArgs; - if (_stateProperties.touchStartPositions.length < 2) { - _stateProperties.touchStartPositions.add(event); + if (widget._stateProperties.touchStartPositions.length < 2) { + widget._stateProperties.touchStartPositions.add(event); } - if (_stateProperties.touchStartPositions.length == 2) { + if (widget._stateProperties.touchStartPositions.length == 2) { for (int axisIndex = 0; axisIndex < - _stateProperties.chartAxis.axisRenderersCollection.length; + widget + ._stateProperties.chartAxis.axisRenderersCollection.length; axisIndex++) { final ChartAxisRendererDetails axisDetails = - AxisHelper.getAxisRendererDetails(_stateProperties + AxisHelper.getAxisRendererDetails(widget._stateProperties .chartAxis.axisRenderersCollection[axisIndex]); if (chart.onZoomStart != null) { @@ -3134,12 +3205,12 @@ class ContainerArea extends StatelessWidget { axisDetails.zoomFactor = zoomStartArgs.currentZoomFactor; axisDetails.zoomPosition = zoomStartArgs.currentZoomPosition; } - _stateProperties.zoomPanBehaviorRenderer.onPinchStart( + widget._stateProperties.zoomPanBehaviorRenderer.onPinchStart( axisDetails.axis, - _stateProperties.touchStartPositions[0].position.dx, - _stateProperties.touchStartPositions[0].position.dy, - _stateProperties.touchStartPositions[1].position.dx, - _stateProperties.touchStartPositions[1].position.dy, + widget._stateProperties.touchStartPositions[0].position.dx, + widget._stateProperties.touchStartPositions[0].position.dy, + widget._stateProperties.touchStartPositions[1].position.dx, + widget._stateProperties.touchStartPositions[1].position.dy, axisDetails.zoomFactor); } } @@ -3160,7 +3231,7 @@ class ContainerArea extends StatelessWidget { trackballRenderingDetails.isMoving = true; trackballRenderingDetails.showTemplateTrackball(position); } else { - _stateProperties.trackballBehaviorRenderer + widget._stateProperties.trackballBehaviorRenderer .onTouchDown(position.dx, position.dy); } } @@ -3171,7 +3242,7 @@ class ContainerArea extends StatelessWidget { cartesianSeriesRenderer != null && SeriesHelper.getSeriesRendererDetails(cartesianSeriesRenderer).series is! ErrorBarSeries) { - _stateProperties.crosshairBehaviorRenderer + widget._stateProperties.crosshairBehaviorRenderer .onTouchDown(position.dx, position.dy); } } @@ -3179,36 +3250,41 @@ class ContainerArea extends StatelessWidget { /// To perform the pointer move event void _performPointerMove(PointerMoveEvent event) { TooltipHelper.getRenderingDetails( - _stateProperties.renderingDetails.tooltipBehaviorRenderer) + widget._stateProperties.renderingDetails.tooltipBehaviorRenderer) .isHovering = false; final ZoomingBehaviorDetails zoomingBehaviorDetails = ZoomPanBehaviorHelper.getRenderingDetails( - _stateProperties.zoomPanBehaviorRenderer); + widget._stateProperties.zoomPanBehaviorRenderer); if (chart.zoomPanBehavior.enablePinching == true && - _stateProperties.touchStartPositions.length == 2) { + widget._stateProperties.touchStartPositions.length == 2) { zoomingBehaviorDetails.isPinching = true; final int pointerID = event.pointer; bool addPointer = true; - for (int i = 0; i < _stateProperties.touchMovePositions.length; i++) { - if (_stateProperties.touchMovePositions[i].pointer == pointerID) { + for (int i = 0; + i < widget._stateProperties.touchMovePositions.length; + i++) { + if (widget._stateProperties.touchMovePositions[i].pointer == + pointerID) { addPointer = false; } } - if (_stateProperties.touchMovePositions.length < 2 && addPointer) { - _stateProperties.touchMovePositions.add(event); + if (widget._stateProperties.touchMovePositions.length < 2 && addPointer) { + widget._stateProperties.touchMovePositions.add(event); } - if (_stateProperties.touchMovePositions.length == 2 && - _stateProperties.touchStartPositions.length == 2) { - if (_stateProperties.touchMovePositions[0].pointer == event.pointer) { - _stateProperties.touchMovePositions[0] = event; + if (widget._stateProperties.touchMovePositions.length == 2 && + widget._stateProperties.touchStartPositions.length == 2) { + if (widget._stateProperties.touchMovePositions[0].pointer == + event.pointer) { + widget._stateProperties.touchMovePositions[0] = event; } - if (_stateProperties.touchMovePositions[1].pointer == event.pointer) { - _stateProperties.touchMovePositions[1] = event; + if (widget._stateProperties.touchMovePositions[1].pointer == + event.pointer) { + widget._stateProperties.touchMovePositions[1] = event; } zoomingBehaviorDetails.performPinchZooming( - _stateProperties.touchStartPositions, - _stateProperties.touchMovePositions); + widget._stateProperties.touchStartPositions, + widget._stateProperties.touchMovePositions); } } } @@ -3217,20 +3293,20 @@ class ContainerArea extends StatelessWidget { void _performPointerUp(PointerUpEvent event) { final ZoomingBehaviorDetails zoomingBehaviorDetails = ZoomPanBehaviorHelper.getRenderingDetails( - _stateProperties.zoomPanBehaviorRenderer); - if (_stateProperties.touchStartPositions.length == 2 && - _stateProperties.touchMovePositions.length == 2 && + widget._stateProperties.zoomPanBehaviorRenderer); + if (widget._stateProperties.touchStartPositions.length == 2 && + widget._stateProperties.touchMovePositions.length == 2 && (zoomingBehaviorDetails.isPinching ?? false)) { _calculatePinchZoomingArgs(); } - _stateProperties.touchStartPositions = []; - _stateProperties.touchMovePositions = []; + widget._stateProperties.touchStartPositions = []; + widget._stateProperties.touchMovePositions = []; zoomingBehaviorDetails.isPinching = false; zoomingBehaviorDetails.delayRedraw = false; final TooltipRenderingDetails tooltipRenderingDetails = TooltipHelper.getRenderingDetails( - _stateProperties.renderingDetails.tooltipBehaviorRenderer); + widget._stateProperties.renderingDetails.tooltipBehaviorRenderer); tooltipRenderingDetails.isHovering = false; final Offset position = renderBox.globalToLocal(event.position); final CartesianSeriesRenderer? cartesianSeriesRenderer = @@ -3252,7 +3328,7 @@ class ContainerArea extends StatelessWidget { chart.zoomPanBehavior.enablePinching || chart.zoomPanBehavior.enableSelectionZooming) && !chart.trackballBehavior.shouldAlwaysShow))) { - _stateProperties.trackballBehaviorRenderer + widget._stateProperties.trackballBehaviorRenderer .onTouchUp(position.dx, position.dy); trackballRenderingDetails.isLongPressActivated = false; @@ -3281,19 +3357,19 @@ class ContainerArea extends StatelessWidget { chart.zoomPanBehavior.enablePinching || chart.zoomPanBehavior.enableSelectionZooming) && !chart.crosshairBehavior.shouldAlwaysShow))) { - _stateProperties.crosshairBehaviorRenderer + widget._stateProperties.crosshairBehaviorRenderer .onTouchUp(position.dx, position.dy); CrosshairHelper.getRenderingDetails( - _stateProperties.crosshairBehaviorRenderer) + widget._stateProperties.crosshairBehaviorRenderer) .isLongPressActivated = false; } if (chart.tooltipBehavior.enable && chart.tooltipBehavior.activationMode == ActivationMode.singleTap || - shouldShowAxisTooltip(_stateProperties)) { + shouldShowAxisTooltip(widget._stateProperties)) { tooltipRenderingDetails.isInteraction = true; chart.tooltipBehavior.builder != null ? tooltipRenderingDetails.showTemplateTooltip(position) - : _stateProperties.renderingDetails.tooltipBehaviorRenderer + : widget._stateProperties.renderingDetails.tooltipBehaviorRenderer .onTouchUp(position.dx, position.dy); } } @@ -3304,9 +3380,9 @@ class ContainerArea extends StatelessWidget { if (_mousePointerDetails != null) { final Offset position = renderBox.globalToLocal(event.position); if (chart.zoomPanBehavior.enableMouseWheelZooming && - _stateProperties.chartAxis.axisClipRect.contains(position)) { + widget._stateProperties.chartAxis.axisClipRect.contains(position)) { ZoomPanBehaviorHelper.getRenderingDetails( - _stateProperties.zoomPanBehaviorRenderer) + widget._stateProperties.zoomPanBehaviorRenderer) .performMouseWheelZooming(event, position.dx, position.dy); } } @@ -3318,11 +3394,12 @@ class ContainerArea extends StatelessWidget { bool resetFlag = false; int axisIndex; for (axisIndex = 0; - axisIndex < _stateProperties.chartAxis.axisRenderersCollection.length; + axisIndex < + widget._stateProperties.chartAxis.axisRenderersCollection.length; axisIndex++) { final ChartAxisRendererDetails axisDetails = - AxisHelper.getAxisRendererDetails( - _stateProperties.chartAxis.axisRenderersCollection[axisIndex]); + AxisHelper.getAxisRendererDetails(widget + ._stateProperties.chartAxis.axisRenderersCollection[axisIndex]); if (chart.onZoomEnd != null) { zoomEndArgs = bindZoomEvent(chart, axisDetails, chart.onZoomEnd!); axisDetails.zoomFactor = zoomEndArgs.currentZoomFactor; @@ -3335,22 +3412,23 @@ class ContainerArea extends StatelessWidget { chart.onZoomReset != null) { resetFlag = true; } - _stateProperties.zoomAxes = []; - _stateProperties.zoomPanBehaviorRenderer.onPinchEnd( + widget._stateProperties.zoomAxes = []; + widget._stateProperties.zoomPanBehaviorRenderer.onPinchEnd( axisDetails.axis, - _stateProperties.touchMovePositions[0].position.dx, - _stateProperties.touchMovePositions[0].position.dy, - _stateProperties.touchMovePositions[1].position.dx, - _stateProperties.touchMovePositions[1].position.dy, + widget._stateProperties.touchMovePositions[0].position.dx, + widget._stateProperties.touchMovePositions[0].position.dy, + widget._stateProperties.touchMovePositions[1].position.dx, + widget._stateProperties.touchMovePositions[1].position.dy, axisDetails.zoomFactor); } if (resetFlag) { for (int index = 0; - index < _stateProperties.chartAxis.axisRenderersCollection.length; + index < + widget._stateProperties.chartAxis.axisRenderersCollection.length; index++) { final ChartAxisRendererDetails axisDetails = - AxisHelper.getAxisRendererDetails( - _stateProperties.chartAxis.axisRenderersCollection[index]); + AxisHelper.getAxisRendererDetails(widget + ._stateProperties.chartAxis.axisRenderersCollection[index]); bindZoomEvent(chart, axisDetails, chart.onZoomReset!); } } @@ -3361,13 +3439,13 @@ class ContainerArea extends StatelessWidget { final Offset? position = renderBox.globalToLocal(details.globalPosition); final ZoomingBehaviorDetails zoomingBehaviorDetails = ZoomPanBehaviorHelper.getRenderingDetails( - _stateProperties.zoomPanBehaviorRenderer); + widget._stateProperties.zoomPanBehaviorRenderer); if (zoomingBehaviorDetails.isPinching != true) { if (chart.zoomPanBehavior.enableSelectionZooming && position != null && _zoomStartPosition != null) { zoomingBehaviorDetails.canPerformSelection = true; - _stateProperties.zoomPanBehaviorRenderer.onDrawSelectionZoomRect( + widget._stateProperties.zoomPanBehaviorRenderer.onDrawSelectionZoomRect( position.dx, position.dy, _zoomStartPosition!.dx, @@ -3387,14 +3465,14 @@ class ContainerArea extends StatelessWidget { if (chart.trackballBehavior.activationMode == ActivationMode.singleTap) { chart.trackballBehavior.builder != null ? trackballRenderingDetails.showTemplateTrackball(position) - : _stateProperties.trackballBehaviorRenderer + : widget._stateProperties.trackballBehaviorRenderer .onTouchMove(position.dx, position.dy); } if (chart.trackballBehavior.activationMode == ActivationMode.longPress && trackballRenderingDetails.isLongPressActivated == true) { chart.trackballBehavior.builder != null ? trackballRenderingDetails.showTemplateTrackball(position) - : _stateProperties.trackballBehaviorRenderer + : widget._stateProperties.trackballBehaviorRenderer .onTouchMove(position.dx, position.dy); } } @@ -3406,18 +3484,18 @@ class ContainerArea extends StatelessWidget { SeriesHelper.getSeriesRendererDetails(_findSeries(position)!).series is! ErrorBarSeries) { if (chart.crosshairBehavior.activationMode == ActivationMode.singleTap) { - _stateProperties.crosshairBehaviorRenderer + widget._stateProperties.crosshairBehaviorRenderer .onTouchMove(position.dx, position.dy); // ignore: unnecessary_null_comparison } else if ((chart.crosshairBehavior != null && chart.crosshairBehavior.activationMode == ActivationMode.longPress && CrosshairHelper.getRenderingDetails( - _stateProperties.crosshairBehaviorRenderer) + widget._stateProperties.crosshairBehaviorRenderer) .isLongPressActivated == true) && !chart.zoomPanBehavior.enableSelectionZooming) { - _stateProperties.crosshairBehaviorRenderer + widget._stateProperties.crosshairBehaviorRenderer .onTouchMove(position.dx, position.dy); } } @@ -3427,7 +3505,7 @@ class ContainerArea extends StatelessWidget { void _performLongPressEnd() { final ZoomingBehaviorDetails zoomingBehaviorDetails = ZoomPanBehaviorHelper.getRenderingDetails( - _stateProperties.zoomPanBehaviorRenderer); + widget._stateProperties.zoomPanBehaviorRenderer); if (zoomingBehaviorDetails.isPinching != true) { zoomingBehaviorDetails.canPerformSelection = false; if (chart.zoomPanBehavior.enableSelectionZooming && @@ -3443,11 +3521,11 @@ class ContainerArea extends StatelessWidget { /// To perform pan down void _performPanDown(DragDownDetails details) { - _stateProperties.startOffset = + widget._stateProperties.startOffset = renderBox.globalToLocal(details.globalPosition); final ZoomingBehaviorDetails zoomingBehaviorDetails = ZoomPanBehaviorHelper.getRenderingDetails( - _stateProperties.zoomPanBehaviorRenderer); + widget._stateProperties.zoomPanBehaviorRenderer); if (zoomingBehaviorDetails.isPinching != true) { _zoomStartPosition = renderBox.globalToLocal(details.globalPosition); if (chart.zoomPanBehavior.enablePanning == true) { @@ -3469,25 +3547,25 @@ class ContainerArea extends StatelessWidget { .series .onPointLongPress != null) { - calculatePointSeriesIndex( - chart, _stateProperties, position, null, ActivationMode.longPress); + calculatePointSeriesIndex(chart, widget._stateProperties, position, + null, ActivationMode.longPress); } if (chart.tooltipBehavior.enable && chart.tooltipBehavior.activationMode == ActivationMode.longPress || - shouldShowAxisTooltip(_stateProperties)) { + shouldShowAxisTooltip(widget._stateProperties)) { final TooltipRenderingDetails tooltipRenderingDetails = - TooltipHelper.getRenderingDetails( - _stateProperties.renderingDetails.tooltipBehaviorRenderer); + TooltipHelper.getRenderingDetails(widget + ._stateProperties.renderingDetails.tooltipBehaviorRenderer); tooltipRenderingDetails.isInteraction = true; chart.tooltipBehavior.builder != null ? tooltipRenderingDetails.showTemplateTooltip(position) - : _stateProperties.renderingDetails.tooltipBehaviorRenderer + : widget._stateProperties.renderingDetails.tooltipBehaviorRenderer .onLongPress(position.dx, position.dy); } } // ignore: unnecessary_null_comparison - if (_stateProperties.chartSeries.visibleSeriesRenderers != null && + if (widget._stateProperties.chartSeries.visibleSeriesRenderers != null && position != null && chart.selectionGesture == ActivationMode.longPress) { final CartesianSeriesRenderer selectionSeriesRenderer = @@ -3503,7 +3581,7 @@ class ContainerArea extends StatelessWidget { } final ZoomingBehaviorDetails zoomingBehaviorDetails = ZoomPanBehaviorHelper.getRenderingDetails( - _stateProperties.zoomPanBehaviorRenderer); + widget._stateProperties.zoomPanBehaviorRenderer); // ignore: unnecessary_null_comparison if ((chart.trackballBehavior != null && chart.trackballBehavior.enable == true && @@ -3518,7 +3596,7 @@ class ContainerArea extends StatelessWidget { trackballRenderingDetails.isLongPressActivated = true; chart.trackballBehavior.builder != null ? trackballRenderingDetails.showTemplateTrackball(position) - : _stateProperties.trackballBehaviorRenderer + : widget._stateProperties.trackballBehaviorRenderer .onTouchDown(position.dx, position.dy); } // ignore: unnecessary_null_comparison @@ -3533,9 +3611,9 @@ class ContainerArea extends StatelessWidget { // ignore: unnecessary_null_comparison position != null) { CrosshairHelper.getRenderingDetails( - _stateProperties.crosshairBehaviorRenderer) + widget._stateProperties.crosshairBehaviorRenderer) .isLongPressActivated = true; - _stateProperties.crosshairBehaviorRenderer + widget._stateProperties.crosshairBehaviorRenderer .onTouchDown(position.dx, position.dy); } } @@ -3551,8 +3629,8 @@ class ContainerArea extends StatelessWidget { .series .onPointDoubleTap != null) { - calculatePointSeriesIndex( - chart, _stateProperties, position, null, ActivationMode.doubleTap); + calculatePointSeriesIndex(chart, widget._stateProperties, position, + null, ActivationMode.doubleTap); } // ignore: unnecessary_null_comparison if (chart.trackballBehavior != null && @@ -3563,14 +3641,14 @@ class ContainerArea extends StatelessWidget { chart.trackballBehavior.activationMode == ActivationMode.doubleTap) { chart.trackballBehavior.builder != null ? trackballRenderingDetails.showTemplateTrackball(position) - : _stateProperties.trackballBehaviorRenderer + : widget._stateProperties.trackballBehaviorRenderer .onDoubleTap(position.dx, position.dy); - _stateProperties.enableDoubleTap = true; - _stateProperties.isTouchUp = true; - _stateProperties.trackballBehaviorRenderer + widget._stateProperties.enableDoubleTap = true; + widget._stateProperties.isTouchUp = true; + widget._stateProperties.trackballBehaviorRenderer .onTouchUp(position.dx, position.dy); - _stateProperties.isTouchUp = false; - _stateProperties.enableDoubleTap = false; + widget._stateProperties.isTouchUp = false; + widget._stateProperties.enableDoubleTap = false; } // ignore: unnecessary_null_comparison if (chart.crosshairBehavior != null && @@ -3579,31 +3657,31 @@ class ContainerArea extends StatelessWidget { SeriesHelper.getSeriesRendererDetails(cartesianSeriesRenderer).series is! ErrorBarSeries && chart.crosshairBehavior.activationMode == ActivationMode.doubleTap) { - _stateProperties.crosshairBehaviorRenderer + widget._stateProperties.crosshairBehaviorRenderer .onDoubleTap(position.dx, position.dy); - _stateProperties.enableDoubleTap = true; - _stateProperties.isTouchUp = true; - _stateProperties.crosshairBehaviorRenderer + widget._stateProperties.enableDoubleTap = true; + widget._stateProperties.isTouchUp = true; + widget._stateProperties.crosshairBehaviorRenderer .onTouchUp(position.dx, position.dy); - _stateProperties.isTouchUp = false; - _stateProperties.enableDoubleTap = false; + widget._stateProperties.isTouchUp = false; + widget._stateProperties.enableDoubleTap = false; } if (chart.tooltipBehavior.enable && chart.tooltipBehavior.activationMode == ActivationMode.doubleTap || - shouldShowAxisTooltip(_stateProperties)) { + shouldShowAxisTooltip(widget._stateProperties)) { final TooltipRenderingDetails tooltipRenderingDetails = - TooltipHelper.getRenderingDetails( - _stateProperties.renderingDetails.tooltipBehaviorRenderer); + TooltipHelper.getRenderingDetails(widget + ._stateProperties.renderingDetails.tooltipBehaviorRenderer); tooltipRenderingDetails.isInteraction = true; chart.tooltipBehavior.builder != null ? tooltipRenderingDetails.showTemplateTooltip(position) - : _stateProperties.renderingDetails.tooltipBehaviorRenderer + : widget._stateProperties.renderingDetails.tooltipBehaviorRenderer .onDoubleTap(position.dx, position.dy); } // ignore: unnecessary_null_comparison - if (_stateProperties.chartSeries.visibleSeriesRenderers != null && + if (widget._stateProperties.chartSeries.visibleSeriesRenderers != null && chart.selectionGesture == ActivationMode.doubleTap) { final CartesianSeriesRenderer selectionSeriesRenderer = _findSeries(position)!; @@ -3622,11 +3700,11 @@ class ContainerArea extends StatelessWidget { final Offset? doubleTapPosition = _touchPosition; final Offset? position = doubleTapPosition; if (position != null) { - _stateProperties.zoomPanBehaviorRenderer.onDoubleTap( + widget._stateProperties.zoomPanBehaviorRenderer.onDoubleTap( position.dx, position.dy, ZoomPanBehaviorHelper.getRenderingDetails( - _stateProperties.zoomPanBehaviorRenderer) + widget._stateProperties.zoomPanBehaviorRenderer) .zoomFactor); } } @@ -3634,20 +3712,20 @@ class ContainerArea extends StatelessWidget { /// Update the details for pan void _performPanUpdate(DragUpdateDetails details) { - if (_stateProperties.chartState.mounted) { + if (widget._stateProperties.chartState.mounted) { Offset? position; - _stateProperties.currentPosition = + widget._stateProperties.currentPosition = renderBox.globalToLocal(details.globalPosition); final ZoomingBehaviorDetails zoomingBehaviorDetails = ZoomPanBehaviorHelper.getRenderingDetails( - _stateProperties.zoomPanBehaviorRenderer); + widget._stateProperties.zoomPanBehaviorRenderer); if (zoomingBehaviorDetails.isPinching != true) { position = renderBox.globalToLocal(details.globalPosition); if ((zoomingBehaviorDetails.isPanning ?? false) && chart.zoomPanBehavior.enablePanning && zoomingBehaviorDetails.previousMovedPosition != null && - !_stateProperties.isLoadMoreIndicator) { - _stateProperties.zoomPanBehaviorRenderer + !widget._stateProperties.isLoadMoreIndicator) { + widget._stateProperties.zoomPanBehaviorRenderer .onPan(position.dx, position.dy); } zoomingBehaviorDetails.previousMovedPosition = position; @@ -3669,7 +3747,7 @@ class ContainerArea extends StatelessWidget { trackballRenderingDetails.isMoving = true; trackballRenderingDetails.showTemplateTrackball(position); } else { - _stateProperties.trackballBehaviorRenderer + widget._stateProperties.trackballBehaviorRenderer .onTouchMove(position.dx, position.dy); } // ignore: unnecessary_null_comparison @@ -3684,7 +3762,7 @@ class ContainerArea extends StatelessWidget { trackballRenderingDetails.isLongPressActivated == true) { chart.trackballBehavior.builder != null ? trackballRenderingDetails.showTemplateTrackball(position) - : _stateProperties.trackballBehaviorRenderer + : widget._stateProperties.trackballBehaviorRenderer .onTouchMove(position.dx, position.dy); } } @@ -3699,17 +3777,17 @@ class ContainerArea extends StatelessWidget { !panInProgress) { if (chart.crosshairBehavior.activationMode == ActivationMode.singleTap) { - _stateProperties.crosshairBehaviorRenderer + widget._stateProperties.crosshairBehaviorRenderer .onTouchMove(position.dx, position.dy); // ignore: unnecessary_null_comparison } else if (chart.crosshairBehavior != null && chart.crosshairBehavior.activationMode == ActivationMode.longPress && CrosshairHelper.getRenderingDetails( - _stateProperties.crosshairBehaviorRenderer) + widget._stateProperties.crosshairBehaviorRenderer) .isLongPressActivated == true) { - _stateProperties.crosshairBehaviorRenderer + widget._stateProperties.crosshairBehaviorRenderer .onTouchMove(position.dx, position.dy); } } @@ -3718,10 +3796,10 @@ class ContainerArea extends StatelessWidget { /// Method for the pan end event void _performPanEnd(DragEndDetails details) { - if (_stateProperties.chartState.mounted) { + if (widget._stateProperties.chartState.mounted) { final ZoomingBehaviorDetails zoomingBehaviorDetails = ZoomPanBehaviorHelper.getRenderingDetails( - _stateProperties.zoomPanBehaviorRenderer); + widget._stateProperties.zoomPanBehaviorRenderer); if (zoomingBehaviorDetails.isPinching != true) { zoomingBehaviorDetails.isPanning = false; zoomingBehaviorDetails.previousMovedPosition = null; @@ -3732,10 +3810,10 @@ class ContainerArea extends StatelessWidget { _touchPosition != null && SeriesHelper.getSeriesRendererDetails(_findSeries(_touchPosition!)!) .series is! ErrorBarSeries) { - _stateProperties.isTouchUp = true; - _stateProperties.trackballBehaviorRenderer + widget._stateProperties.isTouchUp = true; + widget._stateProperties.trackballBehaviorRenderer .onTouchUp(_touchPosition!.dx, _touchPosition!.dy); - _stateProperties.isTouchUp = false; + widget._stateProperties.isTouchUp = false; trackballRenderingDetails.isLongPressActivated = false; } if (chart.crosshairBehavior.enable && @@ -3744,106 +3822,109 @@ class ContainerArea extends StatelessWidget { SeriesHelper.getSeriesRendererDetails(_findSeries(_touchPosition!)!) .series is! ErrorBarSeries && chart.crosshairBehavior.activationMode != ActivationMode.doubleTap) { - _stateProperties.isTouchUp = true; - _stateProperties.crosshairBehaviorRenderer + widget._stateProperties.isTouchUp = true; + widget._stateProperties.crosshairBehaviorRenderer .onTouchUp(_touchPosition!.dx, _touchPosition!.dy); - _stateProperties.isTouchUp = false; + widget._stateProperties.isTouchUp = false; CrosshairHelper.getRenderingDetails( - _stateProperties.crosshairBehaviorRenderer) + widget._stateProperties.crosshairBehaviorRenderer) .isLongPressActivated = false; } /// Pagination/Swiping feature if (chart.onPlotAreaSwipe != null && - _stateProperties.zoomedState != true && - _stateProperties.startOffset != null && - _stateProperties.currentPosition != null && - _stateProperties.chartAxis.axisClipRect - .contains(_stateProperties.startOffset!) && - _stateProperties.chartAxis.axisClipRect - .contains(_stateProperties.currentPosition!)) { + widget._stateProperties.zoomedState != true && + widget._stateProperties.startOffset != null && + widget._stateProperties.currentPosition != null && + widget._stateProperties.chartAxis.axisClipRect + .contains(widget._stateProperties.startOffset!) && + widget._stateProperties.chartAxis.axisClipRect + .contains(widget._stateProperties.currentPosition!)) { //swipe configuration options const double swipeMaxDistanceThreshold = 50.0; final double swipeMinDisplacement = - (_stateProperties.requireInvertedAxis - ? _stateProperties.chartAxis.axisClipRect.height - : _stateProperties.chartAxis.axisClipRect.width) * + (widget._stateProperties.requireInvertedAxis + ? widget._stateProperties.chartAxis.axisClipRect.height + : widget._stateProperties.chartAxis.axisClipRect.width) * 0.1; final double swipeMinVelocity = - _stateProperties.pointerDeviceKind == PointerDeviceKind.mouse + widget._stateProperties.pointerDeviceKind == PointerDeviceKind.mouse ? 0.0 : 240; ChartSwipeDirection swipeDirection; - final double dx = (_stateProperties.currentPosition!.dx - - _stateProperties.startOffset!.dx) + final double dx = (widget._stateProperties.currentPosition!.dx - + widget._stateProperties.startOffset!.dx) .abs(); - final double dy = (_stateProperties.currentPosition!.dy - - _stateProperties.startOffset!.dy) + final double dy = (widget._stateProperties.currentPosition!.dy - + widget._stateProperties.startOffset!.dy) .abs(); final double velocity = details.primaryVelocity!; - if (_stateProperties.requireInvertedAxis && + if (widget._stateProperties.requireInvertedAxis && dx <= swipeMaxDistanceThreshold && dy >= swipeMinDisplacement && velocity.abs() >= swipeMinVelocity) { ///vertical - swipeDirection = - _stateProperties.pointerDeviceKind == PointerDeviceKind.mouse - ? (_stateProperties.currentPosition!.dy > - _stateProperties.startOffset!.dy - ? ChartSwipeDirection.end - : ChartSwipeDirection.start) - : (velocity < 0 - ? ChartSwipeDirection.start - : ChartSwipeDirection.end); + swipeDirection = widget._stateProperties.pointerDeviceKind == + PointerDeviceKind.mouse + ? (widget._stateProperties.currentPosition!.dy > + widget._stateProperties.startOffset!.dy + ? ChartSwipeDirection.end + : ChartSwipeDirection.start) + : (velocity < 0 + ? ChartSwipeDirection.start + : ChartSwipeDirection.end); chart.onPlotAreaSwipe!(swipeDirection); - } else if (!_stateProperties.requireInvertedAxis && + } else if (!widget._stateProperties.requireInvertedAxis && dx >= swipeMinDisplacement && dy <= swipeMaxDistanceThreshold && velocity.abs() >= swipeMinVelocity) { ///horizontal - swipeDirection = - _stateProperties.pointerDeviceKind == PointerDeviceKind.mouse - ? (_stateProperties.currentPosition!.dx > - _stateProperties.startOffset!.dx - ? ChartSwipeDirection.start - : ChartSwipeDirection.end) - : (velocity > 0 - ? ChartSwipeDirection.start - : ChartSwipeDirection.end); + swipeDirection = widget._stateProperties.pointerDeviceKind == + PointerDeviceKind.mouse + ? (widget._stateProperties.currentPosition!.dx > + widget._stateProperties.startOffset!.dx + ? ChartSwipeDirection.start + : ChartSwipeDirection.end) + : (velocity > 0 + ? ChartSwipeDirection.start + : ChartSwipeDirection.end); chart.onPlotAreaSwipe!(swipeDirection); } } ///Load More feature if (chart.loadMoreIndicatorBuilder != null && - _stateProperties.startOffset != null && - _stateProperties.currentPosition != null) { - final bool verticallyDragging = (_stateProperties.currentPosition!.dy - - _stateProperties.startOffset!.dy) - .abs() > - (_stateProperties.currentPosition!.dx - - _stateProperties.startOffset!.dx) - .abs(); - if ((!verticallyDragging && !_stateProperties.requireInvertedAxis) || - (verticallyDragging && _stateProperties.requireInvertedAxis)) { + widget._stateProperties.startOffset != null && + widget._stateProperties.currentPosition != null) { + final bool verticallyDragging = + (widget._stateProperties.currentPosition!.dy - + widget._stateProperties.startOffset!.dy) + .abs() > + (widget._stateProperties.currentPosition!.dx - + widget._stateProperties.startOffset!.dx) + .abs(); + if ((!verticallyDragging && + !widget._stateProperties.requireInvertedAxis) || + (verticallyDragging && + widget._stateProperties.requireInvertedAxis)) { bool loadMore = false; final ChartAxisRendererDetails primaryXAxisDetails = - _stateProperties.chartAxis.primaryXAxisDetails; + widget._stateProperties.chartAxis.primaryXAxisDetails; // Here, direction is set accordingly based on the axis transposed value // and primary x-axis inversed value. final ChartSwipeDirection direction = - _stateProperties.requireInvertedAxis - ? (_stateProperties.currentPosition!.dy > - _stateProperties.startOffset!.dy + widget._stateProperties.requireInvertedAxis + ? (widget._stateProperties.currentPosition!.dy > + widget._stateProperties.startOffset!.dy ? primaryXAxisDetails.axis.isInversed ? ChartSwipeDirection.start : ChartSwipeDirection.end : primaryXAxisDetails.axis.isInversed ? ChartSwipeDirection.end : ChartSwipeDirection.start) - : (_stateProperties.currentPosition!.dx > - _stateProperties.startOffset!.dx + : (widget._stateProperties.currentPosition!.dx > + widget._stateProperties.startOffset!.dx ? primaryXAxisDetails.axis.isInversed ? ChartSwipeDirection.end : ChartSwipeDirection.start @@ -3852,10 +3933,11 @@ class ContainerArea extends StatelessWidget { : ChartSwipeDirection.end); for (int axisIndex = 0; axisIndex < - _stateProperties.chartAxis.axisRenderersCollection.length; + widget._stateProperties.chartAxis.axisRenderersCollection + .length; axisIndex++) { final ChartAxisRendererDetails axisDetails = - AxisHelper.getAxisRendererDetails(_stateProperties + AxisHelper.getAxisRendererDetails(widget._stateProperties .chartAxis.axisRenderersCollection[axisIndex]); if (((!verticallyDragging && axisDetails.orientation == @@ -3874,27 +3956,27 @@ class ContainerArea extends StatelessWidget { } } - if (loadMore && !_stateProperties.isLoadMoreIndicator) { - _stateProperties.isLoadMoreIndicator = true; - _stateProperties.loadMoreViewStateSetter(() { - _stateProperties.swipeDirection = direction; + if (loadMore && !widget._stateProperties.isLoadMoreIndicator) { + widget._stateProperties.isLoadMoreIndicator = true; + widget._stateProperties.loadMoreViewStateSetter(() { + widget._stateProperties.swipeDirection = direction; }); } else { - _stateProperties.isLoadMoreIndicator = false; + widget._stateProperties.isLoadMoreIndicator = false; } } } - _stateProperties.startOffset = null; - _stateProperties.currentPosition = null; + widget._stateProperties.startOffset = null; + widget._stateProperties.currentPosition = null; } } /// To perform mouse hover event void _performMouseHover(PointerEvent event) { - if (_stateProperties.chartState.mounted) { + if (widget._stateProperties.chartState.mounted) { final TooltipRenderingDetails tooltipRenderingDetails = TooltipHelper.getRenderingDetails( - _stateProperties.renderingDetails.tooltipBehaviorRenderer); + widget._stateProperties.renderingDetails.tooltipBehaviorRenderer); tooltipRenderingDetails.isHovering = true; tooltipRenderingDetails.isInteraction = true; final Offset position = renderBox.globalToLocal(event.position); @@ -3903,10 +3985,10 @@ class ContainerArea extends StatelessWidget { if ((chart.tooltipBehavior.enable && chart.tooltipBehavior.activationMode == ActivationMode.singleTap) || - shouldShowAxisTooltip(_stateProperties)) { + shouldShowAxisTooltip(widget._stateProperties)) { chart.tooltipBehavior.builder != null ? tooltipRenderingDetails.showTemplateTooltip(position) - : _stateProperties.renderingDetails.tooltipBehaviorRenderer + : widget._stateProperties.renderingDetails.tooltipBehaviorRenderer .onEnter(position.dx, position.dy); } if (chart.trackballBehavior.enable && @@ -3916,7 +3998,7 @@ class ContainerArea extends StatelessWidget { chart.trackballBehavior.activationMode == ActivationMode.singleTap) { chart.trackballBehavior.builder != null ? trackballRenderingDetails.showTemplateTrackball(position) - : _stateProperties.trackballBehaviorRenderer + : widget._stateProperties.trackballBehaviorRenderer .onEnter(position.dx, position.dy); } if (chart.crosshairBehavior.enable && @@ -3924,7 +4006,7 @@ class ContainerArea extends StatelessWidget { SeriesHelper.getSeriesRendererDetails(cartesianSeriesRenderer).series is! ErrorBarSeries && chart.crosshairBehavior.activationMode == ActivationMode.singleTap) { - _stateProperties.crosshairBehaviorRenderer + widget._stateProperties.crosshairBehaviorRenderer .onEnter(position.dx, position.dy); } } @@ -3932,22 +4014,22 @@ class ContainerArea extends StatelessWidget { /// To perform the mouse exit event void _performMouseExit(PointerEvent event) { - if (_stateProperties.chartState.mounted) { + if (widget._stateProperties.chartState.mounted) { TooltipHelper.getRenderingDetails( - _stateProperties.renderingDetails.tooltipBehaviorRenderer) + widget._stateProperties.renderingDetails.tooltipBehaviorRenderer) .isHovering = false; final Offset position = renderBox.globalToLocal(event.position); if (chart.tooltipBehavior.enable || - shouldShowAxisTooltip(_stateProperties)) { - _stateProperties.renderingDetails.tooltipBehaviorRenderer + shouldShowAxisTooltip(widget._stateProperties)) { + widget._stateProperties.renderingDetails.tooltipBehaviorRenderer .onExit(position.dx, position.dy); } if (chart.crosshairBehavior.enable) { - _stateProperties.crosshairBehaviorRenderer + widget._stateProperties.crosshairBehaviorRenderer .onExit(position.dx, position.dy); } if (chart.trackballBehavior.enable) { - _stateProperties.trackballBehaviorRenderer + widget._stateProperties.trackballBehaviorRenderer .onExit(position.dx, position.dy); } } @@ -3961,31 +4043,33 @@ class ContainerArea extends StatelessWidget { final List userInteractionWidgets = []; final ZoomRectPainter zoomRectPainter = - ZoomRectPainter(stateProperties: _stateProperties); + ZoomRectPainter(stateProperties: widget._stateProperties); final ZoomingBehaviorDetails zoomingBehaviorDetails = ZoomPanBehaviorHelper.getRenderingDetails( - _stateProperties.zoomPanBehaviorRenderer); + widget._stateProperties.zoomPanBehaviorRenderer); zoomingBehaviorDetails.painter = zoomRectPainter; CrosshairHelper.setStateProperties( - chart.crosshairBehavior, _stateProperties); - TooltipHelper.setStateProperties(chart.tooltipBehavior, _stateProperties); + chart.crosshairBehavior, widget._stateProperties); + TooltipHelper.setStateProperties( + chart.tooltipBehavior, widget._stateProperties); TrackballHelper.setStateProperties( - chart.trackballBehavior, _stateProperties); + chart.trackballBehavior, widget._stateProperties); ZoomPanBehaviorHelper.setStateProperties( - chart.zoomPanBehavior, _stateProperties); + chart.zoomPanBehavior, widget._stateProperties); // ignore: unnecessary_null_comparison if (chart.trackballBehavior != null && chart.trackballBehavior.enable) { if (chart.trackballBehavior.builder != null) { trackballRenderingDetails.trackballTemplate = TrackballTemplate( key: GlobalKey>(), trackballBehavior: chart.trackballBehavior, - stateProperties: _stateProperties); + stateProperties: widget._stateProperties); userInteractionWidgets .add(trackballRenderingDetails.trackballTemplate!); } else { trackballPainter = TrackballPainter( - stateProperties: _stateProperties, - valueNotifier: _stateProperties.repaintNotifiers['trackball']!); + stateProperties: widget._stateProperties, + valueNotifier: + widget._stateProperties.repaintNotifiers['trackball']!); trackballRenderingDetails.trackballPainter = trackballPainter; userInteractionWidgets.add(Container( height: constraints.maxHeight, @@ -3997,10 +4081,11 @@ class ContainerArea extends StatelessWidget { // ignore: unnecessary_null_comparison if (chart.crosshairBehavior != null && chart.crosshairBehavior.enable) { crosshairPainter = CrosshairPainter( - stateProperties: _stateProperties, - valueNotifier: _stateProperties.repaintNotifiers['crosshair']!); + stateProperties: widget._stateProperties, + valueNotifier: + widget._stateProperties.repaintNotifiers['crosshair']!); CrosshairHelper.getRenderingDetails( - _stateProperties.crosshairBehaviorRenderer) + widget._stateProperties.crosshairBehaviorRenderer) .crosshairPainter = crosshairPainter; userInteractionWidgets.add(Container( height: constraints.maxHeight, @@ -4013,7 +4098,7 @@ class ContainerArea extends StatelessWidget { TooltipHelper.getRenderingDetails( _renderingDetails.tooltipBehaviorRenderer); if (chart.tooltipBehavior.enable || - shouldShowAxisTooltip(_stateProperties)) { + shouldShowAxisTooltip(widget._stateProperties)) { tooltipRenderingDetails.prevTooltipValue = tooltipRenderingDetails.currentTooltipValue = null; tooltipRenderingDetails.chartTooltip = SfTooltip( @@ -4046,17 +4131,17 @@ class ContainerArea extends StatelessWidget { final Widget uiWidget = IgnorePointer( ignoring: chart.annotations != null, child: Stack(children: userInteractionWidgets)); - _stateProperties.renderingDetails.chartWidgets!.add(uiWidget); + widget._stateProperties.renderingDetails.chartWidgets!.add(uiWidget); } /// Triggering onAxisLabelTapped event void _triggerAxisLabelEvent(Offset position) { for (int i = 0; - i < _stateProperties.chartAxis.axisRenderersCollection.length; + i < widget._stateProperties.chartAxis.axisRenderersCollection.length; i++) { final ChartAxisRendererDetails axisDetails = AxisHelper.getAxisRendererDetails( - _stateProperties.chartAxis.axisRenderersCollection[i]); + widget._stateProperties.chartAxis.axisRenderersCollection[i]); final List labels = axisDetails.visibleLabels; for (int k = 0; k < labels.length; k++) { if (axisDetails.axis.isVisible && @@ -4078,17 +4163,17 @@ class ContainerArea extends StatelessWidget { CustomPainter? customPainter; final PainterKey painterKey = PainterKey( index: value, name: 'series $value', isRenderCompleted: false); - _stateProperties.painterKeys.add(painterKey); + widget._stateProperties.painterKeys.add(painterKey); final SeriesRendererDetails seriesRendererDetails = SeriesHelper.getSeriesRendererDetails(seriesRenderer); switch (seriesRendererDetails.seriesType) { case 'line': customPainter = LineChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as LineSeriesRenderer, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), painterKey: painterKey, animationController: controller, @@ -4096,22 +4181,22 @@ class ContainerArea extends StatelessWidget { break; case 'spline': customPainter = SplineChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as SplineSeriesRenderer, painterKey: painterKey, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), animationController: controller, notifier: seriesRendererDetails.repaintNotifier); break; case 'column': customPainter = ColumnChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as ColumnSeriesRenderer, - isRepaint: !(_stateProperties.zoomedState != null) || - _stateProperties.zoomedAxisRendererStates.isNotEmpty, + isRepaint: !(widget._stateProperties.zoomedState != null) || + widget._stateProperties.zoomedAxisRendererStates.isNotEmpty, painterKey: painterKey, animationController: controller, notifier: seriesRendererDetails.repaintNotifier); @@ -4119,11 +4204,11 @@ class ContainerArea extends StatelessWidget { case 'scatter': customPainter = ScatterChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as ScatterSeriesRenderer, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), painterKey: painterKey, animationController: controller, @@ -4131,23 +4216,23 @@ class ContainerArea extends StatelessWidget { break; case 'stepline': customPainter = StepLineChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as StepLineSeriesRenderer, painterKey: painterKey, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), animationController: controller, notifier: seriesRendererDetails.repaintNotifier); break; case 'area': customPainter = AreaChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as AreaSeriesRenderer, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), painterKey: painterKey, animationController: controller, @@ -4155,11 +4240,11 @@ class ContainerArea extends StatelessWidget { break; case 'bubble': customPainter = BubbleChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as BubbleSeriesRenderer, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), painterKey: painterKey, animationController: controller, @@ -4167,21 +4252,22 @@ class ContainerArea extends StatelessWidget { break; case 'bar': customPainter = BarChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as BarSeriesRenderer, - isRepaint: ((_stateProperties.zoomedState != null) == false) || - _stateProperties.zoomedAxisRendererStates.isNotEmpty, + isRepaint: + ((widget._stateProperties.zoomedState != null) == false) || + widget._stateProperties.zoomedAxisRendererStates.isNotEmpty, painterKey: painterKey, animationController: controller, notifier: seriesRendererDetails.repaintNotifier); break; case 'fastline': customPainter = FastLineChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as FastLineSeriesRenderer, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), painterKey: painterKey, animationController: controller, @@ -4189,168 +4275,168 @@ class ContainerArea extends StatelessWidget { break; case 'rangecolumn': customPainter = RangeColumnChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as RangeColumnSeriesRenderer, painterKey: painterKey, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), animationController: controller, notifier: seriesRendererDetails.repaintNotifier); break; case 'rangearea': customPainter = RangeAreaChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as RangeAreaSeriesRenderer, painterKey: painterKey, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), animationController: controller, notifier: seriesRendererDetails.repaintNotifier); break; case 'steparea': customPainter = StepAreaChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as StepAreaSeriesRenderer, painterKey: painterKey, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), animationController: controller, notifier: seriesRendererDetails.repaintNotifier); break; case 'splinearea': customPainter = SplineAreaChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as SplineAreaSeriesRenderer, painterKey: painterKey, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), animationController: controller, notifier: seriesRendererDetails.repaintNotifier); break; case 'splinerangearea': customPainter = SplineRangeAreaChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as SplineRangeAreaSeriesRenderer, painterKey: painterKey, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), animationController: controller, notifier: seriesRendererDetails.repaintNotifier); break; case 'stackedarea': customPainter = StackedAreaChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as StackedAreaSeriesRenderer, painterKey: painterKey, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), animationController: controller, notifier: seriesRendererDetails.repaintNotifier); break; case 'stackedbar': customPainter = StackedBarChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as StackedBarSeriesRenderer, painterKey: painterKey, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), animationController: controller, notifier: seriesRendererDetails.repaintNotifier); break; case 'stackedcolumn': customPainter = StackedColummnChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as StackedColumnSeriesRenderer, painterKey: painterKey, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), animationController: controller, notifier: seriesRendererDetails.repaintNotifier); break; case 'stackedline': customPainter = StackedLineChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as StackedLineSeriesRenderer, painterKey: painterKey, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), animationController: controller, notifier: seriesRendererDetails.repaintNotifier); break; case 'stackedarea100': customPainter = StackedArea100ChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as StackedArea100SeriesRenderer, painterKey: painterKey, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), animationController: controller, notifier: seriesRendererDetails.repaintNotifier); break; case 'stackedbar100': customPainter = StackedBar100ChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as StackedBar100SeriesRenderer, painterKey: painterKey, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), animationController: controller, notifier: seriesRendererDetails.repaintNotifier); break; case 'stackedcolumn100': customPainter = StackedColumn100ChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as StackedColumn100SeriesRenderer, painterKey: painterKey, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), animationController: controller, notifier: seriesRendererDetails.repaintNotifier); break; case 'stackedline100': customPainter = StackedLine100ChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as StackedLine100SeriesRenderer, painterKey: painterKey, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), animationController: controller, notifier: seriesRendererDetails.repaintNotifier); break; case 'hilo': customPainter = HiloPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as HiloSeriesRenderer, painterKey: painterKey, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), animationController: controller, notifier: seriesRendererDetails.repaintNotifier); @@ -4358,73 +4444,73 @@ class ContainerArea extends StatelessWidget { case 'hiloopenclose': customPainter = HiloOpenClosePainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as HiloOpenCloseSeriesRenderer, painterKey: painterKey, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), animationController: controller, notifier: seriesRendererDetails.repaintNotifier); break; case 'candle': customPainter = CandlePainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as CandleSeriesRenderer, painterKey: painterKey, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), animationController: controller, notifier: seriesRendererDetails.repaintNotifier); break; case 'histogram': customPainter = HistogramChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as HistogramSeriesRenderer, - chartSeries: _stateProperties.chartSeries, + chartSeries: widget._stateProperties.chartSeries, painterKey: painterKey, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), animationController: controller, notifier: seriesRendererDetails.repaintNotifier); break; case 'boxandwhisker': customPainter = BoxAndWhiskerPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as BoxAndWhiskerSeriesRenderer, painterKey: painterKey, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), animationController: controller, notifier: seriesRendererDetails.repaintNotifier); break; case 'waterfall': customPainter = WaterfallChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as WaterfallSeriesRenderer, painterKey: painterKey, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), animationController: controller, notifier: seriesRendererDetails.repaintNotifier); break; case 'errorbar': customPainter = ErrorBarChartPainter( - stateProperties: _stateProperties, + stateProperties: widget._stateProperties, seriesRenderer: seriesRenderer as ErrorBarSeriesRenderer, painterKey: painterKey, - isRepaint: _stateProperties.zoomedState != null - ? _stateProperties.zoomedAxisRendererStates.isNotEmpty - : (_stateProperties.legendToggling || + isRepaint: widget._stateProperties.zoomedState != null + ? widget._stateProperties.zoomedAxisRendererStates.isNotEmpty + : (widget._stateProperties.legendToggling || seriesRendererDetails.needsRepaint == true), animationController: controller, notifier: seriesRendererDetails.repaintNotifier); 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 8a38f7359..d8262fdb4 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 @@ -1818,24 +1818,22 @@ void calculateDataLabelPosition( point.isVisible && point.isGap != true && (point.y != 0 || dataLabel.showZeroValue)) { - final double markerPointX = dataLabel.builder == null - ? seriesRendererDetails.seriesType.contains('hilo') == true || + final double markerPointX = + (seriesRendererDetails.seriesType.contains('hilo') == true || seriesRendererDetails.seriesType == 'candle' || - isBoxSeries + isBoxSeries) ? seriesRendererDetails.stateProperties.requireInvertedAxis == true ? point.region!.centerRight.dx : point.region!.topCenter.dx - : point.markerPoint!.x - : templateLocation!.dx; - final double markerPointY = dataLabel.builder == null - ? seriesRendererDetails.seriesType.contains('hilo') == true || + : point.markerPoint!.x; + final double markerPointY = + seriesRendererDetails.seriesType.contains('hilo') == true || seriesRendererDetails.seriesType == 'candle' || isBoxSeries ? seriesRendererDetails.stateProperties.requireInvertedAxis == true ? point.region!.centerRight.dy : point.region!.topCenter.dy - : point.markerPoint!.y - : templateLocation!.dy; + : point.markerPoint!.y; final ChartLocation markerPoint2 = calculatePoint( point.xValue, seriesRendererDetails.yAxisDetails!.axis.isInversed == true @@ -1880,28 +1878,22 @@ void calculateDataLabelPosition( ? measureText(point.label2!, font) : templateSize!; chartLocation2 = ChartLocation( - dataLabel.builder == null - ? seriesRendererDetails.seriesType.contains('hilo') == true || - seriesRendererDetails.seriesType == 'candle' || - isBoxSeries - ? seriesRendererDetails - .stateProperties.requireInvertedAxis == - true - ? point.region!.centerLeft.dx - : point.region!.bottomCenter.dx - : point.markerPoint2!.x - : templateLocation!.dx, - dataLabel.builder == null - ? seriesRendererDetails.seriesType.contains('hilo') == true || - seriesRendererDetails.seriesType == 'candle' || - isBoxSeries - ? seriesRendererDetails - .stateProperties.requireInvertedAxis == - true - ? point.region!.centerLeft.dy - : point.region!.bottomCenter.dy - : point.markerPoint2!.y - : templateLocation!.dy); + seriesRendererDetails.seriesType.contains('hilo') == true || + seriesRendererDetails.seriesType == 'candle' || + isBoxSeries + ? seriesRendererDetails.stateProperties.requireInvertedAxis == + true + ? point.region!.centerLeft.dx + : point.region!.bottomCenter.dx + : point.markerPoint2!.x, + seriesRendererDetails.seriesType.contains('hilo') == true || + seriesRendererDetails.seriesType == 'candle' || + isBoxSeries + ? seriesRendererDetails.stateProperties.requireInvertedAxis == + true + ? point.region!.centerLeft.dy + : point.region!.bottomCenter.dy + : point.markerPoint2!.y); if (isBoxSeries) { if (seriesRendererDetails.stateProperties.requireInvertedAxis == false) { 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 984fffdd7..5c7220860 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 @@ -1307,7 +1307,12 @@ class TrackballRenderingDetails { for (final ChartPointInfo pointInfo in chartPointInfo) { if (pointInfo.xPosition == leastX) { leastPointInfo.add(pointInfo); - visiblePoints.clear(); + if (!(trackballBehavior.tooltipDisplayMode == + TrackballDisplayMode.floatAllPoints && + leastPointInfo.length > 1 && + pointInfo.seriesIndex != + leastPointInfo[leastPointInfo.length - 2].seriesIndex)) + visiblePoints.clear(); seriesType = pointInfo.seriesRendererDetails!.seriesType; isRangeTypeSeries = seriesType.contains('range') || seriesType.contains('hilo') || 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 aff4e977f..21824bf30 100644 --- a/packages/syncfusion_flutter_charts/lib/src/chart/utils/helper.dart +++ b/packages/syncfusion_flutter_charts/lib/src/chart/utils/helper.dart @@ -189,9 +189,18 @@ num calculateMinPointsDelta( seriesRendererDetails.series; num value; xValues = []; + final String seriesType = seriesRendererDetails.seriesType; + final bool isRectSeries = seriesType.contains('column') || + seriesType.contains('stackedbar') || + seriesType == 'bar' || + seriesType == 'histogram' || + seriesType == 'waterfall' || + seriesType.contains('candle') || + seriesType.contains('hilo'); final ChartAxisRendererDetails axisRendererDetails = AxisHelper.getAxisRendererDetails(axisRenderer); if (seriesRendererDetails.visible! == true && + isRectSeries && ((axisRendererDetails.name == series.xAxisName) || (axisRendererDetails.name == (stateProperties.chart.primaryXAxis.name ?? diff --git a/packages/syncfusion_flutter_charts/lib/src/circular_chart/series_painter/doughnut_series_painter.dart b/packages/syncfusion_flutter_charts/lib/src/circular_chart/series_painter/doughnut_series_painter.dart index 86952e23e..31c8fc42e 100644 --- a/packages/syncfusion_flutter_charts/lib/src/circular_chart/series_painter/doughnut_series_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/circular_chart/series_painter/doughnut_series_painter.dart @@ -68,21 +68,23 @@ class DoughnutChartPainter extends CustomPainter { stateProperties.prevSeriesRenderer?.seriesType == 'doughnut') ? stateProperties.oldPoints![i] : null); - pointStartAngle = seriesRenderer.circularRenderPoint( - stateProperties.chart, - seriesRenderer, - point, - pointStartAngle, - point.innerRadius, - point.outerRadius, - canvas, - index, - i, - seriesAnimation?.value ?? 1, - 1, - checkIsAnyPointSelect(seriesRenderer, point, stateProperties.chart), - oldPoint, - stateProperties.oldPoints); + if (point.isVisible || (oldPoint != null && oldPoint.isVisible)) { + pointStartAngle = seriesRenderer.circularRenderPoint( + stateProperties.chart, + seriesRenderer, + point, + pointStartAngle, + point.innerRadius, + point.outerRadius, + canvas, + index, + i, + seriesAnimation?.value ?? 1, + 1, + checkIsAnyPointSelect(seriesRenderer, point, stateProperties.chart), + oldPoint, + stateProperties.oldPoints); + } } if (seriesRenderer.renderList.isNotEmpty) { diff --git a/packages/syncfusion_flutter_charts/lib/src/circular_chart/series_painter/pie_chart_painter.dart b/packages/syncfusion_flutter_charts/lib/src/circular_chart/series_painter/pie_chart_painter.dart index 6d1998844..97f108294 100644 --- a/packages/syncfusion_flutter_charts/lib/src/circular_chart/series_painter/pie_chart_painter.dart +++ b/packages/syncfusion_flutter_charts/lib/src/circular_chart/series_painter/pie_chart_painter.dart @@ -76,21 +76,23 @@ class PieChartPainter extends CustomPainter { ? stateProperties.oldPoints![i] : null); point.innerRadius = 0.0; - pointStartAngle = seriesRenderer.circularRenderPoint( - chart, - seriesRenderer, - point, - pointStartAngle, - point.innerRadius, - point.outerRadius, - canvas, - index, - i, - seriesAnimation?.value ?? 1, - seriesAnimation?.value ?? 1, - isAnyPointNeedSelect, - oldPoint, - stateProperties.oldPoints); + if (point.isVisible || (oldPoint != null && oldPoint.isVisible)) { + pointStartAngle = seriesRenderer.circularRenderPoint( + chart, + seriesRenderer, + point, + pointStartAngle, + point.innerRadius, + point.outerRadius, + canvas, + index, + i, + seriesAnimation?.value ?? 1, + seriesAnimation?.value ?? 1, + isAnyPointNeedSelect, + oldPoint, + stateProperties.oldPoints); + } } if (seriesRenderer.renderList.isNotEmpty) { Shader? chartShader; 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 086a1d925..370c9b3e3 100644 --- a/packages/syncfusion_flutter_charts/lib/src/common/template/rendering.dart +++ b/packages/syncfusion_flutter_charts/lib/src/common/template/rendering.dart @@ -21,12 +21,14 @@ import '../utils/helper.dart'; class RenderTemplate extends StatefulWidget { /// Creates an instance of render template. // ignore: prefer_const_constructors_in_immutables - RenderTemplate( - {required this.template, - this.needMeasure, - required this.templateLength, - required this.templateIndex, - required this.stateProperties}); + RenderTemplate({ + required this.template, + this.needMeasure, + required this.templateLength, + required this.templateIndex, + required this.stateProperties, + this.notifier, + }); /// Hold the value of chart template info. final ChartTemplateInfo template; @@ -46,6 +48,9 @@ class RenderTemplate extends StatefulWidget { /// Specifies whether it is annotation. bool? isAnnotation; + /// Specifies the template notifier value. + final ValueNotifier? notifier; + @override State createState() => _RenderTemplateState(); } @@ -72,6 +77,7 @@ class _RenderTemplateState extends State templateInfo: templateInfo, stateProperties: widget.stateProperties, animationController: animationController, + notifier: widget.notifier, child: templateInfo.widget!); } else { renderWidget = _ChartTemplateRenderObject( @@ -138,7 +144,8 @@ class _ChartTemplateRenderObject extends SingleChildRenderObjectWidget { required Widget child, required this.templateInfo, required this.stateProperties, - required this.animationController}) + required this.animationController, + this.notifier}) : super(key: key, child: child); final ChartTemplateInfo templateInfo; @@ -147,10 +154,12 @@ class _ChartTemplateRenderObject extends SingleChildRenderObjectWidget { final AnimationController? animationController; + final ValueNotifier? notifier; + @override RenderObject createRenderObject(BuildContext context) { return _ChartTemplateRenderBox( - templateInfo, stateProperties, animationController); + templateInfo, stateProperties, animationController, notifier); } @override @@ -162,8 +171,8 @@ class _ChartTemplateRenderObject extends SingleChildRenderObjectWidget { /// Render the annotation widget in the respective position. class _ChartTemplateRenderBox extends RenderShiftedBox { - _ChartTemplateRenderBox( - this._templateInfo, this.stateProperties, this._animationController, + _ChartTemplateRenderBox(this._templateInfo, this.stateProperties, + this._animationController, this.notifier, [RenderBox? child]) : super(child); @@ -173,6 +182,8 @@ class _ChartTemplateRenderBox extends RenderShiftedBox { final AnimationController? _animationController; + final ValueNotifier? notifier; + ChartTemplateInfo get templateInfo => _templateInfo; set templateInfo(ChartTemplateInfo value) { @@ -182,6 +193,22 @@ class _ChartTemplateRenderBox extends RenderShiftedBox { } } + @override + void attach(PipelineOwner owner) { + if (notifier != null) { + notifier?.addListener(markNeedsLayout); + } + super.attach(owner); + } + + @override + void detach() { + if (notifier != null) { + notifier?.removeListener(markNeedsLayout); + } + super.detach(); + } + @override void performLayout() { double locationX, locationY; @@ -258,8 +285,24 @@ class _ChartTemplateRenderBox extends RenderShiftedBox { _animationController!, size, _templateInfo.location); - locationX = point.labelLocation!.x; - locationY = point.labelLocation!.y; + locationX = _templateInfo.labelLocation == 'labelLocation' + ? point.labelLocation!.x + : _templateInfo.labelLocation == 'labelLocation2' + ? point.labelLocation2!.x + : _templateInfo.labelLocation == 'labelLocation3' + ? point.labelLocation3!.x + : _templateInfo.labelLocation == 'labelLocation4' + ? point.labelLocation4!.x + : point.labelLocation5!.x; + locationY = _templateInfo.labelLocation == 'labelLocation' + ? point.labelLocation!.y + : _templateInfo.labelLocation == 'labelLocation2' + ? point.labelLocation2!.y + : _templateInfo.labelLocation == 'labelLocation3' + ? point.labelLocation3!.y + : _templateInfo.labelLocation == 'labelLocation4' + ? point.labelLocation4!.y + : point.labelLocation5!.y; isLabelWithInRange = isLabelWithinRange(seriesRendererDetails, point); } @@ -506,7 +549,8 @@ class ChartTemplateInfo { required this.clipRect, this.needMeasure = true, ChartAlignment? horizontalAlignment, - ChartAlignment? verticalAlignment}) + ChartAlignment? verticalAlignment, + this.labelLocation}) : horizontalAlignment = horizontalAlignment ?? ChartAlignment.center, verticalAlignment = verticalAlignment ?? ChartAlignment.center; @@ -554,4 +598,7 @@ class ChartTemplateInfo { /// Specifies whether to measure the template. bool needMeasure; + + /// Specifies the label location + String? labelLocation; } diff --git a/packages/syncfusion_flutter_charts/pubspec.yaml b/packages/syncfusion_flutter_charts/pubspec.yaml index 14be6e708..908dcd7ae 100644 --- a/packages/syncfusion_flutter_charts/pubspec.yaml +++ b/packages/syncfusion_flutter_charts/pubspec.yaml @@ -1,6 +1,6 @@ name: syncfusion_flutter_charts 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: 20.4.38 +version: 21.1.35 homepage: https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_flutter_charts environment: @@ -9,9 +9,9 @@ environment: dependencies: flutter: sdk: flutter - intl: ">=0.17.0 <0.20.0" + intl: ^0.17.0 vector_math: ">=2.1.0 <=3.0.0" - syncfusion_flutter_core: ^20.4.38 + syncfusion_flutter_core: ^21.1.35 dev_dependencies: flutter_test: diff --git a/packages/syncfusion_flutter_core/example/lib/main.dart b/packages/syncfusion_flutter_core/example/lib/main.dart index 8e7d27b1a..a6c555d3b 100644 --- a/packages/syncfusion_flutter_core/example/lib/main.dart +++ b/packages/syncfusion_flutter_core/example/lib/main.dart @@ -25,11 +25,40 @@ class _MyHomePage extends StatefulWidget { } class _MyHomePageState extends State<_MyHomePage> { + List<_SalesData> data = <_SalesData>[ + _SalesData('Jan', 35), + _SalesData('Feb', 28), + _SalesData('Mar', 34), + _SalesData('Apr', 32), + _SalesData('May', 40) + ]; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Syncfusion Flutter Chart')), - body: - SfCartesianChart()); // Commented until the chart moves to null safety; + body: SfCartesianChart( + primaryXAxis: CategoryAxis(), + // Chart title + title: ChartTitle(text: 'Half yearly sales analysis'), + // Enable legend + legend: Legend(isVisible: true), + // Enable tooltip + tooltipBehavior: TooltipBehavior(enable: true), + series: >[ + LineSeries<_SalesData, String>( + dataSource: data, + xValueMapper: (_SalesData sales, _) => sales.year, + yValueMapper: (_SalesData sales, _) => sales.sales, + name: 'Sales', + // Enable data label + dataLabelSettings: const DataLabelSettings(isVisible: true)) + ])); } } + +class _SalesData { + _SalesData(this.year, this.sales); + + final String year; + final double sales; +} 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 e29c712b9..87e535596 100644 --- a/packages/syncfusion_flutter_core/lib/src/theme/datagrid_theme.dart +++ b/packages/syncfusion_flutter_core/lib/src/theme/datagrid_theme.dart @@ -129,6 +129,8 @@ class SfDataGridThemeData with Diagnosticable { Color? filterIconHoverColor, Color? sortOrderNumberColor, Color? sortOrderNumberBackgroundColor, + TextStyle? filterPopupTextStyle, + TextStyle? filterPopupDisabledTextStyle, }) { return SfDataGridThemeData.raw( brightness: brightness, @@ -151,7 +153,9 @@ class SfDataGridThemeData with Diagnosticable { filterIconColor: filterIconColor, filterIconHoverColor: filterIconHoverColor, sortOrderNumberColor: sortOrderNumberColor, - sortOrderNumberBackgroundColor: sortOrderNumberBackgroundColor); + sortOrderNumberBackgroundColor: sortOrderNumberBackgroundColor, + filterPopupTextStyle: filterPopupTextStyle, + filterPopupDisabledTextStyle: filterPopupDisabledTextStyle); } /// Create a [SfDataGridThemeData] given a set of exact values. @@ -182,7 +186,9 @@ class SfDataGridThemeData with Diagnosticable { required this.filterIconColor, required this.filterIconHoverColor, required this.sortOrderNumberColor, - required this.sortOrderNumberBackgroundColor}); + required this.sortOrderNumberBackgroundColor, + required this.filterPopupTextStyle, + required this.filterPopupDisabledTextStyle}); /// The brightness of the overall theme of the /// application for the [SfDataGrid] widgets. @@ -482,6 +488,13 @@ class SfDataGridThemeData with Diagnosticable { /// when the order of the sorting is shown. final Color? sortOrderNumberBackgroundColor; + /// The [TextStyle] of the options in filter popup menu except the items + /// which are already selected. + final TextStyle? filterPopupTextStyle; + + /// The [TextStyle] of the disabled options in filter popup menu. + final TextStyle? filterPopupDisabledTextStyle; + /// Creates a copy of this theme but with the given /// fields replaced with the new values. SfDataGridThemeData copyWith({ @@ -506,6 +519,8 @@ class SfDataGridThemeData with Diagnosticable { Color? filterIconHoverColor, Color? sortOrderNumberColor, Color? sortOrderNumberBackgroundColor, + TextStyle? filterPopupTextStyle, + TextStyle? filterPopupDisabledTextStyle, }) { return SfDataGridThemeData.raw( brightness: brightness ?? this.brightness, @@ -532,6 +547,9 @@ class SfDataGridThemeData with Diagnosticable { sortOrderNumberColor: sortOrderNumberColor ?? this.sortOrderNumberColor, sortOrderNumberBackgroundColor: sortOrderNumberBackgroundColor ?? this.sortOrderNumberBackgroundColor, + filterPopupTextStyle: filterPopupTextStyle ?? this.filterPopupTextStyle, + filterPopupDisabledTextStyle: + filterPopupDisabledTextStyle ?? this.filterPopupDisabledTextStyle, ); } @@ -575,6 +593,10 @@ class SfDataGridThemeData with Diagnosticable { a.sortOrderNumberBackgroundColor, b.sortOrderNumberBackgroundColor, t), + filterPopupTextStyle: + TextStyle.lerp(a.filterPopupTextStyle, b.filterPopupTextStyle, t), + filterPopupDisabledTextStyle: TextStyle.lerp( + a.filterPopupDisabledTextStyle, b.filterPopupDisabledTextStyle, t), ); } @@ -609,7 +631,10 @@ class SfDataGridThemeData with Diagnosticable { other.filterIconColor == filterIconColor && other.filterIconHoverColor == filterIconHoverColor && other.sortOrderNumberColor == sortOrderNumberColor && - other.sortOrderNumberBackgroundColor == sortOrderNumberBackgroundColor; + other.sortOrderNumberBackgroundColor == + sortOrderNumberBackgroundColor && + other.filterPopupTextStyle == filterPopupTextStyle && + other.filterPopupDisabledTextStyle == filterPopupDisabledTextStyle; } @override @@ -634,7 +659,9 @@ class SfDataGridThemeData with Diagnosticable { filterIconColor, filterIconHoverColor, sortOrderNumberColor, - sortOrderNumberBackgroundColor + sortOrderNumberBackgroundColor, + filterPopupTextStyle, + filterPopupDisabledTextStyle ]; return Object.hashAll(values); } @@ -690,6 +717,12 @@ class SfDataGridThemeData with Diagnosticable { properties.add(ColorProperty( 'sortOrderNumberBackgroundColor', sortOrderNumberBackgroundColor, defaultValue: defaultData.sortOrderNumberBackgroundColor)); + properties.add(DiagnosticsProperty( + 'filterPopupTextStyle', filterPopupTextStyle, + defaultValue: defaultData.filterPopupTextStyle)); + properties.add(DiagnosticsProperty( + 'filterPopupDisabledTextStyle', filterPopupDisabledTextStyle, + defaultValue: defaultData.filterPopupDisabledTextStyle)); } } diff --git a/packages/syncfusion_flutter_core/pubspec.yaml b/packages/syncfusion_flutter_core/pubspec.yaml index bd8e2ae89..6fc2d86cc 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: 20.4.38 +version: 21.1.35 homepage: https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_flutter_core environment: diff --git a/packages/syncfusion_flutter_datagrid/CHANGELOG.md b/packages/syncfusion_flutter_datagrid/CHANGELOG.md index 0fefdc892..2187f9141 100644 --- a/packages/syncfusion_flutter_datagrid/CHANGELOG.md +++ b/packages/syncfusion_flutter_datagrid/CHANGELOG.md @@ -2,6 +2,49 @@ **Features** +* Provided the support to change the position (left or right) of sort and filter icons in the column headers when sorting or filtering is applied. +* The `onCellSubmit`, `canSubmitCell`, and `performSorting` methods are marked async so that cell submission and sorting can be asynchronous. +* Provided the support to change the text style of all the elements in the filter pop menu. Users can set different text styles for enabled and disabled items. +* Provided the support to get the `currentcell` details when `navigationMode` is `row`. + +## [20.4.53] - 03/07/2023 + +**Bugs** + +* The scrolling performance is improved when autofitting the rows using `onQueryRowHeight` callback with large collection of rows. + +## [20.4.51] - 02/21/2023 + +**Bugs** + +* The RangeError exception is no longer be thrown when rebuilding the DataGrid after applying the filtering. +* The filter is now applied properly to the DataGrid when changing the DataGridSource dynamically. + +## [20.4.48] - 02/01/2023 + +**Features** + +* Added support for restricting column resizing by checking the `columnIndex` in the `onColumnResizeStart` callback. + +**Bugs** + +* Fixed a LateInitializationError that occurred when opening the filter popup menu after rebuilding the DataGrid with an applied filter. +* Fixed a Type mismatch error that occurred when using an integer value in advanced filtering for a double type column. + +**Breaking changes** + +* `Command` key operations for selection and multi-column sorting have been replaced with `Control` key operations on the macOS platform. + +## [20.4.43] - 01/10/2023 + +**Bugs** + +* Edited rows from the grid when filtering and paging are applied. If the currently applied filter condition is not satisfied, the edited row will no longer be displayed in the DataGrid. + +## [20.4.38] - 12/21/2022 + +**Features** + * Provided the support to change the shape of the built-in checkbox column. * Provided the support to change the filter icon and its color. The padding around the filter icon can also be customized. * Provided the support to customize the filter options in the filter popup. Users can hide the sorting and “Clear Filter” options and show only the checked listbox view or advanced filter popup view to apply filtering. diff --git a/packages/syncfusion_flutter_datagrid/lib/datagrid.dart b/packages/syncfusion_flutter_datagrid/lib/datagrid.dart index 24975b337..ea867ea43 100644 --- a/packages/syncfusion_flutter_datagrid/lib/datagrid.dart +++ b/packages/syncfusion_flutter_datagrid/lib/datagrid.dart @@ -17,7 +17,6 @@ export './src/datagrid_widget/sfdatagrid.dart' hide updateSelectedIndex, updateSelectedRow, - updateCurrentCellIndex, updateVerticalOffset, updateHorizontalOffset, notifyDataGridPropertyChangeListeners, diff --git a/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/helper/enums.dart b/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/helper/enums.dart index 9297764e3..571ba51cc 100644 --- a/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/helper/enums.dart +++ b/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/helper/enums.dart @@ -353,3 +353,12 @@ enum FilterMode { /// Specifies whether both the checked listbox and advanced filter dropdown options should be shown. both, } + +/// The position of the icon in the column headers. +enum ColumnHeaderIconPosition { + /// Specifies that an icon is positioned at the starting position of the column header. + start, + + /// Specifies that an icon is positioned at the ending position of the column header. + end, +} diff --git a/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/runtime/column.dart b/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/runtime/column.dart index 407879225..4b59ed69b 100644 --- a/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/runtime/column.dart +++ b/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/runtime/column.dart @@ -26,6 +26,8 @@ class GridColumn { this.columnWidthMode = ColumnWidthMode.none, this.visible = true, this.allowSorting = true, + this.sortIconPosition = ColumnHeaderIconPosition.end, + this.filterIconPosition = ColumnHeaderIconPosition.end, this.autoFitPadding = const EdgeInsets.all(16.0), this.minimumWidth = double.nan, this.maximumWidth = double.nan, @@ -165,6 +167,13 @@ class GridColumn { /// The amount of space which should be added with the filter icon final EdgeInsetsGeometry filterIconPadding; + + /// The position of the sort icon in the column headers. + final ColumnHeaderIconPosition sortIconPosition; + + /// The position of the filter icon in the column headers. + /// Typically, filter icon is placed next to sort icon. + final ColumnHeaderIconPosition filterIconPosition; } /// A column which displays the values of the string in its cells. @@ -195,6 +204,8 @@ class GridTextColumn extends GridColumn { EdgeInsets autoFitPadding = const EdgeInsets.all(16.0), bool visible = true, bool allowSorting = true, + ColumnHeaderIconPosition sortIconPosition = ColumnHeaderIconPosition.end, + ColumnHeaderIconPosition filterIconPosition = ColumnHeaderIconPosition.end, double minimumWidth = double.nan, double maximumWidth = double.nan, double width = double.nan, @@ -206,6 +217,8 @@ class GridTextColumn extends GridColumn { autoFitPadding: autoFitPadding, visible: visible, allowSorting: allowSorting, + sortIconPosition: sortIconPosition, + filterIconPosition: filterIconPosition, minimumWidth: minimumWidth, maximumWidth: maximumWidth, width: width, @@ -697,7 +710,14 @@ class ColumnSizer { late DataGridRow dataGridRow; switch (dataGridConfiguration.columnWidthCalculationRange) { case ColumnWidthCalculationRange.allRows: - dataGridRow = effectiveRows(dataGridConfiguration.source)[rowIndex]; + // Issue: + // FLUT-7340 - The RangeError exception is thrown when rebuilding the DataGrid after applying the filtering. + // + // Fix: + // The issue occurred because the rows were being fetched from the effective rows collection, + // which contains only the filtered rows instead of all the rows. + // Now, we fetched the rows from the entire collection to calculate the width for all the rows. + dataGridRow = dataGridConfiguration.source.rows[rowIndex]; break; case ColumnWidthCalculationRange.visibleRows: dataGridRow = @@ -814,6 +834,12 @@ class ColumnSizer { for (final GridColumn column in dataGridConfiguration.columns) { column._autoWidth = double.nan; } + + // Need to set `needToSetHorizontalOffset` property to true when the column + // widths change in the RTL mode to get proper visible columns. + if (dataGridConfiguration.textDirection == TextDirection.rtl) { + dataGridConfiguration.container.needToSetHorizontalOffset = true; + } } double _getSortIconWidth(GridColumn column) { @@ -1668,13 +1694,15 @@ class ColumnResizeController { // * Pointer Events /// Handles the pointer down event for the column resizing. - void onPointerDown(PointerDownEvent event, DataRowBase dataRow) { + Future onPointerDown( + PointerDownEvent event, DataRowBase dataRow) async { final DataGridConfiguration dataGridConfiguration = dataGridStateDetails(); if (dataGridConfiguration.isDesktop || _canStartResizeInMobile) { if (_isHeaderRow(dataRow)) { // Clears the editing before start resizing a column. if (dataGridConfiguration.currentCell.isEditing) { - dataGridConfiguration.currentCell.onCellSubmit(dataGridConfiguration); + await dataGridConfiguration.currentCell + .onCellSubmit(dataGridConfiguration); } final VisibleLineInfo? resizingLine = @@ -1914,7 +1942,17 @@ class DataGridFilterHelper { late DataGridAdvancedFilterHelper advancedFilterHelper; /// Provides the height of the popup menu tile. - double get tileHeight => _dataGridStateDetails().isDesktop ? 40.0 : 52.0; + double get tileHeight => _dataGridStateDetails().isDesktop + ? _dataGridStateDetails() + .dataGridThemeHelper! + .filterPopupTextStyle! + .fontSize! + + 26 + : _dataGridStateDetails() + .dataGridThemeHelper! + .filterPopupTextStyle! + .fontSize! + + 38; /// Provides the icon color. Color get iconColor => @@ -1940,18 +1978,13 @@ class DataGridFilterHelper { Color get primaryColor => _dataGridStateDetails().colorScheme!.primary; /// Provides the text style to the tiles. - TextStyle get textStyle => TextStyle( - fontSize: 14.0, - color: textColor, - fontFamily: 'Roboto', - fontWeight: FontWeight.normal); + TextStyle get textStyle => + _dataGridStateDetails().dataGridThemeHelper!.filterPopupTextStyle!; /// Provides the text style to the disabled tiles. - TextStyle get disableTextStyle => TextStyle( - fontSize: 14.0, - color: disableIconColor, - fontFamily: 'Roboto', - fontWeight: FontWeight.normal); + TextStyle get disableTextStyle => _dataGridStateDetails() + .dataGridThemeHelper! + .filterPopupDisabledTextStyle!; /// Apply filter to the effective rows based on `filterConditions`. void applyFilter() { diff --git a/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/selection/selection_manager.dart b/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/selection/selection_manager.dart index 0333d94d8..1b63e4dfb 100644 --- a/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/selection/selection_manager.dart +++ b/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/selection/selection_manager.dart @@ -900,17 +900,17 @@ class RowSelectionManager extends SelectionManagerBase { //KeyNavigation @override - void handleKeyEvent(RawKeyEvent keyEvent) { + Future handleKeyEvent(RawKeyEvent keyEvent) async { final DataGridConfiguration dataGridConfiguration = _dataGridStateDetails!(); if (dataGridConfiguration.currentCell.isEditing && keyEvent.logicalKey != LogicalKeyboardKey.escape) { - if (!dataGridConfiguration.currentCell + if (!await dataGridConfiguration.currentCell .canSubmitCell(dataGridConfiguration)) { return; } - dataGridConfiguration.currentCell + await dataGridConfiguration.currentCell .onCellSubmit(dataGridConfiguration, cancelCanSubmitCell: true); } @@ -989,7 +989,7 @@ class RowSelectionManager extends SelectionManagerBase { if (dataGridConfiguration.allowEditing && dataGridConfiguration.navigationMode == GridNavigationMode.cell && dataGridConfiguration.currentCell.isEditing) { - dataGridConfiguration.currentCell + await dataGridConfiguration.currentCell .onCellSubmit(dataGridConfiguration, isCellCancelEdit: true); } } @@ -1451,6 +1451,8 @@ class CurrentCellManager { _updateBorderForMultipleSelection(dataGridConfiguration, previousRowColumnIndex: previousRowColumnIndex, nextRowColumnIndex: rowColumnIndex); + } else if (dataGridConfiguration.navigationMode == GridNavigationMode.row) { + _updateCurrentRowColumnIndex(-1, -1); } return true; @@ -1482,11 +1484,6 @@ class CurrentCellManager { dataCellBase.updateColumn(); } } - - updateCurrentCellIndex( - dataGridConfiguration.controller, - grid_helper.resolveToRecordRowColumnIndex( - dataGridConfiguration, RowColumnIndex(rowIndex, columnIndex))); } void _removeCurrentCell(DataGridConfiguration dataGridConfiguration, @@ -1506,8 +1503,6 @@ class CurrentCellManager { } _updateCurrentRowColumnIndex(-1, -1); - updateCurrentCellIndex( - dataGridConfiguration.controller, RowColumnIndex(-1, -1)); } DataRowBase? _getDataRow( @@ -1804,8 +1799,8 @@ class CurrentCellManager { dataGridConfiguration, editingRowColumnIndex, editingDataCell); if (beginEdit) { - void submitCell() { - onCellSubmit(dataGridConfiguration); + Future submitCell() async { + await onCellSubmit(dataGridConfiguration); } final Widget? child = dataGridConfiguration.source.buildEditWidget( @@ -1823,7 +1818,7 @@ class CurrentCellManager { editingDataCell.editingWidget = FocusScope( canRequestFocus: true, node: _focusScopeNode, - onFocusChange: (bool details) { + onFocusChange: (bool details) async { /// We should not allow the focus to the other widgets /// when the cell is in the edit mode and return false from the canSubmitCell /// So, we need to request the focus here. @@ -1836,7 +1831,7 @@ class CurrentCellManager { // In this case, it is true. So we fixed it by checking the value of the `canCellSumbit` method. if (!_focusScopeNode.hasFocus && !dataGridConfiguration.dataGridFocusNode!.hasFocus && - !canSubmitCell(dataGridConfiguration)) { + !await canSubmitCell(dataGridConfiguration)) { _focusScopeNode.requestFocus(); } }, @@ -1877,10 +1872,10 @@ class CurrentCellManager { /// 1) _onCellSubmit is call from handleDataGridSource we no need to call the /// _notifyDataGridPropertyChangeListeners to refresh twice.By, set value false /// it will skip the refreshing. - void onCellSubmit(DataGridConfiguration dataGridConfiguration, + Future onCellSubmit(DataGridConfiguration dataGridConfiguration, {bool isCellCancelEdit = false, bool cancelCanSubmitCell = false, - bool canRefresh = true}) { + bool canRefresh = true}) async { if (!isEditing) { return; } @@ -1922,15 +1917,18 @@ class CurrentCellManager { /// moving to other cell or another row. so we need to skip the /// canCellSubmit method calling once again if (!cancelCanSubmitCell) { - canSubmitCell = dataGridConfiguration.source + canSubmitCell = await dataGridConfiguration.source .canSubmitCell(dataGridRow, rowColumnIndex, dataCell.gridColumn!); } else { canSubmitCell = true; } if (canSubmitCell) { resetEditing(); - dataGridConfiguration.source + await dataGridConfiguration.source .onCellSubmit(dataGridRow, rowColumnIndex, dataCell.gridColumn!); + + notifyDataGridPropertyChangeListeners(dataGridConfiguration.source, + rowColumnIndex: rowColumnIndex, propertyName: 'editing'); } } else { resetEditing(); @@ -1982,7 +1980,8 @@ class CurrentCellManager { } /// Called when the editing is submitted in the data cell. - bool canSubmitCell(DataGridConfiguration dataGridConfiguration) { + Future canSubmitCell( + DataGridConfiguration dataGridConfiguration) async { final DataRowBase? dataRow = _getEditingRow(dataGridConfiguration); if (dataRow == null) { diff --git a/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/sfdatagrid.dart b/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/sfdatagrid.dart index c6818d0bf..a3f0351f2 100644 --- a/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/sfdatagrid.dart +++ b/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/sfdatagrid.dart @@ -1848,6 +1848,21 @@ class SfDataGridState extends State _source = widget.source.._dataGridStateDetails = _dataGridStateDetails; _addDataGridSourceListeners(); } + + // Issue: + // FLUT-7337 - The filter is not functioning properly when changing the source at runtime + // + // Fix: + // The issue occurred because we did not update the source and columns before applying the filter. + // Instead, we updated those properties after applying the filter. + // To fix this issue, we need to reset the source and columns in the _dataGridConfiguration + // before applying the filter when the source is changed at runtime. + _dataGridConfiguration.source = _source!; + + if (_columns != widget.columns) { + _dataGridConfiguration.columns = widget.columns; + } + _source?._updateDataSource(); } @@ -1897,11 +1912,11 @@ class SfDataGridState extends State // FLUT-7231 - Editing of filtered data when paging is applied does not work properly // // Fix: - // After editing a filtered record, the collection of effective rows contained an empty record. - // This caused a range exception to be thrown when trying to retrieve a row by index from the collection. + // After editing a filtered record, the collection of effective rows contained an empty record + // or the record is not contained in the collection. // To fix this issue, we added a check to return immediately if the collection is empty - // or the datagrid row is not available in collection. - if (rowIndex == -1 || _source!.effectiveRows.isEmpty) { + // or the datagrid row is not available in collection i.e., rowIndex ==-1. + if (rowIndex == -1) { setState(() {}); return; } @@ -1921,7 +1936,6 @@ class SfDataGridState extends State // To fix this issue, we implemented a check to determine whether pagination is being used, // and we now retrieve the data grid row from the paginated effective rows // instead of the entire set of effective rows in the data grid - dataRow.dataGridRow = effectiveRows(_source!)[rowColumnIndex.rowIndex]; dataRow.dataGridRowAdapter = grid_helper.getDataGridRowAdapter( @@ -1985,67 +1999,66 @@ class SfDataGridState extends State } } - void _processUpdateDataSource() { + Future _processUpdateDataSource() async { if (_dataGridConfiguration.source._suspendDataPagerUpdate) { return; } - setState(() { - // Resets the editing before processing the `onCellSubmit`. - _processEditing(); + // Resets the editing before processing the `onCellSubmit`. + _processEditing(); - // Need to endEdit the editing [DataGridCell] before perform refreshing. - if (_dataGridConfiguration.currentCell.isEditing) { - _dataGridConfiguration.currentCell - .onCellSubmit(_dataGridConfiguration, canRefresh: false); - } - - _initializeDataGridDataSource(); - _dataGridConfiguration.source = _source!; - - if (!listEquals(_columns, widget.columns)) { - if (widget.selectionMode != SelectionMode.none && - widget.navigationMode == GridNavigationMode.cell && - _rowSelectionManager != null) { - selection_manager.onRowColumnChanged( - _dataGridConfiguration, -1, widget.columns.length); - } + // Need to endEdit the editing [DataGridCell] before perform refreshing. + if (_dataGridConfiguration.currentCell.isEditing) { + await _dataGridConfiguration.currentCell + .onCellSubmit(_dataGridConfiguration, canRefresh: false); + } - _resetColumn(); - } + _initializeDataGridDataSource(); + _dataGridConfiguration.source = _source!; - if (widget.selectionMode != SelectionMode.none) - selection_manager.removeUnWantedDataGridRows(_dataGridConfiguration); + if (!listEquals(_columns, widget.columns)) { if (widget.selectionMode != SelectionMode.none && widget.navigationMode == GridNavigationMode.cell && _rowSelectionManager != null) { selection_manager.onRowColumnChanged( - _dataGridConfiguration, widget.source._effectiveRows.length, -1); + _dataGridConfiguration, -1, widget.columns.length); } - if (widget.allowSwiping) { - _dataGridConfiguration.container.resetSwipeOffset(); - } + _resetColumn(); + } - if (widget.footer != null) { - final DataRowBase? footerRow = _rowGenerator.items.firstWhereOrNull( - (DataRowBase row) => - row.rowType == RowType.footerRow && row.rowIndex >= 0); - if (footerRow != null) { - // Need to reset the old footer row height in rowHeights collection. - _container.rowHeights[footerRow.rowIndex] = - _dataGridConfiguration.rowHeight; - } + if (widget.selectionMode != SelectionMode.none) + selection_manager.removeUnWantedDataGridRows(_dataGridConfiguration); + if (widget.selectionMode != SelectionMode.none && + widget.navigationMode == GridNavigationMode.cell && + _rowSelectionManager != null) { + selection_manager.onRowColumnChanged( + _dataGridConfiguration, widget.source._effectiveRows.length, -1); + } + + if (widget.allowSwiping) { + _dataGridConfiguration.container.resetSwipeOffset(); + } + + if (widget.footer != null) { + final DataRowBase? footerRow = _rowGenerator.items.firstWhereOrNull( + (DataRowBase row) => + row.rowType == RowType.footerRow && row.rowIndex >= 0); + if (footerRow != null) { + // Need to reset the old footer row height in rowHeights collection. + _container.rowHeights[footerRow.rowIndex] = + _dataGridConfiguration.rowHeight; } + } - _container - ..updateRowAndColumnCount() - ..refreshView() - ..isDirty = true; + _container + ..updateRowAndColumnCount() + ..refreshView() + ..isDirty = true; - // FLUT-3219 Need to refresh the scrolling offsets if the container's - // offsets and the ScrollController's offsets are not identical. - _refreshScrollOffsets(); - }); + // FLUT-3219 Need to refresh the scrolling offsets if the container's + // offsets and the ScrollController's offsets are not identical. + _refreshScrollOffsets(); + setState(() {}); if (_dataGridConfiguration.source.shouldRecalculateColumnWidths()) { resetAutoCalculation(_dataGridConfiguration.columnSizer); } @@ -2189,14 +2202,14 @@ class SfDataGridState extends State } } - void _handleDataGridPropertyChangeListeners( + Future _handleDataGridPropertyChangeListeners( {RowColumnIndex? rowColumnIndex, String? propertyName, - bool recalculateRowHeight = false}) { + bool recalculateRowHeight = false}) async { if (propertyName == 'refreshRow') { if (rowColumnIndex != null) { // Need to endEdit before refreshing the row. - _dataGridConfiguration.currentCell + await _dataGridConfiguration.currentCell .onCellSubmit(_dataGridConfiguration, canRefresh: false); final int rowIndex = grid_helper.resolveToRowIndex( _dataGridConfiguration, rowColumnIndex.rowIndex); @@ -2234,13 +2247,12 @@ class SfDataGridState extends State } if (propertyName == 'Swiping') { - setState(() { - // Need to end-edit the editing [DataGridCell] before swiping a - // [DataGridRow] or refreshing - _dataGridConfiguration.currentCell - .onCellSubmit(_dataGridConfiguration, canRefresh: false); - _container.isDirty = true; - }); + // Need to end-edit the editing [DataGridCell] before swiping a + // [DataGridRow] or refreshing + await _dataGridConfiguration.currentCell + .onCellSubmit(_dataGridConfiguration, canRefresh: false); + _container.isDirty = true; + setState(() {}); } if (propertyName == 'columnResizing') { @@ -2364,7 +2376,7 @@ class SfDataGridState extends State DataGridConfiguration _onDataGridStateDetailsChanged() => _dataGridConfiguration; - void _updateProperties(SfDataGrid oldWidget) { + Future _updateProperties(SfDataGrid oldWidget) async { final bool isSourceChanged = widget.source != oldWidget.source; final bool isDataSourceChanged = !listEquals(oldWidget.source.rows, widget.source.rows); @@ -2450,7 +2462,7 @@ class SfDataGridState extends State oldWidget.allowEditing != widget.allowEditing || oldWidget.editingGestureType != widget.editingGestureType; - void refreshEditing() { + Future refreshEditing() async { bool isEditingImpactAPIsChanged = isSourceChanged || isDataSourceChanged || oldWidget.stackedHeaderRows.length != widget.stackedHeaderRows.length; @@ -2473,7 +2485,7 @@ class SfDataGridState extends State isStackedHeaderRowsChanged; if (_dataGridConfiguration.currentCell.isEditing) { - _dataGridConfiguration.currentCell.onCellSubmit( + await _dataGridConfiguration.currentCell.onCellSubmit( _dataGridConfiguration, canRefresh: !isEditingImpactAPIsChanged); } @@ -2684,6 +2696,13 @@ class SfDataGridState extends State _refreshScrollOffsets(true); } + // Need to set `needToSetHorizontalOffset` property to when the column + // widths change in the RTL mode to get proper visible columns. + if (oldWidget.defaultColumnWidth != widget.defaultColumnWidth && + _dataGridConfiguration.textDirection == TextDirection.rtl) { + _container.needToSetHorizontalOffset = true; + } + _container.isDirty = true; } else { if (oldWidget.gridLinesVisibility != widget.gridLinesVisibility || @@ -2694,7 +2713,7 @@ class SfDataGridState extends State isEditingChanged) { // Need to endEdit before refreshing if (isEditingChanged && _dataGridConfiguration.currentCell.isEditing) { - _dataGridConfiguration.currentCell + await _dataGridConfiguration.currentCell .onCellSubmit(_dataGridConfiguration, canRefresh: false); } _initializeProperties(); @@ -2880,7 +2899,6 @@ class SfDataGridState extends State _dataGridConfiguration.isMacPlatform = themeData.platform == TargetPlatform.macOS; - // Sets column resizing hitTestPrecision based on the platform. if (_dataGridConfiguration.allowColumnsResizing) { @@ -3225,7 +3243,7 @@ abstract class DataGridSource extends DataGridSourceChangeNotifier /// [DataGridSource.compare] – To write the custom sorting for most of the use /// cases. @protected - void performSorting(List rows) { + Future performSorting(List rows) async { if (sortedColumns.isEmpty) { return; } @@ -3366,7 +3384,7 @@ abstract class DataGridSource extends DataGridSourceChangeNotifier } } - void _updateDataSource() { + Future _updateDataSource() async { if (sortedColumns.isNotEmpty) { _unSortedRows = rows.toList(); _effectiveRows = _unSortedRows; @@ -3424,8 +3442,8 @@ abstract class DataGridSource extends DataGridSourceChangeNotifier /// ); /// } /// ``` - void sort() { - _updateDataSource(); + Future sort() async { + await _updateDataSource(); _notifyDataGridPropertyChangeListeners(propertyName: 'Sorting'); } @@ -3720,8 +3738,8 @@ abstract class DataGridSource extends DataGridSourceChangeNotifier /// } ///``` /// This method will never be called when you return false from [onCellBeginEdit]. - void onCellSubmit(DataGridRow dataGridRow, RowColumnIndex rowColumnIndex, - GridColumn column) {} + Future onCellSubmit(DataGridRow dataGridRow, + RowColumnIndex rowColumnIndex, GridColumn column) async {} /// Called whenever the cell’s editing is completed i.e. prior to /// [onCellSubmit] method. @@ -3729,8 +3747,8 @@ abstract class DataGridSource extends DataGridSourceChangeNotifier /// If you want to restrict the cell from being end its editing, you can /// return false. Otherwise, return true. [onCellSubmit] will be called only /// if the [canSubmitCell] returns true. - bool canSubmitCell(DataGridRow dataGridRow, RowColumnIndex rowColumnIndex, - GridColumn column) { + Future canSubmitCell(DataGridRow dataGridRow, + RowColumnIndex rowColumnIndex, GridColumn column) async { return true; } @@ -3856,7 +3874,6 @@ class DataGridController extends DataGridSourceChangeNotifier { : _selectedRow = selectedRow, _selectedIndex = selectedIndex, _selectedRows = selectedRows.toList() { - _currentCell = RowColumnIndex(-1, -1); _horizontalOffset = 0.0; _verticalOffset = 0.0; } @@ -3955,8 +3972,18 @@ class DataGridController extends DataGridSourceChangeNotifier { /// /// This is used to identify the currently active cell to process the /// key navigation. - RowColumnIndex get currentCell => _currentCell; - RowColumnIndex _currentCell = RowColumnIndex.empty; + RowColumnIndex get currentCell { + final DataGridConfiguration dataGridConfiguration = + _dataGridStateDetails!(); + final CurrentCellManager currentCell = dataGridConfiguration.currentCell; + if (dataGridConfiguration.navigationMode == GridNavigationMode.row) { + return grid_helper.resolveToRecordRowColumnIndex( + dataGridConfiguration, RowColumnIndex(currentCell.rowIndex, -1)); + } else { + return grid_helper.resolveToRecordRowColumnIndex(dataGridConfiguration, + RowColumnIndex(currentCell.rowIndex, currentCell.columnIndex)); + } + } /// Moves the current-cell to the specified cell coordinates. void moveCurrentCellTo(RowColumnIndex rowColumnIndex) { @@ -4113,7 +4140,7 @@ class DataGridController extends DataGridSourceChangeNotifier { } /// Begins the edit to the given [RowColumnIndex] in [SfDataGrid]. - void beginEdit(RowColumnIndex rowColumnIndex) { + Future beginEdit(RowColumnIndex rowColumnIndex) async { if (_dataGridStateDetails != null) { final DataGridConfiguration dataGridConfiguration = _dataGridStateDetails!(); @@ -4122,14 +4149,22 @@ class DataGridController extends DataGridSourceChangeNotifier { dataGridConfiguration.navigationMode == GridNavigationMode.row) { return; } + if (isCurrentCellInEditing) { + if (!await dataGridConfiguration.currentCell + .canSubmitCell(dataGridConfiguration)) { + return; + } + await dataGridConfiguration.currentCell + .onCellSubmit(dataGridConfiguration, cancelCanSubmitCell: true); + } dataGridConfiguration.currentCell.onCellBeginEdit( editingRowColumnIndex: rowColumnIndex, isProgrammatic: true); } } /// Ends the current editing of a cell in [SfDataGrid]. - void endEdit() { + Future endEdit() async { if (_dataGridStateDetails != null) { final DataGridConfiguration dataGridConfiguration = _dataGridStateDetails!(); @@ -4139,7 +4174,8 @@ class DataGridController extends DataGridSourceChangeNotifier { return; } - dataGridConfiguration.currentCell.onCellSubmit(dataGridConfiguration); + await dataGridConfiguration.currentCell + .onCellSubmit(dataGridConfiguration); } } } @@ -4308,12 +4344,6 @@ void updateSelectedRow( controller._selectedRow = newSelectedRow; } -/// Updates the given index to the controller's `currentCell` property. -void updateCurrentCellIndex( - DataGridController controller, RowColumnIndex newCurrentCellIndex) { - controller._currentCell = newCurrentCellIndex; -} - /// Updates the given offset to the controller's `verticalOffset` property. void updateVerticalOffset(DataGridController controller, double offset) { controller._verticalOffset = offset; @@ -4391,9 +4421,22 @@ class DataGridThemeHelper { sortOrderNumberColor = dataGridThemeData.sortOrderNumberColor; sortOrderNumberBackgroundColor = dataGridThemeData.sortOrderNumberBackgroundColor; + filterPopupTextStyle = dataGridThemeData.filterPopupTextStyle ?? + TextStyle( + fontSize: 14.0, + color: colorScheme!.onSurface.withOpacity(0.89), + fontFamily: 'Roboto', + fontWeight: FontWeight.normal); + filterPopupDisabledTextStyle = + dataGridThemeData.filterPopupDisabledTextStyle ?? + TextStyle( + fontSize: 14.0, + color: colorScheme!.onSurface.withOpacity(0.38), + fontFamily: 'Roboto', + fontWeight: FontWeight.normal); } - ///To do + ///To Do late Brightness brightness; // ignore: public_member_api_docs @@ -4528,4 +4571,10 @@ class DataGridThemeHelper { /// Creates a copy of this theme but with the given fields replaced with the new values. late Color? sortOrderNumberBackgroundColor; + + /// The [TextStyle] of the options in filter popup menu except the items which are already selected. + late TextStyle? filterPopupTextStyle; + + /// The [TextStyle] of the disabled options in filter popup menu. + late TextStyle? filterPopupDisabledTextStyle; } diff --git a/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/widgets/cell_widget.dart b/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/widgets/cell_widget.dart index c2565b7d2..38168ab38 100644 --- a/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/widgets/cell_widget.dart +++ b/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/widgets/cell_widget.dart @@ -61,7 +61,8 @@ class _GridCellState extends State { dataGridConfiguration.editingGestureType == EditingGestureType.doubleTap); - void _handleOnTapDown(TapDownDetails details, bool isSecondaryTapDown) { + Future _handleOnTapDown( + TapDownDetails details, bool isSecondaryTapDown) async { _kind = details.kind!; final DataCellBase dataCell = widget.dataCell; final DataGridConfiguration dataGridConfiguration = dataGridStateDetails(); @@ -69,7 +70,8 @@ class _GridCellState extends State { // Clear editing when tap on the stacked header cell. if (widget.dataCell.cellType == CellType.stackedHeaderCell && dataGridConfiguration.currentCell.isEditing) { - dataGridConfiguration.currentCell.onCellSubmit(dataGridConfiguration); + await dataGridConfiguration.currentCell + .onCellSubmit(dataGridConfiguration); } if (_isDoubleTapEnabled(dataGridConfiguration)) { @@ -309,9 +311,11 @@ class _GridHeaderCellState extends State { } /// Helps to clear the editing cell when tap on header cells - void _clearEditing(DataGridConfiguration dataGridConfiguration) { + Future _clearEditing( + DataGridConfiguration dataGridConfiguration) async { if (dataGridConfiguration.currentCell.isEditing) { - dataGridConfiguration.currentCell.onCellSubmit(dataGridConfiguration); + await dataGridConfiguration.currentCell + .onCellSubmit(dataGridConfiguration); } } @@ -463,15 +467,88 @@ class _GridHeaderCellState extends State { column: gridColumn)); } - return Row(children: [ - Flexible( - child: Container(child: child), - ), - Container( - padding: dataGridConfiguration.columnSizer.iconsOuterPadding, - child: Center(child: Row(children: children)), - ) - ]); + if (gridColumn.sortIconPosition == ColumnHeaderIconPosition.end && + gridColumn.filterIconPosition == ColumnHeaderIconPosition.end || + (dataGridConfiguration.allowSorting && + !dataGridConfiguration.allowFiltering && + gridColumn.sortIconPosition == ColumnHeaderIconPosition.end) || + (!dataGridConfiguration.allowSorting && + dataGridConfiguration.allowFiltering && + gridColumn.filterIconPosition == + ColumnHeaderIconPosition.end)) { + return Row(children: [ + Flexible( + child: Container(child: child), + ), + Container( + padding: dataGridConfiguration.columnSizer.iconsOuterPadding, + child: Center(child: Row(children: children)), + ) + ]); + } else if (gridColumn.sortIconPosition == + ColumnHeaderIconPosition.start && + gridColumn.filterIconPosition == + ColumnHeaderIconPosition.start || + (dataGridConfiguration.allowSorting && + !dataGridConfiguration.allowFiltering && + gridColumn.sortIconPosition == + ColumnHeaderIconPosition.start) || + (!dataGridConfiguration.allowSorting && + dataGridConfiguration.allowFiltering && + gridColumn.filterIconPosition == + ColumnHeaderIconPosition.start)) { + return Row(children: [ + Container( + padding: dataGridConfiguration.columnSizer.iconsOuterPadding, + child: Center(child: Row(children: children)), + ), + Flexible( + child: Container(child: child), + ), + ]); + } else if (dataGridConfiguration.allowSorting && + dataGridConfiguration.allowFiltering) { + if (gridColumn.sortIconPosition == ColumnHeaderIconPosition.end && + gridColumn.filterIconPosition == ColumnHeaderIconPosition.start) { + return Row(children: [ + Container( + padding: dataGridConfiguration.columnSizer.iconsOuterPadding, + child: Center(child: children[_sortNumber == -1 ? 1 : 2]), + ), + Flexible( + child: Container(child: child), + ), + Container( + padding: dataGridConfiguration.columnSizer.iconsOuterPadding, + child: Center(child: children[0]), + ), + if (_sortNumber != -1) + Container( + padding: dataGridConfiguration.columnSizer.iconsOuterPadding, + child: children[1], + ), + ]); + } else { + return Row(children: [ + Container( + padding: dataGridConfiguration.columnSizer.iconsOuterPadding, + child: children[0]), + if (_sortNumber != -1) + Container( + child: children[1], + ), + Flexible( + child: Container( + child: child, + ), + ), + Container( + padding: dataGridConfiguration.columnSizer.iconsOuterPadding, + child: Center(child: children[_sortNumber == -1 ? 1 : 2]), + ) + ]); + } + } } } return child; @@ -500,12 +577,12 @@ class _GridHeaderCellState extends State { } } - void _makeSort(DataCellBase dataCell) { + Future _makeSort(DataCellBase dataCell) async { final DataGridConfiguration dataGridConfiguration = dataGridStateDetails(); //End-edit before perform sorting if (dataGridConfiguration.currentCell.isEditing) { - dataGridConfiguration.currentCell + await dataGridConfiguration.currentCell .onCellSubmit(dataGridConfiguration, canRefresh: false); } @@ -876,7 +953,6 @@ class _FilterPopupState extends State<_FilterPopup> { late bool isAdvancedFilter; late DataGridFilterHelper filterHelper; - @override void initState() { super.initState(); @@ -1030,7 +1106,6 @@ class _FilterPopupState extends State<_FilterPopup> { widget.column.filterPopupMenuOptions!.canShowClearFilterOption; showColumnName = widget.column.filterPopupMenuOptions!.showColumnName; } - Widget buildPopup({Size? viewSize}) { return SingleChildScrollView( key: const ValueKey('datagrid_filtering_scrollView'), @@ -1046,19 +1121,27 @@ class _FilterPopupState extends State<_FilterPopup> { : filterHelper.disableTextStyle, height: filterHelper.tileHeight, prefix: Icon( - const IconData(0xe700, - fontFamily: 'FilterIcon', - fontPackage: 'syncfusion_flutter_datagrid'), - color: isSortAscendingEnabled - ? iconColor - : filterHelper.disableIconColor), - prefixPadding: - const EdgeInsets.only(left: 4.0, right: 14.0), + const IconData(0xe700, + fontFamily: 'FilterIcon', + fontPackage: 'syncfusion_flutter_datagrid'), + color: isSortAscendingEnabled + ? iconColor + : filterHelper.disableIconColor, + size: filterHelper.textStyle.fontSize! + 10, + ), + prefixPadding: EdgeInsets.only( + left: 4.0, + right: filterHelper.textStyle.fontSize!, + bottom: filterHelper.textStyle.fontSize! > 14 + ? filterHelper.textStyle.fontSize! - 14 + : 0), onTap: isSortAscendingEnabled ? onHandleSortAscendingTap : null, - child: Text(grid_helper.getSortButtonText( - localizations, true, filterType))), + child: Text( + grid_helper.getSortButtonText( + localizations, true, filterType), + overflow: TextOverflow.ellipsis)), if (canShowSortingOptions) _FilterPopupMenuTile( style: isSortDescendingEnabled @@ -1066,18 +1149,31 @@ class _FilterPopupState extends State<_FilterPopup> { : filterHelper.disableTextStyle, height: filterHelper.tileHeight, prefix: Icon( - const IconData(0xe701, - fontFamily: 'FilterIcon', - fontPackage: 'syncfusion_flutter_datagrid'), - color: isSortDescendingEnabled - ? iconColor - : filterHelper.disableIconColor), - prefixPadding: const EdgeInsets.only(left: 4.0, right: 14.0), + const IconData(0xe701, + fontFamily: 'FilterIcon', + fontPackage: 'syncfusion_flutter_datagrid'), + color: isSortDescendingEnabled + ? iconColor + : filterHelper.disableIconColor, + size: filterHelper.textStyle.fontSize! + 10, + ), + prefixPadding: EdgeInsets.only( + left: 4.0, + right: filterHelper.textStyle.fontSize!, + bottom: filterHelper.textStyle.fontSize! > 14 + ? filterHelper.textStyle.fontSize! - 14 + : 0), onTap: isSortDescendingEnabled ? onHandleSortDescendingTap : null, - child: Text(grid_helper.getSortButtonText( - localizations, false, filterType)), + child: Text( + grid_helper.getSortButtonText( + localizations, + false, + filterType, + ), + overflow: TextOverflow.ellipsis, + ), ), if (canShowSortingOptions) const Divider(indent: 8.0, endIndent: 8.0), @@ -1091,11 +1187,16 @@ class _FilterPopupState extends State<_FilterPopup> { const IconData(0xe703, fontFamily: 'FilterIcon', fontPackage: 'syncfusion_flutter_datagrid'), - size: 22.0, + size: filterHelper.textStyle.fontSize! + 8, color: isClearFilterEnabled ? iconColor : filterHelper.disableIconColor), - prefixPadding: const EdgeInsets.only(left: 4.0, right: 14.0), + prefixPadding: EdgeInsets.only( + left: 4.0, + right: filterHelper.textStyle.fontSize!, + bottom: filterHelper.textStyle.fontSize! > 14 + ? filterHelper.textStyle.fontSize! - 14 + : 0), onTap: isClearFilterEnabled ? onHandleClearFilterTap : null, child: Text(getClearFilterText(localizations, showColumnName), overflow: TextOverflow.ellipsis), @@ -1120,17 +1221,24 @@ class _FilterPopupState extends State<_FilterPopup> { : const IconData(0xe702, fontFamily: 'FilterIcon', fontPackage: 'syncfusion_flutter_datagrid'), - size: 20.0, + size: filterHelper.textStyle.fontSize! + 6, color: iconColor), suffix: Icon( isAdvancedFilter ? Icons.keyboard_arrow_down : Icons.keyboard_arrow_right, - size: 20.0, + size: filterHelper.textStyle.fontSize! + 6, color: iconColor), - prefixPadding: const EdgeInsets.only(left: 4.0, right: 14.0), + prefixPadding: EdgeInsets.only( + left: 4.0, + right: filterHelper.textStyle.fontSize!, + bottom: filterHelper.textStyle.fontSize! > 14 + ? filterHelper.textStyle.fontSize! - 14 + : 0), child: Text( - grid_helper.getFilterTileText(localizations, filterType)), + grid_helper.getFilterTileText(localizations, filterType), + overflow: TextOverflow.ellipsis, + ), ), if (isCheckboxFilterEnabled || isBothFilterEnabled) Visibility( @@ -1156,24 +1264,30 @@ class _FilterPopupState extends State<_FilterPopup> { children: [ SizedBox( width: 120.0, - height: 32.0, + height: filterHelper.tileHeight - 8, child: ElevatedButton( onPressed: canDisableOkButton() ? null : onHandleOkButtonTap, child: Text(localizations.okDataGridFilteringLabel, - style: - const TextStyle(color: Color(0xFFFFFFFF)))), + style: TextStyle( + color: const Color(0xFFFFFFFF), + fontSize: filterHelper.textStyle.fontSize, + fontFamily: + filterHelper.textStyle.fontFamily))), ), SizedBox( width: 120.0, - height: 32.0, + height: filterHelper.tileHeight - 8, child: OutlinedButton( onPressed: closePage, child: Text( localizations.cancelDataGridFilteringLabel, - style: - TextStyle(color: filterHelper.primaryColor), + style: TextStyle( + color: filterHelper.primaryColor, + fontSize: filterHelper.textStyle.fontSize, + fontFamily: + filterHelper.textStyle.fontFamily), )), ), ], @@ -1308,7 +1422,7 @@ class _FilterPopupMenuTile extends StatelessWidget { @override Widget build(BuildContext context) { return SizedBox( - height: height ?? 40.0, + height: height, child: MaterialButton( onPressed: onTap, child: Row( @@ -1438,7 +1552,8 @@ class _CheckboxFilterMenu extends StatelessWidget { // listview in the mobile platform. final double checkboxHeight = isMobile ? max(viewSize!.height - occupiedHeight, 120.0) : 200.0; - final double selectAllButtonHeight = isMobile ? 48.0 : 40.0; + final double selectAllButtonHeight = + isMobile ? helper.tileHeight - 4 : helper.tileHeight; return Padding( padding: const EdgeInsets.only(left: 4.0), @@ -1458,35 +1573,36 @@ class _CheckboxFilterMenu extends StatelessWidget { .withOpacity(0.6)), fillColor: MaterialStateProperty.resolveWith( (_) => helper.primaryColor)), - child: Column( - children: [ - _FilterPopupMenuTile( - style: helper.textStyle, - height: selectAllButtonHeight, - prefixPadding: const EdgeInsets.only(left: 4.0, right: 10.0), - prefix: Checkbox( - focusNode: checkboxFocusNode, - tristate: filterHelper.isSelectAllInTriState, - value: filterHelper.isSelectAllChecked, - onChanged: (_) => onHandleSelectAllCheckboxTap(), - ), - onTap: onHandleSelectAllCheckboxTap, - child: Text(dataGridConfiguration - .localizations.selectAllDataGridFilteringLabel), + child: Column(children: [ + _FilterPopupMenuTile( + style: helper.textStyle, + height: selectAllButtonHeight, + prefixPadding: const EdgeInsets.only(left: 4.0, right: 10.0), + prefix: Checkbox( + focusNode: checkboxFocusNode, + tristate: filterHelper.isSelectAllInTriState, + value: filterHelper.isSelectAllChecked, + onChanged: (_) => onHandleSelectAllCheckboxTap(), ), - SizedBox( - height: checkboxHeight, - child: ListView.builder( - key: const ValueKey( - 'datagrid_filtering_checkbox_listView'), - prototypeItem: buildCheckboxTile( - filterHelper.items.length - 1, helper.textStyle), - itemCount: filterHelper.items.length, - itemBuilder: (BuildContext context, int index) => - buildCheckboxTile(index, helper.textStyle)!), + onTap: onHandleSelectAllCheckboxTap, + child: Text( + dataGridConfiguration + .localizations.selectAllDataGridFilteringLabel, + overflow: TextOverflow.ellipsis, ), - ], - ), + ), + SizedBox( + height: checkboxHeight, + child: ListView.builder( + key: const ValueKey( + 'datagrid_filtering_checkbox_listView'), + prototypeItem: buildCheckboxTile( + filterHelper.items.length - 1, helper.textStyle), + itemCount: filterHelper.items.length, + itemBuilder: (BuildContext context, int index) => + buildCheckboxTile(index, helper.textStyle)), + ), + ]), ), ), ); @@ -1508,8 +1624,9 @@ class _CheckboxFilterMenu extends StatelessWidget { return Padding( padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), child: SizedBox( - height: isMobile ? 52.0 : 36.0, + height: isMobile ? helper.tileHeight : helper.tileHeight - 4, child: TextField( + style: helper.textStyle, key: const ValueKey('datagrid_filtering_search_textfield'), focusNode: filterHelper.searchboxFocusNode, controller: filterHelper.textController, @@ -1523,7 +1640,7 @@ class _CheckboxFilterMenu extends StatelessWidget { replacement: IconButton( key: const ValueKey( 'datagrid_filtering_clearSearch_icon'), - iconSize: 22.0, + iconSize: helper.textStyle.fontSize! + 8, padding: EdgeInsets.zero, constraints: const BoxConstraints.tightFor( width: 22.0, height: 22.0), @@ -1532,13 +1649,14 @@ class _CheckboxFilterMenu extends StatelessWidget { onHandleSearchTextFieldChanged(''); }, icon: Icon(Icons.close, color: helper.iconColor)), - child: - Icon(Icons.search, size: 22.0, color: helper.iconColor)), + child: Icon(Icons.search, + size: helper.textStyle.fontSize! + 8, + color: helper.iconColor)), contentPadding: isMobile ? const EdgeInsets.all(16.0) : const EdgeInsets.all(8.0), border: const OutlineInputBorder(), - hintStyle: const TextStyle(fontSize: 14.0), + hintStyle: helper.textStyle, hintText: dataGridConfiguration .localizations.searchDataGridFilteringLabel), ), @@ -1553,7 +1671,7 @@ class _CheckboxFilterMenu extends StatelessWidget { .getDisplayValue(element.value); return _FilterPopupMenuTile( style: style, - height: isMobile ? 48.0 : 40.0, + height: isMobile ? style.fontSize! + 34 : style.fontSize! + 26, prefixPadding: const EdgeInsets.only(left: 4.0, right: 10.0), prefix: Checkbox( focusNode: checkboxFocusNode, @@ -1621,32 +1739,37 @@ class _AdvancedFilterPopupMenu extends StatelessWidget { child: Column( children: [ _FilterMenuDropdown( - height: 16.0, + height: helper.textStyle.fontSize! + 2, padding: EdgeInsets.only(top: advanceFilterTopPadding, bottom: 8.0), child: Text( - '${dataGridConfiguration.localizations.showRowsWhereDataGridFilteringLabel}:', - style: TextStyle( - color: helper.textColor, fontWeight: FontWeight.bold)), + '${dataGridConfiguration.localizations.showRowsWhereDataGridFilteringLabel}:', + style: TextStyle( + fontFamily: helper.textStyle.fontFamily, + fontSize: helper.textStyle.fontSize, + color: helper.textStyle.color, + fontWeight: FontWeight.bold), + overflow: TextOverflow.ellipsis, + ), ), _FilterMenuDropdown( - height: isMobile ? 56.0 : 36.0, + height: isMobile ? helper.tileHeight + 4 : helper.tileHeight - 4, padding: const EdgeInsets.only(top: 8.0), child: _buildFilterTypeDropdown(isFirstButton: true), ), _FilterMenuDropdown( - height: isMobile ? 56.0 : 36.0, + height: isMobile ? helper.tileHeight + 4 : helper.tileHeight - 4, padding: const EdgeInsets.only(top: 8.0, bottom: 16.0), suffix: _getTrailingWidget(context, true), child: _buildFilterValueDropdown(isTopButton: true), ), _buildRadioButtons(), _FilterMenuDropdown( - height: isMobile ? 56.0 : 36.0, + height: isMobile ? helper.tileHeight + 4 : helper.tileHeight - 4, padding: const EdgeInsets.only(top: 16.0, bottom: 8.0), child: _buildFilterTypeDropdown(isFirstButton: false), ), _FilterMenuDropdown( - height: isMobile ? 56.0 : 36.0, + height: isMobile ? helper.tileHeight + 4 : helper.tileHeight - 4, padding: const EdgeInsets.only(bottom: 8.0), suffix: _getTrailingWidget(context, false), child: _buildFilterValueDropdown(isTopButton: false), @@ -1680,7 +1803,10 @@ class _AdvancedFilterPopupMenu extends StatelessWidget { groupValue: filterHelper.isOrPredicate), ), const SizedBox(width: 8.0), - Text(localizations.andDataGridFilteringLabel), + Text( + localizations.andDataGridFilteringLabel, + style: helper.textStyle, + ), ]), const SizedBox(width: 16.0), Row(children: [ @@ -1694,7 +1820,10 @@ class _AdvancedFilterPopupMenu extends StatelessWidget { groupValue: filterHelper.isOrPredicate), ), const SizedBox(width: 8.0), - Text(localizations.orDataGridFilteringLabel), + Text( + localizations.orDataGridFilteringLabel, + style: helper.textStyle, + ), ]), ], ); @@ -1744,18 +1873,19 @@ class _AdvancedFilterPopupMenu extends StatelessWidget { 'datagrid_filtering_filterValue_second_button'), decoration: InputDecoration( enabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: helper.borderColor)), + borderSide: BorderSide(color: helper.borderColor), + ), contentPadding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 12.0), border: OutlineInputBorder( borderSide: BorderSide(color: helper.borderColor))), icon: Icon(Icons.keyboard_arrow_down, - size: 22.0, color: helper.iconColor), + size: helper.textStyle.fontSize! + 8, color: helper.iconColor), isExpanded: true, value: isTopButton ? filterHelper.filterValue1 : filterHelper.filterValue2, - style: TextStyle(fontSize: 14.0, color: helper.textColor), + style: helper.textStyle, items: filterHelper.items .map>((FilterElement value) => DropdownMenuItem( @@ -1769,6 +1899,7 @@ class _AdvancedFilterPopupMenu extends StatelessWidget { Widget buildTextField() { return TextField( + style: helper.textStyle, key: isTopButton ? const ValueKey( 'datagrid_filtering_filterValue_first_button') @@ -1864,10 +1995,11 @@ class _AdvancedFilterPopupMenu extends StatelessWidget { border: OutlineInputBorder( borderSide: BorderSide(color: helper.borderColor))), icon: Icon(Icons.keyboard_arrow_down, - size: 22.0, color: helper.iconColor), + size: helper.textStyle.fontSize! + 8, color: helper.iconColor), + isExpanded: true, value: isFirstButton ? filterHelper.filterType1 : filterHelper.filterType2, - style: TextStyle(fontSize: 14.0, color: helper.textColor), + style: helper.textStyle, items: filterHelper.filterTypeItems .map>((String value) => DropdownMenuItem(value: value, child: Text(value))) @@ -2301,18 +2433,18 @@ Widget _wrapInsideCellContainer( // Gesture Events -void _handleOnTapUp( +Future _handleOnTapUp( {required TapUpDetails? tapUpDetails, required TapDownDetails? tapDownDetails, required DataCellBase dataCell, required DataGridConfiguration dataGridConfiguration, required PointerDeviceKind kind, - bool isSecondaryTapDown = false}) { + bool isSecondaryTapDown = false}) async { // End edit the current editing cell if its editing mode is differed if (dataGridConfiguration.currentCell.isEditing) { - if (dataGridConfiguration.currentCell + if (await dataGridConfiguration.currentCell .canSubmitCell(dataGridConfiguration)) { - dataGridConfiguration.currentCell + await dataGridConfiguration.currentCell .onCellSubmit(dataGridConfiguration, cancelCanSubmitCell: true); } else { return; @@ -2343,14 +2475,14 @@ void _handleOnTapUp( } } -void _handleOnDoubleTap( +Future _handleOnDoubleTap( {required DataCellBase dataCell, - required DataGridConfiguration dataGridConfiguration}) { + required DataGridConfiguration dataGridConfiguration}) async { // End edit the current editing cell if its editing mode is differed if (dataGridConfiguration.currentCell.isEditing) { - if (dataGridConfiguration.currentCell + if (await dataGridConfiguration.currentCell .canSubmitCell(dataGridConfiguration)) { - dataGridConfiguration.currentCell + await dataGridConfiguration.currentCell .onCellSubmit(dataGridConfiguration, cancelCanSubmitCell: true); } else { return; @@ -2375,16 +2507,16 @@ void _handleOnDoubleTap( } } -void _handleOnSecondaryTapUp( +Future _handleOnSecondaryTapUp( {required TapUpDetails tapUpDetails, required DataCellBase dataCell, required DataGridConfiguration dataGridConfiguration, - required PointerDeviceKind kind}) { + required PointerDeviceKind kind}) async { // Need to end the editing cell when interacting with other tap gesture if (dataGridConfiguration.currentCell.isEditing) { - if (dataGridConfiguration.currentCell + if (await dataGridConfiguration.currentCell .canSubmitCell(dataGridConfiguration)) { - dataGridConfiguration.currentCell + await dataGridConfiguration.currentCell .onCellSubmit(dataGridConfiguration, cancelCanSubmitCell: true); } else { return; diff --git a/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/widgets/rendering_widget.dart b/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/widgets/rendering_widget.dart index d3a7c041d..1d6358683 100644 --- a/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/widgets/rendering_widget.dart +++ b/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/widgets/rendering_widget.dart @@ -1340,7 +1340,7 @@ class RenderVirtualizingCellsWidget extends RenderBox } // To handle long press end event. - void _onLongPressEnd(LongPressEndDetails details) { + Future _onLongPressEnd(LongPressEndDetails details) async { final DataGridConfiguration dataGridConfiguration = _dataGridStateDetails(); DataCellBase? dataCell; dataCell = _getDataCellBase(dataRow, details); @@ -1351,9 +1351,9 @@ class RenderVirtualizingCellsWidget extends RenderBox dataGridConfiguration.currentCell.onCellSubmit(dataGridConfiguration); } else if (dataCell.cellType == CellType.gridCell) { // Clear editing when tap on the grid cell - if (dataGridConfiguration.currentCell + if (await dataGridConfiguration.currentCell .canSubmitCell(dataGridConfiguration)) { - dataGridConfiguration.currentCell + await dataGridConfiguration.currentCell .onCellSubmit(dataGridConfiguration, cancelCanSubmitCell: true); } } diff --git a/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/widgets/scrollview_widget.dart b/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/widgets/scrollview_widget.dart index 04b359ab1..afb4efdb0 100644 --- a/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/widgets/scrollview_widget.dart +++ b/packages/syncfusion_flutter_datagrid/lib/src/datagrid_widget/widgets/scrollview_widget.dart @@ -362,24 +362,21 @@ class _ScrollViewWidgetState extends State { _horizontalController!.offset <= 0.0 || _horizontalController!.position.maxScrollExtent <= 0.0 || _container.extentWidth <= _width) { + // When the shrinkWrapColumns property is set to true, the extent width + // and view width of the container are the same. So, the scroll controller's + // offset has considered to arrange the header row in RTL mode. + if (_container.extentWidth <= _width && + _horizontalController!.hasClients && + dataGridConfiguration.shrinkWrapColumns) { + return -_horizontalController!.offset; + } return 0.0; - } else if (_horizontalController!.position.maxScrollExtent == - _horizontalController!.offset) { - return -_horizontalController!.position.maxScrollExtent; - } - - late double maxScrollExtent; - if (dataGridConfiguration - .columnResizeController.isResizeIndicatorVisible) { - // In RTL, Resolves the glitching issue of header rows while resizing - // the column by calculating the maxScrollExtent manually. - maxScrollExtent = _container.extentWidth - - _horizontalController!.position.viewportDimension; } else { - maxScrollExtent = _horizontalController!.position.maxScrollExtent; + // When the scroll view's content dimension change at runtime, the scroll view's + // maxScrollExtent will not be changed. So calculates the maximum scroll extent manually. + final double maxScrollExtent = _container.extentWidth - _width; + return -(maxScrollExtent - _container.horizontalOffset); } - - return -(maxScrollExtent - _container.horizontalOffset); } } @@ -1019,7 +1016,7 @@ class _ScrollViewWidgetState extends State { _updateColumnSizer(); } _container - ..setRowHeights() + ..setRowHeights(initialLoading: true) ..needToRefreshColumn = true; // FLUT-6545 if shrinkWrapRows is ture, we need to the set the DataGrid maximum height @@ -1627,7 +1624,7 @@ class VisualContainerHelper { } /// Sets the row height of all the data grid rows. - void setRowHeights() { + void setRowHeights({bool initialLoading = false}) { final DataGridConfiguration dataGridConfiguration = dataGridStateDetails(); if (dataGridConfiguration.onQueryRowHeight == null) { return; @@ -1675,7 +1672,15 @@ class VisualContainerHelper { final LineSizeCollection lineSizeCollection = rowHeights as LineSizeCollection; - lineSizeCollection.suspendUpdates(); + + // We only need to suspend updates when shrinkWrap is set to true + // to recompute the size of all rows at initial loading, + // instead of suspending updates every time while scrolling. + if (dataGridConfiguration.shrinkWrapRows && + dataGridConfiguration.onQueryRowHeight != null && + initialLoading) { + lineSizeCollection.suspendUpdates(); + } for (int index = bodyStartLineIndex; ((current <= bodyEnd || (current <= dataGridConfiguration.viewHeight)) || @@ -1716,7 +1721,15 @@ class VisualContainerHelper { rowHeightManager.dirtyRows.clear(); } - lineSizeCollection.resumeUpdates(); + // We need to resume updates here, as we previously suspended them + // when the shrinkWrapRows property was enabled during the initial loading + // to determine the row height for all rows + if (dataGridConfiguration.shrinkWrapRows && + dataGridConfiguration.onQueryRowHeight != null && + initialLoading) { + initialLoading = false; + lineSizeCollection.resumeUpdates(); + } scrollRows.updateScrollBar(false); } diff --git a/packages/syncfusion_flutter_datagrid/lib/src/datapager/sfdatapager.dart b/packages/syncfusion_flutter_datagrid/lib/src/datapager/sfdatapager.dart index bfdd3e72e..fd7af183b 100644 --- a/packages/syncfusion_flutter_datagrid/lib/src/datapager/sfdatapager.dart +++ b/packages/syncfusion_flutter_datagrid/lib/src/datapager/sfdatapager.dart @@ -588,8 +588,10 @@ class SfDataPagerState extends State { } _suspendDataPagerUpdate = true; + // When the index is greater than the page count, + // it is necessary to set the index to the page count index. if (index > widget.pageCount - 1) { - index -= 1; + index = (widget.pageCount - 1).toInt(); } final bool canChange = await _canChangePage(index); diff --git a/packages/syncfusion_flutter_datagrid/lib/src/grid_common/tree_table.dart b/packages/syncfusion_flutter_datagrid/lib/src/grid_common/tree_table.dart index 476bdb0ae..7c15a4254 100644 --- a/packages/syncfusion_flutter_datagrid/lib/src/grid_common/tree_table.dart +++ b/packages/syncfusion_flutter_datagrid/lib/src/grid_common/tree_table.dart @@ -565,7 +565,6 @@ class TreeTableEntry extends TreeTableNode with TreeTableEntryBase { /// * tree - _required_ - Tree table instance /// /// Returns the instance of newly created branch - @override TreeTableBranchBase? createBranch(TreeTable tree) => TreeTableBranch(tree); @@ -642,7 +641,6 @@ class TreeTableBase extends ListBase { bool _sorted = false; /// Gets the root node. - /// Gets a value indicating whether the tree was initialize or not. bool get isInitializing => _isInitializing; late bool _isInitializing; @@ -1064,19 +1062,22 @@ class TreeTable extends TreeTableBase { } /// - void deleteFixup(TreeTableBranchBase? x, bool isLeft) { + void deleteFixup(TreeTableBranchBase? x) { const bool inAddMode = false; while (x != null && + x.parent != null && !referenceEquals(x, _root) && x._color == TreeTableNodeColor.black) { - if (isLeft) { + if (referenceEquals(x.parent!.left, x)) { TreeTableNodeBase? w = x.parent?.right; if (w != null && w.color == TreeTableNodeColor.red) { w.color = TreeTableNodeColor.black; - x.parent?.color = TreeTableNodeColor.black; + x.parent?.color = TreeTableNodeColor.red; leftRotate(x.parent, inAddMode); if (x.parent != null) { - w = x.parent!.right! as TreeTableBranchBase; + w = x.parent!.right; + } else { + return; } } @@ -1085,44 +1086,39 @@ class TreeTable extends TreeTableBase { } if (w is TreeTableBranchBase && - w.color == TreeTableNodeColor.black && (w.left!.isEntry() || w.getLeftBranch()!.color == TreeTableNodeColor.black) && (w.right!.isEntry() || w.getRightBranch()!.color == TreeTableNodeColor.black)) { w.color = TreeTableNodeColor.red; - if (x.color == TreeTableNodeColor.red) { - x.color = TreeTableNodeColor.black; + x = x.parent; + } else { + if (w is TreeTableBranchBase && + (w.right!.isEntry() || + w.getRightBranch()!.color == TreeTableNodeColor.black)) { + w.left?.color = TreeTableNodeColor.black; + w.color = TreeTableNodeColor.red; + rightRotate(w, inAddMode); + if (x.parent != null) { + w = x.parent!.right; + } else { + break; + } + } + if (w == null) { return; - } else { - isLeft = x.parent!.left == x; - x = x.parent; } - } else if (w is TreeTableBranchBase && - w.color == TreeTableNodeColor.black && - !w.right!.isEntry() && - w.getRightBranch()!.color == TreeTableNodeColor.red) { - leftRotate(x.parent, inAddMode); - w.color = x.parent!.color; - x.parent!.color = w.color; - return; - } else if (w is TreeTableBranchBase && - w.color == TreeTableNodeColor.black && - !w.left!.isEntry() && - w.getLeftBranch()!.color == TreeTableNodeColor.red && - (w.right!.isEntry() || - w.getRightBranch()!.color == TreeTableNodeColor.black)) { - rightRotate(w, inAddMode); - - w.parent!.color = TreeTableNodeColor.black; - w.color = TreeTableNodeColor.red; - + if (w is TreeTableBranchBase) { + w.color = x.parent!.color; + w.right?.color = TreeTableNodeColor.black; + } + x.parent?.color = TreeTableNodeColor.black; leftRotate(x.parent, inAddMode); - w.color = x.parent!.color; - x.parent!.color = w.color; - return; - } else { - return; + if (_root is TreeTableBranchBase && _root != null) { + x = _root! as TreeTableBranchBase; + } else { + return; + } } } else { TreeTableNodeBase? w = x.parent?.left; @@ -1130,7 +1126,11 @@ class TreeTable extends TreeTableBase { w.color = TreeTableNodeColor.black; x.parent!.color = TreeTableNodeColor.red; rightRotate(x.parent, inAddMode); - w = x.parent!.left; + if (x.parent != null) { + w = x.parent!.left; + } else { + return; + } } if (w == null) { @@ -1138,53 +1138,44 @@ class TreeTable extends TreeTableBase { } if (w is TreeTableBranchBase && - w.color == TreeTableNodeColor.black && - (w.left!.isEntry() || - w.getLeftBranch()!.color == TreeTableNodeColor.black) && (w.right!.isEntry() || - w.getRightBranch()!.color == TreeTableNodeColor.black)) { + w.getRightBranch()!.color == TreeTableNodeColor.black) && + (w.left!.isEntry() || + w.getLeftBranch()!.color == TreeTableNodeColor.black)) { w.color = TreeTableNodeColor.red; - if (x.color == TreeTableNodeColor.red) { - x.color = TreeTableNodeColor.black; - return; - } else if (x.parent != null) { - isLeft = x.parent!.left == x; - x = x.parent; - } + x = x.parent; } else { if (w is TreeTableBranchBase && - w.color == TreeTableNodeColor.black && - !w.right!.isEntry() && - w.getRightBranch()!.color == TreeTableNodeColor.red) { - final TreeTableBranchBase xParent = x.parent!; - leftRotate(xParent, inAddMode); - final TreeTableNodeColor t = w.color!; - w.color = xParent.color; - xParent.color = t; - return; - } else if (w is TreeTableBranchBase && - w.color == TreeTableNodeColor.black && - !w.left!.isEntry() && - w.getLeftBranch()!.color == TreeTableNodeColor.red && - (w.right!.isEntry() || - w.getRightBranch()!.color == TreeTableNodeColor.black)) { - final TreeTableBranchBase wParent = w.parent!; - final TreeTableBranchBase xParent = x.parent!; - rightRotate(w, inAddMode); - - wParent.color = TreeTableNodeColor.black; + (w.left!.isEntry() || + w.getLeftBranch()!.color == TreeTableNodeColor.black)) { + w.right?.color = TreeTableNodeColor.black; w.color = TreeTableNodeColor.red; - - leftRotate(x.parent, inAddMode); - w.color = xParent.color; - xParent.color = w.color; + leftRotate(w, inAddMode); + if (x.parent != null) { + w = x.parent!.left; + } else { + break; + } + } + if (w == null) { + return; + } + if (w is TreeTableBranchBase) { + w.color = x.parent!.color; + w.left?.color = TreeTableNodeColor.black; + } + x.parent?.color = TreeTableNodeColor.black; + rightRotate(x.parent, inAddMode); + if (_root is TreeTableBranchBase && _root != null) { + x = _root! as TreeTableBranchBase; + } else { return; } } } } - x!.color = TreeTableNodeColor.black; + x?.color = TreeTableNodeColor.black; } /// Finds the node in a sorted tree is just one entry ahead of the @@ -1399,6 +1390,15 @@ class TreeTable extends TreeTableBase { /// void insertFixup(TreeTableBranchBase? x, bool inAddMode) { + // We set the color of all newly inserted nodes to red, + // except for the root node which is always black + if (x != null) { + if (x.parent == null) { + x.color = TreeTableNodeColor.black; + } else { + x.color = TreeTableNodeColor.red; + } + } // Check Red-Black properties while (x != null && x.parent != null && @@ -1406,46 +1406,89 @@ class TreeTable extends TreeTableBase { x.parent!.color == TreeTableNodeColor.red && x.parent!.parent != null) { // We have a violation - if (x.parent == x.parent!.parent!.left) { - final TreeTableNodeBase? y = x.parent!.parent?.right; - if (y != null && y.color == TreeTableNodeColor.red) { - // uncle is red + if (referenceEquals(x.parent, x.parent!.parent!.left)) { + // Uncle of x + final TreeTableNodeBase? uncle = x.parent!.parent!.right; + if (uncle != null && uncle.color == TreeTableNodeColor.red) { + // If uncle is red, we make the parent and uncle black, and the grandparent red, + // since all newly inserted nodes should be colored red except the root node which is always black. x.parent!.color = TreeTableNodeColor.black; - y.color = TreeTableNodeColor.black; - x.parent!.parent?.color = TreeTableNodeColor.red; + uncle.color = TreeTableNodeColor.black; + x.parent!.parent!.color = TreeTableNodeColor.red; x = x.parent!.parent; } else { // uncle is black - if (x == x.parent!.right) { - // Make x a left child - x = x.parent; - leftRotate(x, inAddMode); + if (referenceEquals(x, x.parent!.right)) { + // If the node is on the right side of the parent, + // we need to perform a rotation to convert it to the case + // where the node is on the left side of the parent. + final TreeTableBranchBase xParent = x.parent!; + final TreeTableBranchBase xGrandparent = xParent.parent!; + leftRotate(xParent, inAddMode); + // Now our node x should be the LEFT child of the grandparent. + assert(referenceEquals(xGrandparent.left, x)); + assert(referenceEquals(x.parent, xGrandparent)); + // Perform a right rotation, making the parent our left child and taking our place + assert(referenceEquals(x.left, xParent)); + assert(referenceEquals(xParent.parent, x)); + x = xParent; } - // Recolor and rotate - x!.parent!.color = TreeTableNodeColor.black; - x.parent!.parent!.color = TreeTableNodeColor.red; - rightRotate(x.parent!.parent, inAddMode); + // If the parent of the inserted node is red, we need to perform a left or right + // rotation followed by a color change to ensure that the tree maintains the red-black property. + // In this case, we color the parent black and the grandparent red, + // then perform a right rotation on the grandparent node. + final TreeTableBranchBase xParent = x.parent!; + final TreeTableBranchBase xGrandparent = xParent.parent!; + xParent.color = TreeTableNodeColor.black; + xGrandparent.color = TreeTableNodeColor.red; + rightRotate(xGrandparent, inAddMode); + assert(referenceEquals(x.parent, xParent)); + assert(referenceEquals(xParent.right, xGrandparent)); + assert(referenceEquals(xGrandparent.parent, xParent)); } } else { // Mirror image of above code - final TreeTableNodeBase? y = x.parent!.parent?.left; - if (y != null && y.color == TreeTableNodeColor.red) { - // uncle is red + // Uncle of x + final TreeTableNodeBase? uncle = x.parent!.parent!.left; + if (uncle != null && uncle.color == TreeTableNodeColor.red) { + // Since the uncle node is red, we need to make the parent and uncle nodes black, + // and the grandparent node red. x.parent!.color = TreeTableNodeColor.black; - y.color = TreeTableNodeColor.black; + uncle.color = TreeTableNodeColor.black; x.parent!.parent!.color = TreeTableNodeColor.red; x = x.parent!.parent; } else { // uncle is black - if (x == x.parent!.left) { - x = x.parent; - rightRotate(x, inAddMode); + if (referenceEquals(x, x.parent!.left)) { + // If the node is on the left of the parent, transform the tree to the case + // where the node is on the right of the parent by performing a right rotation. + final TreeTableBranchBase xParent = x.parent!; + final TreeTableBranchBase xGrandparent = xParent.parent!; + rightRotate(xParent, inAddMode); + // After rotating the parent node to the left, + // the node x is now in the correct position to become the right child of the grandparent node. + assert(referenceEquals(xGrandparent.right, x)); + assert(referenceEquals(x.parent, xGrandparent)); + // Since our parent has become our right child, + // we now take the place of our parent as the left child of the grandparent. + assert(referenceEquals(x.right, xParent)); + assert(referenceEquals(xParent.parent, x)); + x = xParent; } - x!.parent!.color = TreeTableNodeColor.black; - x.parent!.parent!.color = TreeTableNodeColor.red; - leftRotate(x.parent!.parent, inAddMode); + // To maintain the balance of the tree, we need to perform a left rotation on our grandparent + // after coloring our parent black and grandparent red. This ensures that the node we inserted + // (which is now the parent's left child) is properly positioned and does not violate any of + // the red-black tree properties. + final TreeTableBranchBase xParent = x.parent!; + final TreeTableBranchBase xGrandparent = xParent.parent!; + xParent.color = TreeTableNodeColor.black; + xGrandparent.color = TreeTableNodeColor.red; + leftRotate(xGrandparent, inAddMode); + assert(referenceEquals(x.parent, xParent)); + assert(referenceEquals(xParent.left, xGrandparent)); + assert(referenceEquals(xGrandparent.parent, xParent)); } } } @@ -1541,21 +1584,35 @@ class TreeTable extends TreeTableBase { return; } - final TreeTableBranchBase y = x.right! as TreeTableBranchBase; - - if (y.left is TreeTableNodeBase) { + // The goal is to replace the node x with its right child y as the new root of the subtree. + // Then, the left child of y becomes the right child of x, and the left child of y will become x. + final TreeTableNodeBase? y = x.right; + if (y != null && y is TreeTableBranchBase) { + final TreeTableNodeBase? yLeft = y.left; + final TreeTableBranchBase? xParent = x.parent; + // In order to perform a rotation on a node, we need to first place its right child y at the new head of the subtree. + // This is necessary to avoid infinite loops in computations that could occur during the setLeft/setRight operations. + // It's important to note that the order of operations is critical for this process to work correctly. + // Once y is in place, we can proceed with the rotation by making y the new root of the subtree, + // placing x as its left child, and making the left child of y the right child of x. y.setLeft(TreeTableEmpty.empty, inAddMode, sorted); - x.setRight(y.left, inAddMode); - if (x.parent != null) { - if (referenceEquals(x, x.parent!.left)) { - x.parent!.setLeft(y, inAddMode, sorted); + if (xParent != null) { + if (referenceEquals(x, xParent.left)) { + xParent.setLeft(y, inAddMode, sorted); } else { - x.parent!.setRight(y, inAddMode); + xParent.setRight(y, inAddMode); } } else { _root = y; + y._parent = null; } + // We want to replace x with y as the root of its subtree, and make x the left child of y. + // First, set y as the new root of the subtree. We must be careful with the order of operations + // to avoid creating a loop. + // Then, set x's right subtree to be y's old left subtree, and make x the left child of y. + x.setRight(TreeTableEmpty.empty, inAddMode); y.setLeft(x, inAddMode, sorted); + x.setRight(yLeft, inAddMode); } } @@ -1604,12 +1661,11 @@ class TreeTable extends TreeTableBase { _root = sisterNode..parent = null; } else { final TreeTableBranchBase leafsParentParent = leafsParent.parent!; - final bool isLeft = leafsParentParent.left == leafsParent; _replaceNode(leafsParentParent, leafsParent, sisterNode, false); if (leafsParent.color == TreeTableNodeColor.black) { leafsParent.parent = leafsParentParent; - deleteFixup(leafsParent, isLeft); + deleteFixup(leafsParent); } } @@ -1644,22 +1700,35 @@ class TreeTable extends TreeTableBase { return; } - final TreeTableBranchBase y = x.left! as TreeTableBranchBase; - - final TreeTableNodeBase yRight = y.right!; - y.setRight( - TreeTableEmpty.empty, inAddMode); // make sure Parent is not reset later - x.setLeft(yRight, inAddMode, sorted); - if (x.parent != null) { - if (x == x.parent!.right) { - x.parent!.setRight(y, inAddMode); + // Goal is that y (left child of x) becomes the new root of the subtree + // in the place of x, and that the right child of y becomes the left + // child of x. The right child of y will become x. + + final TreeTableNodeBase? y = x.left; + if (y != null && y is TreeTableBranchBase) { + final TreeTableNodeBase? yRight = y.right; + final TreeTableBranchBase? xParent = x.parent; + // first place y at the new head of the subtree. + // the order is important to avoid infinite loops in computations + // in the setLeft/setRight operations. + y.setRight(TreeTableEmpty.empty, inAddMode); + if (xParent != null) { + if (referenceEquals(x, xParent.right)) { + xParent.setRight(y, inAddMode); + } else { + xParent.setLeft(y, inAddMode, sorted); + } } else { - x.parent!.setLeft(y, inAddMode, sorted); + _root = y; + y._parent = null; } - } else { - _root = y; + // now setup x's left with y's old right subtree, + // and place x as the right subtree of y. careful with the order + // to avoid creating a temporary loop. + x.setLeft(TreeTableEmpty.empty, inAddMode, sorted); + y.setRight(x, inAddMode); + x.setLeft(yRight, inAddMode, sorted); } - y.setRight(x, inAddMode); } /// Sets the node at the specified index. @@ -3597,7 +3666,6 @@ class TreeTableWithSummary extends TreeTable { } /// A strongly typed enumerator for the `TreeTableWithSummary` collection. - class TreeTableWithSummaryEnumerator extends TreeTableEnumerator { /// Initializes a new instance of the `TreeTableWithSummaryEnumerator` class. /// diff --git a/packages/syncfusion_flutter_datagrid/pubspec.yaml b/packages/syncfusion_flutter_datagrid/pubspec.yaml index 5263edcad..47d1652ee 100644 --- a/packages/syncfusion_flutter_datagrid/pubspec.yaml +++ b/packages/syncfusion_flutter_datagrid/pubspec.yaml @@ -1,6 +1,6 @@ name: syncfusion_flutter_datagrid description: The Syncfusion Flutter DataGrid is used to display and manipulate data in a tabular view. Its rich feature set includes different types of columns, selections, column sizing, etc. -version: 20.4.38 +version: 21.1.35-beta homepage: https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_flutter_datagrid environment: @@ -10,8 +10,8 @@ dependencies: flutter: sdk: flutter - syncfusion_flutter_core: ^20.4.38 - + syncfusion_flutter_core: ^21.1.35 + collection: ">=1.9.0 <=2.0.0" dev_dependencies: diff --git a/packages/syncfusion_flutter_datagrid_export/pubspec.yaml b/packages/syncfusion_flutter_datagrid_export/pubspec.yaml index 3d03099b7..096746060 100644 --- a/packages/syncfusion_flutter_datagrid_export/pubspec.yaml +++ b/packages/syncfusion_flutter_datagrid_export/pubspec.yaml @@ -1,6 +1,6 @@ name: syncfusion_flutter_datagrid_export description: The Syncfusion Flutter DataGrid Export library is used to export the DataGrid content to Excel and Pdf format with several customization options. -version: 20.4.38-beta +version: 21.1.35-beta homepage: https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_flutter_datagrid_export environment: @@ -11,11 +11,11 @@ dependencies: flutter: sdk: flutter - syncfusion_flutter_datagrid: ^20.4.38 + syncfusion_flutter_datagrid: ^21.1.35 - syncfusion_flutter_xlsio: ^20.4.38-beta + syncfusion_flutter_xlsio: ^21.1.35 - syncfusion_flutter_pdf: ^20.4.38 + syncfusion_flutter_pdf: ^21.1.35 collection: ">=1.9.0 <=2.0.0" diff --git a/packages/syncfusion_flutter_datepicker/lib/src/date_picker/date_picker.dart b/packages/syncfusion_flutter_datepicker/lib/src/date_picker/date_picker.dart index 5bf1b6c20..738bbba8c 100644 --- a/packages/syncfusion_flutter_datepicker/lib/src/date_picker/date_picker.dart +++ b/packages/syncfusion_flutter_datepicker/lib/src/date_picker/date_picker.dart @@ -11807,14 +11807,19 @@ class _PickerViewState extends State<_PickerView> case DateRangePickerNavigationDirection.horizontal: { width = width - widget.picker.viewSpacing - weekNumberPanelWidth; + final double singleViewWidth = width / 2; totalColumnCount *= 2; - if (xPosition > width / 2 && + + /// return -1 while the position in between the view spacing. + if (xPosition > weekNumberPanelWidth + singleViewWidth && xPosition < - (width / 2) + + singleViewWidth + widget.picker.viewSpacing + - weekNumberPanelWidth) { + (2 * weekNumberPanelWidth)) { return index; - } else if (xPosition > width / 2) { + } else if (xPosition > singleViewWidth + weekNumberPanelWidth) { + /// Subtract the 2nd view week number panel width and in between + /// spacing while the position is after the 1st view. xPosition = xPosition - widget.picker.viewSpacing - weekNumberPanelWidth; } @@ -11824,6 +11829,8 @@ class _PickerViewState extends State<_PickerView> { height = (height - widget.picker.viewSpacing) / 2; totalRowCount *= 2; + + /// return -1 while the position in between the view spacing. if (yPosition > height && yPosition < height + widget.picker.viewSpacing) { return index; diff --git a/packages/syncfusion_flutter_datepicker/pubspec.yaml b/packages/syncfusion_flutter_datepicker/pubspec.yaml index 2583747ba..fa7cb1203 100644 --- a/packages/syncfusion_flutter_datepicker/pubspec.yaml +++ b/packages/syncfusion_flutter_datepicker/pubspec.yaml @@ -1,6 +1,6 @@ name: syncfusion_flutter_datepicker description: The Flutter Date Range Picker widget allows users to easily select dates or a range of dates. It has four built-in views that allow quick navigation to the desired date. -version: 20.4.38 +version: 21.1.35 homepage: https://github.com/syncfusion/flutter-examples environment: @@ -10,7 +10,7 @@ dependencies: flutter: sdk: flutter intl: ">=0.17.0 <0.20.0" - syncfusion_flutter_core: ^20.4.38 + syncfusion_flutter_core: ^21.1.35 dev_dependencies: flutter_test: diff --git a/packages/syncfusion_flutter_gauges/CHANGELOG.md b/packages/syncfusion_flutter_gauges/CHANGELOG.md index 69c2ba5cd..db6747270 100644 --- a/packages/syncfusion_flutter_gauges/CHANGELOG.md +++ b/packages/syncfusion_flutter_gauges/CHANGELOG.md @@ -1,3 +1,10 @@ +## Unreleased + +## Radial Gauge + +**Bugs** +* #FB40920 - Now, the axis tapped callback returns the proper value while enabling the [`canScaleToFit`](https://pub.dev/documentation/syncfusion_flutter_gauges/latest/gauges/RadialAxis/canScaleToFit.html) property in gauges. + ## [20.2.36] - 07/01/2022 ## Radial Gauge diff --git a/packages/syncfusion_flutter_gauges/lib/src/radial_gauge/axis/radial_axis_widget.dart b/packages/syncfusion_flutter_gauges/lib/src/radial_gauge/axis/radial_axis_widget.dart index 65499fb33..06b6346d1 100644 --- a/packages/syncfusion_flutter_gauges/lib/src/radial_gauge/axis/radial_axis_widget.dart +++ b/packages/syncfusion_flutter_gauges/lib/src/radial_gauge/axis/radial_axis_widget.dart @@ -1611,18 +1611,17 @@ class RenderRadialAxisWidget extends RenderBox { /// Calculates the axis rect. void _calculateAxisRect() { - _axisRect = Rect.fromLTRB( - -(_radius - (_actualAxisWidth / 2 + _axisOffset)), - -(_radius - (_actualAxisWidth / 2 + _axisOffset)), - _radius - (_actualAxisWidth / 2 + _axisOffset), - _radius - (_actualAxisWidth / 2 + _axisOffset)); + final double axisOffset = _radius - (_actualAxisWidth / 2 + _axisOffset); + _axisRect = Rect.fromLTRB(-axisOffset, -axisOffset, axisOffset, axisOffset); axisPath.reset(); + final double centerX = canScaleToFit ? _axisCenter.dx : size.width / 2; + final double centerY = canScaleToFit ? _axisCenter.dy : size.height / 2; final Rect rect = Rect.fromLTRB( - _axisRect.left + size.width / 2, - _axisRect.top + size.height / 2, - _axisRect.right + size.width / 2, - _axisRect.bottom + size.height / 2, + _axisRect.left + centerX, + _axisRect.top + centerY, + _axisRect.right + centerX, + _axisRect.bottom + centerY, ); axisPath.arcTo(rect, _startRadian, _endRadian, false); @@ -1630,8 +1629,10 @@ class RenderRadialAxisWidget extends RenderBox { /// Method to calculate the angle from the tapped point. void calculateAngleFromOffset(Offset offset) { - final double actualCenterX = size.width * centerX; - final double actualCenterY = size.height * centerY; + final double actualCenterX = + canScaleToFit ? _axisCenter.dx : size.width * centerX; + final double actualCenterY = + canScaleToFit ? _axisCenter.dy : size.height * centerY; double angle = math.atan2(offset.dy - actualCenterY, offset.dx - actualCenterX) * (180 / math.pi) + diff --git a/packages/syncfusion_flutter_gauges/pubspec.yaml b/packages/syncfusion_flutter_gauges/pubspec.yaml index 35b896a08..336f4b837 100644 --- a/packages/syncfusion_flutter_gauges/pubspec.yaml +++ b/packages/syncfusion_flutter_gauges/pubspec.yaml @@ -1,6 +1,6 @@ name: syncfusion_flutter_gauges description: The Flutter gauges library includes a linear gauge and radial gauge (a.k.a. circular gauge) to create modern, interactive, animated gauges and radial sliders. -version: 20.4.38 +version: 21.1.35 homepage: https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_flutter_gauges environment: @@ -9,7 +9,7 @@ environment: dependencies: flutter: sdk: flutter - syncfusion_flutter_core: ^20.4.38 + syncfusion_flutter_core: ^21.1.35 intl: ">=0.17.0 <0.20.0" dev_dependencies: diff --git a/packages/syncfusion_flutter_maps/CHANGELOG.md b/packages/syncfusion_flutter_maps/CHANGELOG.md index 319f5de60..a8d4204d8 100644 --- a/packages/syncfusion_flutter_maps/CHANGELOG.md +++ b/packages/syncfusion_flutter_maps/CHANGELOG.md @@ -1,3 +1,16 @@ +## [20.4.50] - 02/14/2023 + +**Bugs** + +* #FB40437 - The issue with panning after the pinch zooming on the iPhone browser has been resolved. + +* The issue with the custom button zooming on the MapZoomPanBehavior not working when the enablePinching is set to false has been resolved. + +## [20.4.43] - 01/10/2023 + +**Feature** +* #FB39509 - The issue with the previous and new zoom levels of onWillZoom not updating properly while zooming through the toolbar has been resolved. + ## [20.3.47] - 09/29/2022 **Bug** diff --git a/packages/syncfusion_flutter_maps/example/lib/main.dart b/packages/syncfusion_flutter_maps/example/lib/main.dart index 5727ebb4e..9c40b5912 100644 --- a/packages/syncfusion_flutter_maps/example/lib/main.dart +++ b/packages/syncfusion_flutter_maps/example/lib/main.dart @@ -87,11 +87,12 @@ class _MyHomePageState extends State { ); }, dataLabelSettings: MapDataLabelSettings( - textStyle: TextStyle( - color: Colors.black, - fontWeight: FontWeight.bold, - fontSize: - Theme.of(context).textTheme.bodySmall!.fontSize)), + textStyle: TextStyle( + color: Colors.black, + fontWeight: FontWeight.bold, + fontSize: + Theme.of(context).textTheme.bodySmall!.fontSize), + ), ), ], ), diff --git a/packages/syncfusion_flutter_maps/lib/src/layer/shape_layer.dart b/packages/syncfusion_flutter_maps/lib/src/layer/shape_layer.dart index 6d6181b7d..031f7ba64 100644 --- a/packages/syncfusion_flutter_maps/lib/src/layer/shape_layer.dart +++ b/packages/syncfusion_flutter_maps/lib/src/layer/shape_layer.dart @@ -2009,6 +2009,7 @@ class _RenderGeoJSONLayer extends RenderStack double _actualFactor = 1.0; double _maximumReachedScaleOnInteraction = 1.0; int _pointerCount = 0; + bool _initailPinchZooming = false; Offset _panDistanceBeforeFlinging = Offset.zero; bool _avoidPanUpdate = false; bool _isFlingAnimationActive = false; @@ -2696,6 +2697,16 @@ class _RenderGeoJSONLayer extends RenderStack _controller.gesture = null; } + // HACK: If the initial pinch zooming is performed then reinitialize + // the ScaleGestureRecognizer for the web platform alone + if (!_initailPinchZooming && kIsWeb) { + _scaleGestureRecognizer.dispose(); + _scaleGestureRecognizer = ScaleGestureRecognizer() + ..onStart = _handleScaleStart + ..onUpdate = _handleScaleUpdate + ..onEnd = _handleScaleEnd; + _initailPinchZooming = true; + } } void _startFlingAnimationForPanning(ScaleEndDetails details) { @@ -3003,9 +3014,7 @@ class _RenderGeoJSONLayer extends RenderStack _zoomingDelayTimer = null; _zoomDetails = null; _panDetails = null; - if (_zoomPanBehavior != null && - _zoomPanBehavior!.enablePinching && - !_state.isSublayer) { + if (_zoomPanBehavior != null && !_state.isSublayer) { _controller.shapeLayerOffset = _controller.getZoomingTranslation() + _controller.normalize; _controller.shapeLayerOrigin = _controller.getZoomingTranslation( @@ -3156,7 +3165,7 @@ class _RenderGeoJSONLayer extends RenderStack _zoomDetails = null; _panDetails = null; _panDistanceBeforeFlinging = Offset.zero; - if (_zoomPanBehavior!.enablePanning && !_state.isSublayer) { + if (!_state.isSublayer) { _controller.shapeLayerOffset += _controller.panDistance; _controller.shapeLayerOrigin += _controller.panDistance; _updateMapDataSourceForVisual(); diff --git a/packages/syncfusion_flutter_maps/lib/src/layer/vector_layers.dart b/packages/syncfusion_flutter_maps/lib/src/layer/vector_layers.dart index 87f626277..bb42e2db1 100644 --- a/packages/syncfusion_flutter_maps/lib/src/layer/vector_layers.dart +++ b/packages/syncfusion_flutter_maps/lib/src/layer/vector_layers.dart @@ -1086,7 +1086,9 @@ class _RenderMapLine extends RenderBox implements MouseTrackerAnnotation { @override bool hitTestSelf(Offset position) { - if (_animation != null && !_animation!.isCompleted) { + if ((_animation != null && !_animation!.isCompleted) || + linesInList == null || + linesInList!.isEmpty) { return false; } @@ -2118,7 +2120,9 @@ class _RenderMapArc extends RenderBox implements MouseTrackerAnnotation { @override bool hitTestSelf(Offset position) { - if (_animation != null && !_animation!.isCompleted) { + if ((_animation != null && !_animation!.isCompleted) || + arcsInList == null || + arcsInList!.isEmpty) { return false; } @@ -3214,7 +3218,9 @@ class _RenderMapPolyline extends RenderBox implements MouseTrackerAnnotation { @override bool hitTestSelf(Offset position) { - if (_animation != null && !_animation!.isCompleted) { + if ((_animation != null && !_animation!.isCompleted) || + polylinesInList == null || + polylinesInList!.isEmpty) { return false; } @@ -4269,6 +4275,9 @@ class _RenderMapPolygon extends RenderBox implements MouseTrackerAnnotation { @override bool hitTestSelf(Offset position) { + if (_polygonsInList == null || _polygonsInList!.isEmpty) { + return false; + } int index = _polygonsInList!.length - 1; final Size boxSize = _controller?.layerType == LayerType.tile ? _controller!.totalTileSize! @@ -5478,7 +5487,9 @@ class _RenderMapCircle extends RenderBox implements MouseTrackerAnnotation { @override bool hitTestSelf(Offset position) { - if (_animation != null && !_animation!.isCompleted) { + if ((_animation != null && !_animation!.isCompleted) || + _circlesInList == null || + _circlesInList!.isEmpty) { return false; } diff --git a/packages/syncfusion_flutter_maps/pubspec.yaml b/packages/syncfusion_flutter_maps/pubspec.yaml index 807902244..2c320c37e 100644 --- a/packages/syncfusion_flutter_maps/pubspec.yaml +++ b/packages/syncfusion_flutter_maps/pubspec.yaml @@ -1,6 +1,6 @@ name: syncfusion_flutter_maps description: A Flutter Maps library for creating beautiful, interactive, and customizable maps from shape files or WMTS services to visualize the geographical area. -version: 20.4.38 +version: 21.1.35 homepage: https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_flutter_maps environment: @@ -10,7 +10,7 @@ dependencies: flutter: sdk: flutter - syncfusion_flutter_core: ^20.4.38 + syncfusion_flutter_core: ^21.1.35 http: ">=0.12.0 <=0.13.5" vector_math: ">=2.1.0 <=3.0.0" diff --git a/packages/syncfusion_flutter_pdf/CHANGELOG.md b/packages/syncfusion_flutter_pdf/CHANGELOG.md index 455eb0c0b..c27ef043a 100644 --- a/packages/syncfusion_flutter_pdf/CHANGELOG.md +++ b/packages/syncfusion_flutter_pdf/CHANGELOG.md @@ -1,3 +1,17 @@ +## [20.4.54] - 03/15/2023 + +**Bugs** + +* The issue of PDF document size increasing after removing pages has been resolved. + +## [20.4.50] - 02/14/2023 + +**Bugs** + +* Resolved the document corruption exception when signing existing signed PDF documents. + +* Text bounds are now retrieved properly when finding text from cropped PDF documents. + ## [20.3.57] - 11/15/2022 **Bugs** diff --git a/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/exporting/pdf_text_extractor/pdf_text_extractor.dart b/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/exporting/pdf_text_extractor/pdf_text_extractor.dart index 8f293ed1e..7fb471aad 100644 --- a/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/exporting/pdf_text_extractor/pdf_text_extractor.dart +++ b/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/exporting/pdf_text_extractor/pdf_text_extractor.dart @@ -1081,7 +1081,7 @@ class PdfTextExtractor { Rect _calculateBounds(Rect bounds) { if (_currentPage != null) { - if (PdfPageHelper.getHelper(_currentPage!).cropBox != Rect.zero && + if (!PdfPageHelper.getHelper(_currentPage!).cropBox.isEmpty && PdfPageHelper.getHelper(_currentPage!).cropBox != PdfPageHelper.getHelper(_currentPage!).mediaBox) { final double x = @@ -1089,6 +1089,15 @@ class PdfTextExtractor { final double y = bounds.top + PdfPageHelper.getHelper(_currentPage!).cropBox.top; return Rect.fromLTWH(x, y, bounds.width, bounds.height); + } else if (!PdfPageHelper.getHelper(_currentPage!).mediaBox.isEmpty && + (PdfPageHelper.getHelper(_currentPage!).mediaBox.left != 0 || + PdfPageHelper.getHelper(_currentPage!).mediaBox.top != 0)) { + final double cLeft = + PdfPageHelper.getHelper(_currentPage!).mediaBox.left; + final double ctop = PdfPageHelper.getHelper(_currentPage!).mediaBox.top; + final double x = bounds.left - ((cLeft < 0 && ctop < 0) ? 0 : cLeft); + final double y = bounds.top + ctop; + return Rect.fromLTWH(x, y, bounds.width, bounds.height); } } return bounds; diff --git a/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/forms/pdf_form.dart b/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/forms/pdf_form.dart index e0446be65..34ec55983 100644 --- a/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/forms/pdf_form.dart +++ b/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/forms/pdf_form.dart @@ -1325,6 +1325,7 @@ class PdfFormHelper { break; } } + helper.dictionary!.isSkip = true; fields.changed = true; if (!formHasKids || !helper.dictionary!.items! diff --git a/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/forms/pdf_form_field_collection.dart b/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/forms/pdf_form_field_collection.dart index 055db9b7e..de2fb2f2b 100644 --- a/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/forms/pdf_form_field_collection.dart +++ b/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/forms/pdf_form_field_collection.dart @@ -534,6 +534,31 @@ class PdfFormFieldCollectionHelper extends PdfObjectCollectionHelper { return i; } + /// internal method + void removeContainingField(PdfReferenceHolder pageReferenceHolder) { + for (int i = array.count - 1; i >= 0; --i) { + final IPdfPrimitive? fieldDictionary = + PdfCrossTable.dereference(array[i]); + if (fieldDictionary != null && fieldDictionary is PdfDictionary) { + if (fieldDictionary.containsKey(PdfDictionaryProperties.p)) { + final IPdfPrimitive? holder = + fieldDictionary[PdfDictionaryProperties.p]; + if (holder != null && + holder is PdfReferenceHolder && + holder.object == pageReferenceHolder.object) { + _doRemoveAt(i); + } + } else if (fieldDictionary.containsKey(PdfDictionaryProperties.kids)) { + final bool removed = + _removeContainingFieldItems(fieldDictionary, pageReferenceHolder); + if (removed) { + _doRemoveAt(i); + } + } + } + } + } + void _removeFromDictionary(PdfField field) { if (PdfFieldHelper.getHelper(field).isLoadedField) { PdfFormHelper.getHelper(form!).removeFromDictionaries(field); @@ -586,4 +611,34 @@ class PdfFormFieldCollectionHelper extends PdfObjectCollectionHelper { array.clear(); list.clear(); } + + bool _removeContainingFieldItems( + PdfDictionary fieldDictionary, PdfReferenceHolder pageReferenceHolder) { + bool isAllKidsRemoved = false; + if (fieldDictionary.containsKey(PdfDictionaryProperties.kids)) { + final IPdfPrimitive? array = PdfCrossTable.dereference( + fieldDictionary[PdfDictionaryProperties.kids]); + if (array != null && array is PdfArray) { + for (int i = array.count - 1; i >= 0; --i) { + IPdfPrimitive? holder; + final IPdfPrimitive? kidObject = PdfCrossTable.dereference(array[i]); + if (kidObject != null && + kidObject is PdfDictionary && + kidObject.containsKey(PdfDictionaryProperties.p)) + holder = kidObject[PdfDictionaryProperties.p]; + if (holder != null && + holder is PdfReferenceHolder && + holder.object == pageReferenceHolder.object) { + (kidObject as PdfDictionary?)!.isSkip = true; + array.removeAt(i); + array.changed = true; + } + } + if (array.count == 0) { + isAllKidsRemoved = true; + } + } + } + return isAllKidsRemoved; + } } diff --git a/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/forms/pdf_signature_field.dart b/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/forms/pdf_signature_field.dart index 722dd93ae..1404c512d 100644 --- a/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/forms/pdf_signature_field.dart +++ b/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/forms/pdf_signature_field.dart @@ -132,6 +132,7 @@ class PdfSignatureField extends PdfField { PdfSignatureHelper.getHelper(_signature!).signatureDictionary!; if (!PdfDocumentHelper.getHelper(document).isLoadedDocument || document.fileStructure.incrementalUpdate != false) { + signatureDictionary.dictionary!.archive = false; PdfDocumentHelper.getHelper(document) .objects .add(signatureDictionary.element); diff --git a/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/forms/pdf_xfdf_document.dart b/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/forms/pdf_xfdf_document.dart index 5537e4abb..d5c7d3f81 100644 --- a/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/forms/pdf_xfdf_document.dart +++ b/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/forms/pdf_xfdf_document.dart @@ -35,7 +35,8 @@ class XFdfDocument { builder.element(PdfDictionaryProperties.fields.toLowerCase(), nest: _writeFormData()); builder.element('f', nest: () { - builder.attribute('href', _pdfFilePath); + // ignore: unnecessary_null_checks + builder.attribute('href', _pdfFilePath!); }); }); xmlData = utf8.encode(builder.buildDocument().toXmlString(pretty: true)); diff --git a/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/io/pdf_cross_table.dart b/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/io/pdf_cross_table.dart index ef392db9a..af4289c12 100644 --- a/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/io/pdf_cross_table.dart +++ b/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/io/pdf_cross_table.dart @@ -353,7 +353,13 @@ class PdfCrossTable { final PdfReference ref = getReference(obj); objInfo.reference = ref; } - _saveIndirectObject(obj, writer); + bool skip = false; + if (obj is PdfDictionary && obj.isSkip) { + skip = true; + } + if (!skip) { + _saveIndirectObject(obj, writer); + } } } } @@ -374,7 +380,13 @@ class PdfCrossTable { final PdfReference ref = await getReferenceAsync(obj); objInfo.reference = ref; } - await _saveIndirectObjectAsync(obj, writer); + bool skip = false; + if (obj is PdfDictionary && obj.isSkip) { + skip = true; + } + if (!skip) { + await _saveIndirectObjectAsync(obj, writer); + } } } } diff --git a/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/pages/pdf_page_collection.dart b/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/pages/pdf_page_collection.dart index 602ff6e3d..c42e4f76f 100644 --- a/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/pages/pdf_page_collection.dart +++ b/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/pages/pdf_page_collection.dart @@ -1,6 +1,7 @@ import 'dart:ui'; import '../../interfaces/pdf_interface.dart'; +import '../forms/pdf_form_field_collection.dart'; import '../graphics/pdf_graphics.dart'; import '../graphics/pdf_margins.dart'; import '../io/pdf_constants.dart'; @@ -644,6 +645,7 @@ class PdfPageCollection { } } if (remove != null) { + _removeFormFields(remove); kids.remove(remove); if (kids.count == 0 && parent.containsKey(PdfDictionaryProperties.parent)) { @@ -688,6 +690,15 @@ class PdfPageCollection { } } + void _removeFormFields(PdfReferenceHolder pageHolder) { + if (PdfDocumentHelper.getHelper(_helper.document!).isLoadedDocument && + _helper.document!.form != null) { + PdfFormFieldCollectionHelper.getHelper( + PdfPageCollectionHelper.getHelper(this).document!.form.fields) + .removeContainingField(pageHolder); + } + } + void _updateCountDecrement(PdfDictionary? parent) { while (parent != null) { int count = _getNodeCount(parent) - 1; diff --git a/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/primitives/pdf_dictionary.dart b/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/primitives/pdf_dictionary.dart index b1cf8c580..1867e75d9 100644 --- a/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/primitives/pdf_dictionary.dart +++ b/packages/syncfusion_flutter_pdf/lib/src/pdf/implementation/primitives/pdf_dictionary.dart @@ -43,6 +43,7 @@ class PdfDictionary implements IPdfPrimitive, IPdfChangable { int? _objectCollectionIndex; int? _position; PdfObjectStatus? _status; + bool? _isSkip; /// internal field PdfCrossTable? crossTable; @@ -61,6 +62,16 @@ class PdfDictionary implements IPdfPrimitive, IPdfChangable { /// Set the PdfDictionary items. operator []=(dynamic key, dynamic value) => addItems(key, value); + /// internal property + bool get isSkip { + _isSkip ??= false; + return _isSkip!; + } + + set isSkip(bool value) { + _isSkip = value; + } + /// internal method dynamic addItems(dynamic key, dynamic value) { if (key == null) { diff --git a/packages/syncfusion_flutter_pdf/pubspec.yaml b/packages/syncfusion_flutter_pdf/pubspec.yaml index 3bf21b4c3..2884143ea 100644 --- a/packages/syncfusion_flutter_pdf/pubspec.yaml +++ b/packages/syncfusion_flutter_pdf/pubspec.yaml @@ -1,6 +1,6 @@ name: syncfusion_flutter_pdf description: The Flutter PDF is a library written natively in Dart for creating, reading, editing, and securing PDF files in Android, iOS, and web platforms. -version: 20.4.38 +version: 21.1.35 homepage: https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_flutter_pdf environment: @@ -11,7 +11,7 @@ dependencies: sdk: flutter intl: ">=0.17.0 <0.20.0" xml: ">=5.1.0 <7.0.0" - syncfusion_flutter_core: ^20.4.38 + syncfusion_flutter_core: ^21.1.35 crypto: ">=3.0.0 <4.0.0" convert: ">=3.0.0 <4.0.0" diff --git a/packages/syncfusion_flutter_pdfviewer/CHANGELOG.md b/packages/syncfusion_flutter_pdfviewer/CHANGELOG.md index 07e72519e..ed765f7f0 100644 --- a/packages/syncfusion_flutter_pdfviewer/CHANGELOG.md +++ b/packages/syncfusion_flutter_pdfviewer/CHANGELOG.md @@ -1,3 +1,22 @@ +## [20.4.55] - 03/21/2023 + +**Bugs** + +* Now, the `SfPdfViewer` does not cause text fields to lose focus when scrolling is active on an Android tablet or the iPad. +* Now, the keyboard shortcut navigation on the web platform works properly. + +**Features** + +* Support to set and adjust the maximum zoom level has been provided. + +## [20.4.51] - 02/21/2022 + +* The password dialog is now displayed properly in the `SfPdfViewer` when localized. + +## [20.4.43] - 01/10/2022 + +* Support for scrolling via remote button clicks on Android TV has been provided. + ## [20.3.58] - 11/22/2022 **Bugs** diff --git a/packages/syncfusion_flutter_pdfviewer/lib/src/control/pdf_page_view.dart b/packages/syncfusion_flutter_pdfviewer/lib/src/control/pdf_page_view.dart index 0ea394641..6316ead9f 100644 --- a/packages/syncfusion_flutter_pdfviewer/lib/src/control/pdf_page_view.dart +++ b/packages/syncfusion_flutter_pdfviewer/lib/src/control/pdf_page_view.dart @@ -29,6 +29,7 @@ class PdfPageView extends StatefulWidget { this.pdfPages, this.pageIndex, this.pdfViewerController, + this.maxZoomLevel, this.enableDocumentLinkAnnotation, this.enableTextSelection, this.onTextSelectionChanged, @@ -51,6 +52,7 @@ class PdfPageView extends StatefulWidget { this.textDirection, this.canShowHyperlinkDialog, this.enableHyperlinkNavigation, + this.isAndroidTV, ) : super(key: key); /// Image stream @@ -95,6 +97,9 @@ class PdfPageView extends StatefulWidget { /// Instance of [PdfViewerController] final PdfViewerController pdfViewerController; + /// Represents the maximum zoom level . + final double maxZoomLevel; + /// If false,text selection is disabled.Default value is true. final bool enableTextSelection; @@ -152,6 +157,9 @@ class PdfPageView extends StatefulWidget { ///A direction of text flow. final TextDirection textDirection; + /// Returns true when the SfPdfViewer is deployed in Android TV. + final bool isAndroidTV; + @override State createState() { return PdfPageViewState(); @@ -385,37 +393,23 @@ class PdfPageViewState extends State { } if (isPrimaryKeyPressed && event.logicalKey == LogicalKeyboardKey.minus) { - if (event.runtimeType.toString() == 'RawKeyDownEvent') { + if (event is RawKeyDownEvent) { double zoomLevel = widget.pdfViewerController.zoomLevel; - if (zoomLevel >= 1.0 && zoomLevel <= 1.25) { - zoomLevel = 1.0; - } else if (zoomLevel > 1.25 && zoomLevel <= 1.50) { - zoomLevel = 1.25; - } else if (zoomLevel > 1.50 && zoomLevel <= 2.0) { - zoomLevel = 1.50; - } else { - zoomLevel = 2.0; + if (zoomLevel > 1) { + zoomLevel = zoomLevel - 0.5; } widget.pdfViewerController.zoomLevel = zoomLevel; } } if (isPrimaryKeyPressed && event.logicalKey == LogicalKeyboardKey.equal) { - if (event.runtimeType.toString() == 'RawKeyDownEvent') { + if (event is RawKeyDownEvent) { double zoomLevel = widget.pdfViewerController.zoomLevel; - if (zoomLevel >= 1.0 && zoomLevel < 1.25) { - zoomLevel = 1.25; - } else if (zoomLevel >= 1.25 && zoomLevel < 1.50) { - zoomLevel = 1.50; - } else if (zoomLevel >= 1.50 && zoomLevel < 2.0) { - zoomLevel = 2.0; - } else { - zoomLevel = 3.0; - } + zoomLevel = zoomLevel + 0.5; widget.pdfViewerController.zoomLevel = zoomLevel; } } - if (event.runtimeType.toString() == 'RawKeyDownEvent') { + if (event is RawKeyDownEvent) { if (event.logicalKey == LogicalKeyboardKey.home || (kIsMacOS && event.logicalKey == LogicalKeyboardKey.fn && @@ -497,9 +491,8 @@ class PdfPageViewState extends State { onPointerUp: (PointerUpEvent details) { widget.onPdfPagePointerUp(details); }, - child: widget.isMobileWebView - ? canvasContainer - : RawKeyboardListener( + child: widget.isAndroidTV + ? RawKeyboardListener( focusNode: focusNode, onKey: (RawKeyEvent event) { if (event.runtimeType.toString() == @@ -520,7 +513,8 @@ class PdfPageViewState extends State { canvasRenderBox!.scroll(false, false); } }, - child: canvasContainer), + child: canvasContainer) + : canvasContainer, )); return Stack(children: [ pdfPage, diff --git a/packages/syncfusion_flutter_pdfviewer/lib/src/control/scroll_head_overlay.dart b/packages/syncfusion_flutter_pdfviewer/lib/src/control/scroll_head_overlay.dart index 7b9f58f31..8d3adf5d9 100644 --- a/packages/syncfusion_flutter_pdfviewer/lib/src/control/scroll_head_overlay.dart +++ b/packages/syncfusion_flutter_pdfviewer/lib/src/control/scroll_head_overlay.dart @@ -159,7 +159,7 @@ class ScrollHeadOverlayState extends State { bool isScrolled = false; /// Focus node for page navigation dialogue. - FocusNode focusNode = FocusNode(); + final FocusNode _focusNode = FocusNode(); double _scale = 1; @@ -189,7 +189,7 @@ class ScrollHeadOverlayState extends State { void dispose() { _pdfViewerThemeData = null; _localizations = null; - focusNode.dispose(); + _focusNode.dispose(); _scrollTimer?.cancel(); _scrollTimer = null; super.dispose(); @@ -486,7 +486,7 @@ class ScrollHeadOverlayState extends State { fontFamily: 'Roboto', fontSize: 16, color: _themeData!.colorScheme.onSurface.withOpacity(0.87)), - focusNode: focusNode, + focusNode: _focusNode, decoration: InputDecoration( isDense: true, focusedBorder: UnderlineInputBorder( diff --git a/packages/syncfusion_flutter_pdfviewer/lib/src/control/single_page_view.dart b/packages/syncfusion_flutter_pdfviewer/lib/src/control/single_page_view.dart index 5b673790a..ca51c268e 100644 --- a/packages/syncfusion_flutter_pdfviewer/lib/src/control/single_page_view.dart +++ b/packages/syncfusion_flutter_pdfviewer/lib/src/control/single_page_view.dart @@ -30,6 +30,7 @@ class SinglePageView extends StatefulWidget { this.onPageChanged, this.interactionUpdate, this.viewportDimension, + this.maxZoomLevel, this.canShowPaginationDialog, this.canShowScrollHead, this.canShowScrollStatus, @@ -71,6 +72,9 @@ class SinglePageView extends StatefulWidget { /// Viewport dimension of PdfViewer. final Size viewportDimension; + /// Represents the maximum zoom level + final double maxZoomLevel; + /// Indicates whether page navigation dialog must be shown or not. final bool canShowPaginationDialog; @@ -194,16 +198,16 @@ class SinglePageViewState extends State { zoomLevel : 0; final double childWidth = viewportDimension.width > imageWidth - ? viewportDimension.width / widthFactor.clamp(1, 3) - : imageWidth / widthFactor.clamp(1, 3); + ? viewportDimension.width / widthFactor.clamp(1, widget.maxZoomLevel) + : imageWidth / widthFactor.clamp(1, widget.maxZoomLevel); final double imageHeight = widget.pdfPages.isNotEmpty ? widget.pdfPages[widget.pdfViewerController.pageNumber]!.pageSize .height * zoomLevel : 0; double childHeight = viewportDimension.height > imageHeight - ? viewportDimension.height / heightFactor.clamp(1, 3) - : imageHeight / heightFactor.clamp(1, 3); + ? viewportDimension.height / heightFactor.clamp(1, widget.maxZoomLevel) + : imageHeight / heightFactor.clamp(1, widget.maxZoomLevel); if (childHeight > viewportDimension.height) { childHeight = widget.viewportDimension.height; } @@ -350,6 +354,7 @@ class SinglePageViewState extends State { : widget.viewportDimension.width, child: Center(child: page)), clipBehavior: Clip.none, + maxScale: widget.maxZoomLevel, boundaryMargin: EdgeInsets.only( top: isHeightFitted || isLandscape ? 0 diff --git a/packages/syncfusion_flutter_pdfviewer/lib/src/pdfviewer.dart b/packages/syncfusion_flutter_pdfviewer/lib/src/pdfviewer.dart index 768979ac8..41a6ff9d0 100644 --- a/packages/syncfusion_flutter_pdfviewer/lib/src/pdfviewer.dart +++ b/packages/syncfusion_flutter_pdfviewer/lib/src/pdfviewer.dart @@ -19,6 +19,7 @@ import 'dart:math'; import 'dart:ui'; import 'package:async/async.dart'; +import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; @@ -141,6 +142,7 @@ class SfPdfViewer extends StatefulWidget { this.canShowPaginationDialog = true, this.initialScrollOffset = Offset.zero, this.initialZoomLevel = 1, + this.maxZoomLevel = 3, this.interactionMode = PdfInteractionMode.selection, this.scrollDirection = PdfScrollDirection.vertical, this.pageLayoutMode = PdfPageLayoutMode.continuous, @@ -197,6 +199,7 @@ class SfPdfViewer extends StatefulWidget { this.canShowPaginationDialog = true, this.initialScrollOffset = Offset.zero, this.initialZoomLevel = 1, + this.maxZoomLevel = 3, this.interactionMode = PdfInteractionMode.selection, this.scrollDirection = PdfScrollDirection.vertical, this.pageLayoutMode = PdfPageLayoutMode.continuous, @@ -251,6 +254,7 @@ class SfPdfViewer extends StatefulWidget { this.canShowPaginationDialog = true, this.initialScrollOffset = Offset.zero, this.initialZoomLevel = 1, + this.maxZoomLevel = 3, this.interactionMode = PdfInteractionMode.selection, this.scrollDirection = PdfScrollDirection.vertical, this.pageLayoutMode = PdfPageLayoutMode.continuous, @@ -309,6 +313,7 @@ class SfPdfViewer extends StatefulWidget { this.canShowPaginationDialog = true, this.initialScrollOffset = Offset.zero, this.initialZoomLevel = 1, + this.maxZoomLevel = 3, this.interactionMode = PdfInteractionMode.selection, this.scrollDirection = PdfScrollDirection.vertical, this.pageLayoutMode = PdfPageLayoutMode.continuous, @@ -370,6 +375,40 @@ class SfPdfViewer extends StatefulWidget { /// ``` final double initialZoomLevel; + /// Represents the maximum allowed zoom level. + /// + /// Defaults to 3.0. + /// + /// If the [zoomLevel] value is set higher than the maximum zoom level, then it will be restricted to the maximum zoom level. + /// + /// This example demonstrates how to set the maximum allowed zoom level in the [SfPdfViewer]. + /// + /// ```dart + /// class MyAppState extends State { + /// final GlobalKey _pdfViewerKey = GlobalKey(); + /// + /// @override + /// void initState() { + /// super.initState(); + /// } + /// + /// @override + /// Widget build(BuildContext context) { + /// return Scaffold( + /// appBar: AppBar( + /// title: const Text('Syncfusion Flutter PDF Viewer'), + /// ), + /// body: SfPdfViewer.network( + /// 'https://cdn.syncfusion.com/content/PDFViewer/flutter-succinctly.pdf', + /// key: _pdfViewerKey, + /// maxZoomLevel: 6, + /// ), + /// ); + /// } + /// } + /// ``` + final double maxZoomLevel; + /// Represents the initial scroll offset position to be displayed when the [SfPdfViewer] widget is loaded. /// /// Defaults to Offset(0, 0) @@ -859,7 +898,6 @@ class SfPdfViewerState extends State with WidgetsBindingObserver { double? _otherContextHeight; double _maxPdfPageWidth = 0.0; final double _minScale = 1; - final double _maxScale = 3; bool _isScaleEnabled = !kIsDesktop; bool _isPdfPageTapped = false; bool _isDocumentLoadInitiated = false; @@ -929,6 +967,7 @@ class SfPdfViewerState extends State with WidgetsBindingObserver { Isolate? _textSearchIsolate; Isolate? _textExtractionIsolate; bool _isTablet = false; + bool _isAndroidTV = false; /// PdfViewer theme data. SfPdfViewerThemeData? _pdfViewerThemeData; @@ -1288,33 +1327,39 @@ class SfPdfViewerState extends State with WidgetsBindingObserver { visible: _visibility, child: Center( child: Container( - height: 225, - width: 328, + height: 230, + width: 345, decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), color: (_themeData!.colorScheme.brightness == Brightness.light) ? Colors.white : const Color(0xFF424242)), child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: const EdgeInsets.fromLTRB(16, 14, 17, 15), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - _localizations!.passwordDialogHeaderTextLabel, - style: _pdfViewerThemeData! - .passwordDialogStyle?.headerTextStyle ?? - TextStyle( - fontFamily: 'Roboto', - fontSize: 20, - fontWeight: FontWeight.w500, - color: _themeData!.colorScheme.onSurface - .withOpacity(0.87), - ), + Row( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.only(left: 16, top: 10), + child: Text( + _localizations!.passwordDialogHeaderTextLabel, + style: _pdfViewerThemeData! + .passwordDialogStyle?.headerTextStyle ?? + TextStyle( + fontFamily: 'Roboto', + fontSize: 20, + fontWeight: FontWeight.w500, + color: _themeData!.colorScheme.onSurface + .withOpacity(0.87), + ), + ), ), - SizedBox( + ), + Padding( + padding: const EdgeInsets.fromLTRB(0, 10, 16, 0), + child: SizedBox( height: 36, width: 36, child: RawMaterialButton( @@ -1337,11 +1382,11 @@ class SfPdfViewerState extends State with WidgetsBindingObserver { ), ), ), - ], - ), + ), + ], ), Padding( - padding: const EdgeInsets.fromLTRB(0, 0, 0, 24), + padding: const EdgeInsets.fromLTRB(16, 0, 16, 8), child: Text( _localizations!.passwordDialogContentLabel, style: _pdfViewerThemeData! @@ -1355,109 +1400,13 @@ class SfPdfViewerState extends State with WidgetsBindingObserver { ), ), ), - SizedBox( - width: 296, - height: 70, - child: TextFormField( - style: _pdfViewerThemeData! - .passwordDialogStyle?.inputFieldTextStyle ?? - TextStyle( - fontFamily: 'Roboto', - fontSize: 17, - fontWeight: FontWeight.w400, - color: _themeData!.colorScheme.onSurface - .withOpacity(0.87), - ), - obscureText: _passwordVisible, - obscuringCharacter: '*', - decoration: InputDecoration( - border: OutlineInputBorder( - borderSide: BorderSide( - color: _pdfViewerThemeData! - .passwordDialogStyle?.inputFieldBorderColor ?? - _themeData!.colorScheme.primary, - )), - errorBorder: OutlineInputBorder( - borderSide: BorderSide( - color: _pdfViewerThemeData! - .passwordDialogStyle?.errorBorderColor ?? - _themeData!.colorScheme.error, - )), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: _pdfViewerThemeData! - .passwordDialogStyle?.inputFieldBorderColor ?? - _themeData!.colorScheme.primary, - )), - focusedErrorBorder: OutlineInputBorder( - borderSide: BorderSide( - color: _pdfViewerThemeData! - .passwordDialogStyle?.errorBorderColor ?? - _themeData!.colorScheme.error, - )), - hintText: _localizations!.passwordDialogHintTextLabel, - errorText: _errorTextPresent ? 'Invalid Password' : null, - hintStyle: _pdfViewerThemeData! - .passwordDialogStyle?.inputFieldHintTextStyle ?? - TextStyle( - fontFamily: 'Roboto', - fontSize: 16, - fontWeight: FontWeight.w400, - color: _themeData!.colorScheme.onSurface - .withOpacity(0.6), - ), - labelText: _localizations!.passwordDialogHintTextLabel, - labelStyle: _pdfViewerThemeData! - .passwordDialogStyle?.inputFieldLabelTextStyle ?? - TextStyle( - fontFamily: 'Roboto', - fontSize: 18, - fontWeight: FontWeight.w500, - color: _errorTextPresent - ? _themeData!.colorScheme.error - : _themeData!.colorScheme.onSurface - .withOpacity(0.87), - ), - errorStyle: _pdfViewerThemeData! - .passwordDialogStyle?.errorTextStyle ?? - TextStyle( - fontFamily: 'Roboto', - fontSize: 14, - fontWeight: FontWeight.w500, - color: _themeData!.colorScheme.error, - ), - suffixIcon: IconButton( - icon: Icon( - _passwordVisible - ? Icons.visibility - : Icons.visibility_off, - color: _pdfViewerThemeData! - .passwordDialogStyle?.visibleIconColor ?? - Theme.of(context) - .colorScheme - .onSurface - .withOpacity(0.6)), - onPressed: () { - setState(() { - _passwordVisible = !_passwordVisible; - }); - }), - ), - enableInteractiveSelection: false, - controller: _textFieldController, - autofocus: true, - focusNode: _focusNode, - textInputAction: TextInputAction.none, - onFieldSubmitted: (String value) { - _passwordValidation(value); - }, - ), - ), Padding( - padding: const EdgeInsets.fromLTRB(0, 0, 16, 14), + padding: const EdgeInsets.fromLTRB(16, 0, 16, 0), + child: _textField()), + Padding( + padding: const EdgeInsets.fromLTRB(0, 0, 16, 10), child: Row( mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.end, children: [ TextButton( onPressed: () { @@ -1507,6 +1456,96 @@ class SfPdfViewerState extends State with WidgetsBindingObserver { ); } + // TextFormField of password dialogue + Widget _textField() { + return SizedBox( + width: 296, + child: TextFormField( + style: _pdfViewerThemeData!.passwordDialogStyle?.inputFieldTextStyle ?? + TextStyle( + fontFamily: 'Roboto', + fontSize: 17, + fontWeight: FontWeight.w400, + color: _themeData!.colorScheme.onSurface.withOpacity(0.87), + ), + obscureText: _passwordVisible, + obscuringCharacter: '*', + decoration: InputDecoration( + border: OutlineInputBorder( + borderSide: BorderSide( + color: _pdfViewerThemeData! + .passwordDialogStyle?.inputFieldBorderColor ?? + _themeData!.colorScheme.primary, + )), + errorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: _pdfViewerThemeData!.passwordDialogStyle?.errorBorderColor ?? + _themeData!.colorScheme.error, + )), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: _pdfViewerThemeData! + .passwordDialogStyle?.inputFieldBorderColor ?? + _themeData!.colorScheme.primary, + )), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: _pdfViewerThemeData!.passwordDialogStyle?.errorBorderColor ?? + _themeData!.colorScheme.error, + )), + hintText: _localizations!.passwordDialogHintTextLabel, + errorText: _errorTextPresent ? 'Invalid Password' : null, + hintStyle: _pdfViewerThemeData! + .passwordDialogStyle?.inputFieldHintTextStyle ?? + TextStyle( + fontFamily: 'Roboto', + fontSize: 16, + fontWeight: FontWeight.w400, + color: _themeData!.colorScheme.onSurface.withOpacity(0.6), + ), + labelText: _localizations!.passwordDialogHintTextLabel, + labelStyle: _pdfViewerThemeData! + .passwordDialogStyle?.inputFieldLabelTextStyle ?? + TextStyle( + fontFamily: 'Roboto', + fontSize: 18, + fontWeight: FontWeight.w500, + color: _errorTextPresent + ? _themeData!.colorScheme.error + : _themeData!.colorScheme.onSurface.withOpacity(0.87), + ), + errorStyle: + _pdfViewerThemeData!.passwordDialogStyle?.errorTextStyle ?? + TextStyle( + fontFamily: 'Roboto', + fontSize: 14, + fontWeight: FontWeight.w500, + color: _themeData!.colorScheme.error, + ), + suffixIcon: IconButton( + icon: Icon( + _passwordVisible ? Icons.visibility : Icons.visibility_off, + color: _pdfViewerThemeData! + .passwordDialogStyle?.visibleIconColor ?? + Theme.of(context).colorScheme.onSurface.withOpacity(0.6)), + onPressed: () { + setState(() { + _passwordVisible = !_passwordVisible; + }); + }), + ), + enableInteractiveSelection: false, + controller: _textFieldController, + autofocus: true, + focusNode: _focusNode, + textInputAction: TextInputAction.none, + onFieldSubmitted: (String value) { + _passwordValidation(value); + }, + ), + ); + } + ///validate the password for encrypted document for web. void _passwordValidation(String password) { try { @@ -1553,24 +1592,27 @@ class SfPdfViewerState extends State with WidgetsBindingObserver { buttonPadding: orientation == Orientation.portrait ? const EdgeInsets.all(8) : const EdgeInsets.all(4), - backgroundColor: _pdfViewerThemeData!.backgroundColor ?? + backgroundColor: _pdfViewerThemeData! + .passwordDialogStyle!.backgroundColor ?? (Theme.of(context).colorScheme.brightness == Brightness.light ? Colors.white : const Color(0xFF424242)), title: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - _localizations!.passwordDialogHeaderTextLabel, - style: _pdfViewerThemeData! - .passwordDialogStyle?.headerTextStyle ?? - TextStyle( - fontFamily: 'Roboto', - fontSize: 20, - fontWeight: FontWeight.w500, - color: - _themeData!.colorScheme.onSurface.withOpacity(0.87), - ), + Expanded( + child: Text( + _localizations!.passwordDialogHeaderTextLabel, + style: _pdfViewerThemeData! + .passwordDialogStyle?.headerTextStyle ?? + TextStyle( + fontFamily: 'Roboto', + fontSize: 20, + fontWeight: FontWeight.w500, + color: _themeData!.colorScheme.onSurface + .withOpacity(0.87), + ), + ), ), SizedBox( height: 36, @@ -1881,14 +1923,23 @@ class SfPdfViewerState extends State with WidgetsBindingObserver { } /// Find whether device is mobile or tablet. - void _findDevice(BuildContext context) { + Future _findDevice(BuildContext context) async { /// Standard diagonal offset of tablet. const double kPdfStandardDiagonalOffset = 1100.0; final Size size = MediaQuery.of(context).size; final double diagonal = sqrt((size.width * size.width) + (size.height * size.height)); _isMobileView = diagonal < kPdfStandardDiagonalOffset; - _isTablet = diagonal > kPdfStandardDiagonalOffset; + if (!kIsDesktop && + !Platform.isIOS && + !Platform.environment.containsKey('FLUTTER_TEST')) { + final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); + final AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; + _isAndroidTV = + androidInfo.systemFeatures.contains('android.software.leanback'); + } + _isTablet = + _isAndroidTV ? !_isAndroidTV : diagonal > kPdfStandardDiagonalOffset; } /// Get the global rect of viewport region. @@ -2104,6 +2155,7 @@ class SfPdfViewerState extends State with WidgetsBindingObserver { _pdfPages, index, _pdfViewerController, + widget.maxZoomLevel, widget.enableDocumentLinkAnnotation, widget.enableTextSelection, widget.onTextSelectionChanged, @@ -2126,6 +2178,7 @@ class SfPdfViewerState extends State with WidgetsBindingObserver { _textDirection, widget.canShowHyperlinkDialog, widget.enableHyperlinkNavigation, + _isAndroidTV, ); final double pageSpacing = index == _pdfViewerController.pageCount - 1 @@ -2222,6 +2275,7 @@ class SfPdfViewerState extends State with WidgetsBindingObserver { _handleSinglePageViewPageChanged, _interactionUpdate, viewportDimension, + widget.maxZoomLevel, widget.canShowPaginationDialog, widget.canShowScrollHead, widget.canShowScrollStatus, @@ -2304,7 +2358,7 @@ class SfPdfViewerState extends State with WidgetsBindingObserver { viewportDimension, _handlePdfOffsetChanged, _panEnabled, - _maxScale, + widget.maxZoomLevel, _minScale, widget.enableDoubleTapZooming, widget.interactionMode, @@ -2411,8 +2465,8 @@ class SfPdfViewerState extends State with WidgetsBindingObserver { totalImageWidth = currentPageSize.width * zoomLevel; } childWidth = viewportDimension.width > totalImageWidth - ? viewportDimension.width / widthFactor.clamp(1, 3) - : totalImageWidth / widthFactor.clamp(1, 3); + ? viewportDimension.width / widthFactor.clamp(1, widget.maxZoomLevel) + : totalImageWidth / widthFactor.clamp(1, widget.maxZoomLevel); double totalImageHeight = currentPageSize.height * zoomLevel; if (_scrollDirection == PdfScrollDirection.vertical) { @@ -2421,8 +2475,9 @@ class SfPdfViewerState extends State with WidgetsBindingObserver { zoomLevel; } childHeight = viewportDimension.height > totalImageHeight - ? viewportDimension.height / heightFactor.clamp(1, 3) - : totalImageHeight / heightFactor.clamp(1, 3); + ? viewportDimension.height / + heightFactor.clamp(1, widget.maxZoomLevel) + : totalImageHeight / heightFactor.clamp(1, widget.maxZoomLevel); _totalImageSize = Size(totalImageWidth / zoomLevel, totalImageHeight / zoomLevel); if (_isMobileView && @@ -3214,8 +3269,8 @@ class SfPdfViewerState extends State with WidgetsBindingObserver { _jumpToBookmark(_pdfViewerController._pdfBookmark); } } else if (property == 'zoomLevel') { - if (_pdfViewerController.zoomLevel > _maxScale) { - _pdfViewerController.zoomLevel = _maxScale; + if (_pdfViewerController.zoomLevel > widget.maxZoomLevel) { + _pdfViewerController.zoomLevel = widget.maxZoomLevel; } else if (_pdfViewerController.zoomLevel < _minScale) { _pdfViewerController.zoomLevel = _minScale; } @@ -3624,11 +3679,6 @@ class SfPdfViewerState extends State with WidgetsBindingObserver { ?.currentState ?.focusNode .requestFocus(); - if (_isTablet) { - _pdfScrollableStateKey - .currentState?.scrollHeadStateKey.currentState?.focusNode - .requestFocus(); - } } if (widget.pageLayoutMode == PdfPageLayoutMode.continuous) { if (_scrollDirection == PdfScrollDirection.horizontal) { diff --git a/packages/syncfusion_flutter_pdfviewer/pubspec.yaml b/packages/syncfusion_flutter_pdfviewer/pubspec.yaml index cae5236cc..c442d319d 100644 --- a/packages/syncfusion_flutter_pdfviewer/pubspec.yaml +++ b/packages/syncfusion_flutter_pdfviewer/pubspec.yaml @@ -1,6 +1,6 @@ name: syncfusion_flutter_pdfviewer description: Flutter PDF Viewer library is used to display a PDF document seamlessly and efficiently. -version: 20.4.38 +version: 21.1.35 homepage: https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_flutter_pdfviewer environment: @@ -34,19 +34,20 @@ dependencies: async: ^2.5.0 http: ^0.13.0 uuid: ^3.0.5 + device_info_plus: ^8.1.0 intl: ">=0.17.0 <0.20.0" - syncfusion_pdfviewer_platform_interface: ^20.4.38 + syncfusion_pdfviewer_platform_interface: ^21.1.35 - syncfusion_pdfviewer_web: ^20.4.38 + syncfusion_pdfviewer_web: ^21.1.35 - syncfusion_pdfviewer_macos: ^20.4.38 + syncfusion_pdfviewer_macos: ^21.1.35 - syncfusion_pdfviewer_windows: ^20.4.38 + syncfusion_pdfviewer_windows: ^21.1.35 - syncfusion_flutter_core: ^20.4.38 + syncfusion_flutter_core: ^21.1.35 - syncfusion_flutter_pdf: ^20.4.38 + syncfusion_flutter_pdf: ^21.1.35 url_launcher: ^6.1.0 diff --git a/packages/syncfusion_flutter_signaturepad/lib/signaturepad.dart b/packages/syncfusion_flutter_signaturepad/lib/signaturepad.dart index efad4507e..867d4847b 100644 --- a/packages/syncfusion_flutter_signaturepad/lib/signaturepad.dart +++ b/packages/syncfusion_flutter_signaturepad/lib/signaturepad.dart @@ -5,6 +5,7 @@ import 'dart:ui' as ui; import 'package:flutter/gestures.dart' show + DeviceGestureSettings, DragStartBehavior, GestureArenaTeam, HorizontalDragGestureRecognizer, @@ -489,14 +490,14 @@ class _SfSignaturePadRenderObjectWidget extends LeafRenderObjectWidget { @override RenderObject createRenderObject(BuildContext context) { return RenderSignaturePad( - minimumStrokeWidth: minimumStrokeWidth, - maximumStrokeWidth: maximumStrokeWidth, - backgroundColor: backgroundColor, - strokeColor: strokeColor, - onDrawEnd: onDrawEnd, - onDraw: onDraw, - onDrawStart: onDrawStart, - ); + minimumStrokeWidth: minimumStrokeWidth, + maximumStrokeWidth: maximumStrokeWidth, + backgroundColor: backgroundColor, + strokeColor: strokeColor, + onDrawEnd: onDrawEnd, + onDraw: onDraw, + onDrawStart: onDrawStart, + gestureSettings: MediaQuery.of(context).gestureSettings); } @override @@ -521,6 +522,7 @@ class RenderSignaturePad extends RenderBox { required double maximumStrokeWidth, required Color backgroundColor, required Color strokeColor, + required DeviceGestureSettings gestureSettings, SignatureOnDrawStartCallback? onDrawStart, SignatureDrawCallback? onDraw, VoidCallback? onDrawEnd}) @@ -537,14 +539,17 @@ class RenderSignaturePad extends RenderBox { ..onStart = _handleDragStart ..onUpdate = _handleDragUpdate ..onEnd = _handleDragEnd + ..gestureSettings = gestureSettings ..dragStartBehavior = DragStartBehavior.down; _verticalDragGestureRecognizer = VerticalDragGestureRecognizer() ..team = _gestureArenaTeam + ..gestureSettings = gestureSettings ..onStart = _dragStart; _horizontalDragGestureRecognizer = HorizontalDragGestureRecognizer() ..team = _gestureArenaTeam + ..gestureSettings = gestureSettings ..onStart = _dragStart; _tapGestureRecognizer = TapGestureRecognizer()..onTapUp = _handleTapUp; diff --git a/packages/syncfusion_flutter_signaturepad/pubspec.yaml b/packages/syncfusion_flutter_signaturepad/pubspec.yaml index 51ad6bcbc..e6d00addd 100644 --- a/packages/syncfusion_flutter_signaturepad/pubspec.yaml +++ b/packages/syncfusion_flutter_signaturepad/pubspec.yaml @@ -1,6 +1,6 @@ name: syncfusion_flutter_signaturepad description: The Flutter Signature Pad widget allows you to capture smooth and more realistic signatures through drawn gestures and save it as an image. -version: 20.4.38 +version: 21.1.35 homepage: https://github.com/syncfusion/flutter-examples environment: @@ -9,7 +9,7 @@ environment: dependencies: flutter: sdk: flutter - syncfusion_flutter_core: ^20.4.38 + syncfusion_flutter_core: ^21.1.35 dev_dependencies: flutter_test: diff --git a/packages/syncfusion_flutter_sliders/CHANGELOG.md b/packages/syncfusion_flutter_sliders/CHANGELOG.md index ca8b9e725..fabf73200 100644 --- a/packages/syncfusion_flutter_sliders/CHANGELOG.md +++ b/packages/syncfusion_flutter_sliders/CHANGELOG.md @@ -1,4 +1,5 @@ ## Unreleased +## [20.3.60] - 12/06/2022 ## Range Slider diff --git a/packages/syncfusion_flutter_sliders/pubspec.yaml b/packages/syncfusion_flutter_sliders/pubspec.yaml index c8af92ef8..37b286d8f 100644 --- a/packages/syncfusion_flutter_sliders/pubspec.yaml +++ b/packages/syncfusion_flutter_sliders/pubspec.yaml @@ -1,6 +1,6 @@ name: syncfusion_flutter_sliders description: A Flutter Sliders library for creating highly customizable and UI-rich slider, range slider, and range selector widgets for filtering purposes. -version: 20.4.38 +version: 21.1.35 homepage: https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_flutter_sliders environment: @@ -12,7 +12,7 @@ dependencies: intl: ">=0.17.0 <0.20.0" - syncfusion_flutter_core: ^20.4.38 + syncfusion_flutter_core: ^21.1.35 dev_dependencies: flutter_test: diff --git a/packages/syncfusion_flutter_treemap/pubspec.yaml b/packages/syncfusion_flutter_treemap/pubspec.yaml index 2e1f23380..b89b5ab06 100644 --- a/packages/syncfusion_flutter_treemap/pubspec.yaml +++ b/packages/syncfusion_flutter_treemap/pubspec.yaml @@ -1,6 +1,6 @@ name: syncfusion_flutter_treemap description: A Flutter Treemap library for creating interactive treemap to visualize flat and hierarchical data based on squarified, slice, and dice algorithms. -version: 20.4.38-beta +version: 21.1.35 homepage: https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_flutter_treemap environment: @@ -10,7 +10,7 @@ dependencies: flutter: sdk: flutter - syncfusion_flutter_core: ^20.4.38 + syncfusion_flutter_core: ^21.1.35 dev_dependencies: flutter_test: diff --git a/packages/syncfusion_flutter_xlsio/lib/src/xlsio/general/chart_helper.dart b/packages/syncfusion_flutter_xlsio/lib/src/xlsio/general/chart_helper.dart index de07e4dfb..110b5ebe7 100644 --- a/packages/syncfusion_flutter_xlsio/lib/src/xlsio/general/chart_helper.dart +++ b/packages/syncfusion_flutter_xlsio/lib/src/xlsio/general/chart_helper.dart @@ -3,7 +3,13 @@ part of xlsio; /// Base class for charts abstract class ChartHelper { /// Serialize the charts. - void serializeCharts(Worksheet sheet); + void serializeChartsSync(Worksheet sheet); + + /// Serialize the charts. + Future serializeCharts(Worksheet sheet); + + /// Serialize the chart drawings + void serializeChartDrawingSync(XmlBuilder builder, Worksheet sheet); /// Serialize the chart drawings void serializeChartDrawing(XmlBuilder builder, Worksheet sheet); diff --git a/packages/syncfusion_flutter_xlsio/lib/src/xlsio/general/serialize_workbook.dart b/packages/syncfusion_flutter_xlsio/lib/src/xlsio/general/serialize_workbook.dart index 50e4f4cab..15f31bc96 100644 --- a/packages/syncfusion_flutter_xlsio/lib/src/xlsio/general/serialize_workbook.dart +++ b/packages/syncfusion_flutter_xlsio/lib/src/xlsio/general/serialize_workbook.dart @@ -66,6 +66,29 @@ class SerializeWorkbook { _saveWorkbookRelation(); } + Future _saveInternalAsync() async { + await _updateGlobalStylesAsync().then((_) async { + await _saveWorkbookAsync().then((_) async { + await _saveWorksheetsAsync().then((_) async { + await _saveSharedStringAsync().then((_) async { + await _saveStylesAsync().then((_) async { + await _saveAppAsync(_workbook.builtInProperties).then((_) async { + await _saveCoreAsync(_workbook.builtInProperties) + .then((_) async { + await _saveContentTypeAsync().then((_) async { + await _saveTopLevelRelationAsync().then((_) async { + await _saveWorkbookRelationAsync(); + }); + }); + }); + }); + }); + }); + }); + }); + }); + } + /// Serialize workbook. void _saveWorkbook() { final XmlBuilder builder = XmlBuilder(); @@ -139,6 +162,78 @@ class SerializeWorkbook { _addToArchive(bytes, 'xl/workbook.xml'); } + Future _saveWorkbookAsync() async { + final XmlBuilder builder = XmlBuilder(); + + builder.processing('xml', 'version="1.0"'); + builder.element('workbook', nest: () async { + builder.namespace( + 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'); + builder.attribute('xmlns:r', + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + + builder.element('workbookPr', nest: () async { + builder.attribute('codeName', 'ThisWorkbook'); + builder.attribute('defaultThemeVersion', '153222'); + }); + _serializeWorkbookProtectionAsync(builder); + builder.element('bookViews', nest: () async { + builder.element('workbookView', nest: () async { + int firstsheet = 0; + if (_workbook.worksheets[0].visibility == + WorksheetVisibility.hidden) { + for (int i = 1; i < _workbook.worksheets.count; i++) { + if (_workbook.worksheets[i].visibility == + WorksheetVisibility.visible) { + firstsheet = i; + break; + } + } + } + builder.attribute('activeTab', firstsheet.toString()); + }); + }); + builder.element('sheets', nest: () async { + for (int i = 0; i < _workbook.worksheets.count; i++) { + builder.element('sheet', nest: () async { + builder.attribute('name', _workbook.worksheets[i].name); + builder.attribute( + 'sheetId', (_workbook.worksheets[i].index).toString()); + if (_workbook.worksheets[i].visibility == + WorksheetVisibility.hidden) { + builder.attribute('state', 'hidden'); + } + builder.attribute('r:id', 'rId${i + 1}'); + }); + } + }); + final List list = _workbook.innerNamesCollection; + if (list.isNotEmpty) { + builder.element('definedNames', nest: () async { + for (int i = 0; i < list.length; i++) { + builder.element('definedName', nest: () async { + builder.attribute('name', list[i].name); + builder.attribute('comment', list[i].description); + if (list[i].isLocal) { + builder.attribute( + 'localSheetId', + _getLocalSheetIndexAsync(_workbook, list[i]).toString(), + ); + } + if (!list[i].isVisible) { + builder.attribute('hidden', '1'); + } + builder.text(list[i].value); + }); + } + }); + } + }); + final String stringXml = builder.buildDocument().toString(); + final List bytes = utf8.encode(stringXml); + _addToArchiveAsync(bytes, 'xl/workbook.xml'); + } + /// Get local sSheet id. int _getLocalSheetIndex(Workbook workbook, Name list) { int result = -1; @@ -150,6 +245,16 @@ class SerializeWorkbook { return result; } + Future _getLocalSheetIndexAsync(Workbook workbook, Name list) async { + int result = -1; + for (int i = 0; i < workbook.worksheets.count; i++) { + if (workbook.worksheets[i].name == list.scope) { + result = i; + } + } + return result; + } + /// Serializes workbook protection options. void _serializeWorkbookProtection(XmlBuilder builder) { if (_workbook._bWindowProtect || _workbook._bCellProtect) { @@ -168,6 +273,23 @@ class SerializeWorkbook { } } + Future _serializeWorkbookProtectionAsync(XmlBuilder builder) async { + if (_workbook._bWindowProtect || _workbook._bCellProtect) { + builder.element('workbookProtection', nest: () async { + if (_workbook._password != null) { + final int usPassword = _workbook._isPassword; + if (usPassword != 0) { + builder.attribute('workbookPassword', usPassword.toRadixString(16)); + } + } + _serializeAttributesAsync( + builder, 'lockStructure', _workbook._bCellProtect, false); + _serializeAttributesAsync( + builder, 'lockWindows', _workbook._bWindowProtect, false); + }); + } + } + /// serialize Attributes void _serializeAttributes( XmlBuilder builder, String attributeName, bool value, bool defaultValue) { @@ -180,6 +302,17 @@ class SerializeWorkbook { } } + Future _serializeAttributesAsync(XmlBuilder builder, + String attributeName, bool value, bool defaultValue) async { + String? strValue; + if (value != defaultValue) { + strValue = value ? '1' : '0'; + } + if (strValue != null) { + builder.attribute(attributeName, strValue); + } + } + /// Serializes attribute if it differs from default value. void _serializeAttributeInt( XmlBuilder builder, String attributeName, int value, int defaultValue) { @@ -189,6 +322,14 @@ class SerializeWorkbook { } } + Future _serializeAttributeIntAsync(XmlBuilder builder, + String attributeName, int value, int defaultValue) async { + if (value != defaultValue) { + final String strValue = value.toString(); + builder.attribute(attributeName, strValue); + } + } + /// Serialize worksheets. void _saveWorksheets() { final int length = _workbook.worksheets.count; @@ -199,6 +340,15 @@ class SerializeWorkbook { } } + Future _saveWorksheetsAsync() async { + final int length = _workbook.worksheets.count; + for (int i = 0; i < length; i++) { + final Worksheet worksheet = _workbook.worksheets[i]; + _updateHyperlinkCellsAsync(worksheet); + _saveWorksheetAsync(worksheet, i); + } + } + /// Update the Hyperlink cells void _updateHyperlinkCells(Worksheet worksheet) { for (final Hyperlink hyperlink in worksheet.hyperlinks.innerList) { @@ -222,6 +372,28 @@ class SerializeWorkbook { } } + Future _updateHyperlinkCellsAsync(Worksheet worksheet) async { + for (final Hyperlink hyperlink in worksheet.hyperlinks.innerList) { + final Row? row = worksheet.rows._getRow(hyperlink._row); + if (row != null) { + final Range? cell = row.ranges._getCell(hyperlink._column); + if (cell != null) { + if (hyperlink.textToDisplay != null && + cell.number == null && + cell.text == null) { + cell.value = hyperlink.textToDisplay; + } else if (cell.text != null) { + cell.value = cell.text; + } else if (cell.number != null) { + cell.value = cell.number; + } else { + cell.value = hyperlink.address; + } + } + } + } + } + /// Serialize worksheet. void _saveWorksheet(Worksheet sheet, int index) { final XmlBuilder builder = XmlBuilder(); @@ -467,7 +639,7 @@ class SerializeWorkbook { _workbook._drawingCount++; _saveDrawings(sheet); if (sheet.charts != null && sheet.chartCount > 0) { - sheet.charts!.serializeCharts(sheet); + sheet.charts!.serializeChartsSync(sheet); } builder.element('drawing', nest: () { int id = 1; @@ -505,154 +677,559 @@ class SerializeWorkbook { _addToArchive(bytes, 'xl/worksheets/sheet${index + 1}.xml'); } - /// Serializes worksheet printOptions - void _serializePrintOptions(XmlBuilder builder, Worksheet sheet) { - builder.element('printOptions', nest: () { - if (sheet.pageSetup.isCenterHorizontally) { - builder.attribute('horizontalCentered', '1'); - } - if (sheet.pageSetup.isCenterVertically) { - builder.attribute('verticalCentered', '1'); - } - if (sheet.pageSetup.showHeadings) { - builder.attribute('headings', '1'); - } - if (sheet.pageSetup.showGridlines) { - builder.attribute('gridLines', '1'); - } - }); - } + Future _saveWorksheetAsync(Worksheet sheet, int index) async { + final XmlBuilder builder = XmlBuilder(); - /// Validate whether Page margins are fit into the Page - void _validatePageMargins(Worksheet sheet) { - if ((sheet.pageSetup as _PageSetupImpl) - ._paperHight - .containsKey(sheet.pageSetup.paperSize) && - (sheet.pageSetup as _PageSetupImpl) - ._paperWidth - .containsKey(sheet.pageSetup.paperSize)) { - final double maxHight = - sheet.pageSetup.topMargin + sheet.pageSetup.bottomMargin; - final double maxWidth = - sheet.pageSetup.leftMargin + sheet.pageSetup.rightMargin; - (sheet.pageSetup as _PageSetupImpl) - ._paperHight - .forEach((ExcelPaperSize key, double value) { - if (sheet.pageSetup.paperSize == key) { - if (maxHight > value) { - throw Exception( - 'Top Margin and Bottom Margin size exceeds the allowed size'); - } + builder.processing('xml', 'version="1.0"'); + builder.element('worksheet', nest: () async { + builder.namespace( + 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'); + builder.attribute('xmlns:r', + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + builder.attribute('xmlns:x14', + 'http://schemas.microsoft.com/office/spreadsheetml/2009/9/main'); + builder.attribute('xmlns:mc', + 'http://schemas.openxmlformats.org/markup-compatibility/2006'); + + builder.element('sheetPr', nest: () async { + if (!sheet._isSummaryRowBelow) { + builder.element('OutlinePr', nest: () async { + builder.attribute('summaryBelow', '0'); + }); } - }); - (sheet.pageSetup as _PageSetupImpl) - ._paperWidth - .forEach((ExcelPaperSize key, double value) { - if (sheet.pageSetup.paperSize == key) { - if (maxWidth > value) { - throw Exception( - 'Left Margin and Right Margin size exceeds the allowed size'); - } + if (sheet.pageSetup.isFitToPage) { + builder.element('pageSetUpPr', nest: () async { + builder.attribute('fitToPage', '1'); + }); + } + if (sheet._isTapColorApplied) { + _serializeTabColorAsync(sheet, builder); } }); - } - } - - /// Serializes pageMargins - void _serializePageMargins(XmlBuilder builder, Worksheet sheet) { - builder.element('pageMargins', nest: () { - builder.attribute('left', sheet.pageSetup.leftMargin.toString()); - builder.attribute('right', sheet.pageSetup.rightMargin.toString()); - builder.attribute('top', sheet.pageSetup.topMargin.toString()); - builder.attribute('bottom', sheet.pageSetup.bottomMargin.toString()); - builder.attribute('header', sheet.pageSetup.headerMargin.toString()); - builder.attribute('footer', sheet.pageSetup.footerMargin.toString()); - }); - } - - /// Serialize pageSetup - void _serializePageSetup(XmlBuilder builder, Worksheet sheet) { - builder.element('pageSetup', nest: () { - _serializePaperSize(builder, sheet); + _saveSheetViewAsync(sheet, builder); + builder.element('sheetFormatPr', nest: () async { + builder.attribute('defaultRowHeight', sheet._standardHeight.toString()); + }); - if (sheet.pageSetup.firstPageNumber != 1) { - builder.attribute( - 'firstPageNumber', sheet.pageSetup.firstPageNumber.toString()); - } - if (sheet.pageSetup.fitToPagesWide > 1) { - builder.attribute( - 'fitToWidth', sheet.pageSetup.fitToPagesWide.toString()); + if (sheet.columns.count != 0) { + builder.element('cols', nest: () async { + for (int i = 1; i <= sheet.columns.count; i++) { + final Column? column = sheet.columns[i]; + if (column != null) { + builder.element('col', nest: () async { + final int iLastColumn = + _findSameColumns(sheet, column.index, _workbook); + builder.attribute('min', column.index.toString()); + builder.attribute('max', iLastColumn.toString()); + i = iLastColumn; + if (column.width != 0) { + final double dWidth = + sheet._evaluateFileColumnWidth(column.width); + builder.attribute('width', dWidth.toString()); + } else { + builder.attribute('width', '8.43'); + } + if (column._isHidden) { + builder.attribute('hidden', '1'); + } + builder.attribute('customWidth', '1'); + }); + } + } + }); } - if (sheet.pageSetup.fitToPagesTall > 1) { - builder.attribute( - 'fitToHeight', sheet.pageSetup.fitToPagesTall.toString()); + builder.element('sheetData', nest: () async { + if (sheet.rows.count != 0) { + for (final Row? row in sheet.rows.innerList) { + if (row != null) { + builder.element('row', nest: () async { + builder.attribute('r', row.index.toString()); + if (row.height != 0) { + builder.attribute('ht', row.height.toString()); + builder.attribute('customHeight', '1'); + } + if (row._isHidden) { + builder.attribute('hidden', '1'); + } + if (row.ranges.count != 0) { + for (final Range? cell in row.ranges.innerList) { + if (cell != null) { + if (cell.rowSpan > 0) { + cell.rowSpan -= 1; + } + if (cell.columnSpan > 0) { + cell.columnSpan -= 1; + } + sheet.mergeCells = + _processMergeCells(cell, row.index, sheet.mergeCells); + + if (cell._cellStyle != null || !cell.isDefaultFormat) { + cell._styleIndex = cell.cellStyle.index = + _processCellStyle( + cell.cellStyle as CellStyle, _workbook); + } else { + cell._styleIndex = -1; + } + builder.element('c', nest: () async { + String? strFormula = cell.formula; + builder.attribute('r', cell.addressLocal); + if (cell._saveType != '' && + (strFormula == null || + strFormula == '' || + strFormula[0] != '=' || + cell._saveType == 's')) { + builder.attribute('t', cell._saveType); + } + if (cell._styleIndex > 0) { + builder.attribute('s', cell._styleIndex.toString()); + } + String? cellValue; + if (sheet.calcEngine != null && + cell.number == null && + cell.text == null && + cell._boolean == '' && + cell._errorValue == '') { + cellValue = cell.calculatedValue; + } else if (cell._errorValue != '') { + cellValue = cell._errorValue; + } else if (cell._boolean != '') { + cellValue = cell._boolean; + } else if (cell.number != null) { + cellValue = cell.number.toString(); + } else if (cell.text != null) { + if (cell._saveType == 's' && cell._textIndex != -1) { + cellValue = cell._textIndex.toString(); + } else { + cellValue = cell.text; + } + } + if (strFormula != null && + strFormula != '' && + strFormula[0] == '=' && + cell._saveType != 's') { + if (cell._saveType != '') { + builder.attribute('t', cell._saveType); + } + strFormula = + strFormula.substring(1).replaceAll("'", '"'); + final int i = strFormula.indexOf('!'); + if (i != -1 && + strFormula[0] == '"' && + strFormula[i - 1] == '"') { + final String sheetName = + strFormula.substring(1, i - 1); + strFormula = + "'$sheetName'${strFormula.substring(i)}"; + } + if (strFormula.contains('MINIFS')) { + strFormula = strFormula.replaceAllMapped( + RegExp('MINIFS'), + (Match match) => '_xlfn.${match.group(0)}'); + } else if (strFormula.contains('MAXIFS')) { + strFormula = strFormula.replaceAllMapped( + RegExp('MAXIFS'), + (Match match) => '_xlfn.${match.group(0)}'); + } + builder.element('f', nest: strFormula); + } + if (cellValue != null && + (cell._saveType == 'str' || cellValue.isNotEmpty)) { + builder.element('v', nest: cellValue); + } + }); + } + } + } + }); + } + } + } + }); + if (sheet._isPasswordProtected) { + builder.element('sheetProtection', nest: () async { + if (sheet._algorithmName != null) { + // ignore: unnecessary_null_checks + builder.attribute('algorithmName', sheet._algorithmName!); + builder.attribute('hashValue', base64.encode(sheet._hashValue)); + builder.attribute('saltValue', base64.encode(sheet._saltValue)); + builder.attribute('spinCount', sheet._spinCount); + } else if (sheet._isPassword != 1) { + final String password = sheet._isPassword.toRadixString(16); + builder.attribute('password', password); + } + List attributes; + List flags; + List defaultValues; + attributes = sheet._protectionAttributes; + flags = sheet._flag; + defaultValues = sheet._defaultValues; + // ignore: prefer_final_locals + for (int i = 0, iCount = attributes.length; i < iCount; i++) { + _serializeProtectionAttributeAsync( + builder, attributes[i], flags[i], defaultValues[i]); + } + }); } - if (sheet.pageSetup.order == ExcelPageOrder.overThenDown) { - builder.attribute('pageOrder', 'overThenDown'); + //Serializing AutoFilter + _serializeAutoFiltersAsync(builder, sheet.autoFilters); + if (sheet.mergeCells.innerList.isNotEmpty) { + builder.element('mergeCells', nest: () async { + builder.attribute( + 'count', sheet.mergeCells.innerList.length.toString()); + for (final MergeCell mCell in sheet.mergeCells.innerList) { + builder.element('mergeCell', nest: () async { + builder.attribute('ref', mCell._reference); + }); + } + }); } - if (sheet.pageSetup.orientation == ExcelPageOrientation.landscape) { - builder.attribute('orientation', 'landscape'); - } else { - builder.attribute('orientation', 'portrait'); + _serializeConditionalFormattingAsync(builder, sheet); + _serializeDataValidationsAsync(builder, sheet); + _serializeHyperlinksAsync(builder, sheet); + if (sheet.pageSetup.showGridlines || + sheet.pageSetup.showHeadings || + sheet.pageSetup.isCenterHorizontally || + sheet.pageSetup.isCenterVertically) { + _serializePrintOptionsAsync(builder, sheet); } - if (sheet.pageSetup.isBlackAndWhite) { - builder.attribute('blackAndWhite', '1'); + _validatePageMarginsAsync(sheet); + _serializePageMarginsAsync(builder, sheet); + if (sheet.pageSetup.order == ExcelPageOrder.overThenDown || + sheet.pageSetup.orientation == ExcelPageOrientation.landscape || + sheet.pageSetup.printErrors != CellErrorPrintOptions.displayed || + sheet.pageSetup.isBlackAndWhite || + sheet.pageSetup.isDraft || + (sheet.pageSetup.printQuality != 0 && + sheet.pageSetup.printQuality != 600) || + !sheet.pageSetup.autoFirstPageNumber || + sheet.pageSetup.fitToPagesWide > 1 || + sheet.pageSetup.fitToPagesTall > 1 || + sheet.pageSetup.firstPageNumber != 1 || + sheet.pageSetup.paperSize != ExcelPaperSize.paperA4) { + _serializePageSetupAsync(builder, sheet); } - if (sheet.pageSetup.isDraft) { - builder.attribute('draft', '1'); + builder.element('headerFooter', nest: () async { + builder.attribute('scaleWithDoc', '1'); + builder.attribute('alignWithMargins', '0'); + builder.attribute('differentFirst', '0'); + builder.attribute('differentOddEven', '0'); + }); + + if ((sheet.pictures.count > 0) || + (sheet.charts != null && sheet.chartCount > 0)) { + _workbook._drawingCount++; + _saveDrawingsAsync(sheet); + if (sheet.charts != null && sheet.chartCount > 0) { + sheet.charts!.serializeCharts(sheet); + } + builder.element('drawing', nest: () async { + int id = 1; + const String rId = 'rId1'; + if (_relationId.isNotEmpty) { + if (sheet.hyperlinks.count > 0) { + for (int i = 0; i < sheet.hyperlinks.count; i++) { + if (sheet.hyperlinks[i]._attachedType == + ExcelHyperlinkAttachedType.range && + sheet.hyperlinks[i].type != HyperlinkType.workbook) { + id++; + } + } + } + builder.attribute('r:id', 'rId$id'); + } else { + builder.attribute('r:id', rId); + } + }); } - if (!sheet.pageSetup.autoFirstPageNumber) { - builder.attribute('useFirstPageNumber', '1'); + final _TableSerialization tableSerialization = + _TableSerialization(_workbook); + tableSerialization._serializeTablesAsync(builder, sheet); + builder.element('extLst', nest: () async { + _serializeConditionalFormattingExtAsync(builder, sheet); + }); + _addToArchiveAsync(_saveSheetRelations(sheet), + 'xl/worksheets/_rels/sheet${index + 1}.xml.rels'); + }); + final String stringXml = builder.buildDocument().toString(); + final List bytes = utf8.encode(stringXml); + _addToArchiveAsync(bytes, 'xl/worksheets/sheet${index + 1}.xml'); + } + + /// Serializes worksheet printOptions + void _serializePrintOptions(XmlBuilder builder, Worksheet sheet) { + builder.element('printOptions', nest: () { + if (sheet.pageSetup.isCenterHorizontally) { + builder.attribute('horizontalCentered', '1'); } - if (sheet.pageSetup.printErrors != CellErrorPrintOptions.displayed) { - switch (sheet.pageSetup.printErrors) { - case CellErrorPrintOptions.blank: - builder.attribute('errors', 'blank'); - break; - case CellErrorPrintOptions.dash: - builder.attribute('errors', 'dash'); - break; - case CellErrorPrintOptions.notAvailable: - builder.attribute('errors', 'NA'); - break; - case CellErrorPrintOptions.displayed: - break; - } + if (sheet.pageSetup.isCenterVertically) { + builder.attribute('verticalCentered', '1'); } - if (sheet.pageSetup.printQuality != 0 && - sheet.pageSetup.printQuality != 600) { - if (sheet.pageSetup.printQuality <= 38528) { - builder.attribute( - 'horizontalDpi', sheet.pageSetup.printQuality.toString()); - builder.attribute( - 'verticalDpi', sheet.pageSetup.printQuality.toString()); - } else { - builder.attribute('horizontalDpi', '38528'); - builder.attribute('verticalDpi', '38528'); - } + if (sheet.pageSetup.showHeadings) { + builder.attribute('headings', '1'); + } + if (sheet.pageSetup.showGridlines) { + builder.attribute('gridLines', '1'); } }); } - /// Serialize paperSize - void _serializePaperSize(XmlBuilder builder, Worksheet sheet) { - switch (sheet.pageSetup.paperSize) { - case ExcelPaperSize.a2Paper: - builder.attribute('paperSize', '66'); - break; - case ExcelPaperSize.paperDsheet: - builder.attribute('paperSize', '25'); - break; - case ExcelPaperSize.paperEnvelope10: - builder.attribute('paperSize', '20'); - break; - case ExcelPaperSize.paperEnvelope11: - builder.attribute('paperSize', '21'); - break; - case ExcelPaperSize.paperEnvelope12: - builder.attribute('paperSize', '22'); - break; - case ExcelPaperSize.paperEnvelope14: + Future _serializePrintOptionsAsync( + XmlBuilder builder, Worksheet sheet) async { + builder.element('printOptions', nest: () async { + if (sheet.pageSetup.isCenterHorizontally) { + builder.attribute('horizontalCentered', '1'); + } + if (sheet.pageSetup.isCenterVertically) { + builder.attribute('verticalCentered', '1'); + } + if (sheet.pageSetup.showHeadings) { + builder.attribute('headings', '1'); + } + if (sheet.pageSetup.showGridlines) { + builder.attribute('gridLines', '1'); + } + }); + } + + /// Validate whether Page margins are fit into the Page + void _validatePageMargins(Worksheet sheet) { + if ((sheet.pageSetup as _PageSetupImpl) + ._paperHight + .containsKey(sheet.pageSetup.paperSize) && + (sheet.pageSetup as _PageSetupImpl) + ._paperWidth + .containsKey(sheet.pageSetup.paperSize)) { + final double maxHight = + sheet.pageSetup.topMargin + sheet.pageSetup.bottomMargin; + final double maxWidth = + sheet.pageSetup.leftMargin + sheet.pageSetup.rightMargin; + (sheet.pageSetup as _PageSetupImpl) + ._paperHight + .forEach((ExcelPaperSize key, double value) { + if (sheet.pageSetup.paperSize == key) { + if (maxHight > value) { + throw Exception( + 'Top Margin and Bottom Margin size exceeds the allowed size'); + } + } + }); + (sheet.pageSetup as _PageSetupImpl) + ._paperWidth + .forEach((ExcelPaperSize key, double value) { + if (sheet.pageSetup.paperSize == key) { + if (maxWidth > value) { + throw Exception( + 'Left Margin and Right Margin size exceeds the allowed size'); + } + } + }); + } + } + + Future _validatePageMarginsAsync(Worksheet sheet) async { + if ((sheet.pageSetup as _PageSetupImpl) + ._paperHight + .containsKey(sheet.pageSetup.paperSize) && + (sheet.pageSetup as _PageSetupImpl) + ._paperWidth + .containsKey(sheet.pageSetup.paperSize)) { + final double maxHight = + sheet.pageSetup.topMargin + sheet.pageSetup.bottomMargin; + final double maxWidth = + sheet.pageSetup.leftMargin + sheet.pageSetup.rightMargin; + (sheet.pageSetup as _PageSetupImpl) + ._paperHight + .forEach((ExcelPaperSize key, double value) { + if (sheet.pageSetup.paperSize == key) { + if (maxHight > value) { + throw Exception( + 'Top Margin and Bottom Margin size exceeds the allowed size'); + } + } + }); + (sheet.pageSetup as _PageSetupImpl) + ._paperWidth + .forEach((ExcelPaperSize key, double value) { + if (sheet.pageSetup.paperSize == key) { + if (maxWidth > value) { + throw Exception( + 'Left Margin and Right Margin size exceeds the allowed size'); + } + } + }); + } + } + + /// Serializes pageMargins + void _serializePageMargins(XmlBuilder builder, Worksheet sheet) { + builder.element('pageMargins', nest: () { + builder.attribute('left', sheet.pageSetup.leftMargin.toString()); + builder.attribute('right', sheet.pageSetup.rightMargin.toString()); + builder.attribute('top', sheet.pageSetup.topMargin.toString()); + builder.attribute('bottom', sheet.pageSetup.bottomMargin.toString()); + builder.attribute('header', sheet.pageSetup.headerMargin.toString()); + builder.attribute('footer', sheet.pageSetup.footerMargin.toString()); + }); + } + + Future _serializePageMarginsAsync( + XmlBuilder builder, Worksheet sheet) async { + builder.element('pageMargins', nest: () async { + builder.attribute('left', sheet.pageSetup.leftMargin.toString()); + builder.attribute('right', sheet.pageSetup.rightMargin.toString()); + builder.attribute('top', sheet.pageSetup.topMargin.toString()); + builder.attribute('bottom', sheet.pageSetup.bottomMargin.toString()); + builder.attribute('header', sheet.pageSetup.headerMargin.toString()); + builder.attribute('footer', sheet.pageSetup.footerMargin.toString()); + }); + } + + /// Serialize pageSetup + void _serializePageSetup(XmlBuilder builder, Worksheet sheet) { + builder.element('pageSetup', nest: () { + _serializePaperSize(builder, sheet); + + if (sheet.pageSetup.firstPageNumber != 1) { + builder.attribute( + 'firstPageNumber', sheet.pageSetup.firstPageNumber.toString()); + } + if (sheet.pageSetup.fitToPagesWide > 1) { + builder.attribute( + 'fitToWidth', sheet.pageSetup.fitToPagesWide.toString()); + } + if (sheet.pageSetup.fitToPagesTall > 1) { + builder.attribute( + 'fitToHeight', sheet.pageSetup.fitToPagesTall.toString()); + } + if (sheet.pageSetup.order == ExcelPageOrder.overThenDown) { + builder.attribute('pageOrder', 'overThenDown'); + } + if (sheet.pageSetup.orientation == ExcelPageOrientation.landscape) { + builder.attribute('orientation', 'landscape'); + } else { + builder.attribute('orientation', 'portrait'); + } + if (sheet.pageSetup.isBlackAndWhite) { + builder.attribute('blackAndWhite', '1'); + } + if (sheet.pageSetup.isDraft) { + builder.attribute('draft', '1'); + } + if (!sheet.pageSetup.autoFirstPageNumber) { + builder.attribute('useFirstPageNumber', '1'); + } + if (sheet.pageSetup.printErrors != CellErrorPrintOptions.displayed) { + switch (sheet.pageSetup.printErrors) { + case CellErrorPrintOptions.blank: + builder.attribute('errors', 'blank'); + break; + case CellErrorPrintOptions.dash: + builder.attribute('errors', 'dash'); + break; + case CellErrorPrintOptions.notAvailable: + builder.attribute('errors', 'NA'); + break; + case CellErrorPrintOptions.displayed: + break; + } + } + if (sheet.pageSetup.printQuality != 0 && + sheet.pageSetup.printQuality != 600) { + if (sheet.pageSetup.printQuality <= 38528) { + builder.attribute( + 'horizontalDpi', sheet.pageSetup.printQuality.toString()); + builder.attribute( + 'verticalDpi', sheet.pageSetup.printQuality.toString()); + } else { + builder.attribute('horizontalDpi', '38528'); + builder.attribute('verticalDpi', '38528'); + } + } + }); + } + + Future _serializePageSetupAsync( + XmlBuilder builder, Worksheet sheet) async { + builder.element('pageSetup', nest: () async { + _serializePaperSizeAsync(builder, sheet); + if (sheet.pageSetup.firstPageNumber != 1) { + builder.attribute( + 'firstPageNumber', sheet.pageSetup.firstPageNumber.toString()); + } + if (sheet.pageSetup.fitToPagesWide > 1) { + builder.attribute( + 'fitToWidth', sheet.pageSetup.fitToPagesWide.toString()); + } + if (sheet.pageSetup.fitToPagesTall > 1) { + builder.attribute( + 'fitToHeight', sheet.pageSetup.fitToPagesTall.toString()); + } + if (sheet.pageSetup.order == ExcelPageOrder.overThenDown) { + builder.attribute('pageOrder', 'overThenDown'); + } + if (sheet.pageSetup.orientation == ExcelPageOrientation.landscape) { + builder.attribute('orientation', 'landscape'); + } else { + builder.attribute('orientation', 'portrait'); + } + if (sheet.pageSetup.isBlackAndWhite) { + builder.attribute('blackAndWhite', '1'); + } + if (sheet.pageSetup.isDraft) { + builder.attribute('draft', '1'); + } + if (!sheet.pageSetup.autoFirstPageNumber) { + builder.attribute('useFirstPageNumber', '1'); + } + if (sheet.pageSetup.printErrors != CellErrorPrintOptions.displayed) { + switch (sheet.pageSetup.printErrors) { + case CellErrorPrintOptions.blank: + builder.attribute('errors', 'blank'); + break; + case CellErrorPrintOptions.dash: + builder.attribute('errors', 'dash'); + break; + case CellErrorPrintOptions.notAvailable: + builder.attribute('errors', 'NA'); + break; + case CellErrorPrintOptions.displayed: + break; + } + } + if (sheet.pageSetup.printQuality != 0 && + sheet.pageSetup.printQuality != 600) { + if (sheet.pageSetup.printQuality <= 38528) { + builder.attribute( + 'horizontalDpi', sheet.pageSetup.printQuality.toString()); + builder.attribute( + 'verticalDpi', sheet.pageSetup.printQuality.toString()); + } else { + builder.attribute('horizontalDpi', '38528'); + builder.attribute('verticalDpi', '38528'); + } + } + }); + } + + /// Serialize paperSize + void _serializePaperSize(XmlBuilder builder, Worksheet sheet) { + switch (sheet.pageSetup.paperSize) { + case ExcelPaperSize.a2Paper: + builder.attribute('paperSize', '66'); + break; + case ExcelPaperSize.paperDsheet: + builder.attribute('paperSize', '25'); + break; + case ExcelPaperSize.paperEnvelope10: + builder.attribute('paperSize', '20'); + break; + case ExcelPaperSize.paperEnvelope11: + builder.attribute('paperSize', '21'); + break; + case ExcelPaperSize.paperEnvelope12: + builder.attribute('paperSize', '22'); + break; + case ExcelPaperSize.paperEnvelope14: builder.attribute('paperSize', '23'); break; case ExcelPaperSize.paperEnvelope9: @@ -841,16 +1418,229 @@ class SerializeWorkbook { } } - /// Serializes single protection option. - void _serializeProtectionAttribute( - XmlBuilder builder, String attributeName, bool flag, bool defaultValue) { - final bool value = flag; - _serializeAttributes(builder, attributeName, value, defaultValue); - } - - /// Serialize hyperlinks - void _serializeHyperlinks(XmlBuilder builder, Worksheet sheet) { - if (sheet.hyperlinks.count > 0) { + Future _serializePaperSizeAsync( + XmlBuilder builder, Worksheet sheet) async { + switch (sheet.pageSetup.paperSize) { + case ExcelPaperSize.a2Paper: + builder.attribute('paperSize', '66'); + break; + case ExcelPaperSize.paperDsheet: + builder.attribute('paperSize', '25'); + break; + case ExcelPaperSize.paperEnvelope10: + builder.attribute('paperSize', '20'); + break; + case ExcelPaperSize.paperEnvelope11: + builder.attribute('paperSize', '21'); + break; + case ExcelPaperSize.paperEnvelope12: + builder.attribute('paperSize', '22'); + break; + case ExcelPaperSize.paperEnvelope14: + builder.attribute('paperSize', '23'); + break; + case ExcelPaperSize.paperEnvelope9: + builder.attribute('paperSize', '19'); + break; + case ExcelPaperSize.paperEnvelopeB4: + builder.attribute('paperSize', '33'); + break; + case ExcelPaperSize.paperEnvelopeB5: + builder.attribute('paperSize', '34'); + break; + case ExcelPaperSize.paperEnvelopeB6: + builder.attribute('paperSize', '35'); + break; + case ExcelPaperSize.paperEnvelopeC3: + builder.attribute('paperSize', '29'); + break; + case ExcelPaperSize.paperEnvelopeC4: + builder.attribute('paperSize', '30'); + break; + case ExcelPaperSize.paperEnvelopeC5: + builder.attribute('paperSize', '28'); + break; + case ExcelPaperSize.paperEnvelopeC6: + builder.attribute('paperSize', '31'); + break; + case ExcelPaperSize.paperEnvelopeC65: + builder.attribute('paperSize', '32'); + break; + case ExcelPaperSize.paperEnvelopeDL: + builder.attribute('paperSize', '27'); + break; + case ExcelPaperSize.paperEnvelopeItaly: + builder.attribute('paperSize', '36'); + break; + case ExcelPaperSize.paperEnvelopeMonarch: + builder.attribute('paperSize', '37'); + break; + case ExcelPaperSize.paperEnvelopePersonal: + builder.attribute('paperSize', '38'); + break; + case ExcelPaperSize.paperEsheet: + builder.attribute('paperSize', '26'); + break; + case ExcelPaperSize.paperExecutive: + builder.attribute('paperSize', '7'); + break; + case ExcelPaperSize.paperFanfoldLegalGerman: + builder.attribute('paperSize', '41'); + break; + case ExcelPaperSize.paperFanfoldStdGerman: + builder.attribute('paperSize', '40'); + break; + case ExcelPaperSize.paperFanfoldUS: + builder.attribute('paperSize', '39'); + break; + case ExcelPaperSize.paperFolio: + builder.attribute('paperSize', '14'); + break; + case ExcelPaperSize.paperLedger: + builder.attribute('paperSize', '4'); + break; + case ExcelPaperSize.paperLegal: + builder.attribute('paperSize', '5'); + break; + case ExcelPaperSize.paperLetter: + builder.attribute('paperSize', '1'); + break; + case ExcelPaperSize.paperLetterSmall: + builder.attribute('paperSize', '2'); + break; + case ExcelPaperSize.paperNote: + builder.attribute('paperSize', '18'); + break; + case ExcelPaperSize.paperQuarto: + builder.attribute('paperSize', '15'); + break; + case ExcelPaperSize.paperStatement: + builder.attribute('paperSize', '6'); + break; + case ExcelPaperSize.paperTabloid: + builder.attribute('paperSize', '3'); + break; + case ExcelPaperSize.paperUser: + builder.attribute('paperSize', '256'); + break; + case ExcelPaperSize.standardPaper9By11: + builder.attribute('paperSize', '44'); + break; + case ExcelPaperSize.standardPaper10By11: + builder.attribute('paperSize', '45'); + break; + case ExcelPaperSize.standardPaper15By11: + builder.attribute('paperSize', '46'); + break; + case ExcelPaperSize.tabloidExtraPaper: + builder.attribute('paperSize', '52'); + break; + case ExcelPaperSize.superASuperAA4Paper: + builder.attribute('paperSize', '57'); + break; + case ExcelPaperSize.superBSuperBA3Paper: + builder.attribute('paperSize', '58'); + break; + case ExcelPaperSize.paper10x14: + builder.attribute('paperSize', '16'); + break; + case ExcelPaperSize.paper11x17: + builder.attribute('paperSize', '17'); + break; + case ExcelPaperSize.paperA3: + builder.attribute('paperSize', '8'); + break; + case ExcelPaperSize.paperA4: + builder.attribute('paperSize', '9'); + break; + case ExcelPaperSize.paperA4Small: + builder.attribute('paperSize', '10'); + break; + case ExcelPaperSize.paperA5: + builder.attribute('paperSize', '11'); + break; + case ExcelPaperSize.paperB4: + builder.attribute('paperSize', '12'); + break; + case ExcelPaperSize.paperB5: + builder.attribute('paperSize', '13'); + break; + case ExcelPaperSize.paperCsheet: + builder.attribute('paperSize', '24'); + break; + case ExcelPaperSize.iSOB4: + builder.attribute('paperSize', '42'); + break; + case ExcelPaperSize.japaneseDoublePostcard: + builder.attribute('paperSize', '43'); + break; + case ExcelPaperSize.inviteEnvelope: + builder.attribute('paperSize', '47'); + break; + case ExcelPaperSize.letterExtraPaper9275By12: + builder.attribute('paperSize', '50'); + break; + case ExcelPaperSize.legalExtraPaper9275By15: + builder.attribute('paperSize', '51'); + break; + case ExcelPaperSize.a4ExtraPaper: + builder.attribute('paperSize', '53'); + break; + case ExcelPaperSize.letterTransversePaper: + builder.attribute('paperSize', '54'); + break; + case ExcelPaperSize.a4TransversePaper: + builder.attribute('paperSize', '55'); + break; + case ExcelPaperSize.letterExtraTransversePaper: + builder.attribute('paperSize', '56'); + break; + case ExcelPaperSize.letterPlusPaper: + builder.attribute('paperSize', '59'); + break; + case ExcelPaperSize.a4PlusPaper: + builder.attribute('paperSize', '60'); + break; + case ExcelPaperSize.a5TransversePaper: + builder.attribute('paperSize', '61'); + break; + case ExcelPaperSize.jISB5TransversePaper: + builder.attribute('paperSize', '62'); + break; + case ExcelPaperSize.a3ExtraPaper: + builder.attribute('paperSize', '63'); + break; + case ExcelPaperSize.a5ExtraPpaper: + builder.attribute('paperSize', '64'); + break; + case ExcelPaperSize.iSOB5ExtraPaper: + builder.attribute('paperSize', '65'); + break; + case ExcelPaperSize.a3TransversePaper: + builder.attribute('paperSize', '67'); + break; + case ExcelPaperSize.a3ExtraTransversePaper: + builder.attribute('paperSize', '68'); + break; + } + } + + /// Serializes single protection option. + void _serializeProtectionAttribute( + XmlBuilder builder, String attributeName, bool flag, bool defaultValue) { + final bool value = flag; + _serializeAttributes(builder, attributeName, value, defaultValue); + } + + Future _serializeProtectionAttributeAsync(XmlBuilder builder, + String attributeName, bool flag, bool defaultValue) async { + final bool value = flag; + _serializeAttributesAsync(builder, attributeName, value, defaultValue); + } + + /// Serialize hyperlinks + void _serializeHyperlinks(XmlBuilder builder, Worksheet sheet) { + if (sheet.hyperlinks.count > 0) { final int iCount = sheet.hyperlinks.count; final List hyperLinkType = List.filled(iCount, ''); for (int i = 0; i < sheet.hyperlinks.count; i++) { @@ -893,38 +1683,83 @@ class SerializeWorkbook { } } - /// Serialize drawings - void _saveDrawings(Worksheet sheet) { - final XmlBuilder builder = XmlBuilder(); - builder.processing('xml', 'version="1.0"'); - builder.element('xdr:wsDr', nest: () { - builder.attribute('xmlns:xdr', - 'http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing'); - builder.attribute( - 'xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); - final int chartCount = sheet.chartCount; - if (chartCount != 0 && sheet.charts != null) { - sheet.charts!.serializeChartDrawing(builder, sheet); + Future _serializeHyperlinksAsync( + XmlBuilder builder, Worksheet sheet) async { + if (sheet.hyperlinks.count > 0) { + final int iCount = sheet.hyperlinks.count; + final List hyperLinkType = List.filled(iCount, ''); + for (int i = 0; i < sheet.hyperlinks.count; i++) { + final Hyperlink hyperLink = sheet.hyperlinks[i]; + hyperLinkType[i] = + hyperLink._attachedType.toString().split('.').toList().removeAt(1); } - final int idIndex = 0 + chartCount; - final List idRelation = []; - if (sheet.pictures.count != 0) { - int imgId = 0; - int idHyperlink = 1; - int hyperlinkCount = 0; - for (final Picture picture in sheet.pictures.innerList) { - if (picture.height != 0 && picture.width != 0) { - if (picture.lastRow == 0 || picture.lastRow < picture.row) { - _updateLastRowOffset(sheet, picture); - } else if (picture.lastRow != 0) { - picture.lastRowOffset = 0; - } - if (picture.lastColumn == 0 || - picture.lastColumn < picture.column) { - _updateLastColumnOffSet(sheet, picture); - } else if (picture.lastColumn != 0) { - picture.lastColOffset = 0; - } + if (iCount == 0 || !hyperLinkType.contains('range')) { + return; + } + builder.element('hyperlinks', nest: () async { + int id = 1; + for (final Hyperlink link in sheet.hyperlinks.innerList) { + if (link._attachedType == ExcelHyperlinkAttachedType.range) { + builder.element('hyperlink', nest: () async { + if (link.type == HyperlinkType.workbook) { + builder.attribute('ref', link.reference); + builder.attribute('location', link.address); + } else { + builder.attribute('ref', link.reference); + final String rId = 'rId$id'; + builder.attribute('r:id', rId); + _relationId.add(rId); + id++; + } + if (link.screenTip != null) { + // ignore: unnecessary_null_checks + builder.attribute('tooltip', link.screenTip!); + } + if (link.textToDisplay != null) { + // ignore: unnecessary_null_checks + builder.attribute('display', link.textToDisplay!); + } else { + builder.attribute('display', link.address); + } + }); + } + } + }); + } + } + + /// Serialize drawings + void _saveDrawings(Worksheet sheet) { + final XmlBuilder builder = XmlBuilder(); + builder.processing('xml', 'version="1.0"'); + builder.element('xdr:wsDr', nest: () { + builder.attribute('xmlns:xdr', + 'http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing'); + builder.attribute( + 'xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); + final int chartCount = sheet.chartCount; + if (chartCount != 0 && sheet.charts != null) { + sheet.charts!.serializeChartDrawingSync(builder, sheet); + } + final int idIndex = 0 + chartCount; + final List idRelation = []; + if (sheet.pictures.count != 0) { + int imgId = 0; + int idHyperlink = 1; + int hyperlinkCount = 0; + for (final Picture picture in sheet.pictures.innerList) { + if (picture.height != 0 && picture.width != 0) { + if (picture.lastRow == 0 || picture.lastRow < picture.row) { + _updateLastRowOffset(sheet, picture); + } else if (picture.lastRow != 0) { + picture.lastRowOffset = 0; + } + if (picture.lastColumn == 0 || + picture.lastColumn < picture.column) { + _updateLastColumnOffSet(sheet, picture); + } else if (picture.lastColumn != 0) { + picture.lastColOffset = 0; + } } imgId++; builder.element('xdr:twoCellAnchor', nest: () { @@ -1075,6 +1910,188 @@ class SerializeWorkbook { _addToArchive(bytes, 'xl/drawings/drawing${_workbook._drawingCount}.xml'); } + Future _saveDrawingsAsync(Worksheet sheet) async { + final XmlBuilder builder = XmlBuilder(); + builder.processing('xml', 'version="1.0"'); + builder.element('xdr:wsDr', nest: () async { + builder.attribute('xmlns:xdr', + 'http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing'); + builder.attribute( + 'xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); + final int chartCount = sheet.chartCount; + if (chartCount != 0 && sheet.charts != null) { + sheet.charts!.serializeChartDrawing(builder, sheet); + } + final int idIndex = 0 + chartCount; + final List idRelation = []; + if (sheet.pictures.count != 0) { + int imgId = 0; + int idHyperlink = 1; + int hyperlinkCount = 0; + for (final Picture picture in sheet.pictures.innerList) { + if (picture.height != 0 && picture.width != 0) { + if (picture.lastRow == 0 || picture.lastRow < picture.row) { + _updateLastRowOffsetAsync(sheet, picture); + } else if (picture.lastRow != 0) { + picture.lastRowOffset = 0; + } + if (picture.lastColumn == 0 || + picture.lastColumn < picture.column) { + _updateLastColumnOffSetAsync(sheet, picture); + } else if (picture.lastColumn != 0) { + picture.lastColOffset = 0; + } + } + imgId++; + builder.element('xdr:twoCellAnchor', nest: () async { + builder.attribute('editAs', 'twoCell'); + builder.element('xdr:from', nest: () async { + builder.element('xdr:col', nest: picture.column - 1); + builder.element('xdr:colOff', nest: 0); + builder.element('xdr:row', nest: picture.row - 1); + builder.element('xdr:rowOff', nest: 0); + }); + + builder.element('xdr:to', nest: () async { + builder.element('xdr:col', nest: picture.lastColumn - 1); + builder.element('xdr:colOff', + nest: picture.lastColOffset.round()); + builder.element('xdr:row', nest: picture.lastRow - 1); + builder.element('xdr:rowOff', + nest: picture.lastRowOffset.round()); + }); + + builder.element('xdr:pic', nest: () async { + builder.attribute('macro', ''); + builder.element('xdr:nvPicPr', nest: () async { + builder.element('xdr:cNvPr', nest: () async { + builder.attribute('id', imgId); + builder.attribute('name', 'Picture$imgId'); + if (picture._isHyperlink) { + builder.element('a:hlinkClick', nest: () async { + builder.attribute('xmlns:r', + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + int id = idIndex + imgId + idHyperlink; + String rId = 'rId$id'; + if (idRelation.contains(rId)) { + id = id + 1; + rId = 'rId$id'; + } + builder.attribute('r:id', rId); + idRelation.add(rId); + sheet._hyperlinkRelationId.add(rId); + idHyperlink++; + if (picture.hyperlink != null && + picture.hyperlink!.screenTip != null) { + builder.attribute( + 'tooltip', + // ignore: unnecessary_null_checks + picture.hyperlink!.screenTip!); + } + }); + } + }); + builder.element('xdr:cNvPicPr', nest: () async { + builder.element('a:picLocks', nest: () async { + builder.attribute('noChangeAspect', 1); + }); + }); + }); + + builder.element('xdr:blipFill', nest: () async { + builder.element('a:blip', nest: () async { + builder.attribute('xmlns:r', + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + int id; + String rId; + if (idRelation.isEmpty) { + id = idIndex + imgId + hyperlinkCount; + rId = 'rId$id'; + builder.attribute('r:embed', rId); + idRelation.add(rId); + } else { + id = idIndex + imgId + hyperlinkCount; + rId = 'rId$id'; + if (idRelation.contains(rId)) { + id = id + 1; + rId = 'rId$id'; + if (idRelation.contains(rId)) { + id = id + 1; + rId = 'rId$id'; + } + } + builder.attribute('r:embed', rId); + idRelation.add(rId); + if (picture._isHyperlink) { + hyperlinkCount++; + } + } + builder.attribute('cstate', 'print'); + }); + + builder.element('a:stretch', nest: () async { + builder.element('a:fillRect', nest: () async {}); + }); + }); + + builder.element('xdr:spPr', nest: () async { + builder.element('a:xfrm', nest: () async { + if (picture.rotation != 0 && + picture.rotation <= 3600 && + picture.rotation >= -3600) { + builder.attribute('rot', picture.rotation * 60000); + } + if (picture.verticalFlip) { + builder.attribute('flipV', '1'); + } + if (picture.horizontalFlip) { + builder.attribute('flipH', '1'); + } + builder.element('a:off', nest: () async { + builder.attribute('x', '0'); + builder.attribute('y', '0'); + }); + builder.element('a:ext', nest: () async { + builder.attribute('cx', '0'); + builder.attribute('cy', '0'); + }); + }); + + builder.element('a:prstGeom', nest: () async { + builder.attribute('prst', 'rect'); + builder.element('a:avLst', nest: () async {}); + }); + }); + }); + builder.element('xdr:clientData', nest: () async {}); + }); + final List? imageData = picture.imageData; + _workbook._imageCount += 1; + String imgPath; + if (Picture.isJpeg(imageData)) { + imgPath = 'xl/media/image${_workbook._imageCount}.jpeg'; + if (!_workbook._defaultContentTypes.containsKey('jpeg')) { + _workbook._defaultContentTypes['jpeg'] = 'image/jpeg'; + } + } else { + imgPath = 'xl/media/image${_workbook._imageCount}.png'; + if (!_workbook._defaultContentTypes.containsKey('png')) { + _workbook._defaultContentTypes['png'] = 'image/png'; + } + } + if (imageData != null) { + _addToArchiveAsync(imageData, imgPath); + } + } + } + }); + _saveDrawingRelationsAsync(sheet); + final String stringXml = builder.buildDocument().copy().toString(); + final List bytes = utf8.encode(stringXml); + _addToArchiveAsync( + bytes, 'xl/drawings/drawing${_workbook._drawingCount}.xml'); + } + /// Serialize drawing relations. void _saveDrawingRelations(Worksheet sheet) { int idIndex = 0; @@ -1157,29 +2174,110 @@ class SerializeWorkbook { bytes, 'xl/drawings/_rels/drawing${_workbook._drawingCount}.xml.rels'); } - /// Updates the picture shape last row offset. - void _updateLastRowOffset(Worksheet sheet, Picture picture) { - double iCurHeight = picture.height.toDouble(); - int iCurRow = picture.row; - int iCurOffset = 0; - - while (iCurHeight >= 0) { - double iRowHeight; - if (sheet.rows.count != 0 && - iCurRow - 1 < sheet.rows.count && - sheet.rows[iCurRow] != null) { - iRowHeight = _convertToPixels((sheet.rows[iCurRow]!.height == 0) - ? 15 - : sheet.rows[iCurRow]!.height); - } else { - iRowHeight = _convertToPixels(15); + Future _saveDrawingRelationsAsync(Worksheet sheet) async { + int idIndex = 0; + final XmlBuilder builder = XmlBuilder(); + builder.processing('xml', 'version="1.0"'); + builder.element('Relationships', nest: () async { + builder.namespace( + 'http://schemas.openxmlformats.org/package/2006/relationships'); + if (sheet.chartCount != 0) { + final int length = sheet.chartCount; + for (int i = 1; i <= length; i++) { + builder.element('Relationship', nest: () async { + builder.attribute('Id', 'rId${idIndex + i}'); + builder.attribute('Type', + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart'); + builder.attribute('Target', + '/xl/charts/chart${sheet.workbook.chartCount + i}.xml'); + }); + } + idIndex = length; } - final double iSpaceInCell = - iRowHeight - ((iCurOffset * iRowHeight) / 256); - - if (iSpaceInCell > iCurHeight) { - picture.lastRow = iCurRow; - picture.lastRowOffset = iCurOffset + (iCurHeight * 256 / iRowHeight); + if (sheet.hyperlinks.count > 0) { + final int length = sheet.hyperlinks.count; + int j = 0; + for (int i = 0; i < length; i++) { + if (sheet.hyperlinks[i]._attachedType == + ExcelHyperlinkAttachedType.shape) { + builder.element('Relationship', nest: () async { + builder.attribute('Id', sheet._hyperlinkRelationId[j]); + builder.attribute('Type', + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink'); + if (sheet.hyperlinks[i].type == HyperlinkType.workbook) { + String address = sheet.hyperlinks[i].address; + address = address.startsWith('#') ? address : '#$address'; + builder.attribute('Target', address); + } else { + builder.attribute('Target', sheet.hyperlinks[i].address); + } + if (sheet.hyperlinks[i].type != HyperlinkType.workbook) { + builder.attribute('TargetMode', 'External'); + } + }); + j++; + } + } + } + if (sheet.pictures.count != 0) { + final int length = sheet.pictures.count; + int id = _workbook._imageCount - sheet.pictures.count; + int idHyperlink = 0; + for (int i = 1; i <= length; i++) { + id++; + builder.element('Relationship', nest: () async { + String imgPath; + if (Picture.isPng(sheet.pictures[i - 1].imageData)) { + imgPath = '/xl/media/image$id.png'; + } else { + imgPath = '/xl/media/image$id.jpeg'; + } + if (sheet.pictures[i - 1]._isHyperlink) { + builder.attribute('Id', 'rId${idIndex + i + idHyperlink}'); + builder.attribute('Type', + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image'); + builder.attribute('Target', imgPath); + idHyperlink++; + } else { + builder.attribute('Id', 'rId${idIndex + i + idHyperlink}'); + builder.attribute('Type', + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image'); + builder.attribute('Target', imgPath); + } + }); + } + } + }); + + final String stringXml = builder.buildDocument().copy().toString(); + final List bytes = utf8.encode(stringXml); + _addToArchiveAsync( + bytes, 'xl/drawings/_rels/drawing${_workbook._drawingCount}.xml.rels'); + } + + /// Updates the picture shape last row offset. + void _updateLastRowOffset(Worksheet sheet, Picture picture) { + double iCurHeight = picture.height.toDouble(); + int iCurRow = picture.row; + int iCurOffset = 0; + + while (iCurHeight >= 0) { + double iRowHeight; + if (sheet.rows.count != 0 && + iCurRow - 1 < sheet.rows.count && + sheet.rows[iCurRow] != null) { + iRowHeight = _convertToPixels((sheet.rows[iCurRow]!.height == 0) + ? 15 + : sheet.rows[iCurRow]!.height); + } else { + iRowHeight = _convertToPixels(15); + } + final double iSpaceInCell = + iRowHeight - ((iCurOffset * iRowHeight) / 256); + + if (iSpaceInCell > iCurHeight) { + picture.lastRow = iCurRow; + picture.lastRowOffset = iCurOffset + (iCurHeight * 256 / iRowHeight); double rowHiddenHeight; if (sheet.rows.count != 0 && iCurRow < sheet.rows.count && @@ -1205,6 +2303,54 @@ class SerializeWorkbook { } } + Future _updateLastRowOffsetAsync( + Worksheet sheet, Picture picture) async { + double iCurHeight = picture.height.toDouble(); + int iCurRow = picture.row; + int iCurOffset = 0; + + while (iCurHeight >= 0) { + double iRowHeight = 0; + if (sheet.rows.count != 0 && + iCurRow - 1 < sheet.rows.count && + sheet.rows[iCurRow] != null) { + iRowHeight = _convertToPixels((sheet.rows[iCurRow]!.height == 0) + ? 15 + : sheet.rows[iCurRow]!.height); + } else { + iRowHeight = _convertToPixels(15); + } + final double iSpaceInCell = + iRowHeight - ((iCurOffset * iRowHeight) / 256); + + if (iSpaceInCell > iCurHeight) { + picture.lastRow = iCurRow; + picture.lastRowOffset = iCurOffset + (iCurHeight * 256 / iRowHeight); + double rowHiddenHeight = 0; + if (sheet.rows.count != 0 && + iCurRow < sheet.rows.count && + sheet.rows[iCurRow] != null) { + rowHiddenHeight = _convertToPixels((sheet.rows[iCurRow]!.height == 0) + ? 15 + : sheet.rows[iCurRow]!.height); + } else { + rowHiddenHeight = _convertToPixels(15); + } + + picture.lastRowOffset = (rowHiddenHeight * picture.lastRowOffset) / 256; + picture.lastRowOffset = + (picture.lastRowOffset / _workbook._unitProportions[7]) + .round() + .toDouble(); + break; + } else { + iCurHeight -= iSpaceInCell; + iCurRow++; + iCurOffset = 0; + } + } + } + /// Updates the picture shape last column offset. void _updateLastColumnOffSet(Worksheet sheet, Picture picture) { double iCurWidth = picture.width.toDouble(); @@ -1252,6 +2398,53 @@ class SerializeWorkbook { } } + Future _updateLastColumnOffSetAsync( + Worksheet sheet, Picture picture) async { + double iCurWidth = picture.width.toDouble(); + int iCurCol = picture.column; + double iCurOffset = 0; + + while (iCurWidth >= 0) { + double iColWidth = 0; + Column? col = sheet.columns[iCurCol]; + if (sheet.columns.count != 0 && + iCurCol - 1 < sheet.columns.count && + col != null) { + iColWidth = _columnWidthToPixels(col.width == 0 ? 8.43 : col.width); + } else { + iColWidth = _columnWidthToPixels(8.43); + } + final double iSpaceInCell = iColWidth - (iCurOffset * iColWidth / 1024); + + if (iSpaceInCell > iCurWidth) { + picture.lastColumn = iCurCol; + picture.lastColOffset = iCurOffset + (iCurWidth * 1024 / iColWidth); + double colHiddenWidth = 0; + col = sheet.columns[iCurCol]; + if (sheet.columns.count != 0 && + iCurCol - 1 < sheet.columns.count && + col != null) { + colHiddenWidth = + _columnWidthToPixels(col.width == 0 ? 8.43 : col.width); + } else { + colHiddenWidth = _columnWidthToPixels(8.43); + } + + picture.lastColOffset = (colHiddenWidth * picture.lastColOffset) / 1024; + picture.lastColOffset = + (picture.lastColOffset / _workbook._unitProportions[7]) + .round() + .toDouble(); + + break; + } else { + iCurWidth -= iSpaceInCell; + iCurCol++; + iCurOffset = 0; + } + } + } + /// Converts the given value to pixels. double _convertToPixels(double value) { return value * _workbook._unitProportions[6]; @@ -1407,6 +2600,26 @@ class SerializeWorkbook { }); } + static Future _saveSheetViewAsync( + Worksheet sheet, XmlBuilder builder) async { + builder.element('sheetViews', nest: () async { + builder.element('sheetView', nest: () async { + builder.attribute('workbookViewId', '0'); + if (sheet.isRightToLeft) { + builder.attribute('rightToLeft', '1'); + } + if (!sheet.showGridlines) { + builder.attribute('showGridLines', '0'); + } + if (sheet._isfreezePane || + sheet._verticalSplit != 0 || + sheet._horizontalSplit != 0) { + _savePaneAsync(sheet, builder); + } + }); + }); + } + /// Serialize worksheet Pane. static void _savePane(Worksheet sheet, XmlBuilder builder) { builder.element('pane', nest: () { @@ -1437,6 +2650,36 @@ class SerializeWorkbook { }); } + static Future _savePaneAsync( + Worksheet sheet, XmlBuilder builder) async { + builder.element('pane', nest: () async { + if (sheet._verticalSplit != 0) { + builder.attribute('xSplit', sheet._verticalSplit.toString()); + } + if (sheet._horizontalSplit != 0) { + builder.attribute('ySplit', sheet._horizontalSplit.toString()); + } + if (sheet._topLeftCell != '') { + builder.attribute('topLeftCell', sheet._topLeftCell); + } + switch (sheet._activePane) { + case _ActivePane.bottomRight: + builder.attribute('activePane', 'bottomRight'); + break; + case _ActivePane.bottomLeft: + builder.attribute('activePane', 'bottomLeft'); + break; + case _ActivePane.topRight: + builder.attribute('activePane', 'topRight'); + break; + case _ActivePane.topLeft: + builder.attribute('activePane', 'topLeft'); + break; + } + builder.attribute('state', 'frozen'); + }); + } + /// Serialize workbook shared string. void _saveSharedString() { final XmlBuilder builder = XmlBuilder(); @@ -1468,6 +2711,36 @@ class SerializeWorkbook { } } + Future _saveSharedStringAsync() async { + final XmlBuilder builder = XmlBuilder(); + final int length = _workbook._sharedStrings.length; + if (length > 0) { + builder.processing('xml', 'version="1.0"'); + builder.element('sst', nest: () async { + builder.attribute('xmlns', + 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'); + builder.attribute('uniqueCount', length.toString()); + builder.attribute('count', _workbook._sharedStringCount.toString()); + _workbook._sharedStrings.forEach((String key, int value) { + if (key.indexOf('') != 0) { + builder.element('si', nest: () async { + builder.element('t', nest: () async { + builder.text(key); + }); + }); + } else { + builder.element('si', nest: () async { + builder.text(key); + }); + } + }); + }); + final String stringXml = builder.buildDocument().toString(); + final List bytes = utf8.encode(stringXml); + _addToArchiveAsync(bytes, 'xl/sharedStrings.xml'); + } + } + /// Serialize app properties void _saveApp(BuiltInProperties builtInProperties) { final XmlBuilder builder = XmlBuilder(); @@ -1491,6 +2764,28 @@ class SerializeWorkbook { _addToArchive(bytes, 'docProps/app.xml'); } + Future _saveAppAsync(BuiltInProperties builtInProperties) async { + final XmlBuilder builder = XmlBuilder(); + + builder.processing('xml', 'version="1.0"'); + builder.element('Properties', nest: () async { + builder.attribute('xmlns', + 'http://schemas.openxmlformats.org/officeDocument/2006/extended-properties'); + builder.element('Application', nest: 'Essential XlsIO'); + + if (builtInProperties.manager != null) { + builder.element('Manager', nest: builtInProperties.manager); + } + + if (builtInProperties.company != null) { + builder.element('Company', nest: builtInProperties.company); + } + }); + final String stringXml = builder.buildDocument().toString(); + final List bytes = utf8.encode(stringXml); + _addToArchiveAsync(bytes, 'docProps/app.xml'); + } + /// Serialize core properties void _saveCore(BuiltInProperties builtInProperties) { final XmlBuilder builder = XmlBuilder(); @@ -1555,8 +2850,71 @@ class SerializeWorkbook { _addToArchive(bytes, 'docProps/core.xml'); } - /// Serialize content type. - void _saveContentType() { + Future _saveCoreAsync(BuiltInProperties builtInProperties) async { + final XmlBuilder builder = XmlBuilder(); + + builder.processing('xml', 'version="1.0"'); + builder.element('cp:coreProperties', nest: () async { + builder.attribute('xmlns:cp', + 'http://schemas.openxmlformats.org/package/2006/metadata/core-properties'); + builder.attribute('xmlns:dc', 'http://purl.org/dc/elements/1.1/'); + builder.attribute('xmlns:dcterms', 'http://purl.org/dc/terms/'); + builder.attribute('xmlns:dcmitype', 'http://purl.org/dc/dcmitype/'); + builder.attribute( + 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); + final DateTime createdDate = DateTime.now(); + + if (builtInProperties.author != null) { + builder.element('dc:creator', nest: builtInProperties.author); + } + if (builtInProperties.subject != null) { + builder.element('dc:subject', nest: builtInProperties.subject); + } + if (builtInProperties.category != null) { + builder.element('dc:category', nest: builtInProperties.category); + } + if (builtInProperties.comments != null) { + builder.element('dc:description', nest: builtInProperties.comments); + } + if (builtInProperties.title != null) { + builder.element('dc:title', nest: builtInProperties.title); + } + if (builtInProperties.tags != null) { + builder.element('dc:keywords', nest: builtInProperties.tags); + } + if (builtInProperties.status != null) { + builder.element('dc:contentStatus', nest: builtInProperties.status); + } + if (builtInProperties.createdDate != null) { + builder.element('dcterms:created', nest: () async { + builder.attribute('xsi:type', 'dcterms:W3CDTF'); + builder.text(builtInProperties.createdDate!.toIso8601String()); + }); + } else { + builder.element('dcterms:created', nest: () async { + builder.attribute('xsi:type', 'dcterms:W3CDTF'); + builder.text(createdDate.toIso8601String()); + }); + } + if (builtInProperties.modifiedDate != null) { + builder.element('dcterms:modified', nest: () async { + builder.attribute('xsi:type', 'dcterms:W3CDTF'); + builder.text(builtInProperties.modifiedDate!.toIso8601String()); + }); + } else { + builder.element('dcterms:modified', nest: () async { + builder.attribute('xsi:type', 'dcterms:W3CDTF'); + builder.text(createdDate.toIso8601String()); + }); + } + }); + final String stringXml = builder.buildDocument().toString(); + final List bytes = utf8.encode(stringXml); + _addToArchiveAsync(bytes, 'docProps/core.xml'); + } + + /// Serialize content type. + void _saveContentType() { final XmlBuilder builder = XmlBuilder(); builder.processing('xml', 'version="1.0"'); builder.element('Types', nest: () { @@ -1665,6 +3023,115 @@ class SerializeWorkbook { _addToArchive(bytes, '[Content_Types].xml'); } + Future _saveContentTypeAsync() async { + final XmlBuilder builder = XmlBuilder(); + builder.processing('xml', 'version="1.0"'); + builder.element('Types', nest: () async { + builder.attribute('xmlns', + 'http://schemas.openxmlformats.org/package/2006/content-types'); + + builder.element('Default', nest: () async { + builder.attribute('Extension', 'xml'); + builder.attribute('ContentType', 'application/xml'); + }); + builder.element('Default', nest: () async { + builder.attribute('Extension', 'rels'); + builder.attribute('ContentType', + 'application/vnd.openxmlformats-package.relationships+xml'); + }); + builder.element('Override', nest: () async { + builder.attribute('PartName', '/xl/styles.xml'); + builder.attribute('ContentType', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml'); + }); + builder.element('Override', nest: () async { + builder.attribute('PartName', '/xl/workbook.xml'); + builder.attribute('ContentType', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml'); + }); + builder.element('Override', nest: () async { + builder.attribute('PartName', '/docProps/app.xml'); + builder.attribute('ContentType', + 'application/vnd.openxmlformats-officedocument.extended-properties+xml'); + }); + builder.element('Override', nest: () async { + builder.attribute('PartName', '/docProps/core.xml'); + builder.attribute('ContentType', + 'application/vnd.openxmlformats-package.core-properties+xml'); + }); + final int length = _workbook.worksheets.count; + int drawingIndex = 1; + int chartIndex = 1; + for (int i = 0; i < length; i++) { + final int index = i + 1; + builder.element('Override', nest: () async { + builder.attribute('PartName', '/xl/worksheets/sheet$index.xml'); + builder.attribute('ContentType', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml'); + }); + if ((_workbook._imageCount > 0 && + _workbook.worksheets[i].pictures.count > 0) || + _workbook.worksheets[i].charts != null && + _workbook.worksheets[i].chartCount > 0) { + if (_workbook.worksheets[i].charts != null && + _workbook.worksheets[i].chartCount > 0) { + final int chartCount = _workbook.worksheets[i].chartCount; + for (int index = 1; index <= chartCount; index++) { + builder.element('Override', nest: () async { + builder.attribute( + 'PartName', '/xl/charts/chart$chartIndex.xml'); + builder.attribute('ContentType', + 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml'); + }); + chartIndex++; + } + } + builder.element('Override', nest: () async { + builder.attribute( + 'PartName', '/xl/drawings/drawing$drawingIndex.xml'); + builder.attribute('ContentType', + 'application/vnd.openxmlformats-officedocument.drawing+xml'); + }); + drawingIndex++; + } + } + + for (int tableCount = 0; + tableCount < _workbook._tableCount; + tableCount++) { + final int tableIndex = tableCount + 1; + builder.element('Override', nest: () async { + builder.attribute('PartName', '/xl/tables/table$tableIndex.xml'); + builder.attribute('ContentType', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml'); + }); + } + + if (_workbook._imageCount > 0) { + for (final dynamic key in _workbook._defaultContentType.keys) { + builder.element('Default', nest: () async { + builder.attribute('Extension', key); + builder.attribute( + 'ContentType', + // ignore: unnecessary_null_checks + _workbook._defaultContentTypes[key]!); + }); + } + } + + if (_workbook._sharedStringCount > 0) { + builder.element('Override', nest: () async { + builder.attribute('PartName', '/xl/sharedStrings.xml'); + builder.attribute('ContentType', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml'); + }); + } + }); + final String stringXml = builder.buildDocument().toString(); + final List bytes = utf8.encode(stringXml); + _addToArchiveAsync(bytes, '[Content_Types].xml'); + } + /// Serialize workbook releation. void _saveWorkbookRelation() { final XmlBuilder builder = XmlBuilder(); @@ -1708,6 +3175,48 @@ class SerializeWorkbook { _addToArchive(bytes, 'xl/_rels/workbook.xml.rels'); } + Future _saveWorkbookRelationAsync() async { + final XmlBuilder builder = XmlBuilder(); + + builder.processing('xml', 'version="1.0"'); + builder.element('Relationships', nest: () async { + builder.attribute('xmlns', + 'http://schemas.openxmlformats.org/package/2006/relationships'); + + final int length = _workbook.worksheets.count; + int count = 0; + int index; + for (int i = 0; i < length; i++, count++) { + builder.element('Relationship', nest: () async { + index = i + 1; + builder.attribute('Id', 'rId$index'); + builder.attribute('Type', + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet'); + builder.attribute('Target', 'worksheets/sheet$index.xml'); + }); + } + count = ++count; + builder.element('Relationship', nest: () async { + builder.attribute('Id', 'rId$count'); + builder.attribute('Type', + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles'); + builder.attribute('Target', 'styles.xml'); + }); + if (_workbook._sharedStringCount > 0) { + count = ++count; + builder.element('Relationship', nest: () async { + builder.attribute('Id', 'rId$count'); + builder.attribute('Type', + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings'); + builder.attribute('Target', 'sharedStrings.xml'); + }); + } + }); + final String stringXml = builder.buildDocument().toString(); + final List bytes = utf8.encode(stringXml); + _addToArchiveAsync(bytes, 'xl/_rels/workbook.xml.rels'); + } + /// Serialize top level releation. void _saveTopLevelRelation() { final XmlBuilder builder = XmlBuilder(); @@ -1743,6 +3252,40 @@ class SerializeWorkbook { _addToArchive(bytes, '_rels/.rels'); } + Future _saveTopLevelRelationAsync() async { + final XmlBuilder builder = XmlBuilder(); + + builder.processing('xml', 'version="1.0"'); + builder.element('Relationships', nest: () async { + builder.attribute('xmlns', + 'http://schemas.openxmlformats.org/package/2006/relationships'); + + builder.element('Relationship', nest: () async { + builder.attribute('Id', 'rId1'); + builder.attribute('Type', + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument'); + builder.attribute('Target', 'xl/workbook.xml'); + }); + + builder.element('Relationship', nest: () async { + builder.attribute('Id', 'rId2'); + builder.attribute('Type', + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties'); + builder.attribute('Target', 'docProps/app.xml'); + }); + + builder.element('Relationship', nest: () async { + builder.attribute('Id', 'rId3'); + builder.attribute('Type', + 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties'); + builder.attribute('Target', 'docProps/core.xml'); + }); + }); + final String stringXml = builder.buildDocument().toString(); + final List bytes = utf8.encode(stringXml); + _addToArchiveAsync(bytes, '_rels/.rels'); + } + /// Serialize content type. void _saveStyles() { _updateCellStyleXfs(); @@ -1771,6 +3314,33 @@ class SerializeWorkbook { _addToArchive(bytes, 'xl/styles.xml'); } + Future _saveStylesAsync() async { + _updateCellStyleXfsAsync(); + final XmlBuilder builder = XmlBuilder(); + builder.processing('xml', 'version="1.0"'); + builder.element('styleSheet', nest: () async { + builder.attribute( + 'xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'); + builder.attribute('xmlns:mc', + 'http://schemas.openxmlformats.org/markup-compatibility/2006'); + builder.attribute('mc:Ignorable', 'x14ac'); + builder.attribute('xmlns:x14ac', + 'http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac'); + + _saveNumberFormatsAsync(builder); + _saveFontsAsync(builder); + _saveFillsAsync(builder); + _saveBordersAsync(builder); + _saveCellStyleXfsAsync(builder); + _saveCellXfsAsync(builder); + _saveGlobalCellstylesAsync(builder); + _serialiseDxfsAsync(builder); + }); + final String stringXml = builder.buildDocument().toString(); + final List bytes = utf8.encode(stringXml); + _addToArchiveAsync(bytes, 'xl/styles.xml'); + } + /// Update the global style. void _updateGlobalStyles() { for (final Style style in _workbook.styles.innerList) { @@ -1789,6 +3359,23 @@ class SerializeWorkbook { } } + Future _updateGlobalStylesAsync() async { + for (final Style style in _workbook.styles.innerList) { + final CellStyle cellStyle = style as CellStyle; + if (cellStyle.isGlobalStyle) { + if (cellStyle.name == '') { + _workbook.styles.addStyle(cellStyle); + } + final _GlobalStyle globalStyle = _GlobalStyle(); + globalStyle._name = cellStyle.name; + globalStyle._xfId = cellStyle.index; + globalStyle._numberFormat = cellStyle.numberFormat; + globalStyle._builtinId = cellStyle._builtinId; + _workbook._globalStyles[globalStyle._name] = globalStyle; + } + } + } + /// Process the cell style. int _processCellStyle(CellStyle style, Workbook workbook) { final int index = workbook.styles.innerList.indexOf(style); @@ -1923,16 +3510,138 @@ class SerializeWorkbook { } } - /// Serialize number formats. - void _saveNumberFormats(XmlBuilder builder) { - final List<_Format> arrFormats = _workbook.innerFormats._getUsedFormats(); - if (arrFormats.isNotEmpty) { - builder.element('numFmts', nest: () { - builder.attribute('count', arrFormats.length.toString()); - for (int i = 0; i < arrFormats.length; i++) { - builder.element('numFmt', nest: () { - builder.attribute('numFmtId', arrFormats[i]._index.toString()); - final String formatString = + Future _updateCellStyleXfsAsync() async { + for (final Style cellStyle in _workbook.styles.innerList) { + final CellStyle style = cellStyle as CellStyle; + _CellStyleXfs cellXfs; + if (style.isGlobalStyle) { + cellXfs = _CellXfs(); + if (_workbook._globalStyles.containsKey(style.name)) { + (cellXfs as _CellXfs)._xfId = + _workbook._globalStyles[style.name]!._xfId; + } + } else { + cellXfs = _CellXfs(); + (cellXfs as _CellXfs)._xfId = 0; + } + final _ExtendCompareStyle compareFontResult = _workbook._isNewFont(style); + if (!compareFontResult._result) { + final Font font = Font(); + font.bold = style.bold; + font.italic = style.italic; + font.name = style.fontName; + font.size = style.fontSize; + font.underline = style.underline; + if (style.fontColor.length == 7) { + font.color = 'FF${style.fontColor.replaceAll('#', '')}'; + } else { + font.color = style.fontColor; + } + _workbook.fonts.add(font); + cellXfs._fontId = _workbook.fonts.length - 1; + } else { + cellXfs._fontId = compareFontResult._index; + } + if (style.backColor != '#FFFFFF' && style.backColor.length == 7) { + final String backColor = 'FF${style.backColor.replaceAll('#', '')}'; + if (_workbook.fills.containsKey(backColor)) { + final int? fillId = _workbook.fills[backColor]; + cellXfs._fillId = fillId!; + } else { + final int fillId = _workbook.fills.length + 2; + _workbook.fills[backColor] = fillId; + cellXfs._fillId = fillId; + } + } else if (style.backColor.length > 7) { + final String backColorRgb = style.backColor; + if (_workbook.fills.containsKey(backColorRgb)) { + final int? fillId = _workbook.fills[backColorRgb]; + cellXfs._fillId = fillId!; + } else { + final int fillId = _workbook.fills.length + 2; + _workbook.fills[backColorRgb] = fillId; + cellXfs._fillId = fillId; + } + } else { + cellXfs._fillId = 0; + } + + //Add border + if (!Workbook._isNewBorder(style)) { + _workbook.borders.add(style.borders); + cellXfs._borderId = _workbook.borders.length; + } else { + cellXfs._borderId = 0; + } + + //Add Number Format + if (style.numberFormat != 'GENERAL') { + if (_workbook.innerFormats._contains(style.numberFormatIndex)) { + final _Format format = + _workbook.innerFormats[style.numberFormatIndex]; + cellXfs._numberFormatId = format._index; + } else { + cellXfs._numberFormatId = + _workbook.innerFormats._createFormat(style.numberFormat); + } + } else { + if (style.numberFormat == 'GENERAL' && style.numberFormatIndex == 14) { + cellXfs._numberFormatId = 14; + } else { + cellXfs._numberFormatId = 0; + } + } + //Add alignment + cellXfs._alignment = _Alignment(); + cellXfs._alignment!.indent = style.indent; + cellXfs._alignment!.horizontal = + style.hAlign.toString().split('.').toList().removeAt(1); + cellXfs._alignment!.vertical = + style.vAlign.toString().split('.').toList().removeAt(1); + cellXfs._alignment!.wrapText = style.wrapText ? 1 : 0; + cellXfs._alignment!.rotation = style.rotation; + + // Add protection + if (!style.locked) { + cellXfs._locked = 0; + } + if (style.isGlobalStyle) { + _workbook._cellStyleXfs.add(cellXfs); + _workbook._cellXfs.add(cellXfs as _CellXfs); + } else { + //Add cellxfs + _workbook._cellXfs.add(cellXfs as _CellXfs); + } + } + } + + /// Serialize number formats. + void _saveNumberFormats(XmlBuilder builder) { + final List<_Format> arrFormats = _workbook.innerFormats._getUsedFormats(); + if (arrFormats.isNotEmpty) { + builder.element('numFmts', nest: () { + builder.attribute('count', arrFormats.length.toString()); + for (int i = 0; i < arrFormats.length; i++) { + builder.element('numFmt', nest: () { + builder.attribute('numFmtId', arrFormats[i]._index.toString()); + final String formatString = + arrFormats[i]._formatString!.replaceAll("'", '"'); + builder.attribute('formatCode', formatString); + }); + } + }); + } + } + + Future _saveNumberFormatsAsync(XmlBuilder builder) async { + final List<_Format> arrFormats = _workbook.innerFormats._getUsedFormats(); + if (arrFormats.isNotEmpty) { + builder.element('numFmts', nest: () async { + builder.attribute('count', arrFormats.length.toString()); + for (int i = 0; i < arrFormats.length; i++) { + builder.element('numFmt', nest: () async { + builder.attribute('numFmtId', arrFormats[i]._index.toString()); + final String formatString = arrFormats[i]._formatString!.replaceAll("'", '"'); builder.attribute('formatCode', formatString); }); @@ -1973,6 +3682,37 @@ class SerializeWorkbook { }); } + Future _saveFontsAsync(XmlBuilder builder) async { + builder.element('fonts', nest: () async { + builder.attribute('count', _workbook.fonts.length.toString()); + if (_workbook.fonts.isNotEmpty) { + for (int i = 0; i < _workbook.fonts.length; i++) { + final Font font = _workbook.fonts[i]; + builder.element('font', nest: () async { + if (font.bold) { + builder.element('b', nest: () async {}); + } + if (font.italic) { + builder.element('i', nest: () async {}); + } + if (font.underline) { + builder.element('u', nest: () async {}); + } + builder.element('sz', nest: () async { + builder.attribute('val', font.size.toString()); + }); + builder.element('color', nest: () async { + builder.attribute('rgb', font.color); + }); + builder.element('name', nest: () async { + builder.attribute('val', font.name); + }); + }); + } + } + }); + } + /// Serialize fills. void _saveFills(XmlBuilder builder) { builder.element('fills', nest: () { @@ -2005,6 +3745,37 @@ class SerializeWorkbook { }); } + Future _saveFillsAsync(XmlBuilder builder) async { + builder.element('fills', nest: () async { + builder.attribute('count', (_workbook.fills.length + 2).toString()); + builder.element('fill', nest: () async { + builder.element('patternFill', nest: () async { + builder.attribute('patternType', 'none'); + }); + }); + builder.element('fill', nest: () async { + builder.element('patternFill', nest: () async { + builder.attribute('patternType', 'gray125'); + }); + }); + if (_workbook.fills.isNotEmpty) { + _workbook.fills.forEach((String key, int value) { + builder.element('fill', nest: () async { + builder.element('patternFill', nest: () async { + builder.attribute('patternType', 'solid'); + builder.element('fgColor', nest: () async { + builder.attribute('rgb', key); + }); + builder.element('bgColor', nest: () async { + builder.attribute('rgb', 'FFFFFFFF'); + }); + }); + }); + }); + } + }); + } + /// Serialize borders. void _saveBorders(XmlBuilder builder) { builder.element('borders', nest: () { @@ -2034,6 +3805,34 @@ class SerializeWorkbook { }); } + Future _saveBordersAsync(XmlBuilder builder) async { + builder.element('borders', nest: () async { + builder.attribute('count', (_workbook.borders.length + 1).toString()); + builder.element('border', nest: () async { + builder.element('left', nest: () async {}); + builder.element('right', nest: () async {}); + builder.element('top', nest: () async {}); + builder.element('bottom', nest: () async {}); + builder.element('diagonal', nest: () async {}); + }); + if (_workbook.borders.isNotEmpty) { + for (final Borders borders in _workbook.borders) { + if (Workbook._isAllBorder(borders)) { + builder.element('border', nest: () async { + _serializeBorderAsync(borders.all, builder, 'left'); + _serializeBorderAsync(borders.all, builder, 'right'); + _serializeBorderAsync(borders.all, builder, 'top'); + _serializeBorderAsync(borders.all, builder, 'bottom'); + _serializeBorderAsync(borders.all, builder, 'diagonal'); + }); + } else { + _serializeBordersAsync(borders, builder); + } + } + } + }); + } + /// serializeBorders collection. void _serializeBorders(Borders borders, XmlBuilder builder) { builder.element('border', nest: () { @@ -2044,6 +3843,16 @@ class SerializeWorkbook { }); } + Future _serializeBordersAsync( + Borders borders, XmlBuilder builder) async { + builder.element('border', nest: () async { + _serializeBorderAsync(borders.left, builder, 'left'); + _serializeBorderAsync(borders.right, builder, 'right'); + _serializeBorderAsync(borders.top, builder, 'top'); + _serializeBorderAsync(borders.bottom, builder, 'bottom'); + }); + } + /// Serialize borders. void _serializeBorder(Border border, XmlBuilder builder, String borderType) { builder.element(borderType, nest: () { @@ -2059,6 +3868,21 @@ class SerializeWorkbook { }); } + Future _serializeBorderAsync( + Border border, XmlBuilder builder, String borderType) async { + builder.element(borderType, nest: () async { + builder.attribute( + 'style', border.lineStyle.toString().split('.').toList().removeAt(1)); + builder.element('color', nest: () async { + if (border.color.length == 7) { + builder.attribute('rgb', 'FF${border.color.replaceAll('#', '')}'); + } else { + builder.attribute('rgb', border.color); + } + }); + }); + } + /// Serialize cell styles xfs. void _saveCellStyleXfs(XmlBuilder builder) { builder.element('cellStyleXfs', nest: () { @@ -2084,6 +3908,30 @@ class SerializeWorkbook { }); } + Future _saveCellStyleXfsAsync(XmlBuilder builder) async { + builder.element('cellStyleXfs', nest: () async { + builder.attribute( + 'count', (_workbook._cellStyleXfs.length + 1).toString()); + builder.element('xf', nest: () async { + builder.attribute('numFmtId', '0'); + builder.attribute('fontId', '0'); + builder.attribute('fillId', '0'); + builder.attribute('borderId', '0'); + }); + if (_workbook._cellStyleXfs.isNotEmpty) { + for (final _CellStyleXfs cellStyleXfs in _workbook._cellStyleXfs) { + builder.element('xf', nest: () async { + builder.attribute( + 'numFmtId', cellStyleXfs._numberFormatId.toString()); + builder.attribute('fontId', cellStyleXfs._fontId.toString()); + builder.attribute('fillId', cellStyleXfs._fillId.toString()); + builder.attribute('borderId', cellStyleXfs._borderId.toString()); + }); + } + } + }); + } + /// Serialize cell styles xfs. void _saveCellXfs(XmlBuilder builder) { builder.element('cellXfs', nest: () { @@ -2104,6 +3952,25 @@ class SerializeWorkbook { }); } + Future _saveCellXfsAsync(XmlBuilder builder) async { + builder.element('cellXfs', nest: () async { + builder.attribute('count', _workbook._cellXfs.length.toString()); + if (_workbook._cellXfs.isNotEmpty) { + for (final _CellXfs cellXf in _workbook._cellXfs) { + builder.element('xf', nest: () async { + builder.attribute('numFmtId', cellXf._numberFormatId.toString()); + builder.attribute('fontId', cellXf._fontId.toString()); + builder.attribute('fillId', cellXf._fillId.toString()); + builder.attribute('borderId', cellXf._borderId.toString()); + builder.attribute('xfId', cellXf._xfId.toString()); + _saveAlignmentAsync(cellXf, builder); + _saveProtectionAsync(cellXf, builder); + }); + } + } + }); + } + ///Serialize Protection. void _saveProtection(_CellStyleXfs cellXf, XmlBuilder builder) { if (cellXf._locked != 1) { @@ -2113,6 +3980,15 @@ class SerializeWorkbook { } } + Future _saveProtectionAsync( + _CellStyleXfs cellXf, XmlBuilder builder) async { + if (cellXf._locked != 1) { + builder.element('protection', nest: () async { + builder.attribute('locked', cellXf._locked.toString()); + }); + } + } + /// Serialize alignment. void _saveAlignment(_CellStyleXfs cellXf, XmlBuilder builder) { builder.element('alignment', nest: () { @@ -2136,24 +4012,72 @@ class SerializeWorkbook { }); } - /// Serialize cell styles. - void _saveGlobalCellstyles(XmlBuilder builder) { - final int length = _workbook._globalStyles.length + 1; - builder.element('cellStyles', nest: () { - builder.attribute('count', length.toString()); - builder.element('cellStyle', nest: () { - builder.attribute('name', 'Normal'); - builder.attribute('xfId', '0'); - builder.attribute('builtinId', '0'); - }); - _workbook._globalStyles.forEach((String key, _GlobalStyle value) { - builder.element('cellStyle', nest: () { - if (key != '') { - builder.attribute('name', key); - builder.attribute( - 'xfId', _workbook._globalStyles[key]!._xfId.toString()); - if (_workbook._globalStyles[key]!._builtinId != 0) { - builder.attribute('builtinId', + Future _saveAlignmentAsync( + _CellStyleXfs cellXf, XmlBuilder builder) async { + builder.element('alignment', nest: () async { + if (cellXf._alignment != null) { + if (cellXf._alignment!.horizontal != '') { + builder.attribute( + 'horizontal', cellXf._alignment!.horizontal.toLowerCase()); + } + if (cellXf._alignment!.indent != 0) { + builder.attribute('indent', cellXf._alignment!.indent.toString()); + } else if (cellXf._alignment!.rotation != 0) { + builder.attribute( + 'textRotation', cellXf._alignment!.rotation.toString()); + } + if (cellXf._alignment!.vertical != '') { + builder.attribute( + 'vertical', cellXf._alignment!.vertical.toLowerCase()); + } + builder.attribute('wrapText', cellXf._alignment!.wrapText.toString()); + } + }); + } + + /// Serialize cell styles. + void _saveGlobalCellstyles(XmlBuilder builder) { + final int length = _workbook._globalStyles.length + 1; + builder.element('cellStyles', nest: () { + builder.attribute('count', length.toString()); + builder.element('cellStyle', nest: () { + builder.attribute('name', 'Normal'); + builder.attribute('xfId', '0'); + builder.attribute('builtinId', '0'); + }); + _workbook._globalStyles.forEach((String key, _GlobalStyle value) { + builder.element('cellStyle', nest: () { + if (key != '') { + builder.attribute('name', key); + builder.attribute( + 'xfId', _workbook._globalStyles[key]!._xfId.toString()); + if (_workbook._globalStyles[key]!._builtinId != 0) { + builder.attribute('builtinId', + _workbook._globalStyles[key]!._builtinId.toString()); + } + } + }); + }); + }); + } + + Future _saveGlobalCellstylesAsync(XmlBuilder builder) async { + final int length = _workbook._globalStyles.length + 1; + builder.element('cellStyles', nest: () async { + builder.attribute('count', length.toString()); + builder.element('cellStyle', nest: () async { + builder.attribute('name', 'Normal'); + builder.attribute('xfId', '0'); + builder.attribute('builtinId', '0'); + }); + _workbook._globalStyles.forEach((String key, _GlobalStyle value) { + builder.element('cellStyle', nest: () async { + if (key != '') { + builder.attribute('name', key); + builder.attribute( + 'xfId', _workbook._globalStyles[key]!._xfId.toString()); + if (_workbook._globalStyles[key]!._builtinId != 0) { + builder.attribute('builtinId', _workbook._globalStyles[key]!._builtinId.toString()); } } @@ -2225,6 +4149,27 @@ class SerializeWorkbook { } } + Future _serializeDataValidationsAsync( + XmlBuilder builder, Worksheet sheet) async { + final _DataValidationTable dataValidationTable = sheet._dvTable; + + if (dataValidationTable._count == 0) { + return; + } + + for (int dvTable = 0; dvTable < dataValidationTable._count; dvTable++) { + for (int dvCollection = 0; + dvCollection < + dataValidationTable._dataValidationCollectionList.length; + dvCollection++) { + final _DataValidationCollection dataValidationCollection = + dataValidationTable._dataValidationCollectionList[dvCollection]; + _serializeDataValidationCollectionAsync( + builder, dataValidationCollection); + } + } + } + ///Serialize DataValidation Collection void _serializeDataValidationCollection( XmlBuilder builder, _DataValidationCollection dataValidationCollection) { @@ -2253,6 +4198,31 @@ class SerializeWorkbook { }); } + Future _serializeDataValidationCollectionAsync(XmlBuilder builder, + _DataValidationCollection dataValidationCollection) async { + if (dataValidationCollection.count == 0) { + return; + } + + builder.element('dataValidations', nest: () async { + _serializeAttributeIntAsync( + builder, 'count', dataValidationCollection.count, 0); + if (dataValidationCollection._isPromptBoxPositionFixedVal) { + _serializeAttributeIntAsync(builder, 'xWindow', + dataValidationCollection._promptBoxVPositionVal, 0); + _serializeAttributeIntAsync(builder, 'yWindow', + dataValidationCollection._promptBoxHPositionVal, 0); + } + + for (int dvList = 0; + dvList < dataValidationCollection._dataValidationList.length; + dvList++) { + _serializeDataValidationAsync( + builder, dataValidationCollection._dataValidationList[dvList]); + } + }); + } + ///DataType properties for DataValidation String _getDataType(ExcelDataValidationType dataTypeDataVal) { switch (dataTypeDataVal) { @@ -2334,6 +4304,13 @@ class SerializeWorkbook { } } + Future _serializeDataValStringAttributeAsync(XmlBuilder builder, + String attributeName, String value, String defaultValue) async { + if (value != defaultValue) { + builder.attribute(attributeName, value); + } + } + /// Serialize DataValidation void _serializeDataValidation( XmlBuilder builder, _DataValidationImpl dataValidationImpl) { @@ -2524,595 +4501,962 @@ class SerializeWorkbook { }); } - void _serializeConditionalFormatting(XmlBuilder builder, Worksheet sheet) { - if (sheet.conditionalFormats.isNotEmpty) { - final int iCount = sheet.conditionalFormats.length; - int iPriority = 1; - int iPriorityCount = 0; - for (int i = 0; i < iCount; i++) { - final _ConditionalFormatsImpl conditionalFormats = - sheet.conditionalFormats[i]; - final List result = _serializeConditionalFormats( - builder, _iDxfIndex, iPriority, iPriorityCount, conditionalFormats); - _iDxfIndex = result[0] as int; - iPriority = result[1] as int; - iPriorityCount = result[2] as int; - } - } - } + Future _serializeDataValidationAsync( + XmlBuilder builder, _DataValidationImpl dataValidationImpl) async { + builder.element('dataValidation', nest: () { + final ExcelDataValidationType dataType = dataValidationImpl.allowType; - List _serializeConditionalFormats(XmlBuilder builder, int iDxfIndex, - int iPriority, int iPriorityCount, _ConditionalFormatsImpl formats) { - final int iRulesCount = formats.count; - bool serializeCF = false; - if (iRulesCount == 0) - return [iDxfIndex, iPriority, iPriorityCount]; - for (final _ConditionalFormatImpl format in formats.innerList) { - if (format.formatType == ExcelCFType.iconSet) { - _IconSetImpl? iconSet; - if (format.iconSet != null) { - iconSet = format.iconSet as _IconSetImpl?; - } - if (iconSet != null && iconSet._isCustom) { - format._bCFHasExtensionList = true; - } else if (format.iconSet!.iconSet == ExcelIconSetType.threeTriangles || - format.iconSet!.iconSet == ExcelIconSetType.threeStars || - format.iconSet!.iconSet == ExcelIconSetType.fiveBoxes) { - format._bCFHasExtensionList = true; - } - } - if (!format._bCFHasExtensionList) { - serializeCF = true; + if (dataType != ExcelDataValidationType.any) { + builder.attribute('type', _getDataType(dataType)); } - } - if (serializeCF) { - builder.element('conditionalFormatting', nest: () { - builder.attribute('sqref', formats._cellList); - int iCount = iRulesCount + iPriorityCount; - iPriorityCount += iRulesCount; - for (int i = 0; i < iRulesCount; i++) { - final ConditionalFormat condition = formats.innerList[i]; - if (!(condition as _ConditionalFormatImpl)._bCFHasExtensionList) { - final List result = _serializeCondition( - builder, condition, '', iDxfIndex, iPriority, iCount); - iDxfIndex = result[0] as int; - iPriority = result[1] as int; - iCount = result[2] as int; - } - } - }); - } - return [iDxfIndex, iPriority, iPriorityCount]; - } - List _serializeCondition( - XmlBuilder builder, - ConditionalFormat condition, - String prefix, - int iDxfIndex, - int iPriority, - int iCount) { - final _ConditionalFormatImpl condFormat = - condition as _ConditionalFormatImpl; - _IconSetImpl? iconSet; - if (condFormat.iconSet != null) { - iconSet = condFormat.iconSet as _IconSetImpl?; - } - final ExcelCFType cfType = condition.formatType; - final ExcelComparisonOperator comparisonOperator = condition.operator; - final CFTimePeriods cfTimePeriod = condition.timePeriodType; - builder.element('${prefix}cfRule', nest: () { - builder.attribute('type', _getCFType(cfType, comparisonOperator)); - if (_checkFormat(condition)) { - builder.attribute('dxfId', iDxfIndex.toString()); - iDxfIndex++; - } - if (condition.stopIfTrue) { - builder.attribute('stopIfTrue', '1'); - } - if (cfType == ExcelCFType.cellValue) { - builder.attribute( - 'operator', _getCFComparisonOperatorName(condition.operator)); + final ExcelDataValidationErrorStyle errorStyle = + dataValidationImpl.errorStyle; + + if (errorStyle != ExcelDataValidationErrorStyle.stop) { + builder.attribute('errorStyle', _getErrorStyle(errorStyle)); } - if (cfType == ExcelCFType.specificText) { + final ExcelDataValidationComparisonOperator comparisonOperator = + dataValidationImpl.comparisonOperator; + + if (comparisonOperator != ExcelDataValidationComparisonOperator.between && + _getDataType(dataType) != 'custom') { builder.attribute( - 'operator', _getCFComparisonOperatorName(condition.operator)); - // ignore: unnecessary_null_checks - builder.attribute('text', condition.text!); - } - if (cfType == ExcelCFType.timePeriod) { - builder.attribute('timePeriod', _getCFTimePeriodType(cfTimePeriod)); + 'operator', _getComparisonOperator(comparisonOperator)); } - builder.attribute('priority', iCount); - iCount--; + _serializeAttributesAsync( + builder, 'allowBlank', dataValidationImpl.isEmptyCellAllowed, false); - if (cfType == ExcelCFType.topBottom) { - _serializeAttributes(builder, 'bottom', - condition.topBottom!.type == ExcelCFTopBottomType.bottom, false); - _serializeAttributes( - builder, 'percent', condition.topBottom!.percent, false); - builder.attribute('rank', condition.topBottom!.rank.toString()); - } - if (cfType == ExcelCFType.aboveBelowAverage) { - final String avgStrType = condition.aboveBelowAverage!.averageType - .toString() - .split('.') - .toList() - .removeAt(1) - .toLowerCase(); - _serializeAttributes( - builder, 'aboveAverage', !avgStrType.contains('below'), true); - _serializeAttributes( - builder, 'equalAverage', avgStrType.contains('equal'), false); - if (avgStrType.contains('stddev')) { - builder.attribute( - 'stdDev', condition.aboveBelowAverage!.stdDevValue.toString()); + _serializeAttributesAsync(builder, 'showDropDown', + dataValidationImpl.isSuppressDropDownArrow, false); + + _serializeAttributesAsync( + builder, 'showInputMessage', dataValidationImpl.showPromptBox, false); + + _serializeAttributesAsync( + builder, 'showErrorMessage', dataValidationImpl.showErrorBox, false); + + _serializeDataValStringAttributeAsync( + builder, 'errorTitle', dataValidationImpl.errorBoxTitle, ''); + + _serializeDataValStringAttributeAsync( + builder, 'error', dataValidationImpl.errorBoxText, ''); + + _serializeDataValStringAttributeAsync( + builder, 'promptTitle', dataValidationImpl.promptBoxTitle, ''); + + _serializeDataValStringAttributeAsync( + builder, 'prompt', dataValidationImpl.promptBoxText, ''); + + _serializeDataValStringAttributeAsync( + builder, 'sqref', dataValidationImpl._cellRange, ''); + + if ('textLength' == _getDataType(dataType) || + 'decimal' == _getDataType(dataType) || + 'whole' == _getDataType(dataType)) { + final String firstFormula = dataValidationImpl.firstFormula; + final String secondFormula = dataValidationImpl.secondFormula; + if (firstFormula != '') { + builder.element('formula1', nest: firstFormula); } - } - if (condition.firstFormula != '') { - String v1 = condition.firstFormula; - if (v1[0] == '=') { - v1 = v1.substring(1); - } - v1 = v1.replaceAll("'", '"'); - builder.element('${prefix}formula', nest: v1); - } - if (condition.secondFormula != '') { - String v1 = condition.secondFormula; - if (v1[0] == '=') { - v1 = v1.substring(1); - } - v1 = v1.replaceAll("'", '"'); - builder.element('${prefix}formula', nest: v1); - } - if (cfType == ExcelCFType.dataBar) { - _serializeDataBar(builder, condition.dataBar!); - } - if (cfType == ExcelCFType.colorScale) { - _serializeColorScale(builder, condition.colorScale!); - } else if (cfType == ExcelCFType.iconSet) { - if (condFormat._bCFHasExtensionList || - (iconSet != null && iconSet._isCustom)) { - _serializeIconSet(builder, iconSet!, condFormat._bCFHasExtensionList, - iconSet._isCustom); - } else { - _serializeIconSet(builder, iconSet!, false, false); + if (secondFormula != '') { + builder.element('formula2', nest: secondFormula); } - } - if (condition.dataBar != null && - (condition.dataBar! as _DataBarImpl)._hasExtensionList) { - builder.element('extLst', nest: () { - builder.namespace( - 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'); - builder.element('ext', nest: () { - builder.namespace( - 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'); - - builder.attribute('uri', '{B025F937-C7B1-47D3-B67F-A62EFF666E3E}'); - builder.attribute('xmlns:x14', - 'http://schemas.microsoft.com/office/spreadsheetml/2009/9/main'); + } else if ('time' == _getDataType(dataType)) { + final String firstFormula = dataValidationImpl.firstFormula; + final String secondFormula = dataValidationImpl.secondFormula; + final List firstFormulaCheck = firstFormula.split(':'); + final List secondFormulaCheck = secondFormula.split(':'); + late Duration duration; + late double firstFormulaVal; + late double secondFormulaVal; + late String firstTimeVal; + late String secondTimeVal; + if (firstFormulaCheck.length == 2) { + final int? hour = int.tryParse(firstFormulaCheck[0]); + final int? min = int.tryParse(firstFormulaCheck[1]); - builder.element('x14:id', - nest: (condition.dataBar! as _DataBarImpl)._stGUID); - }); - }); - } - }); - return [iDxfIndex, iPriority, iCount]; - } + if (hour != null && min != null) { + duration = Duration(hours: hour, minutes: min); - /// Serializes color scale of conditional format. - void _serializeColorScale(XmlBuilder builder, ColorScale colorScale) { - builder.element('colorScale', nest: () { - final List arrConditions = colorScale.criteria; - for (int i = 0; i < arrConditions.length; i++) { - _serializeConditionValueObject( - builder, arrConditions[i], false, false, false); - } + firstFormulaVal = (duration.inMinutes / 60) / 24; + firstTimeVal = firstFormulaVal.toString(); + } else { + firstTimeVal = firstFormula; + } + } - for (int i = 0; i < arrConditions.length; i++) { - _serializeRgbColor(builder, 'color', arrConditions[i].formatColor); - } - }); - } + if (firstFormulaCheck.length == 3) { + final int? hour = int.tryParse(firstFormulaCheck[0]); + final int? min = int.tryParse(firstFormulaCheck[1]); + final int? sec = int.tryParse(firstFormulaCheck[2]); - /// Serializes icon set. - void _serializeIconSet(XmlBuilder builder, IconSet iconSet, - bool cfHasExtensionList, bool isCustom) { - String element; - if (cfHasExtensionList || isCustom) { - element = 'x14:iconSet'; - } else { - element = 'iconSet'; - } + if (hour != null && min != null && sec != null) { + duration = Duration(hours: hour, minutes: min, seconds: sec); - builder.element(element, nest: () { - final int index = iconSet.iconSet.index; - final String strType = _iconSetTypeNames[index]; + firstFormulaVal = ((duration.inSeconds / 60) / 60) / 24; + firstTimeVal = firstFormulaVal.toString(); + } else { + firstTimeVal = firstFormula; + } + } - builder.attribute('iconSet', strType); - _serializeAttributes(builder, 'percent', iconSet.percentileValues, false); - _serializeAttributes(builder, 'reverse', iconSet.reverseOrder, false); - _serializeAttributes(builder, 'showValue', !iconSet.showIconOnly, true); + if (secondFormulaCheck.length == 2) { + final int? hour = int.tryParse(secondFormulaCheck[0]); + final int? min = int.tryParse(secondFormulaCheck[1]); - if (isCustom) { - _serializeAttributes(builder, 'custom', true, false); - } + if (hour != null && min != null) { + duration = Duration(hours: hour, minutes: min); - final List arrConditions = iconSet.iconCriteria; - for (int i = 0; i < arrConditions.length; i++) { - _serializeConditionValueObject( - builder, arrConditions[i], true, cfHasExtensionList, isCustom); - } - if (isCustom) { - for (int i = 0; i < arrConditions.length; i++) { - _serializeCustomCFIcon( - builder, arrConditions[i] as IconConditionValue, true); + secondFormulaVal = (duration.inMinutes / 60) / 24; + secondTimeVal = secondFormulaVal.toString(); + } else { + secondTimeVal = secondFormula; + } } - } - }); - } - - /// Serializes data bar. - void _serializeDataBar(XmlBuilder builder, DataBar dataBar) { - builder.element('dataBar', nest: () { - _serializeAttributeInt(builder, 'minLength', dataBar.percentMin, 0); - _serializeAttributeInt(builder, 'maxLength', dataBar.percentMax, 100); - _serializeAttributes(builder, 'showValue', dataBar.showValue, true); - _serializeConditionValueObjectForDataBar( - builder, dataBar.minPoint, false, true); - _serializeConditionValueObjectForDataBar( - builder, dataBar.maxPoint, false, false); - _serializeRgbColor(builder, 'color', dataBar.barColor); - }); - } + if (secondFormulaCheck.length == 3) { + final int? hour = int.tryParse(secondFormulaCheck[0]); + final int? min = int.tryParse(secondFormulaCheck[1]); + final int? sec = int.tryParse(secondFormulaCheck[2]); - /// Serializes conditional value object. - void _serializeConditionValueObject( - XmlBuilder builder, - ConditionValue conditionValue, - bool isIconSet, - bool cfHasExtensionList, - bool isCustom) { - String prefix = ''; - if (cfHasExtensionList || isCustom) { - prefix = 'x14:'; - } - builder.element('${prefix}cfvo', nest: () { - final int index = conditionValue.type.index; - final String strType = _valueTypes[index]; - String value = conditionValue.value; + if (hour != null && min != null && sec != null) { + duration = Duration(hours: hour, minutes: min, seconds: sec); - builder.attribute('type', strType); + secondFormulaVal = ((duration.inSeconds / 60) / 60) / 24; + secondTimeVal = secondFormulaVal.toString(); + } else { + secondTimeVal = secondFormula; + } + } - if (strType == 'formula' && value.startsWith(Range._defaultEquivalent)) { - value = value.replaceAll(Range._defaultEquivalent, ''); - } + if (firstFormulaCheck.length != 2 && firstFormulaCheck.length != 3) { + firstTimeVal = firstFormula; + } + if (secondFormulaCheck.length != 2 && secondFormulaCheck.length != 3) { + secondTimeVal = secondFormula; + } + if (firstTimeVal != '') { + builder.element('formula1', nest: firstTimeVal); + } + if (secondTimeVal != '') { + builder.element('formula2', nest: secondTimeVal); + } + } else if ('list' == _getDataType(dataType)) { + final List firstFormula = dataValidationImpl.listOfValues; + late String listOfValues = ''; + if (firstFormula.isNotEmpty) { + for (int listVal = 0; listVal < firstFormula.length; listVal++) { + late String comma; + if (listVal == 0) { + comma = ''; + } else { + comma = ','; + } - if (!cfHasExtensionList) { - builder.attribute('val', value); - } else if (!cfHasExtensionList) { - builder.attribute('val', value); - } + listOfValues = listOfValues + comma + firstFormula[listVal]; + } - builder.attribute('gte', (conditionValue.operator).index.toString()); + builder.element('formula1', nest: '" $listOfValues"'); + } else { + final String firstFormulaSelect = + dataValidationImpl._dataRangeVal.addressGlobal; - if (cfHasExtensionList || isCustom) { - builder.element('xm:f', nest: value); + builder.element('formula1', nest: firstFormulaSelect); + } + } else if ('custom' == _getDataType(dataType)) { + final String firstFormula = dataValidationImpl.firstFormula; + final String secondFormula = dataValidationImpl.secondFormula; + if (firstFormula.isNotEmpty) { + builder.element('formula1', nest: firstFormula); + } else if (firstFormula == '' && secondFormula.isNotEmpty) { + builder.element('formula1', nest: secondFormula); + } + } else { + final DateTime firstDateTime = dataValidationImpl._firstDateTimeVal; + final DateTime secondDateTime = dataValidationImpl.secondDateTime; + final String firstDateTimeVal = + Range._toOADate(firstDateTime).toString(); + final String secondDateTimeVal = + Range._toOADate(secondDateTime).toString(); + if (firstDateTime != DateTime(1)) { + builder.element('formula1', nest: firstDateTimeVal); + } + if (secondDateTime != DateTime(1)) { + builder.element('formula2', nest: secondDateTimeVal); + } } }); } - /// Serializes conditional value object. - void _serializeConditionValueObjectForDataBar(XmlBuilder builder, - ConditionValue conditionValue, bool isIconSet, bool isMinPoint) { - builder.element('cfvo', nest: () { - int index = conditionValue.type.index; - if (index == 7) { - if (isMinPoint) { - index = 2; - } else { - index = 3; - } - } - final String strType = _valueTypes[index]; - builder.attribute('type', strType); - builder.attribute('val', conditionValue.value); - if (isIconSet) { - builder.attribute('gte', (conditionValue.operator).index.toString()); + void _serializeConditionalFormatting(XmlBuilder builder, Worksheet sheet) { + if (sheet.conditionalFormats.isNotEmpty) { + final int iCount = sheet.conditionalFormats.length; + int iPriority = 1; + int iPriorityCount = 0; + for (int i = 0; i < iCount; i++) { + final _ConditionalFormatsImpl conditionalFormats = + sheet.conditionalFormats[i]; + final List result = _serializeConditionalFormats( + builder, _iDxfIndex, iPriority, iPriorityCount, conditionalFormats); + _iDxfIndex = result[0] as int; + iPriority = result[1] as int; + iPriorityCount = result[2] as int; } - }); + } } - /// Serializes Custom iconset object. - void _serializeCustomCFIcon( - XmlBuilder builder, IconConditionValue conditionValue, bool isIconSet) { - builder.element('x14:cfIcon', nest: () { - String iconType = ''; - if (conditionValue.iconSet.toString() == '-1') { - iconType = 'NoIcons'; - } else { - iconType = _iconSetTypeNames[conditionValue.iconSet.index]; + Future _serializeConditionalFormattingAsync( + XmlBuilder builder, Worksheet sheet) async { + if (sheet.conditionalFormats.isNotEmpty) { + final int iCount = sheet.conditionalFormats.length; + int iPriority = 1; + int iPriorityCount = 0; + for (int i = 0; i < iCount; i++) { + final _ConditionalFormatsImpl conditionalFormats = + sheet.conditionalFormats[i]; + final List result = _serializeConditionalFormats( + builder, _iDxfIndex, iPriority, iPriorityCount, conditionalFormats); + _iDxfIndex = result[0] as int; + iPriority = result[1] as int; + iPriorityCount = result[2] as int; } - final String iconIndex = conditionValue.index.toString(); - builder.attribute('iconSet', iconType); - builder.attribute('iconId', iconIndex); - }); + } } - /// Serializes Rgb color value. - void _serializeRgbColor(XmlBuilder builder, String tagName, String color) { - builder.element(tagName, nest: () { - String colorValue = color; - if (colorValue.length <= 7) { - colorValue = 'FF${color.replaceAll('#', '')}'; + List _serializeConditionalFormats(XmlBuilder builder, int iDxfIndex, + int iPriority, int iPriorityCount, _ConditionalFormatsImpl formats) { + final int iRulesCount = formats.count; + bool serializeCF = false; + if (iRulesCount == 0) + return [iDxfIndex, iPriority, iPriorityCount]; + for (final _ConditionalFormatImpl format in formats.innerList) { + if (format.formatType == ExcelCFType.iconSet) { + _IconSetImpl? iconSet; + if (format.iconSet != null) { + iconSet = format.iconSet as _IconSetImpl?; + } + if (iconSet != null && iconSet._isCustom) { + format._bCFHasExtensionList = true; + } else if (format.iconSet!.iconSet == ExcelIconSetType.threeTriangles || + format.iconSet!.iconSet == ExcelIconSetType.threeStars || + format.iconSet!.iconSet == ExcelIconSetType.fiveBoxes) { + format._bCFHasExtensionList = true; + } } - builder.attribute('rgb', colorValue); - }); - } - - /// Returns CF type string name. - String _getCFType(ExcelCFType typeCF, ExcelComparisonOperator compOperator) { - switch (typeCF) { - case ExcelCFType.cellValue: - return 'cellIs'; - case ExcelCFType.specificText: - switch (compOperator) { - case ExcelComparisonOperator.beginsWith: - return 'beginsWith'; - case ExcelComparisonOperator.containsText: - return 'containsText'; - case ExcelComparisonOperator.endsWith: - return 'endsWith'; - case ExcelComparisonOperator.notContainsText: - return 'notContainsText'; - case ExcelComparisonOperator.none: - case ExcelComparisonOperator.between: - case ExcelComparisonOperator.notBetween: - case ExcelComparisonOperator.equal: - case ExcelComparisonOperator.notEqual: - case ExcelComparisonOperator.greater: - case ExcelComparisonOperator.less: - case ExcelComparisonOperator.greaterOrEqual: - case ExcelComparisonOperator.lessOrEqual: - throw Exception('ComOperator'); + if (!format._bCFHasExtensionList) { + serializeCF = true; + } + } + if (serializeCF) { + builder.element('conditionalFormatting', nest: () { + builder.attribute('sqref', formats._cellList); + int iCount = iRulesCount + iPriorityCount; + iPriorityCount += iRulesCount; + for (int i = 0; i < iRulesCount; i++) { + final ConditionalFormat condition = formats.innerList[i]; + if (!(condition as _ConditionalFormatImpl)._bCFHasExtensionList) { + final List result = _serializeCondition( + builder, condition, '', iDxfIndex, iPriority, iCount); + iDxfIndex = result[0] as int; + iPriority = result[1] as int; + iCount = result[2] as int; + } } - case ExcelCFType.formula: - return 'expression'; - case ExcelCFType.dataBar: - return 'dataBar'; - case ExcelCFType.unique: - return 'uniqueValues'; - case ExcelCFType.duplicate: - return 'duplicateValues'; - case ExcelCFType.iconSet: - return 'iconSet'; - case ExcelCFType.colorScale: - return 'colorScale'; - case ExcelCFType.blank: - return 'containsBlanks'; - case ExcelCFType.noBlank: - return 'notContainsBlanks'; - case ExcelCFType.containsErrors: - return 'containsErrors'; - case ExcelCFType.notContainsErrors: - return 'notContainsErrors'; - case ExcelCFType.timePeriod: - return 'timePeriod'; - case ExcelCFType.topBottom: - return 'top10'; - case ExcelCFType.aboveBelowAverage: - return 'aboveAverage'; + }); } + return [iDxfIndex, iPriority, iPriorityCount]; } - /// Returns CF comparison operator string name. - String _getCFComparisonOperatorName( - ExcelComparisonOperator comparisonOperator) { - switch (comparisonOperator) { - case ExcelComparisonOperator.between: - return 'between'; - case ExcelComparisonOperator.beginsWith: - return 'beginsWith'; - case ExcelComparisonOperator.containsText: - return 'containsText'; - case ExcelComparisonOperator.endsWith: - return 'endsWith'; - case ExcelComparisonOperator.notContainsText: - return 'notContains'; - case ExcelComparisonOperator.equal: - return 'equal'; - case ExcelComparisonOperator.greater: - return 'greaterThan'; - case ExcelComparisonOperator.greaterOrEqual: - return 'greaterThanOrEqual'; - case ExcelComparisonOperator.less: - return 'lessThan'; - case ExcelComparisonOperator.lessOrEqual: - return 'lessThanOrEqual'; - case ExcelComparisonOperator.none: - return 'notContains'; - case ExcelComparisonOperator.notBetween: - return 'notBetween'; - case ExcelComparisonOperator.notEqual: - return 'notEqual'; + List _serializeCondition( + XmlBuilder builder, + ConditionalFormat condition, + String prefix, + int iDxfIndex, + int iPriority, + int iCount) { + final _ConditionalFormatImpl condFormat = + condition as _ConditionalFormatImpl; + _IconSetImpl? iconSet; + if (condFormat.iconSet != null) { + iconSet = condFormat.iconSet as _IconSetImpl?; } - } + final ExcelCFType cfType = condition.formatType; + final ExcelComparisonOperator comparisonOperator = condition.operator; + final CFTimePeriods cfTimePeriod = condition.timePeriodType; + builder.element('${prefix}cfRule', nest: () { + builder.attribute('type', _getCFType(cfType, comparisonOperator)); + if (_checkFormat(condition)) { + builder.attribute('dxfId', iDxfIndex.toString()); + iDxfIndex++; + } + if (condition.stopIfTrue) { + builder.attribute('stopIfTrue', '1'); + } + if (cfType == ExcelCFType.cellValue) { + builder.attribute( + 'operator', _getCFComparisonOperatorName(condition.operator)); + } + if (cfType == ExcelCFType.specificText) { + builder.attribute( + 'operator', _getCFComparisonOperatorName(condition.operator)); + // ignore: unnecessary_null_checks + builder.attribute('text', condition.text!); + } + if (cfType == ExcelCFType.timePeriod) { + builder.attribute('timePeriod', _getCFTimePeriodType(cfTimePeriod)); + } - /// Return the CF time period string name - String _getCFTimePeriodType(CFTimePeriods cfTimePeriod) { - switch (cfTimePeriod) { - case CFTimePeriods.today: - return 'today'; - case CFTimePeriods.yesterday: - return 'yesterday'; - case CFTimePeriods.tomorrow: - return 'tomorrow'; - case CFTimePeriods.last7Days: - return 'last7Days'; - case CFTimePeriods.lastWeek: - return 'lastWeek'; - case CFTimePeriods.thisWeek: - return 'thisWeek'; - case CFTimePeriods.nextWeek: - return 'nextWeek'; - case CFTimePeriods.lastMonth: - return 'lastMonth'; - case CFTimePeriods.thisMonth: - return 'thisMonth'; - case CFTimePeriods.nextMonth: - return 'nextMonth'; - } - } + builder.attribute('priority', iCount); + iCount--; - /// Serialize Dxfs. - void _serialiseDxfs(XmlBuilder builder) { - builder.element('dxfs', nest: () { - for (final Worksheet sheet in _workbook.worksheets.innerList) { - if (sheet.autoFilters._innerList.isNotEmpty) { - for (int i = 0; i < sheet.autoFilters.count; i++) { - if (sheet.autoFilters[i]._filtertype == - _ExcelFilterType.colorFilter) { - _serializeDxfColorFilter( - builder, sheet.autoFilters[i] as _AutoFilterImpl); - } - } - } - if (sheet.conditionalFormats.isNotEmpty) { - final int iCount = sheet.conditionalFormats.length; - for (int i = 0; i < iCount; i++) { - final _ConditionalFormatsImpl condFormats = - sheet.conditionalFormats[i]; - for (final _ConditionalFormatImpl condition - in condFormats.innerList) { - if (_checkFormat(condition)) { - _serializeDxf(builder, condition); - } - } - } + if (cfType == ExcelCFType.topBottom) { + _serializeAttributes(builder, 'bottom', + condition.topBottom!.type == ExcelCFTopBottomType.bottom, false); + _serializeAttributes( + builder, 'percent', condition.topBottom!.percent, false); + builder.attribute('rank', condition.topBottom!.rank.toString()); + } + if (cfType == ExcelCFType.aboveBelowAverage) { + final String avgStrType = condition.aboveBelowAverage!.averageType + .toString() + .split('.') + .toList() + .removeAt(1) + .toLowerCase(); + _serializeAttributes( + builder, 'aboveAverage', !avgStrType.contains('below'), true); + _serializeAttributes( + builder, 'equalAverage', avgStrType.contains('equal'), false); + if (avgStrType.contains('stddev')) { + builder.attribute( + 'stdDev', condition.aboveBelowAverage!.stdDevValue.toString()); + } + } + if (condition.firstFormula != '') { + String v1 = condition.firstFormula; + if (v1[0] == '=') { + v1 = v1.substring(1); + } + v1 = v1.replaceAll("'", '"'); + builder.element('${prefix}formula', nest: v1); + } + if (condition.secondFormula != '') { + String v1 = condition.secondFormula; + if (v1[0] == '=') { + v1 = v1.substring(1); + } + v1 = v1.replaceAll("'", '"'); + builder.element('${prefix}formula', nest: v1); + } + if (cfType == ExcelCFType.dataBar) { + _serializeDataBar(builder, condition.dataBar!); + } + if (cfType == ExcelCFType.colorScale) { + _serializeColorScale(builder, condition.colorScale!); + } else if (cfType == ExcelCFType.iconSet) { + if (condFormat._bCFHasExtensionList || + (iconSet != null && iconSet._isCustom)) { + _serializeIconSet(builder, iconSet!, condFormat._bCFHasExtensionList, + iconSet._isCustom); + } else { + _serializeIconSet(builder, iconSet!, false, false); } } + if (condition.dataBar != null && + (condition.dataBar! as _DataBarImpl)._hasExtensionList) { + builder.element('extLst', nest: () { + builder.namespace( + 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'); + builder.element('ext', nest: () { + builder.namespace( + 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'); + + builder.attribute('uri', '{B025F937-C7B1-47D3-B67F-A62EFF666E3E}'); + builder.attribute('xmlns:x14', + 'http://schemas.microsoft.com/office/spreadsheetml/2009/9/main'); + + builder.element('x14:id', + nest: (condition.dataBar! as _DataBarImpl)._stGUID); + }); + }); + } }); + return [iDxfIndex, iPriority, iCount]; } - ///Serialize color filter dxf style. - void _serializeDxfColorFilter( - XmlBuilder builder, _AutoFilterImpl autoFilter) { - builder.element('dxf', nest: () { - _serializeDxfColorFilterFill( - builder, autoFilter._filteredItems as _ColorFilter); + /// Serializes color scale of conditional format. + void _serializeColorScale(XmlBuilder builder, ColorScale colorScale) { + builder.element('colorScale', nest: () { + final List arrConditions = colorScale.criteria; + for (int i = 0; i < arrConditions.length; i++) { + _serializeConditionValueObject( + builder, arrConditions[i], false, false, false); + } + + for (int i = 0; i < arrConditions.length; i++) { + _serializeRgbColor(builder, 'color', arrConditions[i].formatColor); + } }); } - ///Serialize dxf color filter style fill. - void _serializeDxfColorFilterFill( - XmlBuilder builder, _ColorFilter colorFilter) { - String foreColor; - builder.element('fill', nest: () { - builder.element('patternFill', nest: () { - if (colorFilter._colorFilterType == ExcelColorFilterType.cellColor && - colorFilter._color == '#000000') - builder.attribute('patternType', 'none'); - else - builder.attribute('patternType', 'solid'); + /// Serializes icon set. + void _serializeIconSet(XmlBuilder builder, IconSet iconSet, + bool cfHasExtensionList, bool isCustom) { + String element; + if (cfHasExtensionList || isCustom) { + element = 'x14:iconSet'; + } else { + element = 'iconSet'; + } - if (colorFilter._color == '#000000') { - if (colorFilter._colorFilterType == ExcelColorFilterType.cellColor) { - builder.element('fgColor', nest: () { - builder.attribute('indexed', '64'); - }); - } else { - builder.element('fgColor', nest: () { - builder.attribute('indexed', '66'); - }); - } - } else { - if (colorFilter._color.length == 7) { - foreColor = 'FF${colorFilter._color.replaceAll('#', '')}'; - } else { - foreColor = colorFilter._color; - } - builder.element('fgColor', nest: () { - builder.attribute('rgb', foreColor); - }); + builder.element(element, nest: () { + final int index = iconSet.iconSet.index; + final String strType = _iconSetTypeNames[index]; + + builder.attribute('iconSet', strType); + _serializeAttributes(builder, 'percent', iconSet.percentileValues, false); + _serializeAttributes(builder, 'reverse', iconSet.reverseOrder, false); + _serializeAttributes(builder, 'showValue', !iconSet.showIconOnly, true); + + if (isCustom) { + _serializeAttributes(builder, 'custom', true, false); + } + + final List arrConditions = iconSet.iconCriteria; + for (int i = 0; i < arrConditions.length; i++) { + _serializeConditionValueObject( + builder, arrConditions[i], true, cfHasExtensionList, isCustom); + } + if (isCustom) { + for (int i = 0; i < arrConditions.length; i++) { + _serializeCustomCFIcon( + builder, arrConditions[i] as IconConditionValue, true); } - }); + } }); } - /// Serializes Dxf style. - void _serializeDxf(XmlBuilder builder, _ConditionalFormatImpl condition) { - builder.element('dxf', nest: () { - _serializeDxfFont(builder, condition); - _serializeDxfNumberFormat(builder, condition); - _serializeDxfFill(builder, condition); - _serializeDxfBorders(builder, condition); + /// Serializes data bar. + void _serializeDataBar(XmlBuilder builder, DataBar dataBar) { + builder.element('dataBar', nest: () { + _serializeAttributeInt(builder, 'minLength', dataBar.percentMin, 0); + _serializeAttributeInt(builder, 'maxLength', dataBar.percentMax, 100); + _serializeAttributes(builder, 'showValue', dataBar.showValue, true); + + _serializeConditionValueObjectForDataBar( + builder, dataBar.minPoint, false, true); + _serializeConditionValueObjectForDataBar( + builder, dataBar.maxPoint, false, false); + _serializeRgbColor(builder, 'color', dataBar.barColor); }); } - /// Serializes Dxf style font. - void _serializeDxfFont(XmlBuilder builder, _ConditionalFormatImpl condition) { - if (condition.isBold || - condition.isItalic || - condition.underline || - condition.fontColor != '#000000') { - builder.element('font', nest: () { - if (condition.isBold) { - builder.element('b', nest: () {}); - } - if (condition.isItalic) { - builder.element('i', nest: () {}); - } - if (condition.underline) { - builder.element('u', nest: () {}); - } - String fontColor; - if (condition.fontColor.length <= 7) { - fontColor = 'FF${condition.fontColor.replaceAll('#', '')}'; - } else { - fontColor = condition.fontColor; - } - builder.element('color', nest: () { - builder.attribute('rgb', fontColor); - }); - }); + /// Serializes conditional value object. + void _serializeConditionValueObject( + XmlBuilder builder, + ConditionValue conditionValue, + bool isIconSet, + bool cfHasExtensionList, + bool isCustom) { + String prefix = ''; + if (cfHasExtensionList || isCustom) { + prefix = 'x14:'; } - } + builder.element('${prefix}cfvo', nest: () { + final int index = conditionValue.type.index; + final String strType = _valueTypes[index]; + String value = conditionValue.value; - /// Serializes Dxf number format. - void _serializeDxfNumberFormat( - XmlBuilder builder, _ConditionalFormatImpl condition) { - if (condition.numberFormat != 'General') { - int index; - _Format? format; - if (_workbook.innerFormats._contains(condition._numberFormatIndex)) { - format = _workbook.innerFormats[condition._numberFormatIndex]; - index = format._index; - } else { - index = _workbook.innerFormats._createFormat(condition.numberFormat); + builder.attribute('type', strType); + + if (strType == 'formula' && value.startsWith(Range._defaultEquivalent)) { + value = value.replaceAll(Range._defaultEquivalent, ''); } - builder.element('numFmt', nest: () { - builder.attribute('numFmtId', index.toString()); - final String formatString = format!._formatString!.replaceAll("'", '"'); - builder.attribute('formatCode', formatString); - }); - } - } - /// Serializes Dxf style fill. - void _serializeDxfFill(XmlBuilder builder, _ConditionalFormatImpl condition) { - String backColor; - if (condition.backColor != '#FFFFFF') { - if (condition.backColor.length == 7) { - backColor = 'FF${condition.backColor.replaceAll('#', '')}'; - } else { - backColor = condition.backColor; + if (!cfHasExtensionList) { + builder.attribute('val', value); + } else if (!cfHasExtensionList) { + builder.attribute('val', value); } - builder.element('fill', nest: () { - builder.element('patternFill', nest: () { - builder.element('bgColor', nest: () { + + builder.attribute('gte', (conditionValue.operator).index.toString()); + + if (cfHasExtensionList || isCustom) { + builder.element('xm:f', nest: value); + } + }); + } + + /// Serializes conditional value object. + void _serializeConditionValueObjectForDataBar(XmlBuilder builder, + ConditionValue conditionValue, bool isIconSet, bool isMinPoint) { + builder.element('cfvo', nest: () { + int index = conditionValue.type.index; + if (index == 7) { + if (isMinPoint) { + index = 2; + } else { + index = 3; + } + } + final String strType = _valueTypes[index]; + builder.attribute('type', strType); + builder.attribute('val', conditionValue.value); + if (isIconSet) { + builder.attribute('gte', (conditionValue.operator).index.toString()); + } + }); + } + + /// Serializes Custom iconset object. + void _serializeCustomCFIcon( + XmlBuilder builder, IconConditionValue conditionValue, bool isIconSet) { + builder.element('x14:cfIcon', nest: () { + String iconType = ''; + if (conditionValue.iconSet.toString() == '-1') { + iconType = 'NoIcons'; + } else { + iconType = _iconSetTypeNames[conditionValue.iconSet.index]; + } + final String iconIndex = conditionValue.index.toString(); + builder.attribute('iconSet', iconType); + builder.attribute('iconId', iconIndex); + }); + } + + /// Serializes Rgb color value. + void _serializeRgbColor(XmlBuilder builder, String tagName, String color) { + builder.element(tagName, nest: () { + String colorValue = color; + if (colorValue.length <= 7) { + colorValue = 'FF${color.replaceAll('#', '')}'; + } + builder.attribute('rgb', colorValue); + }); + } + + Future _serializeRgbColorAsync( + XmlBuilder builder, String tagName, String color) async { + builder.element(tagName, nest: () async { + String colorValue = color; + if (colorValue.length <= 7) { + colorValue = 'FF${color.replaceAll('#', '')}'; + } + builder.attribute('rgb', colorValue); + }); + } + + /// Returns CF type string name. + String _getCFType(ExcelCFType typeCF, ExcelComparisonOperator compOperator) { + switch (typeCF) { + case ExcelCFType.cellValue: + return 'cellIs'; + case ExcelCFType.specificText: + switch (compOperator) { + case ExcelComparisonOperator.beginsWith: + return 'beginsWith'; + case ExcelComparisonOperator.containsText: + return 'containsText'; + case ExcelComparisonOperator.endsWith: + return 'endsWith'; + case ExcelComparisonOperator.notContainsText: + return 'notContainsText'; + case ExcelComparisonOperator.none: + case ExcelComparisonOperator.between: + case ExcelComparisonOperator.notBetween: + case ExcelComparisonOperator.equal: + case ExcelComparisonOperator.notEqual: + case ExcelComparisonOperator.greater: + case ExcelComparisonOperator.less: + case ExcelComparisonOperator.greaterOrEqual: + case ExcelComparisonOperator.lessOrEqual: + throw Exception('ComOperator'); + } + case ExcelCFType.formula: + return 'expression'; + case ExcelCFType.dataBar: + return 'dataBar'; + case ExcelCFType.unique: + return 'uniqueValues'; + case ExcelCFType.duplicate: + return 'duplicateValues'; + case ExcelCFType.iconSet: + return 'iconSet'; + case ExcelCFType.colorScale: + return 'colorScale'; + case ExcelCFType.blank: + return 'containsBlanks'; + case ExcelCFType.noBlank: + return 'notContainsBlanks'; + case ExcelCFType.containsErrors: + return 'containsErrors'; + case ExcelCFType.notContainsErrors: + return 'notContainsErrors'; + case ExcelCFType.timePeriod: + return 'timePeriod'; + case ExcelCFType.topBottom: + return 'top10'; + case ExcelCFType.aboveBelowAverage: + return 'aboveAverage'; + } + } + + /// Returns CF comparison operator string name. + String _getCFComparisonOperatorName( + ExcelComparisonOperator comparisonOperator) { + switch (comparisonOperator) { + case ExcelComparisonOperator.between: + return 'between'; + case ExcelComparisonOperator.beginsWith: + return 'beginsWith'; + case ExcelComparisonOperator.containsText: + return 'containsText'; + case ExcelComparisonOperator.endsWith: + return 'endsWith'; + case ExcelComparisonOperator.notContainsText: + return 'notContains'; + case ExcelComparisonOperator.equal: + return 'equal'; + case ExcelComparisonOperator.greater: + return 'greaterThan'; + case ExcelComparisonOperator.greaterOrEqual: + return 'greaterThanOrEqual'; + case ExcelComparisonOperator.less: + return 'lessThan'; + case ExcelComparisonOperator.lessOrEqual: + return 'lessThanOrEqual'; + case ExcelComparisonOperator.none: + return 'notContains'; + case ExcelComparisonOperator.notBetween: + return 'notBetween'; + case ExcelComparisonOperator.notEqual: + return 'notEqual'; + } + } + + /// Return the CF time period string name + String _getCFTimePeriodType(CFTimePeriods cfTimePeriod) { + switch (cfTimePeriod) { + case CFTimePeriods.today: + return 'today'; + case CFTimePeriods.yesterday: + return 'yesterday'; + case CFTimePeriods.tomorrow: + return 'tomorrow'; + case CFTimePeriods.last7Days: + return 'last7Days'; + case CFTimePeriods.lastWeek: + return 'lastWeek'; + case CFTimePeriods.thisWeek: + return 'thisWeek'; + case CFTimePeriods.nextWeek: + return 'nextWeek'; + case CFTimePeriods.lastMonth: + return 'lastMonth'; + case CFTimePeriods.thisMonth: + return 'thisMonth'; + case CFTimePeriods.nextMonth: + return 'nextMonth'; + } + } + + /// Serialize Dxfs. + void _serialiseDxfs(XmlBuilder builder) { + builder.element('dxfs', nest: () { + for (final Worksheet sheet in _workbook.worksheets.innerList) { + if (sheet.autoFilters._innerList.isNotEmpty) { + for (int i = 0; i < sheet.autoFilters.count; i++) { + if (sheet.autoFilters[i]._filtertype == + _ExcelFilterType.colorFilter) { + _serializeDxfColorFilter( + builder, sheet.autoFilters[i] as _AutoFilterImpl); + } + } + } + if (sheet.conditionalFormats.isNotEmpty) { + final int iCount = sheet.conditionalFormats.length; + for (int i = 0; i < iCount; i++) { + final _ConditionalFormatsImpl condFormats = + sheet.conditionalFormats[i]; + for (final _ConditionalFormatImpl condition + in condFormats.innerList) { + if (_checkFormat(condition)) { + _serializeDxf(builder, condition); + } + } + } + } + } + }); + } + + Future _serialiseDxfsAsync(XmlBuilder builder) async { + builder.element('dxfs', nest: () async { + for (final Worksheet sheet in _workbook.worksheets.innerList) { + if (sheet.autoFilters._innerList.isNotEmpty) { + for (int i = 0; i < sheet.autoFilters.count; i++) { + if (sheet.autoFilters[i]._filtertype == + _ExcelFilterType.colorFilter) { + _serializeDxfColorFilterAsync( + builder, sheet.autoFilters[i] as _AutoFilterImpl); + } + } + } + if (sheet.conditionalFormats.isNotEmpty) { + final int iCount = sheet.conditionalFormats.length; + for (int i = 0; i < iCount; i++) { + final _ConditionalFormatsImpl condFormats = + sheet.conditionalFormats[i]; + for (final _ConditionalFormatImpl condition + in condFormats.innerList) { + if (_checkFormat(condition)) { + _serializeDxfAsync(builder, condition); + } + } + } + } + } + }); + } + + ///Serialize color filter dxf style. + void _serializeDxfColorFilter( + XmlBuilder builder, _AutoFilterImpl autoFilter) { + builder.element('dxf', nest: () { + _serializeDxfColorFilterFill( + builder, autoFilter._filteredItems as _ColorFilter); + }); + } + + Future _serializeDxfColorFilterAsync( + XmlBuilder builder, _AutoFilterImpl autoFilter) async { + builder.element('dxf', nest: () async { + _serializeDxfColorFilterFillAsync( + builder, autoFilter._filteredItems as _ColorFilter); + }); + } + + ///Serialize dxf color filter style fill. + void _serializeDxfColorFilterFill( + XmlBuilder builder, _ColorFilter colorFilter) { + String foreColor; + builder.element('fill', nest: () { + builder.element('patternFill', nest: () { + if (colorFilter._colorFilterType == ExcelColorFilterType.cellColor && + colorFilter._color == '#000000') + builder.attribute('patternType', 'none'); + else + builder.attribute('patternType', 'solid'); + + if (colorFilter._color == '#000000') { + if (colorFilter._colorFilterType == ExcelColorFilterType.cellColor) { + builder.element('fgColor', nest: () { + builder.attribute('indexed', '64'); + }); + } else { + builder.element('fgColor', nest: () { + builder.attribute('indexed', '66'); + }); + } + } else { + if (colorFilter._color.length == 7) { + foreColor = 'FF${colorFilter._color.replaceAll('#', '')}'; + } else { + foreColor = colorFilter._color; + } + builder.element('fgColor', nest: () { + builder.attribute('rgb', foreColor); + }); + } + }); + }); + } + + Future _serializeDxfColorFilterFillAsync( + XmlBuilder builder, _ColorFilter colorFilter) async { + String foreColor; + builder.element('fill', nest: () async { + builder.element('patternFill', nest: () async { + if (colorFilter._colorFilterType == ExcelColorFilterType.cellColor && + colorFilter._color == '#000000') + builder.attribute('patternType', 'none'); + else + builder.attribute('patternType', 'solid'); + + if (colorFilter._color == '#000000') { + if (colorFilter._colorFilterType == ExcelColorFilterType.cellColor) { + builder.element('fgColor', nest: () async { + builder.attribute('indexed', '64'); + }); + } else { + builder.element('fgColor', nest: () async { + builder.attribute('indexed', '66'); + }); + } + } else { + if (colorFilter._color.length == 7) { + foreColor = 'FF${colorFilter._color.replaceAll('#', '')}'; + } else { + foreColor = colorFilter._color; + } + builder.element('fgColor', nest: () async { + builder.attribute('rgb', foreColor); + }); + } + }); + }); + } + + /// Serializes Dxf style. + void _serializeDxf(XmlBuilder builder, _ConditionalFormatImpl condition) { + builder.element('dxf', nest: () { + _serializeDxfFont(builder, condition); + _serializeDxfNumberFormat(builder, condition); + _serializeDxfFill(builder, condition); + _serializeDxfBorders(builder, condition); + }); + } + + Future _serializeDxfAsync( + XmlBuilder builder, _ConditionalFormatImpl condition) async { + builder.element('dxf', nest: () async { + _serializeDxfFontAsync(builder, condition); + _serializeDxfNumberFormatAsync(builder, condition); + _serializeDxfFillAsync(builder, condition); + _serializeDxfBordersAsync(builder, condition); + }); + } + + /// Serializes Dxf style font. + void _serializeDxfFont(XmlBuilder builder, _ConditionalFormatImpl condition) { + if (condition.isBold || + condition.isItalic || + condition.underline || + condition.fontColor != '#000000') { + builder.element('font', nest: () { + if (condition.isBold) { + builder.element('b', nest: () {}); + } + if (condition.isItalic) { + builder.element('i', nest: () {}); + } + if (condition.underline) { + builder.element('u', nest: () {}); + } + String fontColor; + if (condition.fontColor.length <= 7) { + fontColor = 'FF${condition.fontColor.replaceAll('#', '')}'; + } else { + fontColor = condition.fontColor; + } + builder.element('color', nest: () { + builder.attribute('rgb', fontColor); + }); + }); + } + } + + Future _serializeDxfFontAsync( + XmlBuilder builder, _ConditionalFormatImpl condition) async { + if (condition.isBold || + condition.isItalic || + condition.underline || + condition.fontColor != '#000000') { + builder.element('font', nest: () async { + if (condition.isBold) { + builder.element('b', nest: () async {}); + } + if (condition.isItalic) { + builder.element('i', nest: () async {}); + } + if (condition.underline) { + builder.element('u', nest: () async {}); + } + String fontColor; + if (condition.fontColor.length <= 7) { + fontColor = 'FF${condition.fontColor.replaceAll('#', '')}'; + } else { + fontColor = condition.fontColor; + } + builder.element('color', nest: () async { + builder.attribute('rgb', fontColor); + }); + }); + } + } + + /// Serializes Dxf number format. + void _serializeDxfNumberFormat( + XmlBuilder builder, _ConditionalFormatImpl condition) { + if (condition.numberFormat != 'General') { + int index; + _Format? format; + if (_workbook.innerFormats._contains(condition._numberFormatIndex)) { + format = _workbook.innerFormats[condition._numberFormatIndex]; + index = format._index; + } else { + index = _workbook.innerFormats._createFormat(condition.numberFormat); + } + builder.element('numFmt', nest: () { + builder.attribute('numFmtId', index.toString()); + final String formatString = format!._formatString!.replaceAll("'", '"'); + builder.attribute('formatCode', formatString); + }); + } + } + + Future _serializeDxfNumberFormatAsync( + XmlBuilder builder, _ConditionalFormatImpl condition) async { + if (condition.numberFormat != 'General') { + int index; + _Format? format; + if (_workbook.innerFormats._contains(condition._numberFormatIndex)) { + format = _workbook.innerFormats[condition._numberFormatIndex]; + index = format._index; + } else { + index = _workbook.innerFormats._createFormat(condition.numberFormat); + } + builder.element('numFmt', nest: () async { + builder.attribute('numFmtId', index.toString()); + final String formatString = format!._formatString!.replaceAll("'", '"'); + builder.attribute('formatCode', formatString); + }); + } + } + + /// Serializes Dxf style fill. + void _serializeDxfFill(XmlBuilder builder, _ConditionalFormatImpl condition) { + String backColor; + if (condition.backColor != '#FFFFFF') { + if (condition.backColor.length == 7) { + backColor = 'FF${condition.backColor.replaceAll('#', '')}'; + } else { + backColor = condition.backColor; + } + builder.element('fill', nest: () { + builder.element('patternFill', nest: () { + builder.element('bgColor', nest: () { + builder.attribute('rgb', backColor); + }); + }); + }); + } + } + + Future _serializeDxfFillAsync( + XmlBuilder builder, _ConditionalFormatImpl condition) async { + String backColor; + if (condition.backColor != '#FFFFFF') { + if (condition.backColor.length == 7) { + backColor = 'FF${condition.backColor.replaceAll('#', '')}'; + } else { + backColor = condition.backColor; + } + builder.element('fill', nest: () async { + builder.element('patternFill', nest: () async { + builder.element('bgColor', nest: () async { builder.attribute('rgb', backColor); }); }); @@ -3120,53 +5464,292 @@ class SerializeWorkbook { } } - /// Serializes Dxf borders. - void _serializeDxfBorders( - XmlBuilder builder, _ConditionalFormatImpl condition) { - if (condition.leftBorderStyle != LineStyle.none || - condition.rightBorderStyle != LineStyle.none || - condition.topBorderStyle != LineStyle.none || - condition.bottomBorderStyle != LineStyle.none) { - builder.element('border', nest: () { - if (condition.leftBorderStyle != LineStyle.none) { - _serializeDxfBorder(builder, 'left', condition.leftBorderStyle, - condition.leftBorderColor); - } - if (condition.rightBorderStyle != LineStyle.none) { - _serializeDxfBorder(builder, 'right', condition.rightBorderStyle, - condition.rightBorderColor); - } - if (condition.topBorderStyle != LineStyle.none) { - _serializeDxfBorder(builder, 'top', condition.topBorderStyle, - condition.topBorderColor); - } - if (condition.bottomBorderStyle != LineStyle.none) { - _serializeDxfBorder(builder, 'bottom', condition.bottomBorderStyle, - condition.bottomBorderColor); - } - }); - } - } - - /// Serializes Dxf border. - void _serializeDxfBorder(XmlBuilder builder, String value, - LineStyle borderStyle, String borderColor) { - builder.element(value, nest: () { - final String strStyle = - borderStyle.toString().split('.').toList().removeAt(1).toLowerCase(); - builder.attribute('style', strStyle); - builder.element('color', nest: () { - if (borderColor.length <= 7) { - builder.attribute('rgb', 'FF${borderColor.replaceAll('#', '')}'); - } else { - builder.attribute('rgb', borderColor); - } - }); - }); - } - - /// Serializes conditional formattings. - void _serializeConditionalFormattingExt(XmlBuilder builder, Worksheet sheet) { + /// Serializes Dxf borders. + void _serializeDxfBorders( + XmlBuilder builder, _ConditionalFormatImpl condition) { + if (condition.leftBorderStyle != LineStyle.none || + condition.rightBorderStyle != LineStyle.none || + condition.topBorderStyle != LineStyle.none || + condition.bottomBorderStyle != LineStyle.none) { + builder.element('border', nest: () { + if (condition.leftBorderStyle != LineStyle.none) { + _serializeDxfBorder(builder, 'left', condition.leftBorderStyle, + condition.leftBorderColor); + } + if (condition.rightBorderStyle != LineStyle.none) { + _serializeDxfBorder(builder, 'right', condition.rightBorderStyle, + condition.rightBorderColor); + } + if (condition.topBorderStyle != LineStyle.none) { + _serializeDxfBorder(builder, 'top', condition.topBorderStyle, + condition.topBorderColor); + } + if (condition.bottomBorderStyle != LineStyle.none) { + _serializeDxfBorder(builder, 'bottom', condition.bottomBorderStyle, + condition.bottomBorderColor); + } + }); + } + } + + Future _serializeDxfBordersAsync( + XmlBuilder builder, _ConditionalFormatImpl condition) async { + if (condition.leftBorderStyle != LineStyle.none || + condition.rightBorderStyle != LineStyle.none || + condition.topBorderStyle != LineStyle.none || + condition.bottomBorderStyle != LineStyle.none) { + builder.element('border', nest: () async { + if (condition.leftBorderStyle != LineStyle.none) { + _serializeDxfBorderAsync(builder, 'left', condition.leftBorderStyle, + condition.leftBorderColor); + } + if (condition.rightBorderStyle != LineStyle.none) { + _serializeDxfBorderAsync(builder, 'right', condition.rightBorderStyle, + condition.rightBorderColor); + } + if (condition.topBorderStyle != LineStyle.none) { + _serializeDxfBorderAsync(builder, 'top', condition.topBorderStyle, + condition.topBorderColor); + } + if (condition.bottomBorderStyle != LineStyle.none) { + _serializeDxfBorderAsync(builder, 'bottom', + condition.bottomBorderStyle, condition.bottomBorderColor); + } + }); + } + } + + /// Serializes Dxf border. + void _serializeDxfBorder(XmlBuilder builder, String value, + LineStyle borderStyle, String borderColor) { + builder.element(value, nest: () { + final String strStyle = + borderStyle.toString().split('.').toList().removeAt(1).toLowerCase(); + builder.attribute('style', strStyle); + builder.element('color', nest: () { + if (borderColor.length <= 7) { + builder.attribute('rgb', 'FF${borderColor.replaceAll('#', '')}'); + } else { + builder.attribute('rgb', borderColor); + } + }); + }); + } + + Future _serializeDxfBorderAsync(XmlBuilder builder, String value, + LineStyle borderStyle, String borderColor) async { + builder.element(value, nest: () async { + final String strStyle = + borderStyle.toString().split('.').toList().removeAt(1).toLowerCase(); + builder.attribute('style', strStyle); + builder.element('color', nest: () async { + if (borderColor.length <= 7) { + builder.attribute('rgb', 'FF${borderColor.replaceAll('#', '')}'); + } else { + builder.attribute('rgb', borderColor); + } + }); + }); + } + + /// Serializes conditional formattings. + void _serializeConditionalFormattingExt(XmlBuilder builder, Worksheet sheet) { + final bool hasExtensionList = _hasExtensionListOnCF(sheet); + if (!hasExtensionList) { + return; + } + int iPriority = 1; + int iPriorityCount = 0; + + if (hasExtensionList) { + builder.element('ext', nest: () { + builder.namespace( + 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'); + builder.attribute('uri', '{78C0D931-6437-407d-A8EE-F0AAD7539E65}'); + builder.attribute('xmlns:x14', + 'http://schemas.microsoft.com/office/spreadsheetml/2009/9/main'); + + builder.element('x14:conditionalFormattings', nest: () { + for (final ConditionalFormats conditions + in sheet.conditionalFormats) { + for (int i = 0; i < conditions.count; i++) { + final ConditionalFormat condition = + (conditions as _ConditionalFormatsImpl).innerList[i]; + final _ConditionalFormatImpl format = + condition as _ConditionalFormatImpl; + _IconSetImpl? iconSet; + if (format.iconSet != null) { + iconSet = format.iconSet! as _IconSetImpl; + } + if (format._bCFHasExtensionList) { + final _ConditionalFormatsImpl formats = conditions; + format._rangeRefernce = (formats._cellList.isNotEmpty) + ? (' ${formats._cellList}') + : ''; + + if ((condition.formatType == ExcelCFType.formula && + condition.firstFormula != '') || + condition.formatType == ExcelCFType.specificText && + condition.firstFormula != '' && + (condition.operator == + ExcelComparisonOperator.beginsWith || + condition.operator == + ExcelComparisonOperator.endsWith || + condition.operator == + ExcelComparisonOperator.containsText || + condition.operator == + ExcelComparisonOperator.notContainsText) || + condition.formatType == ExcelCFType.cellValue && + condition.firstFormula != '' && + (condition.operator == ExcelComparisonOperator.equal || + condition.operator == + ExcelComparisonOperator.notEqual || + condition.operator == + ExcelComparisonOperator.greater || + condition.operator == + ExcelComparisonOperator.greaterOrEqual || + condition.operator == + ExcelComparisonOperator.less || + condition.operator == + ExcelComparisonOperator.lessOrEqual || + (condition.secondFormula != '' && + (condition.operator == + ExcelComparisonOperator.between || + condition.operator == + ExcelComparisonOperator.notBetween)))) { + builder.element('x14:conditionalFormatting', nest: () { + builder.attribute('xmlns:xm', + 'http://schemas.microsoft.com/office/excel/2006/main'); + builder.element('x14:cfRule', nest: () { + builder.attribute('type', + _getCFType(condition.formatType, condition.operator)); + if (format._priority > 1) { + builder.attribute( + 'priority', format._priority.toString()); + } else { + builder.attribute('priority', iPriority.toString()); + iPriority++; + } + if (condition.formatType == ExcelCFType.cellValue || + condition.formatType == ExcelCFType.specificText) { + builder.attribute('operator', + _getCFComparisonOperatorName(condition.operator)); + } + // writer.WriteAttributeString(IdAttributeName, format.ST_GUID.ToString()); + final _ConditionalFormatImpl conFormatImpl = condition; + final String strFormula1 = conFormatImpl.firstFormula; + final String strFormula2 = conFormatImpl.secondFormula; + if (strFormula1 != '' && strFormula1 != '') { + builder.element('xm:f', nest: strFormula1); + } + if (strFormula2 != '' && strFormula2 != '') { + builder.element('xm:f', nest: strFormula2); + } else if (format._range != null) { + builder.element('xm:f', + nest: (format._range)!.addressGlobal); + } + + builder.element('dxf', nest: () { + _serializeDxfFont(builder, condition); + _serializeDxfFill(builder, condition); + _serializeDxfBorders(builder, condition); + }); + }); + builder.element('xm:sqref', nest: format._rangeRefernce); + }); + } + } + if (condition.iconSet != null && + (format._bCFHasExtensionList || + format.iconSet!.iconSet == ExcelIconSetType.threeStars || + (iconSet != null && iconSet._isCustom) || + format.iconSet!.iconSet == ExcelIconSetType.fiveBoxes || + format.iconSet!.iconSet == + ExcelIconSetType.threeTriangles)) { + final int iDxfIndex = _iDxfIndex; + + builder.element('x14:conditionalFormatting', nest: () { + builder.attribute('xmlns:xm', + 'http://schemas.microsoft.com/office/excel/2006/main'); + final List result = _serializeCondition(builder, + condition, 'x14:', iDxfIndex, iPriority, iPriorityCount); + _iDxfIndex = result[0] as int; + iPriority = result[1] as int; + iPriorityCount = result[2] as int; + + final _ConditionalFormatsImpl formats = conditions; + final String strAddress = (formats._cellList.isNotEmpty) + ? (' ${formats._cellList}') + : ''; + builder.element('xm:sqref', nest: strAddress); + }); + } + if (condition.dataBar != null && + (condition.dataBar! as _DataBarImpl)._hasExtensionList) { + final DataBar dataBar = condition.dataBar!; + final _DataBarImpl dataBarImpl = dataBar as _DataBarImpl; + + builder.element('x14:conditionalFormatting', nest: () { + builder.element('x14:cfRule', nest: () { + builder.attribute('type', 'dataBar'); + // ignore: unnecessary_null_checks + builder.attribute('id', dataBarImpl._stGUID!); + + builder.element('x14:dataBar', nest: () { + builder.attribute( + 'border', (dataBar.hasBorder) ? '1' : '0'); + builder.attribute( + 'gradient', (dataBar.hasGradientFill) ? '1' : '0'); + builder.attribute( + 'minLength', dataBar.percentMin.toString()); + builder.attribute( + 'maxLength', dataBar.percentMax.toString()); + builder.attribute('direction', + dataBar.dataBarDirection.toString().substring(17)); + builder.attribute('negativeBarColorSameAsPositive', + (dataBarImpl._hasDiffNegativeBarColor) ? '0' : '1'); + builder.attribute( + 'negativeBarBorderColorSameAsPositive', + (dataBarImpl._hasDiffNegativeBarBorderColor) + ? '0' + : '1'); + builder.attribute('axisPosition', + dataBar.dataBarAxisPosition.toString().substring(20)); + + _serializeConditionValueObjectExt( + builder, dataBar.minPoint, false, true); + _serializeConditionValueObjectExt( + builder, dataBar.maxPoint, false, false); + + if (dataBar.borderColor != '') { + _serializeRgbColor( + builder, 'x14:borderColor', dataBar.borderColor); + } + if (dataBar.negativeFillColor != '') { + _serializeRgbColor(builder, 'x14:negativeFillColor', + dataBar.negativeFillColor); + } + if (dataBar.negativeBorderColor != '') { + _serializeRgbColor(builder, 'x14:negativeBorderColor', + dataBar.negativeBorderColor); + } + if (dataBar.barAxisColor != '') { + _serializeRgbColor( + builder, 'x14:axisColor', dataBar.barAxisColor); + } + }); + }); + }); + } + } + } + }); + }); + } + } + + Future _serializeConditionalFormattingExtAsync( + XmlBuilder builder, Worksheet sheet) async { final bool hasExtensionList = _hasExtensionListOnCF(sheet); if (!hasExtensionList) { return; @@ -3175,14 +5758,14 @@ class SerializeWorkbook { int iPriorityCount = 0; if (hasExtensionList) { - builder.element('ext', nest: () { + builder.element('ext', nest: () async { builder.namespace( 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'); builder.attribute('uri', '{78C0D931-6437-407d-A8EE-F0AAD7539E65}'); builder.attribute('xmlns:x14', 'http://schemas.microsoft.com/office/spreadsheetml/2009/9/main'); - builder.element('x14:conditionalFormattings', nest: () { + builder.element('x14:conditionalFormattings', nest: () async { for (final ConditionalFormats conditions in sheet.conditionalFormats) { for (int i = 0; i < conditions.count; i++) { @@ -3230,12 +5813,13 @@ class SerializeWorkbook { ExcelComparisonOperator.between || condition.operator == ExcelComparisonOperator.notBetween)))) { - builder.element('x14:conditionalFormatting', nest: () { + builder.element('x14:conditionalFormatting', nest: () async { builder.attribute('xmlns:xm', 'http://schemas.microsoft.com/office/excel/2006/main'); - builder.element('x14:cfRule', nest: () { + builder.element('x14:cfRule', nest: () async { builder.attribute('type', _getCFType(condition.formatType, condition.operator)); + if (format._priority > 1) { builder.attribute( 'priority', format._priority.toString()); @@ -3262,10 +5846,10 @@ class SerializeWorkbook { nest: (format._range)!.addressGlobal); } - builder.element('dxf', nest: () { - _serializeDxfFont(builder, condition); - _serializeDxfFill(builder, condition); - _serializeDxfBorders(builder, condition); + builder.element('dxf', nest: () async { + _serializeDxfFontAsync(builder, condition); + _serializeDxfFillAsync(builder, condition); + _serializeDxfBordersAsync(builder, condition); }); }); builder.element('xm:sqref', nest: format._rangeRefernce); @@ -3302,13 +5886,13 @@ class SerializeWorkbook { final DataBar dataBar = condition.dataBar!; final _DataBarImpl dataBarImpl = dataBar as _DataBarImpl; - builder.element('x14:conditionalFormatting', nest: () { - builder.element('x14:cfRule', nest: () { + builder.element('x14:conditionalFormatting', nest: () async { + builder.element('x14:cfRule', nest: () async { builder.attribute('type', 'dataBar'); // ignore: unnecessary_null_checks builder.attribute('id', dataBarImpl._stGUID!); - builder.element('x14:dataBar', nest: () { + builder.element('x14:dataBar', nest: () async { builder.attribute( 'border', (dataBar.hasBorder) ? '1' : '0'); builder.attribute( @@ -3329,25 +5913,26 @@ class SerializeWorkbook { builder.attribute('axisPosition', dataBar.dataBarAxisPosition.toString().substring(20)); - _serializeConditionValueObjectExt( + _serializeConditionValueObjectExtAsync( builder, dataBar.minPoint, false, true); - _serializeConditionValueObjectExt( + _serializeConditionValueObjectExtAsync( builder, dataBar.maxPoint, false, false); - if (dataBar.borderColor != '') { - _serializeRgbColor( + _serializeRgbColorAsync( builder, 'x14:borderColor', dataBar.borderColor); } if (dataBar.negativeFillColor != '') { - _serializeRgbColor(builder, 'x14:negativeFillColor', - dataBar.negativeFillColor); + _serializeRgbColorAsync(builder, + 'x14:negativeFillColor', dataBar.negativeFillColor); } if (dataBar.negativeBorderColor != '') { - _serializeRgbColor(builder, 'x14:negativeBorderColor', + _serializeRgbColorAsync( + builder, + 'x14:negativeBorderColor', dataBar.negativeBorderColor); } if (dataBar.barAxisColor != '') { - _serializeRgbColor( + _serializeRgbColorAsync( builder, 'x14:axisColor', dataBar.barAxisColor); } }); @@ -3423,6 +6008,26 @@ class SerializeWorkbook { }); } + Future _serializeConditionValueObjectExtAsync(XmlBuilder builder, + ConditionValue conditionValue, bool isIconSet, bool isMinPoint) async { + builder.element('x14:cfvo', nest: () async { + int index = conditionValue.type.index; + if (!isIconSet) { + index = index == 7 + ? isMinPoint + ? 7 + : 8 + : index; + } + final String strType = _valueTypes[index]; + builder.attribute('type', strType); + builder.attribute('val', conditionValue.value); + if (isIconSet) { + builder.attribute('gte', (conditionValue.operator).index.toString()); + } + }); + } + // Check whether to apply format or not. bool _checkFormat(_ConditionalFormatImpl condition) { return condition.backColor != '#FFFFFF' || @@ -3443,6 +6048,11 @@ class SerializeWorkbook { _workbook.archive.addFile(item); } + Future _addToArchiveAsync(List data, String fileName) async { + final ArchiveFile item = ArchiveFile(fileName, data.length, data); + _workbook.archive.addFile(item); + } + /// Checks whether columns after iColumnIndex have the same settings and /// returns the last number in the sequence. static int _findSameColumns( @@ -3485,6 +6095,25 @@ class SerializeWorkbook { }); } + Future _serializeAutoFiltersAsync( + XmlBuilder builder, AutoFilterCollection? autoFilters) async { + if (autoFilters == null || autoFilters._innerList.isEmpty) { + return; + } + builder.element('autoFilter', nest: () async { + builder.attribute('ref', autoFilters.filterRange.addressLocal); + + // ignore: always_specify_types + for (int i = 0; i < autoFilters.count; i++) { + final _AutoFilterImpl autoFilter = autoFilters[i] as _AutoFilterImpl; + + if (autoFilter._isFiltered) { + _serializeFilterColumnAsync(builder, autoFilter); + } + } + }); + } + /// Serializes filter column. void _serializeFilterColumn( XmlBuilder builder, @@ -3516,6 +6145,36 @@ class SerializeWorkbook { }); } + Future _serializeFilterColumnAsync( + XmlBuilder builder, + _AutoFilterImpl autoFilter, + ) async { + builder.element('filterColumn', nest: () async { + _serializeAttributeIntAsync( + builder, 'colId', autoFilter._colIndex - 1, -1); + switch (autoFilter._typeOfFilter) { + case _ExcelFilterType.combinationFilter: + _serializeFiltersAsync(builder, autoFilter); + break; + case _ExcelFilterType.customFilter: + _serializeCustomFilterAsync(builder, autoFilter); + break; + case _ExcelFilterType.dynamicFilter: + _serializeDateFilterAsync( + builder, autoFilter._filteredItems as _DynamicFilter); + break; + case _ExcelFilterType.colorFilter: + _serializeColorFilterAsync( + builder, autoFilter._filteredItems as _ColorFilter); + break; + case _ExcelFilterType.iconFilter: + break; + case _ExcelFilterType.notUsed: + break; + } + }); + } + /// Serialize color filter. void _serializeColorFilter(XmlBuilder builder, _ColorFilter filter) { builder.element('colorFilter', nest: () { @@ -3527,6 +6186,17 @@ class SerializeWorkbook { }); } + Future _serializeColorFilterAsync( + XmlBuilder builder, _ColorFilter filter) async { + builder.element('colorFilter', nest: () async { + builder.attribute('dxfId', _iDxfIndex.toString()); + _iDxfIndex++; + if (filter._colorFilterType == ExcelColorFilterType.fontColor) { + builder.attribute('cellColor', '0'); + } + }); + } + /// Serialize the dynmaic filter. void _serializeDateFilter(XmlBuilder builder, _DynamicFilter filter) { if (filter._dateFilterType != DynamicFilterType.none) { @@ -3538,6 +6208,16 @@ class SerializeWorkbook { } } + Future _serializeDateFilterAsync( + XmlBuilder builder, _DynamicFilter filter) async { + if (filter._dateFilterType != DynamicFilterType.none) { + builder.element('dynamicFilter', nest: () async { + builder.attribute('type', + _convertDateFilterTypeToString(filter._dateFilterType).toString()); + }); + } + } + /// Convert dynamic filter type to string. String? _convertDateFilterTypeToString(DynamicFilterType filterType) { switch (filterType) { @@ -3653,6 +6333,17 @@ class SerializeWorkbook { }); } + Future _serializeFiltersAsync( + XmlBuilder builder, _AutoFilterImpl autoFilter) async { + builder.element('filters', nest: () async { + if (autoFilter._filteredItems._filterType == + _ExcelFilterType.combinationFilter) { + _serializeCombinationFiltersAsync( + builder, autoFilter._filteredItems as _CombinationFilter); + } + }); + } + ///SerializeCombination filter void _serializeCombinationFilters( XmlBuilder builder, _CombinationFilter combinationFilter) { @@ -3672,6 +6363,25 @@ class SerializeWorkbook { } } + Future _serializeCombinationFiltersAsync( + XmlBuilder builder, _CombinationFilter combinationFilter) async { + if (combinationFilter._isBlank) { + builder.attribute('blank', 1); + } + for (final _MultipleFilter multipleFilter + in combinationFilter._filterCollection) { + switch (multipleFilter._combinationFilterType) { + case _ExcelCombinationFilterType.textFilter: + _serializeFilterAsync(builder, (multipleFilter as _TextFilter)._text); + break; + case _ExcelCombinationFilterType.dateTimeFilter: + _serializeDateTimeFilterAsync( + builder, multipleFilter as _DateTimeFilter); + break; + } + } + } + /// Serialize the date time filter. void _serializeDateTimeFilter(XmlBuilder builder, _DateTimeFilter filter) { final DateTimeFilterType dateGroup = filter._groupingType; @@ -3700,6 +6410,34 @@ class SerializeWorkbook { }); } + Future _serializeDateTimeFilterAsync( + XmlBuilder builder, _DateTimeFilter filter) async { + final DateTimeFilterType dateGroup = filter._groupingType; + final DateTime date = filter._dateTimeValue; + builder.element('dateGroupItem', nest: () { + if (_getDateTimevalue(dateGroup) >= + _getDateTimevalue(DateTimeFilterType.year)) + builder.attribute('year', date.year.toString()); + if (_getDateTimevalue(dateGroup) >= + _getDateTimevalue(DateTimeFilterType.month)) + builder.attribute('month', date.month.toString()); + if (_getDateTimevalue(dateGroup) >= + _getDateTimevalue(DateTimeFilterType.day)) + builder.attribute('day', date.day.toString()); + if (_getDateTimevalue(dateGroup) >= + _getDateTimevalue(DateTimeFilterType.hour)) + builder.attribute('hour', date.hour.toString()); + if (_getDateTimevalue(dateGroup) >= + _getDateTimevalue(DateTimeFilterType.minute)) + builder.attribute('minute', date.minute.toString()); + if (_getDateTimevalue(dateGroup) >= + _getDateTimevalue(DateTimeFilterType.second)) + builder.attribute('second', date.second.toString()); + final String dateTimeStr = _getDateTimeString(dateGroup); + builder.attribute('dateTimeGrouping', dateTimeStr); + }); + } + /// Get the int value based on date time grouping type. int _getDateTimevalue(DateTimeFilterType date) { switch (date) { @@ -3743,6 +6481,13 @@ class SerializeWorkbook { }); } + Future _serializeFilterAsync( + XmlBuilder builder, String strFilterValue) async { + builder.element('filter', nest: () async { + builder.attribute('val', strFilterValue); + }); + } + /// Serializes custom filters. void _serializeCustomFilter(XmlBuilder builder, _AutoFilterImpl autoFilter) { builder.element('customFilters', nest: () { @@ -3761,12 +6506,37 @@ class SerializeWorkbook { }); } + Future _serializeCustomFilterAsync( + XmlBuilder builder, _AutoFilterImpl autoFilter) async { + builder.element('customFilters', nest: () async { + final bool isAnd = autoFilter.logicalOperator == ExcelLogicalOperator.and; + _serializeAttributesAsync(builder, 'and', isAnd, false); + if (autoFilter.isFirstCondition) { + __serializeCustomFiltersAsync( + builder, autoFilter.firstCondition, autoFilter); + } + if (autoFilter.isSecondCondition) { + __serializeCustomFiltersAsync( + builder, autoFilter.firstCondition, autoFilter); + __serializeCustomFiltersAsync( + builder, autoFilter.secondCondition, autoFilter); + } + }); + } + /// Serializes custom filters. void __serializeCustomFilters(XmlBuilder builder, AutoFilterCondition autoFilterCondition, _AutoFilterImpl autoFilter) { _customFilterCondition(builder, autoFilterCondition, autoFilter, 0); } + Future __serializeCustomFiltersAsync( + XmlBuilder builder, + AutoFilterCondition autoFilterCondition, + _AutoFilterImpl autoFilter) async { + _customFilterConditionAsync(builder, autoFilterCondition, autoFilter, 0); + } + /// Returns auto filter condition operator name. String _getAFconditionalOperatorName(ExcelFilterCondition filterCondition) { String empty = ''; @@ -3840,6 +6610,46 @@ class SerializeWorkbook { }); } + Future _customFilterConditionAsync( + XmlBuilder builder, + AutoFilterCondition autoFilterCondition, + _AutoFilterImpl autoFilter, + int dataindex) async { + ExcelFilterCondition conditionOperator = + autoFilterCondition.conditionOperator; + builder.element('customFilter', nest: () { + if (autoFilterCondition._dataType == + _ExcelFilterDataType.matchAllNonBlanks) { + builder.attribute('operator', 'notEqual'); + builder.attribute('val', 0); + } else { + conditionOperator = autoFilterCondition.conditionOperator; + final String operatorValue = + _getAFconditionalOperatorName(conditionOperator); + + if (operatorValue != 'equal') { + builder.attribute('operator', operatorValue); + } + Object strval = _conditionValue(autoFilterCondition); + if (conditionOperator == ExcelFilterCondition.contains || + conditionOperator == ExcelFilterCondition.doesNotContain || + conditionOperator == ExcelFilterCondition.beginsWith || + conditionOperator == ExcelFilterCondition.doesNotBeginWith) { + strval = '$strval*'; + } + + if (conditionOperator == ExcelFilterCondition.contains || + conditionOperator == ExcelFilterCondition.doesNotContain || + conditionOperator == ExcelFilterCondition.endsWith || + conditionOperator == ExcelFilterCondition.doesNotEndWith) { + strval = '*$strval'; + } + + builder.attribute('val', strval); + } + }); + } + /// Returns auto filter condition value. Object _conditionValue(AutoFilterCondition autoFilterCondition) { Object empty = ''; @@ -3877,4 +6687,18 @@ class SerializeWorkbook { builder.attribute('rgb', worksheetTabColor); }); } + + Future _serializeTabColorAsync( + Worksheet sheet, XmlBuilder builder) async { + String worksheetTabColor = ''; + + if (sheet.tabColor.length == 7) { + worksheetTabColor = 'FF${sheet.tabColor.replaceAll('#', '')}'; + } else { + worksheetTabColor = sheet.tabColor; + } + builder.element('tabColor', nest: () async { + builder.attribute('rgb', worksheetTabColor); + }); + } } diff --git a/packages/syncfusion_flutter_xlsio/lib/src/xlsio/general/workbook.dart b/packages/syncfusion_flutter_xlsio/lib/src/xlsio/general/workbook.dart index 9f83eee71..02d6fe10c 100644 --- a/packages/syncfusion_flutter_xlsio/lib/src/xlsio/general/workbook.dart +++ b/packages/syncfusion_flutter_xlsio/lib/src/xlsio/general/workbook.dart @@ -6199,6 +6199,37 @@ class Workbook { return bytes!; } + /// Saves workbook. + /// ```dart + /// Workbook workbook = new Workbook(); + /// Worksheet sheet = workbook.worksheets[0]; + /// List bytes = workbook.saveSync(); + /// File('ExcelSave.xlsx').writeAsBytes(bytes); + /// workbook.dispose(); + /// ``` + List saveSync() { + return saveAsStream(); + } + + /// Saves workbook as Future. + /// ```dart + /// Workbook workbook = new Workbook(); + /// Worksheet sheet = workbook.worksheets[0]; + /// List bytes = await workbook.save(); + /// File('ExcelSave.xlsx').writeAsBytes(bytes); + /// workbook.dispose(); + /// ``` + Future> save() async { + _saving = true; + final SerializeWorkbook serializer = SerializeWorkbook(this); + List? bytes; + await serializer._saveInternalAsync().then((_) async { + bytes = ZipEncoder().encode(archive); + _saving = false; + }); + return bytes!; + } + /// Saves workbook as CSV format. /// ```dart /// Workbook workbook = new Workbook(); diff --git a/packages/syncfusion_flutter_xlsio/lib/src/xlsio/table/table_serialization.dart b/packages/syncfusion_flutter_xlsio/lib/src/xlsio/table/table_serialization.dart index 2366848ba..bc889deaf 100644 --- a/packages/syncfusion_flutter_xlsio/lib/src/xlsio/table/table_serialization.dart +++ b/packages/syncfusion_flutter_xlsio/lib/src/xlsio/table/table_serialization.dart @@ -404,4 +404,177 @@ class _TableSerialization { return 'none'; } } + + /// Serialize Tables + Future _serializeTablesAsync( + XmlBuilder builder, Worksheet sheet) async { + final ExcelTableCollection tableCollection = sheet.tableCollection; + int rid; + int id = 1; + if (tableCollection._count > 0) { + if (sheet.hyperlinks.count > 0) { + for (int hyperlinkIndex = 0; + hyperlinkIndex < sheet.hyperlinks.count; + hyperlinkIndex++) { + if (sheet.hyperlinks[hyperlinkIndex]._attachedType == + ExcelHyperlinkAttachedType.range && + sheet.hyperlinks[hyperlinkIndex].type != HyperlinkType.workbook) { + id++; + } + } + } + if (sheet.pictures.count > 0) { + for (int imageIndex = 0; + imageIndex < sheet.pictures.count; + imageIndex++) { + id++; + } + } + builder.element('tableParts', nest: () async { + builder.attribute('count', tableCollection._count); + for (int tableCount = 0; + tableCount < tableCollection._count; + tableCount++) { + rid = id++; + final ExcelTable table = tableCollection[tableCount]; + _serializeTableAsync(table, tableCount + 1); + builder.element('tablePart', nest: () { + builder.attribute('r:id', 'rId$rid'); + }); + } + }); + } + } + + /// Serializes attribute if it differs from default value. + Future _serializeAttributeBoolAsync(XmlBuilder builder, + String attributeName, bool value, bool defaultValue) async { + String? strValue; + if (value != defaultValue) { + strValue = value ? '1' : '0'; + } + if (strValue != null) { + builder.attribute(attributeName, strValue); + } + } + + /// Serializes attribute if it differs from default value. + Future _serializeAttributeIntAsync(XmlBuilder builder, + String attributeName, int value, int defaultValue) async { + if (value != defaultValue) { + final String strValue = value.toString(); + builder.attribute(attributeName, strValue); + } + } + + /// Serializes attribute if it differs from default value. + Future _serializeAttributeStringAsync(XmlBuilder builder, + String attributeName, String value, String defaultValue) async { + if (value != defaultValue) { + builder.attribute(attributeName, value); + } + } + + /// Serialize Table + Future _serializeTableAsync(ExcelTable table, int index) async { + final XmlBuilder builder = XmlBuilder(); + _workbook._tableCount++; + builder.element('table', nest: () async { + builder.attribute('id', (table as _ExcelTableImpl)._tableIndex); + builder.attribute('name', table._tableName); + builder.attribute('displayName', table.displayName); + builder.attribute('ref', table.dataRange.addressLocal); + if (!table.totalsRowShown) { + _serializeAttributeBoolAsync(builder, 'totalsRowShown', false, true); + } else { + _serializeAttributeIntAsync( + builder, 'totalsRowCount', table.totalRowCount, 0); + } + builder.namespace( + 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'); + + final _ExcelTableImpl typedTable = table; + if (!typedTable.showHeaderRow) { + builder.attribute('headerRowCount', '0'); + } + + _serializeTableColumnsAsync(builder, table.columns); + _serializeTableStyleAsync(builder, table); + _serializeTableExtensionListAsync(builder, table); + }); + final String stringXml = builder.buildDocument().toString(); + final List bytes = utf8.encode(stringXml); + _addToArchiveAsync(bytes, 'xl/tables/table${_workbook._tableCount}.xml'); + } + + /// Add the workbook data with filename to ZipArchive. + Future _addToArchiveAsync(List data, String fileName) async { + final ArchiveFile item = ArchiveFile(fileName, data.length, data); + _workbook.archive.addFile(item); + } + + /// Serialize Columns + Future _serializeTableColumnsAsync( + XmlBuilder builder, List columns) async { + builder.element('tableColumns', nest: () async { + for (int columnCount = 0; columnCount < columns.length; columnCount++) { + final ExcelTableColumn column = columns[columnCount]; + _serializeTableColumnAsync(builder, column); + } + }); + } + + /// Serialize Column. + Future _serializeTableColumnAsync( + XmlBuilder builder, ExcelTableColumn column) async { + builder.element('tableColumn', nest: () async { + builder.attribute('id', (column as _ExcelTableColumnImpl)._columnId); + builder.attribute('name', column.columnName); + _serializeAttributeStringAsync( + builder, 'totalsRowLabel', column.totalRowLabel, ''); + if (column.totalFormula != ExcelTableTotalFormula.none) { + builder.attribute( + 'totalsRowFunction', _getTotalsCalculation(column.totalFormula)); + } + }); + } + + /// Serialize Table Style. + Future _serializeTableStyleAsync( + XmlBuilder builder, ExcelTable table) async { + final ExcelTableBuiltInStyle style = table.builtInTableStyle; + builder.element('tableStyleInfo', nest: () async { + builder.attribute('name', _getTableStyles(style)); + builder.attribute( + 'showFirstColumn', (table.showFirstColumn ? 1 : 0).toString()); + builder.attribute( + 'showLastColumn', (table.showLastColumn ? 1 : 0).toString()); + builder.attribute( + 'showRowStripes', (table.showBandedRows ? 1 : 0).toString()); + builder.attribute( + 'showColumnStripes', (table.showBandedColumns ? 1 : 0).toString()); + }); + } + + /// Serializes the table extension list. + Future _serializeTableExtensionListAsync( + XmlBuilder builder, ExcelTable table) async { + if (table.altTextTitle.isNotEmpty || table.altTextSummary.isNotEmpty) { + builder.element('extLst', nest: () { + builder.element('ext', nest: () { + builder.attribute('uri', '{504A1905-F514-4f6f-8877-14C23A59335A}'); + builder.attribute('xmlns:x14', + 'http://schemas.microsoft.com/office/spreadsheetml/2009/9/main'); + builder.element('x14:table', nest: () { + if (table.altTextTitle.isNotEmpty) { + builder.attribute('altText', table.altTextTitle); + } + if (table.altTextSummary.isNotEmpty) { + builder.attribute('altTextSummary', table.altTextSummary); + } + }); + }); + }); + } + } } diff --git a/packages/syncfusion_flutter_xlsio/pubspec.yaml b/packages/syncfusion_flutter_xlsio/pubspec.yaml index 4df141894..b3fe6611f 100644 --- a/packages/syncfusion_flutter_xlsio/pubspec.yaml +++ b/packages/syncfusion_flutter_xlsio/pubspec.yaml @@ -1,6 +1,6 @@ name: syncfusion_flutter_xlsio description: Flutter XlsIO is a Dart library for creating Excel documents with formulas, charts, images, hyperlinks, autofit rows and columns, and more. -version: 20.4.38-beta +version: 21.1.35 homepage: https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_flutter_xlsio environment: @@ -11,11 +11,11 @@ dependencies: sdk: flutter xml: ">=5.1.0 <7.0.0" archive: ">=3.1.2 <4.0.0" - image: ">=3.0.1 <4.0.0" + image: ">=3.0.1 <5.0.0" intl: ">=0.17.0 <0.20.0" crypto: ">=3.0.0 <4.0.0" jiffy: ^5.0.0 - syncfusion_officecore: ^20.4.38-beta + syncfusion_officecore: ^21.1.35 dev_dependencies: flutter_test: diff --git a/packages/syncfusion_localizations/lib/src/l10n/syncfusion_ar.arb b/packages/syncfusion_localizations/lib/src/l10n/syncfusion_ar.arb index d1429b260..365a9d443 100644 --- a/packages/syncfusion_localizations/lib/src/l10n/syncfusion_ar.arb +++ b/packages/syncfusion_localizations/lib/src/l10n/syncfusion_ar.arb @@ -92,12 +92,12 @@ "searchDataGridFilteringLabel" : "يبحث", "noMatchesDataGridFilteringLabel" : "لا يوجد تطابق", "okDataGridFilteringLabel" : "نعم", -"cancelDataGridFilteringLabel" : "يلغي", +"cancelDataGridFilteringLabel" : "إلغاء", "showRowsWhereDataGridFilteringLabel" : "إظهار الصفوف حيث", "andDataGridFilteringLabel" : "و", "orDataGridFilteringLabel" : "أو", "selectAllDataGridFilteringLabel" : "اختر الكل", "sortAndFilterDataGridFilteringLabel" : "الفرز والتصفية", -"clearFilterDataGridFilteringLabel" : "مرشح واضح", +"clearFilterDataGridFilteringLabel" : "مسح المرشح", "fromDataGridFilteringLabel" : "من" } \ No newline at end of file diff --git a/packages/syncfusion_localizations/pubspec.yaml b/packages/syncfusion_localizations/pubspec.yaml index dce463231..48e77a899 100644 --- a/packages/syncfusion_localizations/pubspec.yaml +++ b/packages/syncfusion_localizations/pubspec.yaml @@ -1,6 +1,6 @@ name: syncfusion_localizations description: Syncfusion Localizations package contains localized text for 77 cultures for all the applicable Syncfusion Flutter Widgets. -version: 20.4.38 +version: 21.1.35-nullsafety homepage: https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_localizations environment: @@ -10,7 +10,7 @@ dependencies: flutter: sdk: flutter intl: ">=0.17.0 <0.20.0" - syncfusion_flutter_core: ^20.4.38 + syncfusion_flutter_core: ^21.1.35 dev_dependencies: flutter_test: diff --git a/packages/syncfusion_officechart/lib/src/chart/chart_collection.dart b/packages/syncfusion_officechart/lib/src/chart/chart_collection.dart index 6a4a60422..2db25e8ad 100644 --- a/packages/syncfusion_officechart/lib/src/chart/chart_collection.dart +++ b/packages/syncfusion_officechart/lib/src/chart/chart_collection.dart @@ -75,17 +75,34 @@ class ChartCollection extends ChartHelper { /// Serialize the charts. @override - void serializeCharts(Worksheet sheet) { + void serializeChartsSync(Worksheet sheet) { _chartSerialization ??= ChartSerialization(sheet.workbook); _chartSerialization!._saveCharts(sheet); } + /// Serialize the charts. + @override + Future serializeCharts(Worksheet sheet) async { + _chartSerialization ??= ChartSerialization(sheet.workbook); + + _chartSerialization!._saveChartsAsync(sheet); + } + /// Serialize the chart drawings. @override - void serializeChartDrawing(XmlBuilder builder, Worksheet sheet) { + void serializeChartDrawingSync(XmlBuilder builder, Worksheet sheet) { _chartSerialization ??= ChartSerialization(sheet.workbook); _chartSerialization!._serializeChartDrawing(builder, sheet); } + + /// Serialize the chart drawings. + @override + Future serializeChartDrawing( + XmlBuilder builder, Worksheet sheet) async { + _chartSerialization ??= ChartSerialization(sheet.workbook); + + _chartSerialization!._serializeChartDrawingAsync(builder, sheet); + } } diff --git a/packages/syncfusion_officechart/lib/src/chart/chart_serialization.dart b/packages/syncfusion_officechart/lib/src/chart/chart_serialization.dart index 38b75b99c..e9d6455b9 100644 --- a/packages/syncfusion_officechart/lib/src/chart/chart_serialization.dart +++ b/packages/syncfusion_officechart/lib/src/chart/chart_serialization.dart @@ -1577,4 +1577,1586 @@ class ChartSerialization { }); } } + + /// serializes charts from the worksheet. + Future _saveChartsAsync(Worksheet sheet) async { + for (final Chart chart in (sheet.charts! as ChartCollection).innerList) { + sheet.workbook.chartCount++; + final XmlBuilder builder = XmlBuilder(); + builder.processing('xml', 'version="1.0"'); + builder.element('c:chartSpace', nest: () async { + builder.attribute( + 'xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main'); + builder.attribute('xmlns:r', + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + builder.attribute('xmlns:c', + 'http://schemas.openxmlformats.org/drawingml/2006/chart'); + + if (chart._hasPlotArea) { + builder.element('c:roundedCorners', nest: () async { + builder.attribute('val', 0); + }); + } + builder.element('c:chart', nest: () async { + if (chart.hasTitle) { + _serializeTitleAsync(builder, chart.chartTitleArea); + } + if (chart._is3DChart) { + _serializeView3DAsync(builder, chart); + } + _serializePlotAreaAsync(builder, chart); + if (chart.series.count > 0 && chart.hasLegend) { + _serializeLegendAsync(builder, chart.legend!); + } + builder.element('c:plotVisOnly', nest: () async { + builder.attribute('val', '1'); + }); + builder.element('c:dispBlanksAs', nest: () async { + builder.attribute('val', 'gap'); + }); + }); + _serializeFillAsync( + builder, chart.linePattern, chart.linePatternColor, false); + _serializePrinterSettingsAsync(builder, chart); + }); + final String stringXml = builder.buildDocument().copy().toString(); + final List bytes = utf8.encode(stringXml); + _addToArchiveAsync( + bytes, 'xl/charts/chart${sheet.workbook.chartCount}.xml'); + } + } + + /// serializes chart's legend. + Future _serializeLegendAsync( + XmlBuilder builder, ChartLegend chartLegend) async { + builder.element('c:legend', nest: () async { + builder.element('c:legendPos', nest: () async { + final ExcelLegendPosition legendPostion = chartLegend.position; + final String positionStr = legendPostion == ExcelLegendPosition.bottom + ? 'b' + : legendPostion == ExcelLegendPosition.left + ? 'l' + : legendPostion == ExcelLegendPosition.right + ? 'r' + : legendPostion == ExcelLegendPosition.top + ? 't' + : 'r'; + builder.attribute('val', positionStr); + }); + if (chartLegend._hasTextArea) { + _serializeDefaultTextAreaPropertiesAsync(builder, chartLegend.textArea); + } + builder.element('c:layout', nest: () async {}); + builder.element('c:overlay', nest: () async { + builder.attribute('val', 0); + }); + builder.element('c:spPr', nest: () async {}); + }); + } + + /// serializes chart's drawing. + Future _serializeChartDrawingAsync( + XmlBuilder builder, Worksheet sheet) async { + for (final Chart chart in (sheet.charts! as ChartCollection).innerList) { + builder.element('xdr:twoCellAnchor', nest: () async { + builder.attribute('editAs', 'twoCell'); + builder.element('xdr:from', nest: () async { + builder.element('xdr:col', + nest: chart.leftColumn > 0 ? chart.leftColumn - 1 : 0); + builder.element('xdr:colOff', nest: 0); + builder.element('xdr:row', + nest: chart.topRow > 0 ? chart.topRow - 1 : 0); + builder.element('xdr:rowOff', nest: 0); + }); + + builder.element('xdr:to', nest: () async { + builder.element('xdr:col', + nest: chart.rightColumn > 0 + ? chart.rightColumn - 1 + : chart.leftColumn + 7); + builder.element('xdr:colOff', nest: 0); + builder.element('xdr:row', + nest: chart.bottomRow > 0 + ? chart.bottomRow - 1 + : chart.topRow + 15); + builder.element('xdr:rowOff', nest: 0); + }); + + builder.element('xdr:graphicFrame', nest: () async { + builder.attribute('macro', ''); + builder.element('xdr:nvGraphicFramePr', nest: () async { + builder.element('xdr:cNvPr', nest: () async { + builder.attribute( + 'id', 1024 + sheet.workbook.chartCount + chart.index); + builder.attribute( + 'name', 'Chart ${sheet.workbook.chartCount + chart.index}'); + }); + builder.element('xdr:cNvGraphicFramePr', nest: () async {}); + }); + builder.element('xdr:xfrm', nest: () async { + builder.element('a:off', nest: () async { + builder.attribute('x', '0'); + builder.attribute('y', 0); + }); + builder.element('a:ext', nest: () async { + builder.attribute('cx', '0'); + builder.attribute('cy', 0); + }); + }); + builder.element('a:graphic', nest: () async { + builder.element('a:graphicData', nest: () async { + builder.attribute('uri', + 'http://schemas.openxmlformats.org/drawingml/2006/chart'); + + builder.element('c:chart', nest: () async { + builder.attribute('p7:id', 'rId${chart.index}'); + builder.attribute('xmlns:p7', + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + builder.attribute('xmlns:c', + 'http://schemas.openxmlformats.org/drawingml/2006/chart'); + }); + }); + }); + }); + builder.element('xdr:clientData', nest: () async {}); + }); + } + } + + /// serialize default text area properties. + Future _serializeDefaultTextAreaPropertiesAsync( + XmlBuilder builder, ChartTextArea textArea) async { + builder.element('c:txPr', nest: () async { + builder.element('a:bodyPr ', nest: () async {}); + builder.element('a:lstStyle ', nest: () async {}); + builder.element('a:p', nest: () async { + builder.element('a:pPr', nest: () async { + builder.element('a:defRPr', nest: () async { + final double size = textArea.size * 100; + builder.attribute('sz', size.toInt().toString()); + builder.attribute('b', textArea.bold ? '1' : '0'); + builder.attribute('i', textArea.italic ? '1' : '0'); + if (textArea.color != '' && textArea.color != 'FF000000') { + builder.element('a:solidFill', nest: () async { + builder.element('a:srgbClr', nest: () async { + builder.attribute('val', textArea.color.replaceAll('#', '')); + }); + }); + } + builder.element('a:latin', nest: () async { + builder.attribute('typeface', textArea.fontName); + }); + builder.element('a:cs', nest: () async { + builder.attribute('typeface', textArea.fontName); + }); + }); + }); + builder.element('a:endParaRPr', nest: () async { + builder.attribute('lang', 'en-US'); + }); + }); + }); + } + + /// serialize printer settings of charts + Future _serializePrinterSettingsAsync( + XmlBuilder builder, Chart chart) async { + builder.element('c:printSettings', nest: () async { + builder.element('c:headerFooter', nest: () async { + builder.attribute('scaleWithDoc', '1'); + builder.attribute('alignWithMargins', '0'); + builder.attribute('differentFirst', '0'); + builder.attribute('differentOddEven', '0'); + }); + builder.element('c:pageMargins', nest: () async { + builder.attribute('l', '0.7'); + builder.attribute('r', '0.7'); + builder.attribute('t', '0.75'); + builder.attribute('b', '0.75'); + builder.attribute('header', '0.3'); + builder.attribute('footer', '0.3'); + }); + }); + } + + /// serialize chart title. + Future _serializeTitleAsync( + XmlBuilder builder, ChartTextArea chartTextArea) async { + builder.element('c:title', nest: () async { + if (chartTextArea._hasText) { + builder.element('c:tx', nest: () async { + builder.element('c:rich', nest: () async { + builder.element('a:bodyPr ', nest: () async {}); + builder.element('a:lstStyle', nest: () async {}); + builder.element('a:p', nest: () async { + builder.element('a:pPr', nest: () async { + builder.attribute('algn', 'ctr'); + builder.element('a:defRPr', nest: () async {}); + }); + builder.element('a:r', nest: () async { + _serializeParagraphRunPropertiesAsync(builder, chartTextArea); + builder.element('a:t', nest: () async { + builder.text(chartTextArea.text!); + }); + }); + }); + }); + }); + } + builder.element('c:layout', nest: () async {}); + builder.element('c:overlay', nest: () async { + builder.attribute('val', chartTextArea._overlay ? '1' : '0'); + }); + _serializeFillAsync(builder, ExcelChartLinePattern.none, null, false); + }); + } + + /// serialize paragraph run properties. + Future _serializeParagraphRunPropertiesAsync( + XmlBuilder builder, ChartTextArea textArea) async { + builder.element('a:rPr', nest: () async { + builder.attribute('lang', 'en-US'); + final double size = textArea.size * 100; + builder.attribute('sz', size.toInt().toString()); + builder.attribute('b', textArea.bold ? '1' : '0'); + builder.attribute('i', textArea.italic ? '1' : '0'); + builder.attribute('baseline', '0'); + if (textArea.color != '' && textArea.color != 'FF000000') { + builder.element('a:solidFill', nest: () async { + builder.element('a:srgbClr', nest: () async { + builder.attribute('val', textArea.color.replaceAll('#', '')); + }); + }); + } + builder.element('a:latin', nest: () async { + builder.attribute('typeface', textArea.fontName); + }); + builder.element('a:ea', nest: () async { + builder.attribute('typeface', textArea.fontName); + }); + builder.element('a:cs', nest: () async { + builder.attribute('typeface', textArea.fontName); + }); + }); + } + + /// serialize plotarea of the chart + Future _serializePlotAreaAsync(XmlBuilder builder, Chart chart) async { + builder.element('c:plotArea', nest: () async { + builder.element('c:layout', nest: () async {}); + if (chart._series.count > 0) { + _serializeMainChartTypeTagAsync(builder, chart); + } else { + _serializeEmptyChartAsync(builder, chart); + _serializeAxesAsync(builder, chart); + } + _serializeFillAsync(builder, chart.plotArea.linePattern, + chart.plotArea.linePatternColor, false); + }); + } + + /// serializes main chart tag. + Future _serializeMainChartTypeTagAsync( + XmlBuilder builder, Chart chart) async { + switch (chart.chartType) { + case ExcelChartType.column: + case ExcelChartType.columnStacked: + case ExcelChartType.columnStacked100: + case ExcelChartType.bar: + case ExcelChartType.barStacked: + case ExcelChartType.barStacked100: + _serializeBarChartAsync(builder, chart); + break; + + case ExcelChartType.line: + case ExcelChartType.lineStacked: + case ExcelChartType.lineMarkers: + case ExcelChartType.lineStacked100: + case ExcelChartType.lineMarkersStacked: + case ExcelChartType.lineMarkersStacked100: + _serializeLineChartAsync(builder, chart); + break; + + case ExcelChartType.area: + case ExcelChartType.areaStacked: + case ExcelChartType.areaStacked100: + _serializeAreaChartAsync(builder, chart); + break; + + case ExcelChartType.pie: + _serializePieChartAsync(builder, chart); + break; + + case ExcelChartType.pieBar: + case ExcelChartType.pieOfPie: + _serializeOfPieChartAsync(builder, chart); + break; + case ExcelChartType.pie3D: + _serializeOfPie3DChartAsync(builder, chart); + break; + case ExcelChartType.line3D: + _serializeLine3DChartAsync(builder, chart); + break; + case ExcelChartType.barStacked1003D: + case ExcelChartType.barStacked3D: + case ExcelChartType.barClustered3D: + case ExcelChartType.columnClustered3D: + case ExcelChartType.columnStacked3D: + case ExcelChartType.columnStacked1003D: + case ExcelChartType.column3D: + _serializeBar3DChartAsync(builder, chart); + break; + + case ExcelChartType.stockHighLowClose: + case ExcelChartType.stockOpenHighLowClose: + case ExcelChartType.stockVolumeHighLowClose: + case ExcelChartType.stockVolumeOpenHighLowClose: + _serializeStockChartAsync(builder, chart); + break; + case ExcelChartType.doughnut: + case ExcelChartType.doughnutExploded: + _serializedoughnutchartAsync(builder, chart); + break; + } + } + + /// serializes Line chart. + Future _serializeLineChartAsync(XmlBuilder builder, Chart chart) async { + final ExcelChartType type = chart.series[0]._serieType; + builder.element('c:lineChart', nest: () async { + _serializeChartGroupingAsync(builder, chart); + builder.element('c:varyColors', nest: () async { + builder.attribute('val', 0); + }); + for (int i = 0; i < chart.series.count; i++) { + final ChartSerie firstSerie = chart.series[i]; + _serializeSerieAsync(builder, firstSerie); + } + if (type == ExcelChartType.lineMarkers || + type == ExcelChartType.lineMarkersStacked || + type == ExcelChartType.lineMarkersStacked100) { + builder.element('c:marker', nest: () async { + builder.attribute('val', 1); + }); + } + builder.element('c:axId', nest: () async { + builder.attribute('val', 59983360); + }); + builder.element('c:axId', nest: () async { + builder.attribute('val', 57253888); + }); + }); + _serializeAxesAsync(builder, chart); + } + + /// serializes Bar/ColumnClustered chart. + Future _serializeBarChartAsync(XmlBuilder builder, Chart chart) async { + late int gapwidth; + builder.element('c:barChart', nest: () async { + final String strDirection = + chart.chartType.toString().contains('bar') ? 'bar' : 'col'; + builder.element('c:barDir', nest: () async { + builder.attribute('val', strDirection); + }); + _serializeChartGroupingAsync(builder, chart); + builder.element('c:varyColors', nest: () async { + builder.attribute('val', 0); + }); + for (int i = 0; i < chart.series.count; i++) { + final ChartSerie firstSerie = chart.series[i]; + _serializeSerieAsync(builder, firstSerie); + gapwidth = firstSerie.serieFormat.commonSerieOptions.gapWidth; + } + builder.element('c:gapWidth', nest: () async { + builder.attribute('val', gapwidth); + }); + if (chart._getIsStacked(chart.chartType) || + chart._getIs100(chart.chartType)) { + builder.element('c:overlap', nest: () async { + builder.attribute('val', '100'); + }); + } + builder.element('c:axId', nest: () async { + builder.attribute('val', 59983360); + }); + builder.element('c:axId', nest: () async { + builder.attribute('val', 57253888); + }); + }); + _serializeAxesAsync(builder, chart); + } + + /// serializes Area chart. + Future _serializeAreaChartAsync(XmlBuilder builder, Chart chart) async { + builder.element('c:areaChart', nest: () async { + _serializeChartGroupingAsync(builder, chart); + builder.element('c:varyColors', nest: () async { + builder.attribute('val', 0); + }); + for (int i = 0; i < chart.series.count; i++) { + final ChartSerie firstSerie = chart.series[i]; + _serializeSerieAsync(builder, firstSerie); + } + builder.element('c:axId', nest: () async { + builder.attribute('val', 59983360); + }); + builder.element('c:axId', nest: () async { + builder.attribute('val', 57253888); + }); + }); + _serializeAxesAsync(builder, chart); + } + + /// serialize chart grouping. + Future _serializeChartGroupingAsync( + XmlBuilder builder, Chart chart) async { + String strGrouping; + if (chart._getIsClustered(chart.chartType)) { + strGrouping = 'clustered'; + } else if (chart._getIs100(chart.chartType)) { + strGrouping = 'percentStacked'; + } else if (chart._getIsStacked(chart.chartType)) { + strGrouping = 'stacked'; + } else { + strGrouping = 'standard'; + } + builder.element('c:grouping', nest: () async { + builder.attribute('val', strGrouping); + }); + } + + /// serializes pie chart. + Future _serializePieChartAsync(XmlBuilder builder, Chart chart) async { + builder.element('c:pieChart', nest: () async { + builder.element('c:varyColors', nest: () async { + builder.attribute('val', 1); + }); + for (int i = 0; i < chart.series.count; i++) { + final ChartSerie firstSerie = chart.series[i]; + _serializeSerieAsync(builder, firstSerie); + } + builder.element('c:firstSliceAng', nest: () async { + builder.attribute('val', 0); + }); + }); + } + + /// serializes series of chart. + Future _serializeSerieAsync( + XmlBuilder builder, ChartSerie firstSerie) async { + final ExcelChartType type = firstSerie._serieType; + builder.element('c:ser', nest: () async { + builder.element('c:idx', nest: () async { + builder.attribute('val', firstSerie._index); + }); + builder.element('c:order', nest: () async { + builder.attribute('val', firstSerie._index); + }); + if (firstSerie._isDefaultName) { + builder.element('c:tx', nest: () async { + final String strName = firstSerie._nameOrFormula; + if (strName.isNotEmpty) { + _serializeStringReferenceAsync( + builder, strName, firstSerie, 'text', null); + } + }); + } + + if (firstSerie.serieFormat.pieExplosionPercent != 0 || + (type == ExcelChartType.doughnutExploded && + firstSerie._chart.series.count == 1)) { + builder.element('c:explosion', nest: () async { + builder.attribute('val', firstSerie.serieFormat.pieExplosionPercent); + }); + } + if (type == ExcelChartType.stockOpenHighLowClose || + type == ExcelChartType.stockVolumeHighLowClose || + type == ExcelChartType.stockVolumeOpenHighLowClose) { + builder.element('c:spPr', nest: () async { + builder.element('a:ln', nest: () async { + builder.element('a:noFill', nest: () async {}); + }); + }); + } else if (type == ExcelChartType.stockHighLowClose) { + if (firstSerie._index == 2) { + builder.element('c:spPr', nest: () async { + builder.element('a:ln', nest: () async { + builder.attribute('w', '3175'); + builder.element('a:solidFill', nest: () async { + builder.element('a:srgbClr', nest: () async { + builder.attribute('val', '000000'); + }); + }); + builder.element('a:prstDash', nest: () async { + builder.attribute('val', 'solid'); + }); + }); + }); + } else { + builder.element('c:spPr', nest: () async { + builder.element('a:ln', nest: () async { + builder.element('a:noFill', nest: () async {}); + }); + }); + } + } + if (firstSerie.linePattern != ExcelChartLinePattern.none) { + _serializeFillAsync( + builder, firstSerie.linePattern, firstSerie.linePatternColor, true); + } + + _serializeMarkerAsync(builder, firstSerie); + + if (firstSerie.dataLabels.isValue) { + builder.element('c:dLbls', nest: () async { + if (firstSerie.dataLabels.numberFormat != null && + firstSerie.dataLabels.numberFormat != '' && + firstSerie.dataLabels.numberFormat != 'General') { + builder.element('c:numFmt', nest: () async { + builder.attribute( + 'formatCode', firstSerie.dataLabels.numberFormat); + builder.attribute('sourceLinked', '0'); + }); + } + builder.element('c:spPr', nest: () async { + builder.element('a:noFill', nest: () async {}); + builder.element('a:ln', nest: () async { + builder.element('a:noFill', nest: () async {}); + }); + builder.element('a:effectLst', nest: () async {}); + }); + final ChartTextArea textArea = firstSerie.dataLabels.textArea; + _serializeChartTextAreaAsync(builder, textArea); + builder.element('c:showLegendKey', nest: () async { + builder.attribute('val', 0); + }); + builder.element('c:showVal', nest: () async { + builder.attribute('val', firstSerie.dataLabels.isValue ? 1 : 0); + }); + builder.element('c:showCatName', nest: () async { + builder.attribute( + 'val', firstSerie.dataLabels.isCategoryName ? 1 : 0); + }); + builder.element('c:showSerName', nest: () async { + builder.attribute( + 'val', firstSerie.dataLabels.isSeriesName ? 1 : 0); + }); + builder.element('c:showPercent', nest: () async { + builder.attribute('val', 0); + }); + builder.element('c:showBubbleSize', nest: () async { + builder.attribute('val', 0); + }); + builder.element('c:showLeaderLines', nest: () async { + builder.attribute('val', 0); + }); + }); + } + if (firstSerie._categoryLabels != null) { + final Range firstRange = firstSerie._chart._worksheet.getRangeByIndex( + firstSerie._categoryLabels!.row, + firstSerie._categoryLabels!.column); + builder.element('c:cat', nest: () async { + Worksheet tempSheet = firstSerie._chart._worksheet; + if (firstSerie._categoryLabels!.addressGlobal != '') { + for (final Worksheet sheet + in firstSerie._chart._worksheet.workbook.worksheets.innerList) { + if (firstSerie._categoryLabels!.addressGlobal + .contains(RegExp('${sheet.name}!')) || + firstSerie._categoryLabels!.addressGlobal + .contains(RegExp("${sheet.name}'!"))) { + tempSheet = sheet; + break; + } + } + } + if (firstRange.text == null && firstRange.number != null) { + builder.element('c:numRef', nest: () async { + builder.element('c:f', + nest: firstSerie._categoryLabels!.addressGlobal); + final Range firstRange = firstSerie._chart._worksheet + .getRangeByIndex(firstSerie._categoryLabels!.row, + firstSerie._categoryLabels!.column); + builder.element('c:numCache', nest: () async { + if (firstRange.numberFormat != null && + firstRange.numberFormat != 'General') { + builder.element('c:formatCode', + nest: firstRange.numberFormat); + } + _serializeNumCacheValuesAsync(builder, firstSerie, tempSheet); + }); + }); + } else { + _serializeStringReferenceAsync( + builder, + firstSerie._categoryLabels!.addressGlobal, + firstSerie, + 'cat', + tempSheet); + } + }); + } + if (firstSerie._values != null) { + builder.element('c:val', nest: () async { + builder.element('c:numRef', nest: () async { + builder.element('c:f', nest: firstSerie._values!.addressGlobal); + final Range firstRange = firstSerie._chart._worksheet + .getRangeByIndex( + firstSerie._values!.row, firstSerie._values!.column); + Worksheet tempSheet = firstSerie._chart._worksheet; + if (firstSerie._values!.addressGlobal != '') { + for (final Worksheet sheet in firstSerie + ._chart._worksheet.workbook.worksheets.innerList) { + if (firstSerie._values!.addressGlobal + .contains(RegExp('${sheet.name}!')) || + firstSerie._values!.addressGlobal + .contains(RegExp("${sheet.name}'!"))) { + tempSheet = sheet; + break; + } + } + } + builder.element('c:numCache', nest: () async { + if (firstRange.numberFormat != null && + firstRange.numberFormat != 'General') { + builder.element('c:formatCode', nest: firstRange.numberFormat); + } else { + builder.element('c:formatCode', nest: 'General'); + } + _serializeNumCacheValuesAsync(builder, firstSerie, tempSheet); + }); + }); + }); + } + if (firstSerie._serieType.toString().contains('line') || + firstSerie._serieType.toString().contains('stock')) { + builder.element('c:smooth', nest: () async { + builder.attribute('val', 0); + }); + } + }); + } + + /// serializes chart text area. + Future _serializeChartTextAreaAsync( + XmlBuilder builder, ChartTextArea textArea) async { + builder.element('c:txPr', nest: () async { + builder.element('a:bodyPr ', nest: () async {}); + builder.element('a:lstStyle', nest: () async {}); + builder.element('a:p', nest: () async { + builder.element('a:pPr', nest: () async { + builder.element('a:defRPr', nest: () async { + final double size = textArea.size * 100; + builder.attribute('sz', size.toInt().toString()); + builder.attribute('b', textArea.bold ? '1' : '0'); + builder.attribute('i', textArea.italic ? '1' : '0'); + builder.attribute('baseline', '0'); + if (textArea.color != '' && textArea.color != 'FF000000') { + builder.element('a:solidFill', nest: () async { + builder.element('a:srgbClr', nest: () async { + builder.attribute('val', textArea.color.replaceAll('#', '')); + }); + }); + } + builder.element('a:latin', nest: () async { + builder.attribute('typeface', textArea.fontName); + }); + builder.element('a:ea', nest: () async { + builder.attribute('typeface', textArea.fontName); + }); + builder.element('a:cs', nest: () async { + builder.attribute('typeface', textArea.fontName); + }); + }); + }); + }); + }); + } + + /// serializes number cache values. + Future _serializeNumCacheValuesAsync( + XmlBuilder builder, ChartSerie firstSerie, Worksheet dataSheet) async { + final Range? serieRange = firstSerie._values; + if (serieRange != null) { + final int count = serieRange.count; + final int serieStartRow = serieRange.row; + final int serieStartColumn = serieRange.column; + builder.element('c:ptCount', nest: () async { + builder.attribute('val', count); + }); + int index = 0; + while (index < count) { + final double? value = dataSheet + .getRangeByIndex(serieStartRow + index, serieStartColumn) + .number; + if (value != null) { + builder.element('c:pt', nest: () async { + builder.attribute('idx', index); + builder.element('c:v', nest: value); + }); + } + index++; + } + } + } + + /// serializes string cache reference. + Future _serializeStringReferenceAsync(XmlBuilder builder, String range, + ChartSerie firstSerie, String tagName, Worksheet? dataSheet) async { + builder.element('c:strRef', nest: () async { + builder.element('c:f', nest: range); + builder.element('c:strCache', nest: () async { + if (tagName == 'cat') { + _serializeCategoryTagCacheValuesAsync(builder, firstSerie, dataSheet); + } else { + _serializeTextTagCacheValuesAsync(builder, firstSerie); + } + }); + }); + } + + /// serializes catergory cache values. + Future _serializeCategoryTagCacheValuesAsync( + XmlBuilder builder, ChartSerie firstSerie, Worksheet? dataSheet) async { + final Range? serieRange = firstSerie._categoryLabels; + if (serieRange != null) { + final int count = serieRange.count; + final int serieStartRow = serieRange.row; + final int serieStartColumn = serieRange.column; + builder.element('c:ptCount', nest: () async { + builder.attribute('val', count); + }); + int index = 0; + while (index < count) { + final String? value = dataSheet != null + ? dataSheet + .getRangeByIndex(serieStartRow + index, serieStartColumn) + .text + : ''; + if (value != null) { + builder.element('c:pt', nest: () async { + builder.attribute('idx', index); + builder.element('c:v', nest: value); + }); + } + index++; + } + } + } + + /// serializes text cache values. + Future _serializeTextTagCacheValuesAsync( + XmlBuilder builder, ChartSerie firstSerie) async { + builder.element('c:ptCount', nest: () async { + builder.attribute('val', 1); + }); + builder.element('c:pt', nest: () async { + builder.attribute('idx', 0); + if (firstSerie.name != null) { + builder.element('c:v', nest: firstSerie.name); + } + }); + } + + /// serializes fill for the charts. + Future _serializeFillAsync( + XmlBuilder builder, + ExcelChartLinePattern linePattern, + String? lineColor, + bool hasSerie) async { + builder.element('c:spPr', nest: () async { + if (lineColor == null) { + builder.element('a:solidFill', nest: () async { + builder.element('a:srgbClr', nest: () async { + builder.attribute('val', 'FFFFFF'); + }); + }); + } + builder.element('a:ln', nest: () async { + if (linePattern != ExcelChartLinePattern.none) { + if (!hasSerie || lineColor != null) { + builder.element('a:solidFill', nest: () async { + builder.element('a:srgbClr', nest: () async { + if (lineColor != null) { + builder.attribute('val', lineColor.replaceAll('#', '')); + } else { + builder.attribute('val', '0070C0'); + } + }); + }); + } + if (linePattern != ExcelChartLinePattern.solid) { + builder.element('a:prstDash', nest: () async { + builder.attribute('val', _linePattern[linePattern]); + }); + } + } else { + builder.element('a:noFill', nest: () async {}); + } + builder.element('a:round', nest: () async {}); + }); + }); + } + + /// serializes axies of the series. + Future _serializeAxesAsync(XmlBuilder builder, Chart chart) async { + if (chart._isCategoryAxisAvail) { + _serializeCategoryAxisAsync(builder, chart.primaryCategoryAxis); + } + if (chart._isValueAxisAvail) { + _serializeValueAxisAsync(builder, chart.primaryValueAxis); + } + } + + /// serializes empty charts. + Future _serializeEmptyChartAsync( + XmlBuilder builder, Chart chart) async { + builder.element('c:barChart', nest: () async { + builder.element('c:barDir', nest: () async { + builder.attribute('val', 'col'); + }); + builder.element('c:grouping', nest: () async { + builder.attribute('val', 'clustered'); + }); + builder.element('c:varyColors', nest: () async { + builder.attribute('val', 0); + }); + _serializeEmptyChartDataLabelsAsync(builder); + builder.element('c:gapWidth', nest: () async { + builder.attribute('val', 150); + }); + builder.element('c:axId', nest: () async { + builder.attribute('val', 59983360); + }); + builder.element('c:axId', nest: () async { + builder.attribute('val', 57253888); + }); + }); + } + + /// serializes category axis of the chart. + Future _serializeCategoryAxisAsync( + XmlBuilder builder, ChartCategoryAxis axis) async { + builder.element('c:catAx', nest: () async { + builder.element('c:axId', nest: () async { + builder.attribute('val', 59983360); + }); + builder.element('c:scaling', nest: () async { + builder.element('c:orientation', nest: () async { + builder.attribute('val', 'minMax'); + }); + }); + builder.element('c:delete', nest: () async { + builder.attribute('val', 0); + }); + builder.element('c:axPos', nest: () async { + builder.attribute('val', 'b'); + }); + if (axis._hasAxisTitle) { + _serializeChartTextAreaAsync(builder, axis.titleArea); + } + if (axis.numberFormat != '' && axis.numberFormat != 'General') { + builder.element('c:numFmt', nest: () async { + builder.attribute('formatCode', axis.numberFormat); + builder.attribute('sourceLinked', '0'); + }); + } + builder.element('c:majorTickMark', nest: () async { + builder.attribute('val', 'out'); + }); + builder.element('c:minorTickMark', nest: () async { + builder.attribute('val', 'none'); + }); + builder.element('c:tickLblPos', nest: () async { + builder.attribute('val', 'nextTo'); + }); + builder.element('c:spPr', nest: () async { + builder.element('a:ln', nest: () async {}); + }); + builder.element('c:crossAx', nest: () async { + builder.attribute('val', 57253888); + }); + builder.element('c:crosses', nest: () async { + builder.attribute('val', 'autoZero'); + }); + builder.element('c:auto', nest: () async { + builder.attribute('val', 1); + }); + builder.element('c:lblAlgn', nest: () async { + builder.attribute('val', 'ctr'); + }); + builder.element('c:lblOffset', nest: () async { + builder.attribute('val', 100); + }); + builder.element('c:noMultiLvlLbl', nest: () async { + builder.attribute('val', 0); + }); + builder.element('c:tickMarkSkip', nest: () async { + builder.attribute('val', 1); + }); + }); + } + + /// serializes Value axis of the chart. + Future _serializeValueAxisAsync( + XmlBuilder builder, ChartValueAxis axis) async { + builder.element('c:valAx', nest: () async { + builder.element('c:axId', nest: () async { + builder.attribute('val', 57253888); + }); + builder.element('c:scaling', nest: () async { + builder.element('c:orientation', nest: () async { + builder.attribute('val', 'minMax'); + }); + if (!axis._isAutoMax) { + builder.element('c:max', nest: () async { + builder.attribute('val', axis.maximumValue); + }); + } + if (axis._isAutoMin) { + builder.element('c:min', nest: () async { + builder.attribute('val', axis.minimumValue); + }); + } + }); + builder.element('c:delete', nest: () async { + builder.attribute('val', '0'); + }); + builder.element('c:axPos', nest: () async { + builder.attribute('val', 'l'); + }); + if (axis._hasAxisTitle) { + _serializeChartTextAreaAsync(builder, axis.titleArea); + } + if (axis.numberFormat != '' && axis.numberFormat != 'General') { + builder.element('c:numFmt', nest: () async { + builder.attribute('formatCode', axis.numberFormat); + builder.attribute('sourceLinked', '0'); + }); + } + if (axis.hasMajorGridLines) { + builder.element('c:majorGridlines', nest: () async {}); + } + builder.element('c:majorTickMark', nest: () async { + builder.attribute('val', 'out'); + }); + builder.element('c:minorTickMark', nest: () async { + builder.attribute('val', 'none'); + }); + builder.element('c:tickLblPos', nest: () async { + builder.attribute('val', 'nextTo'); + }); + builder.element('c:spPr', nest: () async { + builder.element('a:ln', nest: () async {}); + }); + builder.element('c:crossAx', nest: () async { + builder.attribute('val', 59983360); + }); + builder.element('c:crosses', nest: () async { + builder.attribute('val', 'autoZero'); + }); + final Chart chart = axis._parentChart; + final String strCrossBetween = + chart.primaryCategoryAxis._isBetween ? 'between' : 'midCat'; + builder.element('c:crossBetween', nest: () async { + builder.attribute('val', strCrossBetween); + }); + }); + } + + /// serializes empty chart's datalabels. + Future _serializeEmptyChartDataLabelsAsync(XmlBuilder builder) async { + builder.element('c:dLbls', nest: () async { + builder.element('c:showLegendKey', nest: () async { + builder.attribute('val', 0); + }); + builder.element('c:showVal', nest: () async { + builder.attribute('val', 0); + }); + builder.element('c:showCatName', nest: () async { + builder.attribute('val', 0); + }); + builder.element('c:showSerName', nest: () async { + builder.attribute('val', 0); + }); + builder.element('c:showPercent', nest: () async { + builder.attribute('val', 0); + }); + builder.element('c:showBubbleSize', nest: () async { + builder.attribute('val', 0); + }); + }); + } + + /// Add the workbook data with filename to ZipArchive. + Future _addToArchiveAsync(List data, String fileName) async { + final ArchiveFile item = ArchiveFile(fileName, data.length, data); + _workbook.archive.addFile(item); + } + + /// Serialize line 3D Chart. + Future _serializeLine3DChartAsync( + XmlBuilder builder, Chart chart) async { + late int gapdepth; + builder.element('c:line3DChart', nest: () async { + _serializeChartGroupingAsync(builder, chart); + builder.element('c:varyColors', nest: () async { + builder.attribute('val', 0); + }); + for (int i = 0; i < chart.series.count; i++) { + final ChartSerie firstSerie = chart.series[i]; + _serializeSerieAsync(builder, firstSerie); + gapdepth = firstSerie.serieFormat.commonSerieOptions.gapDepth; + } + builder.element('c:gapDepth', nest: () async { + builder.attribute('val', gapdepth); + }); + builder.element('c:axId', nest: () async { + builder.attribute('val', 59983360); + }); + builder.element('c:axId', nest: () async { + builder.attribute('val', 57253888); + }); + builder.element('c:axId', nest: () async { + builder.attribute('val', 63149376); + }); + }); + _serializeAxesAsync(builder, chart); + } + + ///Serialize view 3D + Future _serializeView3DAsync(XmlBuilder builder, Chart chart) async { + final ChartSeriesCollection firstSerie = chart.series; + + builder.element('c:view3D', nest: () async { + if (!chart._isdefaultElevation) { + builder.element('c:rotX', nest: () async { + builder.attribute('val', chart.elevation); + }); + } + if (firstSerie._innerList[0]._serieType == ExcelChartType.pie3D) { + for (int i = 0; i < chart.series.count; i++) { + chart.rotation = + chart.series[i].serieFormat.commonSerieOptions.firstSliceAngle; + } + } + if (!chart._isDefaultRotation) { + builder.element('c:rotY', nest: () async { + builder.attribute('val', chart.rotation); + }); + } + builder.element('c:depthPercent', nest: () async { + builder.attribute('val', chart.depthPercent); + }); + builder.element('c:rAngAx', nest: () async { + int defaultValue = 0; + + if (chart.rightAngleAxes || chart._isColumnOrBar) { + defaultValue = 1; + } + + builder.attribute('val', defaultValue); + }); + builder.element('perspective', nest: () async { + builder.attribute('val', chart.perspective * 2); + }); + }); + } + + /// Serialize bar3D chart. + Future _serializeBar3DChartAsync( + XmlBuilder builder, Chart chart) async { + late int gapdepth; + late int gapwidth; + builder.element('c:bar3DChart', nest: () async { + final String strDirection = + chart.chartType.toString().contains('bar') ? 'bar' : 'col'; + + builder.element('c:barDir', nest: () async { + builder.attribute('val', strDirection); + }); + + _serializeChartGroupingAsync(builder, chart); + builder.element('c:varyColors', nest: () async { + builder.attribute('val', 0); + }); + for (int i = 0; i < chart.series.count; i++) { + final ChartSerie firstSerie = chart.series[i]; + _serializeSerieAsync(builder, firstSerie); + gapdepth = firstSerie.serieFormat.commonSerieOptions.gapDepth; + gapwidth = firstSerie.serieFormat.commonSerieOptions.gapWidth; + } + + builder.element('c:gapWidth', nest: () async { + builder.attribute('val', gapwidth); + }); + builder.element('c:gapDepth', nest: () async { + builder.attribute('val', gapdepth); + }); + builder.element('c:axId', nest: () async { + builder.attribute('val', 59983360); + }); + builder.element('c:axId', nest: () async { + builder.attribute('val', 57253888); + }); + }); + _serializeAxesAsync(builder, chart); + } + + // Serializes pie of pie or pie of bar chart. + Future _serializeOfPieChartAsync( + XmlBuilder builder, Chart chart) async { + late int gapwidth; + late int pieSecondSize; + final ExcelChartType type = chart.series[0]._serieType; + late String isPieOrBar; + if (type == ExcelChartType.pieOfPie) { + isPieOrBar = 'pie'; + } else if (type == ExcelChartType.pieBar) { + isPieOrBar = 'bar'; + } + builder.element('c:ofPieChart', nest: () async { + builder.element('c:ofPieType', nest: () async { + builder.attribute('val', isPieOrBar); + }); + builder.element('c:varyColors', nest: () async { + builder.attribute('val', 1); + }); + + for (int i = 0; i < chart.series.count; i++) { + final ChartSerie firstSerie = chart.series[i]; + _serializeSerieAsync(builder, firstSerie); + pieSecondSize = firstSerie.serieFormat.commonSerieOptions.pieSecondSize; + gapwidth = firstSerie.serieFormat.commonSerieOptions.gapWidth; + } + + builder.element('c:gapWidth', nest: () async { + builder.attribute('val', gapwidth); + }); + builder.element('c:secondPieSize', nest: () async { + builder.attribute('val', pieSecondSize); + }); + builder.element('c:serLines', nest: () async { + builder.element('c:spPr', nest: () async { + builder.element('a:ln', nest: () async {}); + }); + }); + }); + } + + ///Serialize pie 3D chart. + Future _serializeOfPie3DChartAsync( + XmlBuilder builder, Chart chart) async { + builder.element('c:pie3DChart', nest: () async { + builder.element('c:varyColors', nest: () async { + builder.attribute('val', 1); + }); + for (int i = 0; i < chart.series.count; i++) { + final ChartSerie firstSerie = chart.series[i]; + _serializeSerieAsync(builder, firstSerie); + } + }); + } + + /// Serializes stock chart. + Future _serializeStockChartAsync( + XmlBuilder builder, Chart chart) async { + final ExcelChartType type = chart.series.innerList[0]._serieType; + if (type == ExcelChartType.stockVolumeOpenHighLowClose || + type == ExcelChartType.stockVolumeHighLowClose) { + builder.element('c:barChart', nest: () async { + builder.element('c:barDir', nest: () async { + builder.attribute('val', 'col'); + }); + builder.element('c:grouping', nest: () async { + builder.attribute('val', 'clustered'); + }); + + builder.element('c:varyColors', nest: () async { + builder.attribute('val', 0); + }); + + final ChartSerie firstSerie = chart.series[0]; + _serializeSerieAsync(builder, firstSerie); + + builder.element('c:gapWidth', nest: () async { + builder.attribute( + 'val', firstSerie.serieFormat.commonSerieOptions.gapWidth); + }); + builder.element('c:axId', nest: () async { + builder.attribute('val', 59983360); + }); + builder.element('c:axId', nest: () async { + builder.attribute('val', 57253888); + }); + }); + } + builder.element('c:stockChart', nest: () async { + if (type == ExcelChartType.stockHighLowClose || + type == ExcelChartType.stockOpenHighLowClose) { + for (int i = 0; i < chart.series.count; i++) { + final ChartSerie firstSerie = chart.series[i]; + _serializeSerieAsync(builder, firstSerie); + } + } else if (type == ExcelChartType.stockVolumeHighLowClose || + type == ExcelChartType.stockVolumeOpenHighLowClose) { + for (int i = 0; i < chart.series.count; i++) { + if ((type == ExcelChartType.stockVolumeOpenHighLowClose || + type == ExcelChartType.stockVolumeHighLowClose) && + chart.series.innerList[0] != chart.series.innerList[i]) { + final ChartSerie firstSerie = chart.series[i]; + _serializeSerieAsync(builder, firstSerie); + } + } + } + + builder.element('c:hiLowLines', nest: () async { + builder.element('c:spPr', nest: () async { + builder.element('a:ln', nest: () async { + builder.element('a:solidFill', nest: () async { + builder.element('a:srgbClr', nest: () async { + builder.attribute('val', '000000'); + }); + }); + builder.element('a:prstDash', nest: () async { + builder.attribute('val', 'solid'); + }); + }); + }); + }); + if (type == ExcelChartType.stockOpenHighLowClose || + type == ExcelChartType.stockVolumeOpenHighLowClose) { + builder.element('c:upDownBars', nest: () async { + builder.element('c:gapWidth', nest: () async { + builder.attribute('val', '100'); + }); + builder.element('c:upBars', nest: () async { + builder.element('c:spPr', nest: () async { + builder.element('a:solidFill', nest: () async { + builder.element('a:srgbClr', nest: () async { + builder.attribute('val', 'FFFFFF'); + }); + }); + }); + }); + builder.element('c:downBars', nest: () async { + builder.element('c:spPr', nest: () async { + builder.element('a:solidFill', nest: () async { + builder.element('a:srgbClr', nest: () async { + builder.attribute('val', '000000'); + }); + }); + }); + }); + }); + } + if (chart.series[0].serieFormat.markerStyle != + ExcelChartMarkerType.none) { + builder.element('c:marker', nest: () async { + builder.attribute('val', 1); + }); + } + + builder.element('c:axId', nest: () async { + builder.attribute('val', 62908672); + }); + builder.element('c:axId', nest: () async { + builder.attribute('val', 61870848); + }); + }); + if (type == ExcelChartType.stockVolumeOpenHighLowClose || + type == ExcelChartType.stockVolumeHighLowClose) { + _serializeAxesforStockChartAsync( + builder, chart, 59983360, 57253888, true); + _serializeAxesforStockChartAsync( + builder, chart, 62908672, 61870848, false); + } else { + _serializeAxesforStockChartAsync( + builder, chart, 62908672, 61870848, true); + } + } + + ///serialize stock axes + Future _serializeAxesforStockChartAsync(XmlBuilder builder, Chart chart, + int axisId, int crossAx, bool isBar) async { + if (chart._isCategoryAxisAvail) { + _serializeCategoryAxisForStockAsync( + builder, chart.primaryCategoryAxis, axisId, crossAx, isBar); + } + if (chart._isValueAxisAvail) { + _serializeValueAxisForStockchartAsync( + builder, chart.primaryCategoryAxis, axisId, crossAx, isBar); + } + } + + ///Serialize catogory axis for stock chart + Future _serializeCategoryAxisForStockAsync(XmlBuilder builder, + ChartCategoryAxis axis, int axisId, int crossAx, bool isBar) async { + builder.element('c:catAx', nest: () async { + builder.element('c:axId', nest: () async { + builder.attribute('val', axisId); + }); + builder.element('c:scaling', nest: () async { + builder.element('c:orientation', nest: () async { + builder.attribute('val', 'minMax'); + }); + }); + int delete = 0; + String axpos = 'b'; + if (!isBar) { + delete = 1; + axpos = 't'; + } + builder.element('c:delete', nest: () async { + builder.attribute('val', delete); + }); + builder.element('c:axPos', nest: () async { + builder.attribute('val', axpos); + }); + if (axis._hasAxisTitle) { + _serializeChartTextAreaAsync(builder, axis.titleArea); + } + if (axis.numberFormat != '' && axis.numberFormat != 'General') { + builder.element('c:numFmt', nest: () async { + builder.attribute('formatCode', axis.numberFormat); + builder.attribute('sourceLinked', '0'); + }); + } + builder.element('c:majorTickMark', nest: () async { + builder.attribute('val', 'out'); + }); + builder.element('c:minorTickMark', nest: () async { + builder.attribute('val', 'none'); + }); + builder.element('c:tickLblPos', nest: () async { + builder.attribute('val', 'nextTo'); + }); + builder.element('c:spPr', nest: () async { + builder.element('a:ln', nest: () async { + if (!isBar) { + builder.attribute('w', 12700); + } + }); + }); + builder.element('c:crossAx', nest: () async { + builder.attribute('val', crossAx); + }); + builder.element('c:crosses', nest: () async { + builder.attribute('val', 'autoZero'); + }); + builder.element('c:auto', nest: () async { + builder.attribute('val', 1); + }); + builder.element('c:lblAlgn', nest: () async { + builder.attribute('val', 'ctr'); + }); + builder.element('c:lblOffset', nest: () async { + builder.attribute('val', 100); + }); + builder.element('c:noMultiLvlLbl', nest: () async { + builder.attribute('val', 0); + }); + builder.element('c:tickMarkSkip', nest: () async { + builder.attribute('val', 1); + }); + }); + } + + ///Serialize value axis for stock chart + Future _serializeValueAxisForStockchartAsync(XmlBuilder builder, + ChartCategoryAxis axis, int axisId, int crossAx, bool isBar) async { + builder.element('c:valAx', nest: () async { + builder.element('c:axId', nest: () async { + builder.attribute('val', crossAx); + }); + builder.element('c:scaling', nest: () async { + builder.element('c:orientation', nest: () async { + builder.attribute('val', 'minMax'); + }); + if (!axis._isAutoMax) { + builder.element('c:max', nest: () async { + builder.attribute('val', axis.maximumValue); + }); + } + if (axis._isAutoMin) { + builder.element('c:min', nest: () async { + builder.attribute('val', axis.minimumValue); + }); + } + }); + builder.element('c:delete', nest: () async { + builder.attribute('val', '0'); + }); + String axpos = 'l'; + + if (!isBar) { + axpos = 'r'; + } + + builder.element('c:axPos', nest: () async { + builder.attribute('val', axpos); + }); + + if (axpos == 'l') { + builder.element('c:majorGridlines', nest: () async {}); + } + + if (axis._hasAxisTitle) { + _serializeChartTextAreaAsync(builder, axis.titleArea); + } + if (axis.numberFormat != '' && axis.numberFormat != 'General') { + builder.element('c:numFmt', nest: () async { + builder.attribute('formatCode', axis.numberFormat); + builder.attribute('sourceLinked', '0'); + }); + } + if (axis.hasMajorGridLines) { + builder.element('c:majorGridlines', nest: () async {}); + } + builder.element('c:majorTickMark', nest: () async { + builder.attribute('val', 'out'); + }); + builder.element('c:minorTickMark', nest: () async { + builder.attribute('val', 'none'); + }); + builder.element('c:tickLblPos', nest: () async { + builder.attribute('val', 'nextTo'); + }); + builder.element('c:spPr', nest: () async { + builder.element('a:ln', nest: () async {}); + }); + builder.element('c:crossAx', nest: () async { + builder.attribute('val', axisId); + }); + String crosses = 'autoZero'; + if (!isBar) { + crosses = 'max'; + } + builder.element('c:crosses', nest: () async { + builder.attribute('val', crosses); + }); + final Chart chart = axis._parentChart; + final String strCrossBetween = + chart.primaryCategoryAxis._isBetween ? 'between' : 'midCat'; + builder.element('c:crossBetween', nest: () async { + builder.attribute('val', strCrossBetween); + }); + }); + } + + ///Serialize doughnut/doughnut_exploded charts + Future _serializedoughnutchartAsync( + XmlBuilder builder, Chart chart) async { + late int doughnutHoleSize; + late int firstSliceAngle; + + builder.element('c:doughnutChart', nest: () async { + builder.element('c:varyColors', nest: () async { + builder.attribute('val', 1); + }); + + for (int i = 0; i < chart.series.count; i++) { + final ChartSerie firstSerie = chart.series[i]; + _serializeSerieAsync(builder, firstSerie); + firstSliceAngle = + firstSerie.serieFormat.commonSerieOptions.firstSliceAngle; + doughnutHoleSize = + firstSerie.serieFormat.commonSerieOptions.holeSizePercent; + } + builder.element('c:firstSliceAng', nest: () async { + builder.attribute('val', firstSliceAngle); + }); + builder.element('c:holeSize', nest: () async { + builder.attribute('val', doughnutHoleSize); + }); + }); + } + + ///Serialize marker for stock and line charts + Future _serializeMarkerAsync( + XmlBuilder builder, ChartSerie firstSerie) async { + final ExcelChartType type = firstSerie._serieType; + + if ((firstSerie.serieFormat.markerStyle == ExcelChartMarkerType.none) && + (type == ExcelChartType.line || + type == ExcelChartType.lineStacked || + type == ExcelChartType.lineStacked100 || + type == ExcelChartType.stockHighLowClose || + type == ExcelChartType.stockVolumeOpenHighLowClose || + type == ExcelChartType.stockVolumeHighLowClose || + type == ExcelChartType.stockOpenHighLowClose)) { + builder.element('c:marker', nest: () async { + builder.element('c:symbol', nest: () async { + builder.attribute('val', 'none'); + }); + }); + } else if ((firstSerie.serieFormat.markerStyle == + ExcelChartMarkerType.none) && + (type == ExcelChartType.lineMarkers || + type == ExcelChartType.lineMarkersStacked || + type == ExcelChartType.lineMarkersStacked100)) { + builder.element('c:marker', nest: () async { + builder.element('c:symbol', nest: () async { + builder.attribute('val', 'circle'); + }); + builder.element('c:size', nest: () async { + builder.attribute('val', '5'); + }); + }); + } else if (firstSerie.serieFormat.markerStyle != + ExcelChartMarkerType.none) { + final String markerBackgroundColor = + firstSerie.serieFormat.markerBackgroundColor.replaceAll('#', ''); + final String markerBorderColor = + firstSerie.serieFormat.markerBorderColor.replaceAll('#', ''); + late String exclMarkertype; + if (firstSerie.serieFormat.markerStyle == ExcelChartMarkerType.diamond) { + exclMarkertype = 'diamond'; + } else if (firstSerie.serieFormat.markerStyle == + ExcelChartMarkerType.circle) { + exclMarkertype = 'circle'; + } else if (firstSerie.serieFormat.markerStyle == + ExcelChartMarkerType.xSquare) { + exclMarkertype = 'x'; + } else if (firstSerie.serieFormat.markerStyle == + ExcelChartMarkerType.dowJones) { + exclMarkertype = 'dot'; + } else if (firstSerie.serieFormat.markerStyle == + ExcelChartMarkerType.square) { + exclMarkertype = 'square'; + } else if (firstSerie.serieFormat.markerStyle == + ExcelChartMarkerType.plusSign) { + exclMarkertype = 'plus'; + } else if (firstSerie.serieFormat.markerStyle == + ExcelChartMarkerType.standardDeviation) { + exclMarkertype = 'triangle'; + } else if (firstSerie.serieFormat.markerStyle == + ExcelChartMarkerType.starSquare) { + exclMarkertype = 'star'; + } + + builder.element('c:marker', nest: () async { + builder.element('c:symbol', nest: () async { + builder.attribute('val', exclMarkertype); + }); + builder.element('c:size', nest: () async { + builder.attribute('val', '5'); + }); + builder.element('c:spPr', nest: () async { + builder.element('a:solidFill', nest: () async { + builder.element('a:srgbClr', nest: () async { + builder.attribute('val', markerBackgroundColor); + }); + }); + builder.element('a:ln', nest: () async { + builder.element('a:solidFill', nest: () async { + builder.element('a:srgbClr', nest: () async { + builder.attribute('val', markerBorderColor); + }); + }); + }); + }); + }); + } + } } diff --git a/packages/syncfusion_officechart/pubspec.yaml b/packages/syncfusion_officechart/pubspec.yaml index 874681204..3a5ed0324 100644 --- a/packages/syncfusion_officechart/pubspec.yaml +++ b/packages/syncfusion_officechart/pubspec.yaml @@ -1,6 +1,6 @@ name: syncfusion_officechart description: Syncfusion Flutter Office Chart is a library written natively in Dart for creating Office charts from scratch. -version: 20.4.38-beta +version: 21.1.35-beta homepage: https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_officechart environment: @@ -11,7 +11,7 @@ dependencies: sdk: flutter xml: ">=5.1.0 <7.0.0" archive: ">=3.1.2 <4.0.0" - syncfusion_flutter_xlsio: ^20.4.38-beta + syncfusion_flutter_xlsio: ^21.1.35 dev_dependencies: flutter_test: diff --git a/packages/syncfusion_officecore/example/pubspec.yaml b/packages/syncfusion_officecore/example/pubspec.yaml index 07d620244..4fa5be1ea 100644 --- a/packages/syncfusion_officecore/example/pubspec.yaml +++ b/packages/syncfusion_officecore/example/pubspec.yaml @@ -9,12 +9,7 @@ dependencies: sdk: flutter path_provider: ^2.0.1 open_file: ^3.0.1 - syncfusion_flutter_xlsio: - git: - url: https://SyncfusionBuild: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@github.com/essential-studio/flutter-xlsio - path: flutter_xlsio/syncfusion_flutter_xlsio - branch: development - ref: development + syncfusion_flutter_xlsio: ^21.1.35 # The following section is specific to Flutter. diff --git a/packages/syncfusion_officecore/pubspec.yaml b/packages/syncfusion_officecore/pubspec.yaml index 1f7bb5819..c4d999eba 100644 --- a/packages/syncfusion_officecore/pubspec.yaml +++ b/packages/syncfusion_officecore/pubspec.yaml @@ -1,6 +1,6 @@ name: syncfusion_officecore description: Syncfusion Flutter Office Core is a dependant library for Syncfusion Flutter XlsIO, written natively in Dart for creating Excel from scratch. -version: 20.4.38-beta +version: 21.1.35-beta homepage: https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_officecore environment: @@ -9,7 +9,7 @@ environment: dependencies: flutter: sdk: flutter - syncfusion_flutter_core: ^20.4.38 + syncfusion_flutter_core: ^21.1.35 dev_dependencies: flutter_test: diff --git a/packages/syncfusion_pdfviewer_macos/example/pubspec.yaml b/packages/syncfusion_pdfviewer_macos/example/pubspec.yaml index de9729fb7..2d28dec12 100644 --- a/packages/syncfusion_pdfviewer_macos/example/pubspec.yaml +++ b/packages/syncfusion_pdfviewer_macos/example/pubspec.yaml @@ -12,12 +12,7 @@ dependencies: flutter: sdk: flutter - syncfusion_flutter_pdfviewer: - git: - url: https://SyncfusionBuild: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@github.com/essential-studio/flutter-pdfviewer - path: flutter_pdfviewer/syncfusion_flutter_pdfviewer - branch: release/20.4.0.1 - ref: release/20.4.0.1 + syncfusion_flutter_pdfviewer: ^21.1.35 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. diff --git a/packages/syncfusion_pdfviewer_macos/pubspec.yaml b/packages/syncfusion_pdfviewer_macos/pubspec.yaml index 75bb87b03..348f12197 100644 --- a/packages/syncfusion_pdfviewer_macos/pubspec.yaml +++ b/packages/syncfusion_pdfviewer_macos/pubspec.yaml @@ -1,6 +1,6 @@ name: syncfusion_pdfviewer_macos description: macOS platform implementation of the Flutter PDF Viewer library that lets you view the PDF documents seamlessly and efficiently. -version: 20.4.38 +version: 21.1.35 homepage: https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_pdfviewer_macos environment: @@ -11,7 +11,7 @@ dependencies: flutter: sdk: flutter - syncfusion_pdfviewer_platform_interface: ^20.4.38 + syncfusion_pdfviewer_platform_interface: ^21.1.35 dev_dependencies: flutter_test: diff --git a/packages/syncfusion_pdfviewer_platform_interface/example/pubspec.yaml b/packages/syncfusion_pdfviewer_platform_interface/example/pubspec.yaml index d2b0382a2..d9aa8c27c 100644 --- a/packages/syncfusion_pdfviewer_platform_interface/example/pubspec.yaml +++ b/packages/syncfusion_pdfviewer_platform_interface/example/pubspec.yaml @@ -18,12 +18,7 @@ dependencies: flutter: sdk: flutter - syncfusion_flutter_pdfviewer: - git: - url: https://SyncfusionBuild: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@github.com/essential-studio/flutter-pdfviewer - path: flutter_pdfviewer/syncfusion_flutter_pdfviewer - branch: release/20.4.0.1 - ref: release/20.4.0.1 + syncfusion_flutter_pdfviewer: ^21.1.35 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. diff --git a/packages/syncfusion_pdfviewer_platform_interface/pubspec.yaml b/packages/syncfusion_pdfviewer_platform_interface/pubspec.yaml index 6a7cd6d9c..36253e8a6 100644 --- a/packages/syncfusion_pdfviewer_platform_interface/pubspec.yaml +++ b/packages/syncfusion_pdfviewer_platform_interface/pubspec.yaml @@ -1,6 +1,6 @@ name: syncfusion_pdfviewer_platform_interface description: A common platform interface for the Flutter PDF Viewer library that lets you view the PDF documents seamlessly and efficiently. -version: 20.4.38 +version: 21.1.35 homepage: https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_pdfviewer_platform_interface environment: diff --git a/packages/syncfusion_pdfviewer_web/example/pubspec.yaml b/packages/syncfusion_pdfviewer_web/example/pubspec.yaml index 9a35c45f0..46d9fa9ce 100644 --- a/packages/syncfusion_pdfviewer_web/example/pubspec.yaml +++ b/packages/syncfusion_pdfviewer_web/example/pubspec.yaml @@ -11,12 +11,7 @@ environment: dependencies: flutter: sdk: flutter - syncfusion_flutter_pdfviewer: - git: - url: https://SyncfusionBuild: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@github.com/essential-studio/flutter-pdfviewer - path: flutter_pdfviewer/syncfusion_flutter_pdfviewer - branch: release/20.4.0.1 - ref: release/20.4.0.1 + syncfusion_flutter_pdfviewer: ^21.1.35 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. diff --git a/packages/syncfusion_pdfviewer_web/pubspec.yaml b/packages/syncfusion_pdfviewer_web/pubspec.yaml index ecc289183..faaf74691 100644 --- a/packages/syncfusion_pdfviewer_web/pubspec.yaml +++ b/packages/syncfusion_pdfviewer_web/pubspec.yaml @@ -1,6 +1,6 @@ name: syncfusion_pdfviewer_web description: Web platform implementation of the Flutter PDF Viewer library that lets you view the PDF documents seamlessly and efficiently. -version: 20.4.38 +version: 21.1.35 homepage: https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_pdfviewer_web environment: @@ -14,7 +14,7 @@ dependencies: sdk: flutter js: ^0.6.3 meta: ^1.3.0 - syncfusion_pdfviewer_platform_interface: ^20.4.38 + syncfusion_pdfviewer_platform_interface: ^21.1.35 dev_dependencies: flutter_test: diff --git a/packages/syncfusion_pdfviewer_windows/pubspec.yaml b/packages/syncfusion_pdfviewer_windows/pubspec.yaml index 91e8a37f9..f4cad5f3a 100644 --- a/packages/syncfusion_pdfviewer_windows/pubspec.yaml +++ b/packages/syncfusion_pdfviewer_windows/pubspec.yaml @@ -1,6 +1,6 @@ name: syncfusion_pdfviewer_windows description: Windows platform implementation of the Flutter PDF Viewer library that lets you view the PDF documents seamlessly and efficiently. -version: 20.4.38 +version: 21.1.35 homepage: https://github.com/syncfusion/flutter-widgets/tree/master/packages/syncfusion_pdfviewer_windows environment: