首页 > 解决方案 > 在综合浏览量项目中使用全局键的问题

问题描述

我的应用程序中有三个级别的小部件(例如)。

更高级别:_HomePage <-------- 返回 PageView 小部件

中层:_PlayerPage <-------- 页面项(Container)

下层:_YoutubePlayer <------- YoutubePlayerFlutter Widget (Plugin)

页面视图是可垂直滚动的,它会在用户滚动时切换页面。YoutubePlayer 小部件使用 GlobalKey,因此页面之间的滚动和切换在性能上非常流畅和快速。然而,这有一个问题。如果用户翻页失败,但只滚动了一半,所以它回到了原来的位置,它会导致 GlobalKey 重复错误并弄乱元素的生命周期,因为即使翻页失败,翻页事件仍然会被触发,但只要用户开始拖动,并在前一个元素的生命周期按照文档中的状态卸载之前创建具有相同全局键的新小部件:

一个元素只能保持在非活动状态直到当前动画帧结束。在动画帧结束时,任何仍处于非活动状态的元素都将被卸载。

在解决了这个问题之后,我尝试了一些可能的解决方法,我认为它可以解决问题:

  1. 不使用全局密钥。

  2. 使用全局键,但创建自定义 ScrollPhysics 以防止用户按住或缓慢拖动页面。用户只能翻页。

. . 结果: 1. 如果我不使用全局键,它可以正常工作而不会抛出任何错误,但整体性能会变慢,因此会导致另一个问题。如果快速重复滑动,则视频加载时间无法赶上滑动速度。因此,某些视频在加载时会出错(但它不会破坏整个应用程序,只会破坏视频本身)。此外,滑动手势本身并不顺利,因为有全局键。我也尝试过使用 UniqueKey,但它似乎与根本不使用密钥没有什么不同。

结论:我认为在性能方面使用全局密钥比不使用它更好。

  1. 我试过修改 minFlingVelocity 和 DragStartDistanceMotionThreshold。看起来 pageview scrollphysics 正在使用两种独立的方式来翻页:拖动和甩动。所以结果证明调整投掷速度并不是我想要的。但是当我将 DragStartDistanceMotionThreshold 设置为设备屏幕的高度时,它可以禁用缓慢拖动的移动页面,但仍然可以按我的意愿进行投掷。然而出现的其他问题是,1)Fling 无法反向工作(在我的情况下,向上),因此无法返回上一页。2)不知何故,如果没有在两次抛掷之间产生延迟(大约 1 秒),抛掷不会重复工作。所以我必须喜欢一次甩动,然后休息 1 秒,再进行一次甩动。此外,如果用户不断尝试重复投掷,这不是一个很好的用户体验,

结论:这可能是最好的解决方案,只有当有两个问题的解决方案(禁用反向投掷和重复投掷),但我找不到解决它的方法。

如果您对这些问题有任何其他解决方法建议或解决方案,请告诉我。我很感激。我的简化代码如下:

import 'dart:math';
import 'package:JustMusic/global_components/api.dart';
import 'package:JustMusic/routes/home/components/youtube_player.dart';
import 'package:JustMusic/utils/custom_scroll_physics.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

class HomePage extends StatefulWidget {

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

class _HomePageState extends State<HomePage> {
  ScrollPhysics _pageViewScrollPhysics = new CustomScrollPhysics();
  final _pageController = PageController(initialPage: 0);
  PageView pageView;
  List<dynamic> _sources = [];
  int _previousPageIndex = 0;
  var _futureVideo;

  @override
  void initState() {
    _futureVideo = Api.getVideoList();
    _futureVideo.then((videos) {
      _sources = videos;
      pageView = PageView(
          physics: _pageViewScrollPhysics,
          controller: _pageController,
          scrollDirection: Axis.vertical,
          onPageChanged: (index) {
            if (index > _previousPageIndex) {
              print('page switched to $index (down)');
            } else {
              print('page switched to $index (up)');
            }
            _previousPageIndex = index;
          },
          children: []..addAll(_sources.map((_source) {
                return YoutubePlayerScreen(
                    source: _source, pageController: _pageController);
            })));
    });
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: _futureVideo,
        builder: (BuildContext context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            return pageView;
          } else if (snapshot.connectionState == ConnectionState.waiting) {
            return Center(child: CircularProgressIndicator());
          } else {
            return Center(
              Text('Error: ${snapshot.error}'),
            );
          }
        });
  }
}

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

class YoutubePlayerScreen extends StatefulWidget {
  YoutubePlayerScreen({
    @required this.source,
    @required this.pageController
  });

  final PageController pageController;
  final dynamic source;

  State<YoutubePlayerScreen> createState() => _YoutubePlayerScreenState();
}

class _YoutubePlayerScreenState extends State<YoutubePlayerScreen> {
  var _controller = YoutubePlayerController();

  @override
  Widget build(BuildContext context) {
    return Material(
      child: Center(
          child: YoutubePlayer(
            context: context,
            videoId: YoutubePlayer.convertUrlToId(widget.source["videoUrl"]),
            flags: YoutubePlayerFlags(
              disableDragSeek: true,
              autoPlay: false,
              mute: false,
              showVideoProgressIndicator: true,
            ),
            videoProgressIndicatorColor: Colors.amber,
            progressColors: ProgressColors(
              playedColor: Colors.amber,
              handleColor: Colors.amberAccent,
            ),
            onPlayerInitialized: (controller) {
              _controller = controller;
              _controller.cue();
              _controller.addListener(() {
                if (_controller.value.playerState == PlayerState.CUED) {
                  _controller.play();
                }
              }
            );
          }
        )
      )
    );
  }
}

您可以从这里找到 YoutubePlayerFlutter 的源代码: https ://github.com/sarbagyastha/youtube_player_flutter/blob/master/lib/src/youtube_player.dart


编辑:我也想出了另一种解决方法,但不知道如何实施。就是只在PageChanged 上加载YoutubePlayerWidget。因为我已经测试过拖动页面会加载上一个/下一个视频,但不会触发 onPageChanged 监听器。仅当页面实际转到上一页/下一页时才会触发侦听器。

标签: flutterflutter-layout

解决方案


推荐阅读