一、轮播图实现思路

轮播图的组成由:上面搜索框,中间轮播图,下面导航条。搜索框和导航条是叠加在最外层组件。要用Stack,Stack用轮播图,搜索框和导航条通过position的定位固定位置。Stack必须配合position成对出现。

1.1 使用轮播图组件

flutter pub add carousel_slider

看到"carousel_slider"这个插件有版本号,说明已经成功了。

1.2 编写轮播图相关代码

网络图片地址如下,这个用于测试:

https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meituan/1.jpg
https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meituan/2.png
https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meituan/3.jpg

定义轮播图数据对象类型:lib/viewmodels/home.dart代码

class BannerItem{
  String id;
  String imgUrl;
  BannerItem({required this.id, required this.imgUrl});

}

//每一个轮播图具体类型

lib/pages/Home/index.dart代码

import 'package:flutter/cupertino.dart';
import 'package:qing_mall/components/Home/HmCategory.dart';
import 'package:qing_mall/components/Home/HmHot.dart';
import 'package:qing_mall/components/Home/HmMoreList.dart';
import 'package:qing_mall/components/Home/HmSlider.dart';
import 'package:qing_mall/components/Home/HmSuggestion.dart';
import 'package:qing_mall/viewmodels/home.dart';

class HomeView extends StatefulWidget {
  const HomeView({super.key});

  @override
  State<HomeView> createState() => _HomeViewState();
}

class _HomeViewState extends State<HomeView> {
  final List<BannerItem> _bannerList = [
    BannerItem(
      id: "1",
      imgUrl: "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meituan/1.jpg",
    ),
    BannerItem(
      id: "2",
      imgUrl: "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meituan/2.png",
    ),
    BannerItem(
      id: "3",
      imgUrl: "https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/meituan/3.jpg",
    ),
  ];

  //获取滚动容器的内容

  List<Widget> _getScrollChildren() {
    return [
      //包裹普通widget的sliver家族的组件内容
      SliverToBoxAdapter(child: HmSlider(bannerList: _bannerList)), //轮播图组件
      //放置分类组件
      SliverToBoxAdapter(child: SizedBox(height: 10)),
      //SliverGrid SliverList指南纵向排列
      SliverToBoxAdapter(child: HmCategory()), //分类组件
      SliverToBoxAdapter(child: SizedBox(height: 10)),
      SliverToBoxAdapter(child: HmSuggestion()), //推荐组件
      SliverToBoxAdapter(child: SizedBox(height: 10)),

      //Flex和Expanded配合起来可以均分比例
      SliverToBoxAdapter(
        child: Padding(
            padding: EdgeInsets.symmetric(horizontal: 10),
            child: Flex(
              direction: Axis.horizontal,
              children: [
                Expanded(child: HmHot()),
                SizedBox(
                  width: 10,
                ),
                Expanded(child: HmHot()),
              ],
            )),
      ),
      SliverToBoxAdapter(child: SizedBox(height: 10)),
      HmMorelist(), //无限滚动列表
    ];
  }

  @override
  Widget build(BuildContext context) {
    //CustomScrollview要求:必须是sliver家族的内容
    return CustomScrollView(slivers: _getScrollChildren());
  }
}

lib/components/Home/HmSlider.dart代码

import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
import 'package:qing_mall/viewmodels/home.dart';

class HmSlider extends StatefulWidget {
  //父传子
  final List<BannerItem> bannerList;

  HmSlider({Key? key, required this.bannerList}) : super(key: key);

  @override
  State<HmSlider> createState() => _HmSliderState();
}

class _HmSliderState extends State<HmSlider> {
  
  Widget _getSlider(){
    //返回轮播图插件
    //根据数据渲染的不同的轮播选项
    return CarouselSlider(items: List.generate(widget.bannerList.length, (int index){
      return Image.network(widget.bannerList[index].imgUrl);
    }), options: CarouselOptions());
  }
  
  @override
  Widget build(BuildContext context) {
    //Stack里面放轮播图 再盖上搜索框 指示灯导航
    return Stack(
      children: [
        //第一个放轮播图
        _getSlider()
      ],
    );
    // return Container(
    //     color: Colors.blue,
    //     height: 300,
    //     alignment: Alignment.center,
    //     child:
    //         Text('轮播图', style: TextStyle(color: Colors.white, fontSize: 20)));
  }
}

运行鸿蒙端,可以看到轮播图已经出来了,但是效果不太正常。因为还没有给图片设置宽度以及图片的自适应

在开发中,高度和宽度一般是不能写死的。何为写死?比如宽度直接设置为600,高度设置为800就是写死,当手机屏幕分辨率不同的时候会导致异常。所以我们需要动态获取,关键代码:

  //在Flutter中获取屏幕宽度的方法:媒体查询对象: MediaQuery
  final double screenWidth = MediaQuery.of(context).size.width; //屏幕宽度

