前言
本篇文章实现轮播图的搜索和导航条。搜索栏暂时只写样式,主要是指示导航栏,不但有切换还有动画效果。
一、实现搜索框
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)));
}
}
运行可以看到,已经实现了自动播放(未使用鼠标点击跳转),然后也切换成对应的指示灯的位置。

到此为此,已经实现完了轮播图的效果。后面只需要完成封一个接口,赋值给组件即可。
最后一步,别忘了提交自己的代码。

三、总结
感谢大家的观看,如果本篇文章对你有帮助,请你点个赞吧~,如果在文章中遇到问题,可以在评论区评论,看到会回复,您的支持是我创作的动力。
最后,欢迎加入开源鸿蒙跨平台社区: