getBreakpointSegments function

List<ResponsiveBreakpointSegment> getBreakpointSegments(
  1. List<ResponsiveBreakpoint> breakpoints,
  2. ResponsiveBreakpoint defaultBreakpoint
)

Calculate breakpoint segments algorithm.

Complex logic to compute the breakpoint segments efficiently.

Implementation

List<ResponsiveBreakpointSegment> getBreakpointSegments(
    List<ResponsiveBreakpoint> breakpoints,
    ResponsiveBreakpoint defaultBreakpoint) {
  // Mutable breakpoints holder.
  List<ResponsiveBreakpoint> breakpointsHolder = [];
  breakpointsHolder.addAll(breakpoints);
  // Breakpoint segments holder.
  List<ResponsiveBreakpointSegment> breakpointSegments = [];
  // No breakpoints. Create segment from default breakpoint behavior.
  if (breakpointsHolder.isEmpty) {
    breakpointSegments.add(ResponsiveBreakpointSegment(
        breakpoint: 0,
        segmentType: defaultBreakpoint.behavior,
        responsiveBreakpoint: defaultBreakpoint));
    return breakpointSegments;
  }

  List<ResponsiveBreakpoint> breakpointTags =
      breakpointsHolder.where((element) => element.isTag).toList();
  breakpointsHolder =
      breakpointsHolder.where((element) => !element.isTag).toList();

  // Min Width is passed through the default breakpoint.
  double minWidth = defaultBreakpoint.breakpoint;

  // Breakpoint overrides MinWidth default.
  ResponsiveBreakpoint? minWidthOverride = breakpointsHolder
      .firstWhereOrNull((element) => element.breakpoint == minWidth);
  if (minWidthOverride != null) {
    breakpointsHolder.insert(
        0,
        ResponsiveBreakpoint(
                breakpoint: minWidth,
                name: defaultBreakpoint.name,
                behavior: defaultBreakpoint.behavior,
                scaleFactor: defaultBreakpoint.scaleFactor)
            .merge(minWidthOverride));
  } else {
    // Add minWidth breakpoint. MinWidth generates
    // a breakpoint segment and needs to be added.
    breakpointsHolder.insert(
        0,
        ResponsiveBreakpoint(
            breakpoint: minWidth,
            name: defaultBreakpoint.name,
            behavior: defaultBreakpoint.behavior,
            scaleFactor: defaultBreakpoint.scaleFactor));
  }

  // Order breakpoints from smallest to largest.
  // Perform ordering operation to allow breakpoints
  // to be accepted in any order.
  breakpointsHolder.sort(ResponsiveUtils.breakpointComparator);
  breakpointTags.sort(ResponsiveUtils.breakpointComparator);

  // Find the first breakpoint behavior.
  ResponsiveBreakpoint initialBreakpoint = breakpointsHolder.first;

  // Breakpoint variable that holds the active behavior.
  // Used to calculate and remember the breakpoint behavior.
  ResponsiveBreakpoint breakpointHolder = ResponsiveBreakpoint(
      breakpoint: initialBreakpoint.breakpoint,
      name: defaultBreakpoint.name,
      behavior: defaultBreakpoint.behavior,
      scaleFactor: defaultBreakpoint.scaleFactor);

  // Construct initial segment that starts from 0.
  // Initial segment inherits default behavior.
  breakpointSegments.insert(
      0,
      ResponsiveBreakpointSegment(
          breakpoint: 0,
          segmentType: defaultBreakpoint.behavior,
          // Convert initial AutoScaleDown behavior to AutoScale.
          responsiveBreakpoint: breakpointHolder.copyWith(
              behavior: (initialBreakpoint.isAutoScaleDown)
                  ? ResponsiveBreakpointBehavior.AUTOSCALE
                  : defaultBreakpoint.behavior)));

  // A counter to keep track of the actual number of added breakpoints.
  // Needed because breakpoints are merged so not every
  // for-loop iteration adds a breakpoint segment.
  int breakpointCounter = 0;
  // Calculate segments from breakpoints.
  for (int i = 0; i < breakpointsHolder.length; i++) {
    // Convenience variable.
    ResponsiveBreakpoint breakpoint = breakpointsHolder[i];
    // Segment calculation holder.
    ResponsiveBreakpointSegment? breakpointSegmentHolder;
    switch (breakpoint.behavior) {
      case ResponsiveBreakpointBehavior.RESIZE:
      case ResponsiveBreakpointBehavior.AUTOSCALE:
        breakpointSegmentHolder = ResponsiveBreakpointSegment(
            breakpoint: breakpoint.breakpoint,
            segmentType: breakpoint.behavior,
            responsiveBreakpoint: breakpoint);
        break;
      case ResponsiveBreakpointBehavior.AUTOSCALEDOWN:
        // Construct override breakpoint segment.
        // AutoScaleDown needs to override the breakpoint
        // interval because responsive calculations are
        // performed from 0 - ∞.
        int overrideBreakpointIndex = breakpointCounter;
        ResponsiveBreakpointSegment overrideBreakpointSegment =
            breakpointSegments[overrideBreakpointIndex];
        overrideBreakpointSegment = overrideBreakpointSegment.copyWith(
            responsiveBreakpoint: overrideBreakpointSegment.responsiveBreakpoint
                .copyWith(
                    breakpoint: breakpoint.breakpoint,
                    behavior: ResponsiveBreakpointBehavior.AUTOSCALE));
        breakpointSegments[overrideBreakpointIndex] = overrideBreakpointSegment;

        // AutoScaleDown acts as an AutoScale breakpoint
        // in the 0 - ∞ direction.
        breakpointSegmentHolder = ResponsiveBreakpointSegment(
            breakpoint: breakpoint.breakpoint,
            segmentType: breakpoint.behavior,
            responsiveBreakpoint: ResponsiveBreakpoint(
                breakpoint: breakpoint.breakpoint,
                name: breakpoint.name,
                behavior: ResponsiveBreakpointBehavior.AUTOSCALE,
                scaleFactor: breakpoint.scaleFactor));
        break;
      case ResponsiveBreakpointBehavior.TAG:
        break;
    }
    // Update holder with active breakpoint
    breakpointHolder = breakpoint;
    // Merge duplicate segments.
    // Compare current segment to previous segment.
    if (breakpointSegments.last.breakpoint ==
        breakpointSegmentHolder!.breakpoint) {
      breakpointSegments[breakpointSegments.length - 1] =
          breakpointSegments.last.merge(breakpointSegmentHolder);
      continue;
    }
    breakpointSegments.add(breakpointSegmentHolder);
    breakpointCounter += 1;
  }

  // Add tags to segments.
  for (int i = 0; i < breakpointTags.length; i++) {
    // Convenience variable.
    ResponsiveBreakpoint breakpointTag = breakpointTags[i];
    ResponsiveBreakpointSegment breakpointSegmentHolder =
        breakpointSegments.reversed.firstWhere(
            (element) => element.breakpoint <= breakpointTag.breakpoint);
    int breakpointHolderIndex =
        breakpointSegments.indexOf(breakpointSegmentHolder);
    if (breakpointSegmentHolder.breakpoint == breakpointTag.breakpoint) {
      breakpointSegments[breakpointHolderIndex] = ResponsiveBreakpointSegment(
        breakpoint: breakpointSegmentHolder.breakpoint,
        segmentType: breakpointSegmentHolder.segmentType,
        responsiveBreakpoint:
            breakpointSegmentHolder.responsiveBreakpoint.merge(breakpointTag),
      );
    } else {
      breakpointSegments.insert(
        breakpointHolderIndex + 1,
        ResponsiveBreakpointSegment(
            breakpoint: breakpointTag.breakpoint,
            segmentType: breakpointTag.behavior,
            responsiveBreakpoint: breakpointSegmentHolder.responsiveBreakpoint
                .merge(breakpointTag)),
      );
    }
  }

  return breakpointSegments;
}