Flutter轮播图切换改变navigationbar背景色

563 阅读2分钟

先上GIF图,根据轮播图片的主色调切换navigationbar的背景色

未命名.gif

思路

想要根据轮播图的图片主色调切换navbar的背景色,几个关键条件如下:

  1. 轮播图的回调
  2. 获取轮播图的主色调
  3. 由上一个颜色动画过渡到新颜色

具体实现

轮播图

轮播图这里我用的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处理逻辑,这里需要注意的是AnimationControllervalue是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效果了。