Карточки карусельные в Flutter - PullRequest
0 голосов
/ 25 февраля 2020

как я могу сделать горизонтальную карусель для карточек следующим образом:

enter image description here

Я пытался использовать SingleChildScrollView по горизонтали, но при этом показывается смежная карта Я хочу, чтобы одна карта отображалась одновременно и автоматически сдвигалась влево.

Как этого добиться?

РЕДАКТИРОВАТЬ: РАЗРЕШЕНО РЕШЕНИЕ

Проверьте эту статью: https://medium.com/@rageremix / как создать карусель в флаттере-979bc8ecf19

Исходный код: https://github.com/himanshusharma89/Flutter-Card-Carousel

1 Ответ

0 голосов
/ 25 февраля 2020

Используйте эти классы, это прекрасно работает.

Модель карусели:

class Carousel {
  final List<dynamic> images;
  final int currentIndex;
  final dynamic defaultImage;
  final Curve animationCurve;
  final Duration animationDuration;
  final double dotSize;
  final double dotIncreaseSize;
  final double dotSpacing;
  final Color dotColor;
  final Color dotBgColor;
  final Color dotIncreasedColor;
  final double indicatorBgPadding;
  final BoxFit boxFit;
  final double dotHorizontalPadding;
  final double dotVerticalPadding;
  final double moveIndicatorFromBottom;
  final bool noRadiusForIndicator;
  final void Function(int, int) onImageChange;
  final void Function(int) onImageTap;

  Carousel(
    this.images,
    this.currentIndex,
    this.defaultImage,
    this.animationCurve,
    this.animationDuration,
    this.dotSize,
    this.dotIncreaseSize,
    this.dotSpacing,
    this.dotColor,
    this.dotBgColor,
    this.dotIncreasedColor,
    this.indicatorBgPadding,
    this.boxFit,
    this.dotHorizontalPadding,
    this.dotVerticalPadding,
    this.moveIndicatorFromBottom,
    this.noRadiusForIndicator,
    this.onImageChange,
    this.onImageTap,
  );
}

Виджет карусели:

class ImageCarousel extends StatefulWidget implements Carousel {
  @override
  final Curve animationCurve;
  @override
  final Duration animationDuration;
  @override
  final BoxFit boxFit;
  @override
  final int currentIndex;
  @override
  final dynamic defaultImage;
  @override
  final Color dotBgColor;
  @override
  final Color dotColor;
  @override
  final double dotHorizontalPadding;
  @override
  final double dotIncreaseSize;
  @override
  final Color dotIncreasedColor;
  @override
  final double dotSize;
  @override
  final double dotSpacing;
  @override
  final double dotVerticalPadding;
  @override
  final List<dynamic> images;
  @override
  final double indicatorBgPadding;
  @override
  final double moveIndicatorFromBottom;
  @override
  final bool noRadiusForIndicator;
  @override
  final Function(int, int) onImageChange;
  @override
  final Function(int) onImageTap;

  const ImageCarousel({
    this.images,
    this.currentIndex = 0,
    this.animationCurve = Curves.ease,
    this.animationDuration = const Duration(milliseconds: 300),
    this.dotSize = 8.0,
    this.dotSpacing = 25.0,
    this.dotIncreaseSize = 2.0,
    this.dotColor = Colors.white,
    this.dotBgColor,
    this.dotIncreasedColor = Colors.white,
    this.indicatorBgPadding = 20.0,
    this.boxFit = BoxFit.cover,
    this.dotHorizontalPadding = 0.0,
    this.dotVerticalPadding = 0.0,
    this.moveIndicatorFromBottom = 0.0,
    this.noRadiusForIndicator = false,
    this.onImageChange,
    this.defaultImage,
    this.onImageTap,
  });

  @override
  State createState() => ImageCarouselState();
}

class ImageCarouselState extends State<ImageCarousel> {
  PageController _controller;
  int _currentImageIndex = 0;
  Timer timer;

  @override
  void initState() {
    _controller = PageController(initialPage: widget.currentIndex);
    if (widget.images != null && widget.images.isNotEmpty) {
      timer = Timer.periodic(const Duration(seconds: 7), (_) {
        if (_controller.hasClients) {
          if (_controller.page.round() == widget.images.length - 1) {
            _controller.animateToPage(
              0,
              duration: widget.animationDuration,
              curve: widget.animationCurve,
            );
          } else {
            _controller.nextPage(
              duration: widget.animationDuration,
              curve: widget.animationCurve,
            );
          }
        }
      });
    }
    super.initState();
  }

  @override
  void dispose() {
    _controller.dispose();
    timer?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final List<Widget> listImages =
        (widget.images != null && widget.images.isNotEmpty)
            ? _images()
            : _defaultImages();
    return Stack(
      children: <Widget>[
        _pages(listImages),
        widget.images.length > 1 ? _dots(listImages.length) : Container(),
      ],
    );
  }

  List<Widget> _images() => widget.images.map<Widget>(
        (dynamic netImage) {
          return netImage;
        },
      ).toList();

  List<Widget> _defaultImages() => <Widget>[
        widget.defaultImage,
      ];

  Widget _pages(List<Widget> images) {
    final PageView pageView = PageView(
      physics: const BouncingScrollPhysics(),
      controller: _controller,
      children: images,
      onPageChanged: (int currentPage) {
        if (widget.onImageChange != null) {
          widget.onImageChange(_currentImageIndex, currentPage);
        }
        _currentImageIndex = currentPage;
      },
    );
    if (widget.onImageTap == null) {
      return pageView;
    }
    return GestureDetector(
      child: pageView,
      onTap: () => widget.onImageTap(_currentImageIndex),
    );
  }

  Widget _dots(int length) => Positioned(
        bottom: widget.dotVerticalPadding,
        left: 0,
        right: 0,
        child: DotsIndicator(
          controller: _controller,
          itemCount: length,
          color: widget.dotColor,
          increasedColor: widget.dotIncreasedColor,
          dotSize: widget.dotSize,
          dotSpacing: widget.dotSpacing,
          dotIncreaseSize: widget.dotIncreaseSize,
          onPageSelected: (int page) {
            _controller.animateToPage(
              page,
              duration: widget.animationDuration,
              curve: widget.animationCurve,
            );
          },
        ),
      );
}

и индикатор:

class DotsIndicator extends AnimatedWidget {
  const DotsIndicator({
    this.controller,
    this.itemCount,
    this.onPageSelected,
    this.color,
    this.increasedColor,
    this.dotSize,
    this.dotIncreaseSize,
    this.dotSpacing,
  }) : super(listenable: controller);

  final PageController controller;
  final int itemCount;
  final ValueChanged<int> onPageSelected;
  final Color color;
  final Color increasedColor;
  final double dotSize;
  final double dotIncreaseSize;
  final double dotSpacing;

  Widget _buildDot(int index) {
    final double selectedness = Curves.easeOut.transform(
      max(
        0.0,
        1.0 - ((controller.page ?? controller.initialPage) - index).abs(),
      ),
    );
    final double zoom = 1.0 + (dotIncreaseSize - 1.0) * selectedness;
    final Color dotColor = zoom > 1.0 ? increasedColor : color;
    return Container(
      width: dotSpacing,
      child: Center(
        child: Material(
          color: dotColor,
          type: MaterialType.circle,
          child: Container(
            width: dotSize * zoom,
            height: dotSize * zoom,
            child: InkWell(
              onTap: () => onPageSelected(index),
            ),
          ),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: List<Widget>.generate(itemCount, _buildDot),
    );
  }
}

Создать карусель:

Container(
    height: ScreenUtil().height(370),
    child: ImageCarousel(
      boxFit: BoxFit.cover,
      images: state.images
          .map(
            (MainImage image) => CachedNetworkImage(
              height: double.infinity,
              width: double.infinity,
              imageUrl: image.imageUrl,
              fit: BoxFit.cover,
              fadeInDuration: const Duration(milliseconds: 200),
            ),
          )
          .toList(),
      animationCurve: Curves.fastOutSlowIn,
      animationDuration: const Duration(seconds: 1),
      dotSize: 8,
      dotIncreaseSize: 1.01,
      dotSpacing: 16,
      dotVerticalPadding: ScreenUtil().height(50),
      dotIncreasedColor: AppColors.colorButtonGreenLight,
      dotBgColor: AppColors.transparent,
      onImageTap: (int index) => _onImageTap(context, state.images, index),
    ),
  );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...