Flutter鸿蒙开发指南(七):轮播图搜索框和导航栏

前言

本篇文章实现轮播图的搜索和导航条。搜索栏暂时只写样式,主要是指示导航栏,不但有切换还有动画效果。

一、实现搜索框

1.1 搜索框代码

Dart 复制代码
Widget _getSearch() {
  return Positioned(
    top: 10,
    left: 0,
    right: 0,
    child: Padding(
      padding: EdgeInsets.all(10),
      child: Container(
        alignment: Alignment.centerLeft,
        padding: EdgeInsets.symmetric(horizontal: 40),
        height: 50,
        decoration: BoxDecoration(
          color: const Color.fromRGBO(0, 0, 0, 0.4),
          borderRadius: BorderRadius.circular(25)
        ),
        child: Text(
          "搜索...",
          style: TextStyle(color: Colors.white, fontSize: 16),
        ),
      ),
    ),
  );
}

修改HmSlider.dart的代码

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));
  }


  Widget _getSearch() {
    return Positioned(
      top: 10,
      left: 0,
      right: 0,
      child: Padding(
        padding: EdgeInsets.all(10),
        child: Container(
          alignment: Alignment.centerLeft,
          padding: EdgeInsets.symmetric(horizontal: 40),
          height: 50,
          decoration: BoxDecoration(
              color: const Color.fromRGBO(0, 0, 0, 0.4),
              borderRadius: BorderRadius.circular(25)
          ),
          child: Text(
            "搜索...",
            style: TextStyle(color: Colors.white, fontSize: 16),
          ),
        ),
      ),
    );
  }

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

1.2 运行效果

运行到鸿蒙的模拟器,搜索框的效果如下:

二、实现指示灯效果

2.1 实现指示灯UI

修改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));
  }

  Widget _getSearch() {
    return Positioned(
      top: 10,
      left: 0,
      right: 0,
      child: Padding(
        padding: EdgeInsets.all(10),
        child: Container(
          alignment: Alignment.centerLeft,
          padding: EdgeInsets.symmetric(horizontal: 40),
          height: 50,
          decoration: BoxDecoration(
              color: const Color.fromRGBO(0, 0, 0, 0.4),
              borderRadius: BorderRadius.circular(25)),
          child: Text(
            "搜索...",
            style: TextStyle(color: Colors.white, fontSize: 16),
          ),
        ),
      ),
    );
  }

  //返回指示灯
  Widget _getDots() {
    return Positioned(
        left: 0,
        right: 0,
        bottom: 10,
        child: SizedBox(
          height: 40,
          width: double.infinity,
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center, //主轴居中
            children: List.generate(widget.bannerList.length, (int index) {
              return Container(
                height: 6,
                width: 40,
                margin: EdgeInsets.symmetric(horizontal: 4),
                decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.circular(3),
                ),
              );
            }),
          ),
        ));
  }

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

此时指示灯实现了,但是并未实现指示灯切换对应图片的效果,比如第一个指示灯对应第一张图片,第二个指示灯对应第二张图片。点击哪个切换哪个

2.2 实现指示灯切换对应图片

切换指示灯的实现思路是:使用carouselController实现切换轮播图指示灯的效果

当前代码已经实现了,指示灯点击哪里就切换到哪里。

修改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> {
  CarouselSliderController _controller = CarouselSliderController();

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

    //返回轮播图插件
    //根据数据渲染的不同的轮播选项
    return CarouselSlider(
      carouselController: _controller, //绑定controller对象
      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: false,
      ),
    );
  }

  Widget _getSearch() {
    return Positioned(
      top: 10,
      left: 0,
      right: 0,
      child: Padding(
        padding: EdgeInsets.all(10),
        child: Container(
          alignment: Alignment.centerLeft,
          padding: EdgeInsets.symmetric(horizontal: 40),
          height: 50,
          decoration: BoxDecoration(
            color: const Color.fromRGBO(0, 0, 0, 0.4),
            borderRadius: BorderRadius.circular(25),
          ),
          child: Text(
            "搜索...",
            style: TextStyle(color: Colors.white, fontSize: 16),
          ),
        ),
      ),
    );
  }

  //返回指示灯
  Widget _getDots() {
    return Positioned(
      left: 0,
      right: 0,
      bottom: 10,
      child: SizedBox(
        height: 40,
        width: double.infinity,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center, //主轴居中
          children: List.generate(widget.bannerList.length, (int index) {
            return GestureDetector(
              onTap: () {
                _controller.jumpToPage(index);
              },
              child: Container(
                height: 6,
                width: 40,
                margin: EdgeInsets.symmetric(horizontal: 4),
                decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.circular(3),
                ),
              ),
            );
          }),
        ),
      ),
    );
  }

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

