从 Stream (Flutter) 控制轮播滑块

Posted

技术标签:

【中文标题】从 Stream (Flutter) 控制轮播滑块【英文标题】:Controlling Carousel Slider from Stream (Flutter) 【发布时间】:2020-05-25 12:37:17 【问题描述】:

我有来自流的数据,主要是队列和队列的当前mediaItem。所以mediaItem 总是在变化。 我得到了索引使用

index = queue.indexOf(mediaItem);

现在我想将轮播的当前页面设置为我的索引,我在轮播的initialPage 上添加了索引,但它只在索引更改时工作一次,它不会更改当前页面。

我的信息流看起来像这样

//this stream is initialized inside initState()
stream = Rx.combineLatest3<List<MediaItem>, MediaItem, PlaybackState, ScreenState>(
  Audioservice.queueStream,
  AudioService.currentMediaItemStream,
  AudioService.playbackStateStream,
      (queue, mediaItem, playbackState) => ScreenState(queue, mediaItem, playbackState)
);


Scaffold(
  body: new Center(
    child: StreamBuilder<ScreenState>(
      stream: stream,
      builder: (context, snapshot) 
        final screenState = snapshot.data;
        final queue = screenState?.queue;
        final mediaItem = screenState?.mediaItem;
        final state = screenState?.playbackState;
        final basicState = state?.basicState ?? BasicPlaybackState.none;
        int index = queue?.indexWhere((MediaItem mediaItemX)return (mediaItem?.id == mediaItemX.id););
        return (queue!=null&& mediaItem!=null && basicState != null && index != null) 
          ? mainView(queue, mediaItem, basicState, state, index)
          : Container(
            child: Center(
              child: SpinKitWave(
                color: Colors.white70,
              ),
            ),
          );
      ,
    ),
  ),
),

主视图

Widget mainView(List<MediaItem> queue, MediaItem mediaItemX, BasicPlaybackState basicState, PlaybackState state, int index)
  return CarouselSlider.builder(
    itemCount: queue.length,
    initialPage: queue.indexOf(mediaItemX),//only works first time
    itemBuilder: (BuildContext context, int itemIndex) 
      Stack(
        children: <Widget>[
          ClipRRect(
            borderRadius: BorderRadius.circular(8.0),
            child: Container(
              padding: EdgeInsets.fromLTRB(0,0,0,0),
              child: CachedNetworkImage(
                height: ScreenUtil().setWidth(1200),
                width: ScreenUtil().setWidth(1200),
                imageUrl: queue[itemIndex].artUri,
                fit: BoxFit.cover,
                placeholder: (context, url) => new SpinKitWave(color: Colors.white30, size: 30.0,),
                errorWidget: (context, url, error) => new Image.asset(
                  'images/addplaylist.png',
                  color: Colors.white30,
                ),
              ),
            )
          )
        ],
      ),
    
  );

问题是当mediaItem从另一个地方改变时,轮播需要根据数据流的变化来改变它的索引,也就是说,在Carousel里面的功能几乎和currentPage一样,这样每次索引都会改变currentPage 设置为来自流的新数据或控制来自流的轮播。 非常感谢带有示例的解决方案或提示。

附加信息:我正在使用carousel_slider: ^1.4.1

【问题讨论】:

考虑将pageController 传递给您的CarousselSlider,并让您的流修改该控制器 @RémiRousselet 你能举例说明如何做到这一点吗?因为轮播仍然必须在 StreamBuilder 内部,并且要控制使用 pageController,所以必须已经构建了轮播。 如here 所述,因为StreamBuilder 是一个Widget,它基于与Stream 交互的最新快照构建自身,它应该在索引更改时更改页面直接从流中重建 carouselSlider,包括使用新索引值的 initialPage 数据。 我不确定它是否与this 有关,我已经尝试过修复,似乎不起作用。 更改initialPage 不会更改当前页面。 initialPage 仅用于第一次渲染,之后被忽略 【参考方案1】:

initialPage 可以在空容器中正常工作(请参见下面的代码),在我看来,可能在缓存的图像网络或您的容器中缺少一些 key,请尝试放置一个 UniqueKey() 以确保小部件树将检测到更改时新快照来了...

使用StreamBuilder 和滑块的示例:

import 'dart:async';
import 'dart:math';

import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget 
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(body: MyHomePage()),
    );
  


class MyHomePage extends StatefulWidget 
  @override
  _MyHomePageState createState() => _MyHomePageState();


class _MyHomePageState extends State<MyHomePage> 
  final StreamController sc = StreamController();
  final rng = Random();

  final containers = [
    Container(color: Colors.pink),
    Container(color: Colors.black),
    Container(color: Colors.yellow),
    Container(color: Colors.brown),
  ];
  @override
  void initState() 
    super.initState();

    Timer.periodic(
      Duration(seconds: 5),
      (t) 
        sc.add(rng.nextInt(containers.length));
      ,
    );
  

  @override
  Widget build(BuildContext context) 
    return StreamBuilder(
      stream: sc.stream,
      builder: (context, snapshot) 
        if (snapshot.hasData == false) 
          return CircularProgressIndicator();
        
        return Container(
          child: mainView(containers, containers[snapshot.data]),
        );
      ,
    );
  

  Widget mainView(
    List<Container> queue,
    Container mediaItemX,
  ) 
    return CarouselSlider.builder(
      itemCount: queue.length,
      initialPage: queue.indexOf(mediaItemX), //only works first time
      itemBuilder: (BuildContext context, int itemIndex) 
        print('changed');
        
        return mediaItemX;
      ,
    );
  

【讨论】:

以上是关于从 Stream (Flutter) 控制轮播滑块的主要内容,如果未能解决你的问题,请参考以下文章

带有 YouTube 视频的轮播滑块

如何暂停和恢复轮播滑块

Jquery轮播滑块仅在悬停时循环并在鼠标离开时停止?

如何使用 jQuery 自定义动画 <ul> 轮播滑块?

React-Bootstrap 轮播滑块:如何检测显示中的当前幻灯片

当我试图在 laravel 中制作帖子的轮播滑块时,我遇到了一个错误