flutter通过GlobalKey在自定义Widget外部获取其state刷新页面

Posted 一叶飘舟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了flutter通过GlobalKey在自定义Widget外部获取其state刷新页面相关的知识,希望对你有一定的参考价值。

PageA的定义如下

class PageA extends StatefulWidget 
  PageA(Key key) : super(key: key);

  @override
  PageAState createState() 
    return PageAState();
  


class PageAState extends State<PageA> 
  static GlobalKey homeKey = GlobalKey();
  //关键代码
  static currentInstance() 
  	var state = PageAState.homeKey.currentContext.findAncestorStateOfType();
    return state;
  
  @override
  Widget build(BuildContext context) 
    // TODO: implement build
    return return Scaffold(
        key: homeKey,
        body: Container();
    );
  
  reloadData() 
  	setState(() );
  

在 PageB 里调用 PageAState 的 reloadData() 方法

class PageB 
	buttonClick() 
		PageAState.currentInstance().reloadData();
	

注意:

如果PageA没有被打开过,调用 PageAState.currentInstance().reloadData()是会出现空指针的。

需要加空判断。

var state = PageAState.homeKey.currentContext?.findAncestorStateOfType< PageAState >();

调用的时候:

PageAState.currentInstance()?.reloadData();

 Flutter中父widget调用子widget的第二种方法

大致步骤:

一、
定义globalKey,注意<>中是子widget的State类。

final _childWidgetKey = GlobalKey();

二、在父页面初始化子widget
 

ChildPage(key:_receiveKey),

三、

class ChildPage extends StatefulWidget 

ChildPage(Key key) : super(key: key);

@override
ChildPageState createState() => ChildPageState();

四、
在父界面调用子widget中的方法

_childWidgetKey.currentState.onRefresh();

使用GlobalKey实现父布局调用子布局方法完成视频url切换播放 & 视频url中文件名中文导致无法播放的问题  

video_player: ^2.1.10
  chewie: ^1.2.2

当把chewie封装到一个Widget - VideoPlayer,在父布局调用初始化第一个视频url后,发现不知如何切换视频url。

自己封装的VideoPlayer中封装了切换视频的方法

//切换视频url
switchVideo(String url,bool isAuto) 
    _videoPlayerController.pause();
    setState(() 
      _prepareVideo(url,isAuto);
    );
  

void _prepareVideo(String url, bool isAuto) 
    _url = url;
    //解决视频url中文件名中文导致无法播放的问题
    if (!url.contains('%')) 
      int index = url.lastIndexOf('/');
      String prefix = url.substring(0, index + 1);
      String subfix = url.substring(index + 1);
      var encodeSubfix = Uri.encodeComponent(subfix).toString();
      _url = prefix + encodeSubfix;
      print('编码后:' + _url);
    

    _videoPlayerController = VideoPlayerController.network(_url);
    _chewieController = ChewieController(
      videoPlayerController: _videoPlayerController,
      autoPlay: isAuto,
      aspectRatio: widget.aspectRatio,
      //customControls: CustomControls(),
    );
  


父布局调用子布局方法

关键方法:
final GlobalKey<子布局State> _globalKey = GlobalKey(); //一般子布局state时设置成私有的,此时需要去掉_

取得子组件的state对象: _globalKey.currentState.子布局方法()

父布局使用的是Stateful,在父布局State类中定义GlobalKey变量

 final GlobalKey<VideoPlayerState> _globalKey = GlobalKey();

使用子组件VideoPlayer时,通过key传给子组件

VideoPlayer(
      key: _globalKey,
      playUrl: _courseDatas[_currentVideoIndex].videoUrl,
      autoPlay: true,
    )


切换视频:

void _play(CourseData course) 
    _currentVieo = course;
    _currentVideoIndex = _courseDatas.indexOf(course);
    print('current index = $_currentVideoIndex');
    setState(() );
    //when is first play,when is switch?
    _globalKey.currentState!.switchVideo(course.videoUrl, true);
  


url中带有中文导致视频播放器无法播放的问题分析

原因:

ios中,访问HTTP/HTTPS时,url中存在中文或者特殊字符时,会导致无法正常的访问到资源或服务,想要解决这个问题,需要对url进行编码。

网络标准RFC 1738规定url中只能包含英文字母和阿拉伯数字,以及一些特殊字符。“只有字母和数字[0-9a-zA-Z]、和特殊符号”$-_.+!*’(),”[不包括双引号]、及某些保留字,才可以不经过编码直接用于URL。”

dart中对url进行编码:

Uri.encodeComponent(url); 
Uri.decodeComponent(encodedUrl); 
 _url = url;
    //解决视频url中文件名中文导致无法播放的问题
    if (!url.contains('%')) 
      int index = url.lastIndexOf('/');
      String prefix = url.substring(0, index + 1);
      String subfix = url.substring(index + 1);
      var encodeSubfix = Uri.encodeComponent(subfix).toString();
      _url = prefix + encodeSubfix;
      print('编码后:' + _url);
    

注意:

  • 上文中的url编码方式,仅适用于只有最后的文件名中含有中文的情况,且未测试更多复杂的文件名情况
  • 上文中的方式,因为服务器返回的url,部分是已经对中文编码的,部分没有,所以粗暴地根据url文件名部分是否含有%来判定是否已经编码
  • 更好的方式应当检查判定url中是否含有中文以及非RFC 1738规定字符以外的字符,并对这部分字符进行编码
  • 若我直接对全url进行编码,就会对其他字符也编码了, 导致新的url即使在Chrome中也无法访问
     

Flutter:获取状态state的几种方式

1,通过GlobalKey获取到state

在Scaffold中设置key为GlobalKey,然后在使用的地方通过GlobalKey获取

class StatefullDemo extends StatelessWidget 
  static GlobalKey<ScaffoldState> _globalKey = GlobalKey();
  @override
  Widget build(BuildContext context) 
    return Scaffold(
      key: _globalKey,
  ......
  ......
  ScaffoldState _state = _globalKey.currentState;

2,通过查找父级最近的Scaffold对应的ScaffoldState对象

如果使用是在StatelessWidget中,则可以直接获取到widget上下文的content,直接使用context.findAncestorStateOfType获取到state

ScaffoldState _state =
                  context.findAncestorStateOfType<ScaffoldState>();

如果需要在StatefulWidget 的 State中使用呢?如何获取到widget中的state?
此时可以通过Builder来构建widget,Builder会将widget节点的context作为回调参数

Builder(builder: (context) 
              return RaisedButton(
                onPressed: () 
                  ScaffoldState _state = context.findAncestorStateOfType();
                  SnackBar snackBar = SnackBar(content: Text('RaisedButton'));
                  _state.showSnackBar(snackBar);

                  ScaffoldState _state1 = this as ScaffoldState;
                ,
                child: Text('RaisedButton'),
              );
            ),

 

以上是关于flutter通过GlobalKey在自定义Widget外部获取其state刷新页面的主要内容,如果未能解决你的问题,请参考以下文章

Flutter:多个小部件使用相同的 GlobalKey

Flutter中GlobalKey的用法

为啥 Flutter 中的 Form 需要 GlobalKey?

Flutter 使用 GlobalKey 从另一个类更改状态

为啥 Flutter 文档在创建 Form 时要求使用 GlobalKey 而不是其他类型的键?

Flutter 局部Widget刷新