先上GIF图,根据轮播图片的主色调切换navigationbar的背景色
思路
想要根据轮播图的图片主色调切换navbar的背景色,几个关键条件如下:
- 轮播图的回调
- 获取轮播图的主色调
- 由上一个颜色动画过渡到新颜色
具体实现
轮播图
轮播图这里我用的card_swiper
,由于之前的flutter_swiper
已经不支持空安全了,所以就选择了前者。具体代码如下:
Swiper(
onIndexChanged: (index) {
_changeColor(_colorList[index]); //轮播图切换更新颜色
},
itemBuilder: (context, index) {
return _buildImage(index);
},
indicatorLayout: PageIndicatorLayout.COLOR,
autoplay: true,
duration: 1000,
itemCount: _imgList.length,
pagination: const SwiperPagination(),
),
其中_buildImage
方法是创建轮播图的item同时获取其相应的主色调,代码如下:
Widget _buildImage(int index) {
var image = Image.network(
_imgList[index],
fit: BoxFit.fill,
);
image.image
.resolve(ImageConfiguration.empty)
.addListener(ImageStreamListener((info, synchronousCall) {
if (synchronousCall) {
_getImageColor(info.image, index); //得到图片之后获取颜色
}
}, onChunk: (_) {}, onError: (_, stack) {}));
return image;
}
获取图片色调
获取图片主色调用的是官方的库palette_generator
。调用PaletteGenerator.fromImage
获取图片的色调,这里其实会得到一个颜色数组,然后我们这里获取其占比最大的颜色,更新存放颜色的list对应数据即可。
void _getImageColor(ui.Image image, int index) async {
var color = await PaletteGenerator.fromImage(image,
region: Rect.fromCenter(
center: Offset(image.width / 2.0, image.height / 2.0),
width: image.width / 3.0,
height: image.height / 3.0),
maximumColorCount: 3);
_colorList[index] = color.colors.first; //更换存放颜色list中的数据
}
至此获取颜色的逻辑基本就这些。接下来实现颜色过渡的逻辑。
颜色过渡
这里我创建了几个变量,用以处理颜色的RGB以及新旧颜色。
late int _r;
late int _g;
late int _b;
Color? _endColor;
Color? _startColor;
同时创建了AnimationController
用以计算颜色过渡的RGB值,ValueNotifier<Color>
用来更新navbar的颜色,主要逻辑如下:
监听AnimationController
,处理当前色值
_controller.addListener(() {
if (_controller.status == AnimationStatus.dismissed) return;
if (_endColor != null) {
_handleCurrentColor(_controller.value);
}
if (_controller.isCompleted) {
_startColor = bgColor;
_controller.reset();
}
});
变换颜色时记录目标颜色,并开始过渡
void _changeColor(Color color) {
_endColor = color;
_controller.forward();
}
颜色RGB处理逻辑,这里需要注意的是AnimationController
的value
是0的时候的逻辑兼容。
void _handleCurrentColor(double value) {
if (!(_startColor!.red != 0 && value == 0)) {
if (_startColor!.red > _endColor!.red) {
_r = _startColor!.red -
(value * (_startColor!.red - _endColor!.red)).toInt().abs();
} else {
_r = _startColor!.red +
(value * (_startColor!.red - _endColor!.red)).toInt().abs();
}
}
if (!(_startColor!.green != 0 && value == 0)) {
if (_startColor!.green > _endColor!.green) {
_g = _startColor!.green -
(value * (_startColor!.green - _endColor!.green)).toInt().abs();
} else {
_g = _startColor!.green +
(value * (_startColor!.green - _endColor!.green)).toInt().abs();
}
}
if (!(_startColor!.blue != 0 && value == 0)) {
if (_startColor!.blue > _endColor!.blue) {
_b = _startColor!.blue -
(value * (_startColor!.blue - _endColor!.blue)).toInt().abs();
} else {
_b = _startColor!.blue +
(value * (_startColor!.blue - _endColor!.blue)).toInt().abs();
}
}
bgColor = Color.fromARGB(255, _r, _g, _b);
_colorNotifier.value = bgColor;
}
至此,关键逻辑已完成,效果就是如文章开头的GIF效果了。