运行到模拟器,可以看到,效果已经出来了。但是现在仍然有问题,我不知道切换到了哪一个,需要一个索引来控制轮播图,接下来需要完善一下。

2.3 完善指示灯切换效果

实现指示灯切换效果可以使用三元表达式改变UI的样式。

Dart 复制代码
  width: index == _currentIndex ? 40 : 20,

修改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> {
  CarouselSliderController _controller =
      CarouselSliderController(); //控制 轮播图跳转的控制器
  int _currentIndex = 0;

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

    //返回轮播图插件
    //根据数据渲染的不同的轮播选项
    return CarouselSlider(
      carouselController: _controller, //绑定controller对象
      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: false,
        onPageChanged: (int index, reason) {
          _currentIndex = index;
          setState(() {});
        },
      ),
    );
  }

  Widget _getSearch() {
    return Positioned(
      top: 10,
      left: 0,
      right: 0,
      child: Padding(
        padding: EdgeInsets.all(10),
        child: Container(
          alignment: Alignment.centerLeft,
          padding: EdgeInsets.symmetric(horizontal: 40),
          height: 50,
          decoration: BoxDecoration(
            color: const Color.fromRGBO(0, 0, 0, 0.4),
            borderRadius: BorderRadius.circular(25),
          ),
          child: Text(
            "搜索...",
            style: TextStyle(color: Colors.white, fontSize: 16),
          ),
        ),
      ),
    );
  }

  //返回指示灯
  Widget _getDots() {
    return Positioned(
      left: 0,
      right: 0,
      bottom: 10,
      child: SizedBox(
        height: 40,
        width: double.infinity,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center, //主轴居中
          children: List.generate(widget.bannerList.length, (int index) {
            return GestureDetector(
              onTap: () {
                _controller.jumpToPage(index);
              },
              child: Container(
                height: 6,
                width: index == _currentIndex ? 40 : 20,
                margin: EdgeInsets.symmetric(horizontal: 4),
                decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.circular(3),
                ),
              ),
            );
          }),
        ),
      ),
    );
  }

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

如果运行可以看到,指示灯的宽度如果切换会变长了。但是都是白色的,不好看。所以我们需要再改动代码。

使用三元表达式,当未切换时是黑色,切换时是白色,0.3为透明度

Dart 复制代码
color: index == _currentIndex ? Colors.white : Color.fromRGBO(0, 0, 0, 0.3),

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> {
  CarouselSliderController _controller =
      CarouselSliderController(); //控制 轮播图跳转的控制器
  int _currentIndex = 0;

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

    //返回轮播图插件
    //根据数据渲染的不同的轮播选项
    return CarouselSlider(
      carouselController: _controller, //绑定controller对象
      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: false,
        onPageChanged: (int index, reason) {
          _currentIndex = index;
          setState(() {});
        },
      ),
    );
  }

  Widget _getSearch() {
    return Positioned(
      top: 10,
      left: 0,
      right: 0,
      child: Padding(
        padding: EdgeInsets.all(10),
        child: Container(
          alignment: Alignment.centerLeft,
          padding: EdgeInsets.symmetric(horizontal: 40),
          height: 50,
          decoration: BoxDecoration(
            color: const Color.fromRGBO(0, 0, 0, 0.4),
            borderRadius: BorderRadius.circular(25),
          ),
          child: Text(
            "搜索...",
            style: TextStyle(color: Colors.white, fontSize: 16),
          ),
        ),
      ),
    );
  }

  //返回指示灯
  Widget _getDots() {
    return Positioned(
      left: 0,
      right: 0,
      bottom: 10,
      child: SizedBox(
        height: 40,
        width: double.infinity,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center, //主轴居中
          children: List.generate(widget.bannerList.length, (int index) {
            return GestureDetector(
              onTap: () {
                _controller.jumpToPage(index);
              },
              child: Container(
                height: 6,
                width: index == _currentIndex ? 40 : 20,
                margin: EdgeInsets.symmetric(horizontal: 4),
                decoration: BoxDecoration(
                  color: index == _currentIndex ? Colors.white : Color.fromRGBO(0, 0, 0, 0.3),
                  borderRadius: BorderRadius.circular(3),
                ),
              ),
            );
          }),
        ),
      ),
    );
  }

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

2.4 实现动画效果

这个动画效果实现起来非常简单!只需要把原来的Container改成AnimatedContainer,再加上动画的时长即可。改掉指示灯的相关代码

修改HmSlider.dart的代码,代码如下:

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> {
  CarouselSliderController _controller =
      CarouselSliderController(); //控制 轮播图跳转的控制器
  int _currentIndex = 0;

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

    //返回轮播图插件
    //根据数据渲染的不同的轮播选项
    return CarouselSlider(
      carouselController: _controller, //绑定controller对象
      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,
        onPageChanged: (int index, reason) {
          _currentIndex = index;
          setState(() {});
        },
      ),
    );
  }

  Widget _getSearch() {
    return Positioned(
      top: 10,
      left: 0,
      right: 0,
      child: Padding(
        padding: EdgeInsets.all(10),
        child: Container(
          alignment: Alignment.centerLeft,
          padding: EdgeInsets.symmetric(horizontal: 40),
          height: 50,
          decoration: BoxDecoration(
            color: const Color.fromRGBO(0, 0, 0, 0.4),
            borderRadius: BorderRadius.circular(25),
          ),
          child: Text(
            "搜索...",
            style: TextStyle(color: Colors.white, fontSize: 16),
          ),
        ),
      ),
    );
  }

  //返回指示灯
  Widget _getDots() {
    return Positioned(
      left: 0,
      right: 0,
      bottom: 10,
      child: SizedBox(
        height: 40,
        width: double.infinity,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center, //主轴居中
          children: List.generate(widget.bannerList.length, (int index) {
            return GestureDetector(
              onTap: () {
                _controller.jumpToPage(index);
              },
              child: AnimatedContainer(
                duration: Duration(milliseconds: 300),
                height: 6,
                width: index == _currentIndex ? 40 : 20,
                margin: EdgeInsets.symmetric(horizontal: 4),
                decoration: BoxDecoration(
                  color: index == _currentIndex ? Colors.white : Color.fromRGBO(0, 0, 0, 0.3),
                  borderRadius: BorderRadius.circular(3),
                ),
              ),
            );
          }),
        ),
      ),
    );
  }

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

运行可以看到,已经实现了自动播放(未使用鼠标点击跳转),然后也切换成对应的指示灯的位置。

到此为此,已经实现完了轮播图的效果。后面只需要完成封一个接口,赋值给组件即可。

最后一步,别忘了提交自己的代码。

三、总结

感谢大家的观看,如果本篇文章对你有帮助,请你点个赞吧~,如果在文章中遇到问题,可以在评论区评论,看到会回复,您的支持是我创作的动力。

最后,欢迎加入开源鸿蒙跨平台社区:

https://openharmonycrossplatform.csdn.net

相关推荐
Miguo94well2 小时前
Flutter框架跨平台鸿蒙开发——地理知识速记APP的开发流程
flutter·华为·harmonyos·鸿蒙
LawrenceLan2 小时前
Flutter 零基础入门(二十六):StatefulWidget 与状态更新 setState
开发语言·前端·flutter·dart
张祥6422889042 小时前
误差理论与测量平差基础笔记十
笔记·算法·机器学习
qq_192779872 小时前
C++模块化编程指南
开发语言·c++·算法
2401_892000523 小时前
Flutter for OpenHarmony 猫咪管家App实战 - 添加提醒实现
前端·javascript·flutter
时光慢煮3 小时前
【Flutter × OpenHarmony】跨端开发实现全局Toast提示卡片
flutter·华为·开源·openharmony
IT陈图图3 小时前
Flutter × OpenHarmony 混合布局实战:在一个容器中优雅组合列表与网格
flutter·鸿蒙·openharmony
cici158744 小时前
大规模MIMO系统中Alamouti预编码的QPSK复用性能MATLAB仿真
算法·matlab·预编码算法
历程里程碑4 小时前
滑动窗口---- 无重复字符的最长子串
java·数据结构·c++·python·算法·leetcode·django