簡體   English   中英

AnimatedList 中的 Slider 值錯誤

[英]Slider in AnimatedList has wrong value

我有一個聊天氣泡小部件,它支持帶有Slider小部件的音頻播放器。
滑塊的值會根據 AudioPlayer 的進度進行更改,這似乎工作正常。

當第一個音頻完全播放時(意味着滑塊的值現在是 100%),並且現在第二個聊天氣泡被添加到AnimatedList中,那么最新的 Slider 的值是 100 而之前的值是 0。

這是一個更好理解的示例:
消息 1 添加到列表:音頻播放完成 => Slider 值為 100。
消息 2 添加到列表:Slider 值為 100(應為 0),消息 1 中的 slider 值為 0。

這是小部件:

import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';

class MessageBubbleAudioPlayer extends StatefulWidget {
  final Color color;
  final String audioUrl;

  const MessageBubbleAudioPlayer({
    @required this.audioUrl,
    @required this.color,
  });

  @override
  _MessageBubbleAudioPlayerState createState() =>
      _MessageBubbleAudioPlayerState();
}

class _MessageBubbleAudioPlayerState extends State<MessageBubbleAudioPlayer> {
  bool loading = false;
  bool isPlaying = false;
  double audioSeekValue = 0;
  final AudioPlayer audioPlayer = AudioPlayer();
  Duration totalDuration = Duration(milliseconds: 0);

  @override
  void initState() {
    super.initState();

    WidgetsBinding.instance.addPostFrameCallback((_) async {
      audioPlayer.onPlayerStateChanged.listen((event) {
        if (mounted) setState(() => isPlaying = event == PlayerState.PLAYING);
      });

      audioPlayer.onAudioPositionChanged.listen((event) {
        final percent =
            ((event.inMilliseconds * 100) / totalDuration.inMilliseconds) ?? 0;
        if (mounted) setState(() => audioSeekValue = percent);
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        loading
            ? Container(
                height: 30,
                width: 30,
                padding: const EdgeInsets.all(8),
                child: CircularProgressIndicator(
                  color: widget.color,
                  strokeWidth: 1.8,
                ),
              )
            : Container(
                width: 30,
                child: IconButton(
                  icon: Icon(isPlaying ? Icons.pause : Icons.play_arrow,
                      color: widget.color),
                  onPressed: () async {
                    if (audioPlayer.state == PlayerState.PAUSED) {
                      audioPlayer.resume();
                      return;
                    }

                    if (!isPlaying) {
                      setState(() => loading = true);
                      await audioPlayer.play(widget.audioUrl);
                      audioPlayer.getDuration().then((value) {
                        totalDuration = Duration(milliseconds: value);
                        setState(() => loading = false);
                      });
                    } else
                      await audioPlayer.pause();
                  },
                  splashRadius: 25,
                ),
              ),
        SliderTheme(
          data: SliderThemeData(
              trackHeight: 1.4,
              thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7)),
          child: Slider(
            label: "Audio",
            activeColor: widget.color,
            inactiveColor: widget.color.withAlpha(100),
            // this (value) should be 0 for a newly added widget 
            // but is 100 for the newer one & 0 for the previous one,
            // which infact should be opposite
            value: audioSeekValue,
            min: 0,
            max: 100,
            onChanged: (_) {},
          ),
        )
      ],
    );
  }
}

該小部件又用於另一個處理消息類型並顯示適當用戶界面的小部件。
這里是:

class MessageBubble extends StatelessWidget {
  final bool isSender, isAudio;
  final String message;

