Flutter自定义范围滑块

时间:2018-10-25 10:47:58

标签: dart flutter flutter-layout

我正在尝试在“容器行”的顶部创建一个范围滑块,该滑块将创建音频波形,但是我不知道从哪里开始...

主要问题是范围滑块位于容器行的顶部,应该在“选定”部分更改其颜色。

enter image description here

这是我目前拥有的:

enter image description here

用于创建图像和细节的代码。

class BeatLyricsPage extends StatefulWidget {
  final Beat beat;
  BeatLyricsPage(this.beat);

  @override
  _BeatLyricsPageState createState() => _BeatLyricsPageState(beat);
}

class _BeatLyricsPageState extends State<BeatLyricsPage> {
  final Beat beat;


  final _kPicHeight = 190.0;
  // used in _buildPageHeading to add the beat key and beat bpm
  Widget _buildBeatInfoItem(String text) => DecoratedBox(
        decoration: BoxDecoration(
          border: Border.all(color: MyColor.white, width: 1.0),
          borderRadius: BorderRadius.circular(4.0),
        ),
        child: Padding(
          padding: EdgeInsets.symmetric(vertical: 3.0, horizontal: 12.0),
          child: Text(text, style: TextStyle(color: MyColor.white, fontSize: 10.0, fontWeight: FontWeight.w600)),
        ),
      );

  final _kAudioControlsWidth = 180.0;
  final _kAudioControlsHeight = 36.0;
  final _kAudioControlsMainButtonSize = 56.0;

  Widget _buildAudioControls(BuildContext context) => Positioned(
        left: (MediaQuery.of(context).size.width / 2) - (_kAudioControlsWidth / 2),
        top: _kPicHeight - (_kAudioControlsHeight / 2),
        child: Stack(
          overflow: Overflow.visible,
          children: [
            Container(
              width: _kAudioControlsWidth,
              height: _kAudioControlsHeight,
              decoration: BoxDecoration(color: MyColor.darkGrey, borderRadius: BorderRadius.circular(100.0)),
              padding: EdgeInsets.symmetric(horizontal: LayoutSpacing.sm),
              child: Row(
                children: [
                  CButtonLike(beatId: beat.id),
                  Spacer(),
                  GestureDetector(
                    behavior: HitTestBehavior.opaque,
                    child: Icon(BeatPulseIcons.cart),
                    onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => LicenseOptionsPage(beat))),
                  ),
                ],
              ),
            ),
            // ****** MAIN BUTTON (Play/Pause) ******
            Positioned(
              left: (_kAudioControlsWidth / 2) - (_kAudioControlsMainButtonSize / 2),
              top: (_kAudioControlsHeight - _kAudioControlsMainButtonSize) / 2,
              child: Container(
                height: _kAudioControlsMainButtonSize,
                width: _kAudioControlsMainButtonSize,
                decoration: BoxDecoration(
                    gradient: LinearGradient(begin: Alignment.topLeft, colors: [MyColor.primary, Color(0xFFf80d0a)]),
                    borderRadius: BorderRadius.circular(100.0)),
                child: CButtonPlay(),
              ),
            )
          ],
        ),
      );

  Widget _buildWaveForm() {
    // creates a random list of doubles, "fake data"
    var rng = Random();
    final List waveFormData = [];
    for (var i = 0; i < 90; i++) {
      waveFormData.add(rng.nextInt(45).toDouble());
    }
    // player bloc
    final playerBloc = BlocProvider.getPlayerBloc(context);
    // renders
    return Container(
      height: _kPicHeight,
      padding: EdgeInsets.symmetric(vertical: LayoutSpacing.xxxl),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.end,
        children: [
          // current playing second
          StreamBuilder<double>(
            stream: playerBloc.playingSecond,
            initialData: 0.0,
            builder: (_, playingSecondSnapshot) {
              // current beat playing
              return StreamBuilder<Beat>(
                stream: playerBloc.playingBeat,
                builder: (_, playingBeatSnapshot) {
                  final playingBeat = playingBeatSnapshot.data;
                  // if the beat playing is the same as the beat selected for the lyrics, show playing seconds
                  if (playingBeat?.id == beat.id)
                    return Text(secondsToTime(playingSecondSnapshot.data), style: MyFontStyle.sizeXxs);
                  // otherwise show 0:00
                  else
                    return Text(secondsToTime(0), style: MyFontStyle.sizeXxs);
                },
              );
            },
          ),
          SizedBox(width: LayoutSpacing.xs),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.end,
            children: waveFormData
                .map((waveFormDataIndex) => Container(
                      height: waveFormDataIndex > 5.0 ? waveFormDataIndex : 5.0,
                      width: 2,
                      color: MyColor.white,
                      margin: EdgeInsets.only(right: 1),
                    ))
                .toList(),
          ),
          SizedBox(width: LayoutSpacing.xs),
          Text(secondsToTime(beat.length), style: MyFontStyle.sizeXxs),
        ],
      ),
    );
  }

  Widget _buildPageHeading(BuildContext context, {@required String imageUrl}) => Stack(
        children: [
          Column(
            children: [
              Hero(
                tag: MyKeys.makePlayerCoverKey(beat.id),
                child: Opacity(
                  opacity: 0.3,
                  child: Container(
                    height: _kPicHeight,
                    decoration: BoxDecoration(
                      image: DecorationImage(image: CachedNetworkImageProvider(imageUrl), fit: BoxFit.cover),
                    ),
                  ),
                ),
              ),
              Container(color: MyColor.background, height: LayoutSpacing.xl)
            ],
          ),
          Padding(
            padding: EdgeInsets.all(LayoutSpacing.xs),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.end,
              children: [
                _buildBeatInfoItem(beat.key),
                SizedBox(width: 4.0),
                _buildBeatInfoItem('${beat.bpm} BPM'),
              ],
            ),
          ),
          _buildAudioControls(context),
          _buildWaveForm(),
        ],
      );
}