还需要视口,视口默认是0.8,关键代码:

 //CarouselOptions中有一个视口占比:  viewportFraction:
      viewportFraction:1

改变lib/components/Home/HmSlider.dart代码

import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
import 'package:qing_mall/viewmodels/home.dart';

class HmSlider extends StatefulWidget {
  //父传子
  final List<BannerItem> bannerList;

  HmSlider({Key? key, required this.bannerList}) : super(key: key);

  @override
  State<HmSlider> createState() => _HmSliderState();
}

class _HmSliderState extends State<HmSlider> {
  
  Widget _getSlider(){
    //在Flutter中获取屏幕宽度的方法:媒体查询对象: MediaQuery
    final double screenWidth = MediaQuery.of(context).size.width; //屏幕宽度



    //返回轮播图插件
    //根据数据渲染的不同的轮播选项
    return CarouselSlider(items: List.generate(widget.bannerList.length, (int index){
      return Image.network(widget.bannerList[index].imgUrl,
      fit: BoxFit.cover,
      width: screenWidth,);
    }), options: CarouselOptions(
      //CarouselOptions中有一个视口占比:  viewportFraction:
      viewportFraction:1
    ));
  }
  
  @override
  Widget build(BuildContext context) {
    //Stack里面放轮播图 再盖上搜索框 指示灯导航
    return Stack(
      children: [
        //第一个放轮播图
        _getSlider()
      ],
    );
    // return Container(
    //     color: Colors.blue,
    //     height: 300,
    //     alignment: Alignment.center,
    //     child:
    //         Text('轮播图', style: TextStyle(color: Colors.white, fontSize: 20)));
  }
}

运行效果如下,可以看到屏幕已经占满了。但是现在轮播图没有自动播放,我们现在需要实现自动播放

1.3 实现轮播图自动播放

设置为自动播放,只需要添加一个属性即可,关键代码:

     autoPlay: true

默认3秒钟执行一次,运行即可看到3秒后自动播放。

调整播放间距代码:

//默认3秒
autoPlayInterval: Duration(seconds: 5),

HmSlider.dart完整代码如下:

import 'package:carousel_slider/carousel_slider.dart';
import 'package:flutter/material.dart';
import 'package:qing_mall/viewmodels/home.dart';

class HmSlider extends StatefulWidget {
  //父传子
  final List<BannerItem> bannerList;

  HmSlider({Key? key, required this.bannerList}) : super(key: key);

  @override
  State<HmSlider> createState() => _HmSliderState();
}

class _HmSliderState extends State<HmSlider> {
  Widget _getSlider() {
    //在Flutter中获取屏幕宽度的方法:媒体查询对象: MediaQuery
    final double screenWidth = MediaQuery.of(context).size.width; //屏幕宽度

    //返回轮播图插件
    //根据数据渲染的不同的轮播选项
    return CarouselSlider(
        items: List.generate(widget.bannerList.length, (int index) {
          return Image.network(
            widget.bannerList[index].imgUrl,
            fit: BoxFit.cover,
            width: screenWidth,
          );
        }),
        options: CarouselOptions(
            //CarouselOptions中有一个视口占比:  viewportFraction:
            //高度默认300
            height: 300,
            //调整播放间距
            autoPlayInterval: Duration(seconds: 5),
            viewportFraction: 1,
            autoPlay: true));
  }

  @override
  Widget build(BuildContext context) {
    //Stack里面放轮播图 再盖上搜索框 指示灯导航
    return Stack(
      children: [
        //第一个放轮播图
        _getSlider()
      ],
    );
    // return Container(
    //     color: Colors.blue,
    //     height: 300,
    //     alignment: Alignment.center,
    //     child:
    //         Text('轮播图', style: TextStyle(color: Colors.white, fontSize: 20)));
  }
}

二、总结

第一步声明了BannerItem,为什么要声明?

使用Map<String,dynamic>这种类型也行,但是用class类型会更加明确。之前的Tab就是使用Map<String,dynamic>来实现。用class,可以通过.的形式,.不出来就报错。两种选择:一种更加灵活,但是不容易察觉报错的写法,另一种是类型明确。

第二步在Home/index.dart声明了三张图片的数据,通过构造函数的方式传入id和imageurl地址。如果后面要缓存接口,只需要把接口重新删除,再对_bannerList重新赋值就可以了。

第三步声明完数据后。把数据传递给了HmSlider,通过父传子组件接收这个数据,接收之后在对应的内CarouselSider。还需要尺寸适配、适应屏幕宽度、以及是否自动播放,把原来的Container放成了Stack布局。因为后续需要实现指示灯导航和搜索UI结构。

完成本教程后,记得提交代码:

最后,欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