  const MessageBubble(this.message, this.isSender, this.isAudio, Key key)
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 3),
      child: Align(
          alignment: isSender ? Alignment.centerRight : Alignment.centerLeft,
          child: message.contains(Constants.emojiRegex, 0) &&
                  !message.contains(Constants.alphaNumericRegex)
              ? Padding(
                  padding: EdgeInsets.only(
                      top: 6,
                      bottom: 6,
                      left: isSender ? 16 : 0,
                      right: isSender ? 0 : 32),
                  child: Text(message,
                      style: TextStyle(fontSize: 45, color: Colors.white)),
                )
              : Material(
                  borderRadius: BorderRadius.circular(30),
                  elevation: 4,
                  color: isSender
                      ? Colors.deepPurpleAccent.shade100.darken()
                      : Colors.white,
                  child: isAudio
                      ? Padding(
                          padding: const EdgeInsets.symmetric(
                              horizontal: 20, vertical: 6),
                          child: MessageBubbleAudioPlayer(
                            key: ValueKey(message.hashCode.toString()),
                            audioUrl: message,
                            color: isSender
                                ? Colors.white
                                : Colors.deepPurpleAccent,
                          ),
                        )
                      : Padding(
                          padding: const EdgeInsets.symmetric(
                              horizontal: 20, vertical: 14),
                          child: Linkify(
                            onOpen: (link) async {
                              if ((await canLaunch(link.url)))
                                await launch(link.url);
                            },
                            options: LinkifyOptions(humanize: false),
                            linkStyle: TextStyle(
                                color: isSender
                                    ? Colors.white
                                    : Colors.deepPurpleAccent),
                            text: message,
                            style: TextStyle(
                                fontSize: 17,
                                color: isSender ? Colors.white : Colors.black),
                          ),
                        ),
                )),
    );
  }
}

這是AnimatedList

class ChatAnimatedList extends StatefulWidget {
  final bool isInfoShown, isSender;

  const ChatAnimatedList(
      {@required Key key, @required this.isInfoShown, this.isSender})
      : super(key: key);

  @override
  ChatAnimatedListState createState() => ChatAnimatedListState();
}

class ChatAnimatedListState extends State<ChatAnimatedList> {
  final _messageList = <MessageBubble>[];
  final _animatedListState = GlobalKey<AnimatedListState>();

  get messageLength => _messageList.length;

  insertMessageBubble(MessageBubble messageBubble) =>
      _messageList.insert(0, messageBubble);

  insertViaState() {
    if (_animatedListState.currentState != null)
      _animatedListState.currentState.insertItem(0);
  }

  @override
  Widget build(BuildContext context) {
    return widget.isInfoShown
        ? InfoPlaceholder(isSender: widget.isSender)
        : Expanded(
            child: AnimatedList(
                reverse: true,
                key: _animatedListState,
                initialItemCount: _messageList.length,
                itemBuilder: (_, index, animation) {
                  return index == 0
                      ? Padding(
                          padding: const EdgeInsets.only(bottom: 6),
                          child: _messageList[index])
                      : index == _messageList.length - 1
                          ? Padding(
                              padding: const EdgeInsets.only(top: 30),
                              child: _messageList[index])
                          : _messageList[index];
                }),
          );
  }
}

我也嘗試過使用AutomaticKeepAliveClientMixin但仍然沒有用。
對此的任何想法將不勝感激。

這可能是由於您的小部件屬於同一類型而發生的。

當 flutter 檢查小部件樹中的更改時,它會檢查小部件的type以及創建該小部件時提供的key

從您的示例中,很明顯在創建StatefulWidget時沒有提供任何key

因此,當您推送新的Widget時(我假設您在樹中推送此小部件的時間早於舊的小部件),flutter 認為這仍然是舊的小部件並將舊的State ZA8CFDE6331BD59EB2AC96F8911C4B6 分配給它。

開始,每當您創建存在於List類型小部件(如RowColumn等)中的新StatefulWidget時發送一個唯一鍵,

class MessageBubbleAudioPlayer extends StatefulWidget {

  const MessageBubbleAudioPlayer({
    @required this.audioUrl,
    @required this.color,
    Key key.
  }) : super(key: key);

在創造一個新的時,

MessageBubbleAudioPlayer(audioUrl: '', color: '', key: ValueKey(#some unique int or string#)

代替#some unique int or string#放置對該小部件唯一的內容,而不是索引,因為它可以更改,但您可以使用audioUrl本身作為鍵。

  1. 當您的音頻播放完成后,使audioSeekValue = 0 這將從一開始就開始。

  2. 如果要跟蹤: 播放的歌曲 1 = 70% 播放的歌曲 2 = 50%

在這種情況下,您必須將您的索引歌曲播放值保留在列表中,或者從后端動態獲取歌曲播放值。

如果有幫助,請告訴我。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM