首页 > 解决方案 > 如何在颤动中重新访问时保存页面状态

问题描述

我有 2 个屏幕,我试图了解如何实现页面状态。例如,在下面的屏幕中,我有 4 个选项,所有选项都将用户带到同一个屏幕,唯一的区别是为每个选项调用的 API 不同以构建列表。我正在尝试处理后退箭头动作,这就是我遇到问题的地方。

用例 - 当用户在屏幕 2 上时,他正在播放歌曲,现在在后按歌曲继续播放。现在,当用户再次从屏幕 1 中选择相同的选项时,我想显示相同的列表而无需重新加载和选择。如果用户选择任何其他选项,它应该表现正常,这是有效的。

解决方案 -

  1. 我可以对加载的歌曲列表进行硬编码并发送回屏幕 1,然后通过选择再次将其取回,但这会占用大量资源。
  2. AutomaticKeepAliveClientMixin 我尝试了本教程,但没有帮助或保持状态处于活动状态。
  3. PageStorage 是我看到的第三个选项,但我发现大部分时间都在我们有标签的应用程序上使用它。

在此处输入图像描述在此处输入图像描述

屏幕 1 -

class Dashboard  extends StatefulWidget {
  int playingId;
  Dashboard({this.playingId});
  @override
  _DashboardState createState() => _DashboardState(playingId);


}

class _DashboardState extends State<Dashboard> {
  String appname;
  int playingId = 0;
  _DashboardState(this.playingId);  
  // print('${playingId}');

  @override
  void initState() {
    appname="";
    // playingId=0;
  }  

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        elevation: 0,
        backgroundColor: AppColors.darkBlue,
        centerTitle: true,
        title: Text("Tirthankar", 
        style: TextStyle(color: Colors.white),),
      ),  

      backgroundColor: AppColors.styleColor,  
      body: Column(
        children: <Widget>[
          // SizedBox(height: 5),
          Padding(
            padding: const EdgeInsets.all(20),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[                
                CustomGridWidget(                  
                  child: Icon(
                        Icons.file_download,
                        size: 100,
                        color: AppColors.styleColor,                        
                      ),                    

                  // image: 'assets/bhaktambar.png',
                  sizew: MediaQuery.of(context).size.width * .4,
                  sizeh: MediaQuery.of(context).size.width * .5,
                  borderWidth: 2,
                  label: "Bhakti",
                  onTap: () {                    
                    Navigator.of(context).push(
                      MaterialPageRoute(                                                
                        builder: (_) => ListPage(appname: "Kids",playingId: playingId,),                        

                      ),
                    );
                  },
                ),
                CustomGridWidget(                  
                  child: Icon(
                        Icons.file_download,
                        size: 100,
                        color: AppColors.styleColor,                        
                      ),                    

                  // image: 'assets/bhaktambar.png',
                  sizew: MediaQuery.of(context).size.width * .4,
                  sizeh: MediaQuery.of(context).size.width * .5,
                  borderWidth: 2,
                  label: "Kids",
                  onTap: () {                    
                    Navigator.of(context).push(
                      MaterialPageRoute(                                                
                        builder: (_) => ListPage(appname: "Kids",playingId: playingId,),                        

                      ),
                    );
                  },
                ), 
              ],
            ),
          ),
          Padding(
            padding: const EdgeInsets.only(
              left: 20,
              right: 20,
              bottom: 20),                
          child: Row(

              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: <Widget>[
                CustomGridWidget(
                  child: Icon(
                        Icons.favorite,
                        size: 100,
                        color: AppColors.styleColor,
                      ),
                  // image: 'assets/kids.jpg',
                  sizew: MediaQuery.of(context).size.width * .4,
                  sizeh: MediaQuery.of(context).size.width * .5,
                  borderWidth: 2,
                  label: "Favorite",
                  onTap: () {
                    Navigator.of(context).push(
                      MaterialPageRoute(
                        builder: (_) => ListPage(appname: "Songs"),
                      ),
                    );
                  },
                ),
                Align(
                  alignment: Alignment.center,
                    child: CustomGridWidget(
                    child: Icon(
                          Icons.book,
                          size: 100,
                          color: AppColors.styleColor,
                        ),
                    // image: 'assets/vidyasagar.jpg',
                    sizew: MediaQuery.of(context).size.width * .4,
                    sizeh: MediaQuery.of(context).size.width * .5,
                    borderWidth: 2,                  
                    onTap: () {
                      Navigator.of(context).push(
                        MaterialPageRoute(
                          builder: (_) => ListPage(appname: "Bhajan"),
                        ),
                      );
                    },
                  ),
                ),
              ],
            ),
          ),
        ]
      ),   





    );
  }
  Material boxTiles(IconData icon, String name){
    return Material(

      color: AppColors.mainColor,
      elevation: 14.0,
      shadowColor: AppColors.styleColor,
      borderRadius: BorderRadius.circular(24.0),
      child: Center(
        child: Padding(
          padding: const EdgeInsets.all(8.0),
        child:Row(
          mainAxisAlignment: MainAxisAlignment.center,

          children: <Widget>[
            Column(              
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                InkWell(
                    onTap: (){print("tapped");   /*  or any action you want */ },
                    child: Container(                        
                        width: 130.0,
                        height: 10.0,
                        color : Colors.transparent,
                      ), // container
                ), //
                //Text
                Padding(
                  padding: const EdgeInsets.all(8.0),                  
                  child: Text(name,
                  style:TextStyle(
                    color: AppColors.styleColor,
                    fontSize: 20.0,
                    )
                  ),
                ),
                //Icon
                Material(
                  color: AppColors.styleColor,  
                  borderRadius: BorderRadius.circular(50.0),
                  child: Padding(padding: const EdgeInsets.all(10.0),
                    child: Icon(icon, color: AppColors.lightBlue,size: 30,),                      
                  ),
                ),
              ],
            )
          ],
        )
      ),
      )
    );
  }

}

