为什么我的 AsyncMemoizer 在 Flutter 中无法正常运行,我想停止不必要地重建我的小部件

Che*_*iah 1 async-await dart flutter

我正在开发一个像直播电视这样的应用程序,它基本上是一个基于视频的推荐平台

我注意到,当我滚动应用程序的“主页”时,有状态小部件被一次又一次调用(因为我正在使用 Future 构建器),因此它会导致大量数据消耗,这是一个严重的问题我希望它不要发生。

我按照本文使用了记忆化的概念https://medium.com/saugo360/flutter-my-futurebuilder-keeps-firing-6e774830bc2。代码不会抛出任何错误。但是使用它后我的主页无法加载。我不明白为什么会发生这种情况?

这是我未来的构建器代码:

@override
  Widget build(BuildContext context) {
    print("Build function called......................");

  body: SafeArea(
        child: SingleChildScrollView(
            controller: _scrollController,
            child: FutureBuilder(
                future: widgetLoadingFunctions(_selectedIndex),
                builder:
                    (BuildContext context, AsyncSnapshot<String> snapshot) {
                  if (snapshot.hasData) {
                    return widgetOptions(_selectedIndex);
                  } else
                    return //Asset.image('assets/loading.gif');
                        Image.asset('assets/loading.gif');
                })),
      ),
      drawer: Drawer(),
      bottomNavigationBar: BottomNavigationBar(
        backgroundColor: Colors.black,
        showUnselectedLabels: true,
        type: BottomNavigationBarType.shifting,
        currentIndex: _selectedIndex,
        fixedColor: Colors.amber,
        onTap: (int index) {
          setState(() {
            _selectedIndex = index;
          });
        },
        items: <BottomNavigationBarItem>[
//          Icon(
//            FontAwesome.facebook_square,
//            color: Colors.amber,
//          ),
          BottomNavigationBarItem(
            backgroundColor: Colors.black,
            icon: Icon(Icons.home),
            title: Text('Home'),
          ),
          BottomNavigationBarItem(
            backgroundColor: Colors.black,
            icon: Icon(
              MaterialCommunityIcons.video_vintage,
            ),
            title: Text(
              'Movies',
            ),
          ),
          BottomNavigationBarItem(
            backgroundColor: Colors.black,
            icon: Icon(Icons.live_tv),
            title: Text(
              'Tv shows',
            ),
          ),

          BottomNavigationBarItem(
            backgroundColor: Colors.black,
            icon: Icon(Icons.music_video),
            title: Text(
              'Music',
            ),
          ),
          BottomNavigationBarItem(
            backgroundColor: Colors.black,
            icon: Icon(Icons.radio),
            title: Text(
              'News',
            ),
          ),
          BottomNavigationBarItem(
            backgroundColor: Colors.black,
            icon: Icon(
              FontAwesome.heartbeat,
            ),
            title: Text(
              'LifeStyle',
            ),
          ),
          BottomNavigationBarItem(
            backgroundColor: Colors.black,
            icon: Icon(
              Ionicons.md_football,
            ),
            title: Text(
              'Sports',
            ),
          ),
        ],
      ),
    );
Run Code Online (Sandbox Code Playgroud)

负责加载的future函数如下:

 Future<String> widgetLoadingFunctions(int idx) async {
    switch (idx) {
      case 0:
        return _memoizer.runOnce(() async {
          var x = await functionsToBeLoadedForHomePage();
          return x;
        });

      case 1:
        return await functionsToBeLoadedForMoviesPage();

      case 2:
        return await functionsToBeLoadedForTvSHowsPage();

      case 3:
        return await functionsToBeLoadedForMusicPage();

      case 4:
        return await functionsToBeLoadedForNewsPage();

      case 5:
        return await functionsToBeLoadedForLifeStylePage();

      case 6:
        return await functionsToBeLoadedForSportsPage();
    }
  }
Run Code Online (Sandbox Code Playgroud)

之后我将使用 memoizer 以同样的方式实现其余的功能。

memoizer 的声明是:

class _LiveTvHomePageState extends State<LiveTvHomePage> {

  final AsyncMemoizer _memoizer = AsyncMemoizer();
Run Code Online (Sandbox Code Playgroud)

第一个函数,即functionsToBeLoadedForHomePage()如下:

Future<String> functionsToBeLoadedForHomePage() async {
    String url =
        "https://livetvapi.apyhi.com/api/v3/home?pageLocation=home&countries=IN&app_version=13&"
        "user_id=44edc2c905ae163f&package_id=livetv.movies.freemovies.watchtv.tvshows&os_platform=android";

    var res = await http
        .get(url, headers: {'Authorization': dartJsonWebTokenGenerator()});
    var _playListImgUrlsObjects = [];
    List<String> _thumbNailList = [];
    List<String> _titleNames = [];
    List<String> _subtitleNames = [];
    List<String> _noOfSongs = [];
    List<String> _playListVideoLinks = [];

    if (res.statusCode == 200) {
      final _homePage = homePageFromJson(res.body);

      for (var homeBannerObject in _homePage.homeBanners) {
        for (var obj in homeBannerObject.thumbnail) {
          _homeBannerObjectthumbnailList.add(obj.toString());
        }

        _homeBannerObjectMovieIdList.add(homeBannerObject.movieId.substring(8));
      }

      for (var homeBannerObject in _homePage.movies) {
        for (var obj in homeBannerObject.thumbnail) {
          _popularMoviesthumbNail.add(obj.toString());
        }

        _popularMoviesMovieId.add(homeBannerObject.movieId.substring(8));
      }

      for (var homeBannerObject in _homePage.series) {
        for (var obj in homeBannerObject.thumbnail) {
          _seriesData.add(obj.toString());

          _seriesVideoLinks.add(homeBannerObject.posterLink.substring(23, 34) +
              "&list=" +
              homeBannerObject.seriesId.substring(8));
        }
      }

      for (var everyObj in _homePage.musicCategories) {
        for (var playlistsObj in everyObj.playlists) {
          _titleNames.add(playlistsObj.playlistName);
          _subtitleNames.add(playlistsObj.playlistDescription.substring(
                  0,
                  playlistsObj.playlistDescription.length <= 20
                      ? playlistsObj.playlistDescription.length
                      : 20) +
              "...");
          _noOfSongs.add(playlistsObj.playlistTotalTracks.toString());
          _playListVideoLinks.add(playlistsObj.playlistUrl);
          for (var playlistImagesObj in playlistsObj.playlistImages) {
            _thumbNailList.add(playlistImagesObj.url);
          }
        }

        _detailsOfMusicInHomePage[everyObj.categoryName] = [
          _thumbNailList,
          _titleNames,
          _subtitleNames,
          _noOfSongs,
          _playListVideoLinks
        ];

        _thumbNailList = [];
        _titleNames = [];
        _subtitleNames = [];
        _noOfSongs = [];
        _playListVideoLinks = [];
      }

      for (var playListObj in _homePage.musicPlaylists.playlists) {
        _musicCategories.add(playListObj.playlistName);
      }

      for (var publisherObj in _homePage.publishers) {
        _popularNewsChannelNames.add(publisherObj.fullName);
        _popularNewsChannelProfilePicUrl.add(publisherObj.profilePicUrl);
        _popularNewsChanneldesc.add(publisherObj.content.description);
      }

      for (var liveChannelObj in _homePage.liveChannels) {
        _liveNewsChannelProfilePicUrl.add(liveChannelObj.publisherProfilePic);
        _liveNewsChannelNames.add(liveChannelObj.publisherName);
      }

      for (var playlistsObj in _homePage.musicPlaylists.playlists) {
        _playListNames.add(playlistsObj.playlistName);
        _playListTotalTracks.add(playlistsObj.playlistTotalTracks.toString());
        _playListImgUrlsObjects.add(playlistsObj.playlistImages);
        _playListVideoUrls.add(playlistsObj.playlistUrl);
      }

      for (var listObj in _playListImgUrlsObjects) {
        for (var obj in listObj) {
          _playListImgUrls.add(obj.url.toString());
        }
      }

      _musicPlaylistThemeName = _homePage.musicPlaylists.categoryName;
      return "Successfully Exectued";
    } else
      return null;
  }
Run Code Online (Sandbox Code Playgroud)

小部件加载函数是:

Widget widgetOptions(int idx) {
    Widget _selectedWidget = CircularProgressIndicator();
    switch (idx) {
      case 0:
        _selectedWidget = homePageForBottomNavigator();
        break;
      case 1:
        _selectedWidget = MoviesPageForBottomNavigator();
        break;
      case 2:
        _selectedWidget = TvSHowsPageOfBottomNavigator();
        break;
      case 3:
        _selectedWidget = MusicPageofBottomNavigator();

        break;
      case 4:
        _selectedWidget = NewsPageOfBottomNavigator();

        break;
      case 5:
        _selectedWidget = LifeStylePageOfBottomNavigator();
        break;
      case 6:
        _selectedWidget = SportsPageOfBottomNavigator();
    }
    return _selectedWidget;
  }
Run Code Online (Sandbox Code Playgroud)

所以基本上在future builder的未来会加载必要页面的相应功能,然后显示。

HomePageWidget 是这样的:

Widget homePageForBottomNavigator() {
    return Stack(
      children: <Widget>[
        Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            _buildBody(MediaQuery.of(context).size.height * 0.35,
                _homeBannerObjectthumbnailList, _homeBannerObjectMovieIdList),

            HorizontalListView("Popular movies", _popularMoviesthumbNail,
                _popularMoviesMovieId),
            HorizontalListView(
                "Popular Tv shows", _seriesData, _seriesVideoLinks),
            //infinite list view to be implemented here
            HorizontalGridViewOfCardsofGradientColorWithtitle(
                1,
                "Music",
                _detailsOfMusicInHomePage.keys.toList(),
                _detailsOfMusicInHomePage),
            HorizontalListViewWithoutViewAll(
                "_musicPlaylistThemeName,",
                _playListImgUrls,
                _playListNames,
                _playListTotalTracks,
                _playListVideoUrls),
            HorizontalListViewWitCircularCards(
                "Popular News channels",
                _popularNewsChannelProfilePicUrl,
                _popularNewsChannelNames,
                _popularNewsChanneldesc,
                _detailsNewsPage),
            HorizontalListViewWithoutViewAllForLiveNewsChannels(
                "Live News Channels",
                _liveNewsChannelProfilePicUrl,
                _liveNewsChannelNames,
                _newsPageLiveNewsVideoUrls),

            //VerticalListView(["Latest News"],false),
          ],
        ), //end of 1st Widget
        Positioned(
            bottom: MediaQuery.of(context).size.height * 0.01,
            left: MediaQuery.of(context).size.width * 0.3,
            child: returnToTopButton()),
      ],
    );
  }
Run Code Online (Sandbox Code Playgroud)

Chr*_*ore 6

这是因为您使用FutureBuilder 不正确,并且您链接的媒体文章为因不正确使用而产生的问题提供了过于复杂的解决方案。不要相信你读到的所有内容,尤其是当它们不是官方文档时。

国家FutureBuilder文件:

未来必须已提前获得,例如在 State.initState、State.didUpdateConfig 或 State.didChangeDependency 期间。在构造 FutureBuilder 时,不得在 State.build 或 StatelessWidget.build 方法调用期间创建它。如果 future 与 FutureBuilder 同时创建,那么每次重建 FutureBuilder 的父级时,异步任务都会重新启动。

您必须获得Future build,如 中initState。您现在正在做的是获取Futurewhile build

获取Futurewhile initState,将其存储在变量中,然后将其传递给FutureBuilder.

Future storedFuture;

@override
void initState() {
  super.initState();
  storedFuture = widgetLoadingFunctions(_selectedIndex);
}

@override
Widget build(BuildContext context) {
  ...

  body: SafeArea(
    child: SingleChildScrollView(
      controller: _scrollController,
        child: FutureBuilder(
          future: storedFuture,
Run Code Online (Sandbox Code Playgroud)