2 个答案:

答案 0 :(得分:3)

要创建自定义范围滑块,可以使用GestureRecognizer并将每个滑块的位置保存在StatefulWidget内的变量中。要确定索引为 i 的条形图是否在范围内,可以将限制器的像素位置(以下来源中的devDependenciesbar1)除以宽度条形图并将其与 i 进行比较。

很遗憾,我无法处理您的代码示例。相反,我创建了一个最低限度的示例,如下所示。如果您需要花一点时间阅读一下,我相信您可以将其传输到您的应用程序中。

Custom Range Slider

bar2

答案 1 :(得分:0)

要有一个波浪滑块:

class WaveSlider extends StatefulWidget {
  final double initialBarPosition;
  final double barWidth;
  final int maxBarHight;
  final double width;
  WaveSlider({
    this.initialBarPosition = 0.0,
    this.barWidth = 5.0,
    this.maxBarHight = 50,
    this.width = 60.0,
  });
  @override
  State<StatefulWidget> createState() => WaveSliderState();
}

class WaveSliderState extends State<WaveSlider> {
  List<int> bars = [];
  double barPosition;
  double barWidth;
  int maxBarHight;
  double width;

  int numberOfBars;

  void randomNumberGenerator() {
    Random r = Random();
    for (var i = 0; i < numberOfBars; i++) {
      bars.add(r.nextInt(maxBarHight - 10) + 10);
    }
  }

  _onTapDown(TapDownDetails details) {
    var x = details.globalPosition.dx;
    print("tap down " + x.toString());
    setState(() {
      barPosition = x;
    });
  }

  @override
  void initState() {
    super.initState();
    barPosition = widget.initialBarPosition;
    barWidth = widget.barWidth;
    maxBarHight = widget.maxBarHight.toInt();
    width = widget.width;
    if (bars.isNotEmpty) bars = [];
    numberOfBars = width ~/ barWidth;
    randomNumberGenerator();
  }

  @override
  Widget build(BuildContext context) {
    int barItem = 0;
    return Scaffold(
      backgroundColor: Colors.grey[900],
      body: Center(
        child: GestureDetector(
          onTapDown: (TapDownDetails details) => _onTapDown(details),
          onHorizontalDragUpdate: (DragUpdateDetails details) {
            setState(() {
              barPosition = details.globalPosition.dx;
            });
          },
          child: Container(
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.end,
              mainAxisAlignment: MainAxisAlignment.start,
              children: bars.map((int height) {
                Color color = barItem + 1 < barPosition / barWidth
                    ? Colors.white
                    : Colors.grey[600];
                barItem++;
                return Row(
                  children: <Widget>[
                    Container(
                      width: .1,
                      height: height.toDouble(),
                      color: Colors.black,
                    ),
                    Container(
                      decoration: BoxDecoration(
                        color: color,
                        borderRadius: BorderRadius.only(
                          topLeft: const Radius.circular(1.0),
                          topRight: const Radius.circular(1.0),
                        ),
                      ),
                      height: height.toDouble(),
                      width: 4.8,
                    ),
                    Container(
                      width: .1,
                      height: height.toDouble(),
                      color: Colors.black,
                    ),
                  ],
                );
              }).toList(),
            ),
          ),
        ),
      ),
    );
  }
}

并像这样使用它:

WaveSlider(
        initialBarPosition: 180.0,
        barWidth: 5.0,
        maxBarHight: 50,
        width: MediaQuery.of(context).size.width,
      )