美文网首页
Flutter封装switch实践 2023-09-08 周五

Flutter封装switch实践 2023-09-08 周五

作者: 勇往直前888 | 来源:发表于2023-09-07 17:27 被阅读0次

简介

iOS原生开发的时候,Switch是一个很简单的控件,使用起来非常方便。
但是,到了Flutter中,关于Switch却遇到了许多的困惑,带来了一定的混乱。

Android风格的Switch

企业微信截图_8c91f97c-c65e-44b5-bdca-ab8447350875.png
Switch({
  Key? key,
  required bool value,
  required void Function(bool)? onChanged,
  Color? activeColor,
  Color? activeTrackColor,
  Color? inactiveThumbColor,
  Color? inactiveTrackColor,
  ImageProvider<Object>? activeThumbImage,
  void Function(Object, StackTrace?)? onActiveThumbImageError,
  ImageProvider<Object>? inactiveThumbImage,
  void Function(Object, StackTrace?)? onInactiveThumbImageError,
  MaterialStateProperty<Color?>? thumbColor,
  MaterialStateProperty<Color?>? trackColor,
  MaterialStateProperty<Color?>? trackOutlineColor,
  MaterialStateProperty<double?>? trackOutlineWidth,
  MaterialStateProperty<Icon?>? thumbIcon,
  MaterialTapTargetSize? materialTapTargetSize,
  DragStartBehavior dragStartBehavior = DragStartBehavior.start,
  MouseCursor? mouseCursor,
  Color? focusColor,
  Color? hoverColor,
  MaterialStateProperty<Color?>? overlayColor,
  double? splashRadius,
  FocusNode? focusNode,
  void Function(bool)? onFocusChange,
  bool autofocus = false,
})
  • 这么多的参数,想想都头皮发麻。真想不到,小小的Switch尽然要配置这么多的参数。

  • 常用的参数也就valueonChanged两个,实在想不明白,为何要那么多的配置参数。

  • Android手机上,这个样子也就忍了。但是在苹果手机上,这个摸样确实太丑。

iOS风格的Switch

企业微信截图_02ae9533-488e-4e2c-bb93-84d68f22a63a.png
CupertinoSwitch({
  Key? key,
  required bool value,
  required void Function(bool)? onChanged,
  Color? activeColor,
  Color? trackColor,
  Color? thumbColor,
  bool? applyTheme,
  Color? focusColor,
  FocusNode? focusNode,
  void Function(bool)? onFocusChange,
  bool autofocus = false,
  DragStartBehavior dragStartBehavior = DragStartBehavior.start,
})
  • 参数少了很多,感觉好多了;

  • 常用的参数也是valueonChanged这两个;

  • 样子感觉好多了,就算在Android手机上,感觉也是不错的;

是否保持状态?

  • value参数的角度来看,Switch不应该保持状态;状态由父组件通过value参数设置;

  • onChanged参数的角度来看,Switch应该保持状态。随着不断点击Switch组件,开关状态不断变化,并且通过onChanged参数将变化回传给父组件。

  • 两个必填参数,传递出两种不同的意图,到底要不要保持状态?

实际上Switch不能保留状态,但是确实能将变化后的状态传出来。并且传出来值必定跟传入的value值相反

思路

  • 在外观上,iOS风格的完胜;所以没有必要维持两套风格,取iOS的风格就好;

  • 关于状态方面,干脆就支持两种使用方式;想不想保留状态,由父组件通过配置参数来决定。

参数设计

/// 模式1:给value参数,不保持状态,由父组件指定开关状态;
/// 模式2:不给value参数,自己记忆开关状态;
class PandaSwitch extends StatefulWidget {
  const PandaSwitch({
    Key? key,
    this.initialValue,
    this.value,
    this.onChanged,
    this.width,
    this.height,
    this.margin,
    this.padding,
  }) : super(key: key);

  final bool? initialValue;
  final bool? value;
  final void Function(bool)? onChanged;
  final double? width;
  final double? height;
  final EdgeInsetsGeometry? margin;
  final EdgeInsetsGeometry? padding;

  @override
  State<PandaSwitch> createState() => _PandaSwitchState();
}
  • value参数类型改为可选,通过父组件给不给这个参数来决定是否保留状态。

  • onChanged参数类型也改为可选,降低使用要求。

  • width, height, margin, padding这几个直接给Container,为了布局方便。可以根据需求追加。

具体实现

基类

由于要保存状态,响应点击的变化,所以选择StatefulWidget作为基类;

初始值

  • 参数initialValue,就是用来设置初始值的;

  • 如果不设置,那就默认关闭;

  • 只执行一次

  @override
  void initState() {
    super.initState();

    /// 初始值由外部指定;不指定的话,给默认值false
    _isOn = widget.initialValue ?? false;
  }

模式确定

  • 由参数value是否为空来决定使用模式;

  • 每次build都判断一次,调整状态;

  @override
  Widget build(BuildContext context) {
    /// 如果外部指定了值,那么强制设定为外部值
    if (widget.value != null) {
      _isOn = widget.value!;
    }
    ///……
  }

界面实现

  • 使用图片表示状态
企业微信截图_77280fc2-14c4-46ea-a8de-86cfa35865b6.png
  • 外面套个Container是为了使用的时候布局方便

参考代码

import 'package:flutter/material.dart';
import 'package:pandabuy/r.dart';

/// 模式1:给value参数,不保持状态,由父组件指定开关状态;
/// 模式2:不给value参数,自己记忆开关状态;
class PandaSwitch extends StatefulWidget {
  const PandaSwitch({
    Key? key,
    this.initialValue,
    this.value,
    this.onChanged,
    this.width,
    this.height,
    this.margin,
    this.padding,
  }) : super(key: key);

  final bool? initialValue;
  final bool? value;
  final void Function(bool)? onChanged;
  final double? width;
  final double? height;
  final EdgeInsetsGeometry? margin;
  final EdgeInsetsGeometry? padding;

  @override
  State<PandaSwitch> createState() => _PandaSwitchState();
}

class _PandaSwitchState extends State<PandaSwitch> {
  late bool _isOn;

  @override
  void initState() {
    super.initState();

    /// 初始值由外部指定;不指定的话,给默认值false
    _isOn = widget.initialValue ?? false;
  }

  @override
  Widget build(BuildContext context) {
    /// 如果外部指定了值,那么强制设定为外部值
    if (widget.value != null) {
      _isOn = widget.value!;
    }

    var imageName = _isOn ? R.assetsImgSwitchOn : R.assetsImgSwitchOff;
    return GestureDetector(
      behavior: HitTestBehavior.opaque,
      onTap: () {
        setState(() {
          _isOn = !_isOn;
        });
        if (widget.onChanged != null) {
          widget.onChanged!(_isOn);
        }
      },
      child: Container(
        color: Colors.transparent,
        width: widget.width,
        height: widget.height,
        margin: widget.margin,
        padding: widget.padding,
        child: Center(
          child: Image.asset(imageName),
        ),
      ),
    );
  }
}

使用的例子

PandaSwitch(
  margin: EdgeInsets.only(left: 5.w),
  value: ((pi['unPackWeightSelect'] ==
          '1') ||
      (pi['unPackWeightSelect'] == '2')),
  onChanged: (value) {
    if (pi['unPackWeightSelect'] == '2') {
      return;
    }
    logic.unPackClick(pi, value);
  },
),
  • 指定了value,显然是无状态的使用方式;开关状态由父组件决定;

  • 如果要记忆状态,可以不给value参数,而是给initialValue参数(不给的话默认是false

  • Container能带来方便,不然的话,外面就要套一个,或者加个占位的SizedBox

相关文章

网友评论

      本文标题:Flutter封装switch实践 2023-09-08 周五

      本文链接:https://www.haomeiwen.com/subject/ebqrvdtx.html