Flutter练习:实现自定义的分页指示器

先贴一下运行效果

Flutter练习:实现自定义的分页指示器_第1张图片

在开发过程中往往会遇到需求是这样的,我们要实现的效果如图二所示。

项目中使用的轮播图插件是:

通过查阅Swipe_pagination的源代码可知,目前只提供了FractionPaginationBuilder(百分数类型的,例如:1/5)、RectSwipePaginationBuilder(矩形)、DotSwiperPaginationBuilder(圆点)。像我们目前的需求就是做带圆角的,选中的是带圆角的圆柱,未选中的是圆点。

最终通过复制修改代码(swipe_pagination.dart)实现的功能

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_swiper_null_safety/flutter_swiper_null_safety.dart';

class SquareSwiperPaginationBuilder extends SwiperPlugin {
  ///color when current index,if set null , will be Theme.of(context).primaryColor
  final Color activeColor;

  ///,if set null , will be Theme.of(context).scaffoldBackgroundColor
  final Color color;

  ///Size of the dot when activate
  final double activeSize;

  ///Size of the dot
  final double size;

  /// Space between dots
  final double space;

  ///加了一个自定义高度的参数,如果要使用Size.height 的话需要修改size的类型(double->Size),这样的话也需要重新写PageIndicator,过犹不及了。
  ///Height of the dots
  final double height;

  final Key key;

  const SquareSwiperPaginationBuilder(
      {this.activeColor,
      this.color,
      this.key,
      this.height: 4.0,
      this.size: 4.0,
      this.activeSize: 10.0,
      this.space: 3.0});

  @override
  Widget build(BuildContext context, SwiperPluginConfig config) {
    if (config.itemCount > 20) {
      print(
          "The itemCount is too big, we suggest use FractionPaginationBuilder instead of DotSwiperPaginationBuilder in this sitituation");
    }
    Color activeColor = this.activeColor;
    Color color = this.color;

    if (activeColor == null || color == null) {
      ThemeData themeData = Theme.of(context);
      activeColor = this.activeColor ?? themeData.primaryColor;
      color = this.color ?? themeData.scaffoldBackgroundColor;
    }

    if (config.indicatorLayout != PageIndicatorLayout.NONE &&
        config.layout == SwiperLayout.DEFAULT) {
      return new PageIndicator(
        count: config.itemCount,
        controller: config.pageController,
        layout: config.indicatorLayout,
        size: size,
        activeColor: activeColor,
        color: color,
        space: space,
      );
    }

    List list = [];

    int itemCount = config.itemCount;
    int activeIndex = config.activeIndex;

    for (int i = 0; i < itemCount; ++i) {
      bool active = i == activeIndex;
      list.add(Container(
        key: Key("pagination_$i"),
        margin: EdgeInsets.all(space),
        child: active
            ? SizedBox(
                width: activeSize,
                height: height,
                child: DecoratedBox(
                  decoration: BoxDecoration(
                      borderRadius:
                          BorderRadius.all(Radius.circular(height / 2)),
                      color: activeColor),
                ),
              )
            : SizedBox(
                width: size,
                height: height,
                child: DecoratedBox(
                  decoration: BoxDecoration(
                      borderRadius:
                          BorderRadius.all(Radius.circular(height / 2)),
                      color: color),
                ),
              ),
      ));
    }

    if (config.scrollDirection == Axis.vertical) {
      return new Column(
        key: key,
        mainAxisSize: MainAxisSize.min,
        children: list,
      );
    } else {
      return new Row(
        key: key,
        mainAxisSize: MainAxisSize.min,
        children: list,
      );
    }
  }
}

typedef Widget SwiperPaginationBuilder(
    BuildContext context, SwiperPluginConfig config);

class SwiperCustomPagination extends SwiperPlugin {
  final SwiperPaginationBuilder builder;

  SwiperCustomPagination({@required this.builder});

  @override
  Widget build(BuildContext context, SwiperPluginConfig config) {
    return builder(context, config);
  }
}

class SquareSwiperPagination extends SwiperPlugin {
  static const SwiperPlugin square = const SquareSwiperPaginationBuilder();

  /// Alignment.bottomCenter by default when scrollDirection== Axis.horizontal
  /// Alignment.centerRight by default when scrollDirection== Axis.vertical
  final Alignment alignment;

  /// Distance between pagination and the container
  final EdgeInsetsGeometry margin;

  /// Build the widet
  final SwiperPlugin builder;

  final Key key;

  const SquareSwiperPagination(
      {this.alignment,
      this.key,
      this.margin: const EdgeInsets.all(10.0),
      this.builder: SquareSwiperPagination.square});

  Widget build(BuildContext context, SwiperPluginConfig config) {
    Alignment alignment = Alignment.bottomCenter;
    Widget child = Container(
      margin: margin,
      child: this.builder.build(context, config),
    );

    ///拿掉这个判断之后,使得指示器无论在轮播图之上还是下面都可以居中
    // if (!config.outer) {
    child = new Align(
      key: key,
      alignment: alignment,
      child: child,
    );
    // }
    return child;
  }
}

大部分代码都是源代码,主要是改了最后list.add(Container(...))部分(大约在第70行代码附近),通过active的判断,返回不同的Widget。

在实际使用中就可以这样写:

Flutter练习:实现自定义的分页指示器_第2张图片

 这样就可以实现如文章顶部第二个轮播图的效果。

  • color:是指默认的未选中的指示器的颜色
  • activeColor:选中时的指示器的颜色
  • size:未选中时的指示器的宽度
  • activeSize:选中时的指示器的宽度

同时,自定义的指示器的代码里面还加入了一个height参数,用来设置指示器的高度。可以通过设置size和activeSize一致,或包含height,再控制圆角实现, 实现矩形的定长指示器,这里不再赘述。

你可能感兴趣的:(前端,flutter)