屏幕 2 -

// import 'dart:js';


class ListPage extends StatefulWidget {
  String appname;
  int playingId;
  ListPage({this.appname,this.playingId});  
  @override
  _ListPageState createState() => _ListPageState(appname,playingId);
}

class _ListPageState extends State<ListPage>
    with SingleTickerProviderStateMixin,AutomaticKeepAliveClientMixin<ListPage> {
  String appname;
  int playingId;
  bool isPlaying = false;  
  _ListPageState(this.appname,playingId);
  // List<MusicModel> _list1;
  List<MusicData> _list;

  var _value;
  int _playId;
  int _songId;
  String _playURL;

  bool _isRepeat;
  bool _isShuffle;
  bool _isFavorite;
  String _startTime;
  String _endTime;
  AnimationController _controller;
  Duration _duration = new Duration();
  Duration _position = new Duration();
  final _random = new Random();
  AudioPlayer _audioPlayer = AudioPlayer();
  @override
  void initState() {
    _playId = 0;
    // _list1 = MusicModel.list;
    this._fileUpdate();     
    // _list = _list1;
    _controller =
        AnimationController(vsync: this, duration: Duration(microseconds: 250));
    _value = 0.0;
    _startTime = "0.0";
    _endTime = "0.0";
    _isRepeat = false;
    _isShuffle = false;
    _isFavorite = false;



    _audioPlayer.onAudioPositionChanged.listen((Duration duration) {
      setState(() {
        // _startTime = duration.toString().split(".")[0];
        _startTime = duration.toString().split(".")[0];
        _duration = duration;

        // _position = duration.toString().split(".")[0];
      });
    });
    _audioPlayer.onDurationChanged.listen((Duration duration) {
      setState(() {
        _endTime = duration.toString().split(".")[0];
        _position = duration;
      });
    });
    _audioPlayer.onPlayerCompletion.listen((event) {
      setState(() {        
        isPlaying = false;
        _position = _duration;
        if (_isRepeat) {
          _songId = _songId;
        } else {
          if (_isShuffle) {
            var element = _list[_random.nextInt(_list.length)];
            _songId = element.id;
          } else {
            _songId = _songId + 1;
          }
        }
        _player(_songId);
      });
    });

    super.initState();
  }

  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Scaffold(
      appBar: AppBar(
        elevation: 0,
        backgroundColor: AppColors.mainColor,
        centerTitle: true,
        leading: IconButton(
          icon: Icon(Icons.arrow_back),
          onPressed: (){
            Navigator.of(context).push(
               MaterialPageRoute(
                  builder: (_) => Dashboard(playingId: _songId),
                ),
            );       
          },
        ),
        title: Text(
          appname,
          style: TextStyle(color: AppColors.styleColor),
        ),
      ),



      backgroundColor: AppColors.mainColor,
      body: Stack(                
        children: <Widget>[          
          Column(
            children: <Widget>[
              Padding(
                padding: const EdgeInsets.all(24.0),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: <Widget>[
                    CustomButtonWidget(
                      child: Icon(
                        Icons.favorite,                                               
                        color: AppColors.styleColor,
                      ),
                      size: 50,
                      onTap: () {},
                    ),
                    CustomButtonWidget(
                      image: 'assets/logo.jpg',
                      size: 100,
                      borderWidth: 5,
                      onTap: () {
                        Navigator.of(context).push(
                          MaterialPageRoute(
                            builder: (_) => DetailPage(),
                          ),
                        );
                      },
                    ),
                    CustomButtonWidget(
                      child: Icon(
                        Icons.menu,
                        color: AppColors.styleColor,
                      ),
                      size: 50,
                      onTap: () {
                        Navigator.of(context).push(
                          MaterialPageRoute(
                            builder: (_) => HomePage(),
                          ),
                        );
                      },
                    )
                  ],
                ),
              ),
              slider(),
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 15),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: <Widget>[
                    IconButton(
                      icon: Icon(
                        _isRepeat ? Icons.repeat_one : Icons.repeat,
                        color: _isRepeat ? Colors.brown : AppColors.styleColor,
                      ),
                      onPressed: () {
                        if (_isRepeat) {
                          _isRepeat = false;
                        } else {
                          _isRepeat = true;
                        }
                      },
                    ),
                    IconButton(
                        icon: Icon(
                          isPlaying ? Icons.pause : Icons.play_arrow,
                          color: AppColors.styleColor,
                        ),
                        onPressed: () {
                          if (isPlaying) {
                            _audioPlayer.pause();
                            setState(() {
                              isPlaying = false; 
                            });
                          } else {
                            if (!isPlaying){
                              _audioPlayer.resume();
                              setState(() {
                                isPlaying = true;
                              });
                            }                            

                          }
                        }),
                    IconButton(
                        icon: Icon(
                          Icons.stop,
                          color: AppColors.styleColor,
                        ),
                        onPressed: () {
                          if (isPlaying){
                            _audioPlayer.stop();
                            setState(() {
                              isPlaying = false;
                              _duration = new Duration();
                            });
                          }

                          // isPlaying = false;
                        }),
                    IconButton(
                        icon: Icon(
                          Icons.shuffle,
                          color:
                              _isShuffle ? Colors.brown : AppColors.styleColor,
                        ),
                        onPressed: () {
                          if (_isShuffle) {
                            _isShuffle = false;
                          } else {
                            _isShuffle = true;
                          }
                        }),
                  ],
                ),
              ),              

              Expanded(
                //This is added so we can see overlay else this will be over button
                child: ListView.builder(
                  physics:
                      BouncingScrollPhysics(), //This line removes the dark flash when you are at the begining or end of list menu. Just uncomment for
                  // itemCount: _list.length,
                  itemCount: _list == null ? 0 : _list.length,
                  padding: EdgeInsets.all(12),
                  itemBuilder: (context, index) {
                    return GestureDetector(
                      onTap: () {
                        _songId = index;
                        _player(index);                        
                      },
                      child: AnimatedContainer(
                        duration: Duration(milliseconds: 500),
                        //This below code will change the color of sected area or song being played.
                        decoration: BoxDecoration(
                          color: _list[index].id == _playId
                              ? AppColors.activeColor
                              : AppColors.mainColor,
                          borderRadius: BorderRadius.all(
                            Radius.circular(20),
                          ),
                        ),
                        //End of row color change
                        child: Padding(
                          padding: const EdgeInsets.all(
                              16), //This will all padding around all size
                          child: Row(
                            mainAxisAlignment: MainAxisAlignment
                                .spaceBetween, //This will allign button to left, else button will be infront of name
                            children: <Widget>[
                              Column(
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: <Widget>[
                                  Text(
                                    _list[index].title,
                                    style: TextStyle(
                                      color: AppColors.styleColor,
                                      fontSize: 16,
                                    ),
                                  ),
                                  Text(
                                    _list[index].album,
                                    style: TextStyle(
                                      color: AppColors.styleColor.withAlpha(90),
                                      fontSize: 16,
                                    ),
                                  ),
                                ],
                              ),
                              IconButton(
                                  icon: Icon(_isFavorite
                                      ? Icons.favorite
                                      : Icons.favorite_border),
                                  onPressed: () {
                                    if (_isFavorite) {
                                      _isFavorite = false;
                                    } else {
                                      _isFavorite = true;
                                    }
                                  })
                              //Diabled Play button and added fav button.
                              // CustomButtonWidget(
                              //   //This is Play button functionality on list page.
                              //   child: Icon(
                              //     _list[index].id == _playId
                              //         ? Icons.pause
                              //         : Icons.play_arrow,
                              //     color: _list[index].id == _playId
                              //         ? Colors.white
                              //         : AppColors.styleColor,
                              //   ),
                              //   size: 50,

                              //   isActive: _list[index].id == _playId,
                              //   onTap: () async {
                              //     _songId = index;
                              //     _player(index);
                              //   },
                              // )
                            ],
                          ),
                        ),
                      ),
                    );
                  },
                ),
              )
            ],
          ),
          Align(
            alignment: Alignment.bottomCenter,
            child: Container(
              height: 50,
              decoration: BoxDecoration(
                  gradient: LinearGradient(
                colors: [
                  AppColors.mainColor.withAlpha(0),
                  AppColors.mainColor,
                ],
                begin: Alignment.topCenter,
                end: Alignment.bottomCenter,
              )),
            ),
          )
        ],
      ),
      // floatingActionButton: FloatingActionButton(
      //   child: Icon(Icons.music_note),
      //   onPressed: () async {      // String filePath = await FilePicker.getFilePath();

      //     int status = await _audioPlayer.play("https://traffic.libsyn.com/voicebot/Jan_Konig_on_the_Jovo_Open_Source_Framework_for_Voice_App_Development_-_Voicebot_Podcast_Ep_56.mp3");
      //     if (status == 1){
      //        setState(() {
      //          isPlaying = true;
      //        });
      //     }
      //   },
      // )
    );
  }

  Widget slider() {
    return Slider(
        activeColor: AppColors.styleColor,
        inactiveColor: Colors.lightBlue,
        value: _duration.inSeconds.toDouble(),
        min: 0.0,
        max: _position.inSeconds.toDouble(),
        divisions: 10,
        onChangeStart: (double value) {
          print('Start value is ' + value.toString());
        },
        onChangeEnd: (double value) {
          print('Finish value is ' + value.toString());
        },
        onChanged: (double value) {
          setState(() {
            seekToSecond(value.toInt());
            value = value;
          });
        });
  }

  Future<Void> _fileUpdate() async {
    String url =
        "azonaws.com/input.json";
    String arrayObjsText = "";
    try {
      eos.Response response;      
      Dio dio = new Dio();
      response = await dio.get(url,options: Options(
        responseType: ResponseType.plain,
      ),);
       arrayObjsText = response.data;
       print(response.data.toString());
    } catch (e) {
      print(e);
    }    
    var tagObjsJson = jsonDecode(arrayObjsText)['tags'] as List;    
    this.setState(() {
      _list = tagObjsJson.map((tagJson) => MusicData.fromJson(tagJson)).toList();  
    });

    // return _list;
    // print(_list);
  }

  Future<void> _player(int index) async {
    if (isPlaying) {
      if (_playId == _list[index].id) {
        int status = await _audioPlayer.pause();
        if (status == 1) {
          setState(() {
            isPlaying = false;
          });
        }
      } else {
        _playId = _list[index].id;        
        _playURL = _list[index].songURL;
        _audioPlayer.stop();
        int status = await _audioPlayer.play(_playURL);
        if (status == 1) {
          setState(() {
            isPlaying = true;
          });
        }
      }

    } else {
      _playId = _list[index].id;
      _playURL = _list[index].songURL;
      int status = await _audioPlayer.play(_playURL);
      if (status == 1) {
        setState(() {
          isPlaying = true;
        });
      }
    }
  }




  String _printDuration(Duration duration) {
    String twoDigits(int n) {
      if (n >= 10) return "$n";
      return "0$n";
    }

    String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
    String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
    return "${twoDigits(duration.inHours)}:$twoDigitMinutes:$twoDigitSeconds";
  }

  void seekToSecond(int second) {
    Duration newDuration = Duration(seconds: second);
    _audioPlayer.seek(newDuration);
  }


  }

标签: flutterdart

解决方案


PageStorage 是我看到的第三个选项,但我发现大部分时间都在我们有标签的应用程序上使用它。

您仍然可以根据需要使用此方法,无论是否使用 Tab/底部导航栏。

页面视图类

class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> with SingleTickerProviderStateMixin{
  PageController _pageController;

  @override
  void initState() {
    super.initState();
    _pageController = PageController();
  }

  @override
  void dispose() {
    super.dispose();
    _pageController?.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: PageView(
            controller: _pageController,
            physics: NeverScrollableScrollPhysics(), // so the user cannot scroll, only animating when they select an option
            children: <Widget>[
              Dashboard(playingId: 1, key: PageStorageKey<String>('MyPlayList'), pageController: _pageController), //or the name you want, but you need to give them a key to all the child so it can save the Scroll Position
              ListPage(appname: "FirstButton",playingId: 1, key: PageStorageKey<String>('FirstButton'), pageController: _pageController),
              ListPage(appname: "SecondButton",playingId: 1, key: PageStorageKey<String>('SecondButton'), pageController: _pageController),
              ListPage(appname: "ThirdButton",playingId: 1, key: PageStorageKey<String>('ThirdButton'), pageController: _pageController),
              ListPage(appname: "FourthButton",playingId: 1, key: PageStorageKey<String>('FourthButton'), pageController: _pageController)
            ],
          ),
        ) 
    );
  }
}

现在您将 PageController 传递给所有子项(将键和 PageController 属性添加到屏幕 1 和 2)并且在 DashBoard(屏幕 1)中,您可以将按钮的 onTap 从 MaterialRoute 更改为此

onTap: () => widget.pageController.jumpToPage(x), //where x is the index of the children of the PageView you want to see (between 0 and 4 in this case)

在屏幕 2 中,您使用 WillPopScope 包裹所有 Scaffold,因此当用户点击返回而不是关闭路线时,它会返回到索引 0 处的仪表板小部件

@override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () {
        widget.pageController.jumpToPage(0); // Move back to dashboard (index 0)
        return false;
      }
      child: Scaffold(...)
    );

  }

如果你想要一些动画效果(animateTo、nextPage、previousPage 等),你可以使用 PageController 的其他方法


推荐阅读