Flutter 2:如何创建渐变范围滑块

问题描述 投票:0回答:2

我正在尝试创建一个带有渐变主体的范围滑块。

我的代码基于此响应中的渐变滑块代码:带有渐变的颤动滑块

我根据对象的界面调整了绘制覆盖。

所以情况是这样的:

错误

======== Exception caught by rendering library =====================================================
The following _CastError was thrown during paint():
Null check operator used on a null value

The relevant error-causing widget was: 
  RangeSlider RangeSlider:file:///[PATH]flutter/lib/source/shared_components/common/slider_range/slider_range.item.dart:19:14
When the exception was thrown, this was the stack: 
#0      BaseSliderTrackShape.getPreferredRect (package:flutter/src/material/slider_theme.dart:1484:53)
#1      _RenderRangeSlider.paint (package:flutter/src/material/range_slider.dart:1267:58)
#2      RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#3      PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#4      RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:140:15)
#5      PaintingContext.pushLayer (package:flutter/src/rendering/object.dart:387:12)
#6      RenderLeaderLayer.paint (package:flutter/src/rendering/proxy_box.dart:5138:13)
#7      RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#8      PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#9      RenderBoxContainerDefaultsMixin.defaultPaint (package:flutter/src/rendering/box.dart:2844:15)
#10     RenderFlex.paint (package:flutter/src/rendering/flex.dart:1078:7)
#11     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#12     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#13     RenderShiftedBox.paint (package:flutter/src/rendering/shifted_box.dart:79:15)
#14     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#15     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#16     RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:140:15)
#17     RenderDecoratedBox.paint (package:flutter/src/rendering/proxy_box.dart:2169:11)
#18     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#19     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#20     RenderShiftedBox.paint (package:flutter/src/rendering/shifted_box.dart:79:15)
#21     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#22     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#23     RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:140:15)
#24     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#25     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#26     RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:140:15)
#27     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#28     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#29     RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:140:15)
#30     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#31     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#32     RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:140:15)
#33     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#34     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#35     RenderBoxContainerDefaultsMixin.defaultPaint (package:flutter/src/rendering/box.dart:2844:15)
#36     RenderFlex.paint (package:flutter/src/rendering/flex.dart:1078:7)
#37     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#38     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#39     RenderShiftedBox.paint (package:flutter/src/rendering/shifted_box.dart:79:15)
#40     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#41     PaintingContext.paintChild (package:flutter/src/rendering/object.dart:187:13)
#42     _RenderSingleChildViewport.paint.paintContents (package:flutter/src/widgets/single_child_scroll_view.dart:542:17)
#43     _RenderSingleChildViewport.paint (package:flutter/src/widgets/single_child_scroll_view.dart:556:9)
#44     RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2451:7)
#45     PaintingContext._repaintCompositedChild (package:flutter/src/rendering/object.dart:141:11)
#46     PaintingContext.repaintCompositedChild (package:flutter/src/rendering/object.dart:100:5)
#47     PipelineOwner.flushPaint (package:flutter/src/rendering/object.dart:995:29)
#48     RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:499:19)
#49     WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:883:13)
#50     RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:363:5)
#51     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1144:15)
#52     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1081:9)
#53     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:995:5)
#57     _invoke (dart:ui/hooks.dart:151:10)
#58     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:308:5)
#59     _drawFrame (dart:ui/hooks.dart:115:31)
(elided 3 frames from dart:async)
The following RenderObject was being processed when the exception was fired: _RenderRangeSlider#59316
...  parentData: <none> (can use size)
...  constraints: BoxConstraints(0.0<=w<=367.4, 0.0<=h<=Infinity)
...  semantic boundary
...  size: Size(367.4, 48.0)
RenderObject: _RenderRangeSlider#59316
  parentData: <none> (can use size)
  constraints: BoxConstraints(0.0<=w<=367.4, 0.0<=h<=Infinity)
  semantic boundary
  size: Size(367.4, 48.0)
====================================================================================================

追踪

import 'package:flutter/material.dart';

class GradientRectRangeSliderTrackShape extends RangeSliderTrackShape
    with BaseSliderTrackShape {
  const GradientRectRangeSliderTrackShape({
    this.gradient = const LinearGradient(
      colors: [
        Colors.red,
        Colors.yellow,
      ],
    ),
    this.darkenInactive = true,
  });

  final LinearGradient gradient;
  final bool darkenInactive;

  @override
  void paint(
      PaintingContext context,
      Offset offset,
      {
        required RenderBox parentBox,
        required SliderThemeData sliderTheme,
        required Animation<double> enableAnimation,
        required TextDirection textDirection,
        required Offset startThumbCenter,
        required Offset endThumbCenter,
        bool isDiscrete = false,
        bool isEnabled = false,
        double additionalActiveTrackHeight = 2,
      }
    ) {
    assert(
      sliderTheme.disabledActiveTrackColor != null,
      'sliderTheme.disabledActiveTrackColor is required'
    );
    assert(
      sliderTheme.disabledInactiveTrackColor != null,
      'sliderTheme.disabledInactiveTrackColor is required'
    );
    assert(
      sliderTheme.activeTrackColor != null,
      'sliderTheme.activeTrackColor is required'
    );
    assert(
      sliderTheme.inactiveTrackColor != null,
      'sliderTheme.inactiveTrackColor'
    );
    assert(
      sliderTheme.thumbShape != null,
      'sliderTheme.thumbShape is required'
    );
    assert(
      sliderTheme.trackHeight != null && sliderTheme.trackHeight! > 0,
      'sliderTheme.trackHeight != null and sliderTheme.trackHeight! > 0'
      'are required'
    );

    final Rect trackRect = getPreferredRect(
      parentBox: parentBox,
      offset: offset,
      sliderTheme: sliderTheme,
      isEnabled: isEnabled,
      isDiscrete: isDiscrete,
    );

    final activeGradientRect = Rect.fromLTRB(
      startThumbCenter.dx,
      (textDirection == TextDirection.ltr)
          ? trackRect.top - (additionalActiveTrackHeight / 2)
          : trackRect.top,
      endThumbCenter.dx,
      (textDirection == TextDirection.ltr)
          ? trackRect.bottom + (additionalActiveTrackHeight / 2)
          : trackRect.bottom,
    );

    // Assign the track segment paints, which are leading: active and
    // trailing: inactive.
    final ColorTween activeTrackColorTween = ColorTween(
        begin: sliderTheme.disabledActiveTrackColor,
        end: sliderTheme.activeTrackColor);
    final ColorTween inactiveTrackColorTween = darkenInactive
        ? ColorTween(
        begin: sliderTheme.disabledInactiveTrackColor,
        end: sliderTheme.inactiveTrackColor
    )
        : activeTrackColorTween;
    final Paint activePaint = Paint()
      ..shader = gradient.createShader(activeGradientRect)
      ..color = activeTrackColorTween.evaluate(enableAnimation)!;
    final Paint inactivePaint = Paint()
      ..color = inactiveTrackColorTween.evaluate(enableAnimation)!;
    final Paint leftTrackPaint;
    final Paint rightTrackPaint;
    switch (textDirection) {
      case TextDirection.ltr:
        leftTrackPaint = activePaint;
        rightTrackPaint = inactivePaint;
        break;
      case TextDirection.rtl:
        leftTrackPaint = inactivePaint;
        rightTrackPaint = activePaint;
        break;
    }

    final Radius trackRadius = Radius.circular(trackRect.height / 2);
    final Radius activeTrackRadius = Radius.circular(trackRect.height / 2 + 1);

    context.canvas.drawRRect(
      RRect.fromLTRBAndCorners(
        startThumbCenter.dx,
        (textDirection == TextDirection.ltr)
            ? trackRect.top - (additionalActiveTrackHeight / 2)
            : trackRect.top,
        endThumbCenter.dx,
        (textDirection == TextDirection.ltr)
            ? trackRect.bottom + (additionalActiveTrackHeight / 2)
            : trackRect.bottom,
        topLeft: (textDirection == TextDirection.ltr)
            ? activeTrackRadius
            : trackRadius,
        bottomLeft: (textDirection == TextDirection.ltr)
            ? activeTrackRadius
            : trackRadius,
      ),
      leftTrackPaint,
    );
    context.canvas.drawRRect(
      RRect.fromLTRBAndCorners(
        startThumbCenter.dx,
        (textDirection == TextDirection.rtl)
            ? trackRect.top - (additionalActiveTrackHeight / 2)
            : trackRect.top,
        endThumbCenter.dx,
        (textDirection == TextDirection.rtl)
            ? trackRect.bottom + (additionalActiveTrackHeight / 2)
            : trackRect.bottom,
        topRight: (textDirection == TextDirection.rtl)
            ? activeTrackRadius
            : trackRadius,
        bottomRight: (textDirection == TextDirection.rtl)
            ? activeTrackRadius
            : trackRadius,
      ),
      rightTrackPaint,
    );
  }
}

小工具

SliderTheme(
  data: SliderThemeData(
    rangeTrackShape: GradientRectRangeSliderTrackShape()
  ),
  child: RangeSlider(
    onChanged: onChanged,
    values: data.values,
    min: data.min,
    max: data.max,
    divisions: data.labels.length
  )
)
flutter slider
2个回答
0
投票

这是创建渐变滑块范围的代码

Track 可以直接放在您应用程序的

Theme

import 'package:flutter/material.dart';
import 'dart:math' as math;

class GradientRectRangeSliderTrackShape extends RangeSliderTrackShape {
  const GradientRectRangeSliderTrackShape({
    this.gradient = const LinearGradient(
      colors: [
        Colors.red,
        Colors.yellow,
      ],
    ),
    this.darkenInactive = true,
  });

  final LinearGradient gradient;
  final bool darkenInactive;

  @override
  Rect getPreferredRect({
    required RenderBox parentBox,
    Offset offset = Offset.zero,
    required SliderThemeData sliderTheme,
    bool isEnabled = false,
    bool isDiscrete = false,
  }) {
    assert(
      sliderTheme.overlayShape != null,
      'sliderTheme.overlayShape is required'
    );
    assert(
      sliderTheme.trackHeight != null,
      'sliderTheme.trackHeight is required'
    );

    final double overlayWidth = sliderTheme.overlayShape!
      .getPreferredSize(isEnabled, isDiscrete).width;
    final double trackHeight = sliderTheme.trackHeight!;
    assert(overlayWidth >= 0);
    assert(trackHeight >= 0);

    final double trackLeft = offset.dx + overlayWidth / 2;
    final double trackTop = offset.dy
        + (parentBox.size.height - trackHeight) / 2;
    final double trackRight = trackLeft + parentBox.size.width - overlayWidth;
    final double trackBottom = trackTop + trackHeight;
    return Rect.fromLTRB(
      math.min(
        trackLeft,
        trackRight
      ),
      trackTop,
      math.max(
        trackLeft,
        trackRight
      ),
      trackBottom
    );
  }

  @override
  void paint(
      PaintingContext context,
      Offset offset, {
        required RenderBox parentBox,
        required SliderThemeData sliderTheme,
        required Animation<double> enableAnimation,
        required Offset startThumbCenter,
        required Offset endThumbCenter,
        bool isEnabled = false,
        bool isDiscrete = false,
        required TextDirection textDirection,
        double additionalActiveTrackHeight = 2,
      }) {
    assert(
      sliderTheme.disabledActiveTrackColor != null,
      'sliderTheme.disabledActiveTrackColor is required'
    );
    assert(
      sliderTheme.disabledInactiveTrackColor != null,
      'sliderTheme.disabledInactiveTrackColor is required'
    );
    assert(
      sliderTheme.activeTrackColor != null,
      'sliderTheme.activeTrackColor is required'
    );
    assert(
      sliderTheme.inactiveTrackColor != null,
      'sliderTheme.inactiveTrackColor is required'
    );
    assert(
      sliderTheme.rangeThumbShape != null,
      'sliderTheme.rangeThumbShape iss required'
    );
    assert(
      sliderTheme.trackHeight != null && sliderTheme.trackHeight! > 0,
      'sliderTheme.trackHeight != null and sliderTheme.trackHeight! > 0'
        'are required'
    );

    final Rect trackRect = getPreferredRect(
      parentBox: parentBox,
      offset: offset,
      sliderTheme: sliderTheme,
      isEnabled: isEnabled,
      isDiscrete: isDiscrete,
    );

    final ColorTween activeTrackColorTween = ColorTween(
      begin: sliderTheme.disabledActiveTrackColor,
      end: sliderTheme.activeTrackColor,
    );
    final ColorTween inactiveTrackColorTween = darkenInactive
        ? ColorTween(
          begin: sliderTheme.disabledInactiveTrackColor,
          end: sliderTheme.inactiveTrackColor,
        )
        : activeTrackColorTween;
    final Paint activePaint = Paint()
      ..shader = gradient.createShader(trackRect)
      ..color = activeTrackColorTween.evaluate(enableAnimation)!;
    final Paint inactivePaint = Paint()
      ..color = inactiveTrackColorTween.evaluate(enableAnimation)!;

    final Offset leftThumbOffset;
    final Offset rightThumbOffset;
    switch (textDirection) {
      case TextDirection.ltr:
        leftThumbOffset = startThumbCenter;
        rightThumbOffset = endThumbCenter;
        break;
      case TextDirection.rtl:
        leftThumbOffset = endThumbCenter;
        rightThumbOffset = startThumbCenter;
        break;
    }
    final Size thumbSize = sliderTheme.rangeThumbShape!
      .getPreferredSize(
        isEnabled,
        isDiscrete
      );
    final double thumbRadius = thumbSize.width / 2;
    assert(thumbRadius > 0);

    final Radius trackRadius = Radius.circular(trackRect.height / 2);

    context.canvas.drawRRect(
      RRect.fromLTRBAndCorners(
        trackRect.left,
        trackRect.top,
        leftThumbOffset.dx,
        trackRect.bottom,
        topLeft: trackRadius,
        bottomLeft: trackRadius,
      ),
      inactivePaint,
    );
    context.canvas.drawRect(
      Rect.fromLTRB(
        leftThumbOffset.dx,
        trackRect.top - (additionalActiveTrackHeight / 2),
        rightThumbOffset.dx,
        trackRect.bottom + (additionalActiveTrackHeight / 2),
      ),
      activePaint,
    );
    context.canvas.drawRRect(
      RRect.fromLTRBAndCorners(
        rightThumbOffset.dx,
        trackRect.top,
        trackRect.right,
        trackRect.bottom,
        topRight: trackRadius,
        bottomRight: trackRadius,
      ),
      inactivePaint,
    );
  }
}

小工具

SliderTheme(
  data: SliderThemeData(
    rangeTrackShape: GradientRectRangeSliderTrackShape()
  ),
  child: RangeSlider(
    onChanged: onChanged,
    values: data.values,
    min: data.min,
    max: data.max,
    divisions: data.labels.length
  )
)

0
投票
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Gradient Range Slider Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Gradient Range Slider Demo'),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final String title;

  MyHomePage({Key? key, required this.title}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: GradientRangeSlider(),
      ),
    );
  }
}

class GradientRangeSlider extends StatefulWidget {
  @override
  _GradientRangeSliderState createState() => _GradientRangeSliderState();
}

class _GradientRangeSliderState extends State<GradientRangeSlider> {
  RangeValues _currentRangeValues = const RangeValues(20, 80);

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text(
          'Volume Control',
          style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
        ),
        SizedBox(height: 20),
        SliderTheme(
          data: SliderTheme.of(context).copyWith(
            rangeTrackShape: GradientRectRangeSliderTrackShape(
              gradient: LinearGradient(
                colors: [Colors.purple, Colors.pink, Colors.yellow],
              ),
            ),
            activeTrackColor: Colors.transparent,
            inactiveTrackColor: Colors.grey.withOpacity(0.5),
            thumbColor: Colors.green,
            overlayColor: Colors.blue.withOpacity(0.3),
            valueIndicatorColor: Colors.blue,
            activeTickMarkColor: Colors.white,
            inactiveTickMarkColor: Colors.white.withOpacity(0.7),
          ),
          child: RangeSlider(
            values: _currentRangeValues,
            min: 0,
            max: 100,
            divisions: 100,
            labels: RangeLabels(
              _currentRangeValues.start.round().toString(),
              _currentRangeValues.end.round().toString(),
            ),
            onChanged: (RangeValues values) {
              setState(() {
                _currentRangeValues = values;
              });
            },
          ),
        ),
        SizedBox(height: 20),
        Text(
          'Volume Range: ${_currentRangeValues.start.round()} - ${_currentRangeValues.end.round()}',
          style: TextStyle(fontSize: 16),
        ),
      ],
    );
  }
}

class GradientRectRangeSliderTrackShape
    extends RectangularRangeSliderTrackShape {
  final Gradient gradient;

  const GradientRectRangeSliderTrackShape({required this.gradient});

  @override
  void paint(
    PaintingContext context,
    Offset offset, {
    required RenderBox parentBox,
    required SliderThemeData sliderTheme,
    required Animation<double>? enableAnimation,
    required TextDirection textDirection,
    required Offset startThumbCenter,
    required Offset endThumbCenter,
    bool isEnabled = false,
    bool isDiscrete = false,
    double additionalActiveTrackHeight = 2,
  }) {
    if (!isEnabled) {
      super.paint(
        context,
        offset,
        parentBox: parentBox,
        sliderTheme: sliderTheme,
        enableAnimation: enableAnimation,
        textDirection: textDirection,
        startThumbCenter: startThumbCenter,
        endThumbCenter: endThumbCenter,
        isEnabled: isEnabled,
        isDiscrete: isDiscrete,
      );
      return;
    }

    final Canvas canvas = context.canvas;
    final Paint paint = Paint()
      ..shader = gradient.createShader(
          Rect.fromLTWH(0, 0, parentBox.size.width, parentBox.size.height));

    final Rect trackRect = getPreferredRect(
      parentBox: parentBox,
      offset: offset,
      sliderTheme: sliderTheme,
      isEnabled: isEnabled,
      isDiscrete: isDiscrete,
    );

    final Rect activeTrackRect = Rect.fromLTRB(
      startThumbCenter.dx,
      trackRect.top,
      endThumbCenter.dx,
      trackRect.bottom,
    );

    canvas.drawRect(activeTrackRect, paint);
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.