Flutter 購物車計(jì)數(shù)器 CounterStep

Flutter 可能會(huì)用到的計(jì)數(shù)器,支持[最小值, 最大值, 初始值]
全部刪除后賦最小值&選中賦值。

image.png

import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class CounterStep extends StatefulWidget {
  const CounterStep({
    required this.min,
    required this.max,
    this.initial,
    this.step = 1,
    required this.valueChanged,
    Key? key,
  })  : assert(min < max),
        assert(initial == null || (initial >= min && initial <= max)),
        assert(step > 0),
        super(key: key);

  /// 最小值
  final int min;

  /// 最大值
  final int max;

  /// 初始值,如果初始值為null或無效,則初始值為[min]最小值
  final int? initial;

  /// 步進(jìn),每次+/- Value變化數(shù)值,必須是正數(shù)
  final int step;

  /// Value值改變回調(diào)
  final ValueChanged<int> valueChanged;

  @override
  State<CounterStep> createState() => _CounterStepState();
}

class _CounterStepState extends State<CounterStep> {
 
 late final controller = TextEditingController(text: widget.initial ?? widget.min);

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 30,
      decoration: ShapeDecoration(
        shape: RoundedRectangleBorder(
          side: BorderSide(
              color: Theme.of(context).colorScheme.outline, width: 0.5),
          borderRadius: BorderRadius.circular(4.0),
        ),
      ),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          buildReduceButton(),
          VerticalDivider(
              width: 1, color: Theme.of(context).colorScheme.outline),
          SizedBox(
            width: 100,
            child: buildTextFieldInput(),
          ),
          VerticalDivider(
              width: 1, color: Theme.of(context).colorScheme.outline),
          buildIncreaseButton(),
        ],
      ),
    );
  }

  Widget buildTextFieldInput() {
    return TextField(
      decoration: const InputDecoration(
        isDense: true,
        contentPadding: EdgeInsets.zero,
        border: InputBorder.none,
      ),
      controller: controller,
      keyboardType: TextInputType.number,
      textAlign: TextAlign.center,
      maxLines: 1,
      inputFormatters: [
        CounterTextInputFormatter(min: widget.min, max: widget.max),
      ],
      onChanged: (value) {
        widget.valueChanged(int.parse(value));
      },
    );
  }

  Widget buildReduceButton() {
    return InkWell(
      child: Icon(Icons.remove, color: Theme.of(context).colorScheme.outline),
      onTap: () {
        count = max(count - widget.step, widget.min);
        String text = count.toString();
        controller.value = TextEditingValue(text: text, selection: TextSelection.collapsed(offset: text.length));
      },
    );
  }

  Widget buildIncreaseButton() {
    return InkWell(
      child: Icon(Icons.add, color: Theme.of(context).colorScheme.outline),
      onTap: () {
        count = min(count + widget.step, widget.max);
        String text = count.toString();
        controller.value = TextEditingValue(text: text, selection: TextSelection.collapsed(offset: text.length));
      },
    );
  }
}

class CounterTextInputFormatter extends TextInputFormatter {
  final int min;
  final int max;

  CounterTextInputFormatter({required this.min, required this.max});

  int get maxLength => '$max'.length;
  late final regExp = RegExp("^\\d{0,$maxLength}?\$");

  @override
  TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue ) {
    String oldText = oldValue.text;
    String newText = newValue.text;

    if (newText.isEmpty) {
      String text =  '$min';
      return TextEditingValue(
        text: text,
        selection: TextSelection(baseOffset: 0, extentOffset: text.length),
      );
    }
    // 判定 新輸入值符合輸入預(yù)期
    bool isValid = (oldText.length > newText.length) ||
        regExp.hasMatch(newText);
    
    if (isValid) {
      // 如果以0開頭、轉(zhuǎn)換為有效數(shù)字
      if (newText.startsWith('0')) {
        String text =  int.parse(newText).toString();
        return TextEditingValue(
          text: text,
          selection: TextSelection.collapsed(offset: text.length),
        );
      }
      return newValue;
    }

    return oldValue;